diff options
Diffstat (limited to 'src')
448 files changed, 55830 insertions, 20000 deletions
diff --git a/src/Makefile b/src/Makefile index f47da7b5ac735..d73a175abc866 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,4 +1,4 @@ -SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet radius rsn_supp tls utils wps +SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet p2p radius rsn_supp tls utils wps all: for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d; done diff --git a/src/ap/accounting.c b/src/ap/accounting.c index 7939c680f5804..954053131a7a6 100644 --- a/src/ap/accounting.c +++ b/src/ap/accounting.c @@ -1,15 +1,9 @@ /* * hostapd / RADIUS Accounting - * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2009, 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 - * published 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 "utils/includes.h" @@ -23,6 +17,7 @@ #include "ieee802_1x.h" #include "ap_config.h" #include "sta_info.h" +#include "ap_drv_ops.h" #include "accounting.h" @@ -31,8 +26,8 @@ * input/output octets and updates Acct-{Input,Output}-Gigawords. */ #define ACCT_DEFAULT_UPDATE_INTERVAL 300 -static void accounting_sta_get_id(struct hostapd_data *hapd, - struct sta_info *sta); +static void accounting_sta_interim(struct hostapd_data *hapd, + struct sta_info *sta); static struct radius_msg * accounting_msg(struct hostapd_data *hapd, @@ -44,6 +39,7 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, u8 *val; size_t len; int i; + struct wpabuf *b; msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST, radius_client_get_id(hapd->radius)); @@ -72,7 +68,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)) { @@ -81,7 +79,17 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, } if (sta) { + /* Use 802.1X identity if available */ val = ieee802_1x_get_identity(sta->eapol_sm, &len); + + /* Use RADIUS ACL identity if 802.1X provides no identity */ + if (!val && sta->identity) { + val = (u8 *) sta->identity; + len = os_strlen(sta->identity); + } + + /* Use STA MAC if neither 802.1X nor RADIUS ACL provided + * identity */ if (!val) { os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(sta->addr)); @@ -96,70 +104,11 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, } } - if (hapd->conf->own_ip_addr.af == AF_INET && - !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, - (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) { - printf("Could not add NAS-IP-Address\n"); - goto fail; - } - -#ifdef CONFIG_IPV6 - if (hapd->conf->own_ip_addr.af == AF_INET6 && - !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS, - (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) { - printf("Could not add NAS-IPv6-Address\n"); - goto fail; - } -#endif /* CONFIG_IPV6 */ - - if (hapd->conf->nas_identifier && - !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, - (u8 *) hapd->conf->nas_identifier, - os_strlen(hapd->conf->nas_identifier))) { - printf("Could not add NAS-Identifier\n"); - goto fail; - } - - if (sta && - !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) { - printf("Could not add NAS-Port\n"); - goto fail; - } - - os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", - MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid); - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, - (u8 *) buf, os_strlen(buf))) { - printf("Could not add Called-Station-Id\n"); + if (add_common_radius_attr(hapd, hapd->conf->radius_acct_req_attr, sta, + msg) < 0) goto fail; - } if (sta) { - os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, - MAC2STR(sta->addr)); - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, - (u8 *) buf, os_strlen(buf))) { - printf("Could not add Calling-Station-Id\n"); - goto fail; - } - - if (!radius_msg_add_attr_int32( - msg, RADIUS_ATTR_NAS_PORT_TYPE, - RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { - printf("Could not add NAS-Port-Type\n"); - goto fail; - } - - os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s", - radius_sta_rate(hapd, sta) / 2, - (radius_sta_rate(hapd, sta) & 1) ? ".5" : "", - radius_mode_txt(hapd)); - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, - (u8 *) buf, os_strlen(buf))) { - printf("Could not add Connect-Info\n"); - goto fail; - } - for (i = 0; ; i++) { val = ieee802_1x_get_radius_class(sta->eapol_sm, &len, i); @@ -172,6 +121,24 @@ 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; + } + + if (!b && sta->radius_cui && + !radius_msg_add_attr(msg, + RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, + (u8 *) sta->radius_cui, + os_strlen(sta->radius_cui))) { + wpa_printf(MSG_ERROR, "Could not add CUI from ACL"); + goto fail; + } } return msg; @@ -186,7 +153,7 @@ static int accounting_sta_update_stats(struct hostapd_data *hapd, struct sta_info *sta, struct hostap_sta_driver_data *data) { - if (hapd->drv.read_sta_data(hapd, data, sta->addr)) + if (hostapd_drv_read_sta_data(hapd, data, sta->addr)) return -1; if (sta->last_rx_bytes > data->rx_bytes) @@ -235,21 +202,22 @@ static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx) void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) { struct radius_msg *msg; + struct os_time t; int interval; if (sta->acct_session_started) return; - accounting_sta_get_id(hapd, sta); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, "starting accounting session %08X-%08X", sta->acct_session_id_hi, sta->acct_session_id_lo); - time(&sta->acct_session_start); + os_get_time(&t); + sta->acct_session_start = t.sec; sta->last_rx_bytes = sta->last_tx_bytes = 0; sta->acct_input_gigawords = sta->acct_output_gigawords = 0; - hapd->drv.sta_clear_stats(hapd, sta->addr); + hostapd_drv_sta_clear_stats(hapd, sta->addr); if (!hapd->conf->radius->acct_server) return; @@ -262,8 +230,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; } @@ -275,6 +244,7 @@ static void accounting_sta_report(struct hostapd_data *hapd, struct radius_msg *msg; int cause = sta->acct_terminate_cause; struct hostap_sta_driver_data data; + struct os_time now; u32 gigawords; if (!hapd->conf->radius->acct_server) @@ -288,8 +258,9 @@ static void accounting_sta_report(struct hostapd_data *hapd, return; } + os_get_time(&now); if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME, - time(NULL) - sta->acct_session_start)) { + now.sec - sta->acct_session_start)) { printf("Could not add Acct-Session-Time\n"); goto fail; } @@ -344,7 +315,7 @@ static void accounting_sta_report(struct hostapd_data *hapd, } if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP, - time(NULL))) { + now.sec)) { printf("Could not add Event-Timestamp\n"); goto fail; } @@ -359,9 +330,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: @@ -374,7 +346,8 @@ static void accounting_sta_report(struct hostapd_data *hapd, * @hapd: hostapd BSS data * @sta: The station */ -void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta) +static void accounting_sta_interim(struct hostapd_data *hapd, + struct sta_info *sta) { if (sta->acct_session_started) accounting_sta_report(hapd, sta, 0); @@ -401,7 +374,7 @@ void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta) } -static void accounting_sta_get_id(struct hostapd_data *hapd, +void accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta) { sta->acct_session_id_lo = hapd->acct_session_id_lo++; @@ -464,7 +437,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); } @@ -475,9 +449,12 @@ static void accounting_report_state(struct hostapd_data *hapd, int on) */ int accounting_init(struct hostapd_data *hapd) { + struct os_time now; + /* Acct-Session-Id should be unique over reboots. If reliable clock is * not available, this could be replaced with reboot counter, etc. */ - hapd->acct_session_id_hi = time(NULL); + os_get_time(&now); + hapd->acct_session_id_hi = now.sec; if (radius_client_register(hapd->radius, RADIUS_ACCT, accounting_receive, hapd)) diff --git a/src/ap/accounting.h b/src/ap/accounting.h index f3d60f0155a64..dcc54ee94b549 100644 --- a/src/ap/accounting.h +++ b/src/ap/accounting.h @@ -2,21 +2,19 @@ * hostapd / RADIUS Accounting * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef ACCOUNTING_H #define ACCOUNTING_H -void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta); #ifdef CONFIG_NO_ACCOUNTING +static inline void accounting_sta_get_id(struct hostapd_data *hapd, + struct sta_info *sta) +{ +} + static inline void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) { @@ -36,6 +34,7 @@ static inline void accounting_deinit(struct hostapd_data *hapd) { } #else /* CONFIG_NO_ACCOUNTING */ +void accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta); void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta); void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta); int accounting_init(struct hostapd_data *hapd); diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index 59969933e2f9e..25d26e5e77a70 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -1,15 +1,9 @@ /* * hostapd / Configuration helper functions - * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-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 - * published 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 "utils/includes.h" @@ -74,6 +68,8 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) bss->max_listen_interval = 65535; + bss->pwd_group = 19; /* ECC: GF(p=256) */ + #ifdef CONFIG_IEEE80211W bss->assoc_sa_query_max_timeout = 1000; bss->assoc_sa_query_retry_timeout = 201; @@ -84,23 +80,44 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) bss->pac_key_lifetime = 7 * 24 * 60 * 60; bss->pac_key_refresh_time = 1 * 24 * 60 * 60; #endif /* EAP_SERVER_FAST */ + + /* Set to -1 as defaults depends on HT in setup */ + bss->wmm_enabled = -1; + +#ifdef CONFIG_IEEE80211R + bss->ft_over_ds = 1; +#endif /* CONFIG_IEEE80211R */ + + bss->radius_das_time_window = 300; } struct hostapd_config * hostapd_config_defaults(void) { +#define ecw2cw(ecw) ((1 << (ecw)) - 1) + struct hostapd_config *conf; struct hostapd_bss_config *bss; - int i; const int aCWmin = 4, aCWmax = 10; const struct hostapd_wmm_ac_params ac_bk = { aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */ const struct hostapd_wmm_ac_params ac_be = { aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */ const struct hostapd_wmm_ac_params ac_vi = /* video traffic */ - { aCWmin - 1, aCWmin, 2, 3000 / 32, 1 }; + { aCWmin - 1, aCWmin, 2, 3000 / 32, 0 }; const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */ - { aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 1 }; + { aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 0 }; + const struct hostapd_tx_queue_params txq_bk = + { 7, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 }; + const struct hostapd_tx_queue_params txq_be = + { 3, ecw2cw(aCWmin), 4 * (ecw2cw(aCWmin) + 1) - 1, 0}; + const struct hostapd_tx_queue_params txq_vi = + { 1, (ecw2cw(aCWmin) + 1) / 2 - 1, ecw2cw(aCWmin), 30}; + const struct hostapd_tx_queue_params txq_vo = + { 1, (ecw2cw(aCWmin) + 1) / 4 - 1, + (ecw2cw(aCWmin) + 1) / 2 - 1, 15}; + +#undef ecw2cw conf = os_zalloc(sizeof(*conf)); bss = os_zalloc(sizeof(*bss)); @@ -129,16 +146,21 @@ struct hostapd_config * hostapd_config_defaults(void) conf->fragm_threshold = -1; /* user driver default: 2346 */ conf->send_probe_response = 1; - for (i = 0; i < NUM_TX_QUEUES; i++) - conf->tx_queue[i].aifs = -1; /* use hw default */ - conf->wmm_ac_params[0] = ac_be; conf->wmm_ac_params[1] = ac_bk; conf->wmm_ac_params[2] = ac_vi; conf->wmm_ac_params[3] = ac_vo; + conf->tx_queue[0] = txq_vo; + conf->tx_queue[1] = txq_vi; + conf->tx_queue[2] = txq_be; + conf->tx_queue[3] = txq_bk; + conf->ht_capab = HT_CAP_INFO_SMPS_DISABLED; + conf->ap_table_max_size = 255; + conf->ap_table_expiration_time = 60; + return conf; } @@ -319,6 +341,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); @@ -365,6 +411,7 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf) user = user->next; hostapd_config_free_eap_user(prev_user); } + os_free(conf->eap_user_sqlite); os_free(conf->dump_log_name); os_free(conf->eap_req_id_text); @@ -375,6 +422,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); @@ -389,6 +438,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; @@ -403,6 +453,8 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf) ssid->dyn_vlan_keys = NULL; } + os_free(conf->time_zone); + #ifdef CONFIG_IEEE80211R { struct ft_remote_r0kh *r0kh, *r0kh_prev; @@ -433,7 +485,6 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->model_name); os_free(conf->model_number); os_free(conf->serial_number); - os_free(conf->device_type); os_free(conf->config_methods); os_free(conf->ap_pin); os_free(conf->extra_cred); @@ -444,7 +495,30 @@ 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); + os_free(conf->nai_realm_data); + os_free(conf->network_auth_type); + os_free(conf->anqp_3gpp_cell_net); + os_free(conf->domain_name); + +#ifdef CONFIG_RADIUS_TEST + os_free(conf->dump_msk_file); +#endif /* CONFIG_RADIUS_TEST */ + +#ifdef CONFIG_HS20 + os_free(conf->hs20_oper_friendly_name); + os_free(conf->hs20_wan_metrics); + os_free(conf->hs20_connection_capability); + os_free(conf->hs20_operating_class); +#endif /* CONFIG_HS20 */ + + wpabuf_free(conf->vendor_elements); } @@ -549,57 +623,3 @@ const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, return NULL; } - - -const struct hostapd_eap_user * -hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity, - size_t identity_len, int phase2) -{ - struct hostapd_eap_user *user = conf->eap_user; - -#ifdef CONFIG_WPS - if (conf->wps_state && identity_len == WSC_ID_ENROLLEE_LEN && - os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) { - static struct hostapd_eap_user wsc_enrollee; - os_memset(&wsc_enrollee, 0, sizeof(wsc_enrollee)); - wsc_enrollee.methods[0].method = eap_server_get_type( - "WSC", &wsc_enrollee.methods[0].vendor); - return &wsc_enrollee; - } - - if (conf->wps_state && identity_len == WSC_ID_REGISTRAR_LEN && - os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) { - static struct hostapd_eap_user wsc_registrar; - os_memset(&wsc_registrar, 0, sizeof(wsc_registrar)); - wsc_registrar.methods[0].method = eap_server_get_type( - "WSC", &wsc_registrar.methods[0].vendor); - wsc_registrar.password = (u8 *) conf->ap_pin; - wsc_registrar.password_len = conf->ap_pin ? - os_strlen(conf->ap_pin) : 0; - return &wsc_registrar; - } -#endif /* CONFIG_WPS */ - - while (user) { - if (!phase2 && user->identity == NULL) { - /* Wildcard match */ - break; - } - - if (user->phase2 == !!phase2 && user->wildcard_prefix && - identity_len >= user->identity_len && - os_memcmp(user->identity, identity, user->identity_len) == - 0) { - /* Wildcard prefix match */ - break; - } - - if (user->phase2 == !!phase2 && - user->identity_len == identity_len && - os_memcmp(user->identity, identity, identity_len) == 0) - break; - user = user->next; - } - - return user; -} diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index f509b5bbb197e..a1d2b048b5131 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -1,15 +1,9 @@ /* * 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 program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published 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. */ #ifndef HOSTAPD_CONFIG_H @@ -18,6 +12,8 @@ #include "common/defs.h" #include "ip_addr.h" #include "common/wpa_common.h" +#include "common/ieee802_11_common.h" +#include "wps/wps.h" #define MAX_STA_COUNT 2007 #define MAX_VLAN_ID 4094 @@ -53,9 +49,10 @@ typedef enum hostap_security_policy { } secpolicy; struct hostapd_ssid { - char ssid[HOSTAPD_MAX_SSID_LEN + 1]; + u8 ssid[HOSTAPD_MAX_SSID_LEN]; size_t ssid_len; - int ssid_set; + unsigned int ssid_set:1; + unsigned int utf8_ssid:1; char vlan[IFNAMSIZ + 1]; secpolicy security_policy; @@ -70,6 +67,10 @@ struct hostapd_ssid { #define DYNAMIC_VLAN_OPTIONAL 1 #define DYNAMIC_VLAN_REQUIRED 2 int dynamic_vlan; +#define DYNAMIC_VLAN_NAMING_WITHOUT_DEVICE 0 +#define DYNAMIC_VLAN_NAMING_WITH_DEVICE 1 +#define DYNAMIC_VLAN_NAMING_END 2 + int vlan_naming; #ifdef CONFIG_FULL_DYNAMIC_VLAN char *vlan_tagged_interface; #endif /* CONFIG_FULL_DYNAMIC_VLAN */ @@ -96,6 +97,11 @@ struct hostapd_vlan { }; #define PMK_LEN 32 +struct hostapd_sta_wpa_psk_short { + struct hostapd_sta_wpa_psk_short *next; + u8 psk[PMK_LEN]; +}; + struct hostapd_wpa_psk { struct hostapd_wpa_psk *next; int group; @@ -103,7 +109,6 @@ struct hostapd_wpa_psk { u8 addr[ETH_ALEN]; }; -#define EAP_USER_MAX_METHODS 8 struct hostapd_eap_user { struct hostapd_eap_user *next; u8 *identity; @@ -111,7 +116,7 @@ struct hostapd_eap_user { struct { int vendor; u32 method; - } methods[EAP_USER_MAX_METHODS]; + } methods[EAP_MAX_METHODS]; u8 *password; size_t password_len; int phase2; @@ -122,25 +127,52 @@ 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 8 +#define NUM_TX_QUEUES 4 struct hostapd_tx_queue_params { int aifs; int cwmin; int cwmax; int burst; /* maximum burst time in 0.1 ms, i.e., 10 = 1 ms */ - int configured; }; -struct hostapd_wmm_ac_params { - int cwmin; - int cwmax; - int aifs; - int txop_limit; /* in units of 32us */ - int admission_control_mandatory; + +#define MAX_ROAMING_CONSORTIUM_LEN 15 + +struct hostapd_roaming_consortium { + u8 len; + u8 oi[MAX_ROAMING_CONSORTIUM_LEN]; +}; + +struct hostapd_lang_string { + u8 lang[3]; + u8 name_len; + u8 name[252]; }; +#define MAX_NAI_REALMS 10 +#define MAX_NAI_REALMLEN 255 +#define MAX_NAI_EAP_METHODS 5 +#define MAX_NAI_AUTH_TYPES 4 +struct hostapd_nai_realm_data { + u8 encoding; + char realm_buf[MAX_NAI_REALMLEN + 1]; + char *realm[MAX_NAI_REALMS]; + u8 eap_method_count; + struct hostapd_nai_realm_eap { + u8 eap_method; + u8 num_auths; + u8 auth_id[MAX_NAI_AUTH_TYPES]; + u8 auth_val[MAX_NAI_AUTH_TYPES]; + } eap_method[MAX_NAI_EAP_METHODS]; +}; /** * struct hostapd_bss_config - Per-BSS configuration @@ -148,6 +180,7 @@ struct hostapd_wmm_ac_params { struct hostapd_bss_config { char iface[IFNAMSIZ + 1]; char bridge[IFNAMSIZ + 1]; + char wds_bridge[IFNAMSIZ + 1]; enum hostapd_logger_level logger_syslog_level, logger_stdout_level; @@ -165,11 +198,21 @@ struct hostapd_bss_config { int eap_server; /* Use internal EAP server instead of external * RADIUS server */ struct hostapd_eap_user *eap_user; + char *eap_user_sqlite; char *eap_sim_db; struct hostapd_ip_addr own_ip_addr; char *nas_identifier; struct hostapd_radius_servers *radius; int acct_interim_interval; + int radius_request_cui; + struct hostapd_radius_attr *radius_auth_req_attr; + struct hostapd_radius_attr *radius_acct_req_attr; + int radius_das_port; + unsigned int radius_das_time_window; + int radius_das_require_event_timestamp; + struct hostapd_ip_addr radius_das_client_addr; + u8 *radius_das_shared_secret; + size_t radius_das_shared_secret_len; struct hostapd_ssid ssid; @@ -198,6 +241,7 @@ struct hostapd_bss_config { struct mac_acl_entry *deny_mac; int num_deny_mac; int wds_sta; + int isolate; int auth_algs; /* bitfield of allowed IEEE 802.11 authentication * algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */ @@ -211,6 +255,11 @@ struct hostapd_bss_config { /* dot11AssociationSAQueryRetryTimeout (in TUs) */ int assoc_sa_query_retry_timeout; #endif /* CONFIG_IEEE80211W */ + enum { + PSK_RADIUS_IGNORED = 0, + PSK_RADIUS_ACCEPTED = 1, + PSK_RADIUS_REQUIRED = 2 + } wpa_psk_radius; int wpa_pairwise; int wpa_group; int wpa_group_rekey; @@ -231,6 +280,7 @@ struct hostapd_bss_config { struct ft_remote_r0kh *r0kh_list; struct ft_remote_r1kh *r1kh_list; int pmk_r1_push; + int ft_over_ds; #endif /* CONFIG_IEEE80211R */ char *ctrl_interface; /* directory for UNIX domain sockets */ @@ -254,6 +304,8 @@ struct hostapd_bss_config { int pac_key_refresh_time; int eap_sim_aka_result_ind; int tnc; + int fragment_size; + u16 pwd_group; char *radius_server_clients; int radius_server_auth_port; @@ -283,6 +335,7 @@ struct hostapd_bss_config { */ u16 max_listen_interval; + int disable_pmksa_caching; int okc; /* Opportunistic Key Caching */ int wps_state; @@ -295,7 +348,7 @@ struct hostapd_bss_config { char *model_name; char *model_number; char *serial_number; - char *device_type; + u8 device_type[WPS_DEV_TYPE_LEN]; char *config_methods; u8 os_version[4]; char *ap_pin; @@ -311,7 +364,97 @@ struct hostapd_bss_config { char *model_description; 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; + +#define P2P_ENABLED BIT(0) +#define P2P_GROUP_OWNER BIT(1) +#define P2P_GROUP_FORMATION BIT(2) +#define P2P_MANAGE BIT(3) +#define P2P_ALLOW_CROSS_CONNECTION BIT(4) + int p2p; + + int disassoc_low_ack; + int skip_inactivity_poll; + +#define TDLS_PROHIBIT BIT(0) +#define TDLS_PROHIBIT_CHAN_SWITCH BIT(1) + int tdls; + int disable_11n; + int disable_11ac; + + /* IEEE 802.11v */ + int time_advertisement; + char *time_zone; + int wnm_sleep_mode; + int bss_transition; + + /* IEEE 802.11u - Interworking */ + int interworking; + int access_network_type; + int internet; + int asra; + int esr; + int uesa; + int venue_info_set; + u8 venue_group; + u8 venue_type; + u8 hessid[ETH_ALEN]; + + /* IEEE 802.11u - Roaming Consortium list */ + unsigned int roaming_consortium_count; + struct hostapd_roaming_consortium *roaming_consortium; + + /* IEEE 802.11u - Venue Name duples */ + unsigned int venue_name_count; + struct hostapd_lang_string *venue_name; + + /* IEEE 802.11u - Network Authentication Type */ + u8 *network_auth_type; + size_t network_auth_type_len; + + /* IEEE 802.11u - IP Address Type Availability */ + u8 ipaddr_type_availability; + u8 ipaddr_type_configured; + + /* IEEE 802.11u - 3GPP Cellular Network */ + u8 *anqp_3gpp_cell_net; + size_t anqp_3gpp_cell_net_len; + + /* IEEE 802.11u - Domain Name */ + u8 *domain_name; + size_t domain_name_len; + + unsigned int nai_realm_count; + struct hostapd_nai_realm_data *nai_realm_data; + + u16 gas_comeback_delay; + int gas_frag_limit; + +#ifdef CONFIG_HS20 + int hs20; + int disable_dgaf; + unsigned int hs20_oper_friendly_name_count; + struct hostapd_lang_string *hs20_oper_friendly_name; + u8 *hs20_wan_metrics; + u8 *hs20_connection_capability; + size_t hs20_connection_capability_len; + u8 *hs20_operating_class; + u8 hs20_operating_class_len; +#endif /* CONFIG_HS20 */ + + u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */ + +#ifdef CONFIG_RADIUS_TEST + char *dump_msk_file; +#endif /* CONFIG_RADIUS_TEST */ + + struct wpabuf *vendor_elements; }; @@ -332,12 +475,6 @@ struct hostapd_config { LONG_PREAMBLE = 0, SHORT_PREAMBLE = 1 } preamble; - enum { - CTS_PROTECTION_AUTOMATIC = 0, - CTS_PROTECTION_FORCE_ENABLED = 1, - CTS_PROTECTION_FORCE_DISABLED = 2, - CTS_PROTECTION_AUTOMATIC_NO_OLBC = 3, - } cts_protection_type; int *supported_rates; int *basic_rates; @@ -371,6 +508,13 @@ struct hostapd_config { u16 ht_capab; int ieee80211n; int secondary_channel; + int require_ht; + u32 vht_capab; + int ieee80211ac; + int require_vht; + u8 vht_oper_chwidth; + u8 vht_oper_centr_freq_seg0_idx; + u8 vht_oper_centr_freq_seg1_idx; }; @@ -389,8 +533,7 @@ const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf); const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id); -const struct hostapd_eap_user * -hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity, - size_t identity_len, int phase2); +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 f264a3e407ff7..02da25b71483d 100644 --- a/src/ap/ap_drv_ops.c +++ b/src/ap/ap_drv_ops.c @@ -2,14 +2,8 @@ * hostapd - Driver operations * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -17,14 +11,18 @@ #include "utils/common.h" #include "drivers/driver.h" #include "common/ieee802_11_defs.h" +#include "wps/wps.h" +#include "p2p/p2p.h" #include "hostapd.h" #include "ieee802_11.h" #include "sta_info.h" #include "ap_config.h" +#include "p2p_hostapd.h" +#include "hs20.h" #include "ap_drv_ops.h" -static int hostapd_sta_flags_to_drv(int flags) +u32 hostapd_sta_flags_to_drv(u32 flags) { int res = 0; if (flags & WLAN_STA_AUTHORIZED) @@ -39,45 +37,190 @@ static int hostapd_sta_flags_to_drv(int flags) } -static int hostapd_set_ap_wps_ie(struct hostapd_data *hapd) +int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, + struct wpabuf **beacon_ret, + struct wpabuf **proberesp_ret, + struct wpabuf **assocresp_ret) { - struct wpabuf *beacon, *proberesp; - int ret; + struct wpabuf *beacon = NULL, *proberesp = NULL, *assocresp = NULL; + u8 buf[200], *pos; - if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL) - return 0; + *beacon_ret = *proberesp_ret = *assocresp_ret = NULL; - beacon = hapd->wps_beacon_ie; - proberesp = hapd->wps_probe_resp_ie; + pos = buf; + pos = hostapd_eid_time_adv(hapd, pos); + if (pos != buf) { + if (wpabuf_resize(&beacon, pos - buf) != 0) + goto fail; + wpabuf_put_data(beacon, buf, pos - buf); + } + pos = hostapd_eid_time_zone(hapd, pos); + if (pos != buf) { + if (wpabuf_resize(&proberesp, pos - buf) != 0) + goto fail; + wpabuf_put_data(proberesp, buf, pos - buf); + } - ret = hapd->driver->set_ap_wps_ie(hapd->drv_priv, beacon, proberesp); + pos = buf; + pos = hostapd_eid_ext_capab(hapd, pos); + if (pos != buf) { + if (wpabuf_resize(&assocresp, pos - buf) != 0) + goto fail; + wpabuf_put_data(assocresp, buf, pos - buf); + } + pos = hostapd_eid_interworking(hapd, pos); + pos = hostapd_eid_adv_proto(hapd, pos); + pos = hostapd_eid_roaming_consortium(hapd, pos); + if (pos != buf) { + if (wpabuf_resize(&beacon, pos - buf) != 0) + goto fail; + wpabuf_put_data(beacon, buf, pos - buf); + + if (wpabuf_resize(&proberesp, pos - buf) != 0) + goto fail; + wpabuf_put_data(proberesp, buf, pos - buf); + } - return ret; + if (hapd->wps_beacon_ie) { + if (wpabuf_resize(&beacon, wpabuf_len(hapd->wps_beacon_ie)) < + 0) + goto fail; + wpabuf_put_buf(beacon, hapd->wps_beacon_ie); + } + + if (hapd->wps_probe_resp_ie) { + if (wpabuf_resize(&proberesp, + wpabuf_len(hapd->wps_probe_resp_ie)) < 0) + goto fail; + wpabuf_put_buf(proberesp, hapd->wps_probe_resp_ie); + } + +#ifdef CONFIG_P2P + if (hapd->p2p_beacon_ie) { + if (wpabuf_resize(&beacon, wpabuf_len(hapd->p2p_beacon_ie)) < + 0) + goto fail; + wpabuf_put_buf(beacon, hapd->p2p_beacon_ie); + } + + if (hapd->p2p_probe_resp_ie) { + if (wpabuf_resize(&proberesp, + wpabuf_len(hapd->p2p_probe_resp_ie)) < 0) + goto fail; + wpabuf_put_buf(proberesp, hapd->p2p_probe_resp_ie); + } +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_P2P_MANAGER + if (hapd->conf->p2p & P2P_MANAGE) { + if (wpabuf_resize(&beacon, 100) == 0) { + u8 *start, *p; + start = wpabuf_put(beacon, 0); + p = hostapd_eid_p2p_manage(hapd, start); + wpabuf_put(beacon, p - start); + } + + if (wpabuf_resize(&proberesp, 100) == 0) { + u8 *start, *p; + start = wpabuf_put(proberesp, 0); + p = hostapd_eid_p2p_manage(hapd, start); + wpabuf_put(proberesp, p - start); + } + } +#endif /* CONFIG_P2P_MANAGER */ + +#ifdef CONFIG_WPS2 + if (hapd->conf->wps_state) { + struct wpabuf *a = wps_build_assoc_resp_ie(); + if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0) + wpabuf_put_buf(assocresp, a); + wpabuf_free(a); + } +#endif /* CONFIG_WPS2 */ + +#ifdef CONFIG_P2P_MANAGER + if (hapd->conf->p2p & P2P_MANAGE) { + if (wpabuf_resize(&assocresp, 100) == 0) { + u8 *start, *p; + start = wpabuf_put(assocresp, 0); + p = hostapd_eid_p2p_manage(hapd, start); + wpabuf_put(assocresp, p - start); + } + } +#endif /* CONFIG_P2P_MANAGER */ + +#ifdef CONFIG_WIFI_DISPLAY + if (hapd->p2p_group) { + struct wpabuf *a; + a = p2p_group_assoc_resp_ie(hapd->p2p_group, P2P_SC_SUCCESS); + if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0) + wpabuf_put_buf(assocresp, a); + wpabuf_free(a); + } +#endif /* CONFIG_WIFI_DISPLAY */ + +#ifdef CONFIG_HS20 + pos = buf; + pos = hostapd_eid_hs20_indication(hapd, pos); + if (pos != buf) { + if (wpabuf_resize(&beacon, pos - buf) != 0) + goto fail; + wpabuf_put_data(beacon, buf, pos - buf); + + if (wpabuf_resize(&proberesp, pos - buf) != 0) + goto fail; + wpabuf_put_data(proberesp, buf, pos - buf); + } +#endif /* CONFIG_HS20 */ + + *beacon_ret = beacon; + *proberesp_ret = proberesp; + *assocresp_ret = assocresp; + + return 0; + +fail: + wpabuf_free(beacon); + wpabuf_free(proberesp); + wpabuf_free(assocresp); + return -1; } -static int hostapd_send_mgmt_frame(struct hostapd_data *hapd, const void *msg, - size_t len) +void hostapd_free_ap_extra_ies(struct hostapd_data *hapd, + struct wpabuf *beacon, + struct wpabuf *proberesp, + struct wpabuf *assocresp) { - if (hapd->driver == NULL || hapd->driver->send_mlme == NULL) - return 0; - return hapd->driver->send_mlme(hapd->drv_priv, msg, len); + wpabuf_free(beacon); + wpabuf_free(proberesp); + wpabuf_free(assocresp); } -static int hostapd_send_eapol(struct hostapd_data *hapd, const u8 *addr, - const u8 *data, size_t data_len, int encrypt) +int hostapd_set_ap_wps_ie(struct hostapd_data *hapd) { - if (hapd->driver == NULL || hapd->driver->hapd_send_eapol == NULL) + struct wpabuf *beacon, *proberesp, *assocresp; + int ret; + + if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL) return 0; - return hapd->driver->hapd_send_eapol(hapd->drv_priv, addr, data, - data_len, encrypt, - hapd->own_addr); + + if (hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp) < + 0) + return -1; + + ret = hapd->driver->set_ap_wps_ie(hapd->drv_priv, beacon, proberesp, + assocresp); + + hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp); + + return ret; } -static int hostapd_set_authorized(struct hostapd_data *hapd, - struct sta_info *sta, int authorized) +int hostapd_set_authorized(struct hostapd_data *hapd, + struct sta_info *sta, int authorized) { if (authorized) { return hostapd_sta_set_flags(hapd, sta->addr, @@ -92,39 +235,7 @@ static int hostapd_set_authorized(struct hostapd_data *hapd, } -static int hostapd_set_key(const char *ifname, struct hostapd_data *hapd, - enum wpa_alg alg, const u8 *addr, int key_idx, - int set_tx, const u8 *seq, size_t seq_len, - const u8 *key, size_t key_len) -{ - if (hapd->driver == NULL || hapd->driver->set_key == NULL) - return 0; - return hapd->driver->set_key(ifname, hapd->drv_priv, alg, addr, - key_idx, set_tx, seq, seq_len, key, - key_len); -} - - -static int hostapd_read_sta_data(struct hostapd_data *hapd, - struct hostap_sta_driver_data *data, - const u8 *addr) -{ - if (hapd->driver == NULL || hapd->driver->read_sta_data == NULL) - return -1; - return hapd->driver->read_sta_data(hapd->drv_priv, data, addr); -} - - -static int hostapd_sta_clear_stats(struct hostapd_data *hapd, const u8 *addr) -{ - if (hapd->driver == NULL || hapd->driver->sta_clear_stats == NULL) - return 0; - return hapd->driver->sta_clear_stats(hapd->drv_priv, addr); -} - - -static int hostapd_set_sta_flags(struct hostapd_data *hapd, - struct sta_info *sta) +int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta) { int set_flags, total_flags, flags_and, flags_or; total_flags = hostapd_sta_flags_to_drv(sta->flags); @@ -140,8 +251,8 @@ static int hostapd_set_sta_flags(struct hostapd_data *hapd, } -static int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, - const char *ifname, int enabled) +int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname, + int enabled) { struct wpa_bss_params params; os_memset(¶ms, 0, sizeof(params)); @@ -154,166 +265,80 @@ static int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, params.wpa_pairwise = hapd->conf->wpa_pairwise; params.wpa_key_mgmt = hapd->conf->wpa_key_mgmt; params.rsn_preauth = hapd->conf->rsn_preauth; +#ifdef CONFIG_IEEE80211W + params.ieee80211w = hapd->conf->ieee80211w; +#endif /* CONFIG_IEEE80211W */ } return hostapd_set_ieee8021x(hapd, ¶ms); } -static int hostapd_set_radius_acl_auth(struct hostapd_data *hapd, - const u8 *mac, int accepted, - u32 session_timeout) -{ - if (hapd->driver == NULL || hapd->driver->set_radius_acl_auth == NULL) - return 0; - return hapd->driver->set_radius_acl_auth(hapd->drv_priv, mac, accepted, - session_timeout); -} - - -static int hostapd_set_radius_acl_expire(struct hostapd_data *hapd, - const u8 *mac) -{ - if (hapd->driver == NULL || - hapd->driver->set_radius_acl_expire == NULL) - return 0; - return hapd->driver->set_radius_acl_expire(hapd->drv_priv, mac); -} - - -static int hostapd_set_bss_params(struct hostapd_data *hapd, - int use_protection) -{ - int ret = 0; - int preamble; -#ifdef CONFIG_IEEE80211N - u8 buf[60], *ht_capab, *ht_oper, *pos; - - pos = buf; - ht_capab = pos; - pos = hostapd_eid_ht_capabilities(hapd, pos); - ht_oper = pos; - pos = hostapd_eid_ht_operation(hapd, pos); - if (pos > ht_oper && ht_oper > ht_capab && - hostapd_set_ht_params(hapd, ht_capab + 2, ht_capab[1], - ht_oper + 2, ht_oper[1])) { - wpa_printf(MSG_ERROR, "Could not set HT capabilities " - "for kernel driver"); - ret = -1; - } - -#endif /* CONFIG_IEEE80211N */ - - if (hostapd_set_cts_protect(hapd, use_protection)) { - wpa_printf(MSG_ERROR, "Failed to set CTS protect in kernel " - "driver"); - ret = -1; - } - - if (hapd->iface->current_mode && - hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && - hostapd_set_short_slot_time(hapd, - hapd->iface->num_sta_no_short_slot_time - > 0 ? 0 : 1)) { - wpa_printf(MSG_ERROR, "Failed to set Short Slot Time option " - "in kernel driver"); - ret = -1; - } - - if (hapd->iface->num_sta_no_short_preamble == 0 && - hapd->iconf->preamble == SHORT_PREAMBLE) - preamble = SHORT_PREAMBLE; - else - preamble = LONG_PREAMBLE; - if (hostapd_set_preamble(hapd, preamble)) { - wpa_printf(MSG_ERROR, "Could not set preamble for kernel " - "driver"); - ret = -1; - } - - return ret; -} - - -static int hostapd_set_beacon(struct hostapd_data *hapd, - const u8 *head, size_t head_len, - const u8 *tail, size_t tail_len, int dtim_period, - int beacon_int) -{ - if (hapd->driver == NULL || hapd->driver->set_beacon == NULL) - return 0; - return hapd->driver->set_beacon(hapd->drv_priv, - head, head_len, tail, tail_len, - dtim_period, beacon_int); -} - - -static int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname) +int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname) { char force_ifname[IFNAMSIZ]; u8 if_addr[ETH_ALEN]; - return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, NULL, NULL, NULL, - force_ifname, if_addr); + return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, hapd->own_addr, + NULL, NULL, force_ifname, if_addr, NULL); } -static int hostapd_vlan_if_remove(struct hostapd_data *hapd, - const char *ifname) + +int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname) { return hostapd_if_remove(hapd, WPA_IF_AP_VLAN, ifname); } -static int hostapd_set_wds_sta(struct hostapd_data *hapd, const u8 *addr, - int aid, int val) +int hostapd_set_wds_sta(struct hostapd_data *hapd, const u8 *addr, int aid, + int val) { - if (hapd->driver == NULL || hapd->driver->set_wds_sta == NULL) - return 0; - return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val); -} + const char *bridge = NULL; - -static int hostapd_set_sta_vlan(const char *ifname, struct hostapd_data *hapd, - const u8 *addr, int vlan_id) -{ - if (hapd->driver == NULL || hapd->driver->set_sta_vlan == NULL) + if (hapd->driver == NULL || hapd->driver->set_wds_sta == NULL) return 0; - return hapd->driver->set_sta_vlan(hapd->drv_priv, addr, ifname, - vlan_id); + if (hapd->conf->wds_bridge[0]) + bridge = hapd->conf->wds_bridge; + else if (hapd->conf->bridge[0]) + bridge = hapd->conf->bridge; + return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val, + bridge); } -static int hostapd_get_inact_sec(struct hostapd_data *hapd, const u8 *addr) +int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr, + u16 auth_alg) { - if (hapd->driver == NULL || hapd->driver->get_inact_sec == NULL) + if (hapd->driver == NULL || hapd->driver->add_sta_node == NULL) return 0; - return hapd->driver->get_inact_sec(hapd->drv_priv, addr); + return hapd->driver->add_sta_node(hapd->drv_priv, addr, auth_alg); } -static int hostapd_sta_deauth(struct hostapd_data *hapd, const u8 *addr, - int reason) +int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr, + u16 seq, u16 status, const u8 *ie, size_t len) { - if (hapd->driver == NULL || hapd->driver->sta_deauth == NULL) + if (hapd->driver == NULL || hapd->driver->sta_auth == NULL) return 0; - return hapd->driver->sta_deauth(hapd->drv_priv, hapd->own_addr, addr, - reason); + return hapd->driver->sta_auth(hapd->drv_priv, hapd->own_addr, addr, + seq, status, ie, len); } -static int hostapd_sta_disassoc(struct hostapd_data *hapd, const u8 *addr, - int reason) +int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr, + int reassoc, u16 status, const u8 *ie, size_t len) { - if (hapd->driver == NULL || hapd->driver->sta_disassoc == NULL) + if (hapd->driver == NULL || hapd->driver->sta_assoc == NULL) return 0; - return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr, - reason); + return hapd->driver->sta_assoc(hapd->drv_priv, hapd->own_addr, addr, + reassoc, status, ie, len); } -static int hostapd_sta_add(struct hostapd_data *hapd, - const u8 *addr, u16 aid, u16 capability, - const u8 *supp_rates, size_t supp_rates_len, - u16 listen_interval, - const struct ieee80211_ht_capabilities *ht_capab) +int hostapd_sta_add(struct hostapd_data *hapd, + const u8 *addr, u16 aid, u16 capability, + const u8 *supp_rates, size_t supp_rates_len, + u16 listen_interval, + const struct ieee80211_ht_capabilities *ht_capab, + u32 flags, u8 qosinfo) { struct hostapd_sta_add_params params; @@ -330,52 +355,19 @@ static int hostapd_sta_add(struct hostapd_data *hapd, params.supp_rates_len = supp_rates_len; params.listen_interval = listen_interval; params.ht_capabilities = ht_capab; + params.flags = hostapd_sta_flags_to_drv(flags); + params.qosinfo = qosinfo; return hapd->driver->sta_add(hapd->drv_priv, ¶ms); } -static int hostapd_sta_remove(struct hostapd_data *hapd, const u8 *addr) +int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr, + u8 *tspec_ie, size_t tspec_ielen) { - if (hapd->driver == NULL || hapd->driver->sta_remove == NULL) + if (hapd->driver == NULL || hapd->driver->add_tspec == NULL) return 0; - return hapd->driver->sta_remove(hapd->drv_priv, addr); -} - - -static int hostapd_set_countermeasures(struct hostapd_data *hapd, int enabled) -{ - if (hapd->driver == NULL || - hapd->driver->hapd_set_countermeasures == NULL) - return 0; - return hapd->driver->hapd_set_countermeasures(hapd->drv_priv, enabled); -} - - -void hostapd_set_driver_ops(struct hostapd_driver_ops *ops) -{ - ops->set_ap_wps_ie = hostapd_set_ap_wps_ie; - ops->send_mgmt_frame = hostapd_send_mgmt_frame; - ops->send_eapol = hostapd_send_eapol; - ops->set_authorized = hostapd_set_authorized; - ops->set_key = hostapd_set_key; - ops->read_sta_data = hostapd_read_sta_data; - ops->sta_clear_stats = hostapd_sta_clear_stats; - ops->set_sta_flags = hostapd_set_sta_flags; - ops->set_drv_ieee8021x = hostapd_set_drv_ieee8021x; - ops->set_radius_acl_auth = hostapd_set_radius_acl_auth; - ops->set_radius_acl_expire = hostapd_set_radius_acl_expire; - ops->set_bss_params = hostapd_set_bss_params; - ops->set_beacon = hostapd_set_beacon; - ops->vlan_if_add = hostapd_vlan_if_add; - ops->vlan_if_remove = hostapd_vlan_if_remove; - ops->set_wds_sta = hostapd_set_wds_sta; - ops->set_sta_vlan = hostapd_set_sta_vlan; - ops->get_inact_sec = hostapd_get_inact_sec; - ops->sta_deauth = hostapd_sta_deauth; - ops->sta_disassoc = hostapd_sta_disassoc; - ops->sta_add = hostapd_sta_add; - ops->sta_remove = hostapd_sta_remove; - ops->set_countermeasures = hostapd_set_countermeasures; + return hapd->driver->add_tspec(hapd->drv_priv, addr, tspec_ie, + tspec_ielen); } @@ -414,12 +406,14 @@ int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len) int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type, const char *ifname, const u8 *addr, void *bss_ctx, - void **drv_priv, char *force_ifname, u8 *if_addr) + void **drv_priv, char *force_ifname, u8 *if_addr, + const char *bridge) { if (hapd->driver == NULL || hapd->driver->if_add == NULL) return -1; return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr, - bss_ctx, drv_priv, force_ifname, if_addr); + bss_ctx, drv_priv, force_ifname, if_addr, + bridge); } @@ -502,16 +496,6 @@ int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr, } -int hostapd_set_rate_sets(struct hostapd_data *hapd, int *supp_rates, - int *basic_rates, int mode) -{ - if (hapd->driver == NULL || hapd->driver->set_rate_sets == NULL) - return 0; - return hapd->driver->set_rate_sets(hapd->drv_priv, supp_rates, - basic_rates, mode); -} - - int hostapd_set_country(struct hostapd_data *hapd, const char *country) { if (hapd->driver == NULL || @@ -521,30 +505,6 @@ int hostapd_set_country(struct hostapd_data *hapd, const char *country) } -int hostapd_set_cts_protect(struct hostapd_data *hapd, int value) -{ - if (hapd->driver == NULL || hapd->driver->set_cts_protect == NULL) - return 0; - return hapd->driver->set_cts_protect(hapd->drv_priv, value); -} - - -int hostapd_set_preamble(struct hostapd_data *hapd, int value) -{ - if (hapd->driver == NULL || hapd->driver->set_preamble == NULL) - return 0; - return hapd->driver->set_preamble(hapd->drv_priv, value); -} - - -int hostapd_set_short_slot_time(struct hostapd_data *hapd, int value) -{ - if (hapd->driver == NULL || hapd->driver->set_short_slot_time == NULL) - return 0; - return hapd->driver->set_short_slot_time(hapd->drv_priv, value); -} - - int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs, int cw_min, int cw_max, int burst_time) { @@ -555,15 +515,6 @@ int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs, } -int hostapd_valid_bss_mask(struct hostapd_data *hapd, const u8 *addr, - const u8 *mask) -{ - if (hapd->driver == NULL || hapd->driver->valid_bss_mask == NULL) - return 1; - return hapd->driver->valid_bss_mask(hapd->drv_priv, addr, mask); -} - - struct hostapd_hw_modes * hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes, u16 *flags) @@ -584,19 +535,6 @@ int hostapd_driver_commit(struct hostapd_data *hapd) } -int hostapd_set_ht_params(struct hostapd_data *hapd, - const u8 *ht_capab, size_t ht_capab_len, - const u8 *ht_oper, size_t ht_oper_len) -{ - if (hapd->driver == NULL || hapd->driver->set_ht_params == NULL || - ht_capab == NULL || ht_oper == NULL) - return 0; - return hapd->driver->set_ht_params(hapd->drv_priv, - ht_capab, ht_capab_len, - ht_oper, ht_oper_len); -} - - int hostapd_drv_none(struct hostapd_data *hapd) { return hapd->driver && os_strcmp(hapd->driver->name, "none") == 0; @@ -619,3 +557,78 @@ struct wpa_scan_results * hostapd_driver_get_scan_results( return hapd->driver->get_scan_results2(hapd->drv_priv); return NULL; } + + +int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start, + int duration) +{ + if (hapd->driver && hapd->driver->set_noa) + return hapd->driver->set_noa(hapd->drv_priv, count, start, + duration); + return -1; +} + + +int hostapd_drv_set_key(const char *ifname, struct hostapd_data *hapd, + enum wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + if (hapd->driver == NULL || hapd->driver->set_key == NULL) + return 0; + return hapd->driver->set_key(ifname, hapd->drv_priv, alg, addr, + key_idx, set_tx, seq, seq_len, key, + key_len); +} + + +int hostapd_drv_send_mlme(struct hostapd_data *hapd, + const void *msg, size_t len, int noack) +{ + if (hapd->driver == NULL || hapd->driver->send_mlme == NULL) + return 0; + return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack); +} + + +int hostapd_drv_sta_deauth(struct hostapd_data *hapd, + const u8 *addr, int reason) +{ + if (hapd->driver == NULL || hapd->driver->sta_deauth == NULL) + return 0; + return hapd->driver->sta_deauth(hapd->drv_priv, hapd->own_addr, addr, + reason); +} + + +int hostapd_drv_sta_disassoc(struct hostapd_data *hapd, + const u8 *addr, int reason) +{ + if (hapd->driver == NULL || hapd->driver->sta_disassoc == NULL) + return 0; + return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr, + reason); +} + + +int hostapd_drv_wnm_oper(struct hostapd_data *hapd, enum wnm_oper oper, + const u8 *peer, u8 *buf, u16 *buf_len) +{ + if (hapd->driver == NULL || hapd->driver->wnm_oper == NULL) + return 0; + return hapd->driver->wnm_oper(hapd->drv_priv, oper, peer, buf, + buf_len); +} + + +int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq, + unsigned int wait, const u8 *dst, const u8 *data, + size_t len) +{ + if (hapd->driver == NULL || hapd->driver->send_action == NULL) + return 0; + return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst, + hapd->own_addr, hapd->own_addr, data, + len, 0); +} diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h index 9b75d096a0092..9c53b99d82a26 100644 --- a/src/ap/ap_drv_ops.h +++ b/src/ap/ap_drv_ops.h @@ -2,14 +2,8 @@ * hostapd - Driver operations * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef AP_DRV_OPS @@ -18,8 +12,32 @@ enum wpa_driver_if_type; struct wpa_bss_params; struct wpa_driver_scan_params; +struct ieee80211_ht_capabilities; -void hostapd_set_driver_ops(struct hostapd_driver_ops *ops); +u32 hostapd_sta_flags_to_drv(u32 flags); +int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, + struct wpabuf **beacon, + struct wpabuf **proberesp, + struct wpabuf **assocresp); +void hostapd_free_ap_extra_ies(struct hostapd_data *hapd, struct wpabuf *beacon, + struct wpabuf *proberesp, + struct wpabuf *assocresp); +int hostapd_set_ap_wps_ie(struct hostapd_data *hapd); +int hostapd_set_authorized(struct hostapd_data *hapd, + struct sta_info *sta, int authorized); +int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta); +int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname, + int enabled); +int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname); +int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname); +int hostapd_set_wds_sta(struct hostapd_data *hapd, const u8 *addr, int aid, + int val); +int hostapd_sta_add(struct hostapd_data *hapd, + const u8 *addr, u16 aid, u16 capability, + const u8 *supp_rates, size_t supp_rates_len, + u16 listen_interval, + const struct ieee80211_ht_capabilities *ht_capab, + u32 flags, u8 qosinfo); int hostapd_set_privacy(struct hostapd_data *hapd, int enabled); int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem, size_t elem_len); @@ -27,7 +45,8 @@ int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len); int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len); int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type, const char *ifname, const u8 *addr, void *bss_ctx, - void **drv_priv, char *force_ifname, u8 *if_addr); + void **drv_priv, char *force_ifname, u8 *if_addr, + const char *bridge); int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type, const char *ifname); int hostapd_set_ieee8021x(struct hostapd_data *hapd, @@ -41,27 +60,157 @@ int hostapd_set_rts(struct hostapd_data *hapd, int rts); int hostapd_set_frag(struct hostapd_data *hapd, int frag); int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr, int total_flags, int flags_or, int flags_and); -int hostapd_set_rate_sets(struct hostapd_data *hapd, int *supp_rates, - int *basic_rates, int mode); int hostapd_set_country(struct hostapd_data *hapd, const char *country); -int hostapd_set_cts_protect(struct hostapd_data *hapd, int value); -int hostapd_set_preamble(struct hostapd_data *hapd, int value); -int hostapd_set_short_slot_time(struct hostapd_data *hapd, int value); int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs, int cw_min, int cw_max, int burst_time); -int hostapd_valid_bss_mask(struct hostapd_data *hapd, const u8 *addr, - const u8 *mask); struct hostapd_hw_modes * hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes, u16 *flags); int hostapd_driver_commit(struct hostapd_data *hapd); -int hostapd_set_ht_params(struct hostapd_data *hapd, - const u8 *ht_capab, size_t ht_capab_len, - const u8 *ht_oper, size_t ht_oper_len); int hostapd_drv_none(struct hostapd_data *hapd); int hostapd_driver_scan(struct hostapd_data *hapd, struct wpa_driver_scan_params *params); struct wpa_scan_results * hostapd_driver_get_scan_results( struct hostapd_data *hapd); +int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start, + int duration); +int hostapd_drv_set_key(const char *ifname, + struct hostapd_data *hapd, + enum wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len); +int hostapd_drv_send_mlme(struct hostapd_data *hapd, + const void *msg, size_t len, int noack); +int hostapd_drv_sta_deauth(struct hostapd_data *hapd, + const u8 *addr, int reason); +int hostapd_drv_sta_disassoc(struct hostapd_data *hapd, + const u8 *addr, int reason); +int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq, + unsigned int wait, const u8 *dst, const u8 *data, + size_t len); +int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr, + u16 auth_alg); +int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr, + u16 seq, u16 status, const u8 *ie, size_t len); +int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr, + int reassoc, u16 status, const u8 *ie, size_t len); +int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr, + u8 *tspec_ie, size_t tspec_ielen); + + +#include "drivers/driver.h" + +int hostapd_drv_wnm_oper(struct hostapd_data *hapd, + enum wnm_oper oper, const u8 *peer, + u8 *buf, u16 *buf_len); + +static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd, + int enabled) +{ + if (hapd->driver == NULL || + hapd->driver->hapd_set_countermeasures == NULL) + return 0; + return hapd->driver->hapd_set_countermeasures(hapd->drv_priv, enabled); +} + +static inline int hostapd_drv_set_sta_vlan(const char *ifname, + struct hostapd_data *hapd, + const u8 *addr, int vlan_id) +{ + if (hapd->driver == NULL || hapd->driver->set_sta_vlan == NULL) + return 0; + return hapd->driver->set_sta_vlan(hapd->drv_priv, addr, ifname, + vlan_id); +} + +static inline int hostapd_drv_get_inact_sec(struct hostapd_data *hapd, + const u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->get_inact_sec == NULL) + return 0; + return hapd->driver->get_inact_sec(hapd->drv_priv, addr); +} + +static inline int hostapd_drv_sta_remove(struct hostapd_data *hapd, + const u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->sta_remove == NULL) + return 0; + return hapd->driver->sta_remove(hapd->drv_priv, addr); +} + +static inline int hostapd_drv_hapd_send_eapol(struct hostapd_data *hapd, + const u8 *addr, const u8 *data, + size_t data_len, int encrypt, + u32 flags) +{ + if (hapd->driver == NULL || hapd->driver->hapd_send_eapol == NULL) + return 0; + return hapd->driver->hapd_send_eapol(hapd->drv_priv, addr, data, + data_len, encrypt, + hapd->own_addr, flags); +} + +static inline int hostapd_drv_read_sta_data( + struct hostapd_data *hapd, struct hostap_sta_driver_data *data, + const u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->read_sta_data == NULL) + return -1; + return hapd->driver->read_sta_data(hapd->drv_priv, data, addr); +} + +static inline int hostapd_drv_sta_clear_stats(struct hostapd_data *hapd, + const u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->sta_clear_stats == NULL) + return 0; + return hapd->driver->sta_clear_stats(hapd->drv_priv, addr); +} + +static inline int hostapd_drv_set_ap(struct hostapd_data *hapd, + struct wpa_driver_ap_params *params) +{ + if (hapd->driver == NULL || hapd->driver->set_ap == NULL) + return 0; + return hapd->driver->set_ap(hapd->drv_priv, params); +} + +static inline int hostapd_drv_set_radius_acl_auth(struct hostapd_data *hapd, + const u8 *mac, int accepted, + u32 session_timeout) +{ + if (hapd->driver == NULL || hapd->driver->set_radius_acl_auth == NULL) + return 0; + return hapd->driver->set_radius_acl_auth(hapd->drv_priv, mac, accepted, + session_timeout); +} + +static inline int hostapd_drv_set_radius_acl_expire(struct hostapd_data *hapd, + const u8 *mac) +{ + if (hapd->driver == NULL || + hapd->driver->set_radius_acl_expire == NULL) + return 0; + return hapd->driver->set_radius_acl_expire(hapd->drv_priv, mac); +} + +static inline int hostapd_drv_set_authmode(struct hostapd_data *hapd, + int auth_algs) +{ + if (hapd->driver == NULL || hapd->driver->set_authmode == NULL) + return 0; + return hapd->driver->set_authmode(hapd->drv_priv, auth_algs); +} + +static inline void hostapd_drv_poll_client(struct hostapd_data *hapd, + const u8 *own_addr, const u8 *addr, + int qos) +{ + if (hapd->driver == NULL || hapd->driver->poll_client == NULL) + return; + hapd->driver->poll_client(hapd->drv_priv, own_addr, addr, qos); +} #endif /* AP_DRV_OPS */ diff --git a/src/ap/ap_list.c b/src/ap/ap_list.c index 5297dbf3bf5f3..18090ca18c0b9 100644 --- a/src/ap/ap_list.c +++ b/src/ap/ap_list.c @@ -4,14 +4,8 @@ * Copyright (c) 2003-2004, Instant802 Networks, Inc. * Copyright (c) 2006, Devicescape Software, Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -227,6 +221,7 @@ void ap_list_process_beacon(struct hostapd_iface *iface, struct hostapd_frame_info *fi) { struct ap_info *ap; + struct os_time now; int new_ap = 0; size_t len; int set_beacon = 0; @@ -256,23 +251,9 @@ void ap_list_process_beacon(struct hostapd_iface *iface, ap->ssid_len = len; } - os_memset(ap->supported_rates, 0, WLAN_SUPP_RATES_MAX); - len = 0; - if (elems->supp_rates) { - len = elems->supp_rates_len; - if (len > WLAN_SUPP_RATES_MAX) - len = WLAN_SUPP_RATES_MAX; - os_memcpy(ap->supported_rates, elems->supp_rates, len); - } - if (elems->ext_supp_rates) { - int len2; - if (len + elems->ext_supp_rates_len > WLAN_SUPP_RATES_MAX) - len2 = WLAN_SUPP_RATES_MAX - len; - else - len2 = elems->ext_supp_rates_len; - os_memcpy(ap->supported_rates + len, elems->ext_supp_rates, - len2); - } + merge_byte_arrays(ap->supported_rates, WLAN_SUPP_RATES_MAX, + elems->supp_rates, elems->supp_rates_len, + elems->ext_supp_rates, elems->ext_supp_rates_len); ap->wpa = elems->wpa_ie != NULL; @@ -292,11 +273,10 @@ void ap_list_process_beacon(struct hostapd_iface *iface, ap->ht_support = 0; ap->num_beacons++; - time(&ap->last_beacon); - if (fi) { - ap->ssi_signal = fi->ssi_signal; + os_get_time(&now); + ap->last_beacon = now.sec; + 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 @@ -324,14 +304,14 @@ 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); } static void ap_list_timer(void *eloop_ctx, void *timeout_ctx) { struct hostapd_iface *iface = eloop_ctx; - time_t now; + struct os_time now; struct ap_info *ap; int set_beacon = 0; @@ -340,12 +320,12 @@ static void ap_list_timer(void *eloop_ctx, void *timeout_ctx) if (!iface->ap_list) return; - time(&now); + os_get_time(&now); while (iface->ap_list) { ap = iface->ap_list->prev; if (ap->last_beacon + iface->conf->ap_table_expiration_time >= - now) + now.sec) break; ap_free_ap(iface, ap); @@ -379,7 +359,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 f49f58b54e435..f0b41259bda6e 100644 --- a/src/ap/ap_list.h +++ b/src/ap/ap_list.h @@ -4,14 +4,8 @@ * Copyright (c) 2003-2004, Instant802 Networks, Inc. * Copyright (c) 2006, Devicescape Software, Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef AP_LIST_H @@ -40,12 +34,11 @@ struct ap_info { int channel; int datarate; /* in 100 kbps */ - int ssi_signal; int ht_support; unsigned int num_beacons; /* number of beacon frames received */ - time_t last_beacon; + os_time_t last_beacon; int already_seen; /* whether API call AP-NEW has already fetched * information about this AP */ diff --git a/src/ap/ap_mlme.c b/src/ap/ap_mlme.c index 2b09b11e79c17..a9596947fafb2 100644 --- a/src/ap/ap_mlme.c +++ b/src/ap/ap_mlme.c @@ -4,14 +4,8 @@ * Copyright 2003-2004, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" diff --git a/src/ap/ap_mlme.h b/src/ap/ap_mlme.h index c77a9390a8b63..e7fd69d61cf63 100644 --- a/src/ap/ap_mlme.h +++ b/src/ap/ap_mlme.h @@ -4,14 +4,8 @@ * Copyright 2003-2004, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef MLME_H diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c index 0ab0668acc56b..d66d97e4a0db4 100644 --- a/src/ap/authsrv.c +++ b/src/ap/authsrv.c @@ -2,14 +2,8 @@ * Authentication server setup * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -60,7 +54,7 @@ static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity, struct eap_user *user) { const struct hostapd_eap_user *eap_user; - int i, count; + int i; eap_user = hostapd_get_eap_user(ctx, identity, identity_len, phase2); if (eap_user == NULL) @@ -70,10 +64,7 @@ static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity, return 0; os_memset(user, 0, sizeof(*user)); - count = EAP_USER_MAX_METHODS; - if (count > EAP_MAX_METHODS) - count = EAP_MAX_METHODS; - for (i = 0; i < count; i++) { + for (i = 0; i < EAP_MAX_METHODS; i++) { user->methods[i].vendor = eap_user->methods[i].vendor; user->methods[i].method = eap_user->methods[i].method; } @@ -101,7 +92,7 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd) os_memset(&srv, 0, sizeof(srv)); srv.client_file = conf->radius_server_clients; srv.auth_port = conf->radius_server_auth_port; - srv.conf_ctx = conf; + srv.conf_ctx = hapd; srv.eap_sim_db_priv = hapd->eap_sim_db_priv; srv.ssl_ctx = hapd->ssl_ctx; srv.msg_ctx = hapd->msg_ctx; @@ -119,6 +110,10 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd) srv.get_eap_user = hostapd_radius_get_eap_user; srv.eap_req_id_text = conf->eap_req_id_text; srv.eap_req_id_text_len = conf->eap_req_id_text_len; + srv.pwd_group = conf->pwd_group; +#ifdef CONFIG_RADIUS_TEST + srv.dump_msk_file = conf->dump_msk_file; +#endif /* CONFIG_RADIUS_TEST */ hapd->radius_srv = radius_server_init(&srv); if (hapd->radius_srv == NULL) { diff --git a/src/ap/authsrv.h b/src/ap/authsrv.h index be3051ebfa2ec..2f4ed3419c2d7 100644 --- a/src/ap/authsrv.h +++ b/src/ap/authsrv.h @@ -2,14 +2,8 @@ * Authentication server setup * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef AUTHSRV_H diff --git a/src/ap/beacon.c b/src/ap/beacon.c index 004cc8a5f336e..4c47c75841c51 100644 --- a/src/ap/beacon.c +++ b/src/ap/beacon.c @@ -2,7 +2,7 @@ * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response * Copyright (c) 2002-2004, Instant802 Networks, Inc. * Copyright (c) 2005-2006, Devicescape Software, Inc. - * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi> + * 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 @@ -22,15 +22,22 @@ #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "drivers/driver.h" +#include "wps/wps_defs.h" +#include "p2p/p2p.h" #include "hostapd.h" #include "ieee802_11.h" #include "wpa_auth.h" #include "wmm.h" #include "ap_config.h" #include "sta_info.h" +#include "p2p_hostapd.h" +#include "ap_drv_ops.h" #include "beacon.h" +#include "hs20.h" +#ifdef NEED_AP_MLME + static u8 ieee802_11_erp_info(struct hostapd_data *hapd) { u8 erp = 0; @@ -39,23 +46,11 @@ static u8 ieee802_11_erp_info(struct hostapd_data *hapd) hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G) return 0; - switch (hapd->iconf->cts_protection_type) { - case CTS_PROTECTION_FORCE_ENABLED: - erp |= ERP_INFO_NON_ERP_PRESENT | ERP_INFO_USE_PROTECTION; - break; - case CTS_PROTECTION_FORCE_DISABLED: - erp = 0; - break; - case CTS_PROTECTION_AUTOMATIC: - if (hapd->iface->olbc) - erp |= ERP_INFO_USE_PROTECTION; - /* continue */ - case CTS_PROTECTION_AUTOMATIC_NO_OLBC: - if (hapd->iface->num_sta_non_erp > 0) { - erp |= ERP_INFO_NON_ERP_PRESENT | - ERP_INFO_USE_PROTECTION; - } - break; + if (hapd->iface->olbc) + erp |= ERP_INFO_USE_PROTECTION; + if (hapd->iface->num_sta_non_erp > 0) { + erp |= ERP_INFO_NON_ERP_PRESENT | + ERP_INFO_USE_PROTECTION; } if (hapd->iface->num_sta_no_short_preamble > 0 || hapd->iconf->preamble == LONG_PREAMBLE) @@ -178,8 +173,7 @@ static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid, } -static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len, - struct sta_info *sta) +static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len) { const u8 *ie; size_t ielen; @@ -193,92 +187,37 @@ static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len, } -void handle_probe_req(struct hostapd_data *hapd, - const struct ieee80211_mgmt *mgmt, size_t len) +static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, + struct sta_info *sta, + const struct ieee80211_mgmt *req, + int is_p2p, size_t *resp_len) { struct ieee80211_mgmt *resp; - struct ieee802_11_elems elems; - char *ssid; u8 *pos, *epos; - const u8 *ie; - size_t ssid_len, ie_len; - struct sta_info *sta = NULL; size_t buflen; - size_t i; - - ie = mgmt->u.probe_req.variable; - ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); - - for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) - if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx, - mgmt->sa, ie, ie_len) > 0) - return; - - if (!hapd->iconf->send_probe_response) - return; - - if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) { - wpa_printf(MSG_DEBUG, "Could not parse ProbeReq from " MACSTR, - MAC2STR(mgmt->sa)); - return; - } - - ssid = NULL; - ssid_len = 0; - - if ((!elems.ssid || !elems.supp_rates)) { - wpa_printf(MSG_DEBUG, "STA " MACSTR " sent probe request " - "without SSID or supported rates element", - MAC2STR(mgmt->sa)); - return; - } - if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0) { - wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for " - "broadcast SSID ignored", MAC2STR(mgmt->sa)); - return; - } - - sta = ap_get_sta(hapd, mgmt->sa); - - if (elems.ssid_len == 0 || - (elems.ssid_len == hapd->conf->ssid.ssid_len && - os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) == - 0)) { - ssid = hapd->conf->ssid.ssid; - ssid_len = hapd->conf->ssid.ssid_len; - if (sta) - sta->ssid_probe = &hapd->conf->ssid; - } - - if (!ssid) { - if (!(mgmt->da[0] & 0x01)) { - char ssid_txt[33]; - ieee802_11_print_ssid(ssid_txt, elems.ssid, - elems.ssid_len); - wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR - " for foreign SSID '%s'", - MAC2STR(mgmt->sa), ssid_txt); - } - return; - } - - /* TODO: verify that supp_rates contains at least one matching rate - * with AP configuration */ #define MAX_PROBERESP_LEN 768 buflen = MAX_PROBERESP_LEN; #ifdef CONFIG_WPS if (hapd->wps_probe_resp_ie) buflen += wpabuf_len(hapd->wps_probe_resp_ie); #endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P + if (hapd->p2p_probe_resp_ie) + buflen += wpabuf_len(hapd->p2p_probe_resp_ie); +#endif /* CONFIG_P2P */ + if (hapd->conf->vendor_elements) + buflen += wpabuf_len(hapd->conf->vendor_elements); resp = os_zalloc(buflen); if (resp == NULL) - return; + return NULL; + epos = ((u8 *) resp) + MAX_PROBERESP_LEN; resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_PROBE_RESP); - os_memcpy(resp->da, mgmt->sa, ETH_ALEN); + if (req) + os_memcpy(resp->da, req->sa, ETH_ALEN); os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN); os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN); @@ -291,9 +230,9 @@ void handle_probe_req(struct hostapd_data *hapd, pos = resp->u.probe_resp.variable; *pos++ = WLAN_EID_SSID; - *pos++ = ssid_len; - os_memcpy(pos, ssid, ssid_len); - pos += ssid_len; + *pos++ = hapd->conf->ssid.ssid_len; + os_memcpy(pos, hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len); + pos += hapd->conf->ssid.ssid_len; /* Supported rates */ pos = hostapd_eid_supp_rates(hapd, pos); @@ -310,13 +249,27 @@ void handle_probe_req(struct hostapd_data *hapd, pos = hostapd_eid_ext_supp_rates(hapd, pos); /* RSN, MDIE, WPA */ - pos = hostapd_eid_wpa(hapd, pos, epos - pos, sta); + pos = hostapd_eid_wpa(hapd, pos, epos - pos); #ifdef CONFIG_IEEE80211N pos = hostapd_eid_ht_capabilities(hapd, pos); pos = hostapd_eid_ht_operation(hapd, pos); #endif /* CONFIG_IEEE80211N */ + pos = hostapd_eid_ext_capab(hapd, pos); + + pos = hostapd_eid_time_adv(hapd, pos); + pos = hostapd_eid_time_zone(hapd, pos); + + pos = hostapd_eid_interworking(hapd, pos); + 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); @@ -328,23 +281,291 @@ void handle_probe_req(struct hostapd_data *hapd, } #endif /* CONFIG_WPS */ - if (hapd->drv.send_mgmt_frame(hapd, resp, pos - (u8 *) resp) < 0) +#ifdef CONFIG_P2P + if ((hapd->conf->p2p & P2P_ENABLED) && is_p2p && + hapd->p2p_probe_resp_ie) { + os_memcpy(pos, wpabuf_head(hapd->p2p_probe_resp_ie), + wpabuf_len(hapd->p2p_probe_resp_ie)); + pos += wpabuf_len(hapd->p2p_probe_resp_ie); + } +#endif /* CONFIG_P2P */ +#ifdef CONFIG_P2P_MANAGER + if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) == + P2P_MANAGE) + pos = hostapd_eid_p2p_manage(hapd, pos); +#endif /* CONFIG_P2P_MANAGER */ + +#ifdef CONFIG_HS20 + pos = hostapd_eid_hs20_indication(hapd, pos); +#endif /* CONFIG_HS20 */ + + if (hapd->conf->vendor_elements) { + os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements), + wpabuf_len(hapd->conf->vendor_elements)); + pos += wpabuf_len(hapd->conf->vendor_elements); + } + + *resp_len = pos - (u8 *) resp; + return (u8 *) resp; +} + + +enum ssid_match_result { + NO_SSID_MATCH, + EXACT_SSID_MATCH, + WILDCARD_SSID_MATCH +}; + +static enum ssid_match_result ssid_match(struct hostapd_data *hapd, + const u8 *ssid, size_t ssid_len, + const u8 *ssid_list, + size_t ssid_list_len) +{ + const u8 *pos, *end; + int wildcard = 0; + + if (ssid_len == 0) + wildcard = 1; + if (ssid_len == hapd->conf->ssid.ssid_len && + os_memcmp(ssid, hapd->conf->ssid.ssid, ssid_len) == 0) + return EXACT_SSID_MATCH; + + if (ssid_list == NULL) + return wildcard ? WILDCARD_SSID_MATCH : NO_SSID_MATCH; + + pos = ssid_list; + end = ssid_list + ssid_list_len; + while (pos + 1 <= end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[1] == 0) + wildcard = 1; + if (pos[1] == hapd->conf->ssid.ssid_len && + os_memcmp(pos + 2, hapd->conf->ssid.ssid, pos[1]) == 0) + return EXACT_SSID_MATCH; + pos += 2 + pos[1]; + } + + return wildcard ? WILDCARD_SSID_MATCH : NO_SSID_MATCH; +} + + +void handle_probe_req(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len, + int ssi_signal) +{ + u8 *resp; + struct ieee802_11_elems elems; + const u8 *ie; + size_t ie_len; + struct sta_info *sta = NULL; + size_t i, resp_len; + int noack; + enum ssid_match_result res; + + ie = mgmt->u.probe_req.variable; + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)) + return; + ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); + + for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) + if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx, + mgmt->sa, mgmt->da, mgmt->bssid, + ie, ie_len, ssi_signal) > 0) + return; + + if (!hapd->iconf->send_probe_response) + return; + + if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) { + wpa_printf(MSG_DEBUG, "Could not parse ProbeReq from " MACSTR, + MAC2STR(mgmt->sa)); + return; + } + + if ((!elems.ssid || !elems.supp_rates)) { + wpa_printf(MSG_DEBUG, "STA " MACSTR " sent probe request " + "without SSID or supported rates element", + MAC2STR(mgmt->sa)); + return; + } + +#ifdef CONFIG_P2P + if (hapd->p2p && elems.wps_ie) { + struct wpabuf *wps; + wps = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA); + if (wps && !p2p_group_match_dev_type(hapd->p2p_group, wps)) { + wpa_printf(MSG_MSGDUMP, "P2P: Ignore Probe Request " + "due to mismatch with Requested Device " + "Type"); + wpabuf_free(wps); + return; + } + wpabuf_free(wps); + } + + if (hapd->p2p && elems.p2p) { + struct wpabuf *p2p; + p2p = ieee802_11_vendor_ie_concat(ie, ie_len, P2P_IE_VENDOR_TYPE); + if (p2p && !p2p_group_match_dev_id(hapd->p2p_group, p2p)) { + wpa_printf(MSG_MSGDUMP, "P2P: Ignore Probe Request " + "due to mismatch with Device ID"); + wpabuf_free(p2p); + return; + } + wpabuf_free(p2p); + } +#endif /* CONFIG_P2P */ + + if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0 && + elems.ssid_list_len == 0) { + wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for " + "broadcast SSID ignored", MAC2STR(mgmt->sa)); + return; + } + + sta = ap_get_sta(hapd, mgmt->sa); + +#ifdef CONFIG_P2P + if ((hapd->conf->p2p & P2P_GROUP_OWNER) && + elems.ssid_len == P2P_WILDCARD_SSID_LEN && + os_memcmp(elems.ssid, P2P_WILDCARD_SSID, + P2P_WILDCARD_SSID_LEN) == 0) { + /* Process P2P Wildcard SSID like Wildcard SSID */ + elems.ssid_len = 0; + } +#endif /* CONFIG_P2P */ + + res = ssid_match(hapd, elems.ssid, elems.ssid_len, + elems.ssid_list, elems.ssid_list_len); + if (res != NO_SSID_MATCH) { + if (sta) + sta->ssid_probe = &hapd->conf->ssid; + } else { + if (!(mgmt->da[0] & 0x01)) { + char ssid_txt[33]; + ieee802_11_print_ssid(ssid_txt, elems.ssid, + elems.ssid_len); + wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR + " for foreign SSID '%s' (DA " MACSTR ")%s", + MAC2STR(mgmt->sa), ssid_txt, + MAC2STR(mgmt->da), + elems.ssid_list ? " (SSID list)" : ""); + } + return; + } + +#ifdef CONFIG_INTERWORKING + if (elems.interworking && elems.interworking_len >= 1) { + u8 ant = elems.interworking[0] & 0x0f; + if (ant != INTERWORKING_ANT_WILDCARD && + ant != hapd->conf->access_network_type) { + wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR + " for mismatching ANT %u ignored", + MAC2STR(mgmt->sa), ant); + return; + } + } + + if (elems.interworking && + (elems.interworking_len == 7 || elems.interworking_len == 9)) { + const u8 *hessid; + if (elems.interworking_len == 7) + hessid = elems.interworking + 1; + else + hessid = elems.interworking + 1 + 2; + if (!is_broadcast_ether_addr(hessid) && + os_memcmp(hessid, hapd->conf->hessid, ETH_ALEN) != 0) { + wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR + " for mismatching HESSID " MACSTR + " ignored", + MAC2STR(mgmt->sa), MAC2STR(hessid)); + return; + } + } +#endif /* CONFIG_INTERWORKING */ + + /* TODO: verify that supp_rates contains at least one matching rate + * with AP configuration */ + + resp = hostapd_gen_probe_resp(hapd, sta, mgmt, elems.p2p != NULL, + &resp_len); + if (resp == NULL) + return; + + /* + * If this is a broadcast probe request, apply no ack policy to avoid + * excessive retries. + */ + noack = !!(res == WILDCARD_SSID_MATCH && + is_broadcast_ether_addr(mgmt->da)); + + if (hostapd_drv_send_mlme(hapd, resp, resp_len, noack) < 0) perror("handle_probe_req: send"); os_free(resp); - wpa_printf(MSG_MSGDUMP, "STA " MACSTR " sent probe request for %s " + wpa_printf(MSG_EXCESSIVE, "STA " MACSTR " sent probe request for %s " "SSID", MAC2STR(mgmt->sa), elems.ssid_len == 0 ? "broadcast" : "our"); } +static u8 * hostapd_probe_resp_offloads(struct hostapd_data *hapd, + size_t *resp_len) +{ + /* check probe response offloading caps and print warnings */ + if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD)) + return NULL; + +#ifdef CONFIG_WPS + if (hapd->conf->wps_state && hapd->wps_probe_resp_ie && + (!(hapd->iface->probe_resp_offloads & + (WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS | + WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2)))) + wpa_printf(MSG_WARNING, "Device is trying to offload WPS " + "Probe Response while not supporting this"); +#endif /* CONFIG_WPS */ + +#ifdef CONFIG_P2P + if ((hapd->conf->p2p & P2P_ENABLED) && hapd->p2p_probe_resp_ie && + !(hapd->iface->probe_resp_offloads & + WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P)) + wpa_printf(MSG_WARNING, "Device is trying to offload P2P " + "Probe Response while not supporting this"); +#endif /* CONFIG_P2P */ + + if (hapd->conf->interworking && + !(hapd->iface->probe_resp_offloads & + WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING)) + wpa_printf(MSG_WARNING, "Device is trying to offload " + "Interworking Probe Response while not supporting " + "this"); + + /* Generate a Probe Response template for the non-P2P case */ + return hostapd_gen_probe_resp(hapd, NULL, NULL, 0, resp_len); +} + +#endif /* NEED_AP_MLME */ + + void ieee802_11_set_beacon(struct hostapd_data *hapd) { - struct ieee80211_mgmt *head; - u8 *pos, *tail, *tailpos; + struct ieee80211_mgmt *head = NULL; + u8 *tail = NULL; + size_t head_len = 0, tail_len = 0; + u8 *resp = NULL; + size_t resp_len = 0; + struct wpa_driver_ap_params params; + struct wpabuf *beacon, *proberesp, *assocresp; +#ifdef NEED_AP_MLME u16 capab_info; - size_t head_len, tail_len; + u8 *pos, *tailpos; +#endif /* NEED_AP_MLME */ + + hapd->beacon_set_done = 1; + +#ifdef NEED_AP_MLME #define BEACON_HEAD_BUF_SIZE 256 #define BEACON_TAIL_BUF_SIZE 512 @@ -354,6 +575,12 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd) if (hapd->conf->wps_state && hapd->wps_beacon_ie) tail_len += wpabuf_len(hapd->wps_beacon_ie); #endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P + if (hapd->p2p_beacon_ie) + tail_len += wpabuf_len(hapd->p2p_beacon_ie); +#endif /* CONFIG_P2P */ + if (hapd->conf->vendor_elements) + tail_len += wpabuf_len(hapd->conf->vendor_elements); tailpos = tail = os_malloc(tail_len); if (head == NULL || tail == NULL) { wpa_printf(MSG_ERROR, "Failed to set beacon data"); @@ -412,13 +639,30 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd) /* RSN, MDIE, WPA */ tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE - - tailpos, NULL); + tailpos); #ifdef CONFIG_IEEE80211N tailpos = hostapd_eid_ht_capabilities(hapd, tailpos); tailpos = hostapd_eid_ht_operation(hapd, tailpos); #endif /* CONFIG_IEEE80211N */ + tailpos = hostapd_eid_ext_capab(hapd, tailpos); + + /* + * TODO: Time Advertisement element should only be included in some + * DTIM Beacon frames. + */ + tailpos = hostapd_eid_time_adv(hapd, tailpos); + + tailpos = hostapd_eid_interworking(hapd, tailpos); + 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); @@ -430,19 +674,104 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd) } #endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P + if ((hapd->conf->p2p & P2P_ENABLED) && hapd->p2p_beacon_ie) { + os_memcpy(tailpos, wpabuf_head(hapd->p2p_beacon_ie), + wpabuf_len(hapd->p2p_beacon_ie)); + tailpos += wpabuf_len(hapd->p2p_beacon_ie); + } +#endif /* CONFIG_P2P */ +#ifdef CONFIG_P2P_MANAGER + if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) == + P2P_MANAGE) + tailpos = hostapd_eid_p2p_manage(hapd, tailpos); +#endif /* CONFIG_P2P_MANAGER */ + +#ifdef CONFIG_HS20 + tailpos = hostapd_eid_hs20_indication(hapd, tailpos); +#endif /* CONFIG_HS20 */ + + if (hapd->conf->vendor_elements) { + os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements), + wpabuf_len(hapd->conf->vendor_elements)); + tailpos += wpabuf_len(hapd->conf->vendor_elements); + } + tail_len = tailpos > tail ? tailpos - tail : 0; - if (hapd->drv.set_beacon(hapd, (u8 *) head, head_len, - tail, tail_len, hapd->conf->dtim_period, - hapd->iconf->beacon_int)) - wpa_printf(MSG_ERROR, "Failed to set beacon head/tail or DTIM " - "period"); + resp = hostapd_probe_resp_offloads(hapd, &resp_len); +#endif /* NEED_AP_MLME */ + + os_memset(¶ms, 0, sizeof(params)); + params.head = (u8 *) head; + params.head_len = head_len; + params.tail = tail; + params.tail_len = tail_len; + params.proberesp = resp; + params.proberesp_len = resp_len; + params.dtim_period = hapd->conf->dtim_period; + params.beacon_int = hapd->iconf->beacon_int; + params.basic_rates = hapd->iface->basic_rates; + params.ssid = hapd->conf->ssid.ssid; + params.ssid_len = hapd->conf->ssid.ssid_len; + params.pairwise_ciphers = hapd->conf->rsn_pairwise ? + hapd->conf->rsn_pairwise : hapd->conf->wpa_pairwise; + params.group_cipher = hapd->conf->wpa_group; + params.key_mgmt_suites = hapd->conf->wpa_key_mgmt; + params.auth_algs = hapd->conf->auth_algs; + params.wpa_version = hapd->conf->wpa; + params.privacy = hapd->conf->ssid.wep.keys_set || hapd->conf->wpa || + (hapd->conf->ieee802_1x && + (hapd->conf->default_wep_key_len || + hapd->conf->individual_wep_key_len)); + switch (hapd->conf->ignore_broadcast_ssid) { + case 0: + params.hide_ssid = NO_SSID_HIDING; + break; + case 1: + params.hide_ssid = HIDDEN_SSID_ZERO_LEN; + break; + case 2: + params.hide_ssid = HIDDEN_SSID_ZERO_CONTENTS; + break; + } + hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp); + params.beacon_ies = beacon; + params.proberesp_ies = proberesp; + params.assocresp_ies = assocresp; + params.isolate = hapd->conf->isolate; +#ifdef NEED_AP_MLME + params.cts_protect = !!(ieee802_11_erp_info(hapd) & + ERP_INFO_USE_PROTECTION); + params.preamble = hapd->iface->num_sta_no_short_preamble == 0 && + hapd->iconf->preamble == SHORT_PREAMBLE; + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) + params.short_slot_time = + hapd->iface->num_sta_no_short_slot_time > 0 ? 0 : 1; + else + params.short_slot_time = -1; + if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n) + params.ht_opmode = -1; + else + params.ht_opmode = hapd->iface->ht_op_mode; +#endif /* NEED_AP_MLME */ + params.interworking = hapd->conf->interworking; + if (hapd->conf->interworking && + !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; +#ifdef CONFIG_HS20 + params.disable_dgaf = hapd->conf->disable_dgaf; +#endif /* CONFIG_HS20 */ + 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); os_free(tail); os_free(head); - - hapd->drv.set_bss_params(hapd, !!(ieee802_11_erp_info(hapd) & - ERP_INFO_USE_PROTECTION)); + os_free(resp); } @@ -453,4 +782,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 c1510e1942540..37f10d2f587a5 100644 --- a/src/ap/beacon.h +++ b/src/ap/beacon.h @@ -19,18 +19,10 @@ struct ieee80211_mgmt; void handle_probe_req(struct hostapd_data *hapd, - const struct ieee80211_mgmt *mgmt, size_t len); -#ifdef NEED_AP_MLME + 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); -#else /* NEED_AP_MLME */ -static inline void ieee802_11_set_beacon(struct hostapd_data *hapd) -{ -} - -static inline void ieee802_11_set_beacons(struct hostapd_iface *iface) -{ -} -#endif /* NEED_AP_MLME */ +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 e50b0a70ca95d..c55d3fe32a616 100644 --- a/src/ap/ctrl_iface_ap.c +++ b/src/ap/ctrl_iface_ap.c @@ -2,26 +2,45 @@ * Control interface for shared AP commands * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" #include "utils/common.h" +#include "common/ieee802_11_defs.h" #include "hostapd.h" #include "ieee802_1x.h" #include "wpa_auth.h" #include "ieee802_11.h" #include "sta_info.h" #include "wps_hostapd.h" +#include "p2p_hostapd.h" #include "ctrl_iface_ap.h" +#include "ap_drv_ops.h" + + +static int hostapd_get_sta_conn_time(struct sta_info *sta, + char *buf, size_t buflen) +{ + struct os_time now, age; + int len = 0, ret; + + if (!sta->connected_time.sec) + return 0; + + os_get_time(&now); + os_time_sub(&now, &sta->connected_time, &age); + + ret = os_snprintf(buf + len, buflen - len, "connected_time=%u\n", + (unsigned int) age.sec); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + return len; +} static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, @@ -57,6 +76,13 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, buflen - len); if (res >= 0) len += res; + res = hostapd_p2p_get_mib_sta(hapd, sta, buf + len, buflen - len); + if (res >= 0) + len += res; + + res = hostapd_get_sta_conn_time(sta, buf + len, buflen - len); + if (res >= 0) + len += res; return len; } @@ -102,3 +128,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 8690beaad4557..e83f894144c55 100644 --- a/src/ap/ctrl_iface_ap.h +++ b/src/ap/ctrl_iface_ap.h @@ -2,14 +2,8 @@ * Control interface for shared AP commands * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef CTRL_IFACE_AP_H @@ -21,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 26ef5845abfd3..8980bec03f8b1 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -2,14 +2,8 @@ * hostapd / Callback functions for driver wrappers * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -19,26 +13,37 @@ #include "drivers/driver.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" -#include "common/wpa_ctrl.h" +#include "crypto/random.h" +#include "p2p/p2p.h" +#include "wps/wps.h" +#include "wnm_ap.h" #include "hostapd.h" #include "ieee802_11.h" #include "sta_info.h" #include "accounting.h" #include "tkip_countermeasures.h" -#include "iapp.h" #include "ieee802_1x.h" #include "wpa_auth.h" -#include "wmm.h" #include "wps_hostapd.h" +#include "ap_drv_ops.h" #include "ap_config.h" +#include "hw_features.h" int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, - const u8 *ie, size_t ielen) + const u8 *req_ies, size_t req_ies_len, int reassoc) { struct sta_info *sta; int new_assoc, res; struct ieee802_11_elems elems; + const u8 *ie; + size_t ielen; +#ifdef CONFIG_IEEE80211R + u8 buf[sizeof(struct ieee80211_mgmt) + 1024]; + u8 *p = buf; +#endif /* CONFIG_IEEE80211R */ + u16 reason = WLAN_REASON_UNSPECIFIED; + u16 status = WLAN_STATUS_SUCCESS; if (addr == NULL) { /* @@ -52,11 +57,12 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, "no address"); return -1; } + random_add_randomness(addr, ETH_ALEN); hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "associated"); - ieee802_11_parse_elems(ie, ielen, &elems, 0); + ieee802_11_parse_elems(req_ies, req_ies_len, &elems, 0); if (elems.wps_ie) { ie = elems.wps_ie - 2; ielen = elems.wps_ie_len + 2; @@ -79,15 +85,42 @@ 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); + +#ifdef CONFIG_P2P + if (elems.p2p) { + wpabuf_free(sta->p2p_ie); + sta->p2p_ie = ieee802_11_vendor_ie_concat(req_ies, req_ies_len, + P2P_IE_VENDOR_TYPE); } - sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS); +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_HS20 + wpabuf_free(sta->hs20_ie); + if (elems.hs20 && elems.hs20_len > 4) { + sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4, + elems.hs20_len - 4); + } else + sta->hs20_ie = NULL; +#endif /* CONFIG_HS20 */ if (hapd->conf->wpa) { if (ie == NULL || ielen == 0) { +#ifdef CONFIG_WPS if (hapd->conf->wps_state) { wpa_printf(MSG_DEBUG, "STA did not include " "WPA/RSN IE in (Re)Association " @@ -95,15 +128,29 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, sta->flags |= WLAN_STA_MAYBE_WPS; goto skip_wpa_check; } +#endif /* CONFIG_WPS */ wpa_printf(MSG_DEBUG, "No WPA/RSN IE from STA"); return -1; } +#ifdef CONFIG_WPS if (hapd->conf->wps_state && ie[0] == 0xdd && ie[1] >= 4 && os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) { + struct wpabuf *wps; sta->flags |= WLAN_STA_WPS; + wps = ieee802_11_vendor_ie_concat(ie, ielen, + WPS_IE_VENDOR_TYPE); + if (wps) { + if (wps_is_20(wps)) { + wpa_printf(MSG_DEBUG, "WPS: STA " + "supports WPS 2.0"); + sta->flags |= WLAN_STA_WPS2; + } + wpabuf_free(wps); + } goto skip_wpa_check; } +#endif /* CONFIG_WPS */ if (sta->wpa_sm == NULL) sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, @@ -114,48 +161,156 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, return -1; } res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, - ie, ielen, NULL, 0); + ie, ielen, + elems.mdie, elems.mdie_len); if (res != WPA_IE_OK) { - int resp; wpa_printf(MSG_DEBUG, "WPA/RSN information element " "rejected? (res %u)", res); wpa_hexdump(MSG_DEBUG, "IE", ie, ielen); - if (res == WPA_INVALID_GROUP) - resp = WLAN_REASON_GROUP_CIPHER_NOT_VALID; - else if (res == WPA_INVALID_PAIRWISE) - resp = WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID; - else if (res == WPA_INVALID_AKMP) - resp = WLAN_REASON_AKMP_NOT_VALID; + if (res == WPA_INVALID_GROUP) { + reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID; + status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID; + } else if (res == WPA_INVALID_PAIRWISE) { + reason = WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID; + status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; + } else if (res == WPA_INVALID_AKMP) { + reason = WLAN_REASON_AKMP_NOT_VALID; + status = WLAN_STATUS_AKMP_NOT_VALID; + } #ifdef CONFIG_IEEE80211W - else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) - resp = WLAN_REASON_INVALID_IE; - else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) - resp = WLAN_REASON_GROUP_CIPHER_NOT_VALID; + else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) { + reason = WLAN_REASON_INVALID_IE; + status = WLAN_STATUS_INVALID_IE; + } else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) { + reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID; + status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID; + } #endif /* CONFIG_IEEE80211W */ - else - resp = WLAN_REASON_INVALID_IE; - hapd->drv.sta_disassoc(hapd, sta->addr, resp); - ap_free_sta(hapd, sta); - return -1; + else { + reason = WLAN_REASON_INVALID_IE; + status = WLAN_STATUS_INVALID_IE; + } + goto fail; + } +#ifdef CONFIG_IEEE80211W + if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && + sta->sa_query_count > 0) + ap_check_sa_query_timeout(hapd, sta); + if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && + (sta->auth_alg != WLAN_AUTH_FT)) { + /* + * STA has already been associated with MFP and SA + * Query timeout has not been reached. Reject the + * association attempt temporarily and start SA Query, + * if one is not pending. + */ + + if (sta->sa_query_count == 0) + ap_sta_start_sa_query(hapd, sta); + +#ifdef CONFIG_IEEE80211R + status = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY; + + p = hostapd_eid_assoc_comeback_time(hapd, sta, p); + + hostapd_sta_assoc(hapd, addr, reassoc, status, buf, + p - buf); +#endif /* CONFIG_IEEE80211R */ + return 0; } + + if (wpa_auth_uses_mfp(sta->wpa_sm)) + sta->flags |= WLAN_STA_MFP; + else + sta->flags &= ~WLAN_STA_MFP; +#endif /* CONFIG_IEEE80211W */ + +#ifdef CONFIG_IEEE80211R + if (sta->auth_alg == WLAN_AUTH_FT) { + status = wpa_ft_validate_reassoc(sta->wpa_sm, req_ies, + req_ies_len); + if (status != WLAN_STATUS_SUCCESS) { + if (status == WLAN_STATUS_INVALID_PMKID) + reason = WLAN_REASON_INVALID_IE; + if (status == WLAN_STATUS_INVALID_MDIE) + reason = WLAN_REASON_INVALID_IE; + if (status == WLAN_STATUS_INVALID_FTIE) + reason = WLAN_REASON_INVALID_IE; + goto fail; + } + } +#endif /* CONFIG_IEEE80211R */ } else if (hapd->conf->wps_state) { - if (ie && ielen > 4 && ie[0] == 0xdd && ie[1] >= 4 && - os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) { +#ifdef CONFIG_WPS + struct wpabuf *wps; + if (req_ies) + wps = ieee802_11_vendor_ie_concat(req_ies, req_ies_len, + WPS_IE_VENDOR_TYPE); + else + wps = NULL; +#ifdef CONFIG_WPS_STRICT + if (wps && wps_validate_assoc_req(wps) < 0) { + reason = WLAN_REASON_INVALID_IE; + status = WLAN_STATUS_INVALID_IE; + wpabuf_free(wps); + goto fail; + } +#endif /* CONFIG_WPS_STRICT */ + if (wps) { sta->flags |= WLAN_STA_WPS; + if (wps_is_20(wps)) { + wpa_printf(MSG_DEBUG, "WPS: STA supports " + "WPS 2.0"); + sta->flags |= WLAN_STA_WPS2; + } } else sta->flags |= WLAN_STA_MAYBE_WPS; + wpabuf_free(wps); +#endif /* CONFIG_WPS */ } +#ifdef CONFIG_WPS skip_wpa_check: +#endif /* CONFIG_WPS */ + +#ifdef CONFIG_IEEE80211R + p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, buf, sizeof(buf), + sta->auth_alg, req_ies, req_ies_len); + + hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf); +#else /* CONFIG_IEEE80211R */ + /* Keep compiler silent about unused variables */ + if (status) { + } +#endif /* CONFIG_IEEE80211R */ new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0; sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; - wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); + + if (reassoc && (sta->auth_alg == WLAN_AUTH_FT)) + wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT); + else + wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); hostapd_new_assoc_sta(hapd, sta, !new_assoc); ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); +#ifdef CONFIG_P2P + if (req_ies) { + p2p_group_notif_assoc(hapd->p2p_group, sta->addr, + req_ies, req_ies_len); + } +#endif /* CONFIG_P2P */ + return 0; + +fail: +#ifdef CONFIG_IEEE80211R + hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf); +#endif /* CONFIG_IEEE80211R */ + hostapd_drv_sta_disassoc(hapd, sta->addr, reason); + ap_free_sta(hapd, sta); + return -1; } @@ -163,6 +318,19 @@ void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr) { struct sta_info *sta; + if (addr == NULL) { + /* + * This could potentially happen with unexpected event from the + * driver wrapper. This was seen at least in one case where the + * driver ended up reporting a station mode event while hostapd + * was running, so better make sure we stop processing such an + * event here. + */ + wpa_printf(MSG_DEBUG, "hostapd_notif_disassoc: Skip event " + "with no address"); + return; + } + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "disassociated"); @@ -173,9 +341,8 @@ void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr) return; } + ap_sta_set_authorized(hapd, sta, 0); sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); - wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED MACSTR, - MAC2STR(sta->addr)); wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC); sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); @@ -183,50 +350,180 @@ void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr) } -#ifdef HOSTAPD +void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr) +{ + struct sta_info *sta = ap_get_sta(hapd, addr); + + if (!sta || !hapd->conf->disassoc_low_ack) + return; + + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "disconnected due to excessive " + "missing ACKs"); + hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_DISASSOC_LOW_ACK); + if (sta) + ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK); +} + +void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, + int offset) +{ #ifdef NEED_AP_MLME + int channel; -static const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len) + 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, + int ssi_signal) { - u16 fc, type, stype; + size_t i; + int ret = 0; - /* - * PS-Poll frames are 16 bytes. All other frames are - * 24 bytes or longer. - */ - if (len < 16) - return NULL; + if (sa == NULL || ie == NULL) + return -1; - fc = le_to_host16(hdr->frame_control); - type = WLAN_FC_GET_TYPE(fc); - stype = WLAN_FC_GET_STYPE(fc); - - switch (type) { - case WLAN_FC_TYPE_DATA: - if (len < 24) - return NULL; - switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) { - case WLAN_FC_FROMDS | WLAN_FC_TODS: - case WLAN_FC_TODS: - return hdr->addr1; - case WLAN_FC_FROMDS: - return hdr->addr2; - default: - return NULL; + 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, + ssi_signal) > 0) { + ret = 1; + break; } - case WLAN_FC_TYPE_CTRL: - if (stype != WLAN_FC_STYPE_PSPOLL) - return NULL; - return hdr->addr1; - case WLAN_FC_TYPE_MGMT: - return hdr->addr3; - default: - return NULL; } + return ret; } +#ifdef HOSTAPD + +#ifdef CONFIG_IEEE80211R +static void hostapd_notify_auth_ft_finish(void *ctx, const u8 *dst, + const u8 *bssid, + u16 auth_transaction, u16 status, + const u8 *ies, size_t ies_len) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + sta = ap_get_sta(hapd, dst); + if (sta == NULL) + return; + + hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)"); + sta->flags |= WLAN_STA_AUTH; + + hostapd_sta_auth(hapd, dst, auth_transaction, status, ies, ies_len); +} +#endif /* CONFIG_IEEE80211R */ + + +static void hostapd_notif_auth(struct hostapd_data *hapd, + struct auth_info *rx_auth) +{ + struct sta_info *sta; + u16 status = WLAN_STATUS_SUCCESS; + u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN]; + size_t resp_ies_len = 0; + + sta = ap_get_sta(hapd, rx_auth->peer); + if (!sta) { + sta = ap_sta_add(hapd, rx_auth->peer); + if (sta == NULL) { + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + } + sta->flags &= ~WLAN_STA_PREAUTH; + ieee802_1x_notify_pre_auth(sta->eapol_sm, 0); +#ifdef CONFIG_IEEE80211R + if (rx_auth->auth_type == WLAN_AUTH_FT && hapd->wpa_auth) { + sta->auth_alg = WLAN_AUTH_FT; + if (sta->wpa_sm == NULL) + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, + sta->addr); + if (sta->wpa_sm == NULL) { + wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA " + "state machine"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + wpa_ft_process_auth(sta->wpa_sm, rx_auth->bssid, + rx_auth->auth_transaction, rx_auth->ies, + rx_auth->ies_len, + hostapd_notify_auth_ft_finish, hapd); + return; + } +#endif /* CONFIG_IEEE80211R */ +fail: + hostapd_sta_auth(hapd, rx_auth->peer, rx_auth->auth_transaction + 1, + status, resp_ies, resp_ies_len); +} + + +static void hostapd_action_rx(struct hostapd_data *hapd, + struct rx_action *action) +{ + struct sta_info *sta; + + wpa_printf(MSG_DEBUG, "RX_ACTION cat %d action plen %d", + action->category, (int) action->len); + + sta = ap_get_sta(hapd, action->sa); + if (sta == NULL) { + wpa_printf(MSG_DEBUG, "%s: station not found", __func__); + return; + } +#ifdef CONFIG_IEEE80211R + if (action->category == WLAN_ACTION_FT) { + wpa_printf(MSG_DEBUG, "%s: FT_ACTION length %d", + __func__, (int) action->len); + wpa_ft_action_rx(sta->wpa_sm, action->data, action->len); + } +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + if (action->category == WLAN_ACTION_SA_QUERY && action->len >= 4) { + wpa_printf(MSG_DEBUG, "%s: SA_QUERY_ACTION length %d", + __func__, (int) action->len); + ieee802_11_sa_query_action(hapd, action->sa, + *(action->data + 1), + action->data + 2); + } +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_WNM + if (action->category == WLAN_ACTION_WNM) { + wpa_printf(MSG_DEBUG, "%s: WNM_ACTION length %d", + __func__, (int) action->len); + ieee802_11_rx_wnm_action_ap(hapd, action); + } +#endif /* CONFIG_WNM */ +} + + +#ifdef NEED_AP_MLME + #define HAPD_BROADCAST ((struct hostapd_data *) -1) static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface, @@ -250,17 +547,14 @@ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface, static void hostapd_rx_from_unknown_sta(struct hostapd_data *hapd, - const u8 *frame, size_t len) + const u8 *bssid, const u8 *addr, + int wds) { - const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *) frame; - u16 fc = le_to_host16(hdr->frame_control); - hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len)); + hapd = get_hapd_bssid(hapd->iface, bssid); if (hapd == NULL || hapd == HAPD_BROADCAST) return; - ieee802_11_rx_from_unknown(hapd, hdr->addr2, - (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == - (WLAN_FC_TODS | WLAN_FC_FROMDS)); + ieee802_11_rx_from_unknown(hapd, addr, wds); } @@ -303,6 +597,48 @@ static void hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt) rx_mgmt->frame_len, &fi); } else ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len, &fi); + + random_add_randomness(&fi, sizeof(fi)); +} + + +static void hostapd_rx_action(struct hostapd_data *hapd, + struct rx_action *rx_action) +{ + struct rx_mgmt rx_mgmt; + u8 *buf; + struct ieee80211_hdr *hdr; + + wpa_printf(MSG_DEBUG, "EVENT_RX_ACTION DA=" MACSTR " SA=" MACSTR + " BSSID=" MACSTR " category=%u", + MAC2STR(rx_action->da), MAC2STR(rx_action->sa), + MAC2STR(rx_action->bssid), rx_action->category); + wpa_hexdump(MSG_MSGDUMP, "Received action frame contents", + rx_action->data, rx_action->len); + + buf = os_zalloc(24 + 1 + rx_action->len); + if (buf == NULL) + return; + hdr = (struct ieee80211_hdr *) buf; + hdr->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + if (rx_action->category == WLAN_ACTION_SA_QUERY) { + /* + * Assume frame was protected; it would have been dropped if + * not. + */ + hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP); + } + os_memcpy(hdr->addr1, rx_action->da, ETH_ALEN); + os_memcpy(hdr->addr2, rx_action->sa, ETH_ALEN); + os_memcpy(hdr->addr3, rx_action->bssid, ETH_ALEN); + buf[24] = rx_action->category; + os_memcpy(buf + 24 + 1, rx_action->data, rx_action->len); + os_memset(&rx_mgmt, 0, sizeof(rx_mgmt)); + rx_mgmt.frame = buf; + rx_mgmt.frame_len = 24 + 1 + rx_action->len; + hostapd_mgmt_rx(hapd, &rx_mgmt); + os_free(buf); } @@ -320,23 +656,6 @@ static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf, #endif /* NEED_AP_MLME */ -static int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, - const u8 *ie, size_t ie_len) -{ - size_t i; - int ret = 0; - - for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) { - if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx, - sa, ie, ie_len) > 0) { - ret = 1; - break; - } - } - return ret; -} - - static int hostapd_event_new_sta(struct hostapd_data *hapd, const u8 *addr) { struct sta_info *sta = ap_get_sta(hapd, addr); @@ -362,12 +681,15 @@ static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src, const u8 *data, size_t data_len) { struct hostapd_iface *iface = hapd->iface; + struct sta_info *sta; size_t j; for (j = 0; j < iface->num_bss; j++) { - if (ap_get_sta(iface->bss[j], src)) { - hapd = iface->bss[j]; - break; + if ((sta = ap_get_sta(iface->bss[j], src))) { + if (sta->flags & WLAN_STA_ASSOC) { + hapd = iface->bss[j]; + break; + } } } @@ -379,6 +701,23 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data) { struct hostapd_data *hapd = ctx; +#ifndef CONFIG_NO_STDOUT_DEBUG + int level = MSG_DEBUG; + + if (event == EVENT_RX_MGMT && data->rx_mgmt.frame && + data->rx_mgmt.frame_len >= 24) { + const struct ieee80211_hdr *hdr; + u16 fc; + hdr = (const struct ieee80211_hdr *) data->rx_mgmt.frame; + fc = le_to_host16(hdr->frame_control); + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) + level = MSG_EXCESSIVE; + } + + wpa_dbg(hapd->msg_ctx, level, "Event %s (%d) received", + event_to_string(event), event); +#endif /* CONFIG_NO_STDOUT_DEBUG */ switch (event) { case EVENT_MICHAEL_MIC_FAILURE: @@ -395,7 +734,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; #endif /* CONFIG_IEEE80211R */ case EVENT_WPS_BUTTON_PUSHED: - hostapd_wps_button_pushed(hapd); + hostapd_wps_button_pushed(hapd, NULL); break; #ifdef NEED_AP_MLME case EVENT_TX_STATUS: @@ -414,18 +753,34 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; } break; + case EVENT_EAPOL_TX_STATUS: + hostapd_eapol_tx_status(hapd, data->eapol_tx_status.dst, + data->eapol_tx_status.data, + data->eapol_tx_status.data_len, + data->eapol_tx_status.ack); + break; + case EVENT_DRIVER_CLIENT_POLL_OK: + hostapd_client_poll_ok(hapd, data->client_poll.addr); + break; case EVENT_RX_FROM_UNKNOWN: - hostapd_rx_from_unknown_sta(hapd, data->rx_from_unknown.frame, - data->rx_from_unknown.len); + hostapd_rx_from_unknown_sta(hapd, data->rx_from_unknown.bssid, + data->rx_from_unknown.addr, + data->rx_from_unknown.wds); break; case EVENT_RX_MGMT: hostapd_mgmt_rx(hapd, &data->rx_mgmt); break; #endif /* NEED_AP_MLME */ case EVENT_RX_PROBE_REQ: + if (data->rx_probe_req.sa == NULL || + data->rx_probe_req.ie == NULL) + break; hostapd_probe_req_rx(hapd, data->rx_probe_req.sa, + data->rx_probe_req.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); @@ -438,7 +793,8 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, case EVENT_ASSOC: hostapd_notif_assoc(hapd, data->assoc_info.addr, data->assoc_info.req_ies, - data->assoc_info.req_ies_len); + data->assoc_info.req_ies_len, + data->assoc_info.reassoc); break; case EVENT_DISASSOC: if (data) @@ -448,6 +804,30 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, if (data) hostapd_notif_disassoc(hapd, data->deauth_info.addr); break; + case EVENT_STATION_LOW_ACK: + if (!data) + break; + hostapd_event_sta_low_ack(hapd, data->low_ack.addr); + break; + case EVENT_RX_ACTION: + if (data->rx_action.da == NULL || data->rx_action.sa == NULL || + data->rx_action.bssid == NULL) + break; +#ifdef NEED_AP_MLME + hostapd_rx_action(hapd, &data->rx_action); +#endif /* NEED_AP_MLME */ + hostapd_action_rx(hapd, &data->rx_action); + break; + case EVENT_AUTH: + hostapd_notif_auth(hapd, &data->auth); + break; + 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/eap_user_db.c b/src/ap/eap_user_db.c new file mode 100644 index 0000000000000..79d50e51694e6 --- /dev/null +++ b/src/ap/eap_user_db.c @@ -0,0 +1,270 @@ +/* + * hostapd / EAP user database + * 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" +#ifdef CONFIG_SQLITE +#include <sqlite3.h> +#endif /* CONFIG_SQLITE */ + +#include "common.h" +#include "eap_common/eap_wsc_common.h" +#include "eap_server/eap_methods.h" +#include "eap_server/eap.h" +#include "ap_config.h" +#include "hostapd.h" + +#ifdef CONFIG_SQLITE + +static void set_user_methods(struct hostapd_eap_user *user, const char *methods) +{ + char *buf, *start; + int num_methods; + + buf = os_strdup(methods); + if (buf == NULL) + return; + + os_memset(&user->methods, 0, sizeof(user->methods)); + num_methods = 0; + start = buf; + while (*start) { + char *pos3 = os_strchr(start, ','); + if (pos3) + *pos3++ = '\0'; + user->methods[num_methods].method = + eap_server_get_type(start, + &user->methods[num_methods].vendor); + if (user->methods[num_methods].vendor == EAP_VENDOR_IETF && + user->methods[num_methods].method == EAP_TYPE_NONE) { + if (os_strcmp(start, "TTLS-PAP") == 0) { + user->ttls_auth |= EAP_TTLS_AUTH_PAP; + goto skip_eap; + } + if (os_strcmp(start, "TTLS-CHAP") == 0) { + user->ttls_auth |= EAP_TTLS_AUTH_CHAP; + goto skip_eap; + } + if (os_strcmp(start, "TTLS-MSCHAP") == 0) { + user->ttls_auth |= EAP_TTLS_AUTH_MSCHAP; + goto skip_eap; + } + if (os_strcmp(start, "TTLS-MSCHAPV2") == 0) { + user->ttls_auth |= EAP_TTLS_AUTH_MSCHAPV2; + goto skip_eap; + } + wpa_printf(MSG_INFO, "DB: Unsupported EAP type '%s'", + start); + os_free(buf); + return; + } + + num_methods++; + if (num_methods >= EAP_MAX_METHODS) + break; + skip_eap: + if (pos3 == NULL) + break; + start = pos3; + } + + os_free(buf); +} + + +static int get_user_cb(void *ctx, int argc, char *argv[], char *col[]) +{ + struct hostapd_eap_user *user = ctx; + int i; + + for (i = 0; i < argc; i++) { + if (os_strcmp(col[i], "password") == 0 && argv[i]) { + os_free(user->password); + user->password_len = os_strlen(argv[i]); + user->password = (u8 *) os_strdup(argv[i]); + user->next = (void *) 1; + } else if (os_strcmp(col[i], "methods") == 0 && argv[i]) { + set_user_methods(user, argv[i]); + } + } + + return 0; +} + + +static int get_wildcard_cb(void *ctx, int argc, char *argv[], char *col[]) +{ + struct hostapd_eap_user *user = ctx; + int i, id = -1, methods = -1; + size_t len; + + for (i = 0; i < argc; i++) { + if (os_strcmp(col[i], "identity") == 0 && argv[i]) + id = i; + else if (os_strcmp(col[i], "methods") == 0 && argv[i]) + methods = i; + } + + if (id < 0 || methods < 0) + return 0; + + len = os_strlen(argv[id]); + if (len <= user->identity_len && + os_memcmp(argv[id], user->identity, len) == 0 && + (user->password == NULL || len > user->password_len)) { + os_free(user->password); + user->password_len = os_strlen(argv[id]); + user->password = (u8 *) os_strdup(argv[id]); + user->next = (void *) 1; + set_user_methods(user, argv[methods]); + } + + return 0; +} + + +static const struct hostapd_eap_user * +eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity, + size_t identity_len, int phase2) +{ + sqlite3 *db; + struct hostapd_eap_user *user = NULL; + char id_str[256], cmd[300]; + size_t i; + + if (identity_len >= sizeof(id_str)) + return NULL; + os_memcpy(id_str, identity, identity_len); + id_str[identity_len] = '\0'; + for (i = 0; i < identity_len; i++) { + if (id_str[i] >= 'a' && id_str[i] <= 'z') + continue; + if (id_str[i] >= 'A' && id_str[i] <= 'Z') + continue; + if (id_str[i] >= '0' && id_str[i] <= '9') + continue; + if (id_str[i] == '-' || id_str[i] == '_' || id_str[i] == '.' || + id_str[i] == ',' || id_str[i] == '@' || id_str[i] == '\\' || + id_str[i] == '!' || id_str[i] == '#' || id_str[i] == '%' || + id_str[i] == '=' || id_str[i] == ' ') + continue; + wpa_printf(MSG_INFO, "DB: Unsupported character in identity"); + return NULL; + } + + os_free(hapd->tmp_eap_user.identity); + os_free(hapd->tmp_eap_user.password); + os_memset(&hapd->tmp_eap_user, 0, sizeof(hapd->tmp_eap_user)); + hapd->tmp_eap_user.phase2 = phase2; + hapd->tmp_eap_user.identity = os_zalloc(identity_len + 1); + if (hapd->tmp_eap_user.identity == NULL) + return NULL; + os_memcpy(hapd->tmp_eap_user.identity, identity, identity_len); + + if (sqlite3_open(hapd->conf->eap_user_sqlite, &db)) { + wpa_printf(MSG_INFO, "DB: Failed to open database %s: %s", + hapd->conf->eap_user_sqlite, sqlite3_errmsg(db)); + sqlite3_close(db); + return NULL; + } + + os_snprintf(cmd, sizeof(cmd), + "SELECT password,methods FROM users WHERE " + "identity='%s' AND phase2=%d;", id_str, phase2); + wpa_printf(MSG_DEBUG, "DB: %s", cmd); + if (sqlite3_exec(db, cmd, get_user_cb, &hapd->tmp_eap_user, NULL) != + SQLITE_OK) { + wpa_printf(MSG_DEBUG, "DB: Failed to complete SQL operation"); + } else if (hapd->tmp_eap_user.next) + user = &hapd->tmp_eap_user; + + if (user == NULL && !phase2) { + os_snprintf(cmd, sizeof(cmd), + "SELECT identity,methods FROM wildcards;"); + wpa_printf(MSG_DEBUG, "DB: %s", cmd); + if (sqlite3_exec(db, cmd, get_wildcard_cb, &hapd->tmp_eap_user, + NULL) != SQLITE_OK) { + wpa_printf(MSG_DEBUG, "DB: Failed to complete SQL " + "operation"); + } else if (hapd->tmp_eap_user.next) { + user = &hapd->tmp_eap_user; + os_free(user->identity); + user->identity = user->password; + user->identity_len = user->password_len; + user->password = NULL; + user->password_len = 0; + } + } + + sqlite3_close(db); + + return user; +} + +#endif /* CONFIG_SQLITE */ + + +const struct hostapd_eap_user * +hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity, + size_t identity_len, int phase2) +{ + const struct hostapd_bss_config *conf = hapd->conf; + struct hostapd_eap_user *user = conf->eap_user; + +#ifdef CONFIG_WPS + if (conf->wps_state && identity_len == WSC_ID_ENROLLEE_LEN && + os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) { + static struct hostapd_eap_user wsc_enrollee; + os_memset(&wsc_enrollee, 0, sizeof(wsc_enrollee)); + wsc_enrollee.methods[0].method = eap_server_get_type( + "WSC", &wsc_enrollee.methods[0].vendor); + return &wsc_enrollee; + } + + if (conf->wps_state && identity_len == WSC_ID_REGISTRAR_LEN && + os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) { + static struct hostapd_eap_user wsc_registrar; + os_memset(&wsc_registrar, 0, sizeof(wsc_registrar)); + wsc_registrar.methods[0].method = eap_server_get_type( + "WSC", &wsc_registrar.methods[0].vendor); + wsc_registrar.password = (u8 *) conf->ap_pin; + wsc_registrar.password_len = conf->ap_pin ? + os_strlen(conf->ap_pin) : 0; + return &wsc_registrar; + } +#endif /* CONFIG_WPS */ + + while (user) { + if (!phase2 && user->identity == NULL) { + /* Wildcard match */ + break; + } + + if (user->phase2 == !!phase2 && user->wildcard_prefix && + identity_len >= user->identity_len && + os_memcmp(user->identity, identity, user->identity_len) == + 0) { + /* Wildcard prefix match */ + break; + } + + if (user->phase2 == !!phase2 && + user->identity_len == identity_len && + os_memcmp(user->identity, identity, identity_len) == 0) + break; + user = user->next; + } + +#ifdef CONFIG_SQLITE + if (user == NULL && conf->eap_user_sqlite) { + return eap_user_sqlite_get(hapd, identity, identity_len, + phase2); + } +#endif /* CONFIG_SQLITE */ + + return user; +} diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c new file mode 100644 index 0000000000000..851c183e97f89 --- /dev/null +++ b/src/ap/gas_serv.c @@ -0,0 +1,1172 @@ +/* + * 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; +} + + +#ifdef CONFIG_HS20 +static void anqp_add_hs_capab_list(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + u8 *len; + + len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); + wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST); + wpabuf_put_u8(buf, 0); /* Reserved */ + wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST); + if (hapd->conf->hs20_oper_friendly_name) + wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME); + if (hapd->conf->hs20_wan_metrics) + wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS); + if (hapd->conf->hs20_connection_capability) + wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY); + if (hapd->conf->nai_realm_data) + wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY); + if (hapd->conf->hs20_operating_class) + wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS); + gas_anqp_set_element_len(buf, len); +} +#endif /* CONFIG_HS20 */ + + +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->network_auth_type) + wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE); + if (hapd->conf->roaming_consortium) + wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM); + if (hapd->conf->ipaddr_type_configured) + wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY); + if (hapd->conf->nai_realm_data) + wpabuf_put_le16(buf, ANQP_NAI_REALM); + if (hapd->conf->anqp_3gpp_cell_net) + wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK); + if (hapd->conf->domain_name) + wpabuf_put_le16(buf, ANQP_DOMAIN_NAME); +#ifdef CONFIG_HS20 + anqp_add_hs_capab_list(hapd, buf); +#endif /* CONFIG_HS20 */ + 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_lang_string *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_network_auth_type(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->network_auth_type) { + wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE); + wpabuf_put_le16(buf, hapd->conf->network_auth_type_len); + wpabuf_put_data(buf, hapd->conf->network_auth_type, + hapd->conf->network_auth_type_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 void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->ipaddr_type_configured) { + wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY); + wpabuf_put_le16(buf, 1); + wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability); + } +} + + +static void anqp_add_nai_realm_eap(struct wpabuf *buf, + struct hostapd_nai_realm_data *realm) +{ + unsigned int i, j; + + wpabuf_put_u8(buf, realm->eap_method_count); + + for (i = 0; i < realm->eap_method_count; i++) { + struct hostapd_nai_realm_eap *eap = &realm->eap_method[i]; + wpabuf_put_u8(buf, 2 + (3 * eap->num_auths)); + wpabuf_put_u8(buf, eap->eap_method); + wpabuf_put_u8(buf, eap->num_auths); + for (j = 0; j < eap->num_auths; j++) { + wpabuf_put_u8(buf, eap->auth_id[j]); + wpabuf_put_u8(buf, 1); + wpabuf_put_u8(buf, eap->auth_val[j]); + } + } +} + + +static void anqp_add_nai_realm_data(struct wpabuf *buf, + struct hostapd_nai_realm_data *realm, + unsigned int realm_idx) +{ + u8 *realm_data_len; + + wpa_printf(MSG_DEBUG, "realm=%s, len=%d", realm->realm[realm_idx], + (int) os_strlen(realm->realm[realm_idx])); + realm_data_len = wpabuf_put(buf, 2); + wpabuf_put_u8(buf, realm->encoding); + wpabuf_put_u8(buf, os_strlen(realm->realm[realm_idx])); + wpabuf_put_str(buf, realm->realm[realm_idx]); + anqp_add_nai_realm_eap(buf, realm); + gas_anqp_set_element_len(buf, realm_data_len); +} + + +static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd, + struct wpabuf *buf, + const u8 *home_realm, + size_t home_realm_len) +{ + unsigned int i, j, k; + u8 num_realms, num_matching = 0, encoding, realm_len, *realm_list_len; + struct hostapd_nai_realm_data *realm; + const u8 *pos, *realm_name, *end; + struct { + unsigned int realm_data_idx; + unsigned int realm_idx; + } matches[10]; + + pos = home_realm; + end = pos + home_realm_len; + if (pos + 1 > end) { + wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query", + home_realm, home_realm_len); + return -1; + } + num_realms = *pos++; + + for (i = 0; i < num_realms && num_matching < 10; i++) { + if (pos + 2 > end) { + wpa_hexdump(MSG_DEBUG, + "Truncated NAI Home Realm Query", + home_realm, home_realm_len); + return -1; + } + encoding = *pos++; + realm_len = *pos++; + if (pos + realm_len > end) { + wpa_hexdump(MSG_DEBUG, + "Truncated NAI Home Realm Query", + home_realm, home_realm_len); + return -1; + } + realm_name = pos; + for (j = 0; j < hapd->conf->nai_realm_count && + num_matching < 10; j++) { + const u8 *rpos, *rend; + realm = &hapd->conf->nai_realm_data[j]; + if (encoding != realm->encoding) + continue; + + rpos = realm_name; + while (rpos < realm_name + realm_len && + num_matching < 10) { + for (rend = rpos; + rend < realm_name + realm_len; rend++) { + if (*rend == ';') + break; + } + for (k = 0; k < MAX_NAI_REALMS && + realm->realm[k] && + num_matching < 10; k++) { + if ((int) os_strlen(realm->realm[k]) != + rend - rpos || + os_strncmp((char *) rpos, + realm->realm[k], + rend - rpos) != 0) + continue; + matches[num_matching].realm_data_idx = + j; + matches[num_matching].realm_idx = k; + num_matching++; + } + rpos = rend + 1; + } + } + pos += realm_len; + } + + realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM); + wpabuf_put_le16(buf, num_matching); + + /* + * There are two ways to format. 1. each realm in a NAI Realm Data unit + * 2. all realms that share the same EAP methods in a NAI Realm Data + * unit. The first format is likely to be bigger in size than the + * second, but may be easier to parse and process by the receiver. + */ + for (i = 0; i < num_matching; i++) { + wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d", + matches[i].realm_data_idx, matches[i].realm_idx); + realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx]; + anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx); + } + gas_anqp_set_element_len(buf, realm_list_len); + return 0; +} + + +static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf, + const u8 *home_realm, size_t home_realm_len, + int nai_realm, int nai_home_realm) +{ + if (nai_realm && hapd->conf->nai_realm_data) { + u8 *len; + unsigned int i, j; + len = gas_anqp_add_element(buf, ANQP_NAI_REALM); + wpabuf_put_le16(buf, hapd->conf->nai_realm_count); + for (i = 0; i < hapd->conf->nai_realm_count; i++) { + u8 *realm_data_len, *realm_len; + struct hostapd_nai_realm_data *realm; + + realm = &hapd->conf->nai_realm_data[i]; + realm_data_len = wpabuf_put(buf, 2); + wpabuf_put_u8(buf, realm->encoding); + realm_len = wpabuf_put(buf, 1); + for (j = 0; realm->realm[j]; j++) { + if (j > 0) + wpabuf_put_u8(buf, ';'); + wpabuf_put_str(buf, realm->realm[j]); + } + *realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1; + anqp_add_nai_realm_eap(buf, realm); + gas_anqp_set_element_len(buf, realm_data_len); + } + gas_anqp_set_element_len(buf, len); + } else if (nai_home_realm && hapd->conf->nai_realm_data) { + hs20_add_nai_home_realm_matches(hapd, buf, home_realm, + home_realm_len); + } +} + + +static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->anqp_3gpp_cell_net) { + wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK); + wpabuf_put_le16(buf, + hapd->conf->anqp_3gpp_cell_net_len); + wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net, + hapd->conf->anqp_3gpp_cell_net_len); + } +} + + +static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf) +{ + if (hapd->conf->domain_name) { + wpabuf_put_le16(buf, ANQP_DOMAIN_NAME); + wpabuf_put_le16(buf, hapd->conf->domain_name_len); + wpabuf_put_data(buf, hapd->conf->domain_name, + hapd->conf->domain_name_len); + } +} + + +#ifdef CONFIG_HS20 + +static void anqp_add_operator_friendly_name(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->hs20_oper_friendly_name) { + u8 *len; + unsigned int i; + len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); + wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME); + wpabuf_put_u8(buf, 0); /* Reserved */ + for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++) + { + struct hostapd_lang_string *vn; + vn = &hapd->conf->hs20_oper_friendly_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_wan_metrics(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->hs20_wan_metrics) { + u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); + wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS); + wpabuf_put_u8(buf, 0); /* Reserved */ + wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13); + gas_anqp_set_element_len(buf, len); + } +} + + +static void anqp_add_connection_capability(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->hs20_connection_capability) { + u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); + wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY); + wpabuf_put_u8(buf, 0); /* Reserved */ + wpabuf_put_data(buf, hapd->conf->hs20_connection_capability, + hapd->conf->hs20_connection_capability_len); + gas_anqp_set_element_len(buf, len); + } +} + + +static void anqp_add_operating_class(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->hs20_operating_class) { + u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); + wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS); + wpabuf_put_u8(buf, 0); /* Reserved */ + wpabuf_put_data(buf, hapd->conf->hs20_operating_class, + hapd->conf->hs20_operating_class_len); + gas_anqp_set_element_len(buf, len); + } +} + +#endif /* CONFIG_HS20 */ + + +static struct wpabuf * +gas_serv_build_gas_resp_payload(struct hostapd_data *hapd, + unsigned int request, + struct gas_dialog_info *di, + const u8 *home_realm, size_t home_realm_len) +{ + 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_NETWORK_AUTH_TYPE) + anqp_add_network_auth_type(hapd, buf); + if (request & ANQP_REQ_ROAMING_CONSORTIUM) + anqp_add_roaming_consortium(hapd, buf); + if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY) + anqp_add_ip_addr_type_availability(hapd, buf); + if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM)) + anqp_add_nai_realm(hapd, buf, home_realm, home_realm_len, + request & ANQP_REQ_NAI_REALM, + request & ANQP_REQ_NAI_HOME_REALM); + if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK) + anqp_add_3gpp_cellular_network(hapd, buf); + if (request & ANQP_REQ_DOMAIN_NAME) + anqp_add_domain_name(hapd, buf); + +#ifdef CONFIG_HS20 + if (request & ANQP_REQ_HS_CAPABILITY_LIST) + anqp_add_hs_capab_list(hapd, buf); + if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME) + anqp_add_operator_friendly_name(hapd, buf); + if (request & ANQP_REQ_WAN_METRICS) + anqp_add_wan_metrics(hapd, buf); + if (request & ANQP_REQ_CONNECTION_CAPABILITY) + anqp_add_connection_capability(hapd, buf); + if (request & ANQP_REQ_OPERATING_CLASS) + anqp_add_operating_class(hapd, buf); +#endif /* CONFIG_HS20 */ + + 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 u8 *home_realm_query; + size_t home_realm_query_len; + 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_NETWORK_AUTH_TYPE: + set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type", + hapd->conf->network_auth_type != 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; + case ANQP_IP_ADDR_TYPE_AVAILABILITY: + set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY, + "IP Addr Type Availability", + hapd->conf->ipaddr_type_configured, + 0, 0, qi); + break; + case ANQP_NAI_REALM: + set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm", + hapd->conf->nai_realm_data != NULL, + 0, 0, qi); + break; + case ANQP_3GPP_CELLULAR_NETWORK: + set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK, + "3GPP Cellular Network", + hapd->conf->anqp_3gpp_cell_net != NULL, + 0, 0, qi); + break; + case ANQP_DOMAIN_NAME: + set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name", + hapd->conf->domain_name != 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; + } +} + + +#ifdef CONFIG_HS20 + +static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype, + struct anqp_query_info *qi) +{ + switch (subtype) { + case HS20_STYPE_CAPABILITY_LIST: + set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List", + 1, 0, 0, qi); + break; + case HS20_STYPE_OPERATOR_FRIENDLY_NAME: + set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME, + "Operator Friendly Name", + hapd->conf->hs20_oper_friendly_name != NULL, + 0, 0, qi); + break; + case HS20_STYPE_WAN_METRICS: + set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics", + hapd->conf->hs20_wan_metrics != NULL, + 0, 0, qi); + break; + case HS20_STYPE_CONNECTION_CAPABILITY: + set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY, + "Connection Capability", + hapd->conf->hs20_connection_capability != NULL, + 0, 0, qi); + break; + case HS20_STYPE_OPERATING_CLASS: + set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class", + hapd->conf->hs20_operating_class != NULL, + 0, 0, qi); + break; + default: + wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u", + subtype); + break; + } +} + + +static void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd, + const u8 *pos, const u8 *end, + struct anqp_query_info *qi) +{ + qi->request |= ANQP_REQ_NAI_HOME_REALM; + qi->home_realm_query = pos; + qi->home_realm_query_len = end - pos; + if (hapd->conf->nai_realm_data != NULL) { + wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query " + "(local)"); + } else { + wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query not " + "available"); + } +} + + +static void rx_anqp_vendor_specific(struct hostapd_data *hapd, + const u8 *pos, const u8 *end, + struct anqp_query_info *qi) +{ + u32 oui; + u8 subtype; + + if (pos + 4 > end) { + wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP " + "Query element"); + return; + } + + oui = WPA_GET_BE24(pos); + pos += 3; + if (oui != OUI_WFA) { + wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x", + oui); + return; + } + + if (*pos != HS20_ANQP_OUI_TYPE) { + wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u", + *pos); + return; + } + pos++; + + if (pos + 1 >= end) + return; + + subtype = *pos++; + pos++; /* Reserved */ + switch (subtype) { + case HS20_STYPE_QUERY_LIST: + wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Query List"); + while (pos < end) { + rx_anqp_hs_query_list(hapd, *pos, qi); + pos++; + } + break; + case HS20_STYPE_NAI_HOME_REALM_QUERY: + rx_anqp_hs_nai_home_realm(hapd, pos, end, qi); + break; + default: + wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype " + "%u", subtype); + break; + } +} + +#endif /* CONFIG_HS20 */ + + +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, + qi->home_realm_query, + qi->home_realm_query_len); + 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; +#ifdef CONFIG_HS20 + case ANQP_VENDOR_SPECIFIC: + rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi); + break; +#endif /* CONFIG_HS20 */ + 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, NULL, 0); + 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, NULL, 0); + 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 0000000000000..4213cf6da0207 --- /dev/null +++ b/src/ap/gas_serv.h @@ -0,0 +1,71 @@ +/* + * 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_NETWORK_AUTH_TYPE \ + (1 << (ANQP_NETWORK_AUTH_TYPE - ANQP_QUERY_LIST)) +#define ANQP_REQ_ROAMING_CONSORTIUM \ + (1 << (ANQP_ROAMING_CONSORTIUM - ANQP_QUERY_LIST)) +#define ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY \ + (1 << (ANQP_IP_ADDR_TYPE_AVAILABILITY - ANQP_QUERY_LIST)) +#define ANQP_REQ_NAI_REALM \ + (1 << (ANQP_NAI_REALM - ANQP_QUERY_LIST)) +#define ANQP_REQ_3GPP_CELLULAR_NETWORK \ + (1 << (ANQP_3GPP_CELLULAR_NETWORK - ANQP_QUERY_LIST)) +#define ANQP_REQ_DOMAIN_NAME \ + (1 << (ANQP_DOMAIN_NAME - ANQP_QUERY_LIST)) +#define ANQP_REQ_HS_CAPABILITY_LIST \ + (0x10000 << HS20_STYPE_CAPABILITY_LIST) +#define ANQP_REQ_OPERATOR_FRIENDLY_NAME \ + (0x10000 << HS20_STYPE_OPERATOR_FRIENDLY_NAME) +#define ANQP_REQ_WAN_METRICS \ + (0x10000 << HS20_STYPE_WAN_METRICS) +#define ANQP_REQ_CONNECTION_CAPABILITY \ + (0x10000 << HS20_STYPE_CONNECTION_CAPABILITY) +#define ANQP_REQ_NAI_HOME_REALM \ + (0x10000 << HS20_STYPE_NAI_HOME_REALM_QUERY) +#define ANQP_REQ_OPERATING_CLASS \ + (0x10000 << HS20_STYPE_OPERATING_CLASS) + +/* 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 841f9c59c46ba..cef9dafc52e05 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -1,15 +1,9 @@ /* * hostapd / Initialization and configuration - * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-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 - * published 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 "utils/includes.h" @@ -18,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" @@ -35,57 +30,56 @@ #include "wpa_auth_glue.h" #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; +extern struct wpa_driver_ops *wpa_drivers[]; -int hostapd_reload_config(struct hostapd_iface *iface) +int hostapd_for_each_interface(struct hapd_interfaces *interfaces, + int (*cb)(struct hostapd_iface *iface, + void *ctx), void *ctx) { - struct hostapd_data *hapd = iface->bss[0]; - struct hostapd_config *newconf, *oldconf; - size_t j; + size_t i; + int ret; - if (iface->config_read_cb == NULL) - return -1; - newconf = iface->config_read_cb(iface->config_fname); - if (newconf == NULL) - return -1; + for (i = 0; i < interfaces->count; i++) { + ret = cb(interfaces->iface[i], ctx); + if (ret) + return ret; + } - /* - * Deauthenticate all stations since the new configuration may not - * allow them to use the BSS anymore. - */ - for (j = 0; j < iface->num_bss; j++) - hostapd_flush_old_stations(iface->bss[j]); + return 0; +} + +static void hostapd_reload_bss(struct hostapd_data *hapd) +{ #ifndef CONFIG_NO_RADIUS - /* TODO: update dynamic data based on changed configuration - * items (e.g., open/close sockets, etc.) */ - radius_client_flush(hapd->radius, 0); + radius_client_reconfig(hapd->radius, hapd->conf->radius); #endif /* CONFIG_NO_RADIUS */ - oldconf = hapd->iconf; - hapd->iconf = newconf; - hapd->conf = &newconf->bss[0]; - iface->conf = newconf; - if (hostapd_setup_wpa_psk(hapd->conf)) { wpa_printf(MSG_ERROR, "Failed to re-configure WPA PSK " "after reloading configuration"); } if (hapd->conf->ieee802_1x || hapd->conf->wpa) - hapd->drv.set_drv_ieee8021x(hapd, hapd->conf->iface, 1); + hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 1); else - hapd->drv.set_drv_ieee8021x(hapd, hapd->conf->iface, 0); + hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0); - if (hapd->conf->wpa && hapd->wpa_auth == NULL) + if (hapd->conf->wpa && hapd->wpa_auth == NULL) { hostapd_setup_wpa(hapd); - else if (hapd->conf->wpa) { + if (hapd->wpa_auth) + wpa_init_keys(hapd->wpa_auth); + } else if (hapd->conf->wpa) { const u8 *wpa_ie; size_t wpa_ie_len; hostapd_reconfig_wpa(hapd); @@ -105,15 +99,56 @@ int hostapd_reload_config(struct hostapd_iface *iface) hostapd_update_wps(hapd); if (hapd->conf->ssid.ssid_set && - hostapd_set_ssid(hapd, (u8 *) hapd->conf->ssid.ssid, + hostapd_set_ssid(hapd, hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len)) { wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver"); /* try to continue */ } + wpa_printf(MSG_DEBUG, "Reconfigured interface %s", hapd->conf->iface); +} + + +int hostapd_reload_config(struct hostapd_iface *iface) +{ + struct hostapd_data *hapd = iface->bss[0]; + struct hostapd_config *newconf, *oldconf; + size_t j; + + if (iface->interfaces == NULL || + iface->interfaces->config_read_cb == NULL) + return -1; + newconf = iface->interfaces->config_read_cb(iface->config_fname); + if (newconf == NULL) + return -1; + + /* + * Deauthenticate all stations since the new configuration may not + * allow them to use the BSS anymore. + */ + for (j = 0; j < iface->num_bss; j++) { + hostapd_flush_old_stations(iface->bss[j], + WLAN_REASON_PREV_AUTH_NOT_VALID); + hostapd_broadcast_wep_clear(iface->bss[j]); + +#ifndef CONFIG_NO_RADIUS + /* TODO: update dynamic data based on changed configuration + * items (e.g., open/close sockets, etc.) */ + radius_client_flush(iface->bss[j]->radius, 0); +#endif /* CONFIG_NO_RADIUS */ + } + + oldconf = hapd->iconf; + iface->conf = newconf; + + for (j = 0; j < iface->num_bss; j++) { + hapd = iface->bss[j]; + hapd->iconf = newconf; + hapd->conf = &newconf->bss[j]; + hostapd_reload_bss(hapd); + } hostapd_config_free(oldconf); - wpa_printf(MSG_DEBUG, "Reconfigured interface %s", hapd->conf->iface); return 0; } @@ -125,8 +160,8 @@ static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd, int i; for (i = 0; i < NUM_WEP_KEYS; i++) { - if (hapd->drv.set_key(ifname, hapd, WPA_ALG_NONE, NULL, i, - i == 0 ? 1 : 0, NULL, 0, NULL, 0)) { + if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE, NULL, i, + 0, NULL, 0, NULL, 0)) { wpa_printf(MSG_DEBUG, "Failed to clear default " "encryption keys (ifname=%s keyidx=%d)", ifname, i); @@ -135,9 +170,9 @@ static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd, #ifdef CONFIG_IEEE80211W if (hapd->conf->ieee80211w) { for (i = NUM_WEP_KEYS; i < NUM_WEP_KEYS + 2; i++) { - if (hapd->drv.set_key(ifname, hapd, WPA_ALG_NONE, NULL, - i, i == 0 ? 1 : 0, NULL, 0, - NULL, 0)) { + if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE, + NULL, i, 0, NULL, + 0, NULL, 0)) { wpa_printf(MSG_DEBUG, "Failed to clear " "default mgmt encryption keys " "(ifname=%s keyidx=%d)", ifname, i); @@ -162,11 +197,10 @@ static int hostapd_broadcast_wep_set(struct hostapd_data *hapd) idx = ssid->wep.idx; if (ssid->wep.default_len && - hapd->drv.set_key(hapd->conf->iface, - hapd, WPA_ALG_WEP, NULL, idx, - idx == ssid->wep.idx, - NULL, 0, ssid->wep.key[idx], - ssid->wep.len[idx])) { + hostapd_drv_set_key(hapd->conf->iface, + hapd, WPA_ALG_WEP, broadcast_ether_addr, idx, + 1, NULL, 0, ssid->wep.key[idx], + ssid->wep.len[idx])) { wpa_printf(MSG_WARNING, "Could not set WEP encryption."); errors++; } @@ -184,9 +218,10 @@ static int hostapd_broadcast_wep_set(struct hostapd_data *hapd) continue; idx = key->idx; - if (hapd->drv.set_key(ifname, hapd, WPA_ALG_WEP, NULL, - idx, idx == key->idx, NULL, 0, - key->key[idx], key->len[idx])) { + if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_WEP, + broadcast_ether_addr, idx, 1, + NULL, 0, key->key[idx], + key->len[idx])) { wpa_printf(MSG_WARNING, "Could not set " "dynamic VLAN WEP encryption."); errors++; @@ -197,21 +232,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); @@ -221,6 +244,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); @@ -235,6 +260,43 @@ static void hostapd_cleanup(struct hostapd_data *hapd) os_free(hapd->probereq_cb); hapd->probereq_cb = NULL; + +#ifdef CONFIG_P2P + wpabuf_free(hapd->p2p_beacon_ie); + hapd->p2p_beacon_ie = NULL; + wpabuf_free(hapd->p2p_probe_resp_ie); + hapd->p2p_probe_resp_ie = NULL; +#endif /* CONFIG_P2P */ + + wpabuf_free(hapd->time_adv); + +#ifdef CONFIG_INTERWORKING + gas_serv_deinit(hapd); +#endif /* CONFIG_INTERWORKING */ + +#ifdef CONFIG_SQLITE + os_free(hapd->tmp_eap_user.identity); + os_free(hapd->tmp_eap_user.password); +#endif /* CONFIG_SQLITE */ +} + + +/** + * 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->interfaces && + hapd->iface->interfaces->ctrl_iface_deinit) + hapd->iface->interfaces->ctrl_iface_deinit(hapd); + hostapd_free_hapd_data(hapd); } @@ -250,6 +312,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 @@ -259,11 +333,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; - ap_list_deinit(iface); + hostapd_cleanup_iface_partial(iface); hostapd_config_free(iface->conf); iface->conf = NULL; @@ -273,6 +343,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; @@ -284,12 +363,18 @@ static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd) return 0; } + /* + * When IEEE 802.1X is not enabled, the driver may need to know how to + * set authentication algorithms for static WEP. + */ + hostapd_drv_set_authmode(hapd, hapd->conf->auth_algs); + for (i = 0; i < 4; i++) { if (hapd->conf->ssid.wep.key[i] && - hapd->drv.set_key(iface, hapd, WPA_ALG_WEP, NULL, i, - i == hapd->conf->ssid.wep.idx, NULL, 0, - hapd->conf->ssid.wep.key[i], - hapd->conf->ssid.wep.len[i])) { + hostapd_drv_set_key(iface, hapd, WPA_ALG_WEP, NULL, i, + i == hapd->conf->ssid.wep.idx, NULL, 0, + hapd->conf->ssid.wep.key[i], + hapd->conf->ssid.wep.len[i])) { wpa_printf(MSG_WARNING, "Could not set WEP " "encryption."); return -1; @@ -303,30 +388,24 @@ 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]; if (hostapd_drv_none(hapd) || hapd->drv_priv == NULL) return 0; - wpa_printf(MSG_DEBUG, "Flushing old station entries"); + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Flushing old station entries"); if (hostapd_flush(hapd)) { - wpa_printf(MSG_WARNING, "Could not connect to kernel driver."); + wpa_msg(hapd->msg_ctx, MSG_WARNING, "Could not connect to " + "kernel driver"); ret = -1; } - wpa_printf(MSG_DEBUG, "Deauthenticate all stations"); - - /* New Prism2.5/3 STA firmware versions seem to have issues with this - * broadcast deauth frame. This gets the firmware in odd state where - * nothing works correctly, so let's skip sending this for the hostap - * driver. */ - if (hapd->driver && os_strcmp(hapd->driver->name, "hostap") != 0) { - u8 addr[ETH_ALEN]; - os_memset(addr, 0xff, ETH_ALEN); - hapd->drv.sta_deauth(hapd, addr, - WLAN_REASON_PREV_AUTH_NOT_VALID); - } + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Deauthenticate all stations"); + os_memset(addr, 0xff, ETH_ALEN); + hostapd_drv_sta_deauth(hapd, addr, reason); + hostapd_free_stas(hapd); return ret; } @@ -344,7 +423,6 @@ static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface) u8 mask[ETH_ALEN] = { 0 }; struct hostapd_data *hapd = iface->bss[0]; unsigned int i = iface->conf->num_bss, bits = 0, j; - int res; int auto_addr = 0; if (hostapd_drv_none(hapd)) @@ -408,17 +486,6 @@ skip_mask_ext: wpa_printf(MSG_DEBUG, "BSS count %lu, BSSID mask " MACSTR " (%d bits)", (unsigned long) iface->conf->num_bss, MAC2STR(mask), bits); - res = hostapd_valid_bss_mask(hapd, hapd->own_addr, mask); - if (res == 0) - return 0; - - if (res < 0) { - wpa_printf(MSG_ERROR, "Driver did not accept BSSID mask " - MACSTR " for start address " MACSTR ".", - MAC2STR(mask), MAC2STR(hapd->own_addr)); - return -1; - } - if (!auto_addr) return 0; @@ -452,6 +519,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 */ /** @@ -495,14 +642,19 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) hapd->interface_added = 1; if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS, hapd->conf->iface, hapd->own_addr, hapd, - &hapd->drv_priv, force_ifname, if_addr)) { + &hapd->drv_priv, force_ifname, if_addr, + hapd->conf->bridge[0] ? hapd->conf->bridge : + NULL)) { wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID=" MACSTR ")", MAC2STR(hapd->own_addr)); return -1; } } - hostapd_flush_old_stations(hapd); + if (conf->wmm_enabled < 0) + conf->wmm_enabled = hapd->iconf->ieee80211n; + + hostapd_flush_old_stations(hapd, WLAN_REASON_PREV_AUTH_NOT_VALID); hostapd_set_privacy(hapd, 0); hostapd_broadcast_wep_clear(hapd); @@ -535,14 +687,14 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) set_ssid = 0; conf->ssid.ssid_len = ssid_len; os_memcpy(conf->ssid.ssid, ssid, conf->ssid.ssid_len); - conf->ssid.ssid[conf->ssid.ssid_len] = '\0'; } if (!hostapd_drv_none(hapd)) { wpa_printf(MSG_ERROR, "Using interface %s with hwaddr " MACSTR - " and ssid '%s'", + " and ssid \"%s\"", hapd->conf->iface, MAC2STR(hapd->own_addr), - hapd->conf->ssid.ssid); + wpa_ssid_txt(hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len)); } if (hostapd_setup_wpa_psk(conf)) { @@ -552,7 +704,7 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) /* Set SSID for the kernel driver (to be used in beacon and probe * response frames) */ - if (set_ssid && hostapd_set_ssid(hapd, (u8 *) conf->ssid.ssid, + if (set_ssid && hostapd_set_ssid(hapd, conf->ssid.ssid, conf->ssid.ssid_len)) { wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver"); return -1; @@ -566,6 +718,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)) { @@ -598,8 +771,16 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) return -1; } - if (hapd->iface->ctrl_iface_init && - hapd->iface->ctrl_iface_init(hapd)) { +#ifdef CONFIG_INTERWORKING + if (gas_serv_init(hapd)) { + wpa_printf(MSG_ERROR, "GAS server initialization failed"); + return -1; + } +#endif /* CONFIG_INTERWORKING */ + + if (hapd->iface->interfaces && + hapd->iface->interfaces->ctrl_iface_init && + hapd->iface->interfaces->ctrl_iface_init(hapd)) { wpa_printf(MSG_ERROR, "Failed to setup control interface"); return -1; } @@ -611,6 +792,12 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) ieee802_11_set_beacon(hapd); + if (hapd->wpa_auth && wpa_init_keys(hapd->wpa_auth) < 0) + return -1; + + if (hapd->driver && hapd->driver->set_operstate) + hapd->driver->set_operstate(hapd->drv_priv, 1); + return 0; } @@ -624,9 +811,6 @@ static void hostapd_tx_queue_params(struct hostapd_iface *iface) for (i = 0; i < NUM_TX_QUEUES; i++) { p = &iface->conf->tx_queue[i]; - if (!p->configured) - continue; - if (hostapd_set_tx_queue_params(hapd, i, p->aifs, p->cwmin, p->cwmax, p->burst)) { wpa_printf(MSG_DEBUG, "Failed to set TX queue " @@ -717,6 +901,17 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) } } + if (iface->current_mode) { + if (hostapd_prepare_rates(iface, iface->current_mode)) { + wpa_printf(MSG_ERROR, "Failed to prepare rates " + "table."); + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "Failed to prepare rates table."); + return -1; + } + } + if (hapd->iconf->rts_threshold > -1 && hostapd_set_rts(hapd, hapd->iconf->rts_threshold)) { wpa_printf(MSG_ERROR, "Could not set RTS threshold for " @@ -753,6 +948,20 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) return -1; } + /* + * WPS UPnP module can be initialized only when the "upnp_iface" is up. + * If "interface" and "upnp_iface" are the same (e.g., non-bridge + * mode), the interface is up only after driver_commit, so initialize + * WPS after driver_commit. + */ + for (j = 0; j < iface->num_bss; j++) { + if (hostapd_init_wps_complete(iface->bss[j])) + return -1; + } + + if (hapd->setup_complete_cb) + hapd->setup_complete_cb(hapd->setup_complete_cb_ctx); + wpa_printf(MSG_DEBUG, "%s: Setup of interface done.", iface->bss[0]->conf->iface); @@ -807,12 +1016,12 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, if (hapd == NULL) return NULL; - hostapd_set_driver_ops(&hapd->drv); hapd->new_assoc_sta_cb = hostapd_new_assoc_sta; hapd->iconf = conf; hapd->conf = bss; hapd->iface = hapd_iface; hapd->driver = hapd->iconf->driver; + hapd->ctrl_sock = -1; return hapd; } @@ -829,7 +1038,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); } } @@ -844,6 +1054,293 @@ void hostapd_interface_free(struct hostapd_iface *iface) } +#ifdef HOSTAPD + +void hostapd_interface_deinit_free(struct hostapd_iface *iface) +{ + const struct wpa_driver_ops *driver; + void *drv_priv; + if (iface == NULL) + return; + driver = iface->bss[0]->driver; + drv_priv = iface->bss[0]->drv_priv; + hostapd_interface_deinit(iface); + if (driver && driver->hapd_deinit && drv_priv) + driver->hapd_deinit(drv_priv); + hostapd_interface_free(iface); +} + + +int hostapd_enable_iface(struct hostapd_iface *hapd_iface) +{ + if (hapd_iface->bss[0]->drv_priv != NULL) { + wpa_printf(MSG_ERROR, "Interface %s already enabled", + hapd_iface->conf->bss[0].iface); + return -1; + } + + wpa_printf(MSG_DEBUG, "Enable interface %s", + hapd_iface->conf->bss[0].iface); + + if (hapd_iface->interfaces == NULL || + hapd_iface->interfaces->driver_init == NULL || + hapd_iface->interfaces->driver_init(hapd_iface) || + hostapd_setup_interface(hapd_iface)) { + hostapd_interface_deinit_free(hapd_iface); + return -1; + } + return 0; +} + + +int hostapd_reload_iface(struct hostapd_iface *hapd_iface) +{ + size_t j; + + wpa_printf(MSG_DEBUG, "Reload interface %s", + hapd_iface->conf->bss[0].iface); + for (j = 0; j < hapd_iface->num_bss; j++) { + hostapd_flush_old_stations(hapd_iface->bss[j], + WLAN_REASON_PREV_AUTH_NOT_VALID); + +#ifndef CONFIG_NO_RADIUS + /* TODO: update dynamic data based on changed configuration + * items (e.g., open/close sockets, etc.) */ + radius_client_flush(hapd_iface->bss[j]->radius, 0); +#endif /* CONFIG_NO_RADIUS */ + + hostapd_reload_bss(hapd_iface->bss[j]); + } + return 0; +} + + +int hostapd_disable_iface(struct hostapd_iface *hapd_iface) +{ + size_t j; + struct hostapd_bss_config *bss; + const struct wpa_driver_ops *driver; + void *drv_priv; + + if (hapd_iface == NULL) + return -1; + bss = hapd_iface->bss[0]->conf; + driver = hapd_iface->bss[0]->driver; + drv_priv = hapd_iface->bss[0]->drv_priv; + + /* whatever hostapd_interface_deinit does */ + for (j = 0; j < hapd_iface->num_bss; j++) { + struct hostapd_data *hapd = hapd_iface->bss[j]; + hostapd_free_stas(hapd); + hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING); + hostapd_clear_wep(hapd); + hostapd_free_hapd_data(hapd); + } + + if (driver && driver->hapd_deinit && drv_priv) { + driver->hapd_deinit(drv_priv); + hapd_iface->bss[0]->drv_priv = NULL; + } + + /* From hostapd_cleanup_iface: These were initialized in + * hostapd_setup_interface and hostapd_setup_interface_complete + */ + hostapd_cleanup_iface_partial(hapd_iface); + bss->wpa = 0; + bss->wpa_key_mgmt = -1; + bss->wpa_pairwise = -1; + + wpa_printf(MSG_DEBUG, "Interface %s disabled", bss->iface); + return 0; +} + + +static struct hostapd_iface * +hostapd_iface_alloc(struct hapd_interfaces *interfaces) +{ + struct hostapd_iface **iface, *hapd_iface; + + iface = os_realloc_array(interfaces->iface, interfaces->count + 1, + sizeof(struct hostapd_iface *)); + if (iface == NULL) + return NULL; + interfaces->iface = iface; + hapd_iface = interfaces->iface[interfaces->count] = + os_zalloc(sizeof(*hapd_iface)); + if (hapd_iface == NULL) { + wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for " + "the interface", __func__); + return NULL; + } + interfaces->count++; + hapd_iface->interfaces = interfaces; + + return hapd_iface; +} + + +static struct hostapd_config * +hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname, + const char *ctrl_iface) +{ + struct hostapd_bss_config *bss; + struct hostapd_config *conf; + + /* Allocates memory for bss and conf */ + conf = hostapd_config_defaults(); + if (conf == NULL) { + wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for " + "configuration", __func__); + return NULL; + } + + conf->driver = wpa_drivers[0]; + if (conf->driver == NULL) { + wpa_printf(MSG_ERROR, "No driver wrappers registered!"); + hostapd_config_free(conf); + return NULL; + } + + bss = conf->last_bss = conf->bss; + + os_strlcpy(bss->iface, ifname, sizeof(bss->iface)); + bss->ctrl_interface = os_strdup(ctrl_iface); + if (bss->ctrl_interface == NULL) { + hostapd_config_free(conf); + return NULL; + } + + /* Reading configuration file skipped, will be done in SET! + * From reading the configuration till the end has to be done in + * SET + */ + return conf; +} + + +static struct hostapd_iface * hostapd_data_alloc( + struct hapd_interfaces *interfaces, struct hostapd_config *conf) +{ + size_t i; + struct hostapd_iface *hapd_iface = + interfaces->iface[interfaces->count - 1]; + struct hostapd_data *hapd; + + hapd_iface->conf = conf; + hapd_iface->num_bss = conf->num_bss; + + hapd_iface->bss = os_zalloc(conf->num_bss * + sizeof(struct hostapd_data *)); + if (hapd_iface->bss == NULL) + return NULL; + + for (i = 0; i < conf->num_bss; i++) { + hapd = hapd_iface->bss[i] = + hostapd_alloc_bss_data(hapd_iface, conf, + &conf->bss[i]); + if (hapd == NULL) + return NULL; + hapd->msg_ctx = hapd; + } + + hapd_iface->interfaces = interfaces; + + return hapd_iface; +} + + +int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf) +{ + struct hostapd_config *conf = NULL; + struct hostapd_iface *hapd_iface = NULL; + char *ptr; + size_t i; + + ptr = os_strchr(buf, ' '); + if (ptr == NULL) + return -1; + *ptr++ = '\0'; + + for (i = 0; i < interfaces->count; i++) { + if (!os_strcmp(interfaces->iface[i]->conf->bss[0].iface, + buf)) { + wpa_printf(MSG_INFO, "Cannot add interface - it " + "already exists"); + return -1; + } + } + + hapd_iface = hostapd_iface_alloc(interfaces); + if (hapd_iface == NULL) { + wpa_printf(MSG_ERROR, "%s: Failed to allocate memory " + "for interface", __func__); + goto fail; + } + + conf = hostapd_config_alloc(interfaces, buf, ptr); + if (conf == NULL) { + wpa_printf(MSG_ERROR, "%s: Failed to allocate memory " + "for configuration", __func__); + goto fail; + } + + hapd_iface = hostapd_data_alloc(interfaces, conf); + if (hapd_iface == NULL) { + wpa_printf(MSG_ERROR, "%s: Failed to allocate memory " + "for hostapd", __func__); + goto fail; + } + + if (hapd_iface->interfaces && + hapd_iface->interfaces->ctrl_iface_init && + hapd_iface->interfaces->ctrl_iface_init(hapd_iface->bss[0])) { + wpa_printf(MSG_ERROR, "%s: Failed to setup control " + "interface", __func__); + goto fail; + } + wpa_printf(MSG_INFO, "Add interface '%s'", conf->bss[0].iface); + + return 0; + +fail: + if (conf) + hostapd_config_free(conf); + if (hapd_iface) { + os_free(hapd_iface->bss[interfaces->count]); + os_free(hapd_iface); + } + return -1; +} + + +int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf) +{ + struct hostapd_iface *hapd_iface; + size_t i, k = 0; + + for (i = 0; i < interfaces->count; i++) { + hapd_iface = interfaces->iface[i]; + if (hapd_iface == NULL) + return -1; + if (!os_strcmp(hapd_iface->conf->bss[0].iface, buf)) { + wpa_printf(MSG_INFO, "Remove interface '%s'", buf); + hostapd_interface_deinit_free(hapd_iface); + k = i; + while (k < (interfaces->count - 1)) { + interfaces->iface[k] = + interfaces->iface[k + 1]; + k++; + } + interfaces->count--; + return 0; + } + } + return -1; +} + +#endif /* HOSTAPD */ + + /** * hostapd_new_assoc_sta - Notify that a new station associated with the AP * @hapd: Pointer to BSS data @@ -859,8 +1356,8 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, int reassoc) { if (hapd->tkip_countermeasures) { - hapd->drv.sta_deauth(hapd, sta->addr, - WLAN_REASON_MICHAEL_MIC_FAILURE); + hostapd_drv_sta_deauth(hapd, sta->addr, + WLAN_REASON_MICHAEL_MIC_FAILURE); return; } @@ -870,11 +1367,22 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, if (hapd->conf->ieee802_11f) iapp_new_station(hapd->iapp, sta); +#ifdef CONFIG_P2P + if (sta->p2p_ie == NULL && !sta->no_p2p_set) { + sta->no_p2p_set = 1; + hapd->num_sta_no_p2p++; + if (hapd->num_sta_no_p2p == 1) + hostapd_p2p_non_p2p_sta_connected(hapd); + } +#endif /* CONFIG_P2P */ + /* Start accounting here, if IEEE 802.1X and WPA are not used. * IEEE 802.1X/WPA code will start accounting after the station has * been authorized. */ - if (!hapd->conf->ieee802_1x && !hapd->conf->wpa) + if (!hapd->conf->ieee802_1x && !hapd->conf->wpa) { + os_get_time(&sta->connected_time); accounting_sta_start(hapd, sta); + } /* Start IEEE 802.1X authentication process for new stations */ ieee802_1x_new_station(hapd, sta); @@ -884,4 +1392,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 d0d67c8228a49..f1e7d9ff7ec5a 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -2,34 +2,51 @@ * hostapd / Initialization and configuration * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef HOSTAPD_H #define HOSTAPD_H #include "common/defs.h" +#include "ap_config.h" struct wpa_driver_ops; struct wpa_ctrl_dst; struct radius_server_data; struct upnp_wps_device_sm; -struct hapd_interfaces; struct hostapd_data; struct sta_info; struct hostap_sta_driver_data; struct ieee80211_ht_capabilities; struct full_dynamic_vlan; +enum wps_event; +union wps_event_data; + +struct hostapd_iface; + +struct hapd_interfaces { + int (*reload_config)(struct hostapd_iface *iface); + struct hostapd_config * (*config_read_cb)(const char *config_fname); + int (*ctrl_iface_init)(struct hostapd_data *hapd); + void (*ctrl_iface_deinit)(struct hostapd_data *hapd); + int (*for_each_interface)(struct hapd_interfaces *interfaces, + int (*cb)(struct hostapd_iface *iface, + void *ctx), void *ctx); + int (*driver_init)(struct hostapd_iface *iface); + + size_t count; + int global_ctrl_sock; + char *global_iface_path; + char *global_iface_name; + struct hostapd_iface **iface; +}; + struct hostapd_probereq_cb { - int (*cb)(void *ctx, const u8 *sa, const u8 *ie, size_t ie_len); + int (*cb)(void *ctx, const u8 *sa, const u8 *da, const u8 *bssid, + const u8 *ie, size_t ie_len, int ssi_signal); void *ctx; }; @@ -43,59 +60,10 @@ struct hostapd_rate_data { struct hostapd_frame_info { u32 channel; u32 datarate; - u32 ssi_signal; + int ssi_signal; /* dBm */ }; -struct hostapd_driver_ops { - int (*set_ap_wps_ie)(struct hostapd_data *hapd); - int (*send_mgmt_frame)(struct hostapd_data *hapd, const void *msg, - size_t len); - int (*send_eapol)(struct hostapd_data *hapd, const u8 *addr, - const u8 *data, size_t data_len, int encrypt); - int (*set_authorized)(struct hostapd_data *hapd, struct sta_info *sta, - int authorized); - int (*set_key)(const char *ifname, struct hostapd_data *hapd, - enum wpa_alg alg, const u8 *addr, int key_idx, - int set_tx, const u8 *seq, size_t seq_len, - const u8 *key, size_t key_len); - int (*read_sta_data)(struct hostapd_data *hapd, - struct hostap_sta_driver_data *data, - const u8 *addr); - int (*sta_clear_stats)(struct hostapd_data *hapd, const u8 *addr); - int (*set_sta_flags)(struct hostapd_data *hapd, struct sta_info *sta); - int (*set_drv_ieee8021x)(struct hostapd_data *hapd, const char *ifname, - int enabled); - int (*set_radius_acl_auth)(struct hostapd_data *hapd, - const u8 *mac, int accepted, - u32 session_timeout); - int (*set_radius_acl_expire)(struct hostapd_data *hapd, - const u8 *mac); - int (*set_bss_params)(struct hostapd_data *hapd, int use_protection); - int (*set_beacon)(struct hostapd_data *hapd, - const u8 *head, size_t head_len, - const u8 *tail, size_t tail_len, int dtim_period, - int beacon_int); - int (*vlan_if_add)(struct hostapd_data *hapd, const char *ifname); - int (*vlan_if_remove)(struct hostapd_data *hapd, const char *ifname); - int (*set_wds_sta)(struct hostapd_data *hapd, const u8 *addr, int aid, - int val); - int (*set_sta_vlan)(const char *ifname, struct hostapd_data *hapd, - const u8 *addr, int vlan_id); - int (*get_inact_sec)(struct hostapd_data *hapd, const u8 *addr); - int (*sta_deauth)(struct hostapd_data *hapd, const u8 *addr, - int reason); - int (*sta_disassoc)(struct hostapd_data *hapd, const u8 *addr, - int reason); - int (*sta_add)(struct hostapd_data *hapd, - const u8 *addr, u16 aid, u16 capability, - const u8 *supp_rates, size_t supp_rates_len, - u16 listen_interval, - const struct ieee80211_ht_capabilities *ht_capab); - int (*sta_remove)(struct hostapd_data *hapd, const u8 *addr); - int (*set_countermeasures)(struct hostapd_data *hapd, int enabled); -}; - /** * struct hostapd_data - hostapd per-BSS data structure */ @@ -123,15 +91,16 @@ struct hostapd_data { const struct wpa_driver_ops *driver; void *drv_priv; - struct hostapd_driver_ops drv; void (*new_assoc_sta_cb)(struct hostapd_data *hapd, struct sta_info *sta, int reassoc); void *msg_ctx; /* ctx for wpa_msg() calls */ + void *msg_ctx_parent; /* parent interface ctx for wpa_msg() calls */ struct radius_client_data *radius; u32 acct_session_id_hi, acct_session_id_lo; + struct radius_das_data *radius_das; struct iapp_data *iapp; @@ -155,6 +124,10 @@ struct hostapd_data { int parameter_set_count; + /* Time Advertisement */ + u8 time_update_counter; + struct wpabuf *time_adv; + #ifdef CONFIG_FULL_DYNAMIC_VLAN struct full_dynamic_vlan *full_dynamic_vlan; #endif /* CONFIG_FULL_DYNAMIC_VLAN */ @@ -162,10 +135,12 @@ struct hostapd_data { struct l2_packet_data *l2; struct wps_context *wps; + int beacon_set_done; struct wpabuf *wps_beacon_ie; struct wpabuf *wps_probe_resp_ie; #ifdef CONFIG_WPS unsigned int ap_pin_failures; + unsigned int ap_pin_failures_consecutive; struct upnp_wps_device_sm *wps_upnp; unsigned int ap_pin_lockout_time; #endif /* CONFIG_WPS */ @@ -177,9 +152,46 @@ struct hostapd_data { int freq); void *public_action_cb_ctx; + int (*vendor_action_cb)(void *ctx, const u8 *buf, size_t len, + int freq); + void *vendor_action_cb_ctx; + void (*wps_reg_success_cb)(void *ctx, const u8 *mac_addr, const u8 *uuid_e); void *wps_reg_success_cb_ctx; + + void (*wps_event_cb)(void *ctx, enum wps_event event, + union wps_event_data *data); + void *wps_event_cb_ctx; + + void (*sta_authorized_cb)(void *ctx, const u8 *mac_addr, + int authorized, const u8 *p2p_dev_addr); + void *sta_authorized_cb_ctx; + + void (*setup_complete_cb)(void *ctx); + void *setup_complete_cb_ctx; + +#ifdef CONFIG_P2P + struct p2p_data *p2p; + struct p2p_group *p2p_group; + struct wpabuf *p2p_beacon_ie; + struct wpabuf *p2p_probe_resp_ie; + + /* Number of non-P2P association stations */ + int num_sta_no_p2p; + + /* Periodic NoA (used only when no non-P2P clients in the group) */ + int noa_enabled; + int noa_start; + int noa_duration; +#endif /* CONFIG_P2P */ +#ifdef CONFIG_INTERWORKING + size_t gas_frag_limit; +#endif /* CONFIG_INTERWORKING */ + +#ifdef CONFIG_SQLITE + struct hostapd_eap_user tmp_eap_user; +#endif /* CONFIG_SQLITE */ }; @@ -189,8 +201,6 @@ struct hostapd_data { struct hostapd_iface { struct hapd_interfaces *interfaces; void *owner; - int (*reload_config)(struct hostapd_iface *iface); - struct hostapd_config * (*config_read_cb)(const char *config_fname); char *config_fname; struct hostapd_config *conf; @@ -202,6 +212,14 @@ struct hostapd_iface { struct ap_info *ap_hash[STA_HASH_SIZE]; struct ap_info *ap_iter_list; + unsigned int drv_flags; + + /* + * A bitmap of supported protocols for probe response offload. See + * struct wpa_driver_capa in driver.h + */ + unsigned int probe_resp_offloads; + struct hostapd_hw_modes *hw_features; int num_hw_features; struct hostapd_hw_modes *current_mode; @@ -209,6 +227,7 @@ struct hostapd_iface { * current_mode->channels */ int num_rates; struct hostapd_rate_data *current_rates; + int *basic_rates; int freq; u16 hw_flags; @@ -239,16 +258,12 @@ struct hostapd_iface { u16 ht_op_mode; void (*scan_cb)(struct hostapd_iface *iface); - - int (*ctrl_iface_init)(struct hostapd_data *hapd); - void (*ctrl_iface_deinit)(struct hostapd_data *hapd); - - int (*for_each_interface)(struct hapd_interfaces *interfaces, - int (*cb)(struct hostapd_iface *iface, - void *ctx), void *ctx); }; /* hostapd.c */ +int hostapd_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, @@ -260,17 +275,35 @@ void hostapd_interface_deinit(struct hostapd_iface *iface); void hostapd_interface_free(struct hostapd_iface *iface); void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, int reassoc); +void hostapd_interface_deinit_free(struct hostapd_iface *iface); +int hostapd_enable_iface(struct hostapd_iface *hapd_iface); +int hostapd_reload_iface(struct hostapd_iface *hapd_iface); +int hostapd_disable_iface(struct hostapd_iface *hapd_iface); +int hostapd_add_iface(struct hapd_interfaces *ifaces, char *buf); +int hostapd_remove_iface(struct hapd_interfaces *ifaces, char *buf); /* utils.c */ int hostapd_register_probereq_cb(struct hostapd_data *hapd, int (*cb)(void *ctx, const u8 *sa, - const u8 *ie, size_t ie_len), + const u8 *da, const u8 *bssid, + const u8 *ie, size_t ie_len, + int ssi_signal), void *ctx); void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr); /* drv_callbacks.c (TODO: move to somewhere else?) */ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, - const u8 *ie, size_t ielen); + const u8 *ie, size_t ielen, int reassoc); void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr); +void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr); +int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da, + const u8 *bssid, const u8 *ie, size_t ie_len, + int ssi_signal); +void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, + int offset); + +const struct hostapd_eap_user * +hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity, + size_t identity_len, int phase2); #endif /* HOSTAPD_H */ diff --git a/src/ap/hs20.c b/src/ap/hs20.c new file mode 100644 index 0000000000000..45d518bc1e552 --- /dev/null +++ b/src/ap/hs20.c @@ -0,0 +1,31 @@ +/* + * Hotspot 2.0 AP ANQP processing + * Copyright (c) 2009, Atheros Communications, Inc. + * 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 "hostapd.h" +#include "ap_config.h" +#include "hs20.h" + + +u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid) +{ + if (!hapd->conf->hs20) + return eid; + *eid++ = WLAN_EID_VENDOR_SPECIFIC; + *eid++ = 5; + WPA_PUT_BE24(eid, OUI_WFA); + eid += 3; + *eid++ = HS20_INDICATION_OUI_TYPE; + /* Hotspot Configuration: DGAF Enabled */ + *eid++ = hapd->conf->disable_dgaf ? 0x01 : 0x00; + return eid; +} diff --git a/src/ap/hs20.h b/src/ap/hs20.h new file mode 100644 index 0000000000000..98698ce2fd667 --- /dev/null +++ b/src/ap/hs20.h @@ -0,0 +1,16 @@ +/* + * Hotspot 2.0 AP ANQP processing + * 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 HS20_H +#define HS20_H + +struct hostapd_data; + +u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid); + +#endif /* HS20_H */ diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c index 0159c725180e6..923b698230701 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-2009, 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 @@ -101,8 +101,8 @@ int hostapd_get_hw_features(struct hostapd_iface *iface) } -static int hostapd_prepare_rates(struct hostapd_data *hapd, - struct hostapd_hw_modes *mode) +int hostapd_prepare_rates(struct hostapd_iface *iface, + struct hostapd_hw_modes *mode) { int i, num_basic_rates = 0; int basic_rates_a[] = { 60, 120, 240, -1 }; @@ -110,8 +110,8 @@ static int hostapd_prepare_rates(struct hostapd_data *hapd, int basic_rates_g[] = { 10, 20, 55, 110, -1 }; int *basic_rates; - if (hapd->iconf->basic_rates) - basic_rates = hapd->iconf->basic_rates; + if (iface->conf->basic_rates) + basic_rates = iface->conf->basic_rates; else switch (mode->mode) { case HOSTAPD_MODE_IEEE80211A: basic_rates = basic_rates_a; @@ -122,22 +122,28 @@ static int hostapd_prepare_rates(struct hostapd_data *hapd, case HOSTAPD_MODE_IEEE80211G: basic_rates = basic_rates_g; break; + case HOSTAPD_MODE_IEEE80211AD: + return 0; /* No basic rates for 11ad */ default: return -1; } - if (hostapd_set_rate_sets(hapd, hapd->iconf->supported_rates, - basic_rates, mode->mode)) { - wpa_printf(MSG_ERROR, "Failed to update rate sets in kernel " - "module"); - } + i = 0; + while (basic_rates[i] >= 0) + i++; + if (i) + i++; /* -1 termination */ + os_free(iface->basic_rates); + iface->basic_rates = os_malloc(i * sizeof(int)); + if (iface->basic_rates) + os_memcpy(iface->basic_rates, basic_rates, i * sizeof(int)); - os_free(hapd->iface->current_rates); - hapd->iface->num_rates = 0; + os_free(iface->current_rates); + iface->num_rates = 0; - hapd->iface->current_rates = - os_zalloc(mode->num_rates * sizeof(struct hostapd_rate_data)); - if (!hapd->iface->current_rates) { + iface->current_rates = + os_calloc(mode->num_rates, sizeof(struct hostapd_rate_data)); + if (!iface->current_rates) { wpa_printf(MSG_ERROR, "Failed to allocate memory for rate " "table."); return -1; @@ -146,26 +152,27 @@ static int hostapd_prepare_rates(struct hostapd_data *hapd, for (i = 0; i < mode->num_rates; i++) { struct hostapd_rate_data *rate; - if (hapd->iconf->supported_rates && - !hostapd_rate_found(hapd->iconf->supported_rates, + if (iface->conf->supported_rates && + !hostapd_rate_found(iface->conf->supported_rates, mode->rates[i])) continue; - rate = &hapd->iface->current_rates[hapd->iface->num_rates]; + rate = &iface->current_rates[iface->num_rates]; rate->rate = mode->rates[i]; if (hostapd_rate_found(basic_rates, rate->rate)) { rate->flags |= HOSTAPD_RATE_BASIC; num_basic_rates++; } wpa_printf(MSG_DEBUG, "RATE[%d] rate=%d flags=0x%x", - hapd->iface->num_rates, rate->rate, rate->flags); - hapd->iface->num_rates++; + iface->num_rates, rate->rate, rate->flags); + iface->num_rates++; } - if (hapd->iface->num_rates == 0 || num_basic_rates == 0) { + if ((iface->num_rates == 0 || num_basic_rates == 0) && + (!iface->conf->ieee80211n || !iface->conf->require_ht)) { wpa_printf(MSG_ERROR, "No rates remaining in supported/basic " "rate sets (%d,%d).", - hapd->iface->num_rates, num_basic_rates); + iface->num_rates, num_basic_rates); return -1; } @@ -265,11 +272,11 @@ static void ieee80211n_get_pri_sec_chan(struct wpa_scan_res *bss, oper = (struct ieee80211_ht_operation *) elems.ht_operation; *pri_chan = oper->control_chan; if (oper->ht_param & HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH) { - if (oper->ht_param & - HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) + int sec = oper->ht_param & + HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK; + if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) *sec_chan = *pri_chan + 4; - else if (oper->ht_param & - HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW) + else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW) *sec_chan = *pri_chan - 4; } } @@ -406,27 +413,14 @@ static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface, } -static void wpa_scan_results_free(struct wpa_scan_results *res) -{ - size_t i; - - if (res == NULL) - return; - - for (i = 0; i < res->num; i++) - os_free(res->res[i]); - os_free(res->res); - os_free(res); -} - - static void ieee80211n_check_scan(struct hostapd_iface *iface) { struct wpa_scan_results *scan_res; int oper40; + int res; /* Check list of neighboring BSSes (from scan) to see whether 40 MHz is - * allowed per IEEE 802.11n/D7.0, 11.14.3.2 */ + * allowed per IEEE Std 802.11-2012, 10.15.3.2 */ iface->scan_cb = NULL; @@ -452,7 +446,48 @@ static void ieee80211n_check_scan(struct hostapd_iface *iface) iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; } - hostapd_setup_interface_complete(iface, 0); + res = ieee80211n_allowed_ht40_channel_pair(iface); + hostapd_setup_interface_complete(iface, !res); +} + + +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_calloc(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; + } } @@ -466,12 +501,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; @@ -483,9 +521,6 @@ static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface) u16 hw = iface->current_mode->ht_capab; u16 conf = iface->conf->ht_capab; - if (!iface->conf->ieee80211n) - return 1; - if ((conf & HT_CAP_INFO_LDPC_CODING_CAP) && !(hw & HT_CAP_INFO_LDPC_CODING_CAP)) { wpa_printf(MSG_ERROR, "Driver does not support configured " @@ -585,13 +620,15 @@ int hostapd_check_ht_capab(struct hostapd_iface *iface) { #ifdef CONFIG_IEEE80211N int ret; + if (!iface->conf->ieee80211n) + return 0; + if (!ieee80211n_supported_ht_capab(iface)) + return -1; ret = ieee80211n_check_40mhz(iface); if (ret) return ret; if (!ieee80211n_allowed_ht40_channel_pair(iface)) return -1; - if (!ieee80211n_supported_ht_capab(iface)) - return -1; #endif /* CONFIG_IEEE80211N */ return 0; @@ -601,7 +638,7 @@ int hostapd_check_ht_capab(struct hostapd_iface *iface) /** * hostapd_select_hw_mode - Select the hardware mode * @iface: Pointer to interface data. - * Returns: 0 on success, -1 on failure + * Returns: 0 on success, < 0 on failure * * Sets up the hardware mode, channel, rates, and passive scanning * based on the configuration. @@ -628,18 +665,58 @@ int hostapd_select_hw_mode(struct hostapd_iface *iface) hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_WARNING, "Hardware does not support configured mode " - "(%d)", (int) iface->conf->hw_mode); - return -1; + "(%d) (hw_mode in hostapd.conf)", + (int) iface->conf->hw_mode); + return -2; } ok = 0; for (j = 0; j < iface->current_mode->num_channels; j++) { struct hostapd_channel_data *chan = &iface->current_mode->channels[j]; - if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && - (chan->chan == iface->conf->channel)) { - ok = 1; - break; + if (chan->chan == iface->conf->channel) { + if (chan->flag & HOSTAPD_CHAN_DISABLED) { + wpa_printf(MSG_ERROR, + "channel [%i] (%i) is disabled for " + "use in AP mode, flags: 0x%x%s%s%s", + j, chan->chan, chan->flag, + chan->flag & HOSTAPD_CHAN_NO_IBSS ? + " NO-IBSS" : "", + chan->flag & + HOSTAPD_CHAN_PASSIVE_SCAN ? + " PASSIVE-SCAN" : "", + chan->flag & HOSTAPD_CHAN_RADAR ? + " RADAR" : ""); + } else { + ok = 1; + break; + } + } + } + if (ok && iface->conf->secondary_channel) { + int sec_ok = 0; + int sec_chan = iface->conf->channel + + iface->conf->secondary_channel * 4; + for (j = 0; j < iface->current_mode->num_channels; j++) { + struct hostapd_channel_data *chan = + &iface->current_mode->channels[j]; + if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && + (chan->chan == sec_chan)) { + sec_ok = 1; + break; + } + } + if (!sec_ok) { + hostapd_logger(iface->bss[0], NULL, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "Configured HT40 secondary channel " + "(%d) not found from the channel list " + "of current mode (%d) %s", + sec_chan, iface->current_mode->mode, + hostapd_hw_mode_txt( + iface->current_mode->mode)); + ok = 0; } } if (iface->conf->channel == 0) { @@ -647,7 +724,7 @@ int hostapd_select_hw_mode(struct hostapd_iface *iface) * the channel automatically */ wpa_printf(MSG_ERROR, "Channel not configured " "(hw_mode/channel in hostapd.conf)"); - return -1; + return -3; } if (ok == 0 && iface->conf->channel != 0) { hostapd_logger(iface->bss[0], NULL, @@ -665,15 +742,7 @@ int hostapd_select_hw_mode(struct hostapd_iface *iface) hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_WARNING, "Hardware does not support configured channel"); - return -1; - } - - if (hostapd_prepare_rates(iface->bss[0], iface->current_mode)) { - wpa_printf(MSG_ERROR, "Failed to prepare rates table."); - hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_WARNING, - "Failed to prepare rates table."); - return -1; + return -4; } return 0; @@ -689,6 +758,8 @@ const char * hostapd_hw_mode_txt(int mode) return "IEEE 802.11b"; case HOSTAPD_MODE_IEEE80211G: return "IEEE 802.11g"; + case HOSTAPD_MODE_IEEE80211AD: + return "IEEE 802.11ad"; default: return "UNKNOWN"; } diff --git a/src/ap/hw_features.h b/src/ap/hw_features.h index 0295549fe1d38..abadcd137db1e 100644 --- a/src/ap/hw_features.h +++ b/src/ap/hw_features.h @@ -2,6 +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> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -25,6 +26,8 @@ const char * hostapd_hw_mode_txt(int mode); int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan); int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq); int hostapd_check_ht_capab(struct hostapd_iface *iface); +int hostapd_prepare_rates(struct hostapd_iface *iface, + struct hostapd_hw_modes *mode); #else /* NEED_AP_MLME */ static inline void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, @@ -39,7 +42,7 @@ static inline int hostapd_get_hw_features(struct hostapd_iface *iface) static inline int hostapd_select_hw_mode(struct hostapd_iface *iface) { - return -1; + return -100; } static inline const char * hostapd_hw_mode_txt(int mode) @@ -57,6 +60,12 @@ static inline int hostapd_check_ht_capab(struct hostapd_iface *iface) return 0; } +static inline int hostapd_prepare_rates(struct hostapd_iface *iface, + struct hostapd_hw_modes *mode) +{ + return 0; +} + #endif /* NEED_AP_MLME */ #endif /* HW_FEATURES_H */ diff --git a/src/ap/iapp.c b/src/ap/iapp.c index 115d91e8ce30c..be55c695672ce 100644 --- a/src/ap/iapp.c +++ b/src/ap/iapp.c @@ -2,14 +2,8 @@ * hostapd / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP) * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * Note: IEEE 802.11F-2003 was a experimental use specification. It has expired * and IEEE has withdrawn it. In other words, it is likely better to look at diff --git a/src/ap/iapp.h b/src/ap/iapp.h index 5fc01cb703550..c22118342af44 100644 --- a/src/ap/iapp.h +++ b/src/ap/iapp.h @@ -2,14 +2,8 @@ * hostapd / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP) * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef IAPP_H diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 3375aa2a3cf8c..51c8d286d8687 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -1,15 +1,9 @@ /* * hostapd / IEEE 802.11 Management - * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-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 - * published 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 "utils/includes.h" @@ -25,6 +19,7 @@ #include "common/wpa_ctrl.h" #include "radius/radius.h" #include "radius/radius_client.h" +#include "p2p/p2p.h" #include "wps/wps.h" #include "hostapd.h" #include "beacon.h" @@ -37,6 +32,9 @@ #include "accounting.h" #include "ap_config.h" #include "ap_mlme.h" +#include "p2p_hostapd.h" +#include "ap_drv_ops.h" +#include "wnm_ap.h" #include "ieee802_11.h" @@ -50,6 +48,10 @@ u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) *pos++ = WLAN_EID_SUPP_RATES; num = hapd->iface->num_rates; + if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) + num++; + if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) + num++; if (num > 8) { /* rest of the rates are encoded in Extended supported * rates element */ @@ -67,6 +69,16 @@ u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) pos++; } + if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && count < 8) { + count++; + *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY; + } + + if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht && count < 8) { + count++; + *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY; + } + return pos; } @@ -80,6 +92,10 @@ u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid) return eid; num = hapd->iface->num_rates; + if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) + num++; + if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) + num++; if (num <= 8) return eid; num -= 8; @@ -98,6 +114,18 @@ u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid) pos++; } + if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) { + count++; + if (count > 8) + *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY; + } + + if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) { + count++; + if (count > 8) + *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY; + } + return pos; } @@ -148,34 +176,6 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, } -#ifdef CONFIG_IEEE80211W -static u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd, - struct sta_info *sta, u8 *eid) -{ - u8 *pos = eid; - u32 timeout, tu; - struct os_time now, passed; - - *pos++ = WLAN_EID_TIMEOUT_INTERVAL; - *pos++ = 5; - *pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK; - os_get_time(&now); - os_time_sub(&now, &sta->sa_query_start, &passed); - tu = (passed.sec * 1000000 + passed.usec) / 1024; - if (hapd->conf->assoc_sa_query_max_timeout > tu) - timeout = hapd->conf->assoc_sa_query_max_timeout - tu; - else - timeout = 0; - if (timeout < hapd->conf->assoc_sa_query_max_timeout) - timeout++; /* add some extra time for local timers */ - WPA_PUT_LE32(pos, timeout); - pos += 4; - - return pos; -} -#endif /* CONFIG_IEEE80211W */ - - void ieee802_11_print_ssid(char *buf, const u8 *ssid, u8 len) { int i; @@ -204,15 +204,15 @@ static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta, if (!sta->challenge) { /* Generate a pseudo-random challenge */ u8 key[8]; - time_t now; + struct os_time now; int r; sta->challenge = os_zalloc(WLAN_AUTH_CHALLENGE_LEN); if (sta->challenge == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; - now = time(NULL); - r = random(); - os_memcpy(key, &now, 4); + os_get_time(&now); + r = os_random(); + os_memcpy(key, &now.sec, 4); os_memcpy(key + 4, &r, 4); rc4_skip(key, sizeof(key), 0, sta->challenge, WLAN_AUTH_CHALLENGE_LEN); @@ -282,7 +282,7 @@ static void send_auth_reply(struct hostapd_data *hapd, " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu)", MAC2STR(dst), auth_alg, auth_transaction, resp, (unsigned long) ies_len); - if (hapd->drv.send_mgmt_frame(hapd, reply, rlen) < 0) + if (hostapd_drv_send_mlme(hapd, reply, rlen, 0) < 0) perror("send_auth_reply: send"); os_free(buf); @@ -315,6 +315,142 @@ static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid, #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_SAE + +static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(2); + if (buf == NULL) + return NULL; + + wpabuf_put_le16(buf, 19); /* Finite Cyclic Group */ + /* TODO: Anti-Clogging Token (if requested) */ + /* TODO: Scalar */ + /* TODO: Element */ + + return buf; +} + + +static struct wpabuf * auth_build_sae_confirm(struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(2); + if (buf == NULL) + return NULL; + + wpabuf_put_le16(buf, sta->sae_send_confirm); + sta->sae_send_confirm++; + /* TODO: Confirm */ + + return buf; +} + + +static u16 handle_sae_commit(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *data, size_t len) +{ + wpa_hexdump(MSG_DEBUG, "SAE commit fields", data, len); + + /* Check Finite Cyclic Group */ + if (len < 2) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + if (WPA_GET_LE16(data) != 19) { + wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u", + WPA_GET_LE16(data)); + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + } + + return WLAN_STATUS_SUCCESS; +} + + +static u16 handle_sae_confirm(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *data, size_t len) +{ + u16 rc; + + wpa_hexdump(MSG_DEBUG, "SAE confirm fields", data, len); + + if (len < 2) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + rc = WPA_GET_LE16(data); + wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", rc); + + return WLAN_STATUS_SUCCESS; +} + + +static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, + const struct ieee80211_mgmt *mgmt, size_t len, + u8 auth_transaction) +{ + u16 resp = WLAN_STATUS_SUCCESS; + struct wpabuf *data; + + if (auth_transaction == 1) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "start SAE authentication (RX commit)"); + resp = handle_sae_commit(hapd, sta, mgmt->u.auth.variable, + ((u8 *) mgmt) + len - + mgmt->u.auth.variable); + if (resp == WLAN_STATUS_SUCCESS) + sta->sae_state = SAE_COMMIT; + } else if (auth_transaction == 2) { + if (sta->sae_state != SAE_COMMIT) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "SAE confirm before commit"); + resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; + } + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "SAE authentication (RX confirm)"); + resp = handle_sae_confirm(hapd, sta, mgmt->u.auth.variable, + ((u8 *) mgmt) + len - + mgmt->u.auth.variable); + if (resp == WLAN_STATUS_SUCCESS) { + sta->flags |= WLAN_STA_AUTH; + wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); + sta->auth_alg = WLAN_AUTH_SAE; + mlme_authenticate_indication(hapd, sta); + } + } else { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "unexpected SAE authentication transaction %u", + auth_transaction); + resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; + } + + sta->auth_alg = WLAN_AUTH_SAE; + + if (resp == WLAN_STATUS_SUCCESS) { + if (auth_transaction == 1) + data = auth_build_sae_commit(hapd, sta); + else + data = auth_build_sae_confirm(hapd, sta); + if (data == NULL) + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + } else + data = NULL; + + send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, + auth_transaction, resp, + data ? wpabuf_head(data) : (u8 *) "", + data ? wpabuf_len(data) : 0); + wpabuf_free(data); +} +#endif /* CONFIG_SAE */ + + static void handle_auth(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len) { @@ -326,8 +462,11 @@ static void handle_auth(struct hostapd_data *hapd, const u8 *challenge = NULL; u32 session_timeout, acct_interim_interval; int vlan_id = 0; + struct hostapd_sta_wpa_psk_short *psk = NULL; u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN]; size_t resp_ies_len = 0; + char *identity = NULL; + char *radius_cui = NULL; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { printf("handle_auth - too short payload (len=%lu)\n", @@ -360,11 +499,13 @@ static void handle_auth(struct hostapd_data *hapd, if (!(((hapd->conf->auth_algs & WPA_AUTH_ALG_OPEN) && auth_alg == WLAN_AUTH_OPEN) || #ifdef CONFIG_IEEE80211R - (hapd->conf->wpa && - (hapd->conf->wpa_key_mgmt & - (WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_FT_PSK)) && + (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) && auth_alg == WLAN_AUTH_FT) || #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_SAE + (hapd->conf->wpa && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) && + auth_alg == WLAN_AUTH_SAE) || +#endif /* CONFIG_SAE */ ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) && auth_alg == WLAN_AUTH_SHARED_KEY))) { printf("Unsupported authentication algorithm (%d)\n", @@ -373,7 +514,7 @@ static void handle_auth(struct hostapd_data *hapd, goto fail; } - if (!(auth_transaction == 1 || + if (!(auth_transaction == 1 || auth_alg == WLAN_AUTH_SAE || (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 3))) { printf("Unknown authentication transaction number (%d)\n", auth_transaction); @@ -390,7 +531,9 @@ static void handle_auth(struct hostapd_data *hapd, res = hostapd_allowed_address(hapd, mgmt->sa, (u8 *) mgmt, len, &session_timeout, - &acct_interim_interval, &vlan_id); + &acct_interim_interval, &vlan_id, + &psk, &identity, &radius_cui); + if (res == HOSTAPD_ACL_REJECT) { printf("Station " MACSTR " not allowed to authenticate.\n", MAC2STR(mgmt->sa)); @@ -428,6 +571,19 @@ static void handle_auth(struct hostapd_data *hapd, HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id); } + hostapd_free_psk_list(sta->psk); + if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) { + sta->psk = psk; + psk = NULL; + } else { + sta->psk = NULL; + } + + sta->identity = identity; + identity = NULL; + sta->radius_cui = radius_cui; + radius_cui = NULL; + sta->flags &= ~WLAN_STA_PREAUTH; ieee802_1x_notify_pre_auth(sta->eapol_sm, 0); @@ -486,9 +642,18 @@ static void handle_auth(struct hostapd_data *hapd, /* handle_auth_ft_finish() callback will complete auth. */ return; #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_SAE + case WLAN_AUTH_SAE: + handle_auth_sae(hapd, sta, mgmt, len, auth_transaction); + return; +#endif /* CONFIG_SAE */ } fail: + os_free(identity); + os_free(radius_cui); + hostapd_free_psk_list(psk); + send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg, auth_transaction + 1, resp, resp_ies, resp_ies_len); } @@ -552,15 +717,22 @@ static u16 check_wmm(struct hostapd_data *hapd, struct sta_info *sta, const u8 *wmm_ie, size_t wmm_ie_len) { sta->flags &= ~WLAN_STA_WMM; + sta->qosinfo = 0; if (wmm_ie && hapd->conf->wmm_enabled) { - if (hostapd_eid_wmm_valid(hapd, wmm_ie, wmm_ie_len)) + struct wmm_information_element *wmm; + + if (!hostapd_eid_wmm_valid(hapd, wmm_ie, wmm_ie_len)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, HOSTAPD_LEVEL_DEBUG, "invalid WMM element in association " "request"); - else - sta->flags |= WLAN_STA_WMM; + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + sta->flags |= WLAN_STA_WMM; + wmm = (struct wmm_information_element *) wmm_ie; + sta->qosinfo = wmm->qos_info; } return WLAN_STATUS_SUCCESS; } @@ -576,35 +748,20 @@ static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta, return WLAN_STATUS_UNSPECIFIED_FAILURE; } - if (elems->supp_rates_len > sizeof(sta->supported_rates)) { + if (elems->supp_rates_len + elems->ext_supp_rates_len > + sizeof(sta->supported_rates)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, - "Invalid supported rates element length %d", - elems->supp_rates_len); + "Invalid supported rates element length %d+%d", + elems->supp_rates_len, + elems->ext_supp_rates_len); return WLAN_STATUS_UNSPECIFIED_FAILURE; } - os_memset(sta->supported_rates, 0, sizeof(sta->supported_rates)); - os_memcpy(sta->supported_rates, elems->supp_rates, - elems->supp_rates_len); - sta->supported_rates_len = elems->supp_rates_len; - - if (elems->ext_supp_rates) { - if (elems->supp_rates_len + elems->ext_supp_rates_len > - sizeof(sta->supported_rates)) { - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, - "Invalid supported rates element length" - " %d+%d", elems->supp_rates_len, - elems->ext_supp_rates_len); - return WLAN_STATUS_UNSPECIFIED_FAILURE; - } - - os_memcpy(sta->supported_rates + elems->supp_rates_len, - elems->ext_supp_rates, elems->ext_supp_rates_len); - sta->supported_rates_len += elems->ext_supp_rates_len; - } + sta->supported_rates_len = merge_byte_arrays( + sta->supported_rates, sizeof(sta->supported_rates), + elems->supp_rates, elems->supp_rates_len, + elems->ext_supp_rates, elems->ext_supp_rates_len); return WLAN_STATUS_SUCCESS; } @@ -635,12 +792,33 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, if (resp != WLAN_STATUS_SUCCESS) return resp; #ifdef CONFIG_IEEE80211N - resp = copy_sta_ht_capab(sta, elems.ht_capabilities, + resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities, elems.ht_capabilities_len); if (resp != WLAN_STATUS_SUCCESS) return resp; + if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && + !(sta->flags & WLAN_STA_HT)) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "Station does not support " + "mandatory HT PHY - reject association"); + return WLAN_STATUS_ASSOC_DENIED_NO_HT; + } #endif /* CONFIG_IEEE80211N */ +#ifdef CONFIG_IEEE80211AC + resp = copy_sta_vht_capab(hapd, sta, elems.vht_capabilities, + elems.vht_capabilities_len); + if (resp != WLAN_STATUS_SUCCESS) + return resp; + if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht && + !(sta->flags & WLAN_STA_VHT)) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "Station does not support " + "mandatory VHT PHY - reject association"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } +#endif /* CONFIG_IEEE80211AC */ + if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems.rsn_ie) { wpa_ie = elems.rsn_ie; wpa_ie_len = elems.rsn_ie_len; @@ -654,7 +832,7 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, } #ifdef CONFIG_WPS - sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS); + sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2); if (hapd->conf->wps_state && elems.wps_ie) { wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)Association " "Request - assume WPS is used"); @@ -662,8 +840,17 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, wpabuf_free(sta->wps_ie); sta->wps_ie = ieee802_11_vendor_ie_concat(ies, ies_len, WPS_IE_VENDOR_TYPE); + if (sta->wps_ie && wps_is_20(sta->wps_ie)) { + wpa_printf(MSG_DEBUG, "WPS: STA supports WPS 2.0"); + sta->flags |= WLAN_STA_WPS2; + } wpa_ie = NULL; wpa_ie_len = 0; + if (sta->wps_ie && wps_validate_assoc_req(sta->wps_ie) < 0) { + wpa_printf(MSG_DEBUG, "WPS: Invalid WPS IE in " + "(Re)Association Request - reject"); + return WLAN_STATUS_INVALID_IE; + } } else if (hapd->conf->wps_state && wpa_ie == NULL) { wpa_printf(MSG_DEBUG, "STA did not include WPA/RSN IE in " "(Re)Association Request - possible WPS use"); @@ -754,8 +941,18 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_SAE + if (wpa_auth_uses_sae(sta->wpa_sm) && + sta->auth_alg != WLAN_AUTH_SAE) { + wpa_printf(MSG_DEBUG, "SAE: " MACSTR " tried to use " + "SAE AKM after non-SAE auth_alg %u", + MAC2STR(sta->addr), sta->auth_alg); + return WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; + } +#endif /* CONFIG_SAE */ + #ifdef CONFIG_IEEE80211N - if ((sta->flags & WLAN_STA_HT) && + if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) && wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, @@ -768,6 +965,29 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, } else wpa_auth_sta_no_wpa(sta->wpa_sm); +#ifdef CONFIG_P2P + if (elems.p2p) { + wpabuf_free(sta->p2p_ie); + sta->p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len, + P2P_IE_VENDOR_TYPE); + + } else { + wpabuf_free(sta->p2p_ie); + sta->p2p_ie = NULL; + } + + p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len); +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_HS20 + wpabuf_free(sta->hs20_ie); + if (elems.hs20 && elems.hs20_len > 4) { + sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4, + elems.hs20_len - 4); + } else + sta->hs20_ie = NULL; +#endif /* CONFIG_HS20 */ + return WLAN_STATUS_SUCCESS; } @@ -788,7 +1008,7 @@ static void send_deauth(struct hostapd_data *hapd, const u8 *addr, send_len = IEEE80211_HDRLEN + sizeof(reply.u.deauth); reply.u.deauth.reason_code = host_to_le16(reason_code); - if (hapd->drv.send_mgmt_frame(hapd, &reply, send_len) < 0) + if (hostapd_drv_send_mlme(hapd, &reply, send_len, 0) < 0) wpa_printf(MSG_INFO, "Failed to send deauth: %s", strerror(errno)); } @@ -845,11 +1065,20 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, p = hostapd_eid_ht_operation(hapd, p); #endif /* CONFIG_IEEE80211N */ +#ifdef CONFIG_IEEE80211AC + p = hostapd_eid_vht_capabilities(hapd, p); + p = hostapd_eid_vht_operation(hapd, p); +#endif /* CONFIG_IEEE80211AC */ + + 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); #ifdef CONFIG_WPS - if (sta->flags & WLAN_STA_WPS) { + if ((sta->flags & WLAN_STA_WPS) || + ((sta->flags & WLAN_STA_MAYBE_WPS) && hapd->conf->wpa)) { struct wpabuf *wps = wps_build_assoc_resp_ie(); if (wps) { os_memcpy(p, wpabuf_head(wps), wpabuf_len(wps)); @@ -859,9 +1088,39 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P + if (sta->p2p_ie) { + struct wpabuf *p2p_resp_ie; + enum p2p_status_code status; + switch (status_code) { + case WLAN_STATUS_SUCCESS: + status = P2P_SC_SUCCESS; + break; + case WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA: + status = P2P_SC_FAIL_LIMIT_REACHED; + break; + default: + status = P2P_SC_FAIL_INVALID_PARAMS; + break; + } + p2p_resp_ie = p2p_group_assoc_resp_ie(hapd->p2p_group, status); + if (p2p_resp_ie) { + os_memcpy(p, wpabuf_head(p2p_resp_ie), + wpabuf_len(p2p_resp_ie)); + p += wpabuf_len(p2p_resp_ie); + wpabuf_free(p2p_resp_ie); + } + } +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_P2P_MANAGER + if (hapd->conf->p2p & P2P_MANAGE) + p = hostapd_eid_p2p_manage(hapd, p); +#endif /* CONFIG_P2P_MANAGER */ + send_len += p - reply->u.assoc_resp.variable; - if (hapd->drv.send_mgmt_frame(hapd, reply, send_len) < 0) + if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0) wpa_printf(MSG_INFO, "Failed to send assoc resp: %s", strerror(errno)); } @@ -1005,6 +1264,7 @@ static void handle_assoc(struct hostapd_data *hapd, "association OK (aid %d)", sta->aid); /* Station will be marked associated, after it acknowledges AssocResp */ + sta->flags |= WLAN_STA_ASSOC_REQ_OK; #ifdef CONFIG_IEEE80211W if ((sta->flags & WLAN_STA_MFP) && sta->sa_query_timed_out) { @@ -1061,9 +1321,8 @@ static void handle_disassoc(struct hostapd_data *hapd, return; } - sta->flags &= ~WLAN_STA_ASSOC; - wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED MACSTR, - MAC2STR(sta->addr)); + ap_sta_set_authorized(hapd, sta, 0); + sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "disassociated"); @@ -1073,7 +1332,7 @@ static void handle_disassoc(struct hostapd_data *hapd, * authenticated. */ accounting_sta_stop(hapd, sta); ieee802_1x_free_station(sta); - hapd->drv.sta_remove(hapd, sta->addr); + hostapd_drv_sta_remove(hapd, sta->addr); if (sta->timeout_next == STA_NULLFUNC || sta->timeout_next == STA_DISASSOC) { @@ -1094,26 +1353,26 @@ static void handle_deauth(struct hostapd_data *hapd, struct sta_info *sta; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.deauth)) { - printf("handle_deauth - too short payload (len=%lu)\n", - (unsigned long) len); + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "handle_deauth - too short " + "payload (len=%lu)", (unsigned long) len); return; } - wpa_printf(MSG_DEBUG, "deauthentication: STA=" MACSTR - " reason_code=%d", - MAC2STR(mgmt->sa), - le_to_host16(mgmt->u.deauth.reason_code)); + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "deauthentication: STA=" MACSTR + " reason_code=%d", + MAC2STR(mgmt->sa), le_to_host16(mgmt->u.deauth.reason_code)); sta = ap_get_sta(hapd, mgmt->sa); if (sta == NULL) { - printf("Station " MACSTR " trying to deauthenticate, but it " - "is not authenticated.\n", MAC2STR(mgmt->sa)); + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR " trying " + "to deauthenticate, but it is not authenticated", + MAC2STR(mgmt->sa)); return; } - sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); - wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED MACSTR, - MAC2STR(sta->addr)); + ap_sta_set_authorized(hapd, sta, 0); + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | + WLAN_STA_ASSOC_REQ_OK); wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "deauthenticated"); @@ -1148,81 +1407,11 @@ static void handle_beacon(struct hostapd_data *hapd, #ifdef CONFIG_IEEE80211W -/* MLME-SAQuery.request */ -void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, - const u8 *addr, const u8 *trans_id) -{ - struct ieee80211_mgmt mgmt; - u8 *end; - - wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to " - MACSTR, MAC2STR(addr)); - wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", - trans_id, WLAN_SA_QUERY_TR_ID_LEN); - - os_memset(&mgmt, 0, sizeof(mgmt)); - mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_ACTION); - os_memcpy(mgmt.da, addr, ETH_ALEN); - os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); - os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN); - mgmt.u.action.category = WLAN_ACTION_SA_QUERY; - mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST; - os_memcpy(mgmt.u.action.u.sa_query_req.trans_id, trans_id, - WLAN_SA_QUERY_TR_ID_LEN); - end = mgmt.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN; - if (hapd->drv.send_mgmt_frame(hapd, &mgmt, end - (u8 *) &mgmt) < 0) - perror("ieee802_11_send_sa_query_req: send"); -} - - -static void hostapd_sa_query_request(struct hostapd_data *hapd, - const struct ieee80211_mgmt *mgmt) -{ - struct sta_info *sta; - struct ieee80211_mgmt resp; - u8 *end; - - wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from " - MACSTR, MAC2STR(mgmt->sa)); - wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", - mgmt->u.action.u.sa_query_resp.trans_id, - WLAN_SA_QUERY_TR_ID_LEN); - - sta = ap_get_sta(hapd, mgmt->sa); - if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) { - wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignore SA Query Request " - "from unassociated STA " MACSTR, MAC2STR(mgmt->sa)); - return; - } - - wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to " - MACSTR, MAC2STR(mgmt->sa)); - - os_memset(&resp, 0, sizeof(resp)); - resp.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_ACTION); - os_memcpy(resp.da, mgmt->sa, ETH_ALEN); - os_memcpy(resp.sa, hapd->own_addr, ETH_ALEN); - os_memcpy(resp.bssid, hapd->own_addr, ETH_ALEN); - resp.u.action.category = WLAN_ACTION_SA_QUERY; - resp.u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE; - os_memcpy(resp.u.action.u.sa_query_req.trans_id, - mgmt->u.action.u.sa_query_req.trans_id, - WLAN_SA_QUERY_TR_ID_LEN); - end = resp.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN; - if (hapd->drv.send_mgmt_frame(hapd, &resp, end - (u8 *) &resp) < 0) - perror("hostapd_sa_query_request: send"); -} - - static void hostapd_sa_query_action(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len) { - struct sta_info *sta; const u8 *end; - int i; end = mgmt->u.action.u.sa_query_resp.trans_id + WLAN_SA_QUERY_TR_ID_LEN; @@ -1232,50 +1421,9 @@ static void hostapd_sa_query_action(struct hostapd_data *hapd, return; } - if (mgmt->u.action.u.sa_query_resp.action == WLAN_SA_QUERY_REQUEST) { - hostapd_sa_query_request(hapd, mgmt); - return; - } - - if (mgmt->u.action.u.sa_query_resp.action != WLAN_SA_QUERY_RESPONSE) { - wpa_printf(MSG_DEBUG, "IEEE 802.11: Unexpected SA Query " - "Action %d", mgmt->u.action.u.sa_query_resp.action); - return; - } - - wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Response from " - MACSTR, MAC2STR(mgmt->sa)); - wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", - mgmt->u.action.u.sa_query_resp.trans_id, - WLAN_SA_QUERY_TR_ID_LEN); - - /* MLME-SAQuery.confirm */ - - sta = ap_get_sta(hapd, mgmt->sa); - if (sta == NULL || sta->sa_query_trans_id == NULL) { - wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with " - "pending SA Query request found"); - return; - } - - for (i = 0; i < sta->sa_query_count; i++) { - if (os_memcmp(sta->sa_query_trans_id + - i * WLAN_SA_QUERY_TR_ID_LEN, - mgmt->u.action.u.sa_query_resp.trans_id, - WLAN_SA_QUERY_TR_ID_LEN) == 0) - break; - } - - if (i >= sta->sa_query_count) { - wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching SA Query " - "transaction identifier found"); - return; - } - - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, - "Reply to pending SA Query received"); - ap_sta_stop_sa_query(hapd, sta); + ieee802_11_sa_query_action(hapd, mgmt->sa, + mgmt->u.action.u.sa_query_resp.action, + mgmt->u.action.u.sa_query_resp.trans_id); } @@ -1287,10 +1435,32 @@ static int robust_action_frame(u8 category) #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_WNM +static void hostapd_wnm_action(struct hostapd_data *hapd, struct sta_info *sta, + const struct ieee80211_mgmt *mgmt, + size_t len) +{ + struct rx_action action; + if (len < IEEE80211_HDRLEN + 2) + return; + os_memset(&action, 0, sizeof(action)); + action.da = mgmt->da; + action.sa = mgmt->sa; + action.bssid = mgmt->bssid; + action.category = mgmt->u.action.category; + action.data = (const u8 *) &mgmt->u.action.u.wnm_sleep_req.action; + action.len = len - IEEE80211_HDRLEN - 1; + action.freq = hapd->iface->freq; + ieee802_11_rx_wnm_action_ap(hapd, &action); +} +#endif /* CONFIG_WNM */ + + static void handle_action(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len) { struct sta_info *sta; + sta = ap_get_sta(hapd, mgmt->sa); if (len < IEEE80211_HDRLEN + 1) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, @@ -1300,7 +1470,14 @@ static void handle_action(struct hostapd_data *hapd, return; } - sta = ap_get_sta(hapd, mgmt->sa); + if (mgmt->u.action.category != WLAN_ACTION_PUBLIC && + (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored Action " + "frame (category=%u) from unassociated STA " MACSTR, + MAC2STR(mgmt->sa), mgmt->u.action.category); + return; + } + #ifdef CONFIG_IEEE80211W if (sta && (sta->flags & WLAN_STA_MFP) && !(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP) && @@ -1316,20 +1493,10 @@ static void handle_action(struct hostapd_data *hapd, switch (mgmt->u.action.category) { #ifdef CONFIG_IEEE80211R case WLAN_ACTION_FT: - { - if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) { - wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored FT Action " - "frame from unassociated STA " MACSTR, - MAC2STR(mgmt->sa)); - return; - } - if (wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action, len - IEEE80211_HDRLEN)) break; - return; - } #endif /* CONFIG_IEEE80211R */ case WLAN_ACTION_WMM: hostapd_wmm_action(hapd, mgmt, len); @@ -1339,6 +1506,11 @@ static void handle_action(struct hostapd_data *hapd, hostapd_sa_query_action(hapd, mgmt, len); return; #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_WNM + case WLAN_ACTION_WNM: + hostapd_wnm_action(hapd, sta, mgmt, len); + return; +#endif /* CONFIG_WNM */ case WLAN_ACTION_PUBLIC: if (hapd->public_action_cb) { hapd->public_action_cb(hapd->public_action_cb_ctx, @@ -1347,6 +1519,14 @@ static void handle_action(struct hostapd_data *hapd, return; } break; + case WLAN_ACTION_VENDOR_SPECIFIC: + if (hapd->vendor_action_cb) { + if (hapd->vendor_action_cb(hapd->vendor_action_cb_ctx, + (u8 *) mgmt, len, + hapd->iface->freq) == 0) + return; + } + break; } hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, @@ -1374,7 +1554,10 @@ static void handle_action(struct hostapd_data *hapd, os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN); resp->u.action.category |= 0x80; - hapd->drv.send_mgmt_frame(hapd, resp, len); + if (hostapd_drv_send_mlme(hapd, resp, len, 0) < 0) { + wpa_printf(MSG_ERROR, "IEEE 802.11: Failed to send " + "Action frame"); + } os_free(resp); } } @@ -1400,6 +1583,9 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, int broadcast; u16 fc, stype; + if (len < 24) + return; + mgmt = (struct ieee80211_mgmt *) buf; fc = le_to_host16(mgmt->frame_control); stype = WLAN_FC_GET_STYPE(fc); @@ -1414,6 +1600,11 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, mgmt->bssid[4] == 0xff && mgmt->bssid[5] == 0xff; if (!broadcast && +#ifdef CONFIG_P2P + /* Invitation responses can be sent with the peer MAC as BSSID */ + !((hapd->conf->p2p & P2P_GROUP_OWNER) && + stype == WLAN_FC_STYPE_ACTION) && +#endif /* CONFIG_P2P */ os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) { printf("MGMT: BSSID=" MACSTR " not our address\n", MAC2STR(mgmt->bssid)); @@ -1422,7 +1613,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; } @@ -1452,7 +1643,7 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, handle_disassoc(hapd, mgmt, len); break; case WLAN_FC_STYPE_DEAUTH: - wpa_printf(MSG_DEBUG, "mgmt::deauth"); + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "mgmt::deauth"); handle_deauth(hapd, mgmt, len); break; case WLAN_FC_STYPE_ACTION: @@ -1518,13 +1709,6 @@ static void handle_assoc_cb(struct hostapd_data *hapd, int new_assoc = 1; struct ieee80211_ht_capabilities ht_cap; - if (!ok) { - hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, - "did not acknowledge association response"); - return; - } - if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) : sizeof(mgmt->u.assoc_resp))) { printf("handle_assoc_cb(reassoc=%d) - too short payload " @@ -1532,11 +1716,6 @@ static void handle_assoc_cb(struct hostapd_data *hapd, return; } - if (reassoc) - status = le_to_host16(mgmt->u.reassoc_resp.status_code); - else - status = le_to_host16(mgmt->u.assoc_resp.status_code); - sta = ap_get_sta(hapd, mgmt->da); if (!sta) { printf("handle_assoc_cb: STA " MACSTR " not found\n", @@ -1544,6 +1723,19 @@ static void handle_assoc_cb(struct hostapd_data *hapd, return; } + if (!ok) { + hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "did not acknowledge association response"); + sta->flags &= ~WLAN_STA_ASSOC_REQ_OK; + return; + } + + if (reassoc) + status = le_to_host16(mgmt->u.reassoc_resp.status_code); + else + status = le_to_host16(mgmt->u.assoc_resp.status_code); + if (status != WLAN_STATUS_SUCCESS) goto fail; @@ -1565,9 +1757,7 @@ static void handle_assoc_cb(struct hostapd_data *hapd, * Open, static WEP, or FT protocol; no separate authorization * step. */ - sta->flags |= WLAN_STA_AUTHORIZED; - wpa_msg(hapd->msg_ctx, MSG_INFO, - AP_STA_CONNECTED MACSTR, MAC2STR(sta->addr)); + ap_sta_set_authorized(hapd, sta, 1); } if (reassoc) @@ -1584,22 +1774,31 @@ static void handle_assoc_cb(struct hostapd_data *hapd, * cleared and configuration gets updated in case of reassociation back * to the same AP. */ - hapd->drv.sta_remove(hapd, sta->addr); + hostapd_drv_sta_remove(hapd, sta->addr); #ifdef CONFIG_IEEE80211N if (sta->flags & WLAN_STA_HT) hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap); #endif /* CONFIG_IEEE80211N */ - if (hapd->drv.sta_add(hapd, sta->addr, sta->aid, sta->capability, - sta->supported_rates, sta->supported_rates_len, - sta->listen_interval, - sta->flags & WLAN_STA_HT ? &ht_cap : NULL)) { + if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability, + sta->supported_rates, sta->supported_rates_len, + sta->listen_interval, + sta->flags & WLAN_STA_HT ? &ht_cap : NULL, + sta->flags, sta->qosinfo)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, "Could not add STA to kernel driver"); + + ap_sta_disconnect(hapd, sta, sta->addr, + WLAN_REASON_DISASSOC_AP_BUSY); + + goto fail; } + if (sta->flags & WLAN_STA_WDS) + hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 1); + if (sta->eapol_sm == NULL) { /* * This STA does not use RADIUS server for EAP authentication, @@ -1614,7 +1813,7 @@ static void handle_assoc_cb(struct hostapd_data *hapd, goto fail; } - hapd->drv.set_sta_flags(hapd, sta); + hostapd_set_sta_flags(hapd, sta); if (sta->auth_alg == WLAN_AUTH_FT) wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT); @@ -1633,6 +1832,54 @@ static void handle_assoc_cb(struct hostapd_data *hapd, } +static void handle_deauth_cb(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, + size_t len, int ok) +{ + struct sta_info *sta; + if (mgmt->da[0] & 0x01) + return; + sta = ap_get_sta(hapd, mgmt->da); + if (!sta) { + wpa_printf(MSG_DEBUG, "handle_deauth_cb: STA " MACSTR + " not found", MAC2STR(mgmt->da)); + return; + } + if (ok) + wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged deauth", + MAC2STR(sta->addr)); + else + wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge " + "deauth", MAC2STR(sta->addr)); + + ap_sta_deauth_cb(hapd, sta); +} + + +static void handle_disassoc_cb(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, + size_t len, int ok) +{ + struct sta_info *sta; + if (mgmt->da[0] & 0x01) + return; + sta = ap_get_sta(hapd, mgmt->da); + if (!sta) { + wpa_printf(MSG_DEBUG, "handle_disassoc_cb: STA " MACSTR + " not found", MAC2STR(mgmt->da)); + return; + } + if (ok) + wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged disassoc", + MAC2STR(sta->addr)); + else + wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge " + "disassoc", MAC2STR(sta->addr)); + + ap_sta_disassoc_cb(hapd, sta); +} + + /** * ieee802_11_mgmt_cb - Process management frame TX status callback * @hapd: hostapd BSS data structure (the BSS from which the management frame @@ -1662,10 +1909,15 @@ void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len, handle_assoc_cb(hapd, mgmt, len, 1, ok); break; case WLAN_FC_STYPE_PROBE_RESP: - wpa_printf(MSG_DEBUG, "mgmt::proberesp cb"); + wpa_printf(MSG_EXCESSIVE, "mgmt::proberesp cb"); break; case WLAN_FC_STYPE_DEAUTH: - /* ignore */ + wpa_printf(MSG_DEBUG, "mgmt::deauth cb"); + handle_deauth_cb(hapd, mgmt, len, ok); + break; + case WLAN_FC_STYPE_DISASSOC: + wpa_printf(MSG_DEBUG, "mgmt::disassoc cb"); + handle_disassoc_cb(hapd, mgmt, len, ok); break; case WLAN_FC_STYPE_ACTION: wpa_printf(MSG_DEBUG, "mgmt::action cb"); @@ -1708,7 +1960,7 @@ void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr, break; } } - if (sta == NULL) + if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) return; if (sta->flags & WLAN_STA_PENDING_POLL) { wpa_printf(MSG_DEBUG, "STA " MACSTR " %s pending " @@ -1722,6 +1974,59 @@ void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr, } +void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst, + const u8 *data, size_t len, int ack) +{ + struct sta_info *sta; + struct hostapd_iface *iface = hapd->iface; + + sta = ap_get_sta(hapd, dst); + if (sta == NULL && iface->num_bss > 1) { + size_t j; + for (j = 0; j < iface->num_bss; j++) { + hapd = iface->bss[j]; + sta = ap_get_sta(hapd, dst); + if (sta) + break; + } + } + if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) { + wpa_printf(MSG_DEBUG, "Ignore TX status for Data frame to STA " + MACSTR " that is not currently associated", + MAC2STR(dst)); + return; + } + + ieee802_1x_eapol_tx_status(hapd, sta, data, len, ack); +} + + +void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr) +{ + struct sta_info *sta; + struct hostapd_iface *iface = hapd->iface; + + sta = ap_get_sta(hapd, addr); + if (sta == NULL && iface->num_bss > 1) { + size_t j; + for (j = 0; j < iface->num_bss; j++) { + hapd = iface->bss[j]; + sta = ap_get_sta(hapd, addr); + if (sta) + break; + } + } + if (sta == NULL) + return; + if (!(sta->flags & WLAN_STA_PENDING_POLL)) + return; + + wpa_printf(MSG_DEBUG, "STA " MACSTR " ACKed pending " + "activity poll", MAC2STR(sta->addr)); + sta->flags &= ~WLAN_STA_PENDING_POLL; +} + + void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src, int wds) { @@ -1729,24 +2034,40 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src, sta = ap_get_sta(hapd, src); if (sta && (sta->flags & WLAN_STA_ASSOC)) { + if (!hapd->conf->wds_sta) + return; + if (wds && !(sta->flags & WLAN_STA_WDS)) { wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for " "STA " MACSTR " (aid %u)", MAC2STR(sta->addr), sta->aid); sta->flags |= WLAN_STA_WDS; - hapd->drv.set_wds_sta(hapd, sta->addr, sta->aid, 1); + hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 1); } return; } wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA " MACSTR, MAC2STR(src)); + if (src[0] & 0x01) { + /* Broadcast bit set in SA?! Ignore the frame silently. */ + return; + } + + if (sta && (sta->flags & WLAN_STA_ASSOC_REQ_OK)) { + wpa_printf(MSG_DEBUG, "Association Response to the STA has " + "already been sent, but no TX status yet known - " + "ignore Class 3 frame issue with " MACSTR, + MAC2STR(src)); + return; + } + if (sta && (sta->flags & WLAN_STA_AUTH)) - hapd->drv.sta_disassoc( + hostapd_drv_sta_disassoc( hapd, src, WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); else - hapd->drv.sta_deauth( + hostapd_drv_sta_deauth( hapd, src, WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); } diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h index cfc069c8094ff..1e5800d09c76b 100644 --- a/src/ap/ieee802_11.h +++ b/src/ap/ieee802_11.h @@ -2,14 +2,8 @@ * hostapd / IEEE 802.11 Management * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef IEEE802_11_H @@ -46,22 +40,42 @@ static inline int ieee802_11_get_mib_sta(struct hostapd_data *hapd, #endif /* NEED_AP_MLME */ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, int probe); +u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid); +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); void hostapd_get_ht_capab(struct hostapd_data *hapd, struct ieee80211_ht_capabilities *ht_cap, struct ieee80211_ht_capabilities *neg_ht_cap); -u16 copy_sta_ht_capab(struct sta_info *sta, const u8 *ht_capab, - size_t ht_capab_len); +u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *ht_capab, size_t ht_capab_len); void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta); +u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *vht_capab, size_t vht_capab_len); void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr, const u8 *buf, size_t len, int ack); +void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst, + const u8 *data, size_t len, int ack); void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src, int wds); +u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd, + struct sta_info *sta, u8 *eid); +void ieee802_11_sa_query_action(struct hostapd_data *hapd, + const u8 *sa, const u8 action_type, + const u8 *trans_id); +u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid); +int hostapd_update_time_adv(struct hostapd_data *hapd); +void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr); +u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid); #endif /* IEEE802_11_H */ diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c index dec56d1e1a1e0..c311e55949e3d 100644 --- a/src/ap/ieee802_11_auth.c +++ b/src/ap/ieee802_11_auth.c @@ -1,15 +1,9 @@ /* * hostapd / IEEE 802.11 authentication (ACL) - * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-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 - * published 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. * * Access control list for IEEE 802.11 authentication can uses statically * configured ACL from configuration files or an external RADIUS server. @@ -21,29 +15,35 @@ #include "utils/common.h" #include "utils/eloop.h" +#include "crypto/sha1.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "hostapd.h" #include "ap_config.h" +#include "ap_drv_ops.h" #include "ieee802_11.h" +#include "ieee802_1x.h" #include "ieee802_11_auth.h" #define RADIUS_ACL_TIMEOUT 30 struct hostapd_cached_radius_acl { - time_t timestamp; + os_time_t timestamp; macaddr addr; int accepted; /* HOSTAPD_ACL_* */ struct hostapd_cached_radius_acl *next; u32 session_timeout; u32 acct_interim_interval; int vlan_id; + struct hostapd_sta_wpa_psk_short *psk; + char *identity; + char *radius_cui; }; struct hostapd_acl_query_data { - time_t timestamp; + os_time_t timestamp; u8 radius_id; macaddr addr; u8 *auth_msg; /* IEEE 802.11 authentication frame from station */ @@ -53,6 +53,15 @@ struct hostapd_acl_query_data { #ifndef CONFIG_NO_RADIUS +static void hostapd_acl_cache_free_entry(struct hostapd_cached_radius_acl *e) +{ + os_free(e->identity); + os_free(e->radius_cui); + hostapd_free_psk_list(e->psk); + os_free(e); +} + + static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache) { struct hostapd_cached_radius_acl *prev; @@ -60,38 +69,73 @@ static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache) while (acl_cache) { prev = acl_cache; acl_cache = acl_cache->next; - os_free(prev); + hostapd_acl_cache_free_entry(prev); } } +static void copy_psk_list(struct hostapd_sta_wpa_psk_short **psk, + struct hostapd_sta_wpa_psk_short *src) +{ + struct hostapd_sta_wpa_psk_short **copy_to; + struct hostapd_sta_wpa_psk_short *copy_from; + + /* Copy PSK linked list */ + copy_to = psk; + copy_from = src; + while (copy_from && copy_to) { + *copy_to = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short)); + if (*copy_to == NULL) + break; + os_memcpy(*copy_to, copy_from, + sizeof(struct hostapd_sta_wpa_psk_short)); + copy_from = copy_from->next; + copy_to = &((*copy_to)->next); + } + if (copy_to) + *copy_to = NULL; +} + + static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr, u32 *session_timeout, - u32 *acct_interim_interval, int *vlan_id) + u32 *acct_interim_interval, int *vlan_id, + struct hostapd_sta_wpa_psk_short **psk, + char **identity, char **radius_cui) { struct hostapd_cached_radius_acl *entry; - time_t now; + struct os_time now; - time(&now); - entry = hapd->acl_cache; + os_get_time(&now); - while (entry) { - if (os_memcmp(entry->addr, addr, ETH_ALEN) == 0) { - if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) - return -1; /* entry has expired */ - if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT) - if (session_timeout) - *session_timeout = - entry->session_timeout; - if (acct_interim_interval) - *acct_interim_interval = - entry->acct_interim_interval; - if (vlan_id) - *vlan_id = entry->vlan_id; - return entry->accepted; - } + for (entry = hapd->acl_cache; entry; entry = entry->next) { + if (os_memcmp(entry->addr, addr, ETH_ALEN) != 0) + continue; - entry = entry->next; + if (now.sec - entry->timestamp > RADIUS_ACL_TIMEOUT) + return -1; /* entry has expired */ + if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT) + if (session_timeout) + *session_timeout = entry->session_timeout; + if (acct_interim_interval) + *acct_interim_interval = + entry->acct_interim_interval; + if (vlan_id) + *vlan_id = entry->vlan_id; + copy_psk_list(psk, entry->psk); + if (identity) { + if (entry->identity) + *identity = os_strdup(entry->identity); + else + *identity = NULL; + } + if (radius_cui) { + if (entry->radius_cui) + *radius_cui = os_strdup(entry->radius_cui); + else + *radius_cui = NULL; + } + return entry->accepted; } return -1; @@ -137,37 +181,9 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, goto fail; } - if (hapd->conf->own_ip_addr.af == AF_INET && - !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, - (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) { - wpa_printf(MSG_DEBUG, "Could not add NAS-IP-Address"); + if (add_common_radius_attr(hapd, hapd->conf->radius_auth_req_attr, + NULL, msg) < 0) goto fail; - } - -#ifdef CONFIG_IPV6 - if (hapd->conf->own_ip_addr.af == AF_INET6 && - !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS, - (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) { - wpa_printf(MSG_DEBUG, "Could not add NAS-IPv6-Address"); - goto fail; - } -#endif /* CONFIG_IPV6 */ - - if (hapd->conf->nas_identifier && - !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, - (u8 *) hapd->conf->nas_identifier, - os_strlen(hapd->conf->nas_identifier))) { - wpa_printf(MSG_DEBUG, "Could not add NAS-Identifier"); - goto fail; - } - - os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", - MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid); - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, - (u8 *) buf, os_strlen(buf))) { - wpa_printf(MSG_DEBUG, "Could not add Called-Station-Id"); - goto fail; - } os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, MAC2STR(addr)); @@ -177,12 +193,6 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, goto fail; } - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE, - RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { - wpa_printf(MSG_DEBUG, "Could not add NAS-Port-Type"); - goto fail; - } - os_snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b"); if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, (u8 *) buf, os_strlen(buf))) { @@ -190,7 +200,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: @@ -209,11 +220,19 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, * @session_timeout: Buffer for returning session timeout (from RADIUS) * @acct_interim_interval: Buffer for returning account interval (from RADIUS) * @vlan_id: Buffer for returning VLAN ID + * @psk: Linked list buffer for returning WPA PSK + * @identity: Buffer for returning identity (from RADIUS) + * @radius_cui: Buffer for returning CUI (from RADIUS) * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING + * + * The caller is responsible for freeing the returned *identity and *radius_cui + * values with os_free(). */ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, const u8 *msg, size_t len, u32 *session_timeout, - u32 *acct_interim_interval, int *vlan_id) + u32 *acct_interim_interval, int *vlan_id, + struct hostapd_sta_wpa_psk_short **psk, + char **identity, char **radius_cui) { if (session_timeout) *session_timeout = 0; @@ -221,6 +240,12 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, *acct_interim_interval = 0; if (vlan_id) *vlan_id = 0; + if (psk) + *psk = NULL; + if (identity) + *identity = NULL; + if (radius_cui) + *radius_cui = NULL; if (hostapd_maclist_found(hapd->conf->accept_mac, hapd->conf->num_accept_mac, addr, vlan_id)) @@ -240,11 +265,13 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, return HOSTAPD_ACL_REJECT; #else /* CONFIG_NO_RADIUS */ struct hostapd_acl_query_data *query; + struct os_time t; /* Check whether ACL cache has an entry for this station */ int res = hostapd_acl_cache_get(hapd, addr, session_timeout, acct_interim_interval, - vlan_id); + vlan_id, psk, + identity, radius_cui); if (res == HOSTAPD_ACL_ACCEPT || res == HOSTAPD_ACL_ACCEPT_TIMEOUT) return res; @@ -256,6 +283,14 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, if (os_memcmp(query->addr, addr, ETH_ALEN) == 0) { /* pending query in RADIUS retransmit queue; * do not generate a new one */ + if (identity) { + os_free(*identity); + *identity = NULL; + } + if (radius_cui) { + os_free(*radius_cui); + *radius_cui = NULL; + } return HOSTAPD_ACL_PENDING; } query = query->next; @@ -270,7 +305,8 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, wpa_printf(MSG_ERROR, "malloc for query data failed"); return HOSTAPD_ACL_REJECT; } - time(&query->timestamp); + os_get_time(&t); + query->timestamp = t.sec; os_memcpy(query->addr, addr, ETH_ALEN); if (hostapd_radius_acl_query(hapd, addr, query)) { wpa_printf(MSG_DEBUG, "Failed to send Access-Request " @@ -302,7 +338,7 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, #ifndef CONFIG_NO_RADIUS -static void hostapd_acl_expire_cache(struct hostapd_data *hapd, time_t now) +static void hostapd_acl_expire_cache(struct hostapd_data *hapd, os_time_t now) { struct hostapd_cached_radius_acl *prev, *entry, *tmp; @@ -317,12 +353,10 @@ static void hostapd_acl_expire_cache(struct hostapd_data *hapd, time_t now) prev->next = entry->next; else hapd->acl_cache = entry->next; -#ifdef CONFIG_DRIVER_RADIUS_ACL - hapd->drv.set_radius_acl_expire(hapd, entry->addr); -#endif /* CONFIG_DRIVER_RADIUS_ACL */ + hostapd_drv_set_radius_acl_expire(hapd, entry->addr); tmp = entry; entry = entry->next; - os_free(tmp); + hostapd_acl_cache_free_entry(tmp); continue; } @@ -332,7 +366,8 @@ static void hostapd_acl_expire_cache(struct hostapd_data *hapd, time_t now) } -static void hostapd_acl_expire_queries(struct hostapd_data *hapd, time_t now) +static void hostapd_acl_expire_queries(struct hostapd_data *hapd, + os_time_t now) { struct hostapd_acl_query_data *prev, *entry, *tmp; @@ -368,16 +403,64 @@ static void hostapd_acl_expire_queries(struct hostapd_data *hapd, time_t now) static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx) { struct hostapd_data *hapd = eloop_ctx; - time_t now; + struct os_time now; - time(&now); - hostapd_acl_expire_cache(hapd, now); - hostapd_acl_expire_queries(hapd, now); + os_get_time(&now); + hostapd_acl_expire_cache(hapd, now.sec); + hostapd_acl_expire_queries(hapd, now.sec); eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL); } +static void decode_tunnel_passwords(struct hostapd_data *hapd, + const u8 *shared_secret, + size_t shared_secret_len, + struct radius_msg *msg, + struct radius_msg *req, + struct hostapd_cached_radius_acl *cache) +{ + int passphraselen; + char *passphrase, *strpassphrase; + size_t i; + struct hostapd_sta_wpa_psk_short *psk; + + /* + * Decode all tunnel passwords as PSK and save them into a linked list. + */ + for (i = 0; ; i++) { + passphrase = radius_msg_get_tunnel_password( + msg, &passphraselen, shared_secret, shared_secret_len, + req, i); + /* + * Passphrase is NULL iff there is no i-th Tunnel-Password + * attribute in msg. + */ + if (passphrase == NULL) + break; + /* + * passphrase does not contain the NULL termination. + * Add it here as pbkdf2_sha1() requires it. + */ + strpassphrase = os_zalloc(passphraselen + 1); + psk = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short)); + if (strpassphrase && psk) { + os_memcpy(strpassphrase, passphrase, passphraselen); + pbkdf2_sha1(strpassphrase, + hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len, 4096, + psk->psk, PMK_LEN); + psk->next = cache->psk; + cache->psk = psk; + psk = NULL; + } + os_free(strpassphrase); + os_free(psk); + os_free(passphrase); + } +} + + /** * hostapd_acl_recv_radius - Process incoming RADIUS Authentication messages * @msg: RADIUS response message @@ -397,6 +480,7 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, struct hostapd_acl_query_data *query, *prev; struct hostapd_cached_radius_acl *cache; struct radius_hdr *hdr = radius_msg_get_hdr(msg); + struct os_time t; query = hapd->acl_queries; prev = NULL; @@ -431,9 +515,13 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry"); goto done; } - time(&cache->timestamp); + os_get_time(&t); + cache->timestamp = t.sec; os_memcpy(cache->addr, query->addr, sizeof(cache->addr)); if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) { + u8 *buf; + size_t len; + if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT, &cache->session_timeout) == 0) cache->accepted = HOSTAPD_ACL_ACCEPT_TIMEOUT; @@ -452,14 +540,35 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, } cache->vlan_id = radius_msg_get_vlanid(msg); + + decode_tunnel_passwords(hapd, shared_secret, shared_secret_len, + msg, req, cache); + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, + &buf, &len, NULL) == 0) { + cache->identity = os_zalloc(len + 1); + if (cache->identity) + os_memcpy(cache->identity, buf, len); + } + if (radius_msg_get_attr_ptr( + msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, + &buf, &len, NULL) == 0) { + cache->radius_cui = os_zalloc(len + 1); + if (cache->radius_cui) + os_memcpy(cache->radius_cui, buf, len); + } + + if (hapd->conf->wpa_psk_radius == PSK_RADIUS_REQUIRED && + !cache->psk) + cache->accepted = HOSTAPD_ACL_REJECT; } else cache->accepted = HOSTAPD_ACL_REJECT; cache->next = hapd->acl_cache; hapd->acl_cache = cache; #ifdef CONFIG_DRIVER_RADIUS_ACL - hapd->drv.set_radius_acl_auth(hapd, query->addr, cache->accepted, - cache->session_timeout); + hostapd_drv_set_radius_acl_auth(hapd, query->addr, cache->accepted, + cache->session_timeout); #else /* CONFIG_DRIVER_RADIUS_ACL */ #ifdef NEED_AP_MLME /* Re-send original authentication frame for 802.11 processing */ @@ -522,3 +631,13 @@ void hostapd_acl_deinit(struct hostapd_data *hapd) hostapd_acl_query_free(prev); } } + + +void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk) +{ + while (psk) { + struct hostapd_sta_wpa_psk_short *prev = psk; + psk = psk->next; + os_free(prev); + } +} diff --git a/src/ap/ieee802_11_auth.h b/src/ap/ieee802_11_auth.h index b2971e5092b52..2bc1065a226c9 100644 --- a/src/ap/ieee802_11_auth.h +++ b/src/ap/ieee802_11_auth.h @@ -2,14 +2,8 @@ * hostapd / IEEE 802.11 authentication (ACL) * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef IEEE802_11_AUTH_H @@ -24,8 +18,11 @@ enum { int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, const u8 *msg, size_t len, u32 *session_timeout, - u32 *acct_interim_interval, int *vlan_id); + u32 *acct_interim_interval, int *vlan_id, + struct hostapd_sta_wpa_psk_short **psk, + char **identity, char **radius_cui); int hostapd_acl_init(struct hostapd_data *hapd); void hostapd_acl_deinit(struct hostapd_data *hapd); +void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk); #endif /* IEEE802_11_AUTH_H */ diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c index 7541b83816d02..6c3696f5e3698 100644 --- a/src/ap/ieee802_11_ht.c +++ b/src/ap/ieee802_11_ht.c @@ -30,7 +30,8 @@ u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid) struct ieee80211_ht_capabilities *cap; u8 *pos = eid; - if (!hapd->iconf->ieee80211n || !hapd->iface->current_mode) + if (!hapd->iconf->ieee80211n || !hapd->iface->current_mode || + hapd->conf->disable_11n) return eid; *pos++ = WLAN_EID_HT_CAP; @@ -58,7 +59,7 @@ u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid) struct ieee80211_ht_operation *oper; u8 *pos = eid; - if (!hapd->iconf->ieee80211n) + if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n) return eid; *pos++ = WLAN_EID_HT_OPERATION; @@ -92,7 +93,6 @@ Set to 1 (HT non-member protection) if there may be non-HT STAs Set to 2 if only HT STAs are associated in BSS, however and at least one 20 MHz HT STA is associated Set to 3 (HT mixed mode) when one or more non-HT STAs are associated - (currently non-GF HT station is considered as non-HT STA also) */ int hostapd_ht_operation_update(struct hostapd_iface *iface) { @@ -130,13 +130,8 @@ int hostapd_ht_operation_update(struct hostapd_iface *iface) op_mode_changes++; } - /* Note: currently we switch to the MIXED op mode if HT non-greenfield - * station is associated. Probably it's a theoretical case, since - * it looks like all known HT STAs support greenfield. - */ new_op_mode = 0; - if (iface->num_sta_no_ht || - (iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT)) + if (iface->num_sta_no_ht) new_op_mode = OP_MODE_MIXED; else if ((iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && iface->num_sta_ht_20mhz) @@ -160,11 +155,13 @@ int hostapd_ht_operation_update(struct hostapd_iface *iface) } -u16 copy_sta_ht_capab(struct sta_info *sta, const u8 *ht_capab, - size_t ht_capab_len) +u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *ht_capab, size_t ht_capab_len) { + /* Disable HT caps for STAs associated to no-HT BSSes. */ if (!ht_capab || - ht_capab_len < sizeof(struct ieee80211_ht_capabilities)) { + ht_capab_len < sizeof(struct ieee80211_ht_capabilities) || + hapd->conf->disable_11n) { sta->flags &= ~WLAN_STA_HT; os_free(sta->ht_capabilities); sta->ht_capabilities = NULL; @@ -253,8 +250,14 @@ void hostapd_get_ht_capab(struct hostapd_data *hapd, return; os_memcpy(neg_ht_cap, ht_cap, sizeof(*neg_ht_cap)); cap = le_to_host16(neg_ht_cap->ht_capabilities_info); - cap &= hapd->iconf->ht_capab; - cap |= (hapd->iconf->ht_capab & HT_CAP_INFO_SMPS_DISABLED); + + /* + * Mask out HT features we don't support, but don't overwrite + * non-symmetric features like STBC and SMPS. Just because + * we're not in dynamic SMPS mode the STA might still be. + */ + cap &= (hapd->iconf->ht_capab | HT_CAP_INFO_RX_STBC_MASK | + HT_CAP_INFO_TX_STBC | HT_CAP_INFO_SMPS_MASK); /* * STBC needs to be handled specially diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c new file mode 100644 index 0000000000000..76f78a7dbd9c1 --- /dev/null +++ b/src/ap/ieee802_11_shared.c @@ -0,0 +1,458 @@ +/* + * hostapd / IEEE 802.11 Management + * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "hostapd.h" +#include "sta_info.h" +#include "ap_config.h" +#include "ap_drv_ops.h" +#include "ieee802_11.h" + + +#ifdef CONFIG_IEEE80211W + +u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd, + struct sta_info *sta, u8 *eid) +{ + u8 *pos = eid; + u32 timeout, tu; + struct os_time now, passed; + + *pos++ = WLAN_EID_TIMEOUT_INTERVAL; + *pos++ = 5; + *pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK; + os_get_time(&now); + os_time_sub(&now, &sta->sa_query_start, &passed); + tu = (passed.sec * 1000000 + passed.usec) / 1024; + if (hapd->conf->assoc_sa_query_max_timeout > tu) + timeout = hapd->conf->assoc_sa_query_max_timeout - tu; + else + timeout = 0; + if (timeout < hapd->conf->assoc_sa_query_max_timeout) + timeout++; /* add some extra time for local timers */ + WPA_PUT_LE32(pos, timeout); + pos += 4; + + return pos; +} + + +/* MLME-SAQuery.request */ +void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, + const u8 *addr, const u8 *trans_id) +{ + struct ieee80211_mgmt mgmt; + u8 *end; + + wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to " + MACSTR, MAC2STR(addr)); + wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", + trans_id, WLAN_SA_QUERY_TR_ID_LEN); + + os_memset(&mgmt, 0, sizeof(mgmt)); + mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(mgmt.da, addr, ETH_ALEN); + os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); + os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN); + mgmt.u.action.category = WLAN_ACTION_SA_QUERY; + mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST; + os_memcpy(mgmt.u.action.u.sa_query_req.trans_id, trans_id, + WLAN_SA_QUERY_TR_ID_LEN); + end = mgmt.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN; + if (hostapd_drv_send_mlme(hapd, &mgmt, end - (u8 *) &mgmt, 0) < 0) + perror("ieee802_11_send_sa_query_req: send"); +} + + +static void ieee802_11_send_sa_query_resp(struct hostapd_data *hapd, + const u8 *sa, const u8 *trans_id) +{ + struct sta_info *sta; + struct ieee80211_mgmt resp; + u8 *end; + + wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from " + MACSTR, MAC2STR(sa)); + wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", + trans_id, WLAN_SA_QUERY_TR_ID_LEN); + + sta = ap_get_sta(hapd, sa); + if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignore SA Query Request " + "from unassociated STA " MACSTR, MAC2STR(sa)); + return; + } + + wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to " + MACSTR, MAC2STR(sa)); + + os_memset(&resp, 0, sizeof(resp)); + resp.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(resp.da, sa, ETH_ALEN); + os_memcpy(resp.sa, hapd->own_addr, ETH_ALEN); + os_memcpy(resp.bssid, hapd->own_addr, ETH_ALEN); + resp.u.action.category = WLAN_ACTION_SA_QUERY; + resp.u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE; + os_memcpy(resp.u.action.u.sa_query_req.trans_id, trans_id, + WLAN_SA_QUERY_TR_ID_LEN); + end = resp.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN; + if (hostapd_drv_send_mlme(hapd, &resp, end - (u8 *) &resp, 0) < 0) + perror("ieee80211_mgmt_sa_query_request: send"); +} + + +void ieee802_11_sa_query_action(struct hostapd_data *hapd, const u8 *sa, + const u8 action_type, const u8 *trans_id) +{ + struct sta_info *sta; + int i; + + if (action_type == WLAN_SA_QUERY_REQUEST) { + ieee802_11_send_sa_query_resp(hapd, sa, trans_id); + return; + } + + if (action_type != WLAN_SA_QUERY_RESPONSE) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: Unexpected SA Query " + "Action %d", action_type); + return; + } + + wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Response from " + MACSTR, MAC2STR(sa)); + wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", + trans_id, WLAN_SA_QUERY_TR_ID_LEN); + + /* MLME-SAQuery.confirm */ + + sta = ap_get_sta(hapd, sa); + if (sta == NULL || sta->sa_query_trans_id == NULL) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with " + "pending SA Query request found"); + return; + } + + for (i = 0; i < sta->sa_query_count; i++) { + if (os_memcmp(sta->sa_query_trans_id + + i * WLAN_SA_QUERY_TR_ID_LEN, + trans_id, WLAN_SA_QUERY_TR_ID_LEN) == 0) + break; + } + + if (i >= sta->sa_query_count) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching SA Query " + "transaction identifier found"); + return; + } + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Reply to pending SA Query received"); + ap_sta_stop_sa_query(hapd, sta); +} + +#endif /* CONFIG_IEEE80211W */ + + +u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; + u8 len = 0; + + if (hapd->conf->tdls & (TDLS_PROHIBIT | TDLS_PROHIBIT_CHAN_SWITCH)) + len = 5; + if (len < 4 && hapd->conf->interworking) + len = 4; + if (len < 3 && hapd->conf->wnm_sleep_mode) + len = 3; + if (len < 7 && hapd->conf->ssid.utf8_ssid) + len = 7; +#ifdef CONFIG_WNM + if (len < 4) + len = 4; +#endif /* CONFIG_WNM */ + if (len == 0) + return eid; + + *pos++ = WLAN_EID_EXT_CAPAB; + *pos++ = len; + *pos++ = 0x00; + *pos++ = 0x00; + + *pos = 0x00; + if (hapd->conf->wnm_sleep_mode) + *pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */ + if (hapd->conf->bss_transition) + *pos |= 0x08; /* Bit 19 - BSS Transition */ + pos++; + + if (len < 4) + return pos; + *pos = 0x00; +#ifdef CONFIG_WNM + *pos |= 0x02; /* Bit 25 - SSID List */ +#endif /* CONFIG_WNM */ + if (hapd->conf->time_advertisement == 2) + *pos |= 0x08; /* Bit 27 - UTC TSF Offset */ + if (hapd->conf->interworking) + *pos |= 0x80; /* Bit 31 - Interworking */ + pos++; + + if (len < 5) + return pos; + *pos = 0x00; + if (hapd->conf->tdls & TDLS_PROHIBIT) + *pos |= 0x40; /* Bit 38 - TDLS Prohibited */ + if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH) + *pos |= 0x80; /* Bit 39 - TDLS Channel Switching Prohibited */ + pos++; + + if (len < 6) + return pos; + *pos = 0x00; + pos++; + + if (len < 7) + return pos; + *pos = 0x00; + if (hapd->conf->ssid.utf8_ssid) + *pos |= 0x01; /* Bit 48 - UTF-8 SSID */ + pos++; + + return pos; +} + + +u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; +#ifdef CONFIG_INTERWORKING + u8 *len; + + if (!hapd->conf->interworking) + return eid; + + *pos++ = WLAN_EID_INTERWORKING; + len = pos++; + + *pos = hapd->conf->access_network_type; + if (hapd->conf->internet) + *pos |= INTERWORKING_ANO_INTERNET; + if (hapd->conf->asra) + *pos |= INTERWORKING_ANO_ASRA; + if (hapd->conf->esr) + *pos |= INTERWORKING_ANO_ESR; + if (hapd->conf->uesa) + *pos |= INTERWORKING_ANO_UESA; + pos++; + + if (hapd->conf->venue_info_set) { + *pos++ = hapd->conf->venue_group; + *pos++ = hapd->conf->venue_type; + } + + if (!is_zero_ether_addr(hapd->conf->hessid)) { + os_memcpy(pos, hapd->conf->hessid, ETH_ALEN); + pos += ETH_ALEN; + } + + *len = pos - len - 1; +#endif /* CONFIG_INTERWORKING */ + + return pos; +} + + +u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; +#ifdef CONFIG_INTERWORKING + + /* TODO: Separate configuration for ANQP? */ + if (!hapd->conf->interworking) + return eid; + + *pos++ = WLAN_EID_ADV_PROTO; + *pos++ = 2; + *pos++ = 0x7F; /* Query Response Length Limit | PAME-BI */ + *pos++ = ACCESS_NETWORK_QUERY_PROTOCOL; +#endif /* CONFIG_INTERWORKING */ + + return pos; +} + + +u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; +#ifdef CONFIG_INTERWORKING + u8 *len; + unsigned int i, count; + + if (!hapd->conf->interworking || + hapd->conf->roaming_consortium == NULL || + hapd->conf->roaming_consortium_count == 0) + return eid; + + *pos++ = WLAN_EID_ROAMING_CONSORTIUM; + len = pos++; + + /* Number of ANQP OIs (in addition to the max 3 listed here) */ + if (hapd->conf->roaming_consortium_count > 3 + 255) + *pos++ = 255; + else if (hapd->conf->roaming_consortium_count > 3) + *pos++ = hapd->conf->roaming_consortium_count - 3; + else + *pos++ = 0; + + /* OU #1 and #2 Lengths */ + *pos = hapd->conf->roaming_consortium[0].len; + if (hapd->conf->roaming_consortium_count > 1) + *pos |= hapd->conf->roaming_consortium[1].len << 4; + pos++; + + if (hapd->conf->roaming_consortium_count > 3) + count = 3; + else + count = hapd->conf->roaming_consortium_count; + + for (i = 0; i < count; i++) { + os_memcpy(pos, hapd->conf->roaming_consortium[i].oi, + hapd->conf->roaming_consortium[i].len); + pos += hapd->conf->roaming_consortium[i].len; + } + + *len = pos - len - 1; +#endif /* CONFIG_INTERWORKING */ + + return pos; +} + + +u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid) +{ + if (hapd->conf->time_advertisement != 2) + return eid; + + if (hapd->time_adv == NULL && + hostapd_update_time_adv(hapd) < 0) + return eid; + + if (hapd->time_adv == NULL) + return eid; + + os_memcpy(eid, wpabuf_head(hapd->time_adv), + wpabuf_len(hapd->time_adv)); + eid += wpabuf_len(hapd->time_adv); + + return eid; +} + + +u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid) +{ + size_t len; + + if (hapd->conf->time_advertisement != 2) + return eid; + + len = os_strlen(hapd->conf->time_zone); + + *eid++ = WLAN_EID_TIME_ZONE; + *eid++ = len; + os_memcpy(eid, hapd->conf->time_zone, len); + eid += len; + + return eid; +} + + +int hostapd_update_time_adv(struct hostapd_data *hapd) +{ + const int elen = 2 + 1 + 10 + 5 + 1; + struct os_time t; + struct os_tm tm; + u8 *pos; + + if (hapd->conf->time_advertisement != 2) + return 0; + + if (os_get_time(&t) < 0 || os_gmtime(t.sec, &tm) < 0) + return -1; + + if (!hapd->time_adv) { + hapd->time_adv = wpabuf_alloc(elen); + if (hapd->time_adv == NULL) + return -1; + pos = wpabuf_put(hapd->time_adv, elen); + } else + pos = wpabuf_mhead_u8(hapd->time_adv); + + *pos++ = WLAN_EID_TIME_ADVERTISEMENT; + *pos++ = 1 + 10 + 5 + 1; + + *pos++ = 2; /* UTC time at which the TSF timer is 0 */ + + /* Time Value at TSF 0 */ + /* FIX: need to calculate this based on the current TSF value */ + WPA_PUT_LE16(pos, tm.year); /* Year */ + pos += 2; + *pos++ = tm.month; /* Month */ + *pos++ = tm.day; /* Day of month */ + *pos++ = tm.hour; /* Hours */ + *pos++ = tm.min; /* Minutes */ + *pos++ = tm.sec; /* Seconds */ + WPA_PUT_LE16(pos, 0); /* Milliseconds (not used) */ + pos += 2; + *pos++ = 0; /* Reserved */ + + /* Time Error */ + /* TODO: fill in an estimate on the error */ + *pos++ = 0; + *pos++ = 0; + *pos++ = 0; + *pos++ = 0; + *pos++ = 0; + + *pos++ = hapd->time_update_counter++; + + return 0; +} + + +u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; + +#ifdef CONFIG_WNM + if (hapd->conf->ap_max_inactivity > 0) { + unsigned int val; + *pos++ = WLAN_EID_BSS_MAX_IDLE_PERIOD; + *pos++ = 3; + val = hapd->conf->ap_max_inactivity; + if (val > 68000) + val = 68000; + val *= 1000; + val /= 1024; + if (val == 0) + val = 1; + if (val > 65535) + val = 65535; + WPA_PUT_LE16(pos, val); + pos += 2; + *pos++ = 0x00; /* TODO: Protected Keep-Alive Required */ + } +#endif /* CONFIG_WNM */ + + return pos; +} diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c new file mode 100644 index 0000000000000..b21c2b7fb0d22 --- /dev/null +++ b/src/ap/ieee802_11_vht.c @@ -0,0 +1,110 @@ +/* + * 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)); + + /* + * center freq = 5 GHz + (5 * index) + * So index 42 gives center freq 5.210 GHz + * which is channel 42 in 5G band + */ + oper->vht_op_info_chan_center_freq_seg0_idx = + hapd->iconf->vht_oper_centr_freq_seg0_idx; + oper->vht_op_info_chan_center_freq_seg1_idx = + hapd->iconf->vht_oper_centr_freq_seg1_idx; + + 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 = host_to_le16(0xfffc); + pos += sizeof(*oper); + + return pos; +} + + +u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *vht_capab, size_t vht_capab_len) +{ + /* Disable VHT caps for STAs associated to no-VHT BSSes. */ + if (!vht_capab || + vht_capab_len < sizeof(struct ieee80211_vht_capabilities) || + hapd->conf->disable_11ac) { + sta->flags &= ~WLAN_STA_VHT; + os_free(sta->vht_capabilities); + sta->vht_capabilities = NULL; + return WLAN_STATUS_SUCCESS; + } + + if (sta->vht_capabilities == NULL) { + sta->vht_capabilities = + os_zalloc(sizeof(struct ieee80211_vht_capabilities)); + if (sta->vht_capabilities == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + sta->flags |= WLAN_STA_VHT; + os_memcpy(sta->vht_capabilities, vht_capab, + sizeof(struct ieee80211_vht_capabilities)); + + return WLAN_STATUS_SUCCESS; +} diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c index eb160f8e015c0..d9c21a8b8117f 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -1,15 +1,9 @@ /* * hostapd / IEEE 802.1X-2004 Authenticator - * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-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 - * published 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 "utils/includes.h" @@ -18,14 +12,15 @@ #include "utils/eloop.h" #include "crypto/md5.h" #include "crypto/crypto.h" +#include "crypto/random.h" #include "common/ieee802_11_defs.h" -#include "common/wpa_ctrl.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "eap_server/eap.h" #include "eap_common/eap_wsc_common.h" #include "eapol_auth/eapol_auth_sm.h" #include "eapol_auth/eapol_auth_sm_i.h" +#include "p2p/p2p.h" #include "hostapd.h" #include "accounting.h" #include "sta_info.h" @@ -33,6 +28,7 @@ #include "preauth_auth.h" #include "pmksa_cache_auth.h" #include "ap_config.h" +#include "ap_drv_ops.h" #include "ieee802_1x.h" @@ -70,7 +66,9 @@ static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta, if (sta->flags & WLAN_STA_PREAUTH) { rsn_preauth_send(hapd, sta, buf, len); } else { - hapd->drv.send_eapol(hapd, sta->addr, buf, len, encrypt); + hostapd_drv_hapd_send_eapol( + hapd, sta->addr, buf, len, + encrypt, hostapd_sta_flags_to_drv(sta->flags)); } os_free(buf); @@ -86,21 +84,13 @@ void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, return; if (authorized) { - if (!(sta->flags & WLAN_STA_AUTHORIZED)) - wpa_msg(hapd->msg_ctx, MSG_INFO, - AP_STA_CONNECTED MACSTR, MAC2STR(sta->addr)); - sta->flags |= WLAN_STA_AUTHORIZED; - res = hapd->drv.set_authorized(hapd, sta, 1); + ap_sta_set_authorized(hapd, sta, 1); + res = hostapd_set_authorized(hapd, sta, 1); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "authorizing port"); } else { - if ((sta->flags & (WLAN_STA_AUTHORIZED | WLAN_STA_ASSOC)) == - (WLAN_STA_AUTHORIZED | WLAN_STA_ASSOC)) - wpa_msg(hapd->msg_ctx, MSG_INFO, - AP_STA_DISCONNECTED MACSTR, - MAC2STR(sta->addr)); - sta->flags &= ~WLAN_STA_AUTHORIZED; - res = hapd->drv.set_authorized(hapd, sta, 0); + ap_sta_set_authorized(hapd, sta, 0); + res = hostapd_set_authorized(hapd, sta, 0); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "unauthorizing port"); } @@ -110,8 +100,10 @@ void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, "driver (errno=%d).\n", MAC2STR(sta->addr), errno); } - if (authorized) + if (authorized) { + os_get_time(&sta->connected_time); accounting_sta_start(hapd, sta); + } } @@ -137,10 +129,10 @@ static void ieee802_1x_tx_key_one(struct hostapd_data *hapd, hdr = (struct ieee802_1x_hdr *) buf; key = (struct ieee802_1x_eapol_key *) (hdr + 1); key->type = EAPOL_KEY_TYPE_RC4; - key->key_length = htons(key_len); + WPA_PUT_BE16(key->key_length, key_len); wpa_get_ntp_timestamp(key->replay_counter); - if (os_get_random(key->key_iv, sizeof(key->key_iv))) { + if (random_get_bytes(key->key_iv, sizeof(key->key_iv))) { wpa_printf(MSG_ERROR, "Could not get random numbers"); os_free(buf); return; @@ -215,7 +207,7 @@ ieee802_1x_group_alloc(struct hostapd_data *hapd, const char *ifname) if (!key->key[key->idx]) key->key[key->idx] = os_malloc(key->default_len); if (key->key[key->idx] == NULL || - os_get_random(key->key[key->idx], key->default_len)) { + random_get_bytes(key->key[key->idx], key->default_len)) { printf("Could not generate random WEP key (dynamic VLAN).\n"); os_free(key->key[key->idx]); key->key[key->idx] = NULL; @@ -229,11 +221,13 @@ ieee802_1x_group_alloc(struct hostapd_data *hapd, const char *ifname) wpa_hexdump_key(MSG_DEBUG, "Default WEP key (dynamic VLAN)", key->key[key->idx], key->len[key->idx]); - if (hapd->drv.set_key(ifname, hapd, WPA_ALG_WEP, NULL, key->idx, 1, - NULL, 0, key->key[key->idx], key->len[key->idx])) + if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_WEP, + broadcast_ether_addr, key->idx, 1, + NULL, 0, key->key[key->idx], + key->len[key->idx])) printf("Could not set dynamic VLAN WEP encryption key.\n"); - hapd->drv.set_drv_ieee8021x(hapd, ifname, 1); + hostapd_set_drv_ieee8021x(hapd, ifname, 1); return key; } @@ -330,7 +324,8 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) u8 *ikey; ikey = os_malloc(hapd->conf->individual_wep_key_len); if (ikey == NULL || - os_get_random(ikey, hapd->conf->individual_wep_key_len)) { + random_get_bytes(ikey, hapd->conf->individual_wep_key_len)) + { wpa_printf(MSG_ERROR, "Could not generate random " "individual WEP key."); os_free(ikey); @@ -345,9 +340,9 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) /* TODO: set encryption in TX callback, i.e., only after STA * has ACKed EAPOL-Key frame */ - if (hapd->drv.set_key(hapd->conf->iface, hapd, WPA_ALG_WEP, - sta->addr, 0, 1, NULL, 0, ikey, - hapd->conf->individual_wep_key_len)) { + if (hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_WEP, + sta->addr, 0, 1, NULL, 0, ikey, + hapd->conf->individual_wep_key_len)) { wpa_printf(MSG_ERROR, "Could not set individual WEP " "encryption."); } @@ -360,6 +355,8 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) const char *radius_mode_txt(struct hostapd_data *hapd) { switch (hapd->iface->conf->hw_mode) { + case HOSTAPD_MODE_IEEE80211AD: + return "802.11ad"; case HOSTAPD_MODE_IEEE80211A: return "802.11a"; case HOSTAPD_MODE_IEEE80211G: @@ -417,12 +414,142 @@ static void ieee802_1x_learn_identity(struct hostapd_data *hapd, } +static int add_common_radius_sta_attr(struct hostapd_data *hapd, + struct hostapd_radius_attr *req_attr, + struct sta_info *sta, + struct radius_msg *msg) +{ + char buf[128]; + + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_NAS_PORT) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) { + wpa_printf(MSG_ERROR, "Could not add NAS-Port"); + return -1; + } + + os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, + MAC2STR(sta->addr)); + buf[sizeof(buf) - 1] = '\0'; + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_ERROR, "Could not add Calling-Station-Id"); + return -1; + } + + if (sta->flags & WLAN_STA_PREAUTH) { + os_strlcpy(buf, "IEEE 802.11i Pre-Authentication", + sizeof(buf)); + } else { + os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s", + radius_sta_rate(hapd, sta) / 2, + (radius_sta_rate(hapd, sta) & 1) ? ".5" : "", + radius_mode_txt(hapd)); + buf[sizeof(buf) - 1] = '\0'; + } + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_CONNECT_INFO) && + !radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_ERROR, "Could not add Connect-Info"); + return -1; + } + + if (sta->acct_session_id_hi || sta->acct_session_id_lo) { + os_snprintf(buf, sizeof(buf), "%08X-%08X", + sta->acct_session_id_hi, sta->acct_session_id_lo); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID, + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id"); + return -1; + } + } + + return 0; +} + + +int add_common_radius_attr(struct hostapd_data *hapd, + struct hostapd_radius_attr *req_attr, + struct sta_info *sta, + struct radius_msg *msg) +{ + char buf[128]; + struct hostapd_radius_attr *attr; + + if (!hostapd_config_get_radius_attr(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)) { + wpa_printf(MSG_ERROR, "Could not add NAS-IP-Address"); + return -1; + } + +#ifdef CONFIG_IPV6 + if (!hostapd_config_get_radius_attr(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)) { + wpa_printf(MSG_ERROR, "Could not add NAS-IPv6-Address"); + return -1; + } +#endif /* CONFIG_IPV6 */ + + if (!hostapd_config_get_radius_attr(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))) { + wpa_printf(MSG_ERROR, "Could not add NAS-Identifier"); + return -1; + } + + os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", + MAC2STR(hapd->own_addr), + wpa_ssid_txt(hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len)); + buf[sizeof(buf) - 1] = '\0'; + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_CALLED_STATION_ID) && + !radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_ERROR, "Could not add Called-Station-Id"); + return -1; + } + + if (!hostapd_config_get_radius_attr(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)) { + wpa_printf(MSG_ERROR, "Could not add NAS-Port-Type"); + return -1; + } + + if (sta && add_common_radius_sta_attr(hapd, req_attr, sta, msg) < 0) + return -1; + + for (attr = 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"); + return -1; + } + } + + return 0; +} + + static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, struct sta_info *sta, const u8 *eap, size_t len) { struct radius_msg *msg; - char buf[128]; struct eapol_state_machine *sm = sta->eapol_sm; if (sm == NULL) @@ -450,83 +577,20 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, goto fail; } - if (hapd->conf->own_ip_addr.af == AF_INET && - !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, - (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) { - printf("Could not add NAS-IP-Address\n"); - goto fail; - } - -#ifdef CONFIG_IPV6 - if (hapd->conf->own_ip_addr.af == AF_INET6 && - !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS, - (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) { - printf("Could not add NAS-IPv6-Address\n"); - goto fail; - } -#endif /* CONFIG_IPV6 */ - - if (hapd->conf->nas_identifier && - !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, - (u8 *) hapd->conf->nas_identifier, - os_strlen(hapd->conf->nas_identifier))) { - printf("Could not add NAS-Identifier\n"); - goto fail; - } - - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) { - printf("Could not add NAS-Port\n"); - goto fail; - } - - os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", - MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid); - buf[sizeof(buf) - 1] = '\0'; - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, - (u8 *) buf, os_strlen(buf))) { - printf("Could not add Called-Station-Id\n"); - goto fail; - } - - os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, - MAC2STR(sta->addr)); - buf[sizeof(buf) - 1] = '\0'; - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, - (u8 *) buf, os_strlen(buf))) { - printf("Could not add Calling-Station-Id\n"); + if (add_common_radius_attr(hapd, hapd->conf->radius_auth_req_attr, sta, + msg) < 0) goto fail; - } /* TODO: should probably check MTU from driver config; 2304 is max for * IEEE 802.11, but use 1400 to avoid problems with too large packets */ - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) { + 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, - RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { - printf("Could not add NAS-Port-Type\n"); - goto fail; - } - - if (sta->flags & WLAN_STA_PREAUTH) { - os_strlcpy(buf, "IEEE 802.11i Pre-Authentication", - sizeof(buf)); - } else { - os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s", - radius_sta_rate(hapd, sta) / 2, - (radius_sta_rate(hapd, sta) & 1) ? ".5" : "", - radius_mode_txt(hapd)); - buf[sizeof(buf) - 1] = '\0'; - } - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, - (u8 *) buf, os_strlen(buf))) { - printf("Could not add Connect-Info\n"); - goto fail; - } - if (eap && !radius_msg_add_eap(msg, eap, len)) { printf("Could not add EAP-Message\n"); goto fail; @@ -549,7 +613,28 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, } } - radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr); + 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; + } + } + + if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr) < 0) + goto fail; + return; fail: @@ -652,7 +737,8 @@ ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta) flags |= EAPOL_SM_FROM_PMKSA_CACHE; } return eapol_auth_alloc(hapd->eapol_auth, sta->addr, flags, - sta->wps_ie, sta); + sta->wps_ie, sta->p2p_ie, sta, + sta->identity, sta->radius_cui); } @@ -673,6 +759,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, struct ieee802_1x_eapol_key *key; u16 datalen; struct rsn_pmksa_cache_entry *pmksa; + int key_mgmt; if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->wps_state) @@ -681,9 +768,10 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, wpa_printf(MSG_DEBUG, "IEEE 802.1X: %lu bytes from " MACSTR, (unsigned long) len, MAC2STR(sa)); sta = ap_get_sta(hapd, sa); - if (!sta || !(sta->flags & WLAN_STA_ASSOC)) { + if (!sta || (!(sta->flags & (WLAN_STA_ASSOC | WLAN_STA_PREAUTH)) && + !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED))) { wpa_printf(MSG_DEBUG, "IEEE 802.1X data frame from not " - "associated STA"); + "associated/Pre-authenticating STA"); return; } @@ -724,10 +812,19 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, return; } - if ((!hapd->conf->ieee802_1x && - !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) || - wpa_key_mgmt_wpa_psk(wpa_auth_sta_key_mgmt(sta->wpa_sm))) + if (!hapd->conf->ieee802_1x && + !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - " + "802.1X not enabled and WPS not used"); + return; + } + + key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm); + if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - " + "STA is using PSK"); return; + } if (!sta->eapol_sm) { sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta); @@ -735,14 +832,24 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, return; #ifdef CONFIG_WPS - if (!hapd->conf->ieee802_1x && - ((sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) == - WLAN_STA_MAYBE_WPS)) { - /* - * Delay EAPOL frame transmission until a possible WPS - * STA initiates the handshake with EAPOL-Start. - */ - sta->eapol_sm->flags |= EAPOL_SM_WAIT_START; + if (!hapd->conf->ieee802_1x) { + u32 wflags = sta->flags & (WLAN_STA_WPS | + WLAN_STA_WPS2 | + WLAN_STA_MAYBE_WPS); + if (wflags == WLAN_STA_MAYBE_WPS || + wflags == (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) { + /* + * Delay EAPOL frame transmission until a + * possible WPS STA initiates the handshake + * with EAPOL-Start. Only allow the wait to be + * skipped if the STA is known to support WPS + * 2.0. + */ + wpa_printf(MSG_DEBUG, "WPS: Do not start " + "EAPOL until EAPOL-Start is " + "received"); + sta->eapol_sm->flags |= EAPOL_SM_WAIT_START; + } } #endif /* CONFIG_WPS */ @@ -776,6 +883,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, } sta->eapol_sm->eapolStart = TRUE; sta->eapol_sm->dot1xAuthEapolStartFramesRx++; + eap_server_clear_identity(sta->eapol_sm->eap); wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL); break; @@ -788,11 +896,12 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, accounting_sta_stop(hapd, sta); sta->eapol_sm->eapolLogoff = TRUE; sta->eapol_sm->dot1xAuthEapolLogoffFramesRx++; + eap_server_clear_identity(sta->eapol_sm->eap); break; case IEEE802_1X_TYPE_EAPOL_KEY: wpa_printf(MSG_DEBUG, " EAPOL-Key"); - if (!(sta->flags & WLAN_STA_AUTHORIZED)) { + if (!ap_sta_is_authorized(sta)) { wpa_printf(MSG_DEBUG, " Dropped key data from " "unauthorized Supplicant"); break; @@ -827,6 +936,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) struct rsn_pmksa_cache_entry *pmksa; int reassoc = 1; int force_1x = 0; + int key_mgmt; #ifdef CONFIG_WPS if (hapd->conf->wps_state && hapd->conf->wpa && @@ -840,9 +950,27 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) } #endif /* CONFIG_WPS */ - if ((!force_1x && !hapd->conf->ieee802_1x) || - wpa_key_mgmt_wpa_psk(wpa_auth_sta_key_mgmt(sta->wpa_sm))) + 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; + } if (sta->eapol_sm == NULL) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, @@ -860,17 +988,40 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) #ifdef CONFIG_WPS sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START; - if (!hapd->conf->ieee802_1x && !(sta->flags & WLAN_STA_WPS)) { + if (!hapd->conf->ieee802_1x && !(sta->flags & WLAN_STA_WPS2)) { /* - * Delay EAPOL frame transmission until a possible WPS - * initiates the handshake with EAPOL-Start. + * Delay EAPOL frame transmission until a possible WPS STA + * initiates the handshake with EAPOL-Start. Only allow the + * wait to be skipped if the STA is known to support WPS 2.0. */ + wpa_printf(MSG_DEBUG, "WPS: Do not start EAPOL until " + "EAPOL-Start is received"); sta->eapol_sm->flags |= EAPOL_SM_WAIT_START; } #endif /* CONFIG_WPS */ sta->eapol_sm->eap_if->portEnabled = TRUE; +#ifdef CONFIG_IEEE80211R + if (sta->auth_alg == WLAN_AUTH_FT) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, + "PMK from FT - skip IEEE 802.1X/EAP"); + /* Setup EAPOL state machines to already authenticated state + * because of existing FT information from R0KH. */ + sta->eapol_sm->keyRun = TRUE; + sta->eapol_sm->eap_if->eapKeyAvailable = TRUE; + sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING; + sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS; + sta->eapol_sm->authSuccess = TRUE; + sta->eapol_sm->authFail = FALSE; + if (sta->eapol_sm->eap) + eap_sm_notify_cached(sta->eapol_sm->eap); + /* TODO: get vlan_id from R0KH using RRB message */ + return; + } +#endif /* CONFIG_IEEE80211R */ + pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm); if (pmksa) { int old_vlanid; @@ -885,6 +1036,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING; sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS; sta->eapol_sm->authSuccess = TRUE; + sta->eapol_sm->authFail = FALSE; if (sta->eapol_sm->eap) eap_sm_notify_cached(sta->eapol_sm->eap); old_vlanid = sta->vlan_id; @@ -918,6 +1070,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); @@ -929,9 +1082,8 @@ void ieee802_1x_free_station(struct sta_info *sta) static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd, struct sta_info *sta) { - u8 *eap; - size_t len; - struct eap_hdr *hdr; + struct wpabuf *eap; + const struct eap_hdr *hdr; int eap_type = -1; char buf[64]; struct radius_msg *msg; @@ -945,7 +1097,7 @@ static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd, msg = sm->last_recv_radius; - eap = radius_msg_get_eap(msg, &len); + eap = radius_msg_get_eap(msg); if (eap == NULL) { /* RFC 3579, Chap. 2.6.3: * RADIUS server SHOULD NOT send Access-Reject/no EAP-Message @@ -957,19 +1109,19 @@ static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd, return; } - if (len < sizeof(*hdr)) { + if (wpabuf_len(eap) < sizeof(*hdr)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_WARNING, "too short EAP packet " "received from authentication server"); - os_free(eap); + wpabuf_free(eap); sm->eap_if->aaaEapNoReq = TRUE; return; } - if (len > sizeof(*hdr)) - eap_type = eap[sizeof(*hdr)]; + if (wpabuf_len(eap) > sizeof(*hdr)) + eap_type = (wpabuf_head_u8(eap))[sizeof(*hdr)]; - hdr = (struct eap_hdr *) eap; + hdr = wpabuf_head(eap); switch (hdr->code) { case EAP_CODE_REQUEST: if (eap_type >= 0) @@ -1004,7 +1156,7 @@ static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd, sm->eap_if->aaaEapReq = TRUE; wpabuf_free(sm->eap_if->aaaEapReqData); - sm->eap_if->aaaEapReqData = wpabuf_alloc_ext_data(eap, len); + sm->eap_if->aaaEapReqData = eap; } @@ -1069,7 +1221,7 @@ static void ieee802_1x_store_radius_class(struct hostapd_data *hapd, if (count <= 0) return; - nclass = os_zalloc(count * sizeof(struct radius_attr_data)); + nclass = os_calloc(count, sizeof(struct radius_attr_data)); if (nclass == NULL) return; @@ -1139,6 +1291,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; @@ -1298,6 +1476,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 ? @@ -1363,6 +1542,9 @@ void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta) * request and we cannot continue EAP processing (EAP-Failure * could only be sent if the EAP peer actually replied). */ + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "EAP Timeout, STA " MACSTR, + MAC2STR(sta->addr)); + sm->eap_if->portEnabled = FALSE; ap_sta_disconnect(hapd, sta, sta->addr, WLAN_REASON_PREV_AUTH_NOT_VALID); @@ -1380,8 +1562,8 @@ static int ieee802_1x_rekey_broadcast(struct hostapd_data *hapd) os_free(eapol->default_wep_key); eapol->default_wep_key = os_malloc(hapd->conf->default_wep_key_len); if (eapol->default_wep_key == NULL || - os_get_random(eapol->default_wep_key, - hapd->conf->default_wep_key_len)) { + random_get_bytes(eapol->default_wep_key, + hapd->conf->default_wep_key_len)) { printf("Could not generate random WEP key.\n"); os_free(eapol->default_wep_key); eapol->default_wep_key = NULL; @@ -1432,10 +1614,11 @@ static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx) /* TODO: Could setup key for RX here, but change default TX keyid only * after new broadcast key has been sent to all stations. */ - if (hapd->drv.set_key(hapd->conf->iface, hapd, WPA_ALG_WEP, NULL, - eapol->default_wep_key_idx, 1, NULL, 0, - eapol->default_wep_key, - hapd->conf->default_wep_key_len)) { + if (hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_WEP, + broadcast_ether_addr, + eapol->default_wep_key_idx, 1, NULL, 0, + eapol->default_wep_key, + hapd->conf->default_wep_key_len)) { hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_WARNING, "failed to configure a " "new broadcast key"); @@ -1514,19 +1697,15 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity, { struct hostapd_data *hapd = ctx; const struct hostapd_eap_user *eap_user; - int i, count; + int i; - eap_user = hostapd_get_eap_user(hapd->conf, identity, - identity_len, phase2); + eap_user = hostapd_get_eap_user(hapd, identity, identity_len, phase2); if (eap_user == NULL) return -1; os_memset(user, 0, sizeof(*user)); user->phase2 = phase2; - count = EAP_USER_MAX_METHODS; - if (count > EAP_MAX_METHODS) - count = EAP_MAX_METHODS; - for (i = 0; i < count; i++) { + for (i = 0; i < EAP_MAX_METHODS; i++) { user->methods[i].vendor = eap_user->methods[i].vendor; user->methods[i].method = eap_user->methods[i].method; } @@ -1538,6 +1717,7 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity, os_memcpy(user->password, eap_user->password, eap_user->password_len); user->password_len = eap_user->password_len; + user->password_hash = eap_user->password_hash; } user->force_version = eap_user->force_version; user->ttls_auth = eap_user->ttls_auth; @@ -1651,6 +1831,9 @@ int ieee802_1x_init(struct hostapd_data *hapd) conf.eap_sim_aka_result_ind = hapd->conf->eap_sim_aka_result_ind; conf.tnc = hapd->conf->tnc; conf.wps = hapd->wps; + conf.fragment_size = hapd->conf->fragment_size; + conf.pwd_group = hapd->conf->pwd_group; + conf.pbc_in_m1 = hapd->conf->pbc_in_m1; os_memset(&cb, 0, sizeof(cb)); cb.eapol_send = ieee802_1x_eapol_send; @@ -1669,7 +1852,7 @@ int ieee802_1x_init(struct hostapd_data *hapd) return -1; if ((hapd->conf->ieee802_1x || hapd->conf->wpa) && - hapd->drv.set_drv_ieee8021x(hapd, hapd->conf->iface, 1)) + hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 1)) return -1; #ifndef CONFIG_NO_RADIUS @@ -1680,9 +1863,9 @@ int ieee802_1x_init(struct hostapd_data *hapd) if (hapd->conf->default_wep_key_len) { for (i = 0; i < 4; i++) - hapd->drv.set_key(hapd->conf->iface, hapd, - WPA_ALG_NONE, NULL, i, 0, NULL, 0, - NULL, 0); + hostapd_drv_set_key(hapd->conf->iface, hapd, + WPA_ALG_NONE, NULL, i, 0, NULL, 0, + NULL, 0); ieee802_1x_rekey(hapd, NULL); @@ -1700,7 +1883,7 @@ void ieee802_1x_deinit(struct hostapd_data *hapd) if (hapd->driver != NULL && (hapd->conf->ieee802_1x || hapd->conf->wpa)) - hapd->drv.set_drv_ieee8021x(hapd, hapd->conf->iface, 0); + hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0); eapol_auth_deinit(hapd->eapol_auth); hapd->eapol_auth = NULL; @@ -1711,15 +1894,13 @@ int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta, const u8 *buf, size_t len, int ack) { struct ieee80211_hdr *hdr; - struct ieee802_1x_hdr *xhdr; - struct ieee802_1x_eapol_key *key; u8 *pos; const unsigned char rfc1042_hdr[ETH_ALEN] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; if (sta == NULL) return -1; - if (len < sizeof(*hdr) + sizeof(rfc1042_hdr) + 2 + sizeof(*xhdr)) + if (len < sizeof(*hdr) + sizeof(rfc1042_hdr) + 2) return 0; hdr = (struct ieee80211_hdr *) buf; @@ -1731,21 +1912,44 @@ int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta, return 0; pos += 2; - xhdr = (struct ieee802_1x_hdr *) pos; - pos += sizeof(*xhdr); + return ieee802_1x_eapol_tx_status(hapd, sta, pos, buf + len - pos, + ack); +} + + +int ieee802_1x_eapol_tx_status(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *buf, int len, int ack) +{ + const struct ieee802_1x_hdr *xhdr = + (const struct ieee802_1x_hdr *) buf; + const u8 *pos = buf + sizeof(*xhdr); + struct ieee802_1x_eapol_key *key; + if (len < (int) sizeof(*xhdr)) + return 0; wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR " TX status - version=%d " "type=%d length=%d - ack=%d", MAC2STR(sta->addr), xhdr->version, xhdr->type, be_to_host16(xhdr->length), ack); + if (xhdr->type != IEEE802_1X_TYPE_EAPOL_KEY) + return 0; + + if (pos + sizeof(struct wpa_eapol_key) <= buf + len) { + const struct wpa_eapol_key *wpa; + wpa = (const struct wpa_eapol_key *) pos; + if (wpa->type == EAPOL_KEY_TYPE_RSN || + wpa->type == EAPOL_KEY_TYPE_WPA) + wpa_auth_eapol_key_tx_status(hapd->wpa_auth, + sta->wpa_sm, ack); + } + /* EAPOL EAP-Packet packets are eventually re-sent by either Supplicant * or Authenticator state machines, but EAPOL-Key packets are not - * retransmitted in case of failure. Try to re-sent failed EAPOL-Key + * retransmitted in case of failure. Try to re-send failed EAPOL-Key * packets couple of times because otherwise STA keys become * unsynchronized with AP. */ - if (xhdr->type == IEEE802_1X_TYPE_EAPOL_KEY && !ack && - pos + sizeof(*key) <= buf + len) { + if (!ack && pos + sizeof(*key) <= buf + len) { key = (struct ieee802_1x_eapol_key *) pos; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "did not Ack EAPOL-Key " @@ -1789,8 +1993,17 @@ 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; if (sm == NULL) return NULL; @@ -1848,6 +2061,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, { int len = 0, ret; struct eapol_state_machine *sm = sta->eapol_sm; + struct os_time t; if (sm == NULL) return 0; @@ -1962,6 +2176,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, len += ret; /* dot1xAuthSessionStatsTable */ + os_get_time(&t); ret = os_snprintf(buf + len, buflen - len, /* TODO: dot1xAuthSessionOctetsRx */ /* TODO: dot1xAuthSessionOctetsTx */ @@ -1976,8 +2191,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, (wpa_key_mgmt_wpa_ieee8021x( wpa_auth_sta_key_mgmt(sta->wpa_sm))) ? 1 : 2, - (unsigned int) (time(NULL) - - sta->acct_session_start), + (unsigned int) (t.sec - sta->acct_session_start), sm->identity); if (ret < 0 || (size_t) ret >= buflen - len) return len; @@ -2004,22 +2218,25 @@ static void ieee802_1x_finished(struct hostapd_data *hapd, "Added PMKSA cache entry (IEEE 802.1X)"); } -#ifdef CONFIG_WPS - if (!success && (sta->flags & WLAN_STA_WPS)) { + if (!success) { /* * Many devices require deauthentication after WPS provisioning * and some may not be be able to do that themselves, so - * disconnect the client here. + * disconnect the client here. In addition, this may also + * benefit IEEE 802.1X/EAPOL authentication cases, too since + * the EAPOL PAE state machine would remain in HELD state for + * considerable amount of time and some EAP methods, like + * EAP-FAST with anonymous provisioning, may require another + * EAPOL authentication to be started to complete connection. */ - wpa_printf(MSG_DEBUG, "WPS: Force disconnection after " - "EAP-Failure"); + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "IEEE 802.1X: Force " + "disconnection after EAP-Failure"); /* Add a small sleep to increase likelihood of previously * requested EAP-Failure TX getting out before this should the * driver reorder operations. */ os_sleep(0, 10000); ap_sta_disconnect(hapd, sta, sta->addr, - WLAN_REASON_PREV_AUTH_NOT_VALID); + WLAN_REASON_IEEE_802_1X_AUTH_FAILED); } -#endif /* CONFIG_WPS */ } diff --git a/src/ap/ieee802_1x.h b/src/ap/ieee802_1x.h index 1a4d2eb0f2c10..e1df94057de70 100644 --- a/src/ap/ieee802_1x.h +++ b/src/ap/ieee802_1x.h @@ -1,15 +1,9 @@ /* * hostapd / IEEE 802.1X-2004 Authenticator - * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-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 - * published 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. */ #ifndef IEEE802_1X_H @@ -20,38 +14,8 @@ struct sta_info; struct eapol_state_machine; struct hostapd_config; struct hostapd_bss_config; - -#ifdef _MSC_VER -#pragma pack(push, 1) -#endif /* _MSC_VER */ - -/* RFC 3580, 4. RC4 EAPOL-Key Frame */ - -struct ieee802_1x_eapol_key { - u8 type; - u16 key_length; - u8 replay_counter[8]; /* does not repeat within the life of the keying - * material used to encrypt the Key field; - * 64-bit NTP timestamp MAY be used here */ - u8 key_iv[16]; /* cryptographically random number */ - u8 key_index; /* key flag in the most significant bit: - * 0 = broadcast (default key), - * 1 = unicast (key mapping key); key index is in the - * 7 least significant bits */ - u8 key_signature[16]; /* HMAC-MD5 message integrity check computed with - * MS-MPPE-Send-Key as the key */ - - /* followed by key: if packet body length = 44 + key length, then the - * key field (of key_length bytes) contains the key in encrypted form; - * if packet body length = 44, key field is absent and key_length - * represents the number of least significant octets from - * MS-MPPE-Send-Key attribute to be used as the keying material; - * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */ -} STRUCT_PACKED; - -#ifdef _MSC_VER -#pragma pack(pop) -#endif /* _MSC_VER */ +struct hostapd_radius_attr; +struct radius_msg; void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, @@ -68,9 +32,12 @@ int ieee802_1x_init(struct hostapd_data *hapd); void ieee802_1x_deinit(struct hostapd_data *hapd); int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta, const u8 *buf, size_t len, int ack); +int ieee802_1x_eapol_tx_status(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *data, int len, int ack); u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len); u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len, int idx); +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); @@ -86,4 +53,9 @@ char *eap_type_text(u8 type); const char *radius_mode_txt(struct hostapd_data *hapd); int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta); +int add_common_radius_attr(struct hostapd_data *hapd, + struct hostapd_radius_attr *req_attr, + struct sta_info *sta, + struct radius_msg *msg); + #endif /* IEEE802_1X_H */ diff --git a/src/ap/p2p_hostapd.c b/src/ap/p2p_hostapd.c new file mode 100644 index 0000000000000..795d313b8c163 --- /dev/null +++ b/src/ap/p2p_hostapd.c @@ -0,0 +1,114 @@ +/* + * hostapd / P2P integration + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "p2p/p2p.h" +#include "hostapd.h" +#include "ap_config.h" +#include "ap_drv_ops.h" +#include "sta_info.h" +#include "p2p_hostapd.h" + + +#ifdef CONFIG_P2P + +int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, + char *buf, size_t buflen) +{ + if (sta->p2p_ie == NULL) + return 0; + + return p2p_ie_text(sta->p2p_ie, buf, buf + buflen); +} + + +int hostapd_p2p_set_noa(struct hostapd_data *hapd, u8 count, int start, + int duration) +{ + wpa_printf(MSG_DEBUG, "P2P: Set NoA parameters: count=%u start=%d " + "duration=%d", count, start, duration); + + if (count == 0) { + hapd->noa_enabled = 0; + hapd->noa_start = 0; + hapd->noa_duration = 0; + } + + if (count != 255) { + wpa_printf(MSG_DEBUG, "P2P: Non-periodic NoA - set " + "NoA parameters"); + return hostapd_driver_set_noa(hapd, count, start, duration); + } + + hapd->noa_enabled = 1; + hapd->noa_start = start; + hapd->noa_duration = duration; + + if (hapd->num_sta_no_p2p == 0) { + wpa_printf(MSG_DEBUG, "P2P: No legacy STAs connected - update " + "periodic NoA parameters"); + return hostapd_driver_set_noa(hapd, count, start, duration); + } + + wpa_printf(MSG_DEBUG, "P2P: Legacy STA(s) connected - do not enable " + "periodic NoA"); + + return 0; +} + + +void hostapd_p2p_non_p2p_sta_connected(struct hostapd_data *hapd) +{ + wpa_printf(MSG_DEBUG, "P2P: First non-P2P device connected"); + + if (hapd->noa_enabled) { + wpa_printf(MSG_DEBUG, "P2P: Disable periodic NoA"); + hostapd_driver_set_noa(hapd, 0, 0, 0); + } +} + + +void hostapd_p2p_non_p2p_sta_disconnected(struct hostapd_data *hapd) +{ + wpa_printf(MSG_DEBUG, "P2P: Last non-P2P device disconnected"); + + if (hapd->noa_enabled) { + wpa_printf(MSG_DEBUG, "P2P: Enable periodic NoA"); + hostapd_driver_set_noa(hapd, 255, hapd->noa_start, + hapd->noa_duration); + } +} + +#endif /* CONFIG_P2P */ + + +#ifdef CONFIG_P2P_MANAGER +u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid) +{ + u8 bitmap; + *eid++ = WLAN_EID_VENDOR_SPECIFIC; + *eid++ = 4 + 3 + 1; + WPA_PUT_BE24(eid, OUI_WFA); + eid += 3; + *eid++ = P2P_OUI_TYPE; + + *eid++ = P2P_ATTR_MANAGEABILITY; + WPA_PUT_LE16(eid, 1); + eid += 2; + bitmap = P2P_MAN_DEVICE_MANAGEMENT; + if (hapd->conf->p2p & P2P_ALLOW_CROSS_CONNECTION) + bitmap |= P2P_MAN_CROSS_CONNECTION_PERMITTED; + bitmap |= P2P_MAN_COEXISTENCE_OPTIONAL; + *eid++ = bitmap; + + return eid; +} +#endif /* CONFIG_P2P_MANAGER */ diff --git a/src/ap/p2p_hostapd.h b/src/ap/p2p_hostapd.h new file mode 100644 index 0000000000000..0e3921c614611 --- /dev/null +++ b/src/ap/p2p_hostapd.h @@ -0,0 +1,35 @@ +/* + * hostapd / P2P integration + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef P2P_HOSTAPD_H +#define P2P_HOSTAPD_H + +#ifdef CONFIG_P2P + +int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, + char *buf, size_t buflen); +int hostapd_p2p_set_noa(struct hostapd_data *hapd, u8 count, int start, + int duration); +void hostapd_p2p_non_p2p_sta_connected(struct hostapd_data *hapd); +void hostapd_p2p_non_p2p_sta_disconnected(struct hostapd_data *hapd); + + +#else /* CONFIG_P2P */ + +static inline int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, + struct sta_info *sta, + char *buf, size_t buflen) +{ + return 0; +} + +#endif /* CONFIG_P2P */ + +u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid); + +#endif /* P2P_HOSTAPD_H */ diff --git a/src/ap/peerkey_auth.c b/src/ap/peerkey_auth.c index f68c47909ae6e..ba5c606442f43 100644 --- a/src/ap/peerkey_auth.c +++ b/src/ap/peerkey_auth.c @@ -2,14 +2,8 @@ * hostapd - PeerKey for Direct Link Setup (DLS) * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -18,6 +12,7 @@ #include "utils/eloop.h" #include "crypto/sha1.h" #include "crypto/sha256.h" +#include "crypto/random.h" #include "wpa_auth.h" #include "wpa_auth_i.h" #include "wpa_auth_ie.h" @@ -294,7 +289,7 @@ void wpa_smk_m3(struct wpa_authenticator *wpa_auth, return; } - if (os_get_random(smk, PMK_LEN)) { + if (random_get_bytes(smk, PMK_LEN)) { wpa_printf(MSG_DEBUG, "RSN: Failed to generate SMK"); return; } diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c index 22f44b78464a6..d27fd302ba969 100644 --- a/src/ap/pmksa_cache_auth.c +++ b/src/ap/pmksa_cache_auth.c @@ -1,15 +1,9 @@ /* * 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 program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published 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 "utils/includes.h" @@ -46,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 */ @@ -100,11 +95,9 @@ static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) os_get_time(&now); while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) { - struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; - pmksa->pmksa = entry->next; wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for " - MACSTR, MAC2STR(entry->spa)); - pmksa_cache_free_entry(pmksa, entry); + MACSTR, MAC2STR(pmksa->pmksa->spa)); + pmksa_cache_free_entry(pmksa, pmksa->pmksa); } pmksa_cache_set_expiration(pmksa); @@ -142,6 +135,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 */ @@ -169,6 +165,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); @@ -208,6 +209,8 @@ static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa, pmksa->pmkid[PMKID_HASH(entry->pmkid)] = entry; pmksa->pmksa_count++; + if (prev == NULL) + pmksa_cache_set_expiration(pmksa); wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR, MAC2STR(entry->spa)); wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN); @@ -305,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 9628b13da0299..d473f3fdced3a 100644 --- a/src/ap/pmksa_cache_auth.h +++ b/src/ap/pmksa_cache_auth.h @@ -1,15 +1,9 @@ /* * 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 program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published 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. */ #ifndef PMKSA_CACHE_H @@ -31,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/preauth_auth.c b/src/ap/preauth_auth.c index 8e133158a9763..3e0c8000d0625 100644 --- a/src/ap/preauth_auth.c +++ b/src/ap/preauth_auth.c @@ -2,14 +2,8 @@ * hostapd - Authenticator for IEEE 802.11i RSN pre-authentication * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" diff --git a/src/ap/preauth_auth.h b/src/ap/preauth_auth.h index 5348bee9bf72e..69fb3566e0051 100644 --- a/src/ap/preauth_auth.h +++ b/src/ap/preauth_auth.h @@ -2,14 +2,8 @@ * hostapd - Authenticator for IEEE 802.11i RSN pre-authentication * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef PREAUTH_H diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index 335c9a5bd7240..97cd0136b86dc 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -1,15 +1,9 @@ /* * hostapd / Station table - * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -17,27 +11,36 @@ #include "utils/common.h" #include "utils/eloop.h" #include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "drivers/driver.h" +#include "p2p/p2p.h" #include "hostapd.h" #include "accounting.h" #include "ieee802_1x.h" #include "ieee802_11.h" +#include "ieee802_11_auth.h" #include "wpa_auth.h" #include "preauth_auth.h" #include "ap_config.h" #include "beacon.h" #include "ap_mlme.h" #include "vlan_init.h" +#include "p2p_hostapd.h" +#include "ap_drv_ops.h" +#include "gas_serv.h" #include "sta_info.h" static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd, struct sta_info *sta); static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx); +static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx); +static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx); #ifdef CONFIG_IEEE80211W static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx); #endif /* CONFIG_IEEE80211W */ +static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta); int ap_for_each_sta(struct hostapd_data *hapd, int (*cb)(struct hostapd_data *hapd, struct sta_info *sta, @@ -121,11 +124,14 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) accounting_sta_stop(hapd, sta); + /* just in case */ + ap_sta_set_authorized(hapd, sta, 0); + if (sta->flags & WLAN_STA_WDS) - hapd->drv.set_wds_sta(hapd, sta->addr, sta->aid, 0); + hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 0); if (!(sta->flags & WLAN_STA_PREAUTH)) - hapd->drv.sta_remove(hapd, sta->addr); + hostapd_drv_sta_remove(hapd, sta->addr); ap_sta_hash_del(hapd, sta); ap_sta_list_del(hapd, sta); @@ -173,6 +179,15 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) hapd->iface->num_sta_ht_20mhz--; } +#ifdef CONFIG_P2P + if (sta->no_p2p_set) { + sta->no_p2p_set = 0; + hapd->num_sta_no_p2p--; + if (hapd->num_sta_no_p2p == 0) + hostapd_p2p_non_p2p_sta_disconnected(hapd); + } +#endif /* CONFIG_P2P */ + #if defined(NEED_AP_MLME) && defined(CONFIG_IEEE80211N) if (hostapd_ht_operation_update(hapd->iface) > 0) set_beacon++; @@ -181,8 +196,12 @@ 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); + eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta); ieee802_1x_free_station(sta); wpa_auth_sta_deinit(sta->wpa_sm); @@ -199,9 +218,27 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) eloop_cancel_timeout(ap_sa_query_timer, hapd, sta); #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_P2P + p2p_group_notif_disassoc(hapd->p2p_group, sta->addr); +#endif /* CONFIG_P2P */ + +#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); + wpabuf_free(sta->hs20_ie); os_free(sta->ht_capabilities); + hostapd_free_psk_list(sta->psk); + os_free(sta->identity); + os_free(sta->radius_cui); os_free(sta); } @@ -241,6 +278,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 " @@ -253,27 +293,51 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) (sta->timeout_next == STA_NULLFUNC || sta->timeout_next == STA_DISASSOC)) { int inactive_sec; - wpa_printf(MSG_DEBUG, "Checking STA " MACSTR " inactivity:", - MAC2STR(sta->addr)); - inactive_sec = hapd->drv.get_inact_sec(hapd, sta->addr); + /* + * 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_printf(MSG_DEBUG, "Could not get station info " - "from kernel driver for " MACSTR ".", - MAC2STR(sta->addr)); + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "Check inactivity: Could not " + "get station info from kernel driver for " + MACSTR, MAC2STR(sta->addr)); + /* + * The driver may not support this functionality. + * Anyway, try again after the next inactivity timeout, + * but do not disconnect the station now. + */ + 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 */ - wpa_printf(MSG_DEBUG, " Station has been active"); + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "Station " MACSTR " has been active %is ago", + MAC2STR(sta->addr), inactive_sec); sta->timeout_next = STA_NULLFUNC; - next_time = hapd->conf->ap_max_inactivity - + next_time = hapd->conf->ap_max_inactivity + fuzz - inactive_sec; + } else { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "Station " MACSTR " has been " + "inactive too long: %d sec, max allowed: %d", + MAC2STR(sta->addr), inactive_sec, + hapd->conf->ap_max_inactivity); + + if (hapd->conf->skip_inactivity_poll) + sta->timeout_next = STA_DISASSOC; } } if ((sta->flags & WLAN_STA_ASSOC) && sta->timeout_next == STA_DISASSOC && - !(sta->flags & WLAN_STA_PENDING_POLL)) { - wpa_printf(MSG_DEBUG, " Station has ACKed data poll"); + !(sta->flags & WLAN_STA_PENDING_POLL) && + !hapd->conf->skip_inactivity_poll) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR + " has ACKed data poll", MAC2STR(sta->addr)); /* data nullfunc frame poll did not produce TX errors; assume * station ACKed it */ sta->timeout_next = STA_NULLFUNC; @@ -281,6 +345,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; @@ -288,52 +355,24 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) if (sta->timeout_next == STA_NULLFUNC && (sta->flags & WLAN_STA_ASSOC)) { -#ifndef CONFIG_NATIVE_WINDOWS - /* send data frame to poll STA and check whether this frame - * is ACKed */ - struct ieee80211_hdr hdr; - - wpa_printf(MSG_DEBUG, " Polling STA with data frame"); + wpa_printf(MSG_DEBUG, " Polling STA"); sta->flags |= WLAN_STA_PENDING_POLL; - - os_memset(&hdr, 0, sizeof(hdr)); - if (hapd->driver && - os_strcmp(hapd->driver->name, "hostap") == 0) { - /* - * WLAN_FC_STYPE_NULLFUNC would be more appropriate, - * but it is apparently not retried so TX Exc events - * are not received for it. - */ - hdr.frame_control = - IEEE80211_FC(WLAN_FC_TYPE_DATA, - WLAN_FC_STYPE_DATA); - } else { - hdr.frame_control = - IEEE80211_FC(WLAN_FC_TYPE_DATA, - WLAN_FC_STYPE_NULLFUNC); - } - - hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS); - os_memcpy(hdr.IEEE80211_DA_FROMDS, sta->addr, ETH_ALEN); - os_memcpy(hdr.IEEE80211_BSSID_FROMDS, hapd->own_addr, - ETH_ALEN); - os_memcpy(hdr.IEEE80211_SA_FROMDS, hapd->own_addr, ETH_ALEN); - - if (hapd->drv.send_mgmt_frame(hapd, &hdr, sizeof(hdr)) < 0) - perror("ap_handle_timer: send"); -#endif /* CONFIG_NATIVE_WINDOWS */ + hostapd_drv_poll_client(hapd, hapd->own_addr, sta->addr, + sta->flags & WLAN_STA_WMM); } else if (sta->timeout_next != STA_REMOVE) { int deauth = sta->timeout_next == STA_DEAUTH; - wpa_printf(MSG_DEBUG, "Sending %s info to STA " MACSTR, - deauth ? "deauthentication" : "disassociation", - MAC2STR(sta->addr)); + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, + "Timeout, sending %s info to STA " MACSTR, + deauth ? "deauthentication" : "disassociation", + MAC2STR(sta->addr)); if (deauth) { - hapd->drv.sta_deauth(hapd, sta->addr, - WLAN_REASON_PREV_AUTH_NOT_VALID); + hostapd_drv_sta_deauth( + hapd, sta->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); } else { - hapd->drv.sta_disassoc( + hostapd_drv_sta_disassoc( hapd, sta->addr, WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); } @@ -342,10 +381,14 @@ 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; case STA_DISASSOC: + ap_sta_set_authorized(hapd, sta, 0); sta->flags &= ~WLAN_STA_ASSOC; ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); if (!sta->acct_terminate_cause) @@ -357,6 +400,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( @@ -366,7 +412,7 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) case STA_REMOVE: hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "deauthenticated due to " - "inactivity"); + "inactivity (timer DEAUTH/REMOVE)"); if (!sta->acct_terminate_cause) sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT; @@ -385,8 +431,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); @@ -397,7 +449,7 @@ static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx) RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT; os_memcpy(addr, sta->addr, ETH_ALEN); ap_free_sta(hapd, sta); - hapd->drv.sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); + hostapd_drv_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); } @@ -441,8 +493,13 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) return NULL; } sta->acct_interim_interval = hapd->conf->acct_interim_interval; + accounting_sta_get_id(hapd, sta); /* 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); @@ -463,7 +520,7 @@ static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta) wpa_printf(MSG_DEBUG, "Removing STA " MACSTR " from kernel driver", MAC2STR(sta->addr)); - if (hapd->drv.sta_remove(hapd, sta->addr) && + if (hostapd_drv_sta_remove(hapd, sta->addr) && sta->flags & WLAN_STA_ASSOC) { wpa_printf(MSG_DEBUG, "Could not remove station " MACSTR " from kernel driver.", MAC2STR(sta->addr)); @@ -498,21 +555,51 @@ static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd, } +static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + + ap_sta_remove(hapd, sta); + mlme_disassociate_indication(hapd, sta, sta->disassoc_reason); +} + + void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta, u16 reason) { wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR, hapd->conf->iface, MAC2STR(sta->addr)); sta->flags &= ~WLAN_STA_ASSOC; - ap_sta_remove(hapd, sta); + 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); accounting_sta_stop(hapd, sta); ieee802_1x_free_station(sta); - mlme_disassociate_indication(hapd, sta, reason); + sta->disassoc_reason = reason; + sta->flags |= WLAN_STA_PENDING_DISASSOC_CB; + eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta); + eloop_register_timeout(hapd->iface->drv_flags & + WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0, + ap_sta_disassoc_cb_timeout, hapd, sta); +} + + +static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + + ap_sta_remove(hapd, sta); + mlme_deauthenticate_indication(hapd, sta, sta->deauth_reason); } @@ -522,16 +609,43 @@ void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR, hapd->conf->iface, MAC2STR(sta->addr)); sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); - ap_sta_remove(hapd, sta); + 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); accounting_sta_stop(hapd, sta); ieee802_1x_free_station(sta); - mlme_deauthenticate_indication(hapd, sta, reason); + sta->deauth_reason = reason; + sta->flags |= WLAN_STA_PENDING_DEAUTH_CB; + eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta); + eloop_register_timeout(hapd->iface->drv_flags & + WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0, + ap_sta_deauth_cb_timeout, hapd, 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, @@ -636,7 +750,7 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, if (wpa_auth_sta_set_vlan(sta->wpa_sm, sta->vlan_id) < 0) wpa_printf(MSG_INFO, "Failed to update VLAN-ID for WPA"); - ret = hapd->drv.set_sta_vlan(iface, hapd, sta->addr, sta->vlan_id); + ret = hostapd_drv_set_sta_vlan(iface, hapd, sta->addr, sta->vlan_id); if (ret < 0) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "could not bind the STA " @@ -686,8 +800,9 @@ static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx) ap_check_sa_query_timeout(hapd, sta)) return; - nbuf = os_realloc(sta->sa_query_trans_id, - (sta->sa_query_count + 1) * WLAN_SA_QUERY_TR_ID_LEN); + nbuf = os_realloc_array(sta->sa_query_trans_id, + sta->sa_query_count + 1, + WLAN_SA_QUERY_TR_ID_LEN); if (nbuf == NULL) return; if (sta->sa_query_count == 0) { @@ -709,9 +824,7 @@ static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx) HOSTAPD_LEVEL_DEBUG, "association SA Query attempt %d", sta->sa_query_count); -#ifdef NEED_AP_MLME ieee802_11_send_sa_query_req(hapd, sta->addr, trans_id); -#endif /* NEED_AP_MLME */ } @@ -732,6 +845,73 @@ void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta) #endif /* CONFIG_IEEE80211W */ +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 + 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) { + if (dev_addr) + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED + MACSTR " p2p_dev_addr=" MACSTR, + MAC2STR(sta->addr), MAC2STR(dev_addr)); + else + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED + MACSTR, MAC2STR(sta->addr)); + if (hapd->msg_ctx_parent && + hapd->msg_ctx_parent != hapd->msg_ctx && dev_addr) + wpa_msg(hapd->msg_ctx_parent, MSG_INFO, + AP_STA_CONNECTED MACSTR " p2p_dev_addr=" + MACSTR, + MAC2STR(sta->addr), MAC2STR(dev_addr)); + else if (hapd->msg_ctx_parent && + hapd->msg_ctx_parent != hapd->msg_ctx) + wpa_msg(hapd->msg_ctx_parent, MSG_INFO, + AP_STA_CONNECTED MACSTR, MAC2STR(sta->addr)); + + sta->flags |= WLAN_STA_AUTHORIZED; + } else { + if (dev_addr) + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED + MACSTR " p2p_dev_addr=" MACSTR, + MAC2STR(sta->addr), MAC2STR(dev_addr)); + else + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED + MACSTR, MAC2STR(sta->addr)); + if (hapd->msg_ctx_parent && + hapd->msg_ctx_parent != hapd->msg_ctx && dev_addr) + wpa_msg(hapd->msg_ctx_parent, MSG_INFO, + AP_STA_DISCONNECTED MACSTR " p2p_dev_addr=" + MACSTR, MAC2STR(sta->addr), MAC2STR(dev_addr)); + else if (hapd->msg_ctx_parent && + hapd->msg_ctx_parent != hapd->msg_ctx) + wpa_msg(hapd->msg_ctx_parent, MSG_INFO, + AP_STA_DISCONNECTED MACSTR, + MAC2STR(sta->addr)); + sta->flags &= ~WLAN_STA_AUTHORIZED; + } + + if (hapd->sta_authorized_cb) + hapd->sta_authorized_cb(hapd->sta_authorized_cb_ctx, + sta->addr, authorized, dev_addr); +} + + void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta, const u8 *addr, u16 reason) { @@ -740,12 +920,52 @@ void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta, sta = ap_get_sta(hapd, addr); if (addr) - hapd->drv.sta_deauth(hapd, addr, reason); + hostapd_drv_sta_deauth(hapd, addr, reason); if (sta == NULL) return; - sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_AUTHORIZED); + ap_sta_set_authorized(hapd, sta, 0); + 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(0, 0, ap_handle_timer, hapd, sta); + eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0, + ap_handle_timer, hapd, sta); sta->timeout_next = STA_REMOVE; + + sta->deauth_reason = reason; + sta->flags |= WLAN_STA_PENDING_DEAUTH_CB; + eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta); + eloop_register_timeout(hapd->iface->drv_flags & + WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0, + ap_sta_deauth_cb_timeout, hapd, sta); +} + + +void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta) +{ + if (!(sta->flags & WLAN_STA_PENDING_DEAUTH_CB)) { + wpa_printf(MSG_DEBUG, "Ignore deauth cb for test frame"); + return; + } + sta->flags &= ~WLAN_STA_PENDING_DEAUTH_CB; + eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta); + ap_sta_deauth_cb_timeout(hapd, sta); +} + + +void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta) +{ + if (!(sta->flags & WLAN_STA_PENDING_DISASSOC_CB)) { + wpa_printf(MSG_DEBUG, "Ignore disassoc cb for test frame"); + return; + } + sta->flags &= ~WLAN_STA_PENDING_DISASSOC_CB; + eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta); + ap_sta_disassoc_cb_timeout(hapd, sta); } diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index 55faa5a28c5e9..d5e92faa7637a 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -1,15 +1,9 @@ /* * hostapd / Station table - * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef STA_INFO_H @@ -31,6 +25,12 @@ #define WLAN_STA_WPS BIT(12) #define WLAN_STA_MAYBE_WPS BIT(13) #define WLAN_STA_WDS BIT(14) +#define WLAN_STA_ASSOC_REQ_OK BIT(15) +#define WLAN_STA_WPS2 BIT(16) +#define WLAN_STA_GAS BIT(17) +#define WLAN_STA_VHT BIT(18) +#define WLAN_STA_PENDING_DISASSOC_CB BIT(29) +#define WLAN_STA_PENDING_DEAUTH_CB BIT(30) #define WLAN_STA_NONERP BIT(31) /* Maximum number of supported rates (from both Supported Rates and Extended @@ -48,6 +48,7 @@ struct sta_info { u16 listen_interval; /* or beacon_int for APs */ u8 supported_rates[WLAN_SUPP_RATES_MAX]; int supported_rates_len; + u8 qosinfo; /* Valid when WLAN_STA_WMM is set */ unsigned int nonerp_set:1; unsigned int no_short_slot_time_set:1; @@ -55,6 +56,7 @@ struct sta_info { unsigned int no_ht_gf_set:1; unsigned int no_ht_set:1; unsigned int ht_20mhz_set:1; + unsigned int no_p2p_set:1; u16 auth_alg; u8 previous_ap[6]; @@ -63,6 +65,9 @@ struct sta_info { STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE } timeout_next; + u16 deauth_reason; + u16 disassoc_reason; + /* IEEE 802.1X related data */ struct eapol_state_machine *eapol_sm; @@ -90,8 +95,14 @@ struct sta_info { struct hostapd_ssid *ssid_probe; /* SSID selection based on ProbeReq */ int vlan_id; + /* PSKs from RADIUS authentication server */ + struct hostapd_sta_wpa_psk_short *psk; + + char *identity; /* User-Name from RADIUS */ + char *radius_cui; /* Chargeable-User-Identity from RADIUS */ struct ieee80211_ht_capabilities *ht_capabilities; + struct ieee80211_vht_capabilities *vht_capabilities; #ifdef CONFIG_IEEE80211W int sa_query_count; /* number of pending SA Query requests; @@ -103,7 +114,22 @@ 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 */ + struct wpabuf *hs20_ie; /* HS 2.0 IE from (Re)Association Request */ + + struct os_time connected_time; + +#ifdef CONFIG_SAE + enum { SAE_INIT, SAE_COMMIT, SAE_CONFIRM } sae_state; + u16 sae_send_confirm; +#endif /* CONFIG_SAE */ }; @@ -111,7 +137,7 @@ struct sta_info { * passed since last received frame from the station, a nullfunc data frame is * sent to the station. If this frame is not acknowledged and no other frames * have been received, the station will be disassociated after - * AP_DISASSOC_DELAY seconds. Similarily, the station will be deauthenticated + * AP_DISASSOC_DELAY seconds. Similarly, the station will be deauthenticated * after AP_DEAUTH_DELAY seconds has passed after disassociation. */ #define AP_MAX_INACTIVITY (5 * 60) #define AP_DISASSOC_DELAY (1) @@ -132,7 +158,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, @@ -144,6 +169,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); @@ -152,4 +181,14 @@ int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta); void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta, const u8 *addr, u16 reason); +void ap_sta_set_authorized(struct hostapd_data *hapd, + struct sta_info *sta, int authorized); +static inline int ap_sta_is_authorized(struct sta_info *sta) +{ + return sta->flags & WLAN_STA_AUTHORIZED; +} + +void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta); +void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta); + #endif /* STA_INFO_H */ diff --git a/src/ap/tkip_countermeasures.c b/src/ap/tkip_countermeasures.c index 9690348e9ccfa..4a2ea0665d886 100644 --- a/src/ap/tkip_countermeasures.c +++ b/src/ap/tkip_countermeasures.c @@ -1,15 +1,9 @@ /* * hostapd / TKIP countermeasures - * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-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 - * published 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 "utils/includes.h" @@ -17,10 +11,12 @@ #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" #include "wpa_auth.h" +#include "ap_drv_ops.h" #include "tkip_countermeasures.h" @@ -29,7 +25,7 @@ static void ieee80211_tkip_countermeasures_stop(void *eloop_ctx, { struct hostapd_data *hapd = eloop_ctx; hapd->tkip_countermeasures = 0; - hapd->drv.set_countermeasures(hapd, 0); + hostapd_drv_set_countermeasures(hapd, 0); hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "TKIP countermeasures ended"); } @@ -44,24 +40,36 @@ static void ieee80211_tkip_countermeasures_start(struct hostapd_data *hapd) wpa_auth_countermeasures_start(hapd->wpa_auth); hapd->tkip_countermeasures = 1; - hapd->drv.set_countermeasures(hapd, 1); + hostapd_drv_set_countermeasures(hapd, 1); wpa_gtk_rekey(hapd->wpa_auth); eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL); eloop_register_timeout(60, 0, ieee80211_tkip_countermeasures_stop, hapd, NULL); - for (sta = hapd->sta_list; sta != NULL; sta = sta->next) { - hapd->drv.sta_deauth(hapd, sta->addr, - WLAN_REASON_MICHAEL_MIC_FAILURE); - sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | - WLAN_STA_AUTHORIZED); - hapd->drv.sta_remove(hapd, sta->addr); + 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_free_sta(hapd, sta); } } -void michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local) +void ieee80211_tkip_countermeasures_deinit(struct hostapd_data *hapd) +{ + eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL); +} + + +int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local) { - time_t now; + struct os_time now; + int ret = 0; if (addr && local) { struct sta_info *sta = ap_get_sta(hapd, addr); @@ -77,17 +85,21 @@ void michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local) "MLME-MICHAELMICFAILURE.indication " "for not associated STA (" MACSTR ") ignored", MAC2STR(addr)); - return; + return ret; } } - time(&now); - if (now > hapd->michael_mic_failure + 60) { + os_get_time(&now); + if (now.sec > hapd->michael_mic_failure + 60) { hapd->michael_mic_failures = 1; } else { hapd->michael_mic_failures++; - if (hapd->michael_mic_failures > 1) + if (hapd->michael_mic_failures > 1) { ieee80211_tkip_countermeasures_start(hapd); + ret = 1; + } } - hapd->michael_mic_failure = now; + hapd->michael_mic_failure = now.sec; + + return ret; } diff --git a/src/ap/tkip_countermeasures.h b/src/ap/tkip_countermeasures.h index 5a1afceb03148..d3eaed3cf9adb 100644 --- a/src/ap/tkip_countermeasures.h +++ b/src/ap/tkip_countermeasures.h @@ -1,20 +1,15 @@ /* * hostapd / TKIP countermeasures - * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-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 - * published 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. */ #ifndef TKIP_COUNTERMEASURES_H #define TKIP_COUNTERMEASURES_H -void michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local); +int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local); +void ieee80211_tkip_countermeasures_deinit(struct hostapd_data *hapd); #endif /* TKIP_COUNTERMEASURES_H */ diff --git a/src/ap/utils.c b/src/ap/utils.c index 0ff48aeb37dee..931968c84b0e8 100644 --- a/src/ap/utils.c +++ b/src/ap/utils.c @@ -2,14 +2,8 @@ * AP mode helper functions * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -22,13 +16,15 @@ int hostapd_register_probereq_cb(struct hostapd_data *hapd, int (*cb)(void *ctx, const u8 *sa, - const u8 *ie, size_t ie_len), + const u8 *da, const u8 *bssid, + const u8 *ie, size_t ie_len, + int ssi_signal), void *ctx) { struct hostapd_probereq_cb *n; - n = os_realloc(hapd->probereq_cb, (hapd->num_probereq_cb + 1) * - sizeof(struct hostapd_probereq_cb)); + n = os_realloc_array(hapd->probereq_cb, hapd->num_probereq_cb + 1, + sizeof(struct hostapd_probereq_cb)); if (n == NULL) return -1; @@ -82,7 +78,8 @@ void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr) struct prune_data data; data.hapd = hapd; data.addr = addr; - if (hapd->iface->for_each_interface) - hapd->iface->for_each_interface(hapd->iface->interfaces, - prune_associations, &data); + if (hapd->iface->interfaces && + hapd->iface->interfaces->for_each_interface) + hapd->iface->interfaces->for_each_interface( + hapd->iface->interfaces, prune_associations, &data); } diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c index c9d166a8f346a..7b1a9e6711a5c 100644 --- a/src/ap/vlan_init.c +++ b/src/ap/vlan_init.c @@ -19,7 +19,9 @@ #include "utils/common.h" #include "hostapd.h" #include "ap_config.h" +#include "ap_drv_ops.h" #include "vlan_init.h" +#include "vlan_util.h" #ifdef CONFIG_FULL_DYNAMIC_VLAN @@ -334,7 +336,9 @@ static int br_getnumports(const char *br_name) } -static int vlan_rem(const char *if_name) +#ifndef CONFIG_VLAN_NETLINK + +int vlan_rem(const char *if_name) { int fd; struct vlan_ioctl_args if_request; @@ -377,7 +381,7 @@ static int vlan_rem(const char *if_name) returns 1 if the interface already exists returns 0 otherwise */ -static int vlan_add(const char *if_name, int vid) +int vlan_add(const char *if_name, int vid, const char *vlan_if_name) { int fd; struct vlan_ioctl_args if_request; @@ -473,6 +477,8 @@ static int vlan_set_name_type(unsigned int name_type) return 0; } +#endif /* CONFIG_VLAN_NETLINK */ + static void vlan_newlink(char *ifname, struct hostapd_data *hapd) { @@ -480,6 +486,7 @@ static void vlan_newlink(char *ifname, struct hostapd_data *hapd) char br_name[IFNAMSIZ]; struct hostapd_vlan *vlan = hapd->conf->vlan; char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; + int vlan_naming = hapd->conf->ssid.vlan_naming; wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname); @@ -495,13 +502,22 @@ static void vlan_newlink(char *ifname, struct hostapd_data *hapd) ifconfig_up(br_name); if (tagged_interface) { - - if (!vlan_add(tagged_interface, vlan->vlan_id)) + if (vlan_naming == + DYNAMIC_VLAN_NAMING_WITH_DEVICE) + os_snprintf(vlan_ifname, + sizeof(vlan_ifname), + "%s.%d", tagged_interface, + vlan->vlan_id); + else + os_snprintf(vlan_ifname, + sizeof(vlan_ifname), + "vlan%d", vlan->vlan_id); + + ifconfig_up(tagged_interface); + if (!vlan_add(tagged_interface, vlan->vlan_id, + vlan_ifname)) vlan->clean |= DVLAN_CLEAN_VLAN; - os_snprintf(vlan_ifname, sizeof(vlan_ifname), - "vlan%d", vlan->vlan_id); - if (!br_addif(br_name, vlan_ifname)) vlan->clean |= DVLAN_CLEAN_VLAN_PORT; @@ -526,6 +542,7 @@ static void vlan_dellink(char *ifname, struct hostapd_data *hapd) char br_name[IFNAMSIZ]; struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan; char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; + int vlan_naming = hapd->conf->ssid.vlan_naming; wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname); @@ -540,8 +557,16 @@ static void vlan_dellink(char *ifname, struct hostapd_data *hapd) br_delif(br_name, vlan->ifname); if (tagged_interface) { - os_snprintf(vlan_ifname, sizeof(vlan_ifname), - "vlan%d", vlan->vlan_id); + if (vlan_naming == + DYNAMIC_VLAN_NAMING_WITH_DEVICE) + os_snprintf(vlan_ifname, + sizeof(vlan_ifname), + "%s.%d", tagged_interface, + vlan->vlan_id); + else + os_snprintf(vlan_ifname, + sizeof(vlan_ifname), + "vlan%d", vlan->vlan_id); if (vlan->clean & DVLAN_CLEAN_VLAN_PORT) br_delif(br_name, vlan_ifname); ifconfig_down(vlan_ifname); @@ -681,7 +706,12 @@ full_dynamic_vlan_init(struct hostapd_data *hapd) if (priv == NULL) return NULL; - vlan_set_name_type(VLAN_NAME_TYPE_PLUS_VID_NO_PAD); +#ifndef CONFIG_VLAN_NETLINK + vlan_set_name_type(hapd->conf->ssid.vlan_naming == + DYNAMIC_VLAN_NAMING_WITH_DEVICE ? + VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD : + VLAN_NAME_TYPE_PLUS_VID_NO_PAD); +#endif /* CONFIG_VLAN_NETLINK */ priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (priv->s < 0) { @@ -737,9 +767,10 @@ int vlan_setup_encryption_dyn(struct hostapd_data *hapd, * functions for setting up dynamic broadcast keys. */ for (i = 0; i < 4; i++) { if (mssid->wep.key[i] && - hapd->drv.set_key(dyn_vlan, hapd, WPA_ALG_WEP, NULL, i, - i == mssid->wep.idx, NULL, 0, - mssid->wep.key[i], mssid->wep.len[i])) { + hostapd_drv_set_key(dyn_vlan, hapd, WPA_ALG_WEP, NULL, i, + i == mssid->wep.idx, NULL, 0, + mssid->wep.key[i], mssid->wep.len[i])) + { wpa_printf(MSG_ERROR, "VLAN: Could not set WEP " "encryption for dynamic VLAN"); return -1; @@ -755,7 +786,7 @@ static int vlan_dynamic_add(struct hostapd_data *hapd, { while (vlan) { if (vlan->vlan_id != VLAN_ID_WILDCARD) { - if (hapd->drv.vlan_if_add(hapd, vlan->ifname)) { + if (hostapd_vlan_if_add(hapd, vlan->ifname)) { if (errno != EEXIST) { wpa_printf(MSG_ERROR, "VLAN: Could " "not add VLAN %s: %s", @@ -785,7 +816,7 @@ static void vlan_dynamic_remove(struct hostapd_data *hapd, next = vlan->next; if (vlan->vlan_id != VLAN_ID_WILDCARD && - hapd->drv.vlan_if_remove(hapd, vlan->ifname)) { + hostapd_vlan_if_remove(hapd, vlan->ifname)) { wpa_printf(MSG_ERROR, "VLAN: Could not remove VLAN " "iface: %s: %s", vlan->ifname, strerror(errno)); @@ -859,7 +890,7 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, pos); os_free(ifname); - if (hapd->drv.vlan_if_add(hapd, n->ifname)) { + if (hostapd_vlan_if_add(hapd, n->ifname)) { os_free(n); return NULL; } @@ -897,7 +928,7 @@ int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id) return 1; if (vlan->dynamic_vlan == 0) - hapd->drv.vlan_if_remove(hapd, vlan->ifname); + hostapd_vlan_if_remove(hapd, vlan->ifname); return 0; } diff --git a/src/ap/vlan_util.c b/src/ap/vlan_util.c new file mode 100644 index 0000000000000..cc54051b1ecad --- /dev/null +++ b/src/ap/vlan_util.c @@ -0,0 +1,177 @@ +/* + * hostapd / VLAN netlink api + * Copyright (c) 2012, Michael Braun <michael-dev@fami-braun.de> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include <sys/ioctl.h> +#include <linux/sockios.h> +#include <linux/if_vlan.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> +#include <netlink/route/link.h> +#include <netlink/route/link/vlan.h> + +#include "utils/common.h" +#include "utils/eloop.h" +#include "hostapd.h" +#include "vlan_util.h" + +/* + * Add a vlan interface with name 'vlan_if_name', VLAN ID 'vid' and + * tagged interface 'if_name'. + * + * returns -1 on error + * returns 1 if the interface already exists + * returns 0 otherwise +*/ +int vlan_add(const char *if_name, int vid, const char *vlan_if_name) +{ + int ret = -1; + struct nl_sock *handle = NULL; + struct nl_cache *cache = NULL; + struct rtnl_link *rlink = NULL; + int if_idx = 0; + + wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d, " + "vlan_if_name=%s)", if_name, vid, vlan_if_name); + + if ((os_strlen(if_name) + 1) > IFNAMSIZ) { + wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", + if_name); + return -1; + } + + if ((os_strlen(vlan_if_name) + 1) > IFNAMSIZ) { + wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", + vlan_if_name); + return -1; + } + + handle = nl_socket_alloc(); + if (!handle) { + wpa_printf(MSG_ERROR, "VLAN: failed to open netlink socket"); + goto vlan_add_error; + } + + if (nl_connect(handle, NETLINK_ROUTE) < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink"); + goto vlan_add_error; + } + + if (rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache) < 0) { + cache = NULL; + wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache"); + goto vlan_add_error; + } + + if (!(if_idx = rtnl_link_name2i(cache, if_name))) { + /* link does not exist */ + wpa_printf(MSG_ERROR, "VLAN: interface %s does not exist", + if_name); + goto vlan_add_error; + } + + if ((rlink = rtnl_link_get_by_name(cache, vlan_if_name))) { + /* link does exist */ + rtnl_link_put(rlink); + rlink = NULL; + wpa_printf(MSG_ERROR, "VLAN: interface %s already exists", + vlan_if_name); + ret = 1; + goto vlan_add_error; + } + + rlink = rtnl_link_alloc(); + if (!rlink) { + wpa_printf(MSG_ERROR, "VLAN: failed to allocate new link"); + goto vlan_add_error; + } + + if (rtnl_link_set_type(rlink, "vlan") < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to set link type"); + goto vlan_add_error; + } + + rtnl_link_set_link(rlink, if_idx); + rtnl_link_set_name(rlink, vlan_if_name); + + if (rtnl_link_vlan_set_id(rlink, vid) < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to set link vlan id"); + goto vlan_add_error; + } + + if (rtnl_link_add(handle, rlink, NLM_F_CREATE) < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to create link %s for " + "vlan %d on %s (%d)", + vlan_if_name, vid, if_name, if_idx); + goto vlan_add_error; + } + + ret = 0; + +vlan_add_error: + if (rlink) + rtnl_link_put(rlink); + if (cache) + nl_cache_free(cache); + if (handle) + nl_socket_free(handle); + return ret; +} + + +int vlan_rem(const char *if_name) +{ + int ret = -1; + struct nl_sock *handle = NULL; + struct nl_cache *cache = NULL; + struct rtnl_link *rlink = NULL; + + wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(if_name=%s)", if_name); + + handle = nl_socket_alloc(); + if (!handle) { + wpa_printf(MSG_ERROR, "VLAN: failed to open netlink socket"); + goto vlan_rem_error; + } + + if (nl_connect(handle, NETLINK_ROUTE) < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink"); + goto vlan_rem_error; + } + + if (rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache) < 0) { + cache = NULL; + wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache"); + goto vlan_rem_error; + } + + if (!(rlink = rtnl_link_get_by_name(cache, if_name))) { + /* link does not exist */ + wpa_printf(MSG_ERROR, "VLAN: interface %s does not exists", + if_name); + goto vlan_rem_error; + } + + if (rtnl_link_delete(handle, rlink) < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to remove link %s", + if_name); + goto vlan_rem_error; + } + + ret = 0; + +vlan_rem_error: + if (rlink) + rtnl_link_put(rlink); + if (cache) + nl_cache_free(cache); + if (handle) + nl_socket_free(handle); + return ret; +} diff --git a/src/ap/vlan_util.h b/src/ap/vlan_util.h new file mode 100644 index 0000000000000..bef5a16f6c909 --- /dev/null +++ b/src/ap/vlan_util.h @@ -0,0 +1,15 @@ +/* + * hostapd / VLAN netlink api + * Copyright (c) 2012, Michael Braun <michael-dev@fami-braun.de> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef VLAN_UTIL_H +#define VLAN_UTIL_H + +int vlan_add(const char *if_name, int vid, const char *vlan_if_name); +int vlan_rem(const char *if_name); + +#endif /* VLAN_UTIL_H */ diff --git a/src/ap/wmm.c b/src/ap/wmm.c index 36681309c3dcf..d21c82f6de2e4 100644 --- a/src/ap/wmm.c +++ b/src/ap/wmm.c @@ -23,6 +23,7 @@ #include "ieee802_11.h" #include "sta_info.h" #include "ap_config.h" +#include "ap_drv_ops.h" #include "wmm.h" @@ -71,9 +72,12 @@ u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid) wmm->version = WMM_VERSION; wmm->qos_info = hapd->parameter_set_count & 0xf; - if (hapd->conf->wmm_uapsd) + if (hapd->conf->wmm_uapsd && + (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD)) wmm->qos_info |= 0x80; + wmm->reserved = 0; + /* fill in a parameter set record for each AC */ for (e = 0; e < 4; e++) { struct wmm_ac_parameter *ac = &wmm->ac[e]; @@ -94,9 +98,11 @@ u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid) } -/* This function is called when a station sends an association request with - * WMM info element. The function returns zero on success or non-zero on any - * error in WMM element. eid does not include Element ID and Length octets. */ +/* + * This function is called when a station sends an association request with + * WMM info element. The function returns 1 on success or 0 on any error in WMM + * element. eid does not include Element ID and Length octets. + */ int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid, size_t len) { struct wmm_information_element *wmm; @@ -106,7 +112,7 @@ int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid, size_t len) if (len < sizeof(struct wmm_information_element)) { wpa_printf(MSG_DEBUG, "Too short WMM IE (len=%lu)", (unsigned long) len); - return -1; + return 0; } wmm = (struct wmm_information_element *) eid; @@ -117,10 +123,10 @@ int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid, size_t len) if (wmm->oui_subtype != WMM_OUI_SUBTYPE_INFORMATION_ELEMENT || wmm->version != WMM_VERSION) { wpa_printf(MSG_DEBUG, "Unsupported WMM IE Subtype/Version"); - return -1; + return 0; } - return 0; + return 1; } @@ -150,7 +156,7 @@ static void wmm_send_action(struct hostapd_data *hapd, const u8 *addr, os_memcpy(t, tspec, sizeof(struct wmm_tspec_element)); len = ((u8 *) (t + 1)) - buf; - if (hapd->drv.send_mgmt_frame(hapd, m, len) < 0) + if (hostapd_drv_send_mlme(hapd, m, len, 0) < 0) perror("wmm_send_action: send"); } diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c new file mode 100644 index 0000000000000..54a6b857d7845 --- /dev/null +++ b/src/ap/wnm_ap.c @@ -0,0 +1,271 @@ +/* + * hostapd - WNM + * 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 "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "ap/hostapd.h" +#include "ap/sta_info.h" +#include "ap/ap_config.h" +#include "ap/ap_drv_ops.h" +#include "ap/wpa_auth.h" +#include "wnm_ap.h" + +#define MAX_TFS_IE_LEN 1024 + + +/* get the TFS IE from driver */ +static int ieee80211_11_get_tfs_ie(struct hostapd_data *hapd, const u8 *addr, + u8 *buf, u16 *buf_len, enum wnm_oper oper) +{ + wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper); + + return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len); +} + + +/* set the TFS IE to driver */ +static int ieee80211_11_set_tfs_ie(struct hostapd_data *hapd, const u8 *addr, + u8 *buf, u16 *buf_len, enum wnm_oper oper) +{ + wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper); + + return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len); +} + + +/* MLME-SLEEPMODE.response */ +static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, + const u8 *addr, u8 dialog_token, + u8 action_type, u16 intval) +{ + struct ieee80211_mgmt *mgmt; + int res; + size_t len; + size_t gtk_elem_len = 0; + size_t igtk_elem_len = 0; + struct wnm_sleep_element wnmsleep_ie; + u8 *wnmtfs_ie; + u8 wnmsleep_ie_len; + u16 wnmtfs_ie_len; + u8 *pos; + struct sta_info *sta; + enum wnm_oper tfs_oper = action_type == WNM_SLEEP_MODE_ENTER ? + WNM_SLEEP_TFS_RESP_IE_ADD : WNM_SLEEP_TFS_RESP_IE_NONE; + + sta = ap_get_sta(hapd, addr); + if (sta == NULL) { + wpa_printf(MSG_DEBUG, "%s: station not found", __func__); + return -EINVAL; + } + + /* WNM-Sleep Mode IE */ + os_memset(&wnmsleep_ie, 0, sizeof(struct wnm_sleep_element)); + wnmsleep_ie_len = sizeof(struct wnm_sleep_element); + wnmsleep_ie.eid = WLAN_EID_WNMSLEEP; + wnmsleep_ie.len = wnmsleep_ie_len - 2; + wnmsleep_ie.action_type = action_type; + wnmsleep_ie.status = WNM_STATUS_SLEEP_ACCEPT; + wnmsleep_ie.intval = intval; + + /* TFS IE(s) */ + wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN); + if (wnmtfs_ie == NULL) + return -1; + if (ieee80211_11_get_tfs_ie(hapd, addr, wnmtfs_ie, &wnmtfs_ie_len, + tfs_oper)) { + wnmtfs_ie_len = 0; + os_free(wnmtfs_ie); + wnmtfs_ie = NULL; + } + +#define MAX_GTK_SUBELEM_LEN 45 +#define MAX_IGTK_SUBELEM_LEN 26 + mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + + MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN); + if (mgmt == NULL) { + wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " + "WNM-Sleep Response action frame"); + return -1; + } + 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->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + mgmt->u.action.category = WLAN_ACTION_WNM; + mgmt->u.action.u.wnm_sleep_resp.action = WNM_SLEEP_MODE_RESP; + mgmt->u.action.u.wnm_sleep_resp.dialogtoken = dialog_token; + pos = (u8 *)mgmt->u.action.u.wnm_sleep_resp.variable; + /* add key data if MFP is enabled */ + if (!wpa_auth_uses_mfp(sta->wpa_sm) || + action_type != WNM_SLEEP_MODE_EXIT) { + mgmt->u.action.u.wnm_sleep_resp.keydata_len = 0; + } else { + gtk_elem_len = wpa_wnmsleep_gtk_subelem(sta->wpa_sm, pos); + pos += gtk_elem_len; + wpa_printf(MSG_DEBUG, "Pass 4, gtk_len = %d", + (int) gtk_elem_len); +#ifdef CONFIG_IEEE80211W + res = wpa_wnmsleep_igtk_subelem(sta->wpa_sm, pos); + if (res < 0) { + os_free(wnmtfs_ie); + os_free(mgmt); + return -1; + } + igtk_elem_len = res; + pos += igtk_elem_len; + wpa_printf(MSG_DEBUG, "Pass 4 igtk_len = %d", + (int) igtk_elem_len); +#endif /* CONFIG_IEEE80211W */ + + WPA_PUT_LE16((u8 *) + &mgmt->u.action.u.wnm_sleep_resp.keydata_len, + gtk_elem_len + igtk_elem_len); + } + os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len); + /* copy TFS IE here */ + pos += wnmsleep_ie_len; + if (wnmtfs_ie) + os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len); + + len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len + + igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len; + + /* In driver, response frame should be forced to sent when STA is in + * PS mode */ + res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, + mgmt->da, &mgmt->u.action.category, len); + + if (!res) { + wpa_printf(MSG_DEBUG, "Successfully send WNM-Sleep Response " + "frame"); + + /* when entering wnmsleep + * 1. pause the node in driver + * 2. mark the node so that AP won't update GTK/IGTK during + * WNM Sleep + */ + if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT && + wnmsleep_ie.action_type == WNM_SLEEP_MODE_ENTER) { + hostapd_drv_wnm_oper(hapd, WNM_SLEEP_ENTER_CONFIRM, + addr, NULL, NULL); + wpa_set_wnmsleep(sta->wpa_sm, 1); + } + /* when exiting wnmsleep + * 1. unmark the node + * 2. start GTK/IGTK update if MFP is not used + * 3. unpause the node in driver + */ + if ((wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT || + wnmsleep_ie.status == + WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) && + wnmsleep_ie.action_type == WNM_SLEEP_MODE_EXIT) { + wpa_set_wnmsleep(sta->wpa_sm, 0); + hostapd_drv_wnm_oper(hapd, WNM_SLEEP_EXIT_CONFIRM, + addr, NULL, NULL); + if (!wpa_auth_uses_mfp(sta->wpa_sm)) + wpa_wnmsleep_rekey_gtk(sta->wpa_sm); + } + } else + wpa_printf(MSG_DEBUG, "Fail to send WNM-Sleep Response frame"); + +#undef MAX_GTK_SUBELEM_LEN +#undef MAX_IGTK_SUBELEM_LEN + os_free(wnmtfs_ie); + os_free(mgmt); + return res; +} + + +static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd, + const u8 *addr, const u8 *frm, int len) +{ + /* Dialog Token [1] | WNM-Sleep Mode IE | TFS Response IE */ + const u8 *pos = frm; + u8 dialog_token; + struct wnm_sleep_element *wnmsleep_ie = NULL; + /* multiple TFS Req IE (assuming consecutive) */ + u8 *tfsreq_ie_start = NULL; + u8 *tfsreq_ie_end = NULL; + u16 tfsreq_ie_len = 0; + + dialog_token = *pos++; + while (pos + 1 < frm + len) { + u8 ie_len = pos[1]; + if (pos + 2 + ie_len > frm + len) + break; + if (*pos == WLAN_EID_WNMSLEEP) + wnmsleep_ie = (struct wnm_sleep_element *) pos; + else if (*pos == WLAN_EID_TFS_REQ) { + if (!tfsreq_ie_start) + tfsreq_ie_start = (u8 *) pos; + tfsreq_ie_end = (u8 *) pos; + } else + wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized", + *pos); + pos += ie_len + 2; + } + + if (!wnmsleep_ie) { + wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found"); + return; + } + + if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER && + tfsreq_ie_start && tfsreq_ie_end && + tfsreq_ie_end - tfsreq_ie_start >= 0) { + tfsreq_ie_len = (tfsreq_ie_end + tfsreq_ie_end[1] + 2) - + tfsreq_ie_start; + wpa_printf(MSG_DEBUG, "TFS Req IE(s) found"); + /* pass the TFS Req IE(s) to driver for processing */ + if (ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start, + &tfsreq_ie_len, + WNM_SLEEP_TFS_REQ_IE_SET)) + wpa_printf(MSG_DEBUG, "Fail to set TFS Req IE"); + } + + ieee802_11_send_wnmsleep_resp(hapd, addr, dialog_token, + wnmsleep_ie->action_type, + wnmsleep_ie->intval); + + if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) { + /* clear the tfs after sending the resp frame */ + ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start, + &tfsreq_ie_len, WNM_SLEEP_TFS_IE_DEL); + } +} + + +int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, + struct rx_action *action) +{ + if (action->len < 1 || action->data == NULL) + return -1; + + switch (action->data[0]) { + case WNM_BSS_TRANS_MGMT_QUERY: + wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Query"); + /* TODO */ + return -1; + case WNM_BSS_TRANS_MGMT_RESP: + wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management " + "Response"); + /* TODO */ + return -1; + case WNM_SLEEP_MODE_REQ: + ieee802_11_rx_wnmsleep_req(hapd, action->sa, action->data + 1, + action->len - 1); + return 0; + } + + wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR, + action->data[0], MAC2STR(action->sa)); + return -1; +} diff --git a/src/ap/wnm_ap.h b/src/ap/wnm_ap.h new file mode 100644 index 0000000000000..f05726ee74d17 --- /dev/null +++ b/src/ap/wnm_ap.h @@ -0,0 +1,17 @@ +/* + * IEEE 802.11v WNM related functions and structures + * 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 WNM_AP_H +#define WNM_AP_H + +struct rx_action; + +int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, + struct rx_action *action); + +#endif /* WNM_AP_H */ diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 36cb0f4783c34..2c70149a18189 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -1,15 +1,9 @@ /* - * hostapd - IEEE 802.11i-2004 / WPA Authenticator - * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> + * IEEE 802.11 RSN / WPA Authenticator + * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -22,6 +16,7 @@ #include "crypto/crypto.h" #include "crypto/sha1.h" #include "crypto/sha256.h" +#include "crypto/random.h" #include "eapol_auth/eapol_auth_sm.h" #include "ap_config.h" #include "ieee802_11.h" @@ -44,11 +39,14 @@ static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, static void wpa_request_new_ptk(struct wpa_state_machine *sm); static int wpa_gtk_update(struct wpa_authenticator *wpa_auth, struct wpa_group *group); +static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth, + struct wpa_group *group); static const u32 dot11RSNAConfigGroupUpdateCount = 4; static const u32 dot11RSNAConfigPairwiseUpdateCount = 4; static const u32 eapol_key_timeout_first = 100; /* ms */ static const u32 eapol_key_timeout_subseq = 1000; /* ms */ +static const u32 eapol_key_timeout_first_group = 500; /* ms */ /* TODO: make these configurable */ static const int dot11RSNAConfigPMKLifetime = 43200; @@ -56,11 +54,12 @@ static const int dot11RSNAConfigPMKReauthThreshold = 70; static const int dot11RSNAConfigSATimeout = 60; -static inline void wpa_auth_mic_failure_report( +static inline int wpa_auth_mic_failure_report( struct wpa_authenticator *wpa_auth, const u8 *addr) { if (wpa_auth->cb.mic_failure_report) - wpa_auth->cb.mic_failure_report(wpa_auth->cb.ctx, addr); + return wpa_auth->cb.mic_failure_report(wpa_auth->cb.ctx, addr); + return 0; } @@ -191,6 +190,7 @@ static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth, { if (wpa_auth->cb.disconnect == NULL) return; + wpa_printf(MSG_DEBUG, "wpa_sta_disconnect STA " MACSTR, MAC2STR(addr)); wpa_auth->cb.disconnect(wpa_auth->cb.ctx, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); } @@ -215,11 +215,13 @@ static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx) { struct wpa_authenticator *wpa_auth = eloop_ctx; - if (os_get_random(wpa_auth->group->GMK, WPA_GMK_LEN)) { + if (random_get_bytes(wpa_auth->group->GMK, WPA_GMK_LEN)) { wpa_printf(MSG_ERROR, "Failed to get random data for WPA " "initialization."); } else { wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "GMK rekeyd"); + wpa_hexdump_key(MSG_DEBUG, "GMK", + wpa_auth->group->GMK, WPA_GMK_LEN); } if (wpa_auth->conf.wpa_gmk_rekey) { @@ -277,31 +279,40 @@ static void wpa_auth_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, } -static void wpa_group_set_key_len(struct wpa_group *group, int cipher) +static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) { - switch (cipher) { - case WPA_CIPHER_CCMP: - group->GTK_len = 16; - break; - case WPA_CIPHER_TKIP: - group->GTK_len = 32; - break; - case WPA_CIPHER_WEP104: - group->GTK_len = 13; - break; - case WPA_CIPHER_WEP40: - group->GTK_len = 5; - break; - } + u8 buf[ETH_ALEN + 8 + sizeof(group)]; + u8 rkey[32]; + + if (random_get_bytes(group->GMK, WPA_GMK_LEN) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "GMK", group->GMK, WPA_GMK_LEN); + + /* + * Counter = PRF-256(Random number, "Init Counter", + * Local MAC Address || Time) + */ + os_memcpy(buf, wpa_auth->addr, ETH_ALEN); + wpa_get_ntp_timestamp(buf + ETH_ALEN); + os_memcpy(buf + ETH_ALEN + 8, &group, sizeof(group)); + if (random_get_bytes(rkey, sizeof(rkey)) < 0) + return -1; + + if (sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf), + group->Counter, WPA_NONCE_LEN) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "Key Counter", + group->Counter, WPA_NONCE_LEN); + + return 0; } static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth, - int vlan_id) + int vlan_id, int delay_init) { struct wpa_group *group; - u8 buf[ETH_ALEN + 8 + sizeof(group)]; - u8 rkey[32]; group = os_zalloc(sizeof(struct wpa_group)); if (group == NULL) @@ -309,30 +320,37 @@ static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth, group->GTKAuthenticator = TRUE; group->vlan_id = vlan_id; + group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group); - wpa_group_set_key_len(group, wpa_auth->conf.wpa_group); + if (random_pool_ready() != 1) { + wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool " + "for secure operations - update keys later when " + "the first station connects"); + } - /* Counter = PRF-256(Random number, "Init Counter", - * Local MAC Address || Time) + /* + * Set initial GMK/Counter value here. The actual values that will be + * used in negotiations will be set once the first station tries to + * connect. This allows more time for collecting additional randomness + * on embedded devices. */ - os_memcpy(buf, wpa_auth->addr, ETH_ALEN); - wpa_get_ntp_timestamp(buf + ETH_ALEN); - os_memcpy(buf + ETH_ALEN + 8, &group, sizeof(group)); - if (os_get_random(rkey, sizeof(rkey)) || - os_get_random(group->GMK, WPA_GMK_LEN)) { + if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0) { wpa_printf(MSG_ERROR, "Failed to get random data for WPA " "initialization."); os_free(group); return NULL; } - sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf), - group->Counter, WPA_NONCE_LEN); - group->GInit = TRUE; - wpa_group_sm_step(wpa_auth, group); - group->GInit = FALSE; - wpa_group_sm_step(wpa_auth, group); + if (delay_init) { + wpa_printf(MSG_DEBUG, "WPA: Delay group state machine start " + "until Beacon frames have been configured"); + /* Initialization is completed in wpa_init_keys(). */ + } else { + wpa_group_sm_step(wpa_auth, group); + group->GInit = FALSE; + wpa_group_sm_step(wpa_auth, group); + } return group; } @@ -364,7 +382,7 @@ struct wpa_authenticator * wpa_init(const u8 *addr, return NULL; } - wpa_auth->group = wpa_group_init(wpa_auth, 0); + wpa_auth->group = wpa_group_init(wpa_auth, 0, 1); if (wpa_auth->group == NULL) { os_free(wpa_auth->wpa_ie); os_free(wpa_auth); @@ -405,6 +423,19 @@ struct wpa_authenticator * wpa_init(const u8 *addr, } +int wpa_init_keys(struct wpa_authenticator *wpa_auth) +{ + struct wpa_group *group = wpa_auth->group; + + wpa_printf(MSG_DEBUG, "WPA: Start group state machine to set initial " + "keys"); + wpa_group_sm_step(wpa_auth, group); + group->GInit = FALSE; + wpa_group_sm_step(wpa_auth, group); + return 0; +} + + /** * wpa_deinit - Deinitialize WPA authenticator * @wpa_auth: Pointer to WPA authenticator data from wpa_init() @@ -464,7 +495,7 @@ int wpa_reconfig(struct wpa_authenticator *wpa_auth, * configuration. */ group = wpa_auth->group; - wpa_group_set_key_len(group, wpa_auth->conf.wpa_group); + group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group); group->GInit = TRUE; wpa_group_sm_step(wpa_auth, group); group->GInit = FALSE; @@ -539,6 +570,10 @@ void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm) static void wpa_free_sta_sm(struct wpa_state_machine *sm) { + if (sm->GUpdateStationKeys) { + sm->group->GKeyDoneStations--; + sm->GUpdateStationKeys = FALSE; + } #ifdef CONFIG_IEEE80211R os_free(sm->assoc_resp_ftie); #endif /* CONFIG_IEEE80211R */ @@ -563,6 +598,7 @@ void wpa_auth_sta_deinit(struct wpa_state_machine *sm) } eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); + sm->pending_1_of_4_timeout = 0; eloop_cancel_timeout(wpa_sm_call_step, sm, NULL); eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); if (sm->in_step_loop) { @@ -586,14 +622,14 @@ static void wpa_request_new_ptk(struct wpa_state_machine *sm) } -static int wpa_replay_counter_valid(struct wpa_state_machine *sm, +static int wpa_replay_counter_valid(struct wpa_key_replay_counter *ctr, const u8 *replay_counter) { int i; for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { - if (!sm->key_replay[i].valid) + if (!ctr[i].valid) break; - if (os_memcmp(replay_counter, sm->key_replay[i].counter, + if (os_memcmp(replay_counter, ctr[i].counter, WPA_REPLAY_COUNTER_LEN) == 0) return 1; } @@ -601,6 +637,20 @@ static int wpa_replay_counter_valid(struct wpa_state_machine *sm, } +static void wpa_replay_counter_mark_invalid(struct wpa_key_replay_counter *ctr, + const u8 *replay_counter) +{ + int i; + for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { + if (ctr[i].valid && + (replay_counter == NULL || + os_memcmp(replay_counter, ctr[i].counter, + WPA_REPLAY_COUNTER_LEN) == 0)) + ctr[i].valid = FALSE; + } +} + + #ifdef CONFIG_IEEE80211R static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, @@ -651,6 +701,39 @@ static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth, #endif /* CONFIG_IEEE80211R */ +static int wpa_receive_error_report(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, int group) +{ + /* Supplicant reported a Michael MIC error */ + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key Error Request " + "(STA detected Michael MIC failure (group=%d))", + group); + + if (group && wpa_auth->conf.wpa_group != WPA_CIPHER_TKIP) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "ignore Michael MIC failure report since " + "group cipher is not TKIP"); + } else if (!group && sm->pairwise != WPA_CIPHER_TKIP) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "ignore Michael MIC failure report since " + "pairwise cipher is not TKIP"); + } else { + if (wpa_auth_mic_failure_report(wpa_auth, sm->addr) > 0) + return 1; /* STA entry was removed */ + sm->dot11RSNAStatsTKIPRemoteMICFailures++; + wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++; + } + + /* + * Error report is not a request for a new key handshake, but since + * Authenticator may do it, let's change the keys now anyway. + */ + wpa_request_new_ptk(sm); + return 0; +} + + void wpa_receive(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, u8 *data, size_t data_len) @@ -676,6 +759,9 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, key = (struct wpa_eapol_key *) (hdr + 1); key_info = WPA_GET_BE16(key->key_info); key_data_length = WPA_GET_BE16(key->key_data_length); + wpa_printf(MSG_DEBUG, "WPA: Received EAPOL-Key from " MACSTR + " key_info=0x%x type=%u key_data_length=%u", + MAC2STR(sm->addr), key_info, key->type, key_data_length); if (key_data_length > data_len - sizeof(*hdr) - sizeof(*key)) { wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - " "key_data overflow (%d > %lu)", @@ -686,7 +772,14 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, } if (sm->wpa == WPA_VERSION_WPA2) { - if (key->type != EAPOL_KEY_TYPE_RSN) { + if (key->type == EAPOL_KEY_TYPE_WPA) { + /* + * Some deployed station implementations seem to send + * msg 4/4 with incorrect type value in WPA2 mode. + */ + wpa_printf(MSG_DEBUG, "Workaround: Allow EAPOL-Key " + "with unexpected WPA type in RSN mode"); + } else if (key->type != EAPOL_KEY_TYPE_RSN) { wpa_printf(MSG_DEBUG, "Ignore EAPOL-Key with " "unexpected type %d in RSN mode", key->type); @@ -701,6 +794,11 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, } } + wpa_hexdump(MSG_DEBUG, "WPA: Received Key Nonce", key->key_nonce, + WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "WPA: Received Replay Counter", + key->replay_counter, WPA_REPLAY_COUNTER_LEN); + /* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys * are set */ @@ -734,7 +832,8 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, if (msg == REQUEST || msg == PAIRWISE_2 || msg == PAIRWISE_4 || msg == GROUP_2) { u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK; - if (sm->pairwise == WPA_CIPHER_CCMP) { + if (sm->pairwise == WPA_CIPHER_CCMP || + sm->pairwise == WPA_CIPHER_GCMP) { if (wpa_use_aes_cmac(sm) && ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { wpa_auth_logger(wpa_auth, sm->addr, @@ -750,7 +849,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, "did not use HMAC-SHA1-AES " - "with CCMP"); + "with CCMP/GCMP"); return; } } @@ -768,11 +867,44 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, } if (!(key_info & WPA_KEY_INFO_REQUEST) && - !wpa_replay_counter_valid(sm, key->replay_counter)) { + !wpa_replay_counter_valid(sm->key_replay, key->replay_counter)) { int i; - wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, - "received EAPOL-Key %s with unexpected " - "replay counter", msgtxt); + + if (msg == PAIRWISE_2 && + wpa_replay_counter_valid(sm->prev_key_replay, + key->replay_counter) && + sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING && + os_memcmp(sm->SNonce, key->key_nonce, WPA_NONCE_LEN) != 0) + { + /* + * Some supplicant implementations (e.g., Windows XP + * WZC) update SNonce for each EAPOL-Key 2/4. This + * breaks the workaround on accepting any of the + * pending requests, so allow the SNonce to be updated + * even if we have already sent out EAPOL-Key 3/4. + */ + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "Process SNonce update from STA " + "based on retransmitted EAPOL-Key " + "1/4"); + sm->update_snonce = 1; + wpa_replay_counter_mark_invalid(sm->prev_key_replay, + key->replay_counter); + goto continue_processing; + } + + if (msg == PAIRWISE_2 && + wpa_replay_counter_valid(sm->prev_key_replay, + key->replay_counter) && + sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "ignore retransmitted EAPOL-Key %s - " + "SNonce did not change", msgtxt); + } else { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "received EAPOL-Key %s with " + "unexpected replay counter", msgtxt); + } for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { if (!sm->key_replay[i].valid) break; @@ -785,16 +917,37 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, return; } +continue_processing: switch (msg) { case PAIRWISE_2: if (sm->wpa_ptk_state != WPA_PTK_PTKSTART && - sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING) { + sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING && + (!sm->update_snonce || + sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING)) { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key msg 2/4 in " "invalid state (%d) - dropped", sm->wpa_ptk_state); return; } + random_add_randomness(key->key_nonce, WPA_NONCE_LEN); + if (sm->group->reject_4way_hs_for_entropy) { + /* + * The system did not have enough entropy to generate + * strong random numbers. Reject the first 4-way + * handshake(s) and collect some entropy based on the + * information from it. Once enough entropy is + * available, the next atempt will trigger GMK/Key + * Counter update and the station will be allowed to + * continue. + */ + wpa_printf(MSG_DEBUG, "WPA: Reject 4-way handshake to " + "collect more entropy for random number " + "generation"); + random_mark_pool_ready(); + wpa_sta_disconnect(wpa_auth, sm->addr); + return; + } if (wpa_parse_kde_ies((u8 *) (key + 1), key_data_length, &kde) < 0) { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, @@ -897,7 +1050,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, } sm->MICVerified = FALSE; - if (sm->PTK_valid) { + if (sm->PTK_valid && !sm->update_snonce) { if (wpa_verify_key_mic(&sm->PTK, data, data_len)) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key with invalid MIC"); @@ -905,6 +1058,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, } sm->MICVerified = TRUE; eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm); + sm->pending_1_of_4_timeout = 0; } if (key_info & WPA_KEY_INFO_REQUEST) { @@ -930,17 +1084,10 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, #endif /* CONFIG_PEERKEY */ return; } else if (key_info & WPA_KEY_INFO_ERROR) { - /* Supplicant reported a Michael MIC error */ - wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, - "received EAPOL-Key Error Request " - "(STA detected Michael MIC failure)"); - wpa_auth_mic_failure_report(wpa_auth, sm->addr); - sm->dot11RSNAStatsTKIPRemoteMICFailures++; - wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++; - /* Error report is not a request for a new key - * handshake, but since Authenticator may do it, let's - * change the keys now anyway. */ - wpa_request_new_ptk(sm); + if (wpa_receive_error_report( + wpa_auth, sm, + !(key_info & WPA_KEY_INFO_KEY_TYPE)) > 0) + return; /* STA entry was removed */ } else if (key_info & WPA_KEY_INFO_KEY_TYPE) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key Request for new " @@ -958,19 +1105,34 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key Request for GTK " "rekeying"); - /* FIX: why was this triggering PTK rekeying for the - * STA that requested Group Key rekeying?? */ - /* wpa_request_new_ptk(sta->wpa_sm); */ eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); wpa_rekey_gtk(wpa_auth, NULL); } } else { - /* Do not allow the same key replay counter to be reused. This - * does also invalidate all other pending replay counters if - * retransmissions were used, i.e., we will only process one of - * the pending replies and ignore rest if more than one is - * received. */ - sm->key_replay[0].valid = FALSE; + /* Do not allow the same key replay counter to be reused. */ + wpa_replay_counter_mark_invalid(sm->key_replay, + key->replay_counter); + + if (msg == PAIRWISE_2) { + /* + * Maintain a copy of the pending EAPOL-Key frames in + * case the EAPOL-Key frame was retransmitted. This is + * needed to allow EAPOL-Key msg 2/4 reply to another + * pending msg 1/4 to update the SNonce to work around + * unexpected supplicant behavior. + */ + os_memcpy(sm->prev_key_replay, sm->key_replay, + sizeof(sm->key_replay)); + } else { + os_memset(sm->prev_key_replay, 0, + sizeof(sm->prev_key_replay)); + } + + /* + * Make sure old valid counters are not accepted anymore and + * do not get copied again. + */ + wpa_replay_counter_mark_invalid(sm->key_replay, NULL); } #ifdef CONFIG_PEERKEY @@ -987,6 +1149,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, os_memcpy(sm->last_rx_eapol_key, data, data_len); sm->last_rx_eapol_key_len = data_len; + sm->rx_eapol_key_secure = !!(key_info & WPA_KEY_INFO_SECURE); sm->EAPOLKeyReceived = TRUE; sm->EAPOLKeyPairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE); sm->EAPOLKeyRequest = !!(key_info & WPA_KEY_INFO_REQUEST); @@ -995,25 +1158,37 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, } -static void wpa_gmk_to_gtk(const u8 *gmk, const u8 *addr, const u8 *gnonce, - u8 *gtk, size_t gtk_len) +static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr, + const u8 *gnonce, u8 *gtk, size_t gtk_len) { - u8 data[ETH_ALEN + WPA_NONCE_LEN]; + u8 data[ETH_ALEN + WPA_NONCE_LEN + 8 + 16]; + u8 *pos; + int ret = 0; - /* GTK = PRF-X(GMK, "Group key expansion", AA || GNonce) */ + /* GTK = PRF-X(GMK, "Group key expansion", + * AA || GNonce || Time || random data) + * The example described in the IEEE 802.11 standard uses only AA and + * GNonce as inputs here. Add some more entropy since this derivation + * is done only at the Authenticator and as such, does not need to be + * exactly same. + */ os_memcpy(data, addr, ETH_ALEN); os_memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN); + pos = data + ETH_ALEN + WPA_NONCE_LEN; + wpa_get_ntp_timestamp(pos); + pos += 8; + if (random_get_bytes(pos, 16) < 0) + ret = -1; #ifdef CONFIG_IEEE80211W - sha256_prf(gmk, WPA_GMK_LEN, "Group key expansion", - data, sizeof(data), gtk, gtk_len); + sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len); #else /* CONFIG_IEEE80211W */ - sha1_prf(gmk, WPA_GMK_LEN, "Group key expansion", - data, sizeof(data), gtk, gtk_len); + if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len) + < 0) + ret = -1; #endif /* CONFIG_IEEE80211W */ - wpa_hexdump_key(MSG_DEBUG, "GMK", gmk, WPA_GMK_LEN); - wpa_hexdump_key(MSG_DEBUG, "GTK", gtk, gtk_len); + return ret; } @@ -1022,6 +1197,7 @@ static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx) struct wpa_authenticator *wpa_auth = eloop_ctx; struct wpa_state_machine *sm = timeout_ctx; + sm->pending_1_of_4_timeout = 0; wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "EAPOL-Key timeout"); sm->TimeoutEvt = TRUE; wpa_sm_step(sm); @@ -1049,7 +1225,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, version = force_version; else if (wpa_use_aes_cmac(sm)) version = WPA_KEY_INFO_TYPE_AES_128_CMAC; - else if (sm->pairwise == WPA_CIPHER_CCMP) + else if (sm->pairwise != WPA_CIPHER_TKIP) version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else version = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; @@ -1096,20 +1272,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, WPA_PUT_BE16(key->key_info, key_info); alg = pairwise ? sm->pairwise : wpa_auth->conf.wpa_group; - switch (alg) { - case WPA_CIPHER_CCMP: - WPA_PUT_BE16(key->key_length, 16); - break; - case WPA_CIPHER_TKIP: - WPA_PUT_BE16(key->key_length, 32); - break; - case WPA_CIPHER_WEP40: - WPA_PUT_BE16(key->key_length, 5); - break; - case WPA_CIPHER_WEP104: - WPA_PUT_BE16(key->key_length, 13); - break; - } + WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg)); if (key_info & WPA_KEY_INFO_SMK_MESSAGE) WPA_PUT_BE16(key->key_length, 0); @@ -1209,10 +1372,15 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth, keyidx, encr, 0); ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr; - if (ctr == 1) - timeout_ms = eapol_key_timeout_first; + if (ctr == 1 && wpa_auth->conf.tx_status) + timeout_ms = pairwise ? eapol_key_timeout_first : + eapol_key_timeout_first_group; else timeout_ms = eapol_key_timeout_subseq; + if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC)) + sm->pending_1_of_4_timeout = 1; + wpa_printf(MSG_DEBUG, "WPA: Use EAPOL-Key timeout of %u ms (retry " + "counter %d)", timeout_ms, ctr); eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000, wpa_send_eapol_timeout, wpa_auth, sm); } @@ -1247,8 +1415,7 @@ void wpa_remove_ptk(struct wpa_state_machine *sm) { sm->PTK_valid = FALSE; os_memset(&sm->PTK, 0, sizeof(sm->PTK)); - wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, (u8 *) "", - 0); + wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL, 0); sm->pairwise_set = FALSE; eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); } @@ -1338,22 +1505,6 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event) } -static enum wpa_alg wpa_alg_enum(int alg) -{ - switch (alg) { - case WPA_CIPHER_CCMP: - return WPA_ALG_CCMP; - case WPA_CIPHER_TKIP: - return WPA_ALG_TKIP; - case WPA_CIPHER_WEP104: - case WPA_CIPHER_WEP40: - return WPA_ALG_WEP; - default: - return WPA_ALG_NONE; - } -} - - SM_STATE(WPA_PTK, INITIALIZE) { SM_ENTRY_MA(WPA_PTK, INITIALIZE, wpa_ptk); @@ -1411,11 +1562,58 @@ SM_STATE(WPA_PTK, AUTHENTICATION) } +static void wpa_group_ensure_init(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + if (group->first_sta_seen) + return; + /* + * System has run bit further than at the time hostapd was started + * potentially very early during boot up. This provides better chances + * of collecting more randomness on embedded systems. Re-initialize the + * GMK and Counter here to improve their strength if there was not + * enough entropy available immediately after system startup. + */ + wpa_printf(MSG_DEBUG, "WPA: Re-initialize GMK/Counter on first " + "station"); + if (random_pool_ready() != 1) { + wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool " + "to proceed - reject first 4-way handshake"); + group->reject_4way_hs_for_entropy = TRUE; + } else { + group->first_sta_seen = TRUE; + group->reject_4way_hs_for_entropy = FALSE; + } + + wpa_group_init_gmk_and_counter(wpa_auth, group); + wpa_gtk_update(wpa_auth, group); + wpa_group_config_group_keys(wpa_auth, group); +} + + SM_STATE(WPA_PTK, AUTHENTICATION2) { SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk); - os_memcpy(sm->ANonce, sm->group->Counter, WPA_NONCE_LEN); - inc_byte_array(sm->group->Counter, WPA_NONCE_LEN); + + wpa_group_ensure_init(sm->wpa_auth, sm->group); + + /* + * 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); sm->ReAuthenticationRequest = FALSE; /* IEEE 802.11i does not clear TimeoutCtr here, but this is more * logical place than INITIALIZE since AUTHENTICATION2 can be @@ -1531,7 +1729,7 @@ SM_STATE(WPA_PTK, PTKSTART) static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *pmk, struct wpa_ptk *ptk) { - size_t ptk_len = sm->pairwise == WPA_CIPHER_CCMP ? 48 : 64; + size_t ptk_len = sm->pairwise != WPA_CIPHER_TKIP ? 48 : 64; #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) return wpa_auth_derive_ptk_ft(sm, pmk, ptk, ptk_len); @@ -1554,6 +1752,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); sm->EAPOLKeyReceived = FALSE; + sm->update_snonce = FALSE; /* WPA with IEEE 802.1X: use the derived PMK from EAP * WPA-PSK: iterate through possible PSKs and select the one matching @@ -1605,6 +1804,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) } #endif /* CONFIG_IEEE80211R */ + sm->pending_1_of_4_timeout = 0; eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { @@ -1654,6 +1854,14 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, igtk.pn) < 0) os_memset(igtk.pn, 0, sizeof(igtk.pn)); os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN); + if (sm->wpa_auth->conf.disable_gtk) { + /* + * Provide unique random IGTK to each STA to prevent use of + * IGTK in the BSS. + */ + if (random_get_bytes(igtk.igtk, WPA_IGTK_LEN) < 0) + return pos; + } pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK, (const u8 *) &igtk, sizeof(igtk), NULL, 0); @@ -1678,7 +1886,7 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) SM_STATE(WPA_PTK, PTKINITNEGOTIATING) { - u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos; + u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, dummy_gtk[32]; size_t gtk_len, kde_len; struct wpa_group *gsm = sm->group; u8 *wpa_ie; @@ -1716,6 +1924,15 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) secure = 1; gtk = gsm->GTK[gsm->GN - 1]; gtk_len = gsm->GTK_len; + if (sm->wpa_auth->conf.disable_gtk) { + /* + * Provide unique random GTK to each STA to prevent use + * of GTK in the BSS. + */ + if (random_get_bytes(dummy_gtk, gtk_len) < 0) + return; + gtk = dummy_gtk; + } keyidx = gsm->GN; _rsc = rsc; encr = 1; @@ -1726,6 +1943,20 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) gtk_len = 0; keyidx = 0; _rsc = NULL; + if (sm->rx_eapol_key_secure) { + /* + * It looks like Windows 7 supplicant tries to use + * Secure bit in msg 2/4 after having reported Michael + * MIC failure and it then rejects the 4-way handshake + * if msg 3/4 does not set Secure bit. Work around this + * by setting the Secure bit here even in the case of + * WPA if the supplicant used it first. + */ + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "STA used Secure bit in WPA msg 2/4 - " + "set Secure for 3/4 as workaround"); + secure = 1; + } } kde_len = wpa_ie_len + ieee80211w_kde_len(sm); @@ -1813,15 +2044,8 @@ SM_STATE(WPA_PTK, PTKINITDONE) SM_ENTRY_MA(WPA_PTK, PTKINITDONE, wpa_ptk); sm->EAPOLKeyReceived = FALSE; if (sm->Pair) { - enum wpa_alg alg; - int klen; - if (sm->pairwise == WPA_CIPHER_TKIP) { - alg = WPA_ALG_TKIP; - klen = 32; - } else { - alg = WPA_ALG_CCMP; - klen = 16; - } + enum wpa_alg alg = wpa_cipher_to_alg(sm->pairwise); + int klen = wpa_cipher_key_len(sm->pairwise); if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0, sm->PTK.tk1, klen)) { wpa_sta_disconnect(sm->wpa_auth, sm->addr); @@ -1876,8 +2100,11 @@ SM_STEP(WPA_PTK) if (sm->Init) SM_ENTER(WPA_PTK, INITIALIZE); else if (sm->Disconnect - /* || FIX: dot11RSNAConfigSALifetime timeout */) + /* || FIX: dot11RSNAConfigSALifetime timeout */) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "WPA_PTK: sm->Disconnect"); SM_ENTER(WPA_PTK, DISCONNECT); + } else if (sm->DeauthenticationRequest) SM_ENTER(WPA_PTK, DISCONNECTED); else if (sm->AuthenticationRequest) @@ -1913,6 +2140,8 @@ SM_STEP(WPA_PTK) SM_ENTER(WPA_PTK, PTKSTART); else { wpa_auth->dot11RSNA4WayHandshakeFailures++; + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "INITPMK - keyAvailable = false"); SM_ENTER(WPA_PTK, DISCONNECT); } break; @@ -1933,6 +2162,9 @@ SM_STEP(WPA_PTK) else if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { wpa_auth->dot11RSNA4WayHandshakeFailures++; + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "PTKSTART: Retry limit %d reached", + dot11RSNAConfigPairwiseUpdateCount); SM_ENTER(WPA_PTK, DISCONNECT); } else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK, PTKSTART); @@ -1950,12 +2182,18 @@ SM_STEP(WPA_PTK) SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); break; case WPA_PTK_PTKINITNEGOTIATING: - if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && - sm->EAPOLKeyPairwise && sm->MICVerified) + if (sm->update_snonce) + SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); + else if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && + sm->EAPOLKeyPairwise && sm->MICVerified) SM_ENTER(WPA_PTK, PTKINITDONE); else if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { wpa_auth->dot11RSNA4WayHandshakeFailures++; + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "PTKINITNEGOTIATING: Retry limit %d " + "reached", + dot11RSNAConfigPairwiseUpdateCount); SM_ENTER(WPA_PTK, DISCONNECT); } else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); @@ -1984,6 +2222,7 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) struct wpa_group *gsm = sm->group; u8 *kde, *pos, hdr[2]; size_t kde_len; + u8 *gtk, dummy_gtk[32]; SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group); @@ -2004,6 +2243,16 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "sending 1/2 msg of Group Key Handshake"); + gtk = gsm->GTK[gsm->GN - 1]; + if (sm->wpa_auth->conf.disable_gtk) { + /* + * Provide unique random GTK to each STA to prevent use + * of GTK in the BSS. + */ + if (random_get_bytes(dummy_gtk, gsm->GTK_len) < 0) + return; + gtk = dummy_gtk; + } if (sm->wpa == WPA_VERSION_WPA2) { kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len + ieee80211w_kde_len(sm); @@ -2015,10 +2264,10 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) hdr[0] = gsm->GN & 0x03; hdr[1] = 0; pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, - gsm->GTK[gsm->GN - 1], gsm->GTK_len); + gtk, gsm->GTK_len); pos = ieee80211w_kde_add(sm, pos); } else { - kde = gsm->GTK[gsm->GN - 1]; + kde = gtk; pos = kde + gsm->GTK_len; } @@ -2094,20 +2343,24 @@ static int wpa_gtk_update(struct wpa_authenticator *wpa_auth, { int ret = 0; - /* FIX: is this the correct way of getting GNonce? */ os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN); inc_byte_array(group->Counter, WPA_NONCE_LEN); - wpa_gmk_to_gtk(group->GMK, wpa_auth->addr, group->GNonce, - group->GTK[group->GN - 1], group->GTK_len); + if (wpa_gmk_to_gtk(group->GMK, "Group key expansion", + wpa_auth->addr, group->GNonce, + group->GTK[group->GN - 1], group->GTK_len) < 0) + ret = -1; + wpa_hexdump_key(MSG_DEBUG, "GTK", + group->GTK[group->GN - 1], group->GTK_len); #ifdef CONFIG_IEEE80211W if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) { - if (os_get_random(group->IGTK[group->GN_igtk - 4], - WPA_IGTK_LEN) < 0) { - wpa_printf(MSG_INFO, "RSN: Failed to get new random " - "IGTK"); + os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN); + inc_byte_array(group->Counter, WPA_NONCE_LEN); + if (wpa_gmk_to_gtk(group->GMK, "IGTK key expansion", + wpa_auth->addr, group->GNonce, + group->IGTK[group->GN_igtk - 4], + WPA_IGTK_LEN) < 0) ret = -1; - } wpa_hexdump_key(MSG_DEBUG, "IGTK", group->IGTK[group->GN_igtk - 4], WPA_IGTK_LEN); } @@ -2140,28 +2393,118 @@ static void wpa_group_gtk_init(struct wpa_authenticator *wpa_auth, static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx) { + if (ctx != NULL && ctx != sm->group) + return 0; + if (sm->wpa_ptk_state != WPA_PTK_PTKINITDONE) { wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "Not in PTKINITDONE; skip Group Key update"); + sm->GUpdateStationKeys = FALSE; return 0; } if (sm->GUpdateStationKeys) { /* - * This should not really happen, but just in case, make sure - * we do not count the same STA twice in GKeyDoneStations. + * This should not really happen, so add a debug log entry. + * Since we clear the GKeyDoneStations before the loop, the + * station needs to be counted here anyway. */ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, - "GUpdateStationKeys already set - do not " - "increment GKeyDoneStations"); - } else { - sm->group->GKeyDoneStations++; - sm->GUpdateStationKeys = TRUE; + "GUpdateStationKeys was already set when " + "marking station for GTK rekeying"); } + + /* Do not rekey GTK/IGTK when STA is in WNM-Sleep Mode */ + if (sm->is_wnmsleep) + return 0; + + sm->group->GKeyDoneStations++; + sm->GUpdateStationKeys = TRUE; + wpa_sm_step(sm); return 0; } +#ifdef CONFIG_WNM +/* update GTK when exiting WNM-Sleep Mode */ +void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm) +{ + if (sm->is_wnmsleep) + return; + + wpa_group_update_sta(sm, NULL); +} + + +void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag) +{ + sm->is_wnmsleep = !!flag; +} + + +int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos) +{ + struct wpa_group *gsm = sm->group; + u8 *start = pos; + + /* + * GTK subelement: + * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] | + * Key[5..32] + */ + *pos++ = WNM_SLEEP_SUBELEM_GTK; + *pos++ = 11 + gsm->GTK_len; + /* Key ID in B0-B1 of Key Info */ + WPA_PUT_LE16(pos, gsm->GN & 0x03); + pos += 2; + *pos++ = gsm->GTK_len; + if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, pos) != 0) + return 0; + pos += 8; + os_memcpy(pos, gsm->GTK[gsm->GN - 1], gsm->GTK_len); + pos += gsm->GTK_len; + + wpa_printf(MSG_DEBUG, "WNM: GTK Key ID %u in WNM-Sleep Mode exit", + gsm->GN); + wpa_hexdump_key(MSG_DEBUG, "WNM: GTK in WNM-Sleep Mode exit", + gsm->GTK[gsm->GN - 1], gsm->GTK_len); + + return pos - start; +} + + +#ifdef CONFIG_IEEE80211W +int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos) +{ + struct wpa_group *gsm = sm->group; + u8 *start = pos; + + /* + * IGTK subelement: + * Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] | Key[16] + */ + *pos++ = WNM_SLEEP_SUBELEM_IGTK; + *pos++ = 2 + 6 + WPA_IGTK_LEN; + WPA_PUT_LE16(pos, gsm->GN_igtk); + pos += 2; + if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos) != 0) + return 0; + pos += 6; + + os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN); + pos += WPA_IGTK_LEN; + + wpa_printf(MSG_DEBUG, "WNM: IGTK Key ID %u in WNM-Sleep Mode exit", + gsm->GN_igtk); + wpa_hexdump_key(MSG_DEBUG, "WNM: IGTK in WNM-Sleep Mode exit", + gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN); + + return pos - start; +} +#endif /* CONFIG_IEEE80211W */ +#endif /* CONFIG_WNM */ + + static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { @@ -2185,32 +2528,54 @@ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, * including all STAs that could be in not-yet-completed state. */ wpa_gtk_update(wpa_auth, group); - wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, NULL); + if (group->GKeyDoneStations) { + wpa_printf(MSG_DEBUG, "wpa_group_setkeys: Unexpected " + "GKeyDoneStations=%d when starting new GTK rekey", + group->GKeyDoneStations); + group->GKeyDoneStations = 0; + } + wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, group); wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d", group->GKeyDoneStations); } -static void wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth, - struct wpa_group *group) +static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + int ret = 0; + + if (wpa_auth_set_key(wpa_auth, group->vlan_id, + wpa_cipher_to_alg(wpa_auth->conf.wpa_group), + broadcast_ether_addr, group->GN, + group->GTK[group->GN - 1], group->GTK_len) < 0) + ret = -1; + +#ifdef CONFIG_IEEE80211W + if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION && + wpa_auth_set_key(wpa_auth, group->vlan_id, WPA_ALG_IGTK, + broadcast_ether_addr, group->GN_igtk, + group->IGTK[group->GN_igtk - 4], + WPA_IGTK_LEN) < 0) + ret = -1; +#endif /* CONFIG_IEEE80211W */ + + return ret; +} + + +static int wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) { wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " "SETKEYSDONE (VLAN-ID %d)", group->vlan_id); group->changed = TRUE; group->wpa_group_state = WPA_GROUP_SETKEYSDONE; - wpa_auth_set_key(wpa_auth, group->vlan_id, - wpa_alg_enum(wpa_auth->conf.wpa_group), - NULL, group->GN, group->GTK[group->GN - 1], - group->GTK_len); -#ifdef CONFIG_IEEE80211W - if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) { - wpa_auth_set_key(wpa_auth, group->vlan_id, WPA_ALG_IGTK, - NULL, group->GN_igtk, - group->IGTK[group->GN_igtk - 4], - WPA_IGTK_LEN); - } -#endif /* CONFIG_IEEE80211W */ + if (wpa_group_config_group_keys(wpa_auth, group) < 0) + return -1; + + return 0; } @@ -2310,6 +2675,7 @@ void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth) group->GN_igtk = tmp; #endif /* CONFIG_IEEE80211W */ wpa_gtk_update(wpa_auth, group); + wpa_group_config_group_keys(wpa_auth, group); } } @@ -2320,23 +2686,6 @@ static const char * wpa_bool_txt(int bool) } -static int wpa_cipher_bits(int cipher) -{ - switch (cipher) { - case WPA_CIPHER_CCMP: - return 128; - case WPA_CIPHER_TKIP: - return 256; - case WPA_CIPHER_WEP104: - return 104; - case WPA_CIPHER_WEP40: - return 40; - default: - return 0; - } -} - - #define RSN_SUITE "%02x-%02x-%02x-%d" #define RSN_SUITE_ARG(s) \ ((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff @@ -2345,19 +2694,21 @@ int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen) { int len = 0, ret; char pmkid_txt[PMKID_LEN * 2 + 1]; +#ifdef CONFIG_RSN_PREAUTH + const int preauth = 1; +#else /* CONFIG_RSN_PREAUTH */ + const int preauth = 0; +#endif /* CONFIG_RSN_PREAUTH */ if (wpa_auth == NULL) return len; ret = os_snprintf(buf + len, buflen - len, "dot11RSNAOptionImplemented=TRUE\n" -#ifdef CONFIG_RSN_PREAUTH - "dot11RSNAPreauthenticationImplemented=TRUE\n" -#else /* CONFIG_RSN_PREAUTH */ - "dot11RSNAPreauthenticationImplemented=FALSE\n" -#endif /* CONFIG_RSN_PREAUTH */ + "dot11RSNAPreauthenticationImplemented=%s\n" "dot11RSNAEnabled=%s\n" "dot11RSNAPreauthenticationEnabled=%s\n", + wpa_bool_txt(preauth), wpa_bool_txt(wpa_auth->conf.wpa & WPA_PROTO_RSN), wpa_bool_txt(wpa_auth->conf.rsn_preauth)); if (ret < 0 || (size_t) ret >= buflen - len) @@ -2397,7 +2748,7 @@ int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen) !!wpa_auth->conf.wpa_strict_rekey, dot11RSNAConfigGroupUpdateCount, dot11RSNAConfigPairwiseUpdateCount, - wpa_cipher_bits(wpa_auth->conf.wpa_group), + wpa_cipher_key_len(wpa_auth->conf.wpa_group) * 8, dot11RSNAConfigPMKLifetime, dot11RSNAConfigPMKReauthThreshold, dot11RSNAConfigSATimeout, @@ -2440,29 +2791,10 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen) /* dot11RSNAStatsEntry */ - if (sm->wpa == WPA_VERSION_WPA) { - if (sm->pairwise == WPA_CIPHER_CCMP) - pairwise = WPA_CIPHER_SUITE_CCMP; - else if (sm->pairwise == WPA_CIPHER_TKIP) - pairwise = WPA_CIPHER_SUITE_TKIP; - else if (sm->pairwise == WPA_CIPHER_WEP104) - pairwise = WPA_CIPHER_SUITE_WEP104; - else if (sm->pairwise == WPA_CIPHER_WEP40) - pairwise = WPA_CIPHER_SUITE_WEP40; - else if (sm->pairwise == WPA_CIPHER_NONE) - pairwise = WPA_CIPHER_SUITE_NONE; - } else if (sm->wpa == WPA_VERSION_WPA2) { - if (sm->pairwise == WPA_CIPHER_CCMP) - pairwise = RSN_CIPHER_SUITE_CCMP; - else if (sm->pairwise == WPA_CIPHER_TKIP) - pairwise = RSN_CIPHER_SUITE_TKIP; - else if (sm->pairwise == WPA_CIPHER_WEP104) - pairwise = RSN_CIPHER_SUITE_WEP104; - else if (sm->pairwise == WPA_CIPHER_WEP40) - pairwise = RSN_CIPHER_SUITE_WEP40; - else if (sm->pairwise == WPA_CIPHER_NONE) - pairwise = RSN_CIPHER_SUITE_NONE; - } else + pairwise = wpa_cipher_to_suite(sm->wpa == WPA_VERSION_WPA2 ? + WPA_PROTO_RSN : WPA_PROTO_WPA, + sm->pairwise); + if (pairwise == 0) return 0; ret = os_snprintf( @@ -2473,7 +2805,7 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen) "dot11RSNAStatsSelectedPairwiseCipher=" RSN_SUITE "\n" /* TODO: dot11RSNAStatsTKIPICVErrors */ "dot11RSNAStatsTKIPLocalMICFailures=%u\n" - "dot11RSNAStatsTKIPRemoveMICFailures=%u\n" + "dot11RSNAStatsTKIPRemoteMICFailures=%u\n" /* TODO: dot11RSNAStatsCCMPReplays */ /* TODO: dot11RSNAStatsCCMPDecryptErrors */ /* TODO: dot11RSNAStatsTKIPReplays */, @@ -2570,7 +2902,8 @@ const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, size_t *len) int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, int session_timeout, struct eapol_state_machine *eapol) { - if (sm == NULL || sm->wpa != WPA_VERSION_WPA2) + if (sm == NULL || sm->wpa != WPA_VERSION_WPA2 || + sm->wpa_auth->conf.disable_pmksa_caching) return -1; if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, PMK_LEN, @@ -2609,7 +2942,7 @@ wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id) wpa_printf(MSG_DEBUG, "WPA: Add group state machine for VLAN-ID %d", vlan_id); - group = wpa_group_init(wpa_auth, vlan_id); + group = wpa_group_init(wpa_auth, vlan_id, 0); if (group == NULL) return NULL; @@ -2649,3 +2982,41 @@ int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id) sm->group = group; return 0; } + + +void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, int ack) +{ + if (wpa_auth == NULL || sm == NULL) + return; + wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key TX status for STA " MACSTR + " ack=%d", MAC2STR(sm->addr), ack); + if (sm->pending_1_of_4_timeout && ack) { + /* + * Some deployed supplicant implementations update their SNonce + * for each EAPOL-Key 2/4 message even within the same 4-way + * handshake and then fail to use the first SNonce when + * deriving the PTK. This results in unsuccessful 4-way + * handshake whenever the relatively short initial timeout is + * reached and EAPOL-Key 1/4 is retransmitted. Try to work + * around this by increasing the timeout now that we know that + * the station has received the frame. + */ + int timeout_ms = eapol_key_timeout_subseq; + wpa_printf(MSG_DEBUG, "WPA: Increase initial EAPOL-Key 1/4 " + "timeout by %u ms because of acknowledged frame", + timeout_ms); + eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm); + eloop_register_timeout(timeout_ms / 1000, + (timeout_ms % 1000) * 1000, + wpa_send_eapol_timeout, wpa_auth, sm); + } +} + + +int wpa_auth_uses_sae(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return 0; + return wpa_key_mgmt_sae(sm->wpa_key_mgmt); +} diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index d0136c71b6665..465eec6a53301 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -2,14 +2,8 @@ * hostapd - IEEE 802.11i-2004 / WPA Authenticator * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_AUTH_H @@ -143,7 +137,9 @@ struct wpa_auth_config { int peerkey; int wmm_enabled; int wmm_uapsd; + int disable_pmksa_caching; int okc; + int tx_status; #ifdef CONFIG_IEEE80211W enum mfp_options ieee80211w; #endif /* CONFIG_IEEE80211W */ @@ -160,7 +156,10 @@ struct wpa_auth_config { struct ft_remote_r0kh *r0kh_list; struct ft_remote_r1kh *r1kh_list; int pmk_r1_push; + int ft_over_ds; #endif /* CONFIG_IEEE80211R */ + int disable_gtk; + int ap_mlme; }; typedef enum { @@ -178,7 +177,7 @@ struct wpa_auth_callbacks { void (*logger)(void *ctx, const u8 *addr, logger_level level, const char *txt); void (*disconnect)(void *ctx, const u8 *addr, u16 reason); - void (*mic_failure_report)(void *ctx, const u8 *addr); + int (*mic_failure_report)(void *ctx, const u8 *addr); void (*set_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var, int value); int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var); @@ -199,12 +198,15 @@ struct wpa_auth_callbacks { struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr); int (*send_ft_action)(void *ctx, const u8 *dst, const u8 *data, size_t data_len); + int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie, + size_t tspec_ielen); #endif /* CONFIG_IEEE80211R */ }; struct wpa_authenticator * wpa_init(const u8 *addr, struct wpa_auth_config *conf, struct wpa_auth_callbacks *cb); +int wpa_init_keys(struct wpa_authenticator *wpa_auth); void wpa_deinit(struct wpa_authenticator *wpa_auth); int wpa_reconfig(struct wpa_authenticator *wpa_auth, struct wpa_auth_config *conf); @@ -259,6 +261,8 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, int session_timeout, struct eapol_state_machine *eapol); int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id); +void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, int ack); #ifdef CONFIG_IEEE80211R u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, @@ -278,4 +282,11 @@ int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr); #endif /* CONFIG_IEEE80211R */ +void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm); +void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag); +int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos); +int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos); + +int wpa_auth_uses_sae(struct wpa_state_machine *sm); + #endif /* WPA_AUTH_H */ diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c index c9871d9a61bf9..48bf79b9b9b11 100644 --- a/src/ap/wpa_auth_ft.c +++ b/src/ap/wpa_auth_ft.c @@ -2,14 +2,8 @@ * hostapd - IEEE 802.11r - Fast BSS Transition * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -18,38 +12,16 @@ #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "crypto/aes_wrap.h" +#include "crypto/random.h" #include "ap_config.h" #include "ieee802_11.h" #include "wmm.h" #include "wpa_auth.h" #include "wpa_auth_i.h" -#include "wpa_auth_ie.h" #ifdef CONFIG_IEEE80211R -struct wpa_ft_ies { - const u8 *mdie; - size_t mdie_len; - const u8 *ftie; - size_t ftie_len; - const u8 *r1kh_id; - const u8 *gtk; - size_t gtk_len; - const u8 *r0kh_id; - size_t r0kh_id_len; - const u8 *rsn; - size_t rsn_len; - const u8 *rsn_pmkid; - const u8 *ric; - size_t ric_len; -}; - - -static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, - struct wpa_ft_ies *parse); - - static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst, const u8 *data, size_t data_len) { @@ -80,6 +52,19 @@ wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr) } +static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr, + u8 *tspec_ie, size_t tspec_ielen) +{ + if (wpa_auth->cb.add_tspec == NULL) { + wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized"); + return -1; + } + return wpa_auth->cb.add_tspec(wpa_auth->cb.ctx, sta_addr, tspec_ie, + tspec_ielen); +} + + int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len) { u8 *pos = buf; @@ -91,7 +76,9 @@ int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len) *pos++ = MOBILITY_DOMAIN_ID_LEN + 1; os_memcpy(pos, conf->mobility_domain, MOBILITY_DOMAIN_ID_LEN); pos += MOBILITY_DOMAIN_ID_LEN; - capab = RSN_FT_CAPAB_FT_OVER_DS; + capab = 0; + if (conf->ft_over_ds) + capab |= RSN_FT_CAPAB_FT_OVER_DS; *pos++ = capab; return pos - buf; @@ -334,7 +321,7 @@ static int wpa_ft_pull_pmk_r1(struct wpa_authenticator *wpa_auth, /* aes_wrap() does not support inplace encryption, so use a temporary * buffer for the data. */ - if (os_get_random(f.nonce, sizeof(f.nonce))) { + if (random_get_bytes(f.nonce, sizeof(f.nonce))) { wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " "nonce"); return -1; @@ -497,7 +484,8 @@ static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len) #endif /* CONFIG_IEEE80211W */ -static u8 * wpa_ft_process_rdie(u8 *pos, u8 *end, u8 id, u8 descr_count, +static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm, + u8 *pos, u8 *end, u8 id, u8 descr_count, const u8 *ies, size_t ies_len) { struct ieee802_11_elems parse; @@ -530,7 +518,7 @@ static u8 * wpa_ft_process_rdie(u8 *pos, u8 *end, u8 id, u8 descr_count, } #ifdef NEED_AP_MLME - if (parse.wmm_tspec) { + if (parse.wmm_tspec && sm->wpa_auth->conf.ap_mlme) { struct wmm_tspec_element *tspec; int res; @@ -567,13 +555,35 @@ static u8 * wpa_ft_process_rdie(u8 *pos, u8 *end, u8 id, u8 descr_count, } #endif /* NEED_AP_MLME */ + if (parse.wmm_tspec && !sm->wpa_auth->conf.ap_mlme) { + struct wmm_tspec_element *tspec; + int res; + + tspec = (struct wmm_tspec_element *) pos; + os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec)); + res = wpa_ft_add_tspec(sm->wpa_auth, sm->addr, pos, + sizeof(*tspec)); + if (res >= 0) { + if (res) + rdie->status_code = host_to_le16(res); + else { + /* TSPEC accepted; include updated TSPEC in + * response */ + rdie->descr_count = 1; + pos += sizeof(*tspec); + } + return pos; + } + } + wpa_printf(MSG_DEBUG, "FT: No supported resource requested"); rdie->status_code = host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE); return pos; } -static u8 * wpa_ft_process_ric(u8 *pos, u8 *end, const u8 *ric, size_t ric_len) +static u8 * wpa_ft_process_ric(struct wpa_state_machine *sm, u8 *pos, u8 *end, + const u8 *ric, size_t ric_len) { const u8 *rpos, *start; const struct rsn_rdie *rdie; @@ -595,7 +605,7 @@ static u8 * wpa_ft_process_ric(u8 *pos, u8 *end, const u8 *ric, size_t ric_len) break; rpos += 2 + rpos[1]; } - pos = wpa_ft_process_rdie(pos, end, rdie->id, + pos = wpa_ft_process_rdie(sm, pos, end, rdie->id, rdie->descr_count, start, rpos - start); } @@ -704,7 +714,8 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, ric_start = pos; if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse) == 0 && parse.ric) { - pos = wpa_ft_process_ric(pos, end, parse.ric, parse.ric_len); + pos = wpa_ft_process_ric(sm, pos, end, parse.ric, + parse.ric_len); if (auth_alg == WLAN_AUTH_FT) _ftie->mic_control[1] += ieee802_11_ie_count(ric_start, @@ -725,143 +736,6 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, } -static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len, - struct wpa_ft_ies *parse) -{ - const u8 *end, *pos; - - parse->ftie = ie; - parse->ftie_len = ie_len; - - pos = ie + sizeof(struct rsn_ftie); - end = ie + ie_len; - - while (pos + 2 <= end && pos + 2 + pos[1] <= end) { - switch (pos[0]) { - case FTIE_SUBELEM_R1KH_ID: - if (pos[1] != FT_R1KH_ID_LEN) { - wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID " - "length in FTIE: %d", pos[1]); - return -1; - } - parse->r1kh_id = pos + 2; - break; - case FTIE_SUBELEM_GTK: - parse->gtk = pos + 2; - parse->gtk_len = pos[1]; - break; - case FTIE_SUBELEM_R0KH_ID: - if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) { - wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID " - "length in FTIE: %d", pos[1]); - return -1; - } - parse->r0kh_id = pos + 2; - parse->r0kh_id_len = pos[1]; - break; - } - - pos += 2 + pos[1]; - } - - return 0; -} - - -static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, - struct wpa_ft_ies *parse) -{ - const u8 *end, *pos; - struct wpa_ie_data data; - int ret; - const struct rsn_ftie *ftie; - int prot_ie_count = 0; - - os_memset(parse, 0, sizeof(*parse)); - if (ies == NULL) - return 0; - - pos = ies; - end = ies + ies_len; - while (pos + 2 <= end && pos + 2 + pos[1] <= end) { - switch (pos[0]) { - case WLAN_EID_RSN: - parse->rsn = pos + 2; - parse->rsn_len = pos[1]; - ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2, - parse->rsn_len + 2, - &data); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "FT: Failed to parse " - "RSN IE: %d", ret); - return -1; - } - if (data.num_pmkid == 1 && data.pmkid) - parse->rsn_pmkid = data.pmkid; - break; - case WLAN_EID_MOBILITY_DOMAIN: - parse->mdie = pos + 2; - parse->mdie_len = pos[1]; - break; - case WLAN_EID_FAST_BSS_TRANSITION: - if (pos[1] < sizeof(*ftie)) - return -1; - ftie = (const struct rsn_ftie *) (pos + 2); - prot_ie_count = ftie->mic_control[1]; - if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0) - return -1; - break; - case WLAN_EID_RIC_DATA: - if (parse->ric == NULL) - parse->ric = pos; - } - - pos += 2 + pos[1]; - } - - if (prot_ie_count == 0) - return 0; /* no MIC */ - - /* - * Check that the protected IE count matches with IEs included in the - * frame. - */ - if (parse->rsn) - prot_ie_count--; - if (parse->mdie) - prot_ie_count--; - if (parse->ftie) - prot_ie_count--; - if (prot_ie_count < 0) { - wpa_printf(MSG_DEBUG, "FT: Some required IEs not included in " - "the protected IE count"); - return -1; - } - - if (prot_ie_count == 0 && parse->ric) { - wpa_printf(MSG_DEBUG, "FT: RIC IE(s) in the frame, but not " - "included in protected IE count"); - return -1; - } - - /* Determine the end of the RIC IE(s) */ - pos = parse->ric; - while (pos && pos + 2 <= end && pos + 2 + pos[1] <= end && - prot_ie_count) { - prot_ie_count--; - pos += 2 + pos[1]; - } - parse->ric_len = pos - parse->ric; - if (prot_ie_count) { - wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from " - "frame", (int) prot_ie_count); - return -1; - } - - return 0; -} - - static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth, int vlan_id, enum wpa_alg alg, const u8 *addr, int idx, @@ -880,13 +754,9 @@ void wpa_ft_install_ptk(struct wpa_state_machine *sm) int klen; /* MLME-SETKEYS.request(PTK) */ - if (sm->pairwise == WPA_CIPHER_TKIP) { - alg = WPA_ALG_TKIP; - klen = 32; - } else if (sm->pairwise == WPA_CIPHER_CCMP) { - alg = WPA_ALG_CCMP; - klen = 16; - } else { + alg = wpa_cipher_to_alg(sm->pairwise); + klen = wpa_cipher_key_len(sm->pairwise); + if (!wpa_cipher_valid_pairwise(sm->pairwise)) { wpa_printf(MSG_DEBUG, "FT: Unknown pairwise alg 0x%x - skip " "PTK configuration", sm->pairwise); return; @@ -997,7 +867,7 @@ static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm, sm->pmk_r1_name_valid = 1; os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN); - if (os_get_random(sm->ANonce, WPA_NONCE_LEN)) { + if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) { wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " "ANonce"); return WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -1008,7 +878,7 @@ static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm, wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce", sm->ANonce, WPA_NONCE_LEN); - ptk_len = pairwise != WPA_CIPHER_CCMP ? 64 : 48; + ptk_len = pairwise == WPA_CIPHER_TKIP ? 64 : 48; wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, sm->wpa_auth->addr, pmk_r1_name, (u8 *) &sm->PTK, ptk_len, ptk_name); @@ -1204,7 +1074,7 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, count = 3; if (parse.ric) - count++; + count += ieee802_11_ie_count(parse.ric, parse.ric_len); if (ftie->mic_control[1] != count) { wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC " "Control: received %u expected %u", @@ -1224,8 +1094,16 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, if (os_memcmp(mic, ftie->mic, 16) != 0) { wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE"); + wpa_printf(MSG_DEBUG, "FT: addr=" MACSTR " auth_addr=" MACSTR, + MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr)); wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16); wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16); + wpa_hexdump(MSG_MSGDUMP, "FT: MDIE", + parse.mdie - 2, parse.mdie_len + 2); + wpa_hexdump(MSG_MSGDUMP, "FT: FTIE", + parse.ftie - 2, parse.ftie_len + 2); + wpa_hexdump(MSG_MSGDUMP, "FT: RSN", + parse.rsn - 2, parse.rsn_len + 2); return WLAN_STATUS_INVALID_FTIE; } diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index afa13a698bef8..76c61ea18e06b 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -1,15 +1,9 @@ /* * hostapd / WPA authenticator glue code - * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -29,17 +23,13 @@ #include "ap_drv_ops.h" #include "ap_config.h" #include "wpa_auth.h" - - -#ifdef CONFIG_IEEE80211R -static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf, - size_t len); -#endif /* CONFIG_IEEE80211R */ +#include "wpa_auth_glue.h" static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, struct wpa_auth_config *wconf) { + os_memset(wconf, 0, sizeof(*wconf)); wconf->wpa = conf->wpa; wconf->wpa_key_mgmt = conf->wpa_key_mgmt; wconf->wpa_pairwise = conf->wpa_pairwise; @@ -54,6 +44,7 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, wconf->peerkey = conf->peerkey; wconf->wmm_enabled = conf->wmm_enabled; wconf->wmm_uapsd = conf->wmm_uapsd; + wconf->disable_pmksa_caching = conf->disable_pmksa_caching; wconf->okc = conf->okc; #ifdef CONFIG_IEEE80211W wconf->ieee80211w = conf->ieee80211w; @@ -77,7 +68,11 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, wconf->r0kh_list = conf->r0kh_list; wconf->r1kh_list = conf->r1kh_list; wconf->pmk_r1_push = conf->pmk_r1_push; + wconf->ft_over_ds = conf->ft_over_ds; #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_HS20 + wconf->disable_gtk = conf->disable_dgaf; +#endif /* CONFIG_HS20 */ } @@ -117,10 +112,10 @@ static void hostapd_wpa_auth_disconnect(void *ctx, const u8 *addr, } -static void hostapd_wpa_auth_mic_failure_report(void *ctx, const u8 *addr) +static int hostapd_wpa_auth_mic_failure_report(void *ctx, const u8 *addr) { struct hostapd_data *hapd = ctx; - michael_mic_failure(hapd, addr, 0); + return michael_mic_failure(hapd, addr, 0); } @@ -188,7 +183,24 @@ static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr, const u8 *prev_psk) { struct hostapd_data *hapd = ctx; - return hostapd_get_psk(hapd->conf, addr, prev_psk); + struct sta_info *sta = ap_get_sta(hapd, addr); + const u8 *psk = hostapd_get_psk(hapd->conf, addr, prev_psk); + /* + * This is about to iterate over all psks, prev_psk gives the last + * returned psk which should not be returned again. + * logic list (all hostapd_get_psk; all sta->psk) + */ + if (sta && sta->psk && !psk) { + struct hostapd_sta_wpa_psk_short *pos; + psk = sta->psk->psk; + for (pos = sta->psk; pos; pos = pos->next) { + if (pos->psk == prev_psk) { + psk = pos->next ? pos->next->psk : NULL; + break; + } + } + } + return psk; } @@ -230,8 +242,8 @@ static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg, return -1; } - return hapd->drv.set_key(ifname, hapd, alg, addr, idx, 1, NULL, 0, - key, key_len); + return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, 1, NULL, 0, + key, key_len); } @@ -248,7 +260,15 @@ static int hostapd_wpa_auth_send_eapol(void *ctx, const u8 *addr, int encrypt) { struct hostapd_data *hapd = ctx; - return hapd->drv.send_eapol(hapd, addr, data, data_len, encrypt); + struct sta_info *sta; + u32 flags = 0; + + sta = ap_get_sta(hapd, addr); + if (sta) + flags = hostapd_sta_flags_to_drv(sta->flags); + + return hostapd_drv_hapd_send_eapol(hapd, addr, data, data_len, + encrypt, flags); } @@ -291,12 +311,13 @@ static int hostapd_wpa_auth_for_each_auth( { struct hostapd_data *hapd = ctx; struct wpa_auth_iface_iter_data data; - if (hapd->iface->for_each_interface == NULL) + if (hapd->iface->interfaces == NULL || + hapd->iface->interfaces->for_each_interface == NULL) return -1; data.cb = cb; data.cb_ctx = cb_ctx; - return hapd->iface->for_each_interface(hapd->iface->interfaces, - wpa_auth_iface_iter, &data); + return hapd->iface->interfaces->for_each_interface( + hapd->iface->interfaces, wpa_auth_iface_iter, &data); } @@ -327,8 +348,9 @@ static int hostapd_wpa_auth_ft_iter(struct hostapd_iface *iface, void *ctx) MAC2STR(idata->src_hapd->own_addr), idata->src_hapd->conf->iface, MAC2STR(hapd->own_addr), hapd->conf->iface); - hostapd_rrb_receive(hapd, idata->src_hapd->own_addr, - idata->data, idata->data_len); + wpa_ft_rrb_rx(hapd->wpa_auth, + idata->src_hapd->own_addr, + idata->data, idata->data_len); return 1; } } @@ -343,18 +365,21 @@ static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto, const u8 *data, size_t data_len) { struct hostapd_data *hapd = ctx; + struct l2_ethhdr *buf; + int ret; #ifdef CONFIG_IEEE80211R - if (proto == ETH_P_RRB && hapd->iface->for_each_interface) { + if (proto == ETH_P_RRB && hapd->iface->interfaces && + hapd->iface->interfaces->for_each_interface) { int res; struct wpa_auth_ft_iface_iter_data idata; idata.src_hapd = hapd; idata.dst = dst; idata.data = data; idata.data_len = data_len; - res = hapd->iface->for_each_interface(hapd->iface->interfaces, - hostapd_wpa_auth_ft_iter, - &idata); + res = hapd->iface->interfaces->for_each_interface( + hapd->iface->interfaces, hostapd_wpa_auth_ft_iter, + &idata); if (res == 1) return data_len; } @@ -366,7 +391,18 @@ static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto, data, data_len); if (hapd->l2 == NULL) return -1; - return l2_packet_send(hapd->l2, dst, proto, data, data_len); + + buf = os_malloc(sizeof(*buf) + data_len); + if (buf == NULL) + return -1; + os_memcpy(buf->h_dest, dst, ETH_ALEN); + os_memcpy(buf->h_source, hapd->own_addr, ETH_ALEN); + buf->h_proto = host_to_be16(proto); + os_memcpy(buf + 1, data, data_len); + ret = l2_packet_send(hapd->l2, dst, proto, (u8 *) buf, + sizeof(*buf) + data_len); + os_free(buf); + return ret; } @@ -396,7 +432,7 @@ static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst, os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN); os_memcpy(&m->u, data, data_len); - res = hapd->drv.send_mgmt_frame(hapd, (u8 *) m, mlen); + res = hostapd_drv_send_mlme(hapd, (u8 *) m, mlen, 0); os_free(m); return res; } @@ -408,6 +444,9 @@ hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr) struct hostapd_data *hapd = ctx; struct sta_info *sta; + if (hostapd_add_sta_node(hapd, sta_addr, WLAN_AUTH_FT) < 0) + return NULL; + sta = ap_sta_add(hapd, sta_addr); if (sta == NULL) return NULL; @@ -431,7 +470,22 @@ static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { struct hostapd_data *hapd = ctx; - wpa_ft_rrb_rx(hapd->wpa_auth, src_addr, buf, len); + struct l2_ethhdr *ethhdr; + if (len < sizeof(*ethhdr)) + return; + ethhdr = (struct l2_ethhdr *) buf; + wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> " + MACSTR, MAC2STR(ethhdr->h_source), MAC2STR(ethhdr->h_dest)); + wpa_ft_rrb_rx(hapd->wpa_auth, ethhdr->h_source, buf + sizeof(*ethhdr), + len - sizeof(*ethhdr)); +} + + +static int hostapd_wpa_auth_add_tspec(void *ctx, const u8 *sta_addr, + u8 *tspec_ie, size_t tspec_ielen) +{ + struct hostapd_data *hapd = ctx; + return hostapd_add_tspec(hapd, sta_addr, tspec_ie, tspec_ielen); } #endif /* CONFIG_IEEE80211R */ @@ -445,6 +499,10 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) size_t wpa_ie_len; hostapd_wpa_auth_conf(hapd->conf, &_conf); + if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EAPOL_TX_STATUS) + _conf.tx_status = 1; + if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_MLME) + _conf.ap_mlme = 1; os_memset(&cb, 0, sizeof(cb)); cb.ctx = hapd; cb.logger = hostapd_wpa_auth_logger; @@ -463,6 +521,7 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) #ifdef CONFIG_IEEE80211R cb.send_ft_action = hostapd_wpa_auth_send_ft_action; cb.add_sta = hostapd_wpa_auth_add_sta; + cb.add_tspec = hostapd_wpa_auth_add_tspec; #endif /* CONFIG_IEEE80211R */ hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb); if (hapd->wpa_auth == NULL) { @@ -494,7 +553,7 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) hapd->l2 = l2_packet_init(hapd->conf->bridge[0] ? hapd->conf->bridge : hapd->conf->iface, NULL, ETH_P_RRB, - hostapd_rrb_receive, hapd, 0); + hostapd_rrb_receive, hapd, 1); if (hapd->l2 == NULL && (hapd->driver == NULL || hapd->driver->send_ether == NULL)) { @@ -520,6 +579,7 @@ void hostapd_reconfig_wpa(struct hostapd_data *hapd) void hostapd_deinit_wpa(struct hostapd_data *hapd) { + ieee80211_tkip_countermeasures_deinit(hapd); rsn_preauth_iface_deinit(hapd); if (hapd->wpa_auth) { wpa_deinit(hapd->wpa_auth); diff --git a/src/ap/wpa_auth_glue.h b/src/ap/wpa_auth_glue.h index 79d7e05c40550..1b13ae7bef89c 100644 --- a/src/ap/wpa_auth_glue.h +++ b/src/ap/wpa_auth_glue.h @@ -2,14 +2,8 @@ * hostapd / WPA authenticator glue code * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_AUTH_GLUE_H diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h index b69129ff44cf3..97489d343d86f 100644 --- a/src/ap/wpa_auth_i.h +++ b/src/ap/wpa_auth_i.h @@ -2,14 +2,8 @@ * hostapd - IEEE 802.11i-2004 / WPA Authenticator: Internal definitions * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_AUTH_I_H @@ -69,10 +63,11 @@ struct wpa_state_machine { Boolean pairwise_set; int keycount; Boolean Pair; - struct { + struct wpa_key_replay_counter { u8 counter[WPA_REPLAY_COUNTER_LEN]; Boolean valid; - } key_replay[RSNA_MAX_EAPOL_RETRIES]; + } key_replay[RSNA_MAX_EAPOL_RETRIES], + prev_key_replay[RSNA_MAX_EAPOL_RETRIES]; Boolean PInitAKeys; /* WPA only, not in IEEE 802.11i */ Boolean PTKRequest; /* not in IEEE 802.11i state machine */ Boolean has_GTK; @@ -86,10 +81,13 @@ struct wpa_state_machine { unsigned int pending_deinit:1; unsigned int started:1; unsigned int mgmt_frame_prot:1; + unsigned int rx_eapol_key_secure:1; + unsigned int update_snonce:1; #ifdef CONFIG_IEEE80211R unsigned int ft_completed:1; unsigned int pmk_r1_name_valid:1; #endif /* CONFIG_IEEE80211R */ + unsigned int is_wnmsleep:1; u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN]; int req_replay_counter_used; @@ -120,6 +118,8 @@ struct wpa_state_machine { * message 2/4 */ u8 *assoc_resp_ftie; #endif /* CONFIG_IEEE80211R */ + + int pending_1_of_4_timeout; }; @@ -145,6 +145,8 @@ struct wpa_group { u8 GTK[2][WPA_GTK_MAX_LEN]; u8 GNonce[WPA_NONCE_LEN]; Boolean changed; + Boolean first_sta_seen; + Boolean reject_4way_hs_for_entropy; #ifdef CONFIG_IEEE80211W u8 IGTK[2][WPA_IGTK_LEN]; int GN_igtk, GM_igtk; diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c index f8a1804c9f71e..4fd0135fe6ea0 100644 --- a/src/ap/wpa_auth_ie.c +++ b/src/ap/wpa_auth_ie.c @@ -2,14 +2,8 @@ * hostapd - WPA/RSN IE and KDE definitions * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -25,11 +19,17 @@ #include "wpa_auth_i.h" +#ifdef CONFIG_RSN_TESTING +int rsn_testing = 0; +#endif /* CONFIG_RSN_TESTING */ + + static int wpa_write_wpa_ie(struct wpa_auth_config *conf, u8 *buf, size_t len) { struct wpa_ie_hdr *hdr; int num_suites; u8 *pos, *count; + u32 suite; hdr = (struct wpa_ie_hdr *) buf; hdr->elem_id = WLAN_EID_VENDOR_SPECIFIC; @@ -37,46 +37,25 @@ static int wpa_write_wpa_ie(struct wpa_auth_config *conf, u8 *buf, size_t len) WPA_PUT_LE16(hdr->version, WPA_VERSION); pos = (u8 *) (hdr + 1); - if (conf->wpa_group == WPA_CIPHER_CCMP) { - RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP); - } else if (conf->wpa_group == WPA_CIPHER_TKIP) { - RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP); - } else if (conf->wpa_group == WPA_CIPHER_WEP104) { - RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP104); - } else if (conf->wpa_group == WPA_CIPHER_WEP40) { - RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP40); - } else { + suite = wpa_cipher_to_suite(WPA_PROTO_WPA, conf->wpa_group); + if (suite == 0) { wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).", conf->wpa_group); return -1; } + RSN_SELECTOR_PUT(pos, suite); pos += WPA_SELECTOR_LEN; - num_suites = 0; count = pos; pos += 2; - if (conf->wpa_pairwise & WPA_CIPHER_CCMP) { - RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP); - pos += WPA_SELECTOR_LEN; - num_suites++; - } - if (conf->wpa_pairwise & WPA_CIPHER_TKIP) { - RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP); - pos += WPA_SELECTOR_LEN; - num_suites++; - } - if (conf->wpa_pairwise & WPA_CIPHER_NONE) { - RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE); - pos += WPA_SELECTOR_LEN; - num_suites++; - } - + num_suites = wpa_cipher_put_suites(pos, conf->wpa_pairwise); if (num_suites == 0) { wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).", conf->wpa_pairwise); return -1; } + pos += num_suites * WPA_SELECTOR_LEN; WPA_PUT_LE16(count, num_suites); num_suites = 0; @@ -113,49 +92,48 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, const u8 *pmkid) { struct rsn_ie_hdr *hdr; - int num_suites; + int num_suites, res; u8 *pos, *count; u16 capab; + u32 suite; hdr = (struct rsn_ie_hdr *) buf; hdr->elem_id = WLAN_EID_RSN; WPA_PUT_LE16(hdr->version, RSN_VERSION); pos = (u8 *) (hdr + 1); - if (conf->wpa_group == WPA_CIPHER_CCMP) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); - } else if (conf->wpa_group == WPA_CIPHER_TKIP) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); - } else if (conf->wpa_group == WPA_CIPHER_WEP104) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP104); - } else if (conf->wpa_group == WPA_CIPHER_WEP40) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP40); - } else { + suite = wpa_cipher_to_suite(WPA_PROTO_RSN, conf->wpa_group); + if (suite == 0) { wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).", conf->wpa_group); return -1; } + RSN_SELECTOR_PUT(pos, suite); pos += RSN_SELECTOR_LEN; num_suites = 0; count = pos; pos += 2; - if (conf->rsn_pairwise & WPA_CIPHER_CCMP) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); - pos += RSN_SELECTOR_LEN; - num_suites++; - } - if (conf->rsn_pairwise & WPA_CIPHER_TKIP) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); +#ifdef CONFIG_RSN_TESTING + if (rsn_testing) { + RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 1)); pos += RSN_SELECTOR_LEN; num_suites++; } - if (conf->rsn_pairwise & WPA_CIPHER_NONE) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE); +#endif /* CONFIG_RSN_TESTING */ + + res = rsn_cipher_put_suites(pos, conf->rsn_pairwise); + num_suites += res; + pos += res * RSN_SELECTOR_LEN; + +#ifdef CONFIG_RSN_TESTING + if (rsn_testing) { + RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 2)); pos += RSN_SELECTOR_LEN; num_suites++; } +#endif /* CONFIG_RSN_TESTING */ if (num_suites == 0) { wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).", @@ -168,6 +146,14 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, count = pos; pos += 2; +#ifdef CONFIG_RSN_TESTING + if (rsn_testing) { + RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 1)); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_RSN_TESTING */ + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X); pos += RSN_SELECTOR_LEN; @@ -202,6 +188,26 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, num_suites++; } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_SAE + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_SAE */ + +#ifdef CONFIG_RSN_TESTING + if (rsn_testing) { + RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 2)); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_RSN_TESTING */ if (num_suites == 0) { wpa_printf(MSG_DEBUG, "Invalid key management type (%d).", @@ -227,6 +233,10 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, capab |= WPA_CAPABILITY_MFPR; } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_RSN_TESTING + if (rsn_testing) + capab |= BIT(8) | BIT(14) | BIT(15); +#endif /* CONFIG_RSN_TESTING */ WPA_PUT_LE16(pos, capab); pos += 2; @@ -256,6 +266,29 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_RSN_TESTING + if (rsn_testing) { + /* + * Fill in any defined fields and add extra data to the end of + * the element. + */ + int pmkid_count_set = pmkid != NULL; + if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) + pmkid_count_set = 1; + /* PMKID Count */ + WPA_PUT_LE16(pos, 0); + pos += 2; + if (conf->ieee80211w == NO_MGMT_FRAME_PROTECTION) { + /* Management Group Cipher Suite */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); + pos += RSN_SELECTOR_LEN; + } + + os_memset(pos, 0x12, 17); + pos += 17; + } +#endif /* CONFIG_RSN_TESTING */ + hdr->len = (pos - buf) - 2; return pos - buf; @@ -277,8 +310,7 @@ int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth) pos += res; } #ifdef CONFIG_IEEE80211R - if (wpa_auth->conf.wpa_key_mgmt & - (WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_FT_PSK)) { + if (wpa_key_mgmt_ft(wpa_auth->conf.wpa_key_mgmt)) { res = wpa_write_mdie(&wpa_auth->conf, pos, buf + sizeof(buf) - pos); if (res < 0) @@ -322,114 +354,6 @@ u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len, } -static int wpa_selector_to_bitfield(const u8 *s) -{ - if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_NONE) - return WPA_CIPHER_NONE; - if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP40) - return WPA_CIPHER_WEP40; - if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_TKIP) - return WPA_CIPHER_TKIP; - if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_CCMP) - return WPA_CIPHER_CCMP; - if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP104) - return WPA_CIPHER_WEP104; - return 0; -} - - -static int wpa_key_mgmt_to_bitfield(const u8 *s) -{ - if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_UNSPEC_802_1X) - return WPA_KEY_MGMT_IEEE8021X; - if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X) - return WPA_KEY_MGMT_PSK; - if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_NONE) - return WPA_KEY_MGMT_WPA_NONE; - return 0; -} - - -static int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, - struct wpa_ie_data *data) -{ - const struct wpa_ie_hdr *hdr; - const u8 *pos; - int left; - int i, count; - - os_memset(data, 0, sizeof(*data)); - data->pairwise_cipher = WPA_CIPHER_TKIP; - data->group_cipher = WPA_CIPHER_TKIP; - data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; - data->mgmt_group_cipher = 0; - - if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) - return -1; - - hdr = (const struct wpa_ie_hdr *) wpa_ie; - - if (hdr->elem_id != WLAN_EID_VENDOR_SPECIFIC || - hdr->len != wpa_ie_len - 2 || - RSN_SELECTOR_GET(hdr->oui) != WPA_OUI_TYPE || - WPA_GET_LE16(hdr->version) != WPA_VERSION) { - return -2; - } - - pos = (const u8 *) (hdr + 1); - left = wpa_ie_len - sizeof(*hdr); - - if (left >= WPA_SELECTOR_LEN) { - data->group_cipher = wpa_selector_to_bitfield(pos); - pos += WPA_SELECTOR_LEN; - left -= WPA_SELECTOR_LEN; - } else if (left > 0) - return -3; - - if (left >= 2) { - data->pairwise_cipher = 0; - count = WPA_GET_LE16(pos); - pos += 2; - left -= 2; - if (count == 0 || left < count * WPA_SELECTOR_LEN) - return -4; - for (i = 0; i < count; i++) { - data->pairwise_cipher |= wpa_selector_to_bitfield(pos); - pos += WPA_SELECTOR_LEN; - left -= WPA_SELECTOR_LEN; - } - } else if (left == 1) - return -5; - - if (left >= 2) { - data->key_mgmt = 0; - count = WPA_GET_LE16(pos); - pos += 2; - left -= 2; - if (count == 0 || left < count * WPA_SELECTOR_LEN) - return -6; - for (i = 0; i < count; i++) { - data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos); - pos += WPA_SELECTOR_LEN; - left -= WPA_SELECTOR_LEN; - } - } else if (left == 1) - return -7; - - if (left >= 2) { - data->capabilities = WPA_GET_LE16(pos); - pos += 2; - left -= 2; - } - - if (left > 0) { - return -8; - } - - return 0; -} - - struct wpa_auth_okc_iter_data { struct rsn_pmksa_cache_entry *pmksa; const u8 *aa; @@ -495,36 +419,28 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, else if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256) selector = RSN_AUTH_KEY_MGMT_PSK_SHA256; #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_SAE + else if (data.key_mgmt & WPA_KEY_MGMT_SAE) + selector = RSN_AUTH_KEY_MGMT_SAE; + else if (data.key_mgmt & WPA_KEY_MGMT_FT_SAE) + selector = RSN_AUTH_KEY_MGMT_FT_SAE; +#endif /* CONFIG_SAE */ else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; else if (data.key_mgmt & WPA_KEY_MGMT_PSK) selector = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector; - selector = RSN_CIPHER_SUITE_CCMP; - if (data.pairwise_cipher & WPA_CIPHER_CCMP) + selector = wpa_cipher_to_suite(WPA_PROTO_RSN, + data.pairwise_cipher); + if (!selector) selector = RSN_CIPHER_SUITE_CCMP; - else if (data.pairwise_cipher & WPA_CIPHER_TKIP) - selector = RSN_CIPHER_SUITE_TKIP; - else if (data.pairwise_cipher & WPA_CIPHER_WEP104) - selector = RSN_CIPHER_SUITE_WEP104; - else if (data.pairwise_cipher & WPA_CIPHER_WEP40) - selector = RSN_CIPHER_SUITE_WEP40; - else if (data.pairwise_cipher & WPA_CIPHER_NONE) - selector = RSN_CIPHER_SUITE_NONE; wpa_auth->dot11RSNAPairwiseCipherSelected = selector; - selector = RSN_CIPHER_SUITE_CCMP; - if (data.group_cipher & WPA_CIPHER_CCMP) + selector = wpa_cipher_to_suite(WPA_PROTO_RSN, + data.group_cipher); + if (!selector) selector = RSN_CIPHER_SUITE_CCMP; - else if (data.group_cipher & WPA_CIPHER_TKIP) - selector = RSN_CIPHER_SUITE_TKIP; - else if (data.group_cipher & WPA_CIPHER_WEP104) - selector = RSN_CIPHER_SUITE_WEP104; - else if (data.group_cipher & WPA_CIPHER_WEP40) - selector = RSN_CIPHER_SUITE_WEP40; - else if (data.group_cipher & WPA_CIPHER_NONE) - selector = RSN_CIPHER_SUITE_NONE; wpa_auth->dot11RSNAGroupCipherSelected = selector; } else { res = wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, &data); @@ -536,30 +452,16 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, selector = WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X; wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector; - selector = WPA_CIPHER_SUITE_TKIP; - if (data.pairwise_cipher & WPA_CIPHER_CCMP) - selector = WPA_CIPHER_SUITE_CCMP; - else if (data.pairwise_cipher & WPA_CIPHER_TKIP) - selector = WPA_CIPHER_SUITE_TKIP; - else if (data.pairwise_cipher & WPA_CIPHER_WEP104) - selector = WPA_CIPHER_SUITE_WEP104; - else if (data.pairwise_cipher & WPA_CIPHER_WEP40) - selector = WPA_CIPHER_SUITE_WEP40; - else if (data.pairwise_cipher & WPA_CIPHER_NONE) - selector = WPA_CIPHER_SUITE_NONE; + selector = wpa_cipher_to_suite(WPA_PROTO_WPA, + data.pairwise_cipher); + if (!selector) + selector = RSN_CIPHER_SUITE_TKIP; wpa_auth->dot11RSNAPairwiseCipherSelected = selector; - selector = WPA_CIPHER_SUITE_TKIP; - if (data.group_cipher & WPA_CIPHER_CCMP) - selector = WPA_CIPHER_SUITE_CCMP; - else if (data.group_cipher & WPA_CIPHER_TKIP) + selector = wpa_cipher_to_suite(WPA_PROTO_WPA, + data.group_cipher); + if (!selector) selector = WPA_CIPHER_SUITE_TKIP; - else if (data.group_cipher & WPA_CIPHER_WEP104) - selector = WPA_CIPHER_SUITE_WEP104; - else if (data.group_cipher & WPA_CIPHER_WEP40) - selector = WPA_CIPHER_SUITE_WEP40; - else if (data.group_cipher & WPA_CIPHER_NONE) - selector = WPA_CIPHER_SUITE_NONE; wpa_auth->dot11RSNAGroupCipherSelected = selector; } if (res) { @@ -595,6 +497,12 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, else if (key_mgmt & WPA_KEY_MGMT_PSK_SHA256) sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK_SHA256; #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_SAE + else if (key_mgmt & WPA_KEY_MGMT_SAE) + sm->wpa_key_mgmt = WPA_KEY_MGMT_SAE; + else if (key_mgmt & WPA_KEY_MGMT_FT_SAE) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_SAE; +#endif /* CONFIG_SAE */ else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X) sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X; else @@ -658,6 +566,8 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, if (ciphers & WPA_CIPHER_CCMP) sm->pairwise = WPA_CIPHER_CCMP; + else if (ciphers & WPA_CIPHER_GCMP) + sm->pairwise = WPA_CIPHER_GCMP; else sm->pairwise = WPA_CIPHER_TKIP; diff --git a/src/ap/wpa_auth_ie.h b/src/ap/wpa_auth_ie.h index 61d4cb4075fec..4999139510ec3 100644 --- a/src/ap/wpa_auth_ie.h +++ b/src/ap/wpa_auth_ie.h @@ -2,14 +2,8 @@ * hostapd - WPA/RSN IE and KDE definitions * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_AUTH_IE_H diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c index a6ffd4d0db0bf..5ce4f1be352e3 100644 --- a/src/ap/wps_hostapd.c +++ b/src/ap/wps_hostapd.c @@ -1,15 +1,9 @@ /* * hostapd / WPS integration - * Copyright (c) 2008-2010, 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 - * published 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 "utils/includes.h" @@ -17,7 +11,6 @@ #include "utils/common.h" #include "utils/eloop.h" #include "utils/uuid.h" -#include "crypto/dh_groups.h" #include "common/wpa_ctrl.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" @@ -26,8 +19,10 @@ #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" #include "beacon.h" #include "sta_info.h" #include "wps_hostapd.h" @@ -40,11 +35,53 @@ static int hostapd_wps_upnp_init(struct hostapd_data *hapd, static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd); #endif /* CONFIG_WPS_UPNP */ -static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, - const u8 *ie, size_t ie_len); +static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da, + const u8 *bssid, + const u8 *ie, size_t ie_len, + int ssi_signal); static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx); +struct wps_for_each_data { + int (*func)(struct hostapd_data *h, void *ctx); + void *ctx; +}; + + +static int wps_for_each(struct hostapd_iface *iface, void *ctx) +{ + struct wps_for_each_data *data = ctx; + size_t j; + + if (iface == NULL) + return 0; + for (j = 0; j < iface->num_bss; j++) { + struct hostapd_data *hapd = iface->bss[j]; + int ret = data->func(hapd, data->ctx); + if (ret) + return ret; + } + + return 0; +} + + +static int hostapd_wps_for_each(struct hostapd_data *hapd, + int (*func)(struct hostapd_data *h, void *ctx), + void *ctx) +{ + struct hostapd_iface *iface = hapd->iface; + struct wps_for_each_data data; + data.func = func; + data.ctx = ctx; + if (iface->interfaces == NULL || + iface->interfaces->for_each_interface == NULL) + return wps_for_each(iface, &data); + return iface->interfaces->for_each_interface(iface->interfaces, + wps_for_each, &data); +} + + static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk, size_t psk_len) { @@ -100,8 +137,9 @@ static int hostapd_wps_set_ie_cb(void *ctx, struct wpabuf *beacon_ie, hapd->wps_beacon_ie = beacon_ie; wpabuf_free(hapd->wps_probe_resp_ie); hapd->wps_probe_resp_ie = probe_resp_ie; - ieee802_11_set_beacon(hapd); - return hapd->drv.set_ap_wps_ie(hapd); + if (hapd->beacon_set_done) + ieee802_11_set_beacon(hapd); + return hostapd_set_ap_wps_ie(hapd); } @@ -144,11 +182,30 @@ 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, + 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]; + struct wps_stop_reg_data data; if (uuid_bin2str(uuid_e, uuid, sizeof(uuid))) return; wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_REG_SUCCESS MACSTR " %s", @@ -156,6 +213,11 @@ static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr, if (hapd->wps_reg_success_cb) hapd->wps_reg_success_cb(hapd->wps_reg_success_cb_ctx, 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); } @@ -193,16 +255,31 @@ static void wps_reload_config(void *eloop_data, void *user_ctx) struct hostapd_iface *iface = eloop_data; wpa_printf(MSG_DEBUG, "WPS: Reload configuration data"); - if (iface->reload_config(iface) < 0) { + if (iface->interfaces == NULL || + iface->interfaces->reload_config(iface) < 0) { wpa_printf(MSG_WARNING, "WPS: Failed to reload the updated " "configuration"); } } -static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred) +static void hapd_new_ap_event(struct hostapd_data *hapd, const u8 *attr, + size_t attr_len) { - struct hostapd_data *hapd = ctx; + size_t blen = attr_len * 2 + 1; + char *buf = os_malloc(blen); + if (buf) { + wpa_snprintf_hex(buf, blen, attr, attr_len); + wpa_msg(hapd->msg_ctx, MSG_INFO, + WPS_EVENT_NEW_AP_SETTINGS "%s", buf); + os_free(buf); + } +} + + +static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) +{ + const struct wps_credential *cred = ctx; FILE *oconf, *nconf; size_t len, i; char *tmp_fname; @@ -210,6 +287,9 @@ static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred) int multi_bss; int wpa; + if (hapd->wps == NULL) + return 0; + wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute", cred->cred_attr, cred->cred_attr_len); @@ -226,15 +306,15 @@ static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred) if ((hapd->conf->wps_cred_processing == 1 || hapd->conf->wps_cred_processing == 2) && cred->cred_attr) { - size_t blen = cred->cred_attr_len * 2 + 1; - char *_buf = os_malloc(blen); - if (_buf) { - wpa_snprintf_hex(_buf, blen, - cred->cred_attr, cred->cred_attr_len); - wpa_msg(hapd->msg_ctx, MSG_INFO, "%s%s", - WPS_EVENT_NEW_AP_SETTINGS, _buf); - os_free(_buf); - } + hapd_new_ap_event(hapd, cred->cred_attr, cred->cred_attr_len); + } else if (hapd->conf->wps_cred_processing == 1 || + hapd->conf->wps_cred_processing == 2) { + struct wpabuf *attr; + attr = wpabuf_alloc(200); + if (attr && wps_build_credential_wrap(attr, cred) == 0) + hapd_new_ap_event(hapd, wpabuf_head_u8(attr), + wpabuf_len(attr)); + wpabuf_free(attr); } else wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_NEW_AP_SETTINGS); @@ -263,6 +343,8 @@ static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred) } hapd->wps->wps_state = WPS_STATE_CONFIGURED; + if (hapd->iface->config_fname == NULL) + return 0; len = os_strlen(hapd->iface->config_fname) + 5; tmp_fname = os_malloc(len); if (tmp_fname == NULL) @@ -290,10 +372,17 @@ static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred) fprintf(nconf, "wps_state=2\n"); - fprintf(nconf, "ssid="); - for (i = 0; i < cred->ssid_len; i++) - fputc(cred->ssid[i], nconf); - fprintf(nconf, "\n"); + if (is_hex(cred->ssid, cred->ssid_len)) { + fprintf(nconf, "ssid2="); + for (i = 0; i < cred->ssid_len; i++) + fprintf(nconf, "%02x", cred->ssid[i]); + fprintf(nconf, "\n"); + } else { + fprintf(nconf, "ssid="); + for (i = 0; i < cred->ssid_len; i++) + fputc(cred->ssid[i], nconf); + fprintf(nconf, "\n"); + } if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) && (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK))) @@ -383,7 +472,10 @@ static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred) multi_bss = 1; if (!multi_bss && (str_starts(buf, "ssid=") || + str_starts(buf, "ssid2=") || str_starts(buf, "auth_algs=") || + str_starts(buf, "wep_default_key=") || + str_starts(buf, "wep_key") || str_starts(buf, "wps_state=") || str_starts(buf, "wpa=") || str_starts(buf, "wpa_psk=") || @@ -414,20 +506,27 @@ static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred) eloop_register_timeout(0, 100000, wps_reload_config, hapd->iface, NULL); - /* TODO: dualband AP may need to update multiple configuration files */ - wpa_printf(MSG_DEBUG, "WPS: AP configuration updated"); return 0; } +static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred) +{ + struct hostapd_data *hapd = ctx; + return hostapd_wps_for_each(hapd, hapd_wps_cred_cb, (void *) cred); +} + + static void hostapd_wps_reenable_ap_pin(void *eloop_data, void *user_ctx) { struct hostapd_data *hapd = eloop_data; if (hapd->conf->ap_setup_locked) return; + if (hapd->ap_pin_failures_consecutive >= 10) + return; wpa_printf(MSG_DEBUG, "WPS: Re-enable AP PIN"); wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED); @@ -436,11 +535,12 @@ static void hostapd_wps_reenable_ap_pin(void *eloop_data, void *user_ctx) } -static void hostapd_pwd_auth_fail(struct hostapd_data *hapd, - struct wps_event_pwd_auth_fail *data) +static int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx) { - if (!data->enrollee || hapd->conf->ap_pin == NULL) - return; + struct wps_event_pwd_auth_fail *data = ctx; + + if (!data->enrollee || hapd->conf->ap_pin == NULL || hapd->wps == NULL) + return 0; /* * Registrar failed to prove its knowledge of the AP PIN. Lock AP setup @@ -448,17 +548,27 @@ static void hostapd_pwd_auth_fail(struct hostapd_data *hapd, * force attacks. */ hapd->ap_pin_failures++; - wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u", - hapd->ap_pin_failures); + hapd->ap_pin_failures_consecutive++; + wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u " + "(%u consecutive)", + hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive); if (hapd->ap_pin_failures < 3) - return; + return 0; wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_LOCKED); hapd->wps->ap_setup_locked = 1; wps_registrar_update_ie(hapd->wps->registrar); - if (!hapd->conf->ap_setup_locked) { + if (!hapd->conf->ap_setup_locked && + hapd->ap_pin_failures_consecutive >= 10) { + /* + * In indefinite lockdown - disable automatic AP PIN + * reenablement. + */ + eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL); + wpa_printf(MSG_DEBUG, "WPS: AP PIN disabled indefinitely"); + } else if (!hapd->conf->ap_setup_locked) { if (hapd->ap_pin_lockout_time == 0) hapd->ap_pin_lockout_time = 60; else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 && @@ -473,7 +583,60 @@ static void hostapd_pwd_auth_fail(struct hostapd_data *hapd, NULL); } - /* TODO: dualband AP may need to update other interfaces */ + return 0; +} + + +static void hostapd_pwd_auth_fail(struct hostapd_data *hapd, + struct wps_event_pwd_auth_fail *data) +{ + hostapd_wps_for_each(hapd, wps_pwd_auth_fail, data); +} + + +static int wps_ap_pin_success(struct hostapd_data *hapd, void *ctx) +{ + if (hapd->conf->ap_pin == NULL || hapd->wps == NULL) + return 0; + + if (hapd->ap_pin_failures_consecutive == 0) + return 0; + + wpa_printf(MSG_DEBUG, "WPS: Clear consecutive AP PIN failure counter " + "- total validation failures %u (%u consecutive)", + hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive); + hapd->ap_pin_failures_consecutive = 0; + + return 0; +} + + +static void hostapd_wps_ap_pin_success(struct hostapd_data *hapd) +{ + hostapd_wps_for_each(hapd, wps_ap_pin_success, NULL); +} + + +static const char * wps_event_fail_reason[NUM_WPS_EI_VALUES] = { + "No Error", /* WPS_EI_NO_ERROR */ + "TKIP Only Prohibited", /* WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED */ + "WEP Prohibited" /* WPS_EI_SECURITY_WEP_PROHIBITED */ +}; + +static void hostapd_wps_event_fail(struct hostapd_data *hapd, + struct wps_event_fail *fail) +{ + if (fail->error_indication > 0 && + fail->error_indication < NUM_WPS_EI_VALUES) { + wpa_msg(hapd->msg_ctx, MSG_INFO, + WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)", + fail->msg, fail->config_error, fail->error_indication, + wps_event_fail_reason[fail->error_indication]); + } else { + wpa_msg(hapd->msg_ctx, MSG_INFO, + WPS_EVENT_FAIL "msg=%d config_error=%d", + fail->msg, fail->config_error); + } } @@ -482,8 +645,43 @@ static void hostapd_wps_event_cb(void *ctx, enum wps_event event, { struct hostapd_data *hapd = ctx; - if (event == WPS_EV_PWD_AUTH_FAIL) + switch (event) { + case WPS_EV_M2D: + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_M2D); + break; + case WPS_EV_FAIL: + hostapd_wps_event_fail(hapd, &data->fail); + break; + case WPS_EV_SUCCESS: + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_SUCCESS); + break; + case WPS_EV_PWD_AUTH_FAIL: hostapd_pwd_auth_fail(hapd, &data->pwd_auth_fail); + break; + case WPS_EV_PBC_OVERLAP: + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_OVERLAP); + break; + case WPS_EV_PBC_TIMEOUT: + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_TIMEOUT); + break; + case WPS_EV_ER_AP_ADD: + break; + case WPS_EV_ER_AP_REMOVE: + break; + case WPS_EV_ER_ENROLLEE_ADD: + break; + case WPS_EV_ER_ENROLLEE_REMOVE: + break; + case WPS_EV_ER_AP_SETTINGS: + break; + case WPS_EV_ER_SET_SELECTED_REGISTRAR: + break; + case WPS_EV_AP_PIN_SUCCESS: + hostapd_wps_ap_pin_success(hapd); + break; + } + if (hapd->wps_event_cb) + hapd->wps_event_cb(hapd->wps_event_cb_ctx, event, data); } @@ -495,7 +693,84 @@ static void hostapd_wps_clear_ies(struct hostapd_data *hapd) wpabuf_free(hapd->wps_probe_resp_ie); hapd->wps_probe_resp_ie = NULL; - hapd->drv.set_ap_wps_ie(hapd); + hostapd_set_ap_wps_ie(hapd); +} + + +static int get_uuid_cb(struct hostapd_iface *iface, void *ctx) +{ + const u8 **uuid = ctx; + size_t j; + + if (iface == NULL) + return 0; + for (j = 0; j < iface->num_bss; j++) { + struct hostapd_data *hapd = iface->bss[j]; + if (hapd->wps && !is_nil_uuid(hapd->wps->uuid)) { + *uuid = hapd->wps->uuid; + return 1; + } + } + + return 0; +} + + +static const u8 * get_own_uuid(struct hostapd_iface *iface) +{ + const u8 *uuid; + if (iface->interfaces == NULL || + iface->interfaces->for_each_interface == NULL) + return NULL; + uuid = NULL; + iface->interfaces->for_each_interface(iface->interfaces, get_uuid_cb, + &uuid); + return uuid; +} + + +static int count_interface_cb(struct hostapd_iface *iface, void *ctx) +{ + int *count= ctx; + (*count)++; + return 0; +} + + +static int interface_count(struct hostapd_iface *iface) +{ + int count = 0; + if (iface->interfaces == NULL || + iface->interfaces->for_each_interface == NULL) + return 0; + iface->interfaces->for_each_interface(iface->interfaces, + count_interface_cb, &count); + return count; +} + + +static int hostapd_wps_set_vendor_ext(struct hostapd_data *hapd, + struct wps_context *wps) +{ + int i; + + for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) { + wpabuf_free(wps->dev.vendor_ext[i]); + wps->dev.vendor_ext[i] = NULL; + + if (hapd->conf->wps_vendor_ext[i] == NULL) + continue; + + wps->dev.vendor_ext[i] = + wpabuf_dup(hapd->conf->wps_vendor_ext[i]); + if (wps->dev.vendor_ext[i] == NULL) { + while (--i >= 0) + wpabuf_free(wps->dev.vendor_ext[i]); + return -1; + } + } + + return 0; } @@ -522,11 +797,22 @@ int hostapd_init_wps(struct hostapd_data *hapd, wps->wps_state = hapd->conf->wps_state; wps->ap_setup_locked = hapd->conf->ap_setup_locked; if (is_nil_uuid(hapd->conf->uuid)) { - uuid_gen_mac_addr(hapd->own_addr, wps->uuid); - wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC address", - wps->uuid, UUID_LEN); - } else + const u8 *uuid; + uuid = get_own_uuid(hapd->iface); + if (uuid) { + os_memcpy(wps->uuid, uuid, UUID_LEN); + wpa_hexdump(MSG_DEBUG, "WPS: Clone UUID from another " + "interface", wps->uuid, UUID_LEN); + } else { + uuid_gen_mac_addr(hapd->own_addr, wps->uuid); + wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC " + "address", wps->uuid, UUID_LEN); + } + } else { os_memcpy(wps->uuid, hapd->conf->uuid, UUID_LEN); + wpa_hexdump(MSG_DEBUG, "WPS: Use configured UUID", + wps->uuid, UUID_LEN); + } wps->ssid_len = hapd->conf->ssid.ssid_len; os_memcpy(wps->ssid, hapd->conf->ssid.ssid, wps->ssid_len); wps->ap = 1; @@ -543,16 +829,39 @@ int hostapd_init_wps(struct hostapd_data *hapd, os_strdup(hapd->conf->serial_number) : NULL; wps->config_methods = wps_config_methods_str2bin(hapd->conf->config_methods); - if (hapd->conf->device_type && - wps_dev_type_str2bin(hapd->conf->device_type, - wps->dev.pri_dev_type) < 0) { - wpa_printf(MSG_ERROR, "WPS: Invalid device_type"); +#ifdef CONFIG_WPS2 + if ((wps->config_methods & + (WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY | + WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) { + wpa_printf(MSG_INFO, "WPS: Converting display to " + "virtual_display for WPS 2.0 compliance"); + wps->config_methods |= WPS_CONFIG_VIRT_DISPLAY; + } + if ((wps->config_methods & + (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_VIRT_PUSHBUTTON | + WPS_CONFIG_PHY_PUSHBUTTON)) == WPS_CONFIG_PUSHBUTTON) { + wpa_printf(MSG_INFO, "WPS: Converting push_button to " + "virtual_push_button for WPS 2.0 compliance"); + wps->config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON; + } +#endif /* CONFIG_WPS2 */ + os_memcpy(wps->dev.pri_dev_type, hapd->conf->device_type, + WPS_DEV_TYPE_LEN); + + if (hostapd_wps_set_vendor_ext(hapd, wps) < 0) { os_free(wps); return -1; } + wps->dev.os_version = WPA_GET_BE32(hapd->conf->os_version); - wps->dev.rf_bands = hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ? - WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */ + + if (conf->wps_rf_bands) { + wps->dev.rf_bands = conf->wps_rf_bands; + } else { + wps->dev.rf_bands = + hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ? + WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */ + } if (conf->wpa & WPA_PROTO_RSN) { if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) @@ -647,10 +956,16 @@ int hostapd_init_wps(struct hostapd_data *hapd, conf->skip_cred_build; if (conf->ssid.security_policy == SECURITY_STATIC_WEP) cfg.static_wep_only = 1; + cfg.dualband = interface_count(hapd->iface) > 1; + if ((wps->dev.rf_bands & (WPS_RF_50GHZ | WPS_RF_24GHZ)) == + (WPS_RF_50GHZ | WPS_RF_24GHZ)) + cfg.dualband = 1; + if (cfg.dualband) + wpa_printf(MSG_DEBUG, "WPS: Dualband AP"); wps->registrar = wps_registrar_init(wps, &cfg); if (wps->registrar == NULL) { - printf("Failed to initialize WPS Registrar\n"); + wpa_printf(MSG_ERROR, "Failed to initialize WPS Registrar"); os_free(wps->network_key); os_free(wps); return -1; @@ -662,21 +977,49 @@ int hostapd_init_wps(struct hostapd_data *hapd, wps->model_description = hapd->conf->model_description; wps->model_url = hapd->conf->model_url; wps->upc = hapd->conf->upc; +#endif /* CONFIG_WPS_UPNP */ + + hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd); + + hapd->wps = wps; + + return 0; +} + + +int hostapd_init_wps_complete(struct hostapd_data *hapd) +{ + struct wps_context *wps = hapd->wps; + if (wps == NULL) + return 0; + +#ifdef CONFIG_WPS_UPNP if (hostapd_wps_upnp_init(hapd, wps) < 0) { wpa_printf(MSG_ERROR, "Failed to initialize WPS UPnP"); wps_registrar_deinit(wps->registrar); os_free(wps->network_key); os_free(wps); + hapd->wps = NULL; return -1; } #endif /* CONFIG_WPS_UPNP */ - hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd); + return 0; +} - hapd->wps = wps; - return 0; +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 */ } @@ -694,9 +1037,8 @@ void hostapd_deinit_wps(struct hostapd_data *hapd) wps_device_data_free(&hapd->wps->dev); wpabuf_free(hapd->wps->dh_pubkey); wpabuf_free(hapd->wps->dh_privkey); - wpabuf_free(hapd->wps->oob_conf.pubkey_hash); - wpabuf_free(hapd->wps->oob_conf.dev_password); wps_free_pending_msgs(hapd->wps->upnp_msgs); + hostapd_wps_nfc_clear(hapd->wps); os_free(hapd->wps); hapd->wps = NULL; hostapd_wps_clear_ies(hapd); @@ -707,6 +1049,17 @@ void hostapd_update_wps(struct hostapd_data *hapd) { if (hapd->wps == NULL) return; + +#ifdef CONFIG_WPS_UPNP + hapd->wps->friendly_name = hapd->conf->friendly_name; + hapd->wps->manufacturer_url = hapd->conf->manufacturer_url; + hapd->wps->model_description = hapd->conf->model_description; + hapd->wps->model_url = hapd->conf->model_url; + hapd->wps->upc = hapd->conf->upc; +#endif /* CONFIG_WPS_UPNP */ + + hostapd_wps_set_vendor_ext(hapd, hapd->wps); + if (hapd->conf->wps_state) wps_registrar_update_ie(hapd->wps->registrar); else @@ -714,88 +1067,97 @@ void hostapd_update_wps(struct hostapd_data *hapd) } -int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid, - const char *pin, int timeout) +struct wps_add_pin_data { + const u8 *addr; + const u8 *uuid; + const u8 *pin; + size_t pin_len; + int timeout; + int added; +}; + + +static int wps_add_pin(struct hostapd_data *hapd, void *ctx) { - u8 u[UUID_LEN]; - int any = 0; + struct wps_add_pin_data *data = ctx; + int ret; if (hapd->wps == NULL) - return -1; + return 0; + ret = wps_registrar_add_pin(hapd->wps->registrar, data->addr, + data->uuid, data->pin, data->pin_len, + data->timeout); + if (ret == 0) + data->added++; + return ret; +} + + +int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr, + const char *uuid, const char *pin, int timeout) +{ + u8 u[UUID_LEN]; + struct wps_add_pin_data data; + + data.addr = addr; + data.uuid = u; + data.pin = (const u8 *) pin; + data.pin_len = os_strlen(pin); + data.timeout = timeout; + data.added = 0; + if (os_strcmp(uuid, "any") == 0) - any = 1; - else if (uuid_str2bin(uuid, u)) + data.uuid = NULL; + else { + if (uuid_str2bin(uuid, u)) + return -1; + data.uuid = u; + } + if (hostapd_wps_for_each(hapd, wps_add_pin, &data) < 0) return -1; - return wps_registrar_add_pin(hapd->wps->registrar, any ? NULL : u, - (const u8 *) pin, os_strlen(pin), - timeout); + return data.added ? 0 : -1; } -int hostapd_wps_button_pushed(struct hostapd_data *hapd) +static int wps_button_pushed(struct hostapd_data *hapd, void *ctx) { + const u8 *p2p_dev_addr = ctx; if (hapd->wps == NULL) - return -1; - return wps_registrar_button_pushed(hapd->wps->registrar); + return 0; + return wps_registrar_button_pushed(hapd->wps->registrar, p2p_dev_addr); } -#ifdef CONFIG_WPS_OOB -int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type, - char *path, char *method, char *name) +int hostapd_wps_button_pushed(struct hostapd_data *hapd, + const u8 *p2p_dev_addr) { - struct wps_context *wps = hapd->wps; - struct oob_device_data *oob_dev; + return hostapd_wps_for_each(hapd, wps_button_pushed, + (void *) p2p_dev_addr); +} - oob_dev = wps_get_oob_device(device_type); - if (oob_dev == NULL) - return -1; - oob_dev->device_path = path; - oob_dev->device_name = name; - wps->oob_conf.oob_method = wps_get_oob_method(method); - if (wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_R) { - /* - * Use pre-configured DH keys in order to be able to write the - * key hash into the OOB file. - */ - wpabuf_free(wps->dh_pubkey); - wpabuf_free(wps->dh_privkey); - wps->dh_privkey = NULL; - wps->dh_pubkey = dh_init(dh_groups_get(WPS_DH_GROUP), - &wps->dh_privkey); - wps->dh_pubkey = wpabuf_zeropad(wps->dh_pubkey, 192); - if (wps->dh_pubkey == NULL) { - wpa_printf(MSG_ERROR, "WPS: Failed to initialize " - "Diffie-Hellman handshake"); - return -1; - } - } - - if (wps_process_oob(wps, oob_dev, 1) < 0) - goto error; +static int wps_cancel(struct hostapd_data *hapd, void *ctx) +{ + if (hapd->wps == NULL) + return 0; - if ((wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E || - wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_R) && - hostapd_wps_add_pin(hapd, "any", - wpabuf_head(wps->oob_conf.dev_password), 0) < - 0) - goto error; + wps_registrar_wps_cancel(hapd->wps->registrar); + ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL); return 0; +} -error: - wpabuf_free(wps->dh_pubkey); - wps->dh_pubkey = NULL; - wpabuf_free(wps->dh_privkey); - wps->dh_privkey = NULL; - return -1; + +int hostapd_wps_cancel(struct hostapd_data *hapd) +{ + return hostapd_wps_for_each(hapd, wps_cancel, NULL); } -#endif /* CONFIG_WPS_OOB */ -static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, - const u8 *ie, size_t ie_len) +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, + int ssi_signal) { struct hostapd_data *hapd = ctx; struct wpabuf *wps_ie; @@ -819,15 +1181,28 @@ static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, wps_ie = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA); if (wps_ie == NULL) return 0; + if (wps_validate_probe_req(wps_ie, addr) < 0) { + wpabuf_free(wps_ie); + return 0; + } if (wpabuf_len(wps_ie) > 0) { - wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie); + int p2p_wildcard = 0; +#ifdef CONFIG_P2P + if (elems.ssid && elems.ssid_len == P2P_WILDCARD_SSID_LEN && + os_memcmp(elems.ssid, P2P_WILDCARD_SSID, + P2P_WILDCARD_SSID_LEN) == 0) + p2p_wildcard = 1; +#endif /* CONFIG_P2P */ + wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie, + p2p_wildcard); #ifdef CONFIG_WPS_UPNP /* FIX: what exactly should be included in the WLANEvent? * WPS attributes? Full ProbeReq frame? */ - upnp_wps_device_send_wlan_event(hapd->wps_upnp, addr, - UPNP_WPS_WLANEVENT_TYPE_PROBE, - wps_ie); + if (!p2p_wildcard) + upnp_wps_device_send_wlan_event( + hapd->wps_upnp, addr, + UPNP_WPS_WLANEVENT_TYPE_PROBE, wps_ie); #endif /* CONFIG_WPS_UPNP */ } @@ -864,6 +1239,7 @@ static int hostapd_rx_req_put_wlan_response( */ sta = ap_get_sta(hapd, mac_addr); +#ifndef CONFIG_WPS_STRICT if (!sta) { /* * Workaround - Intel wsccmd uses bogus NewWLANEventMAC: @@ -877,8 +1253,9 @@ static int hostapd_rx_req_put_wlan_response( break; } } +#endif /* CONFIG_WPS_STRICT */ - if (!sta) { + if (!sta || !(sta->flags & WLAN_STA_WPS)) { wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found"); return 0; } @@ -911,26 +1288,19 @@ static int hostapd_wps_upnp_init(struct hostapd_data *hapd, if (hapd->conf->ap_pin) ctx->ap_pin = os_strdup(hapd->conf->ap_pin); - hapd->wps_upnp = upnp_wps_device_init(ctx, wps, hapd); - if (hapd->wps_upnp == NULL) { - os_free(ctx); + hapd->wps_upnp = upnp_wps_device_init(ctx, wps, hapd, + hapd->conf->upnp_iface); + if (hapd->wps_upnp == NULL) return -1; - } wps->wps_upnp = hapd->wps_upnp; - if (upnp_wps_device_start(hapd->wps_upnp, hapd->conf->upnp_iface)) { - upnp_wps_device_deinit(hapd->wps_upnp); - hapd->wps_upnp = NULL; - return -1; - } - return 0; } static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd) { - upnp_wps_device_deinit(hapd->wps_upnp); + upnp_wps_device_deinit(hapd->wps_upnp, hapd); } #endif /* CONFIG_WPS_UPNP */ @@ -950,6 +1320,7 @@ static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx) struct hostapd_data *hapd = eloop_data; wpa_printf(MSG_DEBUG, "WPS: AP PIN timed out"); hostapd_wps_ap_pin_disable(hapd); + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_PIN_DISABLED); } @@ -957,6 +1328,7 @@ static void hostapd_wps_ap_pin_enable(struct hostapd_data *hapd, int timeout) { wpa_printf(MSG_DEBUG, "WPS: Enabling AP PIN (timeout=%d)", timeout); hapd->ap_pin_failures = 0; + hapd->ap_pin_failures_consecutive = 0; hapd->conf->ap_setup_locked = 0; if (hapd->wps->ap_setup_locked) { wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED); @@ -970,31 +1342,53 @@ static void hostapd_wps_ap_pin_enable(struct hostapd_data *hapd, int timeout) } -void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd) +static int wps_ap_pin_disable(struct hostapd_data *hapd, void *ctx) { - wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN"); os_free(hapd->conf->ap_pin); hapd->conf->ap_pin = NULL; #ifdef CONFIG_WPS_UPNP upnp_wps_set_ap_pin(hapd->wps_upnp, NULL); #endif /* CONFIG_WPS_UPNP */ eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL); + return 0; } -const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout) +void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd) { - unsigned int pin; + wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN"); + hostapd_wps_for_each(hapd, wps_ap_pin_disable, NULL); +} + + +struct wps_ap_pin_data { char pin_txt[9]; + int timeout; +}; - pin = wps_generate_pin(); - os_snprintf(pin_txt, sizeof(pin_txt), "%u", pin); + +static int wps_ap_pin_set(struct hostapd_data *hapd, void *ctx) +{ + struct wps_ap_pin_data *data = ctx; os_free(hapd->conf->ap_pin); - hapd->conf->ap_pin = os_strdup(pin_txt); + hapd->conf->ap_pin = os_strdup(data->pin_txt); #ifdef CONFIG_WPS_UPNP - upnp_wps_set_ap_pin(hapd->wps_upnp, pin_txt); + upnp_wps_set_ap_pin(hapd->wps_upnp, data->pin_txt); #endif /* CONFIG_WPS_UPNP */ - hostapd_wps_ap_pin_enable(hapd, timeout); + hostapd_wps_ap_pin_enable(hapd, data->timeout); + return 0; +} + + +const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout) +{ + unsigned int pin; + struct wps_ap_pin_data data; + + pin = wps_generate_pin(); + os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%08u", pin); + data.timeout = timeout; + hostapd_wps_for_each(hapd, wps_ap_pin_set, &data); return hapd->conf->ap_pin; } @@ -1008,13 +1402,228 @@ const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd) int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin, int timeout) { - os_free(hapd->conf->ap_pin); - hapd->conf->ap_pin = os_strdup(pin); - if (hapd->conf->ap_pin == NULL) + struct wps_ap_pin_data data; + int ret; + + ret = os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%s", pin); + if (ret < 0 || ret >= (int) sizeof(data.pin_txt)) return -1; -#ifdef CONFIG_WPS_UPNP - upnp_wps_set_ap_pin(hapd->wps_upnp, hapd->conf->ap_pin); -#endif /* CONFIG_WPS_UPNP */ - hostapd_wps_ap_pin_enable(hapd, timeout); + data.timeout = timeout; + return hostapd_wps_for_each(hapd, wps_ap_pin_set, &data); +} + + +static int wps_update_ie(struct hostapd_data *hapd, void *ctx) +{ + if (hapd->wps) + wps_registrar_update_ie(hapd->wps->registrar); + return 0; +} + + +void hostapd_wps_update_ie(struct hostapd_data *hapd) +{ + hostapd_wps_for_each(hapd, wps_update_ie, NULL); +} + + +int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid, + const char *auth, const char *encr, const char *key) +{ + struct wps_credential cred; + size_t len; + + os_memset(&cred, 0, sizeof(cred)); + + len = os_strlen(ssid); + if ((len & 1) || len > 2 * sizeof(cred.ssid) || + hexstr2bin(ssid, cred.ssid, len / 2)) + return -1; + cred.ssid_len = len / 2; + + if (os_strncmp(auth, "OPEN", 4) == 0) + cred.auth_type = WPS_AUTH_OPEN; + else if (os_strncmp(auth, "WPAPSK", 6) == 0) + cred.auth_type = WPS_AUTH_WPAPSK; + else if (os_strncmp(auth, "WPA2PSK", 7) == 0) + cred.auth_type = WPS_AUTH_WPA2PSK; + else + return -1; + + if (encr) { + if (os_strncmp(encr, "NONE", 4) == 0) + cred.encr_type = WPS_ENCR_NONE; + else if (os_strncmp(encr, "WEP", 3) == 0) + cred.encr_type = WPS_ENCR_WEP; + else if (os_strncmp(encr, "TKIP", 4) == 0) + cred.encr_type = WPS_ENCR_TKIP; + else if (os_strncmp(encr, "CCMP", 4) == 0) + cred.encr_type = WPS_ENCR_AES; + else + return -1; + } else + cred.encr_type = WPS_ENCR_NONE; + + if (key) { + len = os_strlen(key); + if ((len & 1) || len > 2 * sizeof(cred.key) || + hexstr2bin(key, cred.key, len / 2)) + return -1; + cred.key_len = len / 2; + } + + return wps_registrar_config_ap(hapd->wps->registrar, &cred); +} + + +#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 e978a1cf668d5..4e5026b45b6c6 100644 --- a/src/ap/wps_hostapd.h +++ b/src/ap/wps_hostapd.h @@ -1,15 +1,9 @@ /* * hostapd / WPS integration - * Copyright (c) 2008-2010, 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 - * published 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. */ #ifndef WPS_HOSTAPD_H @@ -19,13 +13,14 @@ int hostapd_init_wps(struct hostapd_data *hapd, struct hostapd_bss_config *conf); +int hostapd_init_wps_complete(struct hostapd_data *hapd); void hostapd_deinit_wps(struct hostapd_data *hapd); void hostapd_update_wps(struct hostapd_data *hapd); -int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid, - const char *pin, int timeout); -int hostapd_wps_button_pushed(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_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_get_mib_sta(struct hostapd_data *hapd, const u8 *addr, char *buf, size_t buflen); void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd); @@ -33,6 +28,16 @@ const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout); const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd); int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin, int timeout); +void hostapd_wps_update_ie(struct hostapd_data *hapd); +int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid, + const char *auth, const char *encr, const char *key); +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 */ @@ -46,6 +51,11 @@ static inline void hostapd_deinit_wps(struct hostapd_data *hapd) { } +static inline int hostapd_init_wps_complete(struct hostapd_data *hapd) +{ + return 0; +} + static inline void hostapd_update_wps(struct hostapd_data *hapd) { } @@ -57,7 +67,13 @@ static inline int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, return 0; } -static inline int hostapd_wps_button_pushed(struct hostapd_data *hapd) +static inline int hostapd_wps_button_pushed(struct hostapd_data *hapd, + const u8 *p2p_dev_addr) +{ + return 0; +} + +static inline int hostapd_wps_cancel(struct hostapd_data *hapd) { return 0; } diff --git a/src/common/defs.h b/src/common/defs.h index 173bbd1c9897f..281dd8a5eb5c7 100644 --- a/src/common/defs.h +++ b/src/common/defs.h @@ -2,14 +2,8 @@ * WPA Supplicant - Common definitions * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef DEFS_H @@ -32,6 +26,8 @@ typedef enum { FALSE = 0, TRUE = 1 } Boolean; #ifdef CONFIG_IEEE80211W #define WPA_CIPHER_AES_128_CMAC BIT(5) #endif /* CONFIG_IEEE80211W */ +#define WPA_CIPHER_GCMP BIT(6) +#define WPA_CIPHER_SMS4 BIT(7) #define WPA_KEY_MGMT_IEEE8021X BIT(0) #define WPA_KEY_MGMT_PSK BIT(1) @@ -43,41 +39,73 @@ typedef enum { FALSE = 0, TRUE = 1 } Boolean; #define WPA_KEY_MGMT_IEEE8021X_SHA256 BIT(7) #define WPA_KEY_MGMT_PSK_SHA256 BIT(8) #define WPA_KEY_MGMT_WPS BIT(9) +#define WPA_KEY_MGMT_SAE BIT(10) +#define WPA_KEY_MGMT_FT_SAE BIT(11) +#define WPA_KEY_MGMT_WAPI_PSK BIT(12) +#define WPA_KEY_MGMT_WAPI_CERT BIT(13) +#define WPA_KEY_MGMT_CCKM BIT(14) static inline int wpa_key_mgmt_wpa_ieee8021x(int akm) { - return akm == WPA_KEY_MGMT_IEEE8021X || - akm == WPA_KEY_MGMT_FT_IEEE8021X || - akm == WPA_KEY_MGMT_IEEE8021X_SHA256; + return !!(akm & (WPA_KEY_MGMT_IEEE8021X | + WPA_KEY_MGMT_FT_IEEE8021X | + WPA_KEY_MGMT_CCKM | + WPA_KEY_MGMT_IEEE8021X_SHA256)); } static inline int wpa_key_mgmt_wpa_psk(int akm) { - return akm == WPA_KEY_MGMT_PSK || - akm == WPA_KEY_MGMT_FT_PSK || - akm == WPA_KEY_MGMT_PSK_SHA256; + return !!(akm & (WPA_KEY_MGMT_PSK | + WPA_KEY_MGMT_FT_PSK | + WPA_KEY_MGMT_PSK_SHA256 | + WPA_KEY_MGMT_SAE)); } static inline int wpa_key_mgmt_ft(int akm) { - return akm == WPA_KEY_MGMT_FT_PSK || - akm == WPA_KEY_MGMT_FT_IEEE8021X; + return !!(akm & (WPA_KEY_MGMT_FT_PSK | + WPA_KEY_MGMT_FT_IEEE8021X | + WPA_KEY_MGMT_FT_SAE)); +} + +static inline int wpa_key_mgmt_sae(int akm) +{ + return !!(akm & (WPA_KEY_MGMT_SAE | + WPA_KEY_MGMT_FT_SAE)); } static inline int wpa_key_mgmt_sha256(int akm) { - return akm == WPA_KEY_MGMT_PSK_SHA256 || - akm == WPA_KEY_MGMT_IEEE8021X_SHA256; + return !!(akm & (WPA_KEY_MGMT_PSK_SHA256 | + WPA_KEY_MGMT_IEEE8021X_SHA256)); +} + +static inline int wpa_key_mgmt_wpa(int akm) +{ + return wpa_key_mgmt_wpa_ieee8021x(akm) || + wpa_key_mgmt_wpa_psk(akm); +} + +static inline int wpa_key_mgmt_wpa_any(int akm) +{ + return wpa_key_mgmt_wpa(akm) || (akm & WPA_KEY_MGMT_WPA_NONE); +} + +static inline int wpa_key_mgmt_cckm(int akm) +{ + return akm == WPA_KEY_MGMT_CCKM; } #define WPA_PROTO_WPA BIT(0) #define WPA_PROTO_RSN BIT(1) +#define WPA_PROTO_WAPI BIT(2) #define WPA_AUTH_ALG_OPEN BIT(0) #define WPA_AUTH_ALG_SHARED BIT(1) #define WPA_AUTH_ALG_LEAP BIT(2) #define WPA_AUTH_ALG_FT BIT(3) +#define WPA_AUTH_ALG_SAE BIT(4) enum wpa_alg { @@ -86,7 +114,10 @@ enum wpa_alg { WPA_ALG_TKIP, WPA_ALG_CCMP, WPA_ALG_IGTK, - WPA_ALG_PMK + WPA_ALG_PMK, + WPA_ALG_GCMP, + WPA_ALG_SMS4, + WPA_ALG_KRK }; /** @@ -97,7 +128,9 @@ enum wpa_cipher { CIPHER_WEP40, CIPHER_TKIP, CIPHER_CCMP, - CIPHER_WEP104 + CIPHER_WEP104, + CIPHER_GCMP, + CIPHER_SMS4 }; /** @@ -113,7 +146,12 @@ enum wpa_key_mgmt { KEY_MGMT_FT_PSK, KEY_MGMT_802_1X_SHA256, KEY_MGMT_PSK_SHA256, - KEY_MGMT_WPS + KEY_MGMT_WPS, + KEY_MGMT_SAE, + KEY_MGMT_FT_SAE, + KEY_MGMT_WAPI_PSK, + KEY_MGMT_WAPI_CERT, + KEY_MGMT_CCKM }; /** @@ -137,6 +175,15 @@ enum wpa_states { WPA_DISCONNECTED, /** + * WPA_INTERFACE_DISABLED - Interface disabled + * + * This stat eis entered if the network interface is disabled, e.g., + * due to rfkill. wpa_supplicant refuses any new operations that would + * use the radio until the interface has been enabled. + */ + WPA_INTERFACE_DISABLED, + + /** * WPA_INACTIVE - Inactive state (wpa_supplicant disabled) * * This state is entered if there are no enabled networks in the @@ -239,8 +286,9 @@ enum wpa_states { enum mfp_options { NO_MGMT_FRAME_PROTECTION = 0, MGMT_FRAME_PROTECTION_OPTIONAL = 1, - MGMT_FRAME_PROTECTION_REQUIRED = 2 + MGMT_FRAME_PROTECTION_REQUIRED = 2, }; +#define MGMT_FRAME_PROTECTION_DEFAULT 3 /** * enum hostapd_hw_mode - Hardware mode @@ -249,7 +297,25 @@ enum hostapd_hw_mode { HOSTAPD_MODE_IEEE80211B, HOSTAPD_MODE_IEEE80211G, HOSTAPD_MODE_IEEE80211A, + HOSTAPD_MODE_IEEE80211AD, NUM_HOSTAPD_MODES }; +/** + * enum wpa_ctrl_req_type - Control interface request types + */ +enum wpa_ctrl_req_type { + WPA_CTRL_REQ_UNKNOWN, + WPA_CTRL_REQ_EAP_IDENTITY, + WPA_CTRL_REQ_EAP_PASSWORD, + WPA_CTRL_REQ_EAP_NEW_PASSWORD, + WPA_CTRL_REQ_EAP_PIN, + WPA_CTRL_REQ_EAP_OTP, + WPA_CTRL_REQ_EAP_PASSPHRASE, + NUM_WPA_CTRL_REQS +}; + +/* Maximum number of EAP methods to store for EAP server user information */ +#define EAP_MAX_METHODS 8 + #endif /* DEFS_H */ diff --git a/src/common/eapol_common.h b/src/common/eapol_common.h index d70e62d2f6487..4811f38aab36c 100644 --- a/src/common/eapol_common.h +++ b/src/common/eapol_common.h @@ -2,14 +2,8 @@ * EAPOL definitions shared between hostapd and wpa_supplicant * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAPOL_COMMON_H @@ -44,4 +38,44 @@ enum { IEEE802_1X_TYPE_EAP_PACKET = 0, enum { EAPOL_KEY_TYPE_RC4 = 1, EAPOL_KEY_TYPE_RSN = 2, EAPOL_KEY_TYPE_WPA = 254 }; + +#define IEEE8021X_REPLAY_COUNTER_LEN 8 +#define IEEE8021X_KEY_SIGN_LEN 16 +#define IEEE8021X_KEY_IV_LEN 16 + +#define IEEE8021X_KEY_INDEX_FLAG 0x80 +#define IEEE8021X_KEY_INDEX_MASK 0x03 + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct ieee802_1x_eapol_key { + u8 type; + /* Note: key_length is unaligned */ + u8 key_length[2]; + /* does not repeat within the life of the keying material used to + * encrypt the Key field; 64-bit NTP timestamp MAY be used here */ + u8 replay_counter[IEEE8021X_REPLAY_COUNTER_LEN]; + u8 key_iv[IEEE8021X_KEY_IV_LEN]; /* cryptographically random number */ + u8 key_index; /* key flag in the most significant bit: + * 0 = broadcast (default key), + * 1 = unicast (key mapping key); key index is in the + * 7 least significant bits */ + /* HMAC-MD5 message integrity check computed with MS-MPPE-Send-Key as + * the key */ + u8 key_signature[IEEE8021X_KEY_SIGN_LEN]; + + /* followed by key: if packet body length = 44 + key length, then the + * key field (of key_length bytes) contains the key in encrypted form; + * if packet body length = 44, key field is absent and key_length + * represents the number of least significant octets from + * MS-MPPE-Send-Key attribute to be used as the keying material; + * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */ +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + #endif /* EAPOL_COMMON_H */ diff --git a/src/common/gas.c b/src/common/gas.c new file mode 100644 index 0000000000000..cff9254b74401 --- /dev/null +++ b/src/common/gas.c @@ -0,0 +1,273 @@ +/* + * Generic advertisement service (GAS) (IEEE 802.11u) + * Copyright (c) 2009, Atheros Communications + * Copyright (c) 2011-2012, Qualcomm Atheros + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "ieee802_11_defs.h" +#include "gas.h" + + +static struct wpabuf * +gas_build_req(u8 action, u8 dialog_token, size_t size) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(100 + size); + if (buf == NULL) + return NULL; + + wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); + wpabuf_put_u8(buf, action); + wpabuf_put_u8(buf, dialog_token); + + return buf; +} + + +struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size) +{ + return gas_build_req(WLAN_PA_GAS_INITIAL_REQ, dialog_token, + size); +} + + +struct wpabuf * gas_build_comeback_req(u8 dialog_token) +{ + return gas_build_req(WLAN_PA_GAS_COMEBACK_REQ, dialog_token, 0); +} + + +static struct wpabuf * +gas_build_resp(u8 action, u8 dialog_token, u16 status_code, u8 frag_id, + u8 more, u16 comeback_delay, size_t size) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(100 + size); + if (buf == NULL) + return NULL; + + wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); + wpabuf_put_u8(buf, action); + wpabuf_put_u8(buf, dialog_token); + wpabuf_put_le16(buf, status_code); + if (action == WLAN_PA_GAS_COMEBACK_RESP) + wpabuf_put_u8(buf, frag_id | (more ? 0x80 : 0)); + wpabuf_put_le16(buf, comeback_delay); + + return buf; +} + + +struct wpabuf * +gas_build_initial_resp(u8 dialog_token, u16 status_code, u16 comeback_delay, + size_t size) +{ + return gas_build_resp(WLAN_PA_GAS_INITIAL_RESP, dialog_token, + status_code, 0, 0, comeback_delay, size); +} + + +static struct wpabuf * +gas_build_comeback_resp(u8 dialog_token, u16 status_code, u8 frag_id, u8 more, + u16 comeback_delay, size_t size) +{ + return gas_build_resp(WLAN_PA_GAS_COMEBACK_RESP, dialog_token, + status_code, frag_id, more, comeback_delay, + size); +} + + +/** + * gas_add_adv_proto_anqp - Add an Advertisement Protocol element + * @buf: Buffer to which the element is added + * @query_resp_len_limit: Query Response Length Limit in units of 256 octets + * @pame_bi: Pre-Association Message Exchange BSSID Independent (0/1) + * + * + * @query_resp_len_limit is 0 for request and 1-0x7f for response. 0x7f means + * that the maximum limit is determined by the maximum allowable number of + * fragments in the GAS Query Response Fragment ID. + */ +static void gas_add_adv_proto_anqp(struct wpabuf *buf, u8 query_resp_len_limit, + u8 pame_bi) +{ + /* Advertisement Protocol IE */ + wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); + wpabuf_put_u8(buf, 2); /* Length */ + wpabuf_put_u8(buf, (query_resp_len_limit & 0x7f) | + (pame_bi ? 0x80 : 0)); + /* Advertisement Protocol */ + wpabuf_put_u8(buf, ACCESS_NETWORK_QUERY_PROTOCOL); +} + + +struct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size) +{ + struct wpabuf *buf; + + buf = gas_build_initial_req(dialog_token, 4 + size); + if (buf == NULL) + return NULL; + + gas_add_adv_proto_anqp(buf, 0, 0); + + wpabuf_put(buf, 2); /* Query Request Length to be filled */ + + return buf; +} + + +struct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code, + u16 comeback_delay, size_t size) +{ + struct wpabuf *buf; + + buf = gas_build_initial_resp(dialog_token, status_code, comeback_delay, + 4 + size); + if (buf == NULL) + return NULL; + + gas_add_adv_proto_anqp(buf, 0x7f, 0); + + wpabuf_put(buf, 2); /* Query Response Length to be filled */ + + return buf; +} + + +struct wpabuf * gas_anqp_build_initial_resp_buf(u8 dialog_token, + u16 status_code, + u16 comeback_delay, + struct wpabuf *payload) +{ + struct wpabuf *buf; + + buf = gas_anqp_build_initial_resp(dialog_token, status_code, + comeback_delay, + payload ? wpabuf_len(payload) : 0); + if (buf == NULL) + return NULL; + + if (payload) + wpabuf_put_buf(buf, payload); + + gas_anqp_set_len(buf); + + return buf; +} + + +struct wpabuf * gas_anqp_build_comeback_resp(u8 dialog_token, u16 status_code, + u8 frag_id, u8 more, + u16 comeback_delay, size_t size) +{ + struct wpabuf *buf; + + buf = gas_build_comeback_resp(dialog_token, status_code, + frag_id, more, comeback_delay, 4 + size); + if (buf == NULL) + return NULL; + + gas_add_adv_proto_anqp(buf, 0x7f, 0); + + wpabuf_put(buf, 2); /* Query Response Length to be filled */ + + return buf; +} + + +struct wpabuf * gas_anqp_build_comeback_resp_buf(u8 dialog_token, + u16 status_code, + u8 frag_id, u8 more, + u16 comeback_delay, + struct wpabuf *payload) +{ + struct wpabuf *buf; + + buf = gas_anqp_build_comeback_resp(dialog_token, status_code, frag_id, + more, comeback_delay, + payload ? wpabuf_len(payload) : 0); + if (buf == NULL) + return NULL; + + if (payload) + wpabuf_put_buf(buf, payload); + + gas_anqp_set_len(buf); + + return buf; +} + + +/** + * gas_anqp_set_len - Set Query Request/Response Length + * @buf: GAS message + * + * This function is used to update the Query Request/Response Length field once + * the payload has been filled. + */ +void gas_anqp_set_len(struct wpabuf *buf) +{ + u8 action; + size_t offset; + u8 *len; + + if (buf == NULL || wpabuf_len(buf) < 2) + return; + + action = *(wpabuf_head_u8(buf) + 1); + switch (action) { + case WLAN_PA_GAS_INITIAL_REQ: + offset = 3 + 4; + break; + case WLAN_PA_GAS_INITIAL_RESP: + offset = 7 + 4; + break; + case WLAN_PA_GAS_COMEBACK_RESP: + offset = 8 + 4; + break; + default: + return; + } + + if (wpabuf_len(buf) < offset + 2) + return; + + len = wpabuf_mhead_u8(buf) + offset; + WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2); +} + + +/** + * gas_anqp_add_element - Add ANQP element header + * @buf: GAS message + * @info_id: ANQP Info ID + * Returns: Pointer to the Length field for gas_anqp_set_element_len() + */ +u8 * gas_anqp_add_element(struct wpabuf *buf, u16 info_id) +{ + wpabuf_put_le16(buf, info_id); + return wpabuf_put(buf, 2); /* Length to be filled */ +} + + +/** + * gas_anqp_set_element_len - Update ANQP element Length field + * @buf: GAS message + * @len_pos: Length field position from gas_anqp_add_element() + * + * This function is called after the ANQP element payload has been added to the + * buffer. + */ +void gas_anqp_set_element_len(struct wpabuf *buf, u8 *len_pos) +{ + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(buf, 0) - len_pos - 2); +} diff --git a/src/common/gas.h b/src/common/gas.h new file mode 100644 index 0000000000000..306adc58c6ee2 --- /dev/null +++ b/src/common/gas.h @@ -0,0 +1,37 @@ +/* + * Generic advertisement service (GAS) (IEEE 802.11u) + * Copyright (c) 2009, Atheros Communications + * Copyright (c) 2011-2012, Qualcomm Atheros + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef GAS_H +#define GAS_H + +struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size); +struct wpabuf * gas_build_comeback_req(u8 dialog_token); +struct wpabuf * gas_build_initial_resp(u8 dialog_token, u16 status_code, + u16 comeback_delay, size_t size); +struct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size); +struct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code, + u16 comeback_delay, size_t size); +struct wpabuf * gas_anqp_build_initial_resp_buf(u8 dialog_token, + u16 status_code, + u16 comeback_delay, + struct wpabuf *payload); +struct wpabuf * gas_anqp_build_comeback_resp(u8 dialog_token, u16 status_code, + u8 frag_id, u8 more, + u16 comeback_delay, size_t size); +struct wpabuf * gas_anqp_build_comeback_resp_buf(u8 dialog_token, + u16 status_code, + u8 frag_id, u8 more, + u16 comeback_delay, + struct wpabuf *payload); +void gas_anqp_set_len(struct wpabuf *buf); + +u8 * gas_anqp_add_element(struct wpabuf *buf, u16 info_id); +void gas_anqp_set_element_len(struct wpabuf *buf, u8 *len_pos); + +#endif /* GAS_H */ diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c index 96ef5b65a1765..98fadda1f90c1 100644 --- a/src/common/ieee802_11_common.c +++ b/src/common/ieee802_11_common.c @@ -1,15 +1,9 @@ /* * IEEE 802.11 Common routines - * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-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 - * published 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" @@ -75,7 +69,7 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, elems->wmm_tspec_len = elen; break; default: - wpa_printf(MSG_MSGDUMP, "unknown WMM " + wpa_printf(MSG_EXCESSIVE, "unknown WMM " "information element ignored " "(subtype=%d len=%lu)", pos[4], (unsigned long) elen); @@ -88,7 +82,33 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, elems->wps_ie_len = elen; break; default: - wpa_printf(MSG_MSGDUMP, "Unknown Microsoft " + wpa_printf(MSG_EXCESSIVE, "Unknown Microsoft " + "information element ignored " + "(type=%d len=%lu)", + pos[3], (unsigned long) elen); + return -1; + } + break; + + case OUI_WFA: + switch (pos[3]) { + case P2P_OUI_TYPE: + /* Wi-Fi Alliance - P2P IE */ + elems->p2p = pos; + elems->p2p_len = elen; + break; + case WFD_OUI_TYPE: + /* Wi-Fi Alliance - WFD IE */ + elems->wfd = pos; + elems->wfd_len = elen; + break; + case HS20_INDICATION_OUI_TYPE: + /* Hotspot 2.0 */ + elems->hs20 = pos; + elems->hs20_len = elen; + break; + default: + wpa_printf(MSG_MSGDUMP, "Unknown WFA " "information element ignored " "(type=%d len=%lu)\n", pos[3], (unsigned long) elen); @@ -103,18 +123,18 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, elems->vendor_ht_cap_len = elen; break; default: - wpa_printf(MSG_MSGDUMP, "Unknown Broadcom " + wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom " "information element ignored " - "(type=%d len=%lu)\n", + "(type=%d len=%lu)", pos[3], (unsigned long) elen); return -1; } break; default: - wpa_printf(MSG_MSGDUMP, "unknown vendor specific information " - "element ignored (vendor OUI %02x:%02x:%02x " - "len=%lu)", + wpa_printf(MSG_EXCESSIVE, "unknown vendor specific " + "information element ignored (vendor OUI " + "%02x:%02x:%02x len=%lu)", pos[0], pos[1], pos[2], (unsigned long) elen); return -1; } @@ -238,6 +258,36 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->ht_operation = pos; elems->ht_operation_len = elen; break; + case WLAN_EID_VHT_CAP: + elems->vht_capabilities = pos; + elems->vht_capabilities_len = elen; + break; + case WLAN_EID_VHT_OPERATION: + elems->vht_operation = pos; + elems->vht_operation_len = elen; + break; + case WLAN_EID_LINK_ID: + if (elen < 18) + break; + elems->link_id = pos; + break; + case WLAN_EID_INTERWORKING: + elems->interworking = pos; + elems->interworking_len = elen; + break; + case WLAN_EID_EXT_CAPAB: + elems->ext_capab = pos; + elems->ext_capab_len = elen; + break; + case WLAN_EID_BSS_MAX_IDLE_PERIOD: + if (elen < 3) + break; + elems->bss_max_idle_period = pos; + break; + case WLAN_EID_SSID_LIST: + elems->ssid_list = pos; + elems->ssid_list_len = elen; + break; default: unknown++; if (!show_errors) @@ -324,3 +374,115 @@ struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len, return buf; } + + +const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len) +{ + u16 fc, type, stype; + + /* + * PS-Poll frames are 16 bytes. All other frames are + * 24 bytes or longer. + */ + if (len < 16) + return NULL; + + fc = le_to_host16(hdr->frame_control); + type = WLAN_FC_GET_TYPE(fc); + stype = WLAN_FC_GET_STYPE(fc); + + switch (type) { + case WLAN_FC_TYPE_DATA: + if (len < 24) + return NULL; + switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) { + case WLAN_FC_FROMDS | WLAN_FC_TODS: + case WLAN_FC_TODS: + return hdr->addr1; + case WLAN_FC_FROMDS: + return hdr->addr2; + default: + return NULL; + } + case WLAN_FC_TYPE_CTRL: + if (stype != WLAN_FC_STYPE_PSPOLL) + return NULL; + return hdr->addr1; + case WLAN_FC_TYPE_MGMT: + return hdr->addr3; + default: + return NULL; + } +} + + +int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[], + const char *name, const char *val) +{ + int num, v; + const char *pos; + struct hostapd_wmm_ac_params *ac; + + /* skip 'wme_ac_' or 'wmm_ac_' prefix */ + pos = name + 7; + if (os_strncmp(pos, "be_", 3) == 0) { + num = 0; + pos += 3; + } else if (os_strncmp(pos, "bk_", 3) == 0) { + num = 1; + pos += 3; + } else if (os_strncmp(pos, "vi_", 3) == 0) { + num = 2; + pos += 3; + } else if (os_strncmp(pos, "vo_", 3) == 0) { + num = 3; + pos += 3; + } else { + wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos); + return -1; + } + + ac = &wmm_ac_params[num]; + + if (os_strcmp(pos, "aifs") == 0) { + v = atoi(val); + if (v < 1 || v > 255) { + wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v); + return -1; + } + ac->aifs = v; + } else if (os_strcmp(pos, "cwmin") == 0) { + v = atoi(val); + if (v < 0 || v > 12) { + wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v); + return -1; + } + ac->cwmin = v; + } else if (os_strcmp(pos, "cwmax") == 0) { + v = atoi(val); + if (v < 0 || v > 12) { + wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v); + return -1; + } + ac->cwmax = v; + } else if (os_strcmp(pos, "txop_limit") == 0) { + v = atoi(val); + if (v < 0 || v > 0xffff) { + wpa_printf(MSG_ERROR, "Invalid txop value %d", v); + return -1; + } + ac->txop_limit = v; + } else if (os_strcmp(pos, "acm") == 0) { + v = atoi(val); + if (v < 0 || v > 1) { + wpa_printf(MSG_ERROR, "Invalid acm value %d", v); + return -1; + } + ac->admission_control_mandatory = v; + } else { + wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos); + return -1; + } + + return 0; +} diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h index 4a4f5a742cc59..55fa49d1f6da6 100644 --- a/src/common/ieee802_11_common.h +++ b/src/common/ieee802_11_common.h @@ -1,15 +1,9 @@ /* * IEEE 802.11 Common routines - * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-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 - * published 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. */ #ifndef IEEE802_11_COMMON_H @@ -39,7 +33,17 @@ struct ieee802_11_elems { const u8 *timeout_int; const u8 *ht_capabilities; const u8 *ht_operation; + const u8 *vht_capabilities; + const u8 *vht_operation; const u8 *vendor_ht_cap; + const u8 *p2p; + const u8 *wfd; + const u8 *link_id; + const u8 *interworking; + const u8 *hs20; + const u8 *ext_capab; + const u8 *bss_max_idle_period; + const u8 *ssid_list; u8 ssid_len; u8 supp_rates_len; @@ -63,7 +67,15 @@ struct ieee802_11_elems { u8 timeout_int_len; u8 ht_capabilities_len; u8 ht_operation_len; + u8 vht_capabilities_len; + u8 vht_operation_len; u8 vendor_ht_cap_len; + u8 p2p_len; + u8 wfd_len; + u8 interworking_len; + u8 hs20_len; + u8 ext_capab_len; + u8 ssid_list_len; }; typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes; @@ -74,5 +86,18 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, int ieee802_11_ie_count(const u8 *ies, size_t ies_len); struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len, u32 oui_type); +struct ieee80211_hdr; +const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len); + +struct hostapd_wmm_ac_params { + int cwmin; + int cwmax; + int aifs; + int txop_limit; /* in units of 32us */ + int admission_control_mandatory; +}; + +int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[], + const char *name, const char *val); #endif /* IEEE802_11_COMMON_H */ diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index 4881e39a01a8e..e873545f6fa18 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -3,14 +3,8 @@ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> * Copyright (c) 2007-2008 Intel Corporation * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef IEEE802_11_DEFS_H @@ -71,11 +65,18 @@ #define WLAN_FC_STYPE_CFPOLL 6 #define WLAN_FC_STYPE_CFACKPOLL 7 #define WLAN_FC_STYPE_QOS_DATA 8 +#define WLAN_FC_STYPE_QOS_DATA_CFACK 9 +#define WLAN_FC_STYPE_QOS_DATA_CFPOLL 10 +#define WLAN_FC_STYPE_QOS_DATA_CFACKPOLL 11 +#define WLAN_FC_STYPE_QOS_NULL 12 +#define WLAN_FC_STYPE_QOS_CFPOLL 14 +#define WLAN_FC_STYPE_QOS_CFACKPOLL 15 /* Authentication algorithms */ #define WLAN_AUTH_OPEN 0 #define WLAN_AUTH_SHARED_KEY 1 #define WLAN_AUTH_FT 2 +#define WLAN_AUTH_SAE 3 #define WLAN_AUTH_LEAP 128 #define WLAN_AUTH_CHALLENGE_LEN 128 @@ -95,6 +96,11 @@ /* Status codes (IEEE 802.11-2007, 7.3.1.9, Table 7-23) */ #define WLAN_STATUS_SUCCESS 0 #define WLAN_STATUS_UNSPECIFIED_FAILURE 1 +#define WLAN_STATUS_TDLS_WAKEUP_ALTERNATE 2 +#define WLAN_STATUS_TDLS_WAKEUP_REJECT 3 +#define WLAN_STATUS_SECURITY_DISABLED 5 +#define WLAN_STATUS_UNACCEPTABLE_LIFETIME 6 +#define WLAN_STATUS_NOT_IN_SAME_BSS 7 #define WLAN_STATUS_CAPS_UNSUPPORTED 10 #define WLAN_STATUS_REASSOC_NO_ASSOC 11 #define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12 @@ -114,9 +120,10 @@ #define WLAN_STATUS_SUPPORTED_CHANNEL_NOT_VALID 24 /* IEEE 802.11g */ #define WLAN_STATUS_ASSOC_DENIED_NO_SHORT_SLOT_TIME 25 -#define WLAN_STATUS_ASSOC_DENIED_NO_ER_PBCC 26 -#define WLAN_STATUS_ASSOC_DENIED_NO_DSSS_OFDM 27 +#define WLAN_STATUS_ASSOC_DENIED_NO_DSSS_OFDM 26 +#define WLAN_STATUS_ASSOC_DENIED_NO_HT 27 #define WLAN_STATUS_R0KH_UNREACHABLE 28 +#define WLAN_STATUS_ASSOC_DENIED_NO_PCO 29 /* IEEE 802.11w */ #define WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY 30 #define WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION 31 @@ -141,6 +148,19 @@ #define WLAN_STATUS_INVALID_PMKID 53 #define WLAN_STATUS_INVALID_MDIE 54 #define WLAN_STATUS_INVALID_FTIE 55 +#define WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED 59 +#define WLAN_STATUS_NO_OUTSTANDING_GAS_REQ 60 +#define WLAN_STATUS_GAS_RESP_NOT_RECEIVED 61 +#define WLAN_STATUS_STA_TIMED_OUT_WAITING_FOR_GAS_RESP 62 +#define WLAN_STATUS_GAS_RESP_LARGER_THAN_LIMIT 63 +#define WLAN_STATUS_REQ_REFUSED_HOME 64 +#define WLAN_STATUS_ADV_SRV_UNREACHABLE 65 +#define WLAN_STATUS_REQ_REFUSED_SSPN 67 +#define WLAN_STATUS_REQ_REFUSED_UNAUTH_ACCESS 68 +#define WLAN_STATUS_INVALID_RSNIE 72 +#define WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ 76 +#define WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED 77 +#define WLAN_STATUS_TRANSMISSION_FAILURE 79 /* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22) */ #define WLAN_REASON_UNSPECIFIED 1 @@ -168,6 +188,10 @@ #define WLAN_REASON_INVALID_RSN_IE_CAPAB 22 #define WLAN_REASON_IEEE_802_1X_AUTH_FAILED 23 #define WLAN_REASON_CIPHER_SUITE_REJECTED 24 +#define WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE 25 +#define WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED 26 +/* IEEE 802.11e */ +#define WLAN_REASON_DISASSOC_LOW_ACK 34 /* Information Element IDs */ @@ -202,10 +226,33 @@ #define WLAN_EID_RIC_DATA 57 #define WLAN_EID_HT_OPERATION 61 #define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62 +#define WLAN_EID_WAPI 68 +#define WLAN_EID_TIME_ADVERTISEMENT 69 #define WLAN_EID_20_40_BSS_COEXISTENCE 72 #define WLAN_EID_20_40_BSS_INTOLERANT 73 #define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74 #define WLAN_EID_MMIE 76 +#define WLAN_EID_SSID_LIST 84 +#define WLAN_EID_BSS_MAX_IDLE_PERIOD 90 +#define WLAN_EID_TFS_REQ 91 +#define WLAN_EID_TFS_RESP 92 +#define WLAN_EID_WNMSLEEP 93 +#define WLAN_EID_TIME_ZONE 98 +#define WLAN_EID_LINK_ID 101 +#define WLAN_EID_INTERWORKING 107 +#define WLAN_EID_ADV_PROTO 108 +#define WLAN_EID_ROAMING_CONSORTIUM 111 +#define WLAN_EID_EXT_CAPAB 127 +#define WLAN_EID_CCKM 156 +#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 @@ -219,7 +266,20 @@ #define WLAN_ACTION_FT 6 #define WLAN_ACTION_HT 7 #define WLAN_ACTION_SA_QUERY 8 +#define WLAN_ACTION_WNM 10 +#define WLAN_ACTION_UNPROTECTED_WNM 11 +#define WLAN_ACTION_TDLS 12 #define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */ +#define WLAN_ACTION_VENDOR_SPECIFIC 127 + +/* Public action codes */ +#define WLAN_PA_20_40_BSS_COEX 0 +#define WLAN_PA_VENDOR_SPECIFIC 9 +#define WLAN_PA_GAS_INITIAL_REQ 10 +#define WLAN_PA_GAS_INITIAL_RESP 11 +#define WLAN_PA_GAS_COMEBACK_REQ 12 +#define WLAN_PA_GAS_COMEBACK_RESP 13 +#define WLAN_TDLS_DISCOVERY_RESPONSE 14 /* SA Query Action frame (IEEE 802.11w/D8.0, 7.4.9) */ #define WLAN_SA_QUERY_REQUEST 0 @@ -227,11 +287,99 @@ #define WLAN_SA_QUERY_TR_ID_LEN 2 +/* TDLS action codes */ +#define WLAN_TDLS_SETUP_REQUEST 0 +#define WLAN_TDLS_SETUP_RESPONSE 1 +#define WLAN_TDLS_SETUP_CONFIRM 2 +#define WLAN_TDLS_TEARDOWN 3 +#define WLAN_TDLS_PEER_TRAFFIC_INDICATION 4 +#define WLAN_TDLS_CHANNEL_SWITCH_REQUEST 5 +#define WLAN_TDLS_CHANNEL_SWITCH_RESPONSE 6 +#define WLAN_TDLS_PEER_PSM_REQUEST 7 +#define WLAN_TDLS_PEER_PSM_RESPONSE 8 +#define WLAN_TDLS_PEER_TRAFFIC_RESPONSE 9 +#define WLAN_TDLS_DISCOVERY_REQUEST 10 + /* Timeout Interval Type */ #define WLAN_TIMEOUT_REASSOC_DEADLINE 1 #define WLAN_TIMEOUT_KEY_LIFETIME 2 #define WLAN_TIMEOUT_ASSOC_COMEBACK 3 +/* Interworking element (IEEE 802.11u) - Access Network Options */ +#define INTERWORKING_ANO_ACCESS_NETWORK_MASK 0x0f +#define INTERWORKING_ANO_INTERNET 0x10 +#define INTERWORKING_ANO_ASRA 0x20 +#define INTERWORKING_ANO_ESR 0x40 +#define INTERWORKING_ANO_UESA 0x80 + +#define INTERWORKING_ANT_PRIVATE 0 +#define INTERWORKING_ANT_PRIVATE_WITH_GUEST 1 +#define INTERWORKING_ANT_CHARGEABLE_PUBLIC 2 +#define INTERWORKING_ANT_FREE_PUBLIC 3 +#define INTERWORKING_ANT_PERSONAL_DEVICE 4 +#define INTERWORKING_ANT_EMERGENCY_SERVICES 5 +#define INTERWORKING_ANT_TEST 6 +#define INTERWORKING_ANT_WILDCARD 15 + +/* Advertisement Protocol ID definitions (IEEE Std 802.11u-2011) */ +enum adv_proto_id { + ACCESS_NETWORK_QUERY_PROTOCOL = 0, + MIH_INFO_SERVICE = 1, + MIH_CMD_AND_EVENT_DISCOVERY = 2, + EMERGENCY_ALERT_SYSTEM = 3, + ADV_PROTO_VENDOR_SPECIFIC = 221 +}; + +/* Access Network Query Protocol info ID definitions (IEEE Std 802.11u-2011) */ +enum anqp_info_id { + ANQP_QUERY_LIST = 256, + ANQP_CAPABILITY_LIST = 257, + ANQP_VENUE_NAME = 258, + ANQP_EMERGENCY_CALL_NUMBER = 259, + ANQP_NETWORK_AUTH_TYPE = 260, + ANQP_ROAMING_CONSORTIUM = 261, + ANQP_IP_ADDR_TYPE_AVAILABILITY = 262, + ANQP_NAI_REALM = 263, + ANQP_3GPP_CELLULAR_NETWORK = 264, + ANQP_AP_GEOSPATIAL_LOCATION = 265, + ANQP_AP_CIVIC_LOCATION = 266, + ANQP_AP_LOCATION_PUBLIC_URI = 267, + ANQP_DOMAIN_NAME = 268, + ANQP_EMERGENCY_ALERT_URI = 269, + ANQP_EMERGENCY_NAI = 271, + ANQP_VENDOR_SPECIFIC = 56797 +}; + +/* NAI Realm list - EAP Method subfield - Authentication Parameter ID */ +enum nai_realm_eap_auth_param { + NAI_REALM_EAP_AUTH_EXPANDED_EAP_METHOD = 1, + NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH = 2, + NAI_REALM_EAP_AUTH_INNER_AUTH_EAP_METHOD = 3, + NAI_REALM_EAP_AUTH_EXPANDED_INNER_EAP_METHOD = 4, + NAI_REALM_EAP_AUTH_CRED_TYPE = 5, + NAI_REALM_EAP_AUTH_TUNNELED_CRED_TYPE = 6, + NAI_REALM_EAP_AUTH_VENDOR_SPECIFIC = 221 +}; + +enum nai_realm_eap_auth_inner_non_eap { + NAI_REALM_INNER_NON_EAP_PAP = 1, + NAI_REALM_INNER_NON_EAP_CHAP = 2, + NAI_REALM_INNER_NON_EAP_MSCHAP = 3, + NAI_REALM_INNER_NON_EAP_MSCHAPV2 = 4 +}; + +enum nai_realm_eap_cred_type { + NAI_REALM_CRED_TYPE_SIM = 1, + NAI_REALM_CRED_TYPE_USIM = 2, + NAI_REALM_CRED_TYPE_NFC_SECURE_ELEMENT = 3, + NAI_REALM_CRED_TYPE_HARDWARE_TOKEN = 4, + NAI_REALM_CRED_TYPE_SOFTOKEN = 5, + NAI_REALM_CRED_TYPE_CERTIFICATE = 6, + NAI_REALM_CRED_TYPE_USERNAME_PASSWORD = 7, + NAI_REALM_CRED_TYPE_NONE = 8, + NAI_REALM_CRED_TYPE_ANONYMOUS = 9, + NAI_REALM_CRED_TYPE_VENDOR_SPECIFIC = 10 +}; #ifdef _MSC_VER #pragma pack(push, 1) @@ -273,6 +421,7 @@ struct ieee80211_mgmt { } STRUCT_PACKED auth; struct { le16 reason_code; + u8 variable[0]; } STRUCT_PACKED deauth; struct { le16 capab_info; @@ -296,6 +445,7 @@ struct ieee80211_mgmt { } STRUCT_PACKED reassoc_req; struct { le16 reason_code; + u8 variable[0]; } STRUCT_PACKED disassoc; struct { u8 timestamp[8]; @@ -355,12 +505,58 @@ struct ieee80211_mgmt { u8 action; /* */ u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; } 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 { + u8 action; /* 9 */ + u8 oui[3]; + /* Vendor-specific content */ + u8 variable[0]; + } STRUCT_PACKED vs_public_action; + struct { + u8 action; /* 7 */ + u8 dialog_token; + u8 req_mode; + le16 disassoc_timer; + u8 validity_interval; + /* BSS Termination Duration (optional), + * Session Information URL (optional), + * BSS Transition Candidate List + * Entries */ + u8 variable[0]; + } STRUCT_PACKED bss_tm_req; + struct { + u8 action; /* 8 */ + u8 dialog_token; + u8 status_code; + u8 bss_termination_delay; + /* Target BSSID (optional), + * BSS Transition Candidate List + * Entries (optional) */ + u8 variable[0]; + } STRUCT_PACKED bss_tm_resp; } u; } STRUCT_PACKED action; } u; } STRUCT_PACKED; +/* Rx MCS bitmask is in the first 77 bits of supported_mcs_set */ +#define IEEE80211_HT_MCS_MASK_LEN 10 + struct ieee80211_ht_capabilities { le16 ht_capabilities_info; u8 a_mpdu_params; @@ -379,6 +575,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 */ @@ -460,7 +669,7 @@ struct ieee80211_ht_operation { #define OP_MODE_MIXED 3 #define HT_INFO_OPERATION_MODE_OP_MODE_MASK \ - ((le16) (0x0001 | 0x0002)) + (0x0001 | 0x0002) #define HT_INFO_OPERATION_MODE_OP_MODE_OFFSET 0 #define HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT ((u8) BIT(2)) #define HT_INFO_OPERATION_MODE_TRANSMIT_BURST_LIMIT ((u8) BIT(3)) @@ -473,11 +682,45 @@ struct ieee80211_ht_operation { #define HT_INFO_STBC_PARAM_PCO_ACTIVE ((u16) BIT(10)) #define HT_INFO_STBC_PARAM_PCO_PHASE ((u16) BIT(11)) +#define BSS_MEMBERSHIP_SELECTOR_VHT_PHY 126 +#define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127 + +/* VHT Defines */ +#define VHT_CAP_MAX_MPDU_LENGTH_7991 ((u32) BIT(0)) +#define VHT_CAP_MAX_MPDU_LENGTH_11454 ((u32) BIT(1)) +#define VHT_CAP_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 WFD_IE_VENDOR_TYPE 0x506f9a0a +#define WFD_OUI_TYPE 10 +#define HS20_IE_VENDOR_TYPE 0x506f9a10 #define WMM_OUI_TYPE 2 #define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0 @@ -516,6 +759,10 @@ struct wmm_information_element { } STRUCT_PACKED; +#define WMM_QOSINFO_STA_AC_MASK 0x0f +#define WMM_QOSINFO_STA_SP_MASK 0x03 +#define WMM_QOSINFO_STA_SP_SHIFT 5 + #define WMM_AC_AIFSN_MASK 0x0f #define WMM_AC_AIFNS_SHIFT 0 #define WMM_AC_ACM 0x10 @@ -544,7 +791,7 @@ struct wmm_parameter_element { u8 oui_type; /* 2 */ u8 oui_subtype; /* 1 */ u8 version; /* 1 for WMM version 1.0 */ - u8 qos_info; /* AP/STA specif QoS info */ + u8 qos_info; /* AP/STA specific QoS info */ u8 reserved; /* 0 */ struct wmm_ac_parameter ac[4]; /* AC_BE, AC_BK, AC_VI, AC_VO */ @@ -587,6 +834,140 @@ 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 + +enum p2p_attr_id { + P2P_ATTR_STATUS = 0, + P2P_ATTR_MINOR_REASON_CODE = 1, + P2P_ATTR_CAPABILITY = 2, + P2P_ATTR_DEVICE_ID = 3, + P2P_ATTR_GROUP_OWNER_INTENT = 4, + P2P_ATTR_CONFIGURATION_TIMEOUT = 5, + P2P_ATTR_LISTEN_CHANNEL = 6, + P2P_ATTR_GROUP_BSSID = 7, + P2P_ATTR_EXT_LISTEN_TIMING = 8, + P2P_ATTR_INTENDED_INTERFACE_ADDR = 9, + P2P_ATTR_MANAGEABILITY = 10, + P2P_ATTR_CHANNEL_LIST = 11, + P2P_ATTR_NOTICE_OF_ABSENCE = 12, + P2P_ATTR_DEVICE_INFO = 13, + P2P_ATTR_GROUP_INFO = 14, + P2P_ATTR_GROUP_ID = 15, + P2P_ATTR_INTERFACE = 16, + P2P_ATTR_OPERATING_CHANNEL = 17, + P2P_ATTR_INVITATION_FLAGS = 18, + P2P_ATTR_VENDOR_SPECIFIC = 221 +}; + +#define P2P_MAX_GO_INTENT 15 + +/* P2P Capability - Device Capability bitmap */ +#define P2P_DEV_CAPAB_SERVICE_DISCOVERY BIT(0) +#define P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY BIT(1) +#define P2P_DEV_CAPAB_CONCURRENT_OPER BIT(2) +#define P2P_DEV_CAPAB_INFRA_MANAGED BIT(3) +#define P2P_DEV_CAPAB_DEVICE_LIMIT BIT(4) +#define P2P_DEV_CAPAB_INVITATION_PROCEDURE BIT(5) + +/* P2P Capability - Group Capability bitmap */ +#define P2P_GROUP_CAPAB_GROUP_OWNER BIT(0) +#define P2P_GROUP_CAPAB_PERSISTENT_GROUP BIT(1) +#define P2P_GROUP_CAPAB_GROUP_LIMIT BIT(2) +#define P2P_GROUP_CAPAB_INTRA_BSS_DIST BIT(3) +#define P2P_GROUP_CAPAB_CROSS_CONN BIT(4) +#define P2P_GROUP_CAPAB_PERSISTENT_RECONN BIT(5) +#define P2P_GROUP_CAPAB_GROUP_FORMATION BIT(6) + +/* Invitation Flags */ +#define P2P_INVITATION_FLAGS_TYPE BIT(0) + +/* P2P Manageability */ +#define P2P_MAN_DEVICE_MANAGEMENT BIT(0) +#define P2P_MAN_CROSS_CONNECTION_PERMITTED BIT(1) +#define P2P_MAN_COEXISTENCE_OPTIONAL BIT(2) + +enum p2p_status_code { + P2P_SC_SUCCESS = 0, + P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE = 1, + P2P_SC_FAIL_INCOMPATIBLE_PARAMS = 2, + P2P_SC_FAIL_LIMIT_REACHED = 3, + P2P_SC_FAIL_INVALID_PARAMS = 4, + P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE = 5, + P2P_SC_FAIL_PREV_PROTOCOL_ERROR = 6, + P2P_SC_FAIL_NO_COMMON_CHANNELS = 7, + P2P_SC_FAIL_UNKNOWN_GROUP = 8, + P2P_SC_FAIL_BOTH_GO_INTENT_15 = 9, + P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD = 10, + P2P_SC_FAIL_REJECTED_BY_USER = 11, +}; + +#define P2P_WILDCARD_SSID "DIRECT-" +#define P2P_WILDCARD_SSID_LEN 7 + +/* P2P action frames */ +enum p2p_act_frame_type { + P2P_NOA = 0, + P2P_PRESENCE_REQ = 1, + P2P_PRESENCE_RESP = 2, + P2P_GO_DISC_REQ = 3 +}; + +/* P2P public action frames */ +enum p2p_action_frame_type { + P2P_GO_NEG_REQ = 0, + P2P_GO_NEG_RESP = 1, + P2P_GO_NEG_CONF = 2, + P2P_INVITATION_REQ = 3, + P2P_INVITATION_RESP = 4, + P2P_DEV_DISC_REQ = 5, + P2P_DEV_DISC_RESP = 6, + P2P_PROV_DISC_REQ = 7, + P2P_PROV_DISC_RESP = 8 +}; + +enum p2p_service_protocol_type { + P2P_SERV_ALL_SERVICES = 0, + P2P_SERV_BONJOUR = 1, + P2P_SERV_UPNP = 2, + P2P_SERV_WS_DISCOVERY = 3, + P2P_SERV_WIFI_DISPLAY = 4, + P2P_SERV_VENDOR_SPECIFIC = 255 +}; + +enum p2p_sd_status { + P2P_SD_SUCCESS = 0, + P2P_SD_PROTO_NOT_AVAILABLE = 1, + P2P_SD_REQUESTED_INFO_NOT_AVAILABLE = 2, + P2P_SD_BAD_REQUEST = 3 +}; + + +enum wifi_display_subelem { + WFD_SUBELEM_DEVICE_INFO = 0, + WFD_SUBELEM_ASSOCIATED_BSSID = 1, + WFD_SUBELEM_AUDIO_FORMATS = 2, + WFD_SUBELEM_VIDEO_FORMATS = 3, + WFD_SUBELEM_3D_VIDEO_FORMATS = 4, + WFD_SUBELEM_CONTENT_PROTECTION = 5, + WFD_SUBELEM_COUPLED_SINK = 6, + WFD_SUBELEM_EXT_CAPAB = 7, + WFD_SUBELEM_LOCAL_IP_ADDRESS = 8, + WFD_SUBELEM_SESSION_INFO = 9 +}; + + #define OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */ #define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */ @@ -599,9 +980,106 @@ enum { #define WLAN_CIPHER_SUITE_CCMP 0x000FAC04 #define WLAN_CIPHER_SUITE_WEP104 0x000FAC05 #define WLAN_CIPHER_SUITE_AES_CMAC 0x000FAC06 +#define WLAN_CIPHER_SUITE_NO_GROUP_ADDR 0x000FAC07 +#define WLAN_CIPHER_SUITE_GCMP 0x000FAC08 + +#define WLAN_CIPHER_SUITE_SMS4 0x00147201 + +#define WLAN_CIPHER_SUITE_CKIP 0x00409600 +#define WLAN_CIPHER_SUITE_CKIP_CMIC 0x00409601 +#define WLAN_CIPHER_SUITE_CMIC 0x00409602 +#define WLAN_CIPHER_SUITE_KRK 0x004096FF /* for nl80211 use only */ /* AKM suite selectors */ #define WLAN_AKM_SUITE_8021X 0x000FAC01 #define WLAN_AKM_SUITE_PSK 0x000FAC02 +#define WLAN_AKM_SUITE_CCKM 0x00409600 + + +/* IEEE 802.11v - WNM Action field values */ +enum wnm_action { + WNM_EVENT_REQ = 0, + WNM_EVENT_REPORT = 1, + WNM_DIAGNOSTIC_REQ = 2, + WNM_DIAGNOSTIC_REPORT = 3, + WNM_LOCATION_CFG_REQ = 4, + WNM_LOCATION_CFG_RESP = 5, + WNM_BSS_TRANS_MGMT_QUERY = 6, + WNM_BSS_TRANS_MGMT_REQ = 7, + WNM_BSS_TRANS_MGMT_RESP = 8, + WNM_FMS_REQ = 9, + WNM_FMS_RESP = 10, + WNM_COLLOCATED_INTERFERENCE_REQ = 11, + WNM_COLLOCATED_INTERFERENCE_REPORT = 12, + WNM_TFS_REQ = 13, + WNM_TFS_RESP = 14, + WNM_TFS_NOTIFY = 15, + WNM_SLEEP_MODE_REQ = 16, + WNM_SLEEP_MODE_RESP = 17, + WNM_TIM_BROADCAST_REQ = 18, + WNM_TIM_BROADCAST_RESP = 19, + WNM_QOS_TRAFFIC_CAPAB_UPDATE = 20, + WNM_CHANNEL_USAGE_REQ = 21, + WNM_CHANNEL_USAGE_RESP = 22, + WNM_DMS_REQ = 23, + WNM_DMS_RESP = 24, + WNM_TIMING_MEASUREMENT_REQ = 25, + WNM_NOTIFICATION_REQ = 26, + WNM_NOTIFICATION_RESP = 27 +}; + +/* IEEE 802.11v - BSS Transition Management Request - Request Mode */ +#define WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED BIT(0) +#define WNM_BSS_TM_REQ_ABRIDGED BIT(1) +#define WNM_BSS_TM_REQ_DISASSOC_IMMINENT BIT(2) +#define WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED BIT(3) +#define WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT BIT(4) + +/* IEEE Std 802.11-2012, 8.4.2.62 20/40 BSS Coexistence element */ +#define WLAN_20_40_BSS_COEX_INFO_REQ BIT(0) +#define WLAN_20_40_BSS_COEX_40MHZ_INTOL BIT(1) +#define WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ BIT(2) +#define WLAN_20_40_BSS_COEX_OBSS_EXEMPT_REQ BIT(3) +#define WLAN_20_40_BSS_COEX_OBSS_EXEMPT_GRNT BIT(4) + +struct ieee80211_2040_bss_coex_ie { + u8 element_id; + u8 length; + u8 coex_param; +} STRUCT_PACKED; + +struct ieee80211_2040_intol_chan_report { + u8 element_id; + u8 length; + u8 op_class; + u8 variable[0]; /* Channel List */ +} STRUCT_PACKED; + +/* IEEE 802.11v - WNM-Sleep Mode element */ +struct wnm_sleep_element { + u8 eid; /* WLAN_EID_WNMSLEEP */ + u8 len; + u8 action_type; /* WNM_SLEEP_ENTER/WNM_SLEEP_MODE_EXIT */ + u8 status; + le16 intval; +} STRUCT_PACKED; + +#define WNM_SLEEP_MODE_ENTER 0 +#define WNM_SLEEP_MODE_EXIT 1 + +enum wnm_sleep_mode_response_status { + WNM_STATUS_SLEEP_ACCEPT = 0, + WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE = 1, + WNM_STATUS_DENIED_ACTION = 2, + WNM_STATUS_DENIED_TMP = 3, + WNM_STATUS_DENIED_KEY = 4, + WNM_STATUS_DENIED_OTHER_WNM_SERVICE = 5 +}; + +/* WNM-Sleep Mode subelement IDs */ +enum wnm_sleep_mode_subelement_id { + WNM_SLEEP_SUBELEM_GTK = 0, + WNM_SLEEP_SUBELEM_IGTK = 1 +}; #endif /* IEEE802_11_DEFS_H */ diff --git a/src/common/privsep_commands.h b/src/common/privsep_commands.h index cc900be9d20ed..858b51d388c0d 100644 --- a/src/common/privsep_commands.h +++ b/src/common/privsep_commands.h @@ -2,14 +2,8 @@ * WPA Supplicant - privilege separation commands * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef PRIVSEP_COMMANDS_H diff --git a/src/common/version.h b/src/common/version.h index 02f34be3ab125..04ed0acb6238f 100644 --- a/src/common/version.h +++ b/src/common/version.h @@ -1,6 +1,10 @@ #ifndef VERSION_H #define VERSION_H -#define VERSION_STR "0.7.3" +#ifndef VERSION_STR_POSTFIX +#define VERSION_STR_POSTFIX "" +#endif /* VERSION_STR_POSTFIX */ + +#define VERSION_STR "2.0" VERSION_STR_POSTFIX #endif /* VERSION_H */ diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index b295f315624aa..8d7a11cfc74b4 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -2,14 +2,8 @@ * WPA/RSN - Shared functions for supplicant and authenticator * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -49,8 +43,10 @@ int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len, u8 hash[SHA1_MAC_LEN]; switch (ver) { +#ifndef CONFIG_FIPS case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4: return hmac_md5(key, 16, buf, len, mic); +#endif /* CONFIG_FIPS */ case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES: if (hmac_sha1(key, 16, buf, len, hash)) return -1; @@ -126,6 +122,8 @@ void wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, wpa_printf(MSG_DEBUG, "WPA: PTK derivation - A1=" MACSTR " A2=" MACSTR, MAC2STR(addr1), MAC2STR(addr2)); + wpa_hexdump(MSG_DEBUG, "WPA: Nonce1", nonce1, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "WPA: Nonce2", nonce2, WPA_NONCE_LEN); wpa_hexdump_key(MSG_DEBUG, "WPA: PMK", pmk, pmk_len); wpa_hexdump_key(MSG_DEBUG, "WPA: PTK", ptk, ptk_len); } @@ -186,6 +184,154 @@ int wpa_ft_mic(const u8 *kck, const u8 *sta_addr, const u8 *ap_addr, return 0; } + + +static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len, + struct wpa_ft_ies *parse) +{ + const u8 *end, *pos; + + parse->ftie = ie; + parse->ftie_len = ie_len; + + pos = ie + sizeof(struct rsn_ftie); + end = ie + ie_len; + + while (pos + 2 <= end && pos + 2 + pos[1] <= end) { + switch (pos[0]) { + case FTIE_SUBELEM_R1KH_ID: + if (pos[1] != FT_R1KH_ID_LEN) { + wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID " + "length in FTIE: %d", pos[1]); + return -1; + } + parse->r1kh_id = pos + 2; + break; + case FTIE_SUBELEM_GTK: + parse->gtk = pos + 2; + parse->gtk_len = pos[1]; + break; + case FTIE_SUBELEM_R0KH_ID: + if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) { + wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID " + "length in FTIE: %d", pos[1]); + return -1; + } + parse->r0kh_id = pos + 2; + parse->r0kh_id_len = pos[1]; + break; +#ifdef CONFIG_IEEE80211W + case FTIE_SUBELEM_IGTK: + parse->igtk = pos + 2; + parse->igtk_len = pos[1]; + break; +#endif /* CONFIG_IEEE80211W */ + } + + pos += 2 + pos[1]; + } + + return 0; +} + + +int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, + struct wpa_ft_ies *parse) +{ + const u8 *end, *pos; + struct wpa_ie_data data; + int ret; + const struct rsn_ftie *ftie; + int prot_ie_count = 0; + + os_memset(parse, 0, sizeof(*parse)); + if (ies == NULL) + return 0; + + pos = ies; + end = ies + ies_len; + while (pos + 2 <= end && pos + 2 + pos[1] <= end) { + switch (pos[0]) { + case WLAN_EID_RSN: + parse->rsn = pos + 2; + parse->rsn_len = pos[1]; + ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2, + parse->rsn_len + 2, + &data); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to parse " + "RSN IE: %d", ret); + return -1; + } + if (data.num_pmkid == 1 && data.pmkid) + parse->rsn_pmkid = data.pmkid; + break; + case WLAN_EID_MOBILITY_DOMAIN: + parse->mdie = pos + 2; + parse->mdie_len = pos[1]; + break; + case WLAN_EID_FAST_BSS_TRANSITION: + if (pos[1] < sizeof(*ftie)) + return -1; + ftie = (const struct rsn_ftie *) (pos + 2); + prot_ie_count = ftie->mic_control[1]; + if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0) + return -1; + break; + case WLAN_EID_TIMEOUT_INTERVAL: + parse->tie = pos + 2; + parse->tie_len = pos[1]; + break; + case WLAN_EID_RIC_DATA: + if (parse->ric == NULL) + parse->ric = pos; + break; + } + + pos += 2 + pos[1]; + } + + if (prot_ie_count == 0) + return 0; /* no MIC */ + + /* + * Check that the protected IE count matches with IEs included in the + * frame. + */ + if (parse->rsn) + prot_ie_count--; + if (parse->mdie) + prot_ie_count--; + if (parse->ftie) + prot_ie_count--; + if (prot_ie_count < 0) { + wpa_printf(MSG_DEBUG, "FT: Some required IEs not included in " + "the protected IE count"); + return -1; + } + + if (prot_ie_count == 0 && parse->ric) { + wpa_printf(MSG_DEBUG, "FT: RIC IE(s) in the frame, but not " + "included in protected IE count"); + return -1; + } + + /* Determine the end of the RIC IE(s) */ + pos = parse->ric; + while (pos && pos + 2 <= end && pos + 2 + pos[1] <= end && + prot_ie_count) { + prot_ie_count--; + pos += 2 + pos[1]; + } + parse->ric_len = pos - parse->ric; + if (prot_ie_count) { + wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from " + "frame", (int) prot_ie_count); + return -1; + } + + return 0; +} #endif /* CONFIG_IEEE80211R */ @@ -206,6 +352,8 @@ static int rsn_selector_to_bitfield(const u8 *s) if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_AES_128_CMAC) return WPA_CIPHER_AES_128_CMAC; #endif /* CONFIG_IEEE80211W */ + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_GCMP) + return WPA_CIPHER_GCMP; return 0; } @@ -228,6 +376,12 @@ static int rsn_key_mgmt_to_bitfield(const u8 *s) if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_SHA256) return WPA_KEY_MGMT_PSK_SHA256; #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_SAE + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_SAE) + return WPA_KEY_MGMT_SAE; + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_SAE) + return WPA_KEY_MGMT_FT_SAE; +#endif /* CONFIG_SAE */ return 0; } #endif /* CONFIG_NO_WPA2 */ @@ -403,6 +557,144 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, } +static int wpa_selector_to_bitfield(const u8 *s) +{ + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_NONE) + return WPA_CIPHER_NONE; + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP40) + return WPA_CIPHER_WEP40; + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_TKIP) + return WPA_CIPHER_TKIP; + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_CCMP) + return WPA_CIPHER_CCMP; + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP104) + return WPA_CIPHER_WEP104; + return 0; +} + + +static int wpa_key_mgmt_to_bitfield(const u8 *s) +{ + if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_UNSPEC_802_1X) + return WPA_KEY_MGMT_IEEE8021X; + if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X) + return WPA_KEY_MGMT_PSK; + if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_NONE) + return WPA_KEY_MGMT_WPA_NONE; + return 0; +} + + +int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, + struct wpa_ie_data *data) +{ + const struct wpa_ie_hdr *hdr; + const u8 *pos; + int left; + int i, count; + + os_memset(data, 0, sizeof(*data)); + data->proto = WPA_PROTO_WPA; + data->pairwise_cipher = WPA_CIPHER_TKIP; + data->group_cipher = WPA_CIPHER_TKIP; + data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; + data->capabilities = 0; + data->pmkid = NULL; + data->num_pmkid = 0; + data->mgmt_group_cipher = 0; + + if (wpa_ie_len == 0) { + /* No WPA IE - fail silently */ + return -1; + } + + if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) { + wpa_printf(MSG_DEBUG, "%s: ie len too short %lu", + __func__, (unsigned long) wpa_ie_len); + return -1; + } + + hdr = (const struct wpa_ie_hdr *) wpa_ie; + + if (hdr->elem_id != WLAN_EID_VENDOR_SPECIFIC || + hdr->len != wpa_ie_len - 2 || + RSN_SELECTOR_GET(hdr->oui) != WPA_OUI_TYPE || + WPA_GET_LE16(hdr->version) != WPA_VERSION) { + wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version", + __func__); + return -2; + } + + pos = (const u8 *) (hdr + 1); + left = wpa_ie_len - sizeof(*hdr); + + if (left >= WPA_SELECTOR_LEN) { + data->group_cipher = wpa_selector_to_bitfield(pos); + pos += WPA_SELECTOR_LEN; + left -= WPA_SELECTOR_LEN; + } else if (left > 0) { + wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much", + __func__, left); + return -3; + } + + if (left >= 2) { + data->pairwise_cipher = 0; + count = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + if (count == 0 || left < count * WPA_SELECTOR_LEN) { + wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), " + "count %u left %u", __func__, count, left); + return -4; + } + for (i = 0; i < count; i++) { + data->pairwise_cipher |= wpa_selector_to_bitfield(pos); + pos += WPA_SELECTOR_LEN; + left -= WPA_SELECTOR_LEN; + } + } else if (left == 1) { + wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)", + __func__); + return -5; + } + + if (left >= 2) { + data->key_mgmt = 0; + count = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + if (count == 0 || left < count * WPA_SELECTOR_LEN) { + wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), " + "count %u left %u", __func__, count, left); + return -6; + } + for (i = 0; i < count; i++) { + data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos); + pos += WPA_SELECTOR_LEN; + left -= WPA_SELECTOR_LEN; + } + } else if (left == 1) { + wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)", + __func__); + return -7; + } + + if (left >= 2) { + data->capabilities = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + } + + if (left > 0) { + wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored", + __func__, left); + } + + return 0; +} + + #ifdef CONFIG_IEEE80211R /** @@ -624,6 +916,8 @@ const char * wpa_cipher_txt(int cipher) return "CCMP"; case WPA_CIPHER_CCMP | WPA_CIPHER_TKIP: return "CCMP+TKIP"; + case WPA_CIPHER_GCMP: + return "GCMP"; default: return "UNKNOWN"; } @@ -785,3 +1079,138 @@ int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid) return added; } #endif /* CONFIG_IEEE80211R */ + + +int wpa_cipher_key_len(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_CCMP: + case WPA_CIPHER_GCMP: + return 16; + case WPA_CIPHER_TKIP: + return 32; + case WPA_CIPHER_WEP104: + return 13; + case WPA_CIPHER_WEP40: + return 5; + } + + return 0; +} + + +int wpa_cipher_rsc_len(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_CCMP: + case WPA_CIPHER_GCMP: + case WPA_CIPHER_TKIP: + return 6; + case WPA_CIPHER_WEP104: + case WPA_CIPHER_WEP40: + return 0; + } + + return 0; +} + + +int wpa_cipher_to_alg(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_CCMP: + return WPA_ALG_CCMP; + case WPA_CIPHER_GCMP: + return WPA_ALG_GCMP; + case WPA_CIPHER_TKIP: + return WPA_ALG_TKIP; + case WPA_CIPHER_WEP104: + case WPA_CIPHER_WEP40: + return WPA_ALG_WEP; + } + return WPA_ALG_NONE; +} + + +int wpa_cipher_valid_pairwise(int cipher) +{ + return cipher == WPA_CIPHER_CCMP || + cipher == WPA_CIPHER_GCMP || + cipher == WPA_CIPHER_TKIP; +} + + +u32 wpa_cipher_to_suite(int proto, int cipher) +{ + if (cipher & WPA_CIPHER_CCMP) + return (proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_CCMP : WPA_CIPHER_SUITE_CCMP); + if (cipher & WPA_CIPHER_GCMP) + return RSN_CIPHER_SUITE_GCMP; + if (cipher & WPA_CIPHER_TKIP) + return (proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP); + if (cipher & WPA_CIPHER_WEP104) + return (proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_WEP104 : WPA_CIPHER_SUITE_WEP104); + if (cipher & WPA_CIPHER_WEP40) + return (proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_WEP40 : WPA_CIPHER_SUITE_WEP40); + if (cipher & WPA_CIPHER_NONE) + return (proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_NONE : WPA_CIPHER_SUITE_NONE); + return 0; +} + + +int rsn_cipher_put_suites(u8 *pos, int ciphers) +{ + int num_suites = 0; + + if (ciphers & WPA_CIPHER_CCMP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (ciphers & WPA_CIPHER_GCMP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (ciphers & WPA_CIPHER_TKIP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (ciphers & WPA_CIPHER_NONE) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + + return num_suites; +} + + +int wpa_cipher_put_suites(u8 *pos, int ciphers) +{ + int num_suites = 0; + + if (ciphers & WPA_CIPHER_CCMP) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + if (ciphers & WPA_CIPHER_TKIP) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + if (ciphers & WPA_CIPHER_NONE) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + + return num_suites; +} diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h index fd8a79fd602e8..20c79d8099b30 100644 --- a/src/common/wpa_common.h +++ b/src/common/wpa_common.h @@ -2,14 +2,8 @@ * WPA definitions shared between hostapd and wpa_supplicant * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_COMMON_H @@ -38,6 +32,7 @@ #define WPA_AUTH_KEY_MGMT_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0) #define WPA_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 1) #define WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 2) +#define WPA_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0) #define WPA_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0) #define WPA_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x50, 0xf2, 1) #define WPA_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x50, 0xf2, 2) @@ -56,6 +51,10 @@ #endif /* CONFIG_IEEE80211R */ #define RSN_AUTH_KEY_MGMT_802_1X_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 5) #define RSN_AUTH_KEY_MGMT_PSK_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 6) +#define RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE RSN_SELECTOR(0x00, 0x0f, 0xac, 7) +#define RSN_AUTH_KEY_MGMT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 8) +#define RSN_AUTH_KEY_MGMT_FT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 9) +#define RSN_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0x00) #define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0) #define RSN_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x0f, 0xac, 1) @@ -68,6 +67,8 @@ #ifdef CONFIG_IEEE80211W #define RSN_CIPHER_SUITE_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6) #endif /* CONFIG_IEEE80211W */ +#define RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED RSN_SELECTOR(0x00, 0x0f, 0xac, 7) +#define RSN_CIPHER_SUITE_GCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 8) /* EAPOL-Key Key Data Encapsulation * GroupKey and PeerKey require encryption, otherwise, encryption is optional. @@ -87,6 +88,9 @@ #ifdef CONFIG_IEEE80211W #define RSN_KEY_DATA_IGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 9) #endif /* CONFIG_IEEE80211W */ +#define RSN_KEY_DATA_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 10) +#define RSN_KEY_DATA_MULTIBAND_GTK RSN_SELECTOR(0x00, 0x0f, 0xac, 11) +#define RSN_KEY_DATA_MULTIBAND_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 12) #define WPA_OUI_TYPE RSN_SELECTOR(0x00, 0x50, 0xf2, 1) @@ -115,7 +119,13 @@ /* B4-B5: GTKSA Replay Counter */ #define WPA_CAPABILITY_MFPR BIT(6) #define WPA_CAPABILITY_MFPC BIT(7) +/* B8: Reserved */ #define WPA_CAPABILITY_PEERKEY_ENABLED BIT(9) +#define WPA_CAPABILITY_SPP_A_MSDU_CAPABLE BIT(10) +#define WPA_CAPABILITY_SPP_A_MSDU_REQUIRED BIT(11) +#define WPA_CAPABILITY_PBAC BIT(12) +#define WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST BIT(13) +/* B14-B15: Reserved */ /* IEEE 802.11r */ @@ -337,6 +347,8 @@ struct wpa_ie_data { int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, struct wpa_ie_data *data); +int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, + struct wpa_ie_data *data); void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, u8 *pmkid, int use_sha256); @@ -348,4 +360,35 @@ int wpa_compare_rsn_ie(int ft_initial_assoc, const u8 *ie2, size_t ie2len); int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid); +struct wpa_ft_ies { + const u8 *mdie; + size_t mdie_len; + const u8 *ftie; + size_t ftie_len; + const u8 *r1kh_id; + const u8 *gtk; + size_t gtk_len; + const u8 *r0kh_id; + size_t r0kh_id_len; + const u8 *rsn; + size_t rsn_len; + const u8 *rsn_pmkid; + const u8 *tie; + size_t tie_len; + const u8 *igtk; + size_t igtk_len; + const u8 *ric; + size_t ric_len; +}; + +int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse); + +int wpa_cipher_key_len(int cipher); +int wpa_cipher_rsc_len(int cipher); +int wpa_cipher_to_alg(int cipher); +int wpa_cipher_valid_pairwise(int cipher); +u32 wpa_cipher_to_suite(int proto, int cipher); +int rsn_cipher_put_suites(u8 *pos, int ciphers); +int wpa_cipher_put_suites(u8 *pos, int ciphers); + #endif /* WPA_COMMON_H */ diff --git a/src/common/wpa_ctrl.c b/src/common/wpa_ctrl.c index 2b4e3aa5ee1f7..58cbe6a0d1c99 100644 --- a/src/common/wpa_ctrl.c +++ b/src/common/wpa_ctrl.c @@ -2,14 +2,8 @@ * wpa_supplicant/hostapd control interface library * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -18,7 +12,18 @@ #ifdef CONFIG_CTRL_IFACE_UNIX #include <sys/un.h> +#include <unistd.h> +#include <fcntl.h> #endif /* CONFIG_CTRL_IFACE_UNIX */ +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE +#include <netdb.h> +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + +#ifdef ANDROID +#include <dirent.h> +#include <cutils/sockets.h> +#include "private/android_filesystem_config.h" +#endif /* ANDROID */ #include "wpa_ctrl.h" #include "common.h" @@ -44,6 +49,8 @@ struct wpa_ctrl { struct sockaddr_in local; struct sockaddr_in dest; char *cookie; + char *remote_ifname; + char *remote_ip; #endif /* CONFIG_CTRL_IFACE_UDP */ #ifdef CONFIG_CTRL_IFACE_UNIX int s; @@ -58,6 +65,14 @@ struct wpa_ctrl { #ifdef CONFIG_CTRL_IFACE_UNIX +#ifndef CONFIG_CTRL_IFACE_CLIENT_DIR +#define CONFIG_CTRL_IFACE_CLIENT_DIR "/tmp" +#endif /* CONFIG_CTRL_IFACE_CLIENT_DIR */ +#ifndef CONFIG_CTRL_IFACE_CLIENT_PREFIX +#define CONFIG_CTRL_IFACE_CLIENT_PREFIX "wpa_ctrl_" +#endif /* CONFIG_CTRL_IFACE_CLIENT_PREFIX */ + + struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) { struct wpa_ctrl *ctrl; @@ -65,6 +80,7 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) int ret; size_t res; int tries = 0; + int flags; ctrl = os_malloc(sizeof(*ctrl)); if (ctrl == NULL) @@ -81,7 +97,9 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) counter++; try_again: ret = os_snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path), - "/tmp/wpa_ctrl_%d-%d", getpid(), counter); + CONFIG_CTRL_IFACE_CLIENT_DIR "/" + CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d", + (int) getpid(), counter); if (ret < 0 || (size_t) ret >= sizeof(ctrl->local.sun_path)) { close(ctrl->s); os_free(ctrl); @@ -105,6 +123,31 @@ try_again: return NULL; } +#ifdef ANDROID + chmod(ctrl->local.sun_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + chown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI); + /* + * If the ctrl_path isn't an absolute pathname, assume that + * it's the name of a socket in the Android reserved namespace. + * Otherwise, it's a normal UNIX domain socket appearing in the + * filesystem. + */ + if (ctrl_path != NULL && *ctrl_path != '/') { + char buf[21]; + os_snprintf(buf, sizeof(buf), "wpa_%s", ctrl_path); + if (socket_local_client_connect( + ctrl->s, buf, + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_DGRAM) < 0) { + close(ctrl->s); + unlink(ctrl->local.sun_path); + os_free(ctrl); + return NULL; + } + return ctrl; + } +#endif /* ANDROID */ + ctrl->dest.sun_family = AF_UNIX; res = os_strlcpy(ctrl->dest.sun_path, ctrl_path, sizeof(ctrl->dest.sun_path)); @@ -121,17 +164,83 @@ try_again: return NULL; } + /* + * Make socket non-blocking so that we don't hang forever if + * target dies unexpectedly. + */ + flags = fcntl(ctrl->s, F_GETFL); + if (flags >= 0) { + flags |= O_NONBLOCK; + if (fcntl(ctrl->s, F_SETFL, flags) < 0) { + perror("fcntl(ctrl->s, O_NONBLOCK)"); + /* Not fatal, continue on.*/ + } + } + return ctrl; } void wpa_ctrl_close(struct wpa_ctrl *ctrl) { + if (ctrl == NULL) + return; unlink(ctrl->local.sun_path); - close(ctrl->s); + if (ctrl->s >= 0) + close(ctrl->s); os_free(ctrl); } + +#ifdef ANDROID +/** + * wpa_ctrl_cleanup() - Delete any local UNIX domain socket files that + * may be left over from clients that were previously connected to + * wpa_supplicant. This keeps these files from being orphaned in the + * event of crashes that prevented them from being removed as part + * of the normal orderly shutdown. + */ +void wpa_ctrl_cleanup(void) +{ + DIR *dir; + struct dirent entry; + struct dirent *result; + size_t dirnamelen; + int prefixlen = os_strlen(CONFIG_CTRL_IFACE_CLIENT_PREFIX); + size_t maxcopy; + char pathname[PATH_MAX]; + char *namep; + + if ((dir = opendir(CONFIG_CTRL_IFACE_CLIENT_DIR)) == NULL) + return; + + dirnamelen = (size_t) os_snprintf(pathname, sizeof(pathname), "%s/", + CONFIG_CTRL_IFACE_CLIENT_DIR); + if (dirnamelen >= sizeof(pathname)) { + closedir(dir); + return; + } + namep = pathname + dirnamelen; + maxcopy = PATH_MAX - dirnamelen; + while (readdir_r(dir, &entry, &result) == 0 && result != NULL) { + if (os_strncmp(entry.d_name, CONFIG_CTRL_IFACE_CLIENT_PREFIX, + prefixlen) == 0) { + if (os_strlcpy(namep, entry.d_name, maxcopy) < maxcopy) + unlink(pathname); + } + } + closedir(dir); +} +#endif /* ANDROID */ + +#else /* CONFIG_CTRL_IFACE_UNIX */ + +#ifdef ANDROID +void wpa_ctrl_cleanup(void) +{ +} +#endif /* ANDROID */ + #endif /* CONFIG_CTRL_IFACE_UNIX */ @@ -142,6 +251,9 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) struct wpa_ctrl *ctrl; char buf[128]; size_t len; +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + struct hostent *h; +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ ctrl = os_malloc(sizeof(*ctrl)); if (ctrl == NULL) @@ -156,7 +268,11 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) } ctrl->local.sin_family = AF_INET; +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + ctrl->local.sin_addr.s_addr = INADDR_ANY; +#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */ ctrl->local.sin_addr.s_addr = htonl((127 << 24) | 1); +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ if (bind(ctrl->s, (struct sockaddr *) &ctrl->local, sizeof(ctrl->local)) < 0) { close(ctrl->s); @@ -167,10 +283,48 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) ctrl->dest.sin_family = AF_INET; ctrl->dest.sin_addr.s_addr = htonl((127 << 24) | 1); ctrl->dest.sin_port = htons(WPA_CTRL_IFACE_PORT); + +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + if (ctrl_path) { + char *port, *name; + int port_id; + + name = os_strdup(ctrl_path); + if (name == NULL) { + close(ctrl->s); + os_free(ctrl); + return NULL; + } + port = os_strchr(name, ':'); + + if (port) { + port_id = atoi(&port[1]); + port[0] = '\0'; + } else + port_id = WPA_CTRL_IFACE_PORT; + + h = gethostbyname(name); + ctrl->remote_ip = os_strdup(name); + os_free(name); + if (h == NULL) { + perror("gethostbyname"); + close(ctrl->s); + os_free(ctrl->remote_ip); + os_free(ctrl); + return NULL; + } + ctrl->dest.sin_port = htons(port_id); + os_memcpy(h->h_addr, (char *) &ctrl->dest.sin_addr.s_addr, + h->h_length); + } else + ctrl->remote_ip = os_strdup("localhost"); +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest, sizeof(ctrl->dest)) < 0) { perror("connect"); close(ctrl->s); + os_free(ctrl->remote_ip); os_free(ctrl); return NULL; } @@ -181,14 +335,31 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) ctrl->cookie = os_strdup(buf); } + if (wpa_ctrl_request(ctrl, "IFNAME", 6, buf, &len, NULL) == 0) { + buf[len] = '\0'; + ctrl->remote_ifname = os_strdup(buf); + } + return ctrl; } +char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl) +{ +#define WPA_CTRL_MAX_PS_NAME 100 + static char ps[WPA_CTRL_MAX_PS_NAME] = {}; + os_snprintf(ps, WPA_CTRL_MAX_PS_NAME, "%s/%s", + ctrl->remote_ip, ctrl->remote_ifname); + return ps; +} + + void wpa_ctrl_close(struct wpa_ctrl *ctrl) { close(ctrl->s); os_free(ctrl->cookie); + os_free(ctrl->remote_ifname); + os_free(ctrl->remote_ip); os_free(ctrl); } @@ -201,6 +372,7 @@ int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, void (*msg_cb)(char *msg, size_t len)) { struct timeval tv; + struct os_time started_at; int res; fd_set rfds; const char *_cmd; @@ -227,18 +399,43 @@ int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, _cmd_len = cmd_len; } + errno = 0; + started_at.sec = 0; + started_at.usec = 0; +retry_send: if (send(ctrl->s, _cmd, _cmd_len, 0) < 0) { + if (errno == EAGAIN || errno == EBUSY || errno == EWOULDBLOCK) + { + /* + * Must be a non-blocking socket... Try for a bit + * longer before giving up. + */ + if (started_at.sec == 0) + os_get_time(&started_at); + else { + struct os_time n; + os_get_time(&n); + /* Try for a few seconds. */ + if (n.sec > started_at.sec + 5) + goto send_err; + } + os_sleep(1, 0); + goto retry_send; + } + send_err: os_free(cmd_buf); return -1; } os_free(cmd_buf); for (;;) { - tv.tv_sec = 2; + tv.tv_sec = 10; tv.tv_usec = 0; FD_ZERO(&rfds); FD_SET(ctrl->s, &rfds); res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv); + if (res < 0) + return res; if (FD_ISSET(ctrl->s, &rfds)) { res = recv(ctrl->s, reply, *reply_len, 0); if (res < 0) diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index d770fd42d94f7..bb9b5587f26b2 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -2,14 +2,8 @@ * wpa_supplicant/hostapd control interface library * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_CTRL_H @@ -32,6 +26,8 @@ extern "C" { #define WPA_EVENT_CONNECTED "CTRL-EVENT-CONNECTED " /** Disconnected, data connection is not available */ #define WPA_EVENT_DISCONNECTED "CTRL-EVENT-DISCONNECTED " +/** Association rejected during connection attempt */ +#define WPA_EVENT_ASSOC_REJECT "CTRL-EVENT-ASSOC-REJECT " /** wpa_supplicant is exiting */ #define WPA_EVENT_TERMINATING "CTRL-EVENT-TERMINATING " /** Password change was completed successfully */ @@ -52,8 +48,14 @@ extern "C" { #define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS " /** EAP authentication failed (EAP-Failure received) */ #define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE " +/** Network block temporarily disabled (e.g., due to authentication failure) */ +#define WPA_EVENT_TEMP_DISABLED "CTRL-EVENT-SSID-TEMP-DISABLED " +/** Temporarily disabled network block re-enabled */ +#define WPA_EVENT_REENABLED "CTRL-EVENT-SSID-REENABLED " /** New scan results available */ #define WPA_EVENT_SCAN_RESULTS "CTRL-EVENT-SCAN-RESULTS " +/** wpa_supplicant state change */ +#define WPA_EVENT_STATE_CHANGE "CTRL-EVENT-STATE-CHANGE " /** A new BSS entry was added (followed by BSS entry id and BSSID) */ #define WPA_EVENT_BSS_ADDED "CTRL-EVENT-BSS-ADDED " /** A BSS entry was removed (followed by BSS entry id and BSSID) */ @@ -63,6 +65,8 @@ extern "C" { #define WPS_EVENT_OVERLAP "WPS-OVERLAP-DETECTED " /** Available WPS AP with active PBC found in scan results */ #define WPS_EVENT_AP_AVAILABLE_PBC "WPS-AP-AVAILABLE-PBC " +/** Available WPS AP with our address as authorized in scan results */ +#define WPS_EVENT_AP_AVAILABLE_AUTH "WPS-AP-AVAILABLE-AUTH " /** Available WPS AP with recently selected PIN registrar found in scan results */ #define WPS_EVENT_AP_AVAILABLE_PIN "WPS-AP-AVAILABLE-PIN " @@ -81,11 +85,55 @@ extern "C" { #define WPS_EVENT_ENROLLEE_SEEN "WPS-ENROLLEE-SEEN " +#define WPS_EVENT_OPEN_NETWORK "WPS-OPEN-NETWORK " + /* WPS ER events */ #define WPS_EVENT_ER_AP_ADD "WPS-ER-AP-ADD " #define WPS_EVENT_ER_AP_REMOVE "WPS-ER-AP-REMOVE " #define WPS_EVENT_ER_ENROLLEE_ADD "WPS-ER-ENROLLEE-ADD " #define WPS_EVENT_ER_ENROLLEE_REMOVE "WPS-ER-ENROLLEE-REMOVE " +#define WPS_EVENT_ER_AP_SETTINGS "WPS-ER-AP-SETTINGS " +#define WPS_EVENT_ER_SET_SEL_REG "WPS-ER-AP-SET-SEL-REG " + +/** P2P device found */ +#define P2P_EVENT_DEVICE_FOUND "P2P-DEVICE-FOUND " + +/** P2P device lost */ +#define P2P_EVENT_DEVICE_LOST "P2P-DEVICE-LOST " + +/** A P2P device requested GO negotiation, but we were not ready to start the + * negotiation */ +#define P2P_EVENT_GO_NEG_REQUEST "P2P-GO-NEG-REQUEST " +#define P2P_EVENT_GO_NEG_SUCCESS "P2P-GO-NEG-SUCCESS " +#define P2P_EVENT_GO_NEG_FAILURE "P2P-GO-NEG-FAILURE " +#define P2P_EVENT_GROUP_FORMATION_SUCCESS "P2P-GROUP-FORMATION-SUCCESS " +#define P2P_EVENT_GROUP_FORMATION_FAILURE "P2P-GROUP-FORMATION-FAILURE " +#define P2P_EVENT_GROUP_STARTED "P2P-GROUP-STARTED " +#define P2P_EVENT_GROUP_REMOVED "P2P-GROUP-REMOVED " +#define P2P_EVENT_CROSS_CONNECT_ENABLE "P2P-CROSS-CONNECT-ENABLE " +#define P2P_EVENT_CROSS_CONNECT_DISABLE "P2P-CROSS-CONNECT-DISABLE " +/* parameters: <peer address> <PIN> */ +#define P2P_EVENT_PROV_DISC_SHOW_PIN "P2P-PROV-DISC-SHOW-PIN " +/* parameters: <peer address> */ +#define P2P_EVENT_PROV_DISC_ENTER_PIN "P2P-PROV-DISC-ENTER-PIN " +/* parameters: <peer address> */ +#define P2P_EVENT_PROV_DISC_PBC_REQ "P2P-PROV-DISC-PBC-REQ " +/* parameters: <peer address> */ +#define P2P_EVENT_PROV_DISC_PBC_RESP "P2P-PROV-DISC-PBC-RESP " +/* parameters: <peer address> <status> */ +#define P2P_EVENT_PROV_DISC_FAILURE "P2P-PROV-DISC-FAILURE" +/* parameters: <freq> <src addr> <dialog token> <update indicator> <TLVs> */ +#define P2P_EVENT_SERV_DISC_REQ "P2P-SERV-DISC-REQ " +/* parameters: <src addr> <update indicator> <TLVs> */ +#define P2P_EVENT_SERV_DISC_RESP "P2P-SERV-DISC-RESP " +#define P2P_EVENT_INVITATION_RECEIVED "P2P-INVITATION-RECEIVED " +#define P2P_EVENT_INVITATION_RESULT "P2P-INVITATION-RESULT " +#define P2P_EVENT_FIND_STOPPED "P2P-FIND-STOPPED " + +#define INTERWORKING_AP "INTERWORKING-AP " +#define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH " + +#define GAS_RESPONSE_INFO "GAS-RESPONSE-INFO " /* hostapd control interface - fixed message prefixes */ #define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED " @@ -99,6 +147,28 @@ 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) +#define WPA_BSS_MASK_WIFI_DISPLAY BIT(16) + + /* wpa_supplicant/hostapd control interface access */ /** @@ -223,9 +293,25 @@ int wpa_ctrl_pending(struct wpa_ctrl *ctrl); */ int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl); +char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl); + +#ifdef ANDROID +/** + * wpa_ctrl_cleanup() - Delete any local UNIX domain socket files that + * may be left over from clients that were previously connected to + * wpa_supplicant. This keeps these files from being orphaned in the + * event of crashes that prevented them from being removed as part + * of the normal orderly shutdown. + */ +void wpa_ctrl_cleanup(void); +#endif /* ANDROID */ + #ifdef CONFIG_CTRL_IFACE_UDP +/* Port range for multiple wpa_supplicant instances and multiple VIFs */ #define WPA_CTRL_IFACE_PORT 9877 +#define WPA_CTRL_IFACE_PORT_LIMIT 50 /* decremented from start */ #define WPA_GLOBAL_CTRL_IFACE_PORT 9878 +#define WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT 20 /* incremented from start */ #endif /* CONFIG_CTRL_IFACE_UDP */ diff --git a/src/crypto/Makefile b/src/crypto/Makefile index 69aa16ac66b0c..a605a65cd821e 100644 --- a/src/crypto/Makefile +++ b/src/crypto/Makefile @@ -12,12 +12,15 @@ include ../lib.rules CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER #CFLAGS += -DALL_DH_GROUPS +CFLAGS += -DCONFIG_SHA256 LIB_OBJS= \ aes-cbc.o \ + aes-ccm.o \ aes-ctr.o \ aes-eax.o \ aes-encblock.o \ + aes-gcm.o \ aes-internal.o \ aes-internal-dec.o \ aes-internal-enc.o \ @@ -30,16 +33,18 @@ LIB_OBJS= \ md4-internal.o \ md5.o \ md5-internal.o \ - md5-non-fips.o \ milenage.o \ ms_funcs.o \ rc4.o \ sha1.o \ sha1-internal.o \ sha1-pbkdf2.o \ + sha1-prf.o \ sha1-tlsprf.o \ sha1-tprf.o \ sha256.o \ + sha256-prf.o \ + sha256-tlsprf.o \ sha256-internal.o LIB_OBJS += crypto_internal.o @@ -48,6 +53,7 @@ LIB_OBJS += crypto_internal-modexp.o LIB_OBJS += crypto_internal-rsa.o LIB_OBJS += tls_internal.o LIB_OBJS += fips_prf_internal.o +LIB_OBJS += random.o libcrypto.a: $(LIB_OBJS) diff --git a/src/crypto/aes-cbc.c b/src/crypto/aes-cbc.c index bd74769905e14..2833cfcc840d9 100644 --- a/src/crypto/aes-cbc.c +++ b/src/crypto/aes-cbc.c @@ -3,14 +3,8 @@ * * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/crypto/aes-ccm.c b/src/crypto/aes-ccm.c new file mode 100644 index 0000000000000..d14670db8d65f --- /dev/null +++ b/src/crypto/aes-ccm.c @@ -0,0 +1,212 @@ +/* + * Counter with CBC-MAC (CCM) with AES + * + * Copyright (c) 2010-2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "aes.h" +#include "aes_wrap.h" + + +static void xor_aes_block(u8 *dst, const u8 *src) +{ + u32 *d = (u32 *) dst; + u32 *s = (u32 *) src; + *d++ ^= *s++; + *d++ ^= *s++; + *d++ ^= *s++; + *d++ ^= *s++; +} + + +static void aes_ccm_auth_start(void *aes, size_t M, size_t L, const u8 *nonce, + const u8 *aad, size_t aad_len, size_t plain_len, + u8 *x) +{ + u8 aad_buf[2 * AES_BLOCK_SIZE]; + u8 b[AES_BLOCK_SIZE]; + + /* Authentication */ + /* B_0: Flags | Nonce N | l(m) */ + b[0] = aad_len ? 0x40 : 0 /* Adata */; + b[0] |= (((M - 2) / 2) /* M' */ << 3); + b[0] |= (L - 1) /* L' */; + os_memcpy(&b[1], nonce, 15 - L); + WPA_PUT_BE16(&b[AES_BLOCK_SIZE - L], plain_len); + + wpa_hexdump_key(MSG_EXCESSIVE, "CCM B_0", b, AES_BLOCK_SIZE); + aes_encrypt(aes, b, x); /* X_1 = E(K, B_0) */ + + if (!aad_len) + return; + + WPA_PUT_BE16(aad_buf, aad_len); + os_memcpy(aad_buf + 2, aad, aad_len); + os_memset(aad_buf + 2 + aad_len, 0, sizeof(aad_buf) - 2 - aad_len); + + xor_aes_block(aad_buf, x); + aes_encrypt(aes, aad_buf, x); /* X_2 = E(K, X_1 XOR B_1) */ + + if (aad_len > AES_BLOCK_SIZE - 2) { + xor_aes_block(&aad_buf[AES_BLOCK_SIZE], x); + /* X_3 = E(K, X_2 XOR B_2) */ + aes_encrypt(aes, &aad_buf[AES_BLOCK_SIZE], x); + } +} + + +static void aes_ccm_auth(void *aes, const u8 *data, size_t len, u8 *x) +{ + size_t last = len % AES_BLOCK_SIZE; + size_t i; + + for (i = 0; i < len / AES_BLOCK_SIZE; i++) { + /* X_i+1 = E(K, X_i XOR B_i) */ + xor_aes_block(x, data); + data += AES_BLOCK_SIZE; + aes_encrypt(aes, x, x); + } + if (last) { + /* XOR zero-padded last block */ + for (i = 0; i < last; i++) + x[i] ^= *data++; + aes_encrypt(aes, x, x); + } +} + + +static void aes_ccm_encr_start(size_t L, const u8 *nonce, u8 *a) +{ + /* A_i = Flags | Nonce N | Counter i */ + a[0] = L - 1; /* Flags = L' */ + os_memcpy(&a[1], nonce, 15 - L); +} + + +static void aes_ccm_encr(void *aes, size_t L, const u8 *in, size_t len, u8 *out, + u8 *a) +{ + size_t last = len % AES_BLOCK_SIZE; + size_t i; + + /* crypt = msg XOR (S_1 | S_2 | ... | S_n) */ + for (i = 1; i <= len / AES_BLOCK_SIZE; i++) { + WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], i); + /* S_i = E(K, A_i) */ + aes_encrypt(aes, a, out); + xor_aes_block(out, in); + out += AES_BLOCK_SIZE; + in += AES_BLOCK_SIZE; + } + if (last) { + WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], i); + aes_encrypt(aes, a, out); + /* XOR zero-padded last block */ + for (i = 0; i < last; i++) + *out++ ^= *in++; + } +} + + +static void aes_ccm_encr_auth(void *aes, size_t M, u8 *x, u8 *a, u8 *auth) +{ + size_t i; + u8 tmp[AES_BLOCK_SIZE]; + + wpa_hexdump_key(MSG_EXCESSIVE, "CCM T", x, M); + /* U = T XOR S_0; S_0 = E(K, A_0) */ + WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], 0); + aes_encrypt(aes, a, tmp); + for (i = 0; i < M; i++) + auth[i] = x[i] ^ tmp[i]; + wpa_hexdump_key(MSG_EXCESSIVE, "CCM U", auth, M); +} + + +static void aes_ccm_decr_auth(void *aes, size_t M, u8 *a, const u8 *auth, u8 *t) +{ + size_t i; + u8 tmp[AES_BLOCK_SIZE]; + + wpa_hexdump_key(MSG_EXCESSIVE, "CCM U", auth, M); + /* U = T XOR S_0; S_0 = E(K, A_0) */ + WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], 0); + aes_encrypt(aes, a, tmp); + for (i = 0; i < M; i++) + t[i] = auth[i] ^ tmp[i]; + wpa_hexdump_key(MSG_EXCESSIVE, "CCM T", t, M); +} + + +/* AES-CCM with fixed L=2 and aad_len <= 30 assumption */ +int aes_ccm_ae(const u8 *key, size_t key_len, const u8 *nonce, + size_t M, const u8 *plain, size_t plain_len, + const u8 *aad, size_t aad_len, u8 *crypt, u8 *auth) +{ + const size_t L = 2; + void *aes; + u8 x[AES_BLOCK_SIZE], a[AES_BLOCK_SIZE]; + + if (aad_len > 30 || M > AES_BLOCK_SIZE) + return -1; + + aes = aes_encrypt_init(key, key_len); + if (aes == NULL) + return -1; + + aes_ccm_auth_start(aes, M, L, nonce, aad, aad_len, plain_len, x); + aes_ccm_auth(aes, plain, plain_len, x); + + /* Encryption */ + aes_ccm_encr_start(L, nonce, a); + aes_ccm_encr(aes, L, plain, plain_len, crypt, a); + aes_ccm_encr_auth(aes, M, x, a, auth); + + aes_encrypt_deinit(aes); + + return 0; +} + + +/* AES-CCM with fixed L=2 and aad_len <= 30 assumption */ +int aes_ccm_ad(const u8 *key, size_t key_len, const u8 *nonce, + size_t M, const u8 *crypt, size_t crypt_len, + const u8 *aad, size_t aad_len, const u8 *auth, u8 *plain) +{ + const size_t L = 2; + void *aes; + u8 x[AES_BLOCK_SIZE], a[AES_BLOCK_SIZE]; + u8 t[AES_BLOCK_SIZE]; + + if (aad_len > 30 || M > AES_BLOCK_SIZE) + return -1; + + aes = aes_encrypt_init(key, key_len); + if (aes == NULL) + return -1; + + /* Decryption */ + aes_ccm_encr_start(L, nonce, a); + aes_ccm_decr_auth(aes, M, a, auth, t); + + /* plaintext = msg XOR (S_1 | S_2 | ... | S_n) */ + aes_ccm_encr(aes, L, crypt, crypt_len, plain, a); + + aes_ccm_auth_start(aes, M, L, nonce, aad, aad_len, crypt_len, x); + aes_ccm_auth(aes, plain, crypt_len, x); + + aes_encrypt_deinit(aes); + + if (os_memcmp(x, t, M) != 0) { + wpa_printf(MSG_EXCESSIVE, "CCM: Auth mismatch"); + return -1; + } + + return 0; +} diff --git a/src/crypto/aes-ctr.c b/src/crypto/aes-ctr.c index 468f877411997..d4d874daacd01 100644 --- a/src/crypto/aes-ctr.c +++ b/src/crypto/aes-ctr.c @@ -3,14 +3,8 @@ * * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/crypto/aes-eax.c b/src/crypto/aes-eax.c index d5c397151d2e2..21941c66de5d3 100644 --- a/src/crypto/aes-eax.c +++ b/src/crypto/aes-eax.c @@ -3,14 +3,8 @@ * * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/crypto/aes-encblock.c b/src/crypto/aes-encblock.c index 8f35caa221e25..a5216211ddd2b 100644 --- a/src/crypto/aes-encblock.c +++ b/src/crypto/aes-encblock.c @@ -3,14 +3,8 @@ * * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/crypto/aes-gcm.c b/src/crypto/aes-gcm.c new file mode 100644 index 0000000000000..3d91c71de2dd9 --- /dev/null +++ b/src/crypto/aes-gcm.c @@ -0,0 +1,327 @@ +/* + * Galois/Counter Mode (GCM) and GMAC with AES + * + * 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 "common.h" +#include "aes.h" +#include "aes_wrap.h" + +static void inc32(u8 *block) +{ + u32 val; + val = WPA_GET_BE32(block + AES_BLOCK_SIZE - 4); + val++; + WPA_PUT_BE32(block + AES_BLOCK_SIZE - 4, val); +} + + +static void xor_block(u8 *dst, const u8 *src) +{ + u32 *d = (u32 *) dst; + u32 *s = (u32 *) src; + *d++ ^= *s++; + *d++ ^= *s++; + *d++ ^= *s++; + *d++ ^= *s++; +} + + +static void shift_right_block(u8 *v) +{ + u32 val; + + val = WPA_GET_BE32(v + 12); + val >>= 1; + if (v[11] & 0x01) + val |= 0x80000000; + WPA_PUT_BE32(v + 12, val); + + val = WPA_GET_BE32(v + 8); + val >>= 1; + if (v[7] & 0x01) + val |= 0x80000000; + WPA_PUT_BE32(v + 8, val); + + val = WPA_GET_BE32(v + 4); + val >>= 1; + if (v[3] & 0x01) + val |= 0x80000000; + WPA_PUT_BE32(v + 4, val); + + val = WPA_GET_BE32(v); + val >>= 1; + WPA_PUT_BE32(v, val); +} + + +/* Multiplication in GF(2^128) */ +static void gf_mult(const u8 *x, const u8 *y, u8 *z) +{ + u8 v[16]; + int i, j; + + os_memset(z, 0, 16); /* Z_0 = 0^128 */ + os_memcpy(v, y, 16); /* V_0 = Y */ + + for (i = 0; i < 16; i++) { + for (j = 0; j < 8; j++) { + if (x[i] & BIT(7 - j)) { + /* Z_(i + 1) = Z_i XOR V_i */ + xor_block(z, v); + } else { + /* Z_(i + 1) = Z_i */ + } + + if (v[15] & 0x01) { + /* V_(i + 1) = (V_i >> 1) XOR R */ + shift_right_block(v); + /* R = 11100001 || 0^120 */ + v[0] ^= 0xe1; + } else { + /* V_(i + 1) = V_i >> 1 */ + shift_right_block(v); + } + } + } +} + + +static void ghash_start(u8 *y) +{ + /* Y_0 = 0^128 */ + os_memset(y, 0, 16); +} + + +static void ghash(const u8 *h, const u8 *x, size_t xlen, u8 *y) +{ + size_t m, i; + const u8 *xpos = x; + u8 tmp[16]; + + m = xlen / 16; + + for (i = 0; i < m; i++) { + /* Y_i = (Y^(i-1) XOR X_i) dot H */ + xor_block(y, xpos); + xpos += 16; + + /* dot operation: + * multiplication operation for binary Galois (finite) field of + * 2^128 elements */ + gf_mult(y, h, tmp); + os_memcpy(y, tmp, 16); + } + + if (x + xlen > xpos) { + /* Add zero padded last block */ + size_t last = x + xlen - xpos; + os_memcpy(tmp, xpos, last); + os_memset(tmp + last, 0, sizeof(tmp) - last); + + /* Y_i = (Y^(i-1) XOR X_i) dot H */ + xor_block(y, tmp); + + /* dot operation: + * multiplication operation for binary Galois (finite) field of + * 2^128 elements */ + gf_mult(y, h, tmp); + os_memcpy(y, tmp, 16); + } + + /* Return Y_m */ +} + + +static void aes_gctr(void *aes, const u8 *icb, const u8 *x, size_t xlen, u8 *y) +{ + size_t i, n, last; + u8 cb[AES_BLOCK_SIZE], tmp[AES_BLOCK_SIZE]; + const u8 *xpos = x; + u8 *ypos = y; + + if (xlen == 0) + return; + + n = xlen / 16; + + os_memcpy(cb, icb, AES_BLOCK_SIZE); + /* Full blocks */ + for (i = 0; i < n; i++) { + aes_encrypt(aes, cb, ypos); + xor_block(ypos, xpos); + xpos += AES_BLOCK_SIZE; + ypos += AES_BLOCK_SIZE; + inc32(cb); + } + + last = x + xlen - xpos; + if (last) { + /* Last, partial block */ + aes_encrypt(aes, cb, tmp); + for (i = 0; i < last; i++) + *ypos++ = *xpos++ ^ tmp[i]; + } +} + + +static void * aes_gcm_init_hash_subkey(const u8 *key, size_t key_len, u8 *H) +{ + void *aes; + + aes = aes_encrypt_init(key, key_len); + if (aes == NULL) + return NULL; + + /* Generate hash subkey H = AES_K(0^128) */ + os_memset(H, 0, AES_BLOCK_SIZE); + aes_encrypt(aes, H, H); + wpa_hexdump_key(MSG_EXCESSIVE, "Hash subkey H for GHASH", + H, AES_BLOCK_SIZE); + return aes; +} + + +static void aes_gcm_prepare_j0(const u8 *iv, size_t iv_len, const u8 *H, u8 *J0) +{ + u8 len_buf[16]; + + if (iv_len == 12) { + /* Prepare block J_0 = IV || 0^31 || 1 [len(IV) = 96] */ + os_memcpy(J0, iv, iv_len); + os_memset(J0 + iv_len, 0, AES_BLOCK_SIZE - iv_len); + J0[AES_BLOCK_SIZE - 1] = 0x01; + } else { + /* + * s = 128 * ceil(len(IV)/128) - len(IV) + * J_0 = GHASH_H(IV || 0^(s+64) || [len(IV)]_64) + */ + ghash_start(J0); + ghash(H, iv, iv_len, J0); + WPA_PUT_BE64(len_buf, 0); + WPA_PUT_BE64(len_buf + 8, iv_len * 8); + ghash(H, len_buf, sizeof(len_buf), J0); + } +} + + +static void aes_gcm_gctr(void *aes, const u8 *J0, const u8 *in, size_t len, + u8 *out) +{ + u8 J0inc[AES_BLOCK_SIZE]; + + if (len == 0) + return; + + os_memcpy(J0inc, J0, AES_BLOCK_SIZE); + inc32(J0inc); + aes_gctr(aes, J0inc, in, len, out); +} + + +static void aes_gcm_ghash(const u8 *H, const u8 *aad, size_t aad_len, + const u8 *crypt, size_t crypt_len, u8 *S) +{ + u8 len_buf[16]; + + /* + * u = 128 * ceil[len(C)/128] - len(C) + * v = 128 * ceil[len(A)/128] - len(A) + * S = GHASH_H(A || 0^v || C || 0^u || [len(A)]64 || [len(C)]64) + * (i.e., zero padded to block size A || C and lengths of each in bits) + */ + ghash_start(S); + ghash(H, aad, aad_len, S); + ghash(H, crypt, crypt_len, S); + WPA_PUT_BE64(len_buf, aad_len * 8); + WPA_PUT_BE64(len_buf + 8, crypt_len * 8); + ghash(H, len_buf, sizeof(len_buf), S); + + wpa_hexdump_key(MSG_EXCESSIVE, "S = GHASH_H(...)", S, 16); +} + + +/** + * aes_gcm_ae - GCM-AE_K(IV, P, A) + */ +int aes_gcm_ae(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len, + const u8 *plain, size_t plain_len, + const u8 *aad, size_t aad_len, u8 *crypt, u8 *tag) +{ + u8 H[AES_BLOCK_SIZE]; + u8 J0[AES_BLOCK_SIZE]; + u8 S[16]; + void *aes; + + aes = aes_gcm_init_hash_subkey(key, key_len, H); + if (aes == NULL) + return -1; + + aes_gcm_prepare_j0(iv, iv_len, H, J0); + + /* C = GCTR_K(inc_32(J_0), P) */ + aes_gcm_gctr(aes, J0, plain, plain_len, crypt); + + aes_gcm_ghash(H, aad, aad_len, crypt, plain_len, S); + + /* T = MSB_t(GCTR_K(J_0, S)) */ + aes_gctr(aes, J0, S, sizeof(S), tag); + + /* Return (C, T) */ + + aes_encrypt_deinit(aes); + + return 0; +} + + +/** + * aes_gcm_ad - GCM-AD_K(IV, C, A, T) + */ +int aes_gcm_ad(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len, + const u8 *crypt, size_t crypt_len, + const u8 *aad, size_t aad_len, const u8 *tag, u8 *plain) +{ + u8 H[AES_BLOCK_SIZE]; + u8 J0[AES_BLOCK_SIZE]; + u8 S[16], T[16]; + void *aes; + + aes = aes_gcm_init_hash_subkey(key, key_len, H); + if (aes == NULL) + return -1; + + aes_gcm_prepare_j0(iv, iv_len, H, J0); + + /* P = GCTR_K(inc_32(J_0), C) */ + aes_gcm_gctr(aes, J0, crypt, crypt_len, plain); + + aes_gcm_ghash(H, aad, aad_len, crypt, crypt_len, S); + + /* T' = MSB_t(GCTR_K(J_0, S)) */ + aes_gctr(aes, J0, S, sizeof(S), T); + + aes_encrypt_deinit(aes); + + if (os_memcmp(tag, T, 16) != 0) { + wpa_printf(MSG_EXCESSIVE, "GCM: Tag mismatch"); + return -1; + } + + return 0; +} + + +int aes_gmac(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len, + const u8 *aad, size_t aad_len, u8 *tag) +{ + return aes_gcm_ae(key, key_len, iv, iv_len, NULL, 0, aad, aad_len, NULL, + tag); +} diff --git a/src/crypto/aes-internal-dec.c b/src/crypto/aes-internal-dec.c index 2d32c03fbf4cb..720c7036e4e7b 100644 --- a/src/crypto/aes-internal-dec.c +++ b/src/crypto/aes-internal-dec.c @@ -2,23 +2,16 @@ * AES (Rijndael) cipher - decrypt * * Modifications to public domain implementation: - * - support only 128-bit keys * - cleanup * - use C pre-processor to make it easier to change S table access * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at * cost of reduced throughput (quite small difference on Pentium 4, * 10-25% when using -O1 or -O2 optimization) * - * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-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 - * published 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" @@ -32,13 +25,15 @@ * * @return the number of rounds for the given cipher key size. */ -void rijndaelKeySetupDec(u32 rk[/*44*/], const u8 cipherKey[]) +static int rijndaelKeySetupDec(u32 rk[], const u8 cipherKey[], int keyBits) { - int Nr = 10, i, j; + int Nr, i, j; u32 temp; /* expand the cipher key: */ - rijndaelKeySetupEnc(rk, cipherKey); + Nr = rijndaelKeySetupEnc(rk, cipherKey, keyBits); + if (Nr < 0) + return Nr; /* invert the order of the round keys: */ for (i = 0, j = 4*Nr; i < j; i += 4, j -= 4) { temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp; @@ -57,24 +52,30 @@ void rijndaelKeySetupDec(u32 rk[/*44*/], const u8 cipherKey[]) TD3_(TE4((rk[j] ) & 0xff)); } } + + return Nr; } void * aes_decrypt_init(const u8 *key, size_t len) { u32 *rk; - if (len != 16) - return NULL; + int res; rk = os_malloc(AES_PRIV_SIZE); if (rk == NULL) return NULL; - rijndaelKeySetupDec(rk, key); + res = rijndaelKeySetupDec(rk, key, len * 8); + if (res < 0) { + os_free(rk); + return NULL; + } + rk[AES_PRIV_NR_POS] = res; return rk; } -static void rijndaelDecrypt(const u32 rk[/*44*/], const u8 ct[16], u8 pt[16]) +static void rijndaelDecrypt(const u32 rk[/*44*/], int Nr, const u8 ct[16], + u8 pt[16]) { u32 s0, s1, s2, s3, t0, t1, t2, t3; - const int Nr = 10; #ifndef FULL_UNROLL int r; #endif /* ?FULL_UNROLL */ @@ -105,6 +106,14 @@ d##3 = TD0(s##3) ^ TD1(s##2) ^ TD2(s##1) ^ TD3(s##0) ^ rk[4 * i + 3] ROUND(7,t,s); ROUND(8,s,t); ROUND(9,t,s); + if (Nr > 10) { + ROUND(10,s,t); + ROUND(11,t,s); + if (Nr > 12) { + ROUND(12,s,t); + ROUND(13,t,s); + } + } rk += Nr << 2; @@ -140,7 +149,8 @@ d##3 = TD0(s##3) ^ TD1(s##2) ^ TD2(s##1) ^ TD3(s##0) ^ rk[4 * i + 3] void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) { - rijndaelDecrypt(ctx, crypt, plain); + u32 *rk = ctx; + rijndaelDecrypt(ctx, rk[AES_PRIV_NR_POS], crypt, plain); } diff --git a/src/crypto/aes-internal-enc.c b/src/crypto/aes-internal-enc.c index 2f198263bb84a..f3c61b8508f3c 100644 --- a/src/crypto/aes-internal-enc.c +++ b/src/crypto/aes-internal-enc.c @@ -2,23 +2,16 @@ * AES (Rijndael) cipher - encrypt * * Modifications to public domain implementation: - * - support only 128-bit keys * - cleanup * - use C pre-processor to make it easier to change S table access * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at * cost of reduced throughput (quite small difference on Pentium 4, * 10-25% when using -O1 or -O2 optimization) * - * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-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 - * published 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" @@ -27,10 +20,9 @@ #include "crypto.h" #include "aes_i.h" -void rijndaelEncrypt(const u32 rk[/*44*/], const u8 pt[16], u8 ct[16]) +static void rijndaelEncrypt(const u32 rk[], int Nr, const u8 pt[16], u8 ct[16]) { u32 s0, s1, s2, s3, t0, t1, t2, t3; - const int Nr = 10; #ifndef FULL_UNROLL int r; #endif /* ?FULL_UNROLL */ @@ -61,6 +53,14 @@ d##3 = TE0(s##3) ^ TE1(s##0) ^ TE2(s##1) ^ TE3(s##2) ^ rk[4 * i + 3] ROUND(7,t,s); ROUND(8,s,t); ROUND(9,t,s); + if (Nr > 10) { + ROUND(10,s,t); + ROUND(11,t,s); + if (Nr > 12) { + ROUND(12,s,t); + ROUND(13,t,s); + } + } rk += Nr << 2; @@ -98,19 +98,24 @@ d##3 = TE0(s##3) ^ TE1(s##0) ^ TE2(s##1) ^ TE3(s##2) ^ rk[4 * i + 3] void * aes_encrypt_init(const u8 *key, size_t len) { u32 *rk; - if (len != 16) - return NULL; + int res; rk = os_malloc(AES_PRIV_SIZE); if (rk == NULL) return NULL; - rijndaelKeySetupEnc(rk, key); + res = rijndaelKeySetupEnc(rk, key, len * 8); + if (res < 0) { + os_free(rk); + return NULL; + } + rk[AES_PRIV_NR_POS] = res; return rk; } void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) { - rijndaelEncrypt(ctx, plain, crypt); + u32 *rk = ctx; + rijndaelEncrypt(ctx, rk[AES_PRIV_NR_POS], plain, crypt); } diff --git a/src/crypto/aes-internal.c b/src/crypto/aes-internal.c index 4161220220665..bd4535d2096e2 100644 --- a/src/crypto/aes-internal.c +++ b/src/crypto/aes-internal.c @@ -2,23 +2,16 @@ * AES (Rijndael) cipher * * Modifications to public domain implementation: - * - support only 128-bit keys * - cleanup * - use C pre-processor to make it easier to change S table access * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at * cost of reduced throughput (quite small difference on Pentium 4, * 10-25% when using -O1 or -O2 optimization) * - * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-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 - * published 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" @@ -783,7 +776,7 @@ const u8 rcons[] = { * * @return the number of rounds for the given cipher key size. */ -void rijndaelKeySetupEnc(u32 rk[/*44*/], const u8 cipherKey[]) +int rijndaelKeySetupEnc(u32 rk[], const u8 cipherKey[], int keyBits) { int i; u32 temp; @@ -792,14 +785,61 @@ void rijndaelKeySetupEnc(u32 rk[/*44*/], const u8 cipherKey[]) rk[1] = GETU32(cipherKey + 4); rk[2] = GETU32(cipherKey + 8); rk[3] = GETU32(cipherKey + 12); - for (i = 0; i < 10; i++) { - temp = rk[3]; - rk[4] = rk[0] ^ - TE421(temp) ^ TE432(temp) ^ TE443(temp) ^ TE414(temp) ^ - RCON(i); - rk[5] = rk[1] ^ rk[4]; - rk[6] = rk[2] ^ rk[5]; - rk[7] = rk[3] ^ rk[6]; - rk += 4; + + if (keyBits == 128) { + for (i = 0; i < 10; i++) { + temp = rk[3]; + rk[4] = rk[0] ^ TE421(temp) ^ TE432(temp) ^ + TE443(temp) ^ TE414(temp) ^ RCON(i); + rk[5] = rk[1] ^ rk[4]; + rk[6] = rk[2] ^ rk[5]; + rk[7] = rk[3] ^ rk[6]; + rk += 4; + } + return 10; + } + + rk[4] = GETU32(cipherKey + 16); + rk[5] = GETU32(cipherKey + 20); + + if (keyBits == 192) { + for (i = 0; i < 8; i++) { + temp = rk[5]; + rk[6] = rk[0] ^ TE421(temp) ^ TE432(temp) ^ + TE443(temp) ^ TE414(temp) ^ RCON(i); + rk[7] = rk[1] ^ rk[6]; + rk[8] = rk[2] ^ rk[7]; + rk[9] = rk[3] ^ rk[8]; + if (i == 7) + return 12; + rk[10] = rk[4] ^ rk[9]; + rk[11] = rk[5] ^ rk[10]; + rk += 6; + } } + + rk[6] = GETU32(cipherKey + 24); + rk[7] = GETU32(cipherKey + 28); + + if (keyBits == 256) { + for (i = 0; i < 7; i++) { + temp = rk[7]; + rk[8] = rk[0] ^ TE421(temp) ^ TE432(temp) ^ + TE443(temp) ^ TE414(temp) ^ RCON(i); + rk[9] = rk[1] ^ rk[8]; + rk[10] = rk[2] ^ rk[9]; + rk[11] = rk[3] ^ rk[10]; + if (i == 6) + return 14; + temp = rk[11]; + rk[12] = rk[4] ^ TE411(temp) ^ TE422(temp) ^ + TE433(temp) ^ TE444(temp); + rk[13] = rk[5] ^ rk[12]; + rk[14] = rk[6] ^ rk[13]; + rk[15] = rk[7] ^ rk[14]; + rk += 8; + } + } + + return -1; } diff --git a/src/crypto/aes-omac1.c b/src/crypto/aes-omac1.c index f77529617a31c..27895eb007efe 100644 --- a/src/crypto/aes-omac1.c +++ b/src/crypto/aes-omac1.c @@ -3,14 +3,8 @@ * * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/crypto/aes-unwrap.c b/src/crypto/aes-unwrap.c index f233ffa4c04cd..9dd51602f3582 100644 --- a/src/crypto/aes-unwrap.c +++ b/src/crypto/aes-unwrap.c @@ -3,14 +3,8 @@ * * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/crypto/aes-wrap.c b/src/crypto/aes-wrap.c index 28d0c89e88f01..89d6f94bf7610 100644 --- a/src/crypto/aes-wrap.c +++ b/src/crypto/aes-wrap.c @@ -3,14 +3,8 @@ * * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/crypto/aes.h b/src/crypto/aes.h index ba384a9dae374..2de59e04efa5f 100644 --- a/src/crypto/aes.h +++ b/src/crypto/aes.h @@ -2,14 +2,8 @@ * AES functions * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef AES_H diff --git a/src/crypto/aes_i.h b/src/crypto/aes_i.h index 6b40bc7816516..54375cf355837 100644 --- a/src/crypto/aes_i.h +++ b/src/crypto/aes_i.h @@ -1,15 +1,9 @@ /* * AES (Rijndael) cipher - * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-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 - * published 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. */ #ifndef AES_I_H @@ -50,6 +44,10 @@ extern const u8 rcons[10]; #define TE432(i) (Te4[((i) >> 8) & 0xff] & 0x00ff0000) #define TE443(i) (Te4[(i) & 0xff] & 0x0000ff00) #define TE414(i) (Te4[((i) >> 24) & 0xff] & 0x000000ff) +#define TE411(i) (Te4[((i) >> 24) & 0xff] & 0xff000000) +#define TE422(i) (Te4[((i) >> 16) & 0xff] & 0x00ff0000) +#define TE433(i) (Te4[((i) >> 8) & 0xff] & 0x0000ff00) +#define TE444(i) (Te4[(i) & 0xff] & 0x000000ff) #define TE4(i) (Te4[(i)] & 0x000000ff) #define TD0(i) Td0[((i) >> 24) & 0xff] @@ -86,6 +84,10 @@ static inline u32 rotr(u32 val, int bits) #define TE432(i) (Te0[((i) >> 8) & 0xff] & 0x00ff0000) #define TE443(i) (Te0[(i) & 0xff] & 0x0000ff00) #define TE414(i) ((Te0[((i) >> 24) & 0xff] >> 8) & 0x000000ff) +#define TE411(i) ((Te0[((i) >> 24) & 0xff] << 8) & 0xff000000) +#define TE422(i) (Te0[((i) >> 16) & 0xff] & 0x00ff0000) +#define TE433(i) (Te0[((i) >> 8) & 0xff] & 0x0000ff00) +#define TE444(i) ((Te0[(i) & 0xff] >> 8) & 0x000000ff) #define TE4(i) ((Te0[(i)] >> 8) & 0x000000ff) #define TD0(i) Td0[((i) >> 24) & 0xff] @@ -115,8 +117,9 @@ static inline u32 rotr(u32 val, int bits) (ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); } #endif -#define AES_PRIV_SIZE (4 * 44) +#define AES_PRIV_SIZE (4 * 4 * 15 + 4) +#define AES_PRIV_NR_POS (4 * 15) -void rijndaelKeySetupEnc(u32 rk[/*44*/], const u8 cipherKey[]); +int rijndaelKeySetupEnc(u32 rk[], const u8 cipherKey[], int keyBits); #endif /* AES_I_H */ diff --git a/src/crypto/aes_wrap.h b/src/crypto/aes_wrap.h index 4b1c7b083b334..0433c0434eed3 100644 --- a/src/crypto/aes_wrap.h +++ b/src/crypto/aes_wrap.h @@ -6,17 +6,13 @@ * - AES-128 CTR mode encryption * - AES-128 EAX mode encryption/decryption * - AES-128 CBC + * - AES-GCM + * - AES-CCM * - * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-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 - * published 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. */ #ifndef AES_WRAP_H @@ -44,5 +40,25 @@ int __must_check aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len); int __must_check aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len); +int __must_check aes_gcm_ae(const u8 *key, size_t key_len, + const u8 *iv, size_t iv_len, + const u8 *plain, size_t plain_len, + const u8 *aad, size_t aad_len, + u8 *crypt, u8 *tag); +int __must_check aes_gcm_ad(const u8 *key, size_t key_len, + const u8 *iv, size_t iv_len, + const u8 *crypt, size_t crypt_len, + const u8 *aad, size_t aad_len, const u8 *tag, + u8 *plain); +int __must_check aes_gmac(const u8 *key, size_t key_len, + const u8 *iv, size_t iv_len, + const u8 *aad, size_t aad_len, u8 *tag); +int __must_check aes_ccm_ae(const u8 *key, size_t key_len, const u8 *nonce, + size_t M, const u8 *plain, size_t plain_len, + const u8 *aad, size_t aad_len, u8 *crypt, u8 *auth); +int __must_check aes_ccm_ad(const u8 *key, size_t key_len, const u8 *nonce, + size_t M, const u8 *crypt, size_t crypt_len, + const u8 *aad, size_t aad_len, const u8 *auth, + u8 *plain); #endif /* AES_WRAP_H */ diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 587b5a95747fc..26b9acf2f4244 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -2,14 +2,8 @@ * WPA Supplicant / wrapper functions for crypto libraries * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file defines the cryptographic functions that need to be implemented * for wpa_supplicant and hostapd. When TLS is not used, internal @@ -47,21 +41,6 @@ int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); */ int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); -#ifdef CONFIG_FIPS -/** - * md5_vector_non_fips_allow - MD5 hash for data vector (non-FIPS use allowed) - * @num_elem: Number of elements in the data vector - * @addr: Pointers to the data areas - * @len: Lengths of the data blocks - * @mac: Buffer for the hash - * Returns: 0 on success, -1 on failure - */ -int md5_vector_non_fips_allow(size_t num_elem, const u8 *addr[], - const size_t *len, u8 *mac); -#else /* CONFIG_FIPS */ -#define md5_vector_non_fips_allow md5_vector -#endif /* CONFIG_FIPS */ - /** * sha1_vector - SHA-1 hash for data vector @@ -155,7 +134,8 @@ void aes_decrypt_deinit(void *ctx); enum crypto_hash_alg { CRYPTO_HASH_ALG_MD5, CRYPTO_HASH_ALG_SHA1, - CRYPTO_HASH_ALG_HMAC_MD5, CRYPTO_HASH_ALG_HMAC_SHA1 + CRYPTO_HASH_ALG_HMAC_MD5, CRYPTO_HASH_ALG_HMAC_SHA1, + CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256 }; struct crypto_hash; @@ -466,4 +446,15 @@ int __must_check crypto_mod_exp(const u8 *base, size_t base_len, int rc4_skip(const u8 *key, size_t keylen, size_t skip, u8 *data, size_t data_len); +/** + * crypto_get_random - Generate cryptographically strong pseudy-random bytes + * @buf: Buffer for data + * @len: Number of bytes to generate + * Returns: 0 on success, -1 on failure + * + * If the PRNG does not have enough entropy to ensure unpredictable byte + * sequence, this functions must return -1. + */ +int crypto_get_random(void *buf, size_t len); + #endif /* CRYPTO_H */ diff --git a/src/crypto/crypto_cryptoapi.c b/src/crypto/crypto_cryptoapi.c index 2a8d2001b1732..55a069b0d036e 100644 --- a/src/crypto/crypto_cryptoapi.c +++ b/src/crypto/crypto_cryptoapi.c @@ -2,14 +2,8 @@ * Crypto wrapper for Microsoft CryptoAPI * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/crypto/crypto_gnutls.c b/src/crypto/crypto_gnutls.c index 0998cca546721..0dfd54d22d475 100644 --- a/src/crypto/crypto_gnutls.c +++ b/src/crypto/crypto_gnutls.c @@ -2,14 +2,8 @@ * WPA Supplicant / wrapper functions for libgcrypt * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/crypto/crypto_internal-cipher.c b/src/crypto/crypto_internal-cipher.c index 75134f09a403f..ad0930a5a9d36 100644 --- a/src/crypto/crypto_internal-cipher.c +++ b/src/crypto/crypto_internal-cipher.c @@ -2,14 +2,8 @@ * Crypto wrapper for internal crypto implementation - Cipher wrappers * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -30,7 +24,6 @@ struct crypto_cipher { } rc4; struct { u8 cbc[32]; - size_t block_size; void *ctx_enc; void *ctx_dec; } aes; @@ -69,10 +62,6 @@ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, os_memcpy(ctx->u.rc4.key, key, key_len); break; case CRYPTO_CIPHER_ALG_AES: - if (key_len > sizeof(ctx->u.aes.cbc)) { - os_free(ctx); - return NULL; - } ctx->u.aes.ctx_enc = aes_encrypt_init(key, key_len); if (ctx->u.aes.ctx_enc == NULL) { os_free(ctx); @@ -84,8 +73,7 @@ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, os_free(ctx); return NULL; } - ctx->u.aes.block_size = key_len; - os_memcpy(ctx->u.aes.cbc, iv, ctx->u.aes.block_size); + os_memcpy(ctx->u.aes.cbc, iv, AES_BLOCK_SIZE); break; case CRYPTO_CIPHER_ALG_3DES: if (key_len != 24) { @@ -126,18 +114,17 @@ int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, ctx->u.rc4.used_bytes += len; break; case CRYPTO_CIPHER_ALG_AES: - if (len % ctx->u.aes.block_size) + if (len % AES_BLOCK_SIZE) return -1; - blocks = len / ctx->u.aes.block_size; + blocks = len / AES_BLOCK_SIZE; for (i = 0; i < blocks; i++) { - for (j = 0; j < ctx->u.aes.block_size; j++) + for (j = 0; j < AES_BLOCK_SIZE; j++) ctx->u.aes.cbc[j] ^= plain[j]; aes_encrypt(ctx->u.aes.ctx_enc, ctx->u.aes.cbc, ctx->u.aes.cbc); - os_memcpy(crypt, ctx->u.aes.cbc, - ctx->u.aes.block_size); - plain += ctx->u.aes.block_size; - crypt += ctx->u.aes.block_size; + os_memcpy(crypt, ctx->u.aes.cbc, AES_BLOCK_SIZE); + plain += AES_BLOCK_SIZE; + crypt += AES_BLOCK_SIZE; } break; case CRYPTO_CIPHER_ALG_3DES: @@ -191,17 +178,17 @@ int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, ctx->u.rc4.used_bytes += len; break; case CRYPTO_CIPHER_ALG_AES: - if (len % ctx->u.aes.block_size) + if (len % AES_BLOCK_SIZE) return -1; - blocks = len / ctx->u.aes.block_size; + blocks = len / AES_BLOCK_SIZE; for (i = 0; i < blocks; i++) { - os_memcpy(tmp, crypt, ctx->u.aes.block_size); + os_memcpy(tmp, crypt, AES_BLOCK_SIZE); aes_decrypt(ctx->u.aes.ctx_dec, crypt, plain); - for (j = 0; j < ctx->u.aes.block_size; j++) + for (j = 0; j < AES_BLOCK_SIZE; j++) plain[j] ^= ctx->u.aes.cbc[j]; - os_memcpy(ctx->u.aes.cbc, tmp, ctx->u.aes.block_size); - plain += ctx->u.aes.block_size; - crypt += ctx->u.aes.block_size; + os_memcpy(ctx->u.aes.cbc, tmp, AES_BLOCK_SIZE); + plain += AES_BLOCK_SIZE; + crypt += AES_BLOCK_SIZE; } break; case CRYPTO_CIPHER_ALG_3DES: diff --git a/src/crypto/crypto_internal-modexp.c b/src/crypto/crypto_internal-modexp.c index 3124742e87cd4..9dcabb95bdd2a 100644 --- a/src/crypto/crypto_internal-modexp.c +++ b/src/crypto/crypto_internal-modexp.c @@ -2,14 +2,8 @@ * Crypto wrapper for internal crypto implementation - modexp * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/crypto/crypto_internal-rsa.c b/src/crypto/crypto_internal-rsa.c index 205042cf36311..54209fad3c01e 100644 --- a/src/crypto/crypto_internal-rsa.c +++ b/src/crypto/crypto_internal-rsa.c @@ -2,14 +2,8 @@ * Crypto wrapper for internal crypto implementation - RSA parts * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -17,7 +11,6 @@ #include "common.h" #include "crypto.h" #include "tls/rsa.h" -#include "tls/bignum.h" #include "tls/pkcs1.h" #include "tls/pkcs8.h" diff --git a/src/crypto/crypto_internal.c b/src/crypto/crypto_internal.c index 8fdba65802750..f3602dac346ea 100644 --- a/src/crypto/crypto_internal.c +++ b/src/crypto/crypto_internal.c @@ -1,21 +1,16 @@ /* * Crypto wrapper for internal crypto implementation - * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" #include "crypto.h" +#include "sha256_i.h" #include "sha1_i.h" #include "md5_i.h" @@ -24,6 +19,9 @@ struct crypto_hash { union { struct MD5Context md5; struct SHA1Context sha1; +#ifdef CONFIG_SHA256 + struct sha256_state sha256; +#endif /* CONFIG_SHA256 */ } u; u8 key[64]; size_t key_len; @@ -35,7 +33,7 @@ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, { struct crypto_hash *ctx; u8 k_pad[64]; - u8 tk[20]; + u8 tk[32]; size_t i; ctx = os_zalloc(sizeof(*ctx)); @@ -51,6 +49,11 @@ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, case CRYPTO_HASH_ALG_SHA1: SHA1Init(&ctx->u.sha1); break; +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_SHA256: + sha256_init(&ctx->u.sha256); + break; +#endif /* CONFIG_SHA256 */ case CRYPTO_HASH_ALG_HMAC_MD5: if (key_len > sizeof(k_pad)) { MD5Init(&ctx->u.md5); @@ -63,7 +66,8 @@ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, ctx->key_len = key_len; os_memcpy(k_pad, key, key_len); - os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len); + if (key_len < sizeof(k_pad)) + os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len); for (i = 0; i < sizeof(k_pad); i++) k_pad[i] ^= 0x36; MD5Init(&ctx->u.md5); @@ -81,12 +85,34 @@ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, ctx->key_len = key_len; os_memcpy(k_pad, key, key_len); - os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len); + if (key_len < sizeof(k_pad)) + os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len); for (i = 0; i < sizeof(k_pad); i++) k_pad[i] ^= 0x36; SHA1Init(&ctx->u.sha1); SHA1Update(&ctx->u.sha1, k_pad, sizeof(k_pad)); break; +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_HMAC_SHA256: + if (key_len > sizeof(k_pad)) { + sha256_init(&ctx->u.sha256); + sha256_process(&ctx->u.sha256, key, key_len); + sha256_done(&ctx->u.sha256, tk); + key = tk; + key_len = 32; + } + os_memcpy(ctx->key, key, key_len); + ctx->key_len = key_len; + + os_memcpy(k_pad, key, key_len); + if (key_len < sizeof(k_pad)) + os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len); + for (i = 0; i < sizeof(k_pad); i++) + k_pad[i] ^= 0x36; + sha256_init(&ctx->u.sha256); + sha256_process(&ctx->u.sha256, k_pad, sizeof(k_pad)); + break; +#endif /* CONFIG_SHA256 */ default: os_free(ctx); return NULL; @@ -110,6 +136,14 @@ void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) case CRYPTO_HASH_ALG_HMAC_SHA1: SHA1Update(&ctx->u.sha1, data, len); break; +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_SHA256: + case CRYPTO_HASH_ALG_HMAC_SHA256: + sha256_process(&ctx->u.sha256, data, len); + break; +#endif /* CONFIG_SHA256 */ + default: + break; } } @@ -146,6 +180,17 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) *len = 20; SHA1Final(mac, &ctx->u.sha1); break; +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_SHA256: + if (*len < 32) { + *len = 32; + os_free(ctx); + return -1; + } + *len = 32; + sha256_done(&ctx->u.sha256, mac); + break; +#endif /* CONFIG_SHA256 */ case CRYPTO_HASH_ALG_HMAC_MD5: if (*len < 16) { *len = 16; @@ -186,6 +231,31 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) SHA1Update(&ctx->u.sha1, mac, 20); SHA1Final(mac, &ctx->u.sha1); break; +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_HMAC_SHA256: + if (*len < 32) { + *len = 32; + os_free(ctx); + return -1; + } + *len = 32; + + sha256_done(&ctx->u.sha256, mac); + + os_memcpy(k_pad, ctx->key, ctx->key_len); + os_memset(k_pad + ctx->key_len, 0, + sizeof(k_pad) - ctx->key_len); + for (i = 0; i < sizeof(k_pad); i++) + k_pad[i] ^= 0x5c; + sha256_init(&ctx->u.sha256); + sha256_process(&ctx->u.sha256, k_pad, sizeof(k_pad)); + sha256_process(&ctx->u.sha256, mac, 32); + sha256_done(&ctx->u.sha256, mac); + break; +#endif /* CONFIG_SHA256 */ + default: + os_free(ctx); + return -1; } os_free(ctx); diff --git a/src/crypto/crypto_libtomcrypt.c b/src/crypto/crypto_libtomcrypt.c index 52b67a713d699..a55edd14e2d3c 100644 --- a/src/crypto/crypto_libtomcrypt.c +++ b/src/crypto/crypto_libtomcrypt.c @@ -2,14 +2,8 @@ * WPA Supplicant / Crypto wrapper for LibTomCrypt (for internal TLSv1) * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/crypto/crypto_none.c b/src/crypto/crypto_none.c index 9f43775a64622..011f3f35a0551 100644 --- a/src/crypto/crypto_none.c +++ b/src/crypto/crypto_none.c @@ -2,14 +2,8 @@ * WPA Supplicant / Empty template functions for crypto wrapper * Copyright (c) 2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/crypto/crypto_nss.c b/src/crypto/crypto_nss.c index fee4195ca4e3a..acd0a55281c0e 100644 --- a/src/crypto/crypto_nss.c +++ b/src/crypto/crypto_nss.c @@ -2,14 +2,8 @@ * Crypto wrapper functions for NSS * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c index 08c98aff4ba1f..711e312d0fb04 100644 --- a/src/crypto/crypto_openssl.c +++ b/src/crypto/crypto_openssl.c @@ -1,15 +1,9 @@ /* * 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 program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published 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" @@ -20,6 +14,11 @@ #include <openssl/bn.h> #include <openssl/evp.h> #include <openssl/dh.h> +#include <openssl/hmac.h> +#include <openssl/rand.h> +#ifdef CONFIG_OPENSSL_CMAC +#include <openssl/cmac.h> +#endif /* CONFIG_OPENSSL_CMAC */ #include "common.h" #include "wpabuf.h" @@ -74,21 +73,14 @@ static BIGNUM * get_group5_prime(void) #define NO_SHA256_WRAPPER #endif -static int openssl_digest_vector(const EVP_MD *type, int non_fips, - size_t num_elem, const u8 *addr[], - const size_t *len, u8 *mac) +static int openssl_digest_vector(const EVP_MD *type, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) { EVP_MD_CTX ctx; size_t i; unsigned int mac_len; EVP_MD_CTX_init(&ctx); -#ifdef CONFIG_FIPS -#ifdef OPENSSL_FIPS - if (non_fips) - EVP_MD_CTX_set_flags(&ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); -#endif /* OPENSSL_FIPS */ -#endif /* CONFIG_FIPS */ if (!EVP_DigestInit_ex(&ctx, type, NULL)) { wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestInit_ex failed: %s", ERR_error_string(ERR_get_error(), NULL)); @@ -114,7 +106,7 @@ static int openssl_digest_vector(const EVP_MD *type, int non_fips, int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - return openssl_digest_vector(EVP_md4(), 0, num_elem, addr, len, mac); + return openssl_digest_vector(EVP_md4(), num_elem, addr, len, mac); } @@ -178,22 +170,13 @@ out: int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - return openssl_digest_vector(EVP_md5(), 0, num_elem, addr, len, mac); -} - - -#ifdef CONFIG_FIPS -int md5_vector_non_fips_allow(size_t num_elem, const u8 *addr[], - const size_t *len, u8 *mac) -{ - return openssl_digest_vector(EVP_md5(), 1, num_elem, addr, len, mac); + return openssl_digest_vector(EVP_md5(), num_elem, addr, len, mac); } -#endif /* CONFIG_FIPS */ int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - return openssl_digest_vector(EVP_sha1(), 0, num_elem, addr, len, mac); + return openssl_digest_vector(EVP_sha1(), num_elem, addr, len, mac); } @@ -201,60 +184,124 @@ int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - return openssl_digest_vector(EVP_sha256(), 0, num_elem, addr, len, - mac); + return openssl_digest_vector(EVP_sha256(), num_elem, addr, len, mac); } #endif /* NO_SHA256_WRAPPER */ +static const EVP_CIPHER * aes_get_evp_cipher(size_t keylen) +{ + switch (keylen) { + case 16: + return EVP_aes_128_ecb(); + case 24: + return EVP_aes_192_ecb(); + case 32: + return EVP_aes_256_ecb(); + } + + return NULL; +} + + void * aes_encrypt_init(const u8 *key, size_t len) { - AES_KEY *ak; - ak = os_malloc(sizeof(*ak)); - if (ak == NULL) + EVP_CIPHER_CTX *ctx; + const EVP_CIPHER *type; + + type = aes_get_evp_cipher(len); + if (type == NULL) return NULL; - if (AES_set_encrypt_key(key, 8 * len, ak) < 0) { - os_free(ak); + + ctx = os_malloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + EVP_CIPHER_CTX_init(ctx); + if (EVP_EncryptInit_ex(ctx, type, NULL, key, NULL) != 1) { + os_free(ctx); return NULL; } - return ak; + EVP_CIPHER_CTX_set_padding(ctx, 0); + return ctx; } void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) { - AES_encrypt(plain, crypt, ctx); + EVP_CIPHER_CTX *c = ctx; + int clen = 16; + if (EVP_EncryptUpdate(c, crypt, &clen, plain, 16) != 1) { + wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptUpdate failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + } } void aes_encrypt_deinit(void *ctx) { - os_free(ctx); + EVP_CIPHER_CTX *c = ctx; + u8 buf[16]; + int len = sizeof(buf); + if (EVP_EncryptFinal_ex(c, buf, &len) != 1) { + wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptFinal_ex failed: " + "%s", ERR_error_string(ERR_get_error(), NULL)); + } + if (len != 0) { + wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d " + "in AES encrypt", len); + } + EVP_CIPHER_CTX_cleanup(c); + os_free(c); } void * aes_decrypt_init(const u8 *key, size_t len) { - AES_KEY *ak; - ak = os_malloc(sizeof(*ak)); - if (ak == NULL) + EVP_CIPHER_CTX *ctx; + const EVP_CIPHER *type; + + type = aes_get_evp_cipher(len); + if (type == NULL) return NULL; - if (AES_set_decrypt_key(key, 8 * len, ak) < 0) { - os_free(ak); + + ctx = os_malloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + EVP_CIPHER_CTX_init(ctx); + if (EVP_DecryptInit_ex(ctx, type, NULL, key, NULL) != 1) { + os_free(ctx); return NULL; } - return ak; + EVP_CIPHER_CTX_set_padding(ctx, 0); + return ctx; } void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) { - AES_decrypt(crypt, plain, ctx); + EVP_CIPHER_CTX *c = ctx; + int plen = 16; + if (EVP_DecryptUpdate(c, plain, &plen, crypt, 16) != 1) { + wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptUpdate failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + } } void aes_decrypt_deinit(void *ctx) { + EVP_CIPHER_CTX *c = ctx; + u8 buf[16]; + int len = sizeof(buf); + if (EVP_DecryptFinal_ex(c, buf, &len) != 1) { + wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptFinal_ex failed: " + "%s", ERR_error_string(ERR_get_error(), NULL)); + } + if (len != 0) { + wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d " + "in AES decrypt", len); + } + EVP_CIPHER_CTX_cleanup(c); os_free(ctx); } @@ -458,6 +505,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) { @@ -503,3 +585,236 @@ 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; + HMAC_CTX_init(&ctx->ctx); + +#if OPENSSL_VERSION_NUMBER < 0x00909000 + HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL); +#else /* openssl < 0.9.9 */ + if (HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL) != 1) { + 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; +} + + +int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len, + int iterations, u8 *buf, size_t buflen) +{ +#if OPENSSL_VERSION_NUMBER < 0x00908000 + if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase), + (unsigned char *) ssid, + ssid_len, 4096, buflen, buf) != 1) + return -1; +#else /* openssl < 0.9.8 */ + if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase), ssid, + ssid_len, 4096, buflen, buf) != 1) + return -1; +#endif /* openssl < 0.9.8 */ + return 0; +} + + +int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + HMAC_CTX ctx; + size_t i; + unsigned int mdlen; + int res; + + HMAC_CTX_init(&ctx); +#if OPENSSL_VERSION_NUMBER < 0x00909000 + HMAC_Init_ex(&ctx, key, key_len, EVP_sha1(), NULL); +#else /* openssl < 0.9.9 */ + if (HMAC_Init_ex(&ctx, key, key_len, EVP_sha1(), NULL) != 1) + return -1; +#endif /* openssl < 0.9.9 */ + + for (i = 0; i < num_elem; i++) + HMAC_Update(&ctx, addr[i], len[i]); + + mdlen = 20; +#if OPENSSL_VERSION_NUMBER < 0x00909000 + HMAC_Final(&ctx, mac, &mdlen); + res = 1; +#else /* openssl < 0.9.9 */ + res = HMAC_Final(&ctx, mac, &mdlen); +#endif /* openssl < 0.9.9 */ + HMAC_CTX_cleanup(&ctx); + + return res == 1 ? 0 : -1; +} + + +int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac); +} + + +#ifdef CONFIG_SHA256 + +int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + HMAC_CTX ctx; + size_t i; + unsigned int mdlen; + int res; + + HMAC_CTX_init(&ctx); +#if OPENSSL_VERSION_NUMBER < 0x00909000 + HMAC_Init_ex(&ctx, key, key_len, EVP_sha256(), NULL); +#else /* openssl < 0.9.9 */ + if (HMAC_Init_ex(&ctx, key, key_len, EVP_sha256(), NULL) != 1) + return -1; +#endif /* openssl < 0.9.9 */ + + for (i = 0; i < num_elem; i++) + HMAC_Update(&ctx, addr[i], len[i]); + + mdlen = 32; +#if OPENSSL_VERSION_NUMBER < 0x00909000 + HMAC_Final(&ctx, mac, &mdlen); + res = 1; +#else /* openssl < 0.9.9 */ + res = HMAC_Final(&ctx, mac, &mdlen); +#endif /* openssl < 0.9.9 */ + HMAC_CTX_cleanup(&ctx); + + return res == 1 ? 0 : -1; +} + + +int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA256 */ + + +int crypto_get_random(void *buf, size_t len) +{ + if (RAND_bytes(buf, len) != 1) + return -1; + return 0; +} + + +#ifdef CONFIG_OPENSSL_CMAC +int omac1_aes_128_vector(const u8 *key, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + CMAC_CTX *ctx; + int ret = -1; + size_t outlen, i; + + ctx = CMAC_CTX_new(); + if (ctx == NULL) + return -1; + + if (!CMAC_Init(ctx, key, 16, EVP_aes_128_cbc(), NULL)) + goto fail; + for (i = 0; i < num_elem; i++) { + if (!CMAC_Update(ctx, addr[i], len[i])) + goto fail; + } + if (!CMAC_Final(ctx, mac, &outlen) || outlen != 16) + goto fail; + + ret = 0; +fail: + CMAC_CTX_free(ctx); + return ret; +} + + +int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac) +{ + return omac1_aes_128_vector(key, 1, &data, &data_len, mac); +} +#endif /* CONFIG_OPENSSL_CMAC */ diff --git a/src/crypto/des-internal.c b/src/crypto/des-internal.c index ccea9503216ea..dec39ef8c61d0 100644 --- a/src/crypto/des-internal.c +++ b/src/crypto/des-internal.c @@ -4,14 +4,8 @@ * Modifications to LibTomCrypt implementation: * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/crypto/des_i.h b/src/crypto/des_i.h index 6f274144a0e61..c9563d2204551 100644 --- a/src/crypto/des_i.h +++ b/src/crypto/des_i.h @@ -2,14 +2,8 @@ * DES and 3DES-EDE ciphers * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef DES_I_H diff --git a/src/crypto/dh_group5.c b/src/crypto/dh_group5.c index 8c475bf94ae9f..ccdbfc8129583 100644 --- a/src/crypto/dh_group5.c +++ b/src/crypto/dh_group5.c @@ -1,15 +1,9 @@ /* * Diffie-Hellman group 5 operations - * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2009, 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 - * published 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" @@ -22,12 +16,18 @@ void * dh5_init(struct wpabuf **priv, struct wpabuf **publ) { *publ = dh_init(dh_groups_get(5), priv); - if (*publ == 0) + if (*publ == NULL) return NULL; return (void *) 1; } +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 595f1114fe2be..abee8eaafbde3 100644 --- a/src/crypto/dh_group5.h +++ b/src/crypto/dh_group5.h @@ -1,21 +1,16 @@ /* * Diffie-Hellman group 5 operations - * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2009, 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 - * published 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. */ #ifndef DH_GROUP5_H #define DH_GROUP5_H void * dh5_init(struct wpabuf **priv, struct wpabuf **publ); +void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ); struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public, const struct wpabuf *own_private); void dh5_free(void *ctx); diff --git a/src/crypto/dh_groups.c b/src/crypto/dh_groups.c index 7bd2fb7b4e21e..f757b6b54e8f0 100644 --- a/src/crypto/dh_groups.c +++ b/src/crypto/dh_groups.c @@ -2,20 +2,15 @@ * Diffie-Hellman groups * Copyright (c) 2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" #include "crypto.h" +#include "random.h" #include "dh_groups.h" @@ -564,7 +559,8 @@ struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv) if (*priv == NULL) return NULL; - if (os_get_random(wpabuf_put(*priv, dh->prime_len), dh->prime_len)) { + if (random_get_bytes(wpabuf_put(*priv, dh->prime_len), dh->prime_len)) + { wpabuf_free(*priv); *priv = NULL; return NULL; diff --git a/src/crypto/dh_groups.h b/src/crypto/dh_groups.h index 5c61539b70029..225f0067a17f9 100644 --- a/src/crypto/dh_groups.h +++ b/src/crypto/dh_groups.h @@ -2,14 +2,8 @@ * Diffie-Hellman groups * Copyright (c) 2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef DH_GROUPS_H diff --git a/src/crypto/fips_prf_cryptoapi.c b/src/crypto/fips_prf_cryptoapi.c index 17d3116e8b579..dca93a3d33660 100644 --- a/src/crypto/fips_prf_cryptoapi.c +++ b/src/crypto/fips_prf_cryptoapi.c @@ -2,14 +2,8 @@ * FIPS 186-2 PRF for Microsoft CryptoAPI * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/crypto/fips_prf_gnutls.c b/src/crypto/fips_prf_gnutls.c index f742e98589f28..947e6f6414628 100644 --- a/src/crypto/fips_prf_gnutls.c +++ b/src/crypto/fips_prf_gnutls.c @@ -2,14 +2,8 @@ * FIPS 186-2 PRF for libgcrypt * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/crypto/fips_prf_internal.c b/src/crypto/fips_prf_internal.c index a85cb14d7424a..a4bf50a47f445 100644 --- a/src/crypto/fips_prf_internal.c +++ b/src/crypto/fips_prf_internal.c @@ -2,14 +2,8 @@ * FIPS 186-2 PRF for internal crypto implementation * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -28,13 +22,14 @@ int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) u8 *xpos = x; u32 carry; - if (seed_len > sizeof(xkey)) + if (seed_len < sizeof(xkey)) + os_memset(xkey + seed_len, 0, sizeof(xkey) - seed_len); + else seed_len = sizeof(xkey); /* FIPS 186-2 + change notice 1 */ os_memcpy(xkey, seed, seed_len); - os_memset(xkey + seed_len, 0, 64 - seed_len); t[0] = 0x67452301; t[1] = 0xEFCDAB89; t[2] = 0x98BADCFE; diff --git a/src/crypto/fips_prf_nss.c b/src/crypto/fips_prf_nss.c index f941983c43c3c..2c962f4f1301f 100644 --- a/src/crypto/fips_prf_nss.c +++ b/src/crypto/fips_prf_nss.c @@ -2,14 +2,8 @@ * FIPS 186-2 PRF for NSS * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/crypto/fips_prf_openssl.c b/src/crypto/fips_prf_openssl.c index d0af98355f658..d69eceabff1c5 100644 --- a/src/crypto/fips_prf_openssl.c +++ b/src/crypto/fips_prf_openssl.c @@ -2,14 +2,8 @@ * FIPS 186-2 PRF for libcrypto * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -37,13 +31,14 @@ int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) u8 *xpos = x; u32 carry; - if (seed_len > sizeof(xkey)) + if (seed_len < sizeof(xkey)) + os_memset(xkey + seed_len, 0, sizeof(xkey) - seed_len); + else seed_len = sizeof(xkey); /* FIPS 186-2 + change notice 1 */ os_memcpy(xkey, seed, seed_len); - os_memset(xkey + seed_len, 0, 64 - seed_len); t[0] = 0x67452301; t[1] = 0xEFCDAB89; t[2] = 0x98BADCFE; diff --git a/src/crypto/md4-internal.c b/src/crypto/md4-internal.c index d9f499f1d07b9..cd5e6ca8cc883 100644 --- a/src/crypto/md4-internal.c +++ b/src/crypto/md4-internal.c @@ -2,14 +2,8 @@ * MD4 hash implementation * Copyright (c) 2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/crypto/md5-internal.c b/src/crypto/md5-internal.c index f8692a9557aed..f0a2a5d3a5afa 100644 --- a/src/crypto/md5-internal.c +++ b/src/crypto/md5-internal.c @@ -2,14 +2,8 @@ * MD5 hash implementation and interface functions * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -182,13 +176,13 @@ void MD5Final(unsigned char digest[16], struct MD5Context *ctx) byteReverse(ctx->in, 14); /* Append length in bits and transform */ - ((u32 *) ctx->in)[14] = ctx->bits[0]; - ((u32 *) ctx->in)[15] = ctx->bits[1]; + ((u32 *) aliasing_hide_typecast(ctx->in, u32))[14] = ctx->bits[0]; + ((u32 *) aliasing_hide_typecast(ctx->in, u32))[15] = ctx->bits[1]; MD5Transform(ctx->buf, (u32 *) ctx->in); byteReverse((unsigned char *) ctx->buf, 4); os_memcpy(digest, ctx->buf, 16); - os_memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ + os_memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ } /* The four core functions - F1 is optimized somewhat */ diff --git a/src/crypto/md5-non-fips.c b/src/crypto/md5-non-fips.c deleted file mode 100644 index 6f2920145a1ec..0000000000000 --- a/src/crypto/md5-non-fips.c +++ /dev/null @@ -1,113 +0,0 @@ -/* - * MD5 hash implementation and interface functions (non-FIPS allowed cases) - * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" - -#include "common.h" -#include "md5.h" -#include "crypto.h" - - -/** - * hmac_md5_vector_non_fips_allow - HMAC-MD5 over data vector (RFC 2104) - * @key: Key for HMAC operations - * @key_len: Length of the key in bytes - * @num_elem: Number of elements in the data vector - * @addr: Pointers to the data areas - * @len: Lengths of the data blocks - * @mac: Buffer for the hash (16 bytes) - * Returns: 0 on success, -1 on failure - */ -int hmac_md5_vector_non_fips_allow(const u8 *key, size_t key_len, - size_t num_elem, const u8 *addr[], - const size_t *len, u8 *mac) -{ - u8 k_pad[64]; /* padding - key XORd with ipad/opad */ - u8 tk[16]; - const u8 *_addr[6]; - size_t i, _len[6]; - - if (num_elem > 5) { - /* - * Fixed limit on the number of fragments to avoid having to - * allocate memory (which could fail). - */ - return -1; - } - - /* if key is longer than 64 bytes reset it to key = MD5(key) */ - if (key_len > 64) { - if (md5_vector_non_fips_allow(1, &key, &key_len, tk)) - return -1; - key = tk; - key_len = 16; - } - - /* the HMAC_MD5 transform looks like: - * - * MD5(K XOR opad, MD5(K XOR ipad, text)) - * - * where K is an n byte key - * ipad is the byte 0x36 repeated 64 times - * opad is the byte 0x5c repeated 64 times - * and text is the data being protected */ - - /* start out by storing key in ipad */ - os_memset(k_pad, 0, sizeof(k_pad)); - os_memcpy(k_pad, key, key_len); - - /* XOR key with ipad values */ - for (i = 0; i < 64; i++) - k_pad[i] ^= 0x36; - - /* perform inner MD5 */ - _addr[0] = k_pad; - _len[0] = 64; - for (i = 0; i < num_elem; i++) { - _addr[i + 1] = addr[i]; - _len[i + 1] = len[i]; - } - if (md5_vector_non_fips_allow(1 + num_elem, _addr, _len, mac)) - return -1; - - os_memset(k_pad, 0, sizeof(k_pad)); - os_memcpy(k_pad, key, key_len); - /* XOR key with opad values */ - for (i = 0; i < 64; i++) - k_pad[i] ^= 0x5c; - - /* perform outer MD5 */ - _addr[0] = k_pad; - _len[0] = 64; - _addr[1] = mac; - _len[1] = MD5_MAC_LEN; - return md5_vector_non_fips_allow(2, _addr, _len, mac); -} - - -/** - * hmac_md5_non_fips_allow - HMAC-MD5 over data buffer (RFC 2104) - * @key: Key for HMAC operations - * @key_len: Length of the key in bytes - * @data: Pointers to the data area - * @data_len: Length of the data area - * @mac: Buffer for the hash (16 bytes) - * Returns: 0 on success, -1 on failure - */ -int hmac_md5_non_fips_allow(const u8 *key, size_t key_len, const u8 *data, - size_t data_len, u8 *mac) -{ - return hmac_md5_vector_non_fips_allow(key, key_len, 1, &data, - &data_len, mac); -} diff --git a/src/crypto/md5.c b/src/crypto/md5.c index 7f14e9b2ec16b..db2b8cc316bad 100644 --- a/src/crypto/md5.c +++ b/src/crypto/md5.c @@ -2,14 +2,8 @@ * MD5 hash implementation and interface functions * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/crypto/md5.h b/src/crypto/md5.h index 8952590782a3f..33f8426c57c82 100644 --- a/src/crypto/md5.h +++ b/src/crypto/md5.h @@ -2,14 +2,8 @@ * MD5 hash implementation and interface functions * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef MD5_H @@ -21,15 +15,5 @@ int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac); -#ifdef CONFIG_FIPS -int hmac_md5_vector_non_fips_allow(const u8 *key, size_t key_len, - size_t num_elem, const u8 *addr[], - const size_t *len, u8 *mac); -int hmac_md5_non_fips_allow(const u8 *key, size_t key_len, const u8 *data, - size_t data_len, u8 *mac); -#else /* CONFIG_FIPS */ -#define hmac_md5_vector_non_fips_allow hmac_md5_vector -#define hmac_md5_non_fips_allow hmac_md5 -#endif /* CONFIG_FIPS */ #endif /* MD5_H */ diff --git a/src/crypto/md5_i.h b/src/crypto/md5_i.h index b7f6596052a6b..7dfc10037da79 100644 --- a/src/crypto/md5_i.h +++ b/src/crypto/md5_i.h @@ -2,14 +2,8 @@ * MD5 internal definitions * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef MD5_I_H diff --git a/src/crypto/milenage.c b/src/crypto/milenage.c index cf0c60e5510fa..a7f9c6a286a89 100644 --- a/src/crypto/milenage.c +++ b/src/crypto/milenage.c @@ -2,14 +2,8 @@ * 3GPP AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208) * Copyright (c) 2006-2007 <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file implements an example authentication algorithm defined for 3GPP * AKA. This can be used to implement a simple HLR/AuC into hlr_auc_gw to allow diff --git a/src/crypto/milenage.h b/src/crypto/milenage.h index d5054d6dcca6e..62137d95ce825 100644 --- a/src/crypto/milenage.h +++ b/src/crypto/milenage.h @@ -2,14 +2,8 @@ * UMTS AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208) * Copyright (c) 2006-2007 <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef MILENAGE_H diff --git a/src/crypto/ms_funcs.c b/src/crypto/ms_funcs.c index dae15ab915fe7..b2bbab2b5c320 100644 --- a/src/crypto/ms_funcs.c +++ b/src/crypto/ms_funcs.c @@ -1,15 +1,9 @@ /* * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759 - * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-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 - * published 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" @@ -19,6 +13,60 @@ #include "ms_funcs.h" #include "crypto.h" +/** + * utf8_to_ucs2 - Convert UTF-8 string to UCS-2 encoding + * @utf8_string: UTF-8 string (IN) + * @utf8_string_len: Length of utf8_string (IN) + * @ucs2_buffer: UCS-2 buffer (OUT) + * @ucs2_buffer_size: Length of UCS-2 buffer (IN) + * @ucs2_string_size: Number of 2-byte words in the resulting UCS-2 string + * Returns: 0 on success, -1 on failure + */ +static int utf8_to_ucs2(const u8 *utf8_string, size_t utf8_string_len, + u8 *ucs2_buffer, size_t ucs2_buffer_size, + size_t *ucs2_string_size) +{ + size_t i, j; + + for (i = 0, j = 0; i < utf8_string_len; i++) { + u8 c = utf8_string[i]; + if (j >= ucs2_buffer_size) { + /* input too long */ + return -1; + } + if (c <= 0x7F) { + WPA_PUT_LE16(ucs2_buffer + j, c); + j += 2; + } else if (i == utf8_string_len - 1 || + j >= ucs2_buffer_size - 1) { + /* incomplete surrogate */ + return -1; + } else { + u8 c2 = utf8_string[++i]; + if ((c & 0xE0) == 0xC0) { + /* two-byte encoding */ + WPA_PUT_LE16(ucs2_buffer + j, + ((c & 0x1F) << 6) | (c2 & 0x3F)); + j += 2; + } else if (i == utf8_string_len || + j >= ucs2_buffer_size - 1) { + /* incomplete surrogate */ + return -1; + } else { + /* three-byte encoding */ + u8 c3 = utf8_string[++i]; + WPA_PUT_LE16(ucs2_buffer + j, + ((c & 0xF) << 12) | + ((c2 & 0x3F) << 6) | (c3 & 0x3F)); + } + } + } + + if (ucs2_string_size) + *ucs2_string_size = j / 2; + return 0; +} + /** * challenge_hash - ChallengeHash() - RFC 2759, Sect. 8.2 @@ -53,7 +101,7 @@ static int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge, /** * nt_password_hash - NtPasswordHash() - RFC 2759, Sect. 8.3 - * @password: 0-to-256-unicode-char Password (IN; ASCII) + * @password: 0-to-256-unicode-char Password (IN; UTF-8) * @password_len: Length of password * @password_hash: 16-octet PasswordHash (OUT) * Returns: 0 on success, -1 on failure @@ -62,18 +110,13 @@ int nt_password_hash(const u8 *password, size_t password_len, u8 *password_hash) { u8 buf[512], *pos; - size_t i, len; + size_t len, max_len; - if (password_len > 256) - password_len = 256; - - /* Convert password into unicode */ - for (i = 0; i < password_len; i++) { - buf[2 * i] = password[i]; - buf[2 * i + 1] = 0; - } + max_len = sizeof(buf); + if (utf8_to_ucs2(password, password_len, buf, max_len, &len) < 0) + return -1; - len = password_len * 2; + len *= 2; pos = buf; return md4_vector(1, (const u8 **) &pos, &len, password_hash); } @@ -117,7 +160,7 @@ void challenge_response(const u8 *challenge, const u8 *password_hash, * @peer_challenge: 16-octet PeerChallenge (IN) * @username: 0-to-256-char UserName (IN) * @username_len: Length of username - * @password: 0-to-256-unicode-char Password (IN; ASCII) + * @password: 0-to-256-unicode-char Password (IN; UTF-8) * @password_len: Length of password * @response: 24-octet Response (OUT) * Returns: 0 on success, -1 on failure @@ -130,8 +173,9 @@ int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, u8 challenge[8]; u8 password_hash[16]; - challenge_hash(peer_challenge, auth_challenge, username, username_len, - challenge); + if (challenge_hash(peer_challenge, auth_challenge, username, + username_len, challenge)) + return -1; if (nt_password_hash(password, password_len, password_hash)) return -1; challenge_response(challenge, password_hash, response); @@ -217,15 +261,16 @@ int generate_authenticator_response_pwhash( if (sha1_vector(3, addr1, len1, response)) return -1; - challenge_hash(peer_challenge, auth_challenge, username, username_len, - challenge); + if (challenge_hash(peer_challenge, auth_challenge, username, + username_len, challenge)) + return -1; return sha1_vector(3, addr2, len2, response); } /** * generate_authenticator_response - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7 - * @password: 0-to-256-unicode-char Password (IN; ASCII) + * @password: 0-to-256-unicode-char Password (IN; UTF-8) * @password_len: Length of password * @nt_response: 24-octet NT-Response (IN) * @peer_challenge: 16-octet PeerChallenge (IN) @@ -254,7 +299,7 @@ int generate_authenticator_response(const u8 *password, size_t password_len, /** * nt_challenge_response - NtChallengeResponse() - RFC 2433, Sect. A.5 * @challenge: 8-octet Challenge (IN) - * @password: 0-to-256-unicode-char Password (IN; ASCII) + * @password: 0-to-256-unicode-char Password (IN; UTF-8) * @password_len: Length of password * @response: 24-octet Response (OUT) * Returns: 0 on success, -1 on failure @@ -375,7 +420,7 @@ int get_asymetric_start_key(const u8 *master_key, u8 *session_key, /** * encrypt_pw_block_with_password_hash - EncryptPwBlockWithPasswordHash() - RFC 2759, Sect. 8.10 - * @password: 0-to-256-unicode-char Password (IN; ASCII) + * @password: 0-to-256-unicode-char Password (IN; UTF-8) * @password_len: Length of password * @password_hash: 16-octet PasswordHash (IN) * @pw_block: 516-byte PwBlock (OUT) @@ -385,18 +430,23 @@ int encrypt_pw_block_with_password_hash( const u8 *password, size_t password_len, const u8 *password_hash, u8 *pw_block) { - size_t i, offset; + size_t ucs2_len, offset; u8 *pos; - if (password_len > 256) + os_memset(pw_block, 0, PWBLOCK_LEN); + + if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0) return -1; - os_memset(pw_block, 0, PWBLOCK_LEN); - offset = (256 - password_len) * 2; - if (os_get_random(pw_block, offset) < 0) + if (ucs2_len > 256) return -1; - for (i = 0; i < password_len; i++) - pw_block[offset + i * 2] = password[i]; + + offset = (256 - ucs2_len) * 2; + if (offset != 0) { + os_memmove(pw_block + offset, pw_block, ucs2_len * 2); + if (os_get_random(pw_block, offset) < 0) + return -1; + } /* * PasswordLength is 4 octets, but since the maximum password length is * 256, only first two (in little endian byte order) can be non-zero. @@ -410,9 +460,9 @@ int encrypt_pw_block_with_password_hash( /** * new_password_encrypted_with_old_nt_password_hash - NewPasswordEncryptedWithOldNtPasswordHash() - RFC 2759, Sect. 8.9 - * @new_password: 0-to-256-unicode-char NewPassword (IN; ASCII) + * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8) * @new_password_len: Length of new_password - * @old_password: 0-to-256-unicode-char OldPassword (IN; ASCII) + * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8) * @old_password_len: Length of old_password * @encrypted_pw_block: 516-octet EncryptedPwBlock (OUT) * Returns: 0 on success, -1 on failure @@ -450,9 +500,9 @@ void nt_password_hash_encrypted_with_block(const u8 *password_hash, /** * old_nt_password_hash_encrypted_with_new_nt_password_hash - OldNtPasswordHashEncryptedWithNewNtPasswordHash() - RFC 2759, Sect. 8.12 - * @new_password: 0-to-256-unicode-char NewPassword (IN; ASCII) + * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8) * @new_password_len: Length of new_password - * @old_password: 0-to-256-unicode-char OldPassword (IN; ASCII) + * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8) * @old_password_len: Length of old_password * @encrypted_password_hash: 16-octet EncryptedPasswordHash (OUT) * Returns: 0 on success, -1 on failure diff --git a/src/crypto/ms_funcs.h b/src/crypto/ms_funcs.h index 298dbcf4fee34..bd9bfee95b7c2 100644 --- a/src/crypto/ms_funcs.h +++ b/src/crypto/ms_funcs.h @@ -2,14 +2,8 @@ * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759 * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef MS_FUNCS_H diff --git a/src/crypto/random.c b/src/crypto/random.c new file mode 100644 index 0000000000000..053740e9bfea6 --- /dev/null +++ b/src/crypto/random.c @@ -0,0 +1,446 @@ +/* + * Random number generator + * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + * + * This random number generator is used to provide additional entropy to the + * one provided by the operating system (os_get_random()) for session key + * generation. The os_get_random() output is expected to be secure and the + * implementation here is expected to provide only limited protection against + * cases where os_get_random() cannot provide strong randomness. This + * implementation shall not be assumed to be secure as the sole source of + * randomness. The random_get_bytes() function mixes in randomness from + * os_get_random() and as such, calls to os_get_random() can be replaced with + * calls to random_get_bytes() without reducing security. + * + * The design here follows partially the design used in the Linux + * drivers/char/random.c, but the implementation here is simpler and not as + * strong. This is a compromise to reduce duplicated CPU effort and to avoid + * extra code/memory size. As pointed out above, os_get_random() needs to be + * guaranteed to be secure for any of the security assumptions to hold. + */ + +#include "utils/includes.h" +#ifdef __linux__ +#include <fcntl.h> +#endif /* __linux__ */ + +#include "utils/common.h" +#include "utils/eloop.h" +#include "crypto/crypto.h" +#include "sha1.h" +#include "random.h" + +#define POOL_WORDS 32 +#define POOL_WORDS_MASK (POOL_WORDS - 1) +#define POOL_TAP1 26 +#define POOL_TAP2 20 +#define POOL_TAP3 14 +#define POOL_TAP4 7 +#define POOL_TAP5 1 +#define EXTRACT_LEN 16 +#define MIN_READY_MARK 2 + +static u32 pool[POOL_WORDS]; +static unsigned int input_rotate = 0; +static unsigned int pool_pos = 0; +static u8 dummy_key[20]; +#ifdef __linux__ +static size_t dummy_key_avail = 0; +static int random_fd = -1; +#endif /* __linux__ */ +static unsigned int own_pool_ready = 0; +#define RANDOM_ENTROPY_SIZE 20 +static char *random_entropy_file = NULL; +static int random_entropy_file_read = 0; + +#define MIN_COLLECT_ENTROPY 1000 +static unsigned int entropy = 0; +static unsigned int total_collected = 0; + + +static void random_write_entropy(void); + + +static u32 __ROL32(u32 x, u32 y) +{ + return (x << (y & 31)) | (x >> (32 - (y & 31))); +} + + +static void random_mix_pool(const void *buf, size_t len) +{ + static const u32 twist[8] = { + 0x00000000, 0x3b6e20c8, 0x76dc4190, 0x4db26158, + 0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 + }; + const u8 *pos = buf; + u32 w; + + wpa_hexdump_key(MSG_EXCESSIVE, "random_mix_pool", buf, len); + + while (len--) { + w = __ROL32(*pos++, input_rotate & 31); + input_rotate += pool_pos ? 7 : 14; + pool_pos = (pool_pos - 1) & POOL_WORDS_MASK; + w ^= pool[pool_pos]; + w ^= pool[(pool_pos + POOL_TAP1) & POOL_WORDS_MASK]; + w ^= pool[(pool_pos + POOL_TAP2) & POOL_WORDS_MASK]; + w ^= pool[(pool_pos + POOL_TAP3) & POOL_WORDS_MASK]; + w ^= pool[(pool_pos + POOL_TAP4) & POOL_WORDS_MASK]; + w ^= pool[(pool_pos + POOL_TAP5) & POOL_WORDS_MASK]; + pool[pool_pos] = (w >> 3) ^ twist[w & 7]; + } +} + + +static void random_extract(u8 *out) +{ + unsigned int i; + u8 hash[SHA1_MAC_LEN]; + u32 *hash_ptr; + u32 buf[POOL_WORDS / 2]; + + /* First, add hash back to pool to make backtracking more difficult. */ + hmac_sha1(dummy_key, sizeof(dummy_key), (const u8 *) pool, + sizeof(pool), hash); + random_mix_pool(hash, sizeof(hash)); + /* Hash half the pool to extra data */ + for (i = 0; i < POOL_WORDS / 2; i++) + buf[i] = pool[(pool_pos - i) & POOL_WORDS_MASK]; + hmac_sha1(dummy_key, sizeof(dummy_key), (const u8 *) buf, + sizeof(buf), hash); + /* + * Fold the hash to further reduce any potential output pattern. + * Though, compromise this to reduce CPU use for the most common output + * length (32) and return 16 bytes from instead of only half. + */ + hash_ptr = (u32 *) hash; + hash_ptr[0] ^= hash_ptr[4]; + os_memcpy(out, hash, EXTRACT_LEN); +} + + +void random_add_randomness(const void *buf, size_t len) +{ + struct os_time t; + static unsigned int count = 0; + + count++; + if (entropy > MIN_COLLECT_ENTROPY && (count & 0x3ff) != 0) { + /* + * No need to add more entropy at this point, so save CPU and + * skip the update. + */ + return; + } + wpa_printf(MSG_EXCESSIVE, "Add randomness: count=%u entropy=%u", + count, entropy); + + os_get_time(&t); + wpa_hexdump_key(MSG_EXCESSIVE, "random pool", + (const u8 *) pool, sizeof(pool)); + random_mix_pool(&t, sizeof(t)); + random_mix_pool(buf, len); + wpa_hexdump_key(MSG_EXCESSIVE, "random pool", + (const u8 *) pool, sizeof(pool)); + entropy++; + total_collected++; +} + + +int random_get_bytes(void *buf, size_t len) +{ + int ret; + u8 *bytes = buf; + size_t left; + + wpa_printf(MSG_MSGDUMP, "Get randomness: len=%u entropy=%u", + (unsigned int) len, entropy); + + /* Start with assumed strong randomness from OS */ + ret = os_get_random(buf, len); + wpa_hexdump_key(MSG_EXCESSIVE, "random from os_get_random", + buf, len); + + /* Mix in additional entropy extracted from the internal pool */ + left = len; + while (left) { + size_t siz, i; + u8 tmp[EXTRACT_LEN]; + random_extract(tmp); + wpa_hexdump_key(MSG_EXCESSIVE, "random from internal pool", + tmp, sizeof(tmp)); + siz = left > EXTRACT_LEN ? EXTRACT_LEN : left; + for (i = 0; i < siz; i++) + *bytes++ ^= tmp[i]; + left -= siz; + } + +#ifdef CONFIG_FIPS + /* Mix in additional entropy from the crypto module */ + left = len; + while (left) { + size_t siz, i; + u8 tmp[EXTRACT_LEN]; + if (crypto_get_random(tmp, sizeof(tmp)) < 0) { + wpa_printf(MSG_ERROR, "random: No entropy available " + "for generating strong random bytes"); + return -1; + } + wpa_hexdump_key(MSG_EXCESSIVE, "random from crypto module", + tmp, sizeof(tmp)); + siz = left > EXTRACT_LEN ? EXTRACT_LEN : left; + for (i = 0; i < siz; i++) + *bytes++ ^= tmp[i]; + left -= siz; + } +#endif /* CONFIG_FIPS */ + + wpa_hexdump_key(MSG_EXCESSIVE, "mixed random", buf, len); + + if (entropy < len) + entropy = 0; + else + entropy -= len; + + return ret; +} + + +int random_pool_ready(void) +{ +#ifdef __linux__ + int fd; + ssize_t res; + + /* + * Make sure that there is reasonable entropy available before allowing + * some key derivation operations to proceed. + */ + + if (dummy_key_avail == sizeof(dummy_key)) + return 1; /* Already initialized - good to continue */ + + /* + * Try to fetch some more data from the kernel high quality + * /dev/random. There may not be enough data available at this point, + * so use non-blocking read to avoid blocking the application + * completely. + */ + fd = open("/dev/random", O_RDONLY | O_NONBLOCK); + if (fd < 0) { +#ifndef CONFIG_NO_STDOUT_DEBUG + int error = errno; + perror("open(/dev/random)"); + wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s", + strerror(error)); +#endif /* CONFIG_NO_STDOUT_DEBUG */ + return -1; + } + + res = read(fd, dummy_key + dummy_key_avail, + sizeof(dummy_key) - dummy_key_avail); + if (res < 0) { + wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: " + "%s", strerror(errno)); + res = 0; + } + wpa_printf(MSG_DEBUG, "random: Got %u/%u bytes from " + "/dev/random", (unsigned) res, + (unsigned) (sizeof(dummy_key) - dummy_key_avail)); + dummy_key_avail += res; + close(fd); + + if (dummy_key_avail == sizeof(dummy_key)) { + if (own_pool_ready < MIN_READY_MARK) + own_pool_ready = MIN_READY_MARK; + random_write_entropy(); + return 1; + } + + wpa_printf(MSG_INFO, "random: Only %u/%u bytes of strong " + "random data available from /dev/random", + (unsigned) dummy_key_avail, (unsigned) sizeof(dummy_key)); + + if (own_pool_ready >= MIN_READY_MARK || + total_collected + 10 * own_pool_ready > MIN_COLLECT_ENTROPY) { + wpa_printf(MSG_INFO, "random: Allow operation to proceed " + "based on internal entropy"); + return 1; + } + + wpa_printf(MSG_INFO, "random: Not enough entropy pool available for " + "secure operations"); + return 0; +#else /* __linux__ */ + /* TODO: could do similar checks on non-Linux platforms */ + return 1; +#endif /* __linux__ */ +} + + +void random_mark_pool_ready(void) +{ + own_pool_ready++; + wpa_printf(MSG_DEBUG, "random: Mark internal entropy pool to be " + "ready (count=%u/%u)", own_pool_ready, MIN_READY_MARK); + random_write_entropy(); +} + + +#ifdef __linux__ + +static void random_close_fd(void) +{ + if (random_fd >= 0) { + eloop_unregister_read_sock(random_fd); + close(random_fd); + random_fd = -1; + } +} + + +static void random_read_fd(int sock, void *eloop_ctx, void *sock_ctx) +{ + ssize_t res; + + if (dummy_key_avail == sizeof(dummy_key)) { + random_close_fd(); + return; + } + + res = read(sock, dummy_key + dummy_key_avail, + sizeof(dummy_key) - dummy_key_avail); + if (res < 0) { + wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: " + "%s", strerror(errno)); + return; + } + + wpa_printf(MSG_DEBUG, "random: Got %u/%u bytes from /dev/random", + (unsigned) res, + (unsigned) (sizeof(dummy_key) - dummy_key_avail)); + dummy_key_avail += res; + + if (dummy_key_avail == sizeof(dummy_key)) { + random_close_fd(); + if (own_pool_ready < MIN_READY_MARK) + own_pool_ready = MIN_READY_MARK; + random_write_entropy(); + } +} + +#endif /* __linux__ */ + + +static void random_read_entropy(void) +{ + char *buf; + size_t len; + + if (!random_entropy_file) + return; + + buf = os_readfile(random_entropy_file, &len); + if (buf == NULL) + return; /* entropy file not yet available */ + + if (len != 1 + RANDOM_ENTROPY_SIZE) { + wpa_printf(MSG_DEBUG, "random: Invalid entropy file %s", + random_entropy_file); + os_free(buf); + return; + } + + own_pool_ready = (u8) buf[0]; + random_add_randomness(buf + 1, RANDOM_ENTROPY_SIZE); + random_entropy_file_read = 1; + os_free(buf); + wpa_printf(MSG_DEBUG, "random: Added entropy from %s " + "(own_pool_ready=%u)", + random_entropy_file, own_pool_ready); +} + + +static void random_write_entropy(void) +{ + char buf[RANDOM_ENTROPY_SIZE]; + FILE *f; + u8 opr; + int fail = 0; + + if (!random_entropy_file) + return; + + if (random_get_bytes(buf, RANDOM_ENTROPY_SIZE) < 0) + return; + + f = fopen(random_entropy_file, "wb"); + if (f == NULL) { + wpa_printf(MSG_ERROR, "random: Could not open entropy file %s " + "for writing", random_entropy_file); + return; + } + + opr = own_pool_ready > 0xff ? 0xff : own_pool_ready; + if (fwrite(&opr, 1, 1, f) != 1 || + fwrite(buf, RANDOM_ENTROPY_SIZE, 1, f) != 1) + fail = 1; + fclose(f); + if (fail) { + wpa_printf(MSG_ERROR, "random: Could not write entropy data " + "to %s", random_entropy_file); + return; + } + + wpa_printf(MSG_DEBUG, "random: Updated entropy file %s " + "(own_pool_ready=%u)", + random_entropy_file, own_pool_ready); +} + + +void random_init(const char *entropy_file) +{ + os_free(random_entropy_file); + if (entropy_file) + random_entropy_file = os_strdup(entropy_file); + else + random_entropy_file = NULL; + random_read_entropy(); + +#ifdef __linux__ + if (random_fd >= 0) + return; + + random_fd = open("/dev/random", O_RDONLY | O_NONBLOCK); + if (random_fd < 0) { +#ifndef CONFIG_NO_STDOUT_DEBUG + int error = errno; + perror("open(/dev/random)"); + wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s", + strerror(error)); +#endif /* CONFIG_NO_STDOUT_DEBUG */ + return; + } + wpa_printf(MSG_DEBUG, "random: Trying to read entropy from " + "/dev/random"); + + eloop_register_read_sock(random_fd, random_read_fd, NULL, NULL); +#endif /* __linux__ */ + + random_write_entropy(); +} + + +void random_deinit(void) +{ +#ifdef __linux__ + random_close_fd(); +#endif /* __linux__ */ + random_write_entropy(); + os_free(random_entropy_file); + random_entropy_file = NULL; +} diff --git a/src/crypto/random.h b/src/crypto/random.h new file mode 100644 index 0000000000000..d13e1c49292d8 --- /dev/null +++ b/src/crypto/random.h @@ -0,0 +1,28 @@ +/* + * Random number generator + * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef RANDOM_H +#define RANDOM_H + +#ifdef CONFIG_NO_RANDOM_POOL +#define random_init(e) do { } while (0) +#define random_deinit() do { } while (0) +#define random_add_randomness(b, l) do { } while (0) +#define random_get_bytes(b, l) os_get_random((b), (l)) +#define random_pool_ready() 1 +#define random_mark_pool_ready() do { } while (0) +#else /* CONFIG_NO_RANDOM_POOL */ +void random_init(const char *entropy_file); +void random_deinit(void); +void random_add_randomness(const void *buf, size_t len); +int random_get_bytes(void *buf, size_t len); +int random_pool_ready(void); +void random_mark_pool_ready(void); +#endif /* CONFIG_NO_RANDOM_POOL */ + +#endif /* RANDOM_H */ diff --git a/src/crypto/rc4.c b/src/crypto/rc4.c index 5ab1be191e9a5..98ae269a633f0 100644 --- a/src/crypto/rc4.c +++ b/src/crypto/rc4.c @@ -2,14 +2,8 @@ * RC4 stream cipher * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/crypto/sha1-internal.c b/src/crypto/sha1-internal.c index 3f05ca113125d..10bf153ca30e2 100644 --- a/src/crypto/sha1-internal.c +++ b/src/crypto/sha1-internal.c @@ -2,14 +2,8 @@ * SHA1 hash implementation and interface functions * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/crypto/sha1-pbkdf2.c b/src/crypto/sha1-pbkdf2.c index 11323de7a01e2..8effe2fe063a8 100644 --- a/src/crypto/sha1-pbkdf2.c +++ b/src/crypto/sha1-pbkdf2.c @@ -2,24 +2,16 @@ * SHA1-based key derivation function (PBKDF2) for IEEE 802.11i * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" #include "sha1.h" -#include "md5.h" -#include "crypto.h" -static int pbkdf2_sha1_f(const char *passphrase, const char *ssid, +static int pbkdf2_sha1_f(const char *passphrase, const u8 *ssid, size_t ssid_len, int iterations, unsigned int count, u8 *digest) { @@ -30,7 +22,7 @@ static int pbkdf2_sha1_f(const char *passphrase, const char *ssid, size_t len[2]; size_t passphrase_len = os_strlen(passphrase); - addr[0] = (u8 *) ssid; + addr[0] = ssid; len[0] = ssid_len; addr[1] = count_buf; len[1] = 4; @@ -77,7 +69,7 @@ static int pbkdf2_sha1_f(const char *passphrase, const char *ssid, * iterations is set to 4096 and buflen to 32. This function is described in * IEEE Std 802.11-2004, Clause H.4. The main construction is from PKCS#5 v2.0. */ -int pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, +int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len, int iterations, u8 *buf, size_t buflen) { unsigned int count = 0; diff --git a/src/crypto/sha1-prf.c b/src/crypto/sha1-prf.c new file mode 100644 index 0000000000000..90b9e74b745b1 --- /dev/null +++ b/src/crypto/sha1-prf.c @@ -0,0 +1,66 @@ +/* + * SHA1-based PRF + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha1.h" +#include "crypto.h" + + +/** + * sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1) + * @key: Key for PRF + * @key_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @data: Extra data to bind into the key + * @data_len: Length of the data + * @buf: Buffer for the generated pseudo-random key + * @buf_len: Number of bytes of key to generate + * Returns: 0 on success, -1 of failure + * + * This function is used to derive new, cryptographically separate keys from a + * given key (e.g., PMK in IEEE 802.11i). + */ +int sha1_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len) +{ + u8 counter = 0; + size_t pos, plen; + u8 hash[SHA1_MAC_LEN]; + size_t label_len = os_strlen(label) + 1; + const unsigned char *addr[3]; + size_t len[3]; + + addr[0] = (u8 *) label; + len[0] = label_len; + addr[1] = data; + len[1] = data_len; + addr[2] = &counter; + len[2] = 1; + + pos = 0; + while (pos < buf_len) { + plen = buf_len - pos; + if (plen >= SHA1_MAC_LEN) { + if (hmac_sha1_vector(key, key_len, 3, addr, len, + &buf[pos])) + return -1; + pos += SHA1_MAC_LEN; + } else { + if (hmac_sha1_vector(key, key_len, 3, addr, len, + hash)) + return -1; + os_memcpy(&buf[pos], hash, plen); + break; + } + counter++; + } + + return 0; +} diff --git a/src/crypto/sha1-tlsprf.c b/src/crypto/sha1-tlsprf.c index 2c8c029ecf495..0effd9b76dd89 100644 --- a/src/crypto/sha1-tlsprf.c +++ b/src/crypto/sha1-tlsprf.c @@ -2,14 +2,8 @@ * TLS PRF (SHA1 + MD5) * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -17,11 +11,10 @@ #include "common.h" #include "sha1.h" #include "md5.h" -#include "crypto.h" /** - * tls_prf - Pseudo-Random Function for TLS (TLS-PRF, RFC 2246) + * tls_prf_sha1_md5 - Pseudo-Random Function for TLS (TLS-PRF, RFC 2246) * @secret: Key for PRF * @secret_len: Length of the key in bytes * @label: A unique label for each purpose of the PRF @@ -34,8 +27,8 @@ * This function is used to derive new, cryptographically separate keys from a * given key in TLS. This PRF is defined in RFC 2246, Chapter 5. */ -int tls_prf(const u8 *secret, size_t secret_len, const char *label, - const u8 *seed, size_t seed_len, u8 *out, size_t outlen) +int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label, + const u8 *seed, size_t seed_len, u8 *out, size_t outlen) { size_t L_S1, L_S2, i; const u8 *S1, *S2; @@ -78,19 +71,16 @@ int tls_prf(const u8 *secret, size_t secret_len, const char *label, S2--; } - hmac_md5_vector_non_fips_allow(S1, L_S1, 2, &MD5_addr[1], &MD5_len[1], - A_MD5); + hmac_md5_vector(S1, L_S1, 2, &MD5_addr[1], &MD5_len[1], A_MD5); hmac_sha1_vector(S2, L_S2, 2, &SHA1_addr[1], &SHA1_len[1], A_SHA1); MD5_pos = MD5_MAC_LEN; SHA1_pos = SHA1_MAC_LEN; for (i = 0; i < outlen; i++) { if (MD5_pos == MD5_MAC_LEN) { - hmac_md5_vector_non_fips_allow(S1, L_S1, 3, MD5_addr, - MD5_len, P_MD5); + hmac_md5_vector(S1, L_S1, 3, MD5_addr, MD5_len, P_MD5); MD5_pos = 0; - hmac_md5_non_fips_allow(S1, L_S1, A_MD5, MD5_MAC_LEN, - A_MD5); + hmac_md5(S1, L_S1, A_MD5, MD5_MAC_LEN, A_MD5); } if (SHA1_pos == SHA1_MAC_LEN) { hmac_sha1_vector(S2, L_S2, 3, SHA1_addr, SHA1_len, diff --git a/src/crypto/sha1-tprf.c b/src/crypto/sha1-tprf.c index 4a80e96f0193c..a52949462f77b 100644 --- a/src/crypto/sha1-tprf.c +++ b/src/crypto/sha1-tprf.c @@ -2,14 +2,8 @@ * SHA1 T-PRF for EAP-FAST * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/crypto/sha1.c b/src/crypto/sha1.c index fe00bdbc58699..d48c77d75c5f0 100644 --- a/src/crypto/sha1.c +++ b/src/crypto/sha1.c @@ -2,14 +2,8 @@ * SHA1 hash implementation and interface functions * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -108,56 +102,3 @@ int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, { return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac); } - - -/** - * sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1) - * @key: Key for PRF - * @key_len: Length of the key in bytes - * @label: A unique label for each purpose of the PRF - * @data: Extra data to bind into the key - * @data_len: Length of the data - * @buf: Buffer for the generated pseudo-random key - * @buf_len: Number of bytes of key to generate - * Returns: 0 on success, -1 of failure - * - * This function is used to derive new, cryptographically separate keys from a - * given key (e.g., PMK in IEEE 802.11i). - */ -int sha1_prf(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, size_t buf_len) -{ - u8 counter = 0; - size_t pos, plen; - u8 hash[SHA1_MAC_LEN]; - size_t label_len = os_strlen(label) + 1; - const unsigned char *addr[3]; - size_t len[3]; - - addr[0] = (u8 *) label; - len[0] = label_len; - addr[1] = data; - len[1] = data_len; - addr[2] = &counter; - len[2] = 1; - - pos = 0; - while (pos < buf_len) { - plen = buf_len - pos; - if (plen >= SHA1_MAC_LEN) { - if (hmac_sha1_vector(key, key_len, 3, addr, len, - &buf[pos])) - return -1; - pos += SHA1_MAC_LEN; - } else { - if (hmac_sha1_vector(key, key_len, 3, addr, len, - hash)) - return -1; - os_memcpy(&buf[pos], hash, plen); - break; - } - counter++; - } - - return 0; -} diff --git a/src/crypto/sha1.h b/src/crypto/sha1.h index c1a6233bb0fb5..933cd81b95b3a 100644 --- a/src/crypto/sha1.h +++ b/src/crypto/sha1.h @@ -2,14 +2,8 @@ * SHA1 hash implementation and interface functions * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef SHA1_H @@ -25,9 +19,9 @@ int sha1_prf(const u8 *key, size_t key_len, const char *label, const u8 *data, size_t data_len, u8 *buf, size_t buf_len); int sha1_t_prf(const u8 *key, size_t key_len, const char *label, const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len); -int __must_check tls_prf(const u8 *secret, size_t secret_len, - const char *label, const u8 *seed, size_t seed_len, - u8 *out, size_t outlen); -int pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, +int __must_check tls_prf_sha1_md5(const u8 *secret, size_t secret_len, + const char *label, const u8 *seed, + size_t seed_len, u8 *out, size_t outlen); +int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len, int iterations, u8 *buf, size_t buflen); #endif /* SHA1_H */ diff --git a/src/crypto/sha1_i.h b/src/crypto/sha1_i.h index ec2f82f75b969..344387e9738f2 100644 --- a/src/crypto/sha1_i.h +++ b/src/crypto/sha1_i.h @@ -2,14 +2,8 @@ * SHA1 internal definitions * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef SHA1_I_H diff --git a/src/crypto/sha256-internal.c b/src/crypto/sha256-internal.c index b0613739fbc65..35299b0524bd9 100644 --- a/src/crypto/sha256-internal.c +++ b/src/crypto/sha256-internal.c @@ -1,34 +1,18 @@ /* * SHA-256 hash implementation and interface functions - * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" #include "sha256.h" +#include "sha256_i.h" #include "crypto.h" -struct sha256_state { - u64 length; - u32 state[8], curlen; - u8 buf[64]; -}; - -static void sha256_init(struct sha256_state *md); -static int sha256_process(struct sha256_state *md, const unsigned char *in, - unsigned long inlen); -static int sha256_done(struct sha256_state *md, unsigned char *out); - /** * sha256_vector - SHA256 hash for data vector @@ -137,7 +121,7 @@ static int sha256_compress(struct sha256_state *md, unsigned char *buf) /* Initialize the hash state */ -static void sha256_init(struct sha256_state *md) +void sha256_init(struct sha256_state *md) { md->curlen = 0; md->length = 0; @@ -158,32 +142,31 @@ static void sha256_init(struct sha256_state *md) @param inlen The length of the data (octets) @return CRYPT_OK if successful */ -static int sha256_process(struct sha256_state *md, const unsigned char *in, - unsigned long inlen) +int sha256_process(struct sha256_state *md, const unsigned char *in, + unsigned long inlen) { unsigned long n; -#define block_size 64 - if (md->curlen > sizeof(md->buf)) + if (md->curlen >= sizeof(md->buf)) return -1; while (inlen > 0) { - if (md->curlen == 0 && inlen >= block_size) { + if (md->curlen == 0 && inlen >= SHA256_BLOCK_SIZE) { if (sha256_compress(md, (unsigned char *) in) < 0) return -1; - md->length += block_size * 8; - in += block_size; - inlen -= block_size; + md->length += SHA256_BLOCK_SIZE * 8; + in += SHA256_BLOCK_SIZE; + inlen -= SHA256_BLOCK_SIZE; } else { - n = MIN(inlen, (block_size - md->curlen)); + n = MIN(inlen, (SHA256_BLOCK_SIZE - md->curlen)); os_memcpy(md->buf + md->curlen, in, n); md->curlen += n; in += n; inlen -= n; - if (md->curlen == block_size) { + if (md->curlen == SHA256_BLOCK_SIZE) { if (sha256_compress(md, md->buf) < 0) return -1; - md->length += 8 * block_size; + md->length += 8 * SHA256_BLOCK_SIZE; md->curlen = 0; } } @@ -199,7 +182,7 @@ static int sha256_process(struct sha256_state *md, const unsigned char *in, @param out [out] The destination of the hash (32 bytes) @return CRYPT_OK if successful */ -static int sha256_done(struct sha256_state *md, unsigned char *out) +int sha256_done(struct sha256_state *md, unsigned char *out) { int i; @@ -217,14 +200,14 @@ static int sha256_done(struct sha256_state *md, unsigned char *out) * encoding like normal. */ if (md->curlen > 56) { - while (md->curlen < 64) { + while (md->curlen < SHA256_BLOCK_SIZE) { md->buf[md->curlen++] = (unsigned char) 0; } sha256_compress(md, md->buf); md->curlen = 0; } - /* pad upto 56 bytes of zeroes */ + /* pad up to 56 bytes of zeroes */ while (md->curlen < 56) { md->buf[md->curlen++] = (unsigned char) 0; } diff --git a/src/crypto/sha256-prf.c b/src/crypto/sha256-prf.c new file mode 100644 index 0000000000000..0da6d130a88e8 --- /dev/null +++ b/src/crypto/sha256-prf.c @@ -0,0 +1,64 @@ +/* + * SHA256-based PRF (IEEE 802.11r) + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha256.h" +#include "crypto.h" + + +/** + * sha256_prf - SHA256-based Pseudo-Random Function (IEEE 802.11r, 8.5.1.5.2) + * @key: Key for PRF + * @key_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @data: Extra data to bind into the key + * @data_len: Length of the data + * @buf: Buffer for the generated pseudo-random key + * @buf_len: Number of bytes of key to generate + * + * This function is used to derive new, cryptographically separate keys from a + * given key. + */ +void sha256_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len) +{ + u16 counter = 1; + size_t pos, plen; + u8 hash[SHA256_MAC_LEN]; + const u8 *addr[4]; + size_t len[4]; + u8 counter_le[2], length_le[2]; + + addr[0] = counter_le; + len[0] = 2; + addr[1] = (u8 *) label; + len[1] = os_strlen(label); + addr[2] = data; + len[2] = data_len; + addr[3] = length_le; + len[3] = sizeof(length_le); + + WPA_PUT_LE16(length_le, buf_len * 8); + pos = 0; + while (pos < buf_len) { + plen = buf_len - pos; + WPA_PUT_LE16(counter_le, counter); + if (plen >= SHA256_MAC_LEN) { + hmac_sha256_vector(key, key_len, 4, addr, len, + &buf[pos]); + pos += SHA256_MAC_LEN; + } else { + hmac_sha256_vector(key, key_len, 4, addr, len, hash); + os_memcpy(&buf[pos], hash, plen); + break; + } + counter++; + } +} diff --git a/src/crypto/sha256-tlsprf.c b/src/crypto/sha256-tlsprf.c new file mode 100644 index 0000000000000..0528dadfdca60 --- /dev/null +++ b/src/crypto/sha256-tlsprf.c @@ -0,0 +1,66 @@ +/* + * TLS PRF P_SHA256 + * Copyright (c) 2011, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha256.h" + + +/** + * tls_prf_sha256 - Pseudo-Random Function for TLS v1.2 (P_SHA256, RFC 5246) + * @secret: Key for PRF + * @secret_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @seed: Seed value to bind into the key + * @seed_len: Length of the seed + * @out: Buffer for the generated pseudo-random key + * @outlen: Number of bytes of key to generate + * Returns: 0 on success, -1 on failure. + * + * This function is used to derive new, cryptographically separate keys from a + * given key in TLS. This PRF is defined in RFC 2246, Chapter 5. + */ +void tls_prf_sha256(const u8 *secret, size_t secret_len, const char *label, + const u8 *seed, size_t seed_len, u8 *out, size_t outlen) +{ + size_t clen; + u8 A[SHA256_MAC_LEN]; + u8 P[SHA256_MAC_LEN]; + size_t pos; + const unsigned char *addr[3]; + size_t len[3]; + + addr[0] = A; + len[0] = SHA256_MAC_LEN; + addr[1] = (unsigned char *) label; + len[1] = os_strlen(label); + addr[2] = seed; + len[2] = seed_len; + + /* + * RFC 5246, Chapter 5 + * A(0) = seed, A(i) = HMAC(secret, A(i-1)) + * P_hash = HMAC(secret, A(1) + seed) + HMAC(secret, A(2) + seed) + .. + * PRF(secret, label, seed) = P_SHA256(secret, label + seed) + */ + + hmac_sha256_vector(secret, secret_len, 2, &addr[1], &len[1], A); + + pos = 0; + while (pos < outlen) { + hmac_sha256_vector(secret, secret_len, 3, addr, len, P); + hmac_sha256(secret, secret_len, A, SHA256_MAC_LEN, A); + + clen = outlen - pos; + if (clen > SHA256_MAC_LEN) + clen = SHA256_MAC_LEN; + os_memcpy(out + pos, P, clen); + pos += clen; + } +} diff --git a/src/crypto/sha256.c b/src/crypto/sha256.c index 7f320f9bfea5e..b55e976f37b01 100644 --- a/src/crypto/sha256.c +++ b/src/crypto/sha256.c @@ -1,15 +1,9 @@ /* * SHA-256 hash implementation and interface functions - * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-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 - * published 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" @@ -27,9 +21,10 @@ * @addr: Pointers to the data areas * @len: Lengths of the data blocks * @mac: Buffer for the hash (32 bytes) + * Returns: 0 on success, -1 on failure */ -void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, - const u8 *addr[], const size_t *len, u8 *mac) +int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) { unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */ unsigned char tk[32]; @@ -41,12 +36,13 @@ void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, * Fixed limit on the number of fragments to avoid having to * allocate memory (which could fail). */ - return; + return -1; } /* if key is longer than 64 bytes reset it to key = SHA256(key) */ if (key_len > 64) { - sha256_vector(1, &key, &key_len, tk); + if (sha256_vector(1, &key, &key_len, tk) < 0) + return -1; key = tk; key_len = 32; } @@ -74,7 +70,8 @@ void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, _addr[i + 1] = addr[i]; _len[i + 1] = len[i]; } - sha256_vector(1 + num_elem, _addr, _len, mac); + if (sha256_vector(1 + num_elem, _addr, _len, mac) < 0) + return -1; os_memset(k_pad, 0, sizeof(k_pad)); os_memcpy(k_pad, key, key_len); @@ -87,7 +84,7 @@ void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, _len[0] = 64; _addr[1] = mac; _len[1] = SHA256_MAC_LEN; - sha256_vector(2, _addr, _len, mac); + return sha256_vector(2, _addr, _len, mac); } @@ -97,61 +94,11 @@ void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, * @key_len: Length of the key in bytes * @data: Pointers to the data area * @data_len: Length of the data area - * @mac: Buffer for the hash (20 bytes) - */ -void hmac_sha256(const u8 *key, size_t key_len, const u8 *data, - size_t data_len, u8 *mac) -{ - hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac); -} - - -/** - * sha256_prf - SHA256-based Pseudo-Random Function (IEEE 802.11r, 8.5.1.5.2) - * @key: Key for PRF - * @key_len: Length of the key in bytes - * @label: A unique label for each purpose of the PRF - * @data: Extra data to bind into the key - * @data_len: Length of the data - * @buf: Buffer for the generated pseudo-random key - * @buf_len: Number of bytes of key to generate - * - * This function is used to derive new, cryptographically separate keys from a - * given key. + * @mac: Buffer for the hash (32 bytes) + * Returns: 0 on success, -1 on failure */ -void sha256_prf(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, size_t buf_len) +int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) { - u16 counter = 1; - size_t pos, plen; - u8 hash[SHA256_MAC_LEN]; - const u8 *addr[4]; - size_t len[4]; - u8 counter_le[2], length_le[2]; - - addr[0] = counter_le; - len[0] = 2; - addr[1] = (u8 *) label; - len[1] = os_strlen(label); - addr[2] = data; - len[2] = data_len; - addr[3] = length_le; - len[3] = sizeof(length_le); - - WPA_PUT_LE16(length_le, buf_len * 8); - pos = 0; - while (pos < buf_len) { - plen = buf_len - pos; - WPA_PUT_LE16(counter_le, counter); - if (plen >= SHA256_MAC_LEN) { - hmac_sha256_vector(key, key_len, 4, addr, len, - &buf[pos]); - pos += SHA256_MAC_LEN; - } else { - hmac_sha256_vector(key, key_len, 4, addr, len, hash); - os_memcpy(&buf[pos], hash, plen); - break; - } - counter++; - } + return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac); } diff --git a/src/crypto/sha256.h b/src/crypto/sha256.h index dc597f09b53ae..fcac8004ca4f5 100644 --- a/src/crypto/sha256.h +++ b/src/crypto/sha256.h @@ -1,15 +1,9 @@ /* * SHA256 hash implementation and interface functions - * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef SHA256_H @@ -17,11 +11,14 @@ #define SHA256_MAC_LEN 32 -void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, - const u8 *addr[], const size_t *len, u8 *mac); -void hmac_sha256(const u8 *key, size_t key_len, const u8 *data, - size_t data_len, u8 *mac); +int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac); +int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac); void sha256_prf(const u8 *key, size_t key_len, const char *label, const u8 *data, size_t data_len, u8 *buf, size_t buf_len); +void tls_prf_sha256(const u8 *secret, size_t secret_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *out, size_t outlen); #endif /* SHA256_H */ diff --git a/src/crypto/sha256_i.h b/src/crypto/sha256_i.h new file mode 100644 index 0000000000000..a502d2ba5d68a --- /dev/null +++ b/src/crypto/sha256_i.h @@ -0,0 +1,25 @@ +/* + * SHA-256 internal definitions + * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef SHA256_I_H +#define SHA256_I_H + +#define SHA256_BLOCK_SIZE 64 + +struct sha256_state { + u64 length; + u32 state[8], curlen; + u8 buf[SHA256_BLOCK_SIZE]; +}; + +void sha256_init(struct sha256_state *md); +int sha256_process(struct sha256_state *md, const unsigned char *in, + unsigned long inlen); +int sha256_done(struct sha256_state *md, unsigned char *out); + +#endif /* SHA256_I_H */ diff --git a/src/crypto/tls.h b/src/crypto/tls.h index 0928b5ba43b74..b61e43939f879 100644 --- a/src/crypto/tls.h +++ b/src/crypto/tls.h @@ -2,14 +2,8 @@ * SSL/TLS interface definition * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TLS_H @@ -24,13 +18,13 @@ struct tls_keys { size_t client_random_len; const u8 *server_random; size_t server_random_len; - const u8 *inner_secret; /* TLS/IA inner secret */ - size_t inner_secret_len; }; enum tls_event { + TLS_CERT_CHAIN_SUCCESS, TLS_CERT_CHAIN_FAILURE, - TLS_PEER_CERTIFICATE + TLS_PEER_CERTIFICATE, + TLS_ALERT }; /* @@ -65,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 { @@ -72,6 +72,7 @@ struct tls_config { const char *pkcs11_engine_path; const char *pkcs11_module_path; int fips_mode; + int cert_in_cb; void (*event_cb)(void *ctx, enum tls_event ev, union tls_event_data *data); @@ -80,6 +81,7 @@ struct tls_config { #define TLS_CONN_ALLOW_SIGN_RSA_MD5 BIT(0) #define TLS_CONN_DISABLE_TIME_CHECKS BIT(1) +#define TLS_CONN_DISABLE_SESSION_TICKET BIT(2) /** * struct tls_connection_params - Parameters for TLS connection @@ -114,7 +116,6 @@ struct tls_config { * specific for now) * @cert_id: the certificate's id when using engine * @ca_cert_id: the CA certificate's id when using engine - * @tls_ia: Whether to enable TLS/IA (for EAP-TTLSv1) * @flags: Parameter options (TLS_CONN_*) * * TLS connection parameters to be configured with tls_connection_set_params() @@ -142,7 +143,6 @@ struct tls_connection_params { const char *dh_file; const u8 *dh_blob; size_t dh_blob_len; - int tls_ia; /* OpenSSL specific variables */ int engine; @@ -282,20 +282,6 @@ int __must_check tls_connection_set_verify(void *tls_ctx, int verify_peer); /** - * tls_connection_set_ia - Set TLS/IA parameters - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * @tls_ia: 1 = enable TLS/IA - * Returns: 0 on success, -1 on failure - * - * This function is used to configure TLS/IA in server mode where - * tls_connection_set_params() is not used. - */ -int __must_check tls_connection_set_ia(void *tls_ctx, - struct tls_connection *conn, - int tls_ia); - -/** * tls_connection_get_keys - Get master key and random data from TLS connection * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() @@ -322,7 +308,7 @@ int __must_check tls_connection_get_keys(void *tls_ctx, * not exported from the TLS library, tls_connection_prf() is required so that * further keying material can be derived from the master secret. If not * implemented, the function will still need to be defined, but it can just - * return -1. Example implementation of this function is in tls_prf() function + * return -1. Example implementation of this function is in tls_prf_sha1_md5() * when it is called with seed set to client_random|server_random (or * server_random|client_random). */ @@ -364,6 +350,12 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx, const struct wpabuf *in_data, struct wpabuf **appl_data); +struct wpabuf * tls_connection_handshake2(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data, + int *more_data_needed); + /** * tls_connection_server_handshake - Process TLS handshake (server side) * @tls_ctx: TLS context data from tls_init() @@ -409,6 +401,11 @@ struct wpabuf * tls_connection_decrypt(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data); +struct wpabuf * tls_connection_decrypt2(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + int *more_data_needed); + /** * tls_connection_resumed - Was session resumption used * @tls_ctx: TLS context data from tls_init() @@ -514,7 +511,6 @@ int tls_connection_get_write_alerts(void *tls_ctx, int tls_connection_get_keyblock_size(void *tls_ctx, struct tls_connection *conn); -#define TLS_CAPABILITY_IA 0x0001 /* TLS Inner Application (TLS/IA) */ /** * tls_capabilities - Get supported TLS capabilities * @tls_ctx: TLS context data from tls_init() @@ -522,42 +518,6 @@ int tls_connection_get_keyblock_size(void *tls_ctx, */ unsigned int tls_capabilities(void *tls_ctx); -/** - * tls_connection_ia_send_phase_finished - Send a TLS/IA PhaseFinished message - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * @final: 1 = FinalPhaseFinished, 0 = IntermediatePhaseFinished - * Returns: Encrypted TLS/IA data, %NULL on failure - * - * This function is used to send the TLS/IA end phase message, e.g., when the - * EAP server completes EAP-TTLSv1. - */ -struct wpabuf * tls_connection_ia_send_phase_finished( - void *tls_ctx, struct tls_connection *conn, int final); - -/** - * tls_connection_ia_final_phase_finished - Has final phase been completed - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * Returns: 1 if valid FinalPhaseFinished has been received, 0 if not, or -1 - * on failure - */ -int __must_check tls_connection_ia_final_phase_finished( - void *tls_ctx, struct tls_connection *conn); - -/** - * tls_connection_ia_permute_inner_secret - Permute TLS/IA inner secret - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * @key: Session key material (session_key vectors with 2-octet length), or - * %NULL if no session key was generating in the current phase - * @key_len: Length of session key material - * Returns: 0 on success, -1 on failure - */ -int __must_check tls_connection_ia_permute_inner_secret( - void *tls_ctx, struct tls_connection *conn, - const u8 *key, size_t key_len); - typedef int (*tls_session_ticket_cb) (void *ctx, const u8 *ticket, size_t len, const u8 *client_random, const u8 *server_random, u8 *master_secret); diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c index c3a7358c0e77b..a5d72f407a8d2 100644 --- a/src/crypto/tls_gnutls.c +++ b/src/crypto/tls_gnutls.c @@ -1,15 +1,9 @@ /* * SSL/TLS interface functions for GnuTLS - * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -19,28 +13,12 @@ #include <gnutls/pkcs12.h> #endif /* PKCS12_FUNCS */ -#ifdef CONFIG_GNUTLS_EXTRA -#if LIBGNUTLS_VERSION_NUMBER >= 0x010302 -#define GNUTLS_IA -#include <gnutls/extra.h> -#if LIBGNUTLS_VERSION_NUMBER == 0x010302 -/* This function is not included in the current gnutls/extra.h even though it - * should be, so define it here as a workaround for the time being. */ -int gnutls_ia_verify_endphase(gnutls_session_t session, char *checksum); -#endif /* LIBGNUTLS_VERSION_NUMBER == 0x010302 */ -#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ -#endif /* CONFIG_GNUTLS_EXTRA */ - #include "common.h" #include "tls.h" -#ifndef TLS_RANDOM_SIZE -#define TLS_RANDOM_SIZE 32 -#endif -#ifndef TLS_MASTER_SIZE -#define TLS_MASTER_SIZE 48 -#endif +#define WPA_TLS_RANDOM_SIZE 32 +#define WPA_TLS_MASTER_SIZE 48 #if LIBGNUTLS_VERSION_NUMBER < 0x010302 @@ -77,9 +55,9 @@ typedef struct { gnutls_mac_algorithm_t write_mac_algorithm; gnutls_compression_method_t write_compression_algorithm; cipher_suite_st current_cipher_suite; - opaque master_secret[TLS_MASTER_SIZE]; - opaque client_random[TLS_RANDOM_SIZE]; - opaque server_random[TLS_RANDOM_SIZE]; + opaque master_secret[WPA_TLS_MASTER_SIZE]; + opaque client_random[WPA_TLS_RANDOM_SIZE]; + opaque server_random[WPA_TLS_RANDOM_SIZE]; /* followed by stuff we are not interested in */ } security_parameters_st; @@ -118,21 +96,6 @@ struct tls_connection { int params_set; gnutls_certificate_credentials_t xcred; - - int tls_ia; - int final_phase_finished; - -#ifdef GNUTLS_IA - gnutls_ia_server_credentials_t iacred_srv; - gnutls_ia_client_credentials_t iacred_cli; - - /* Session keys generated in the current phase for inner secret - * permutation before generating/verifying PhaseFinished. */ - u8 *session_keys; - size_t session_keys_len; - - u8 inner_secret[TLS_MASTER_SIZE]; -#endif /* GNUTLS_IA */ }; @@ -285,8 +248,12 @@ static ssize_t tls_push_func(gnutls_transport_ptr ptr, const void *buf, static int tls_gnutls_init_session(struct tls_global *global, struct tls_connection *conn) { +#if LIBGNUTLS_VERSION_NUMBER >= 0x020200 + const char *err; +#else /* LIBGNUTLS_VERSION_NUMBER >= 0x020200 */ const int cert_types[2] = { GNUTLS_CRT_X509, 0 }; const int protos[2] = { GNUTLS_TLS1, 0 }; +#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020200 */ int ret; ret = gnutls_init(&conn->session, @@ -301,6 +268,15 @@ static int tls_gnutls_init_session(struct tls_global *global, if (ret < 0) goto fail; +#if LIBGNUTLS_VERSION_NUMBER >= 0x020200 + ret = gnutls_priority_set_direct(conn->session, "NORMAL:-VERS-SSL3.0", + &err); + if (ret < 0) { + wpa_printf(MSG_ERROR, "GnuTLS: Priority string failure at " + "'%s'", err); + goto fail; + } +#else /* LIBGNUTLS_VERSION_NUMBER >= 0x020200 */ ret = gnutls_certificate_type_set_priority(conn->session, cert_types); if (ret < 0) goto fail; @@ -308,6 +284,7 @@ static int tls_gnutls_init_session(struct tls_global *global, ret = gnutls_protocol_set_priority(conn->session, protos); if (ret < 0) goto fail; +#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020200 */ gnutls_transport_set_pull_function(conn->session, tls_pull_func); gnutls_transport_set_push_function(conn->session, tls_push_func); @@ -364,17 +341,6 @@ void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) if (conn == NULL) return; -#ifdef GNUTLS_IA - if (conn->iacred_srv) - gnutls_ia_free_server_credentials(conn->iacred_srv); - if (conn->iacred_cli) - gnutls_ia_free_client_credentials(conn->iacred_cli); - if (conn->session_keys) { - os_memset(conn->session_keys, 0, conn->session_keys_len); - os_free(conn->session_keys); - } -#endif /* GNUTLS_IA */ - gnutls_certificate_free_credentials(conn->xcred); gnutls_deinit(conn->session); os_free(conn->pre_shared_secret); @@ -407,14 +373,6 @@ int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) wpabuf_free(conn->push_buf); conn->push_buf = NULL; conn->established = 0; - conn->final_phase_finished = 0; -#ifdef GNUTLS_IA - if (conn->session_keys) { - os_memset(conn->session_keys, 0, conn->session_keys_len); - os_free(conn->session_keys); - } - conn->session_keys_len = 0; -#endif /* GNUTLS_IA */ gnutls_deinit(conn->session); if (tls_gnutls_init_session(global, conn)) { @@ -597,11 +555,13 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, conn->xcred, GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5); } +#if LIBGNUTLS_VERSION_NUMBER >= 0x020800 if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) { gnutls_certificate_set_verify_flags( conn->xcred, GNUTLS_VERIFY_DISABLE_TIME_CHECKS); } +#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */ } if (params->client_cert && params->private_key) { @@ -646,7 +606,6 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, } } - conn->tls_ia = params->tls_ia; conn->params_set = 1; ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, @@ -656,28 +615,6 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, gnutls_strerror(ret)); } -#ifdef GNUTLS_IA - if (conn->iacred_cli) - gnutls_ia_free_client_credentials(conn->iacred_cli); - - ret = gnutls_ia_allocate_client_credentials(&conn->iacred_cli); - if (ret) { - wpa_printf(MSG_DEBUG, "Failed to allocate IA credentials: %s", - gnutls_strerror(ret)); - return -1; - } - - ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_IA, - conn->iacred_cli); - if (ret) { - wpa_printf(MSG_DEBUG, "Failed to configure IA credentials: %s", - gnutls_strerror(ret)); - gnutls_ia_free_client_credentials(conn->iacred_cli); - conn->iacred_cli = NULL; - return -1; - } -#endif /* GNUTLS_IE */ - return ret; } @@ -729,11 +666,13 @@ int tls_global_set_params(void *tls_ctx, GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5); } +#if LIBGNUTLS_VERSION_NUMBER >= 0x020800 if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) { gnutls_certificate_set_verify_flags( global->xcred, GNUTLS_VERIFY_DISABLE_TIME_CHECKS); } +#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */ } if (params->client_cert && params->private_key) { @@ -822,10 +761,11 @@ int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, os_memset(keys, 0, sizeof(*keys)); +#if LIBGNUTLS_VERSION_NUMBER < 0x020c00 #ifdef GNUTLS_INTERNAL_STRUCTURE_HACK sec = &conn->session->security_parameters; keys->master_key = sec->master_secret; - keys->master_key_len = TLS_MASTER_SIZE; + keys->master_key_len = WPA_TLS_MASTER_SIZE; keys->client_random = sec->client_random; keys->server_random = sec->server_random; #else /* GNUTLS_INTERNAL_STRUCTURE_HACK */ @@ -835,16 +775,12 @@ int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, (u8 *) gnutls_session_get_server_random(conn->session); /* No access to master_secret */ #endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */ +#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */ -#ifdef GNUTLS_IA - gnutls_ia_extract_inner_secret(conn->session, - (char *) conn->inner_secret); - keys->inner_secret = conn->inner_secret; - keys->inner_secret_len = TLS_MASTER_SIZE; -#endif /* GNUTLS_IA */ - - keys->client_random_len = TLS_RANDOM_SIZE; - keys->server_random_len = TLS_RANDOM_SIZE; +#if LIBGNUTLS_VERSION_NUMBER < 0x020c00 + keys->client_random_len = WPA_TLS_RANDOM_SIZE; + keys->server_random_len = WPA_TLS_RANDOM_SIZE; +#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */ return 0; } @@ -883,11 +819,13 @@ static int tls_connection_verify_peer(struct tls_connection *conn, if (conn->verify_peer && (status & GNUTLS_CERT_INVALID)) { wpa_printf(MSG_INFO, "TLS: Peer certificate not trusted"); + *err = GNUTLS_A_INTERNAL_ERROR; if (status & GNUTLS_CERT_INSECURE_ALGORITHM) { wpa_printf(MSG_INFO, "TLS: Certificate uses insecure " "algorithm"); *err = GNUTLS_A_INSUFFICIENT_SECURITY; } +#if LIBGNUTLS_VERSION_NUMBER >= 0x020800 if (status & GNUTLS_CERT_NOT_ACTIVATED) { wpa_printf(MSG_INFO, "TLS: Certificate not yet " "activated"); @@ -897,6 +835,7 @@ static int tls_connection_verify_peer(struct tls_connection *conn, wpa_printf(MSG_INFO, "TLS: Certificate expired"); *err = GNUTLS_A_CERTIFICATE_EXPIRED; } +#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */ return -1; } @@ -988,7 +927,7 @@ static struct wpabuf * gnutls_get_appl_data(struct tls_connection *conn) wpabuf_size(ad)); wpa_printf(MSG_DEBUG, "GnuTLS: gnutls_record_recv: %d", res); if (res < 0) { - wpa_printf(MSG_DEBUG, "%s - gnutls_ia_recv failed: %d " + wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d " "(%s)", __func__, (int) res, gnutls_strerror(res)); wpabuf_free(ad); @@ -1062,20 +1001,7 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx, goto out; } -#ifdef CONFIG_GNUTLS_EXTRA - if (conn->tls_ia && !gnutls_ia_handshake_p(conn->session)) { - wpa_printf(MSG_INFO, "TLS: No TLS/IA negotiation"); - conn->failed++; - return NULL; - } -#endif /* CONFIG_GNUTLS_EXTRA */ - - if (conn->tls_ia) - wpa_printf(MSG_DEBUG, "TLS: Start TLS/IA handshake"); - else { - wpa_printf(MSG_DEBUG, "TLS: Handshake completed " - "successfully"); - } + wpa_printf(MSG_DEBUG, "TLS: Handshake completed successfully"); conn->established = 1; if (conn->push_buf == NULL) { /* Need to return something to get final TLS ACK. */ @@ -1122,12 +1048,6 @@ struct wpabuf * tls_connection_encrypt(void *tls_ctx, ssize_t res; struct wpabuf *buf; -#ifdef GNUTLS_IA - if (conn->tls_ia) - res = gnutls_ia_send(conn->session, wpabuf_head(in_data), - wpabuf_len(in_data)); - else -#endif /* GNUTLS_IA */ res = gnutls_record_send(conn->session, wpabuf_head(in_data), wpabuf_len(in_data)); if (res < 0) { @@ -1170,65 +1090,6 @@ struct wpabuf * tls_connection_decrypt(void *tls_ctx, if (out == NULL) return NULL; -#ifdef GNUTLS_IA - if (conn->tls_ia) { - res = gnutls_ia_recv(conn->session, wpabuf_mhead(out), - wpabuf_size(out)); - if (res == GNUTLS_E_WARNING_IA_IPHF_RECEIVED || - res == GNUTLS_E_WARNING_IA_FPHF_RECEIVED) { - int final = res == GNUTLS_E_WARNING_IA_FPHF_RECEIVED; - wpa_printf(MSG_DEBUG, "%s: Received %sPhaseFinished", - __func__, final ? "Final" : "Intermediate"); - - res = gnutls_ia_permute_inner_secret( - conn->session, conn->session_keys_len, - (char *) conn->session_keys); - if (conn->session_keys) { - os_memset(conn->session_keys, 0, - conn->session_keys_len); - os_free(conn->session_keys); - } - conn->session_keys = NULL; - conn->session_keys_len = 0; - if (res) { - wpa_printf(MSG_DEBUG, "%s: Failed to permute " - "inner secret: %s", - __func__, gnutls_strerror(res)); - wpabuf_free(out); - return NULL; - } - - res = gnutls_ia_verify_endphase(conn->session, - wpabuf_head(out)); - if (res == 0) { - wpa_printf(MSG_DEBUG, "%s: Correct endphase " - "checksum", __func__); - } else { - wpa_printf(MSG_INFO, "%s: Endphase " - "verification failed: %s", - __func__, gnutls_strerror(res)); - wpabuf_free(out); - return NULL; - } - - if (final) - conn->final_phase_finished = 1; - - return out; - } - - if (res < 0) { - wpa_printf(MSG_DEBUG, "%s - gnutls_ia_recv failed: %d " - "(%s)", __func__, (int) res, - gnutls_strerror(res)); - wpabuf_free(out); - return NULL; - } - wpabuf_put(out, res); - return out; - } -#endif /* GNUTLS_IA */ - res = gnutls_record_recv(conn->session, wpabuf_mhead(out), wpabuf_size(out)); if (res < 0) { @@ -1319,133 +1180,7 @@ int tls_connection_get_keyblock_size(void *tls_ctx, unsigned int tls_capabilities(void *tls_ctx) { - unsigned int capa = 0; - -#ifdef GNUTLS_IA - capa |= TLS_CAPABILITY_IA; -#endif /* GNUTLS_IA */ - - return capa; -} - - -int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn, - int tls_ia) -{ -#ifdef GNUTLS_IA - int ret; - - if (conn == NULL) - return -1; - - conn->tls_ia = tls_ia; - if (!tls_ia) - return 0; - - ret = gnutls_ia_allocate_server_credentials(&conn->iacred_srv); - if (ret) { - wpa_printf(MSG_DEBUG, "Failed to allocate IA credentials: %s", - gnutls_strerror(ret)); - return -1; - } - - ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_IA, - conn->iacred_srv); - if (ret) { - wpa_printf(MSG_DEBUG, "Failed to configure IA credentials: %s", - gnutls_strerror(ret)); - gnutls_ia_free_server_credentials(conn->iacred_srv); - conn->iacred_srv = NULL; - return -1; - } - - return 0; -#else /* GNUTLS_IA */ - return -1; -#endif /* GNUTLS_IA */ -} - - -struct wpabuf * tls_connection_ia_send_phase_finished( - void *tls_ctx, struct tls_connection *conn, int final) -{ -#ifdef GNUTLS_IA - int ret; - struct wpabuf *buf; - - if (conn == NULL || conn->session == NULL || !conn->tls_ia) - return NULL; - - ret = gnutls_ia_permute_inner_secret(conn->session, - conn->session_keys_len, - (char *) conn->session_keys); - if (conn->session_keys) { - os_memset(conn->session_keys, 0, conn->session_keys_len); - os_free(conn->session_keys); - } - conn->session_keys = NULL; - conn->session_keys_len = 0; - if (ret) { - wpa_printf(MSG_DEBUG, "%s: Failed to permute inner secret: %s", - __func__, gnutls_strerror(ret)); - return NULL; - } - - ret = gnutls_ia_endphase_send(conn->session, final); - if (ret) { - wpa_printf(MSG_DEBUG, "%s: Failed to send endphase: %s", - __func__, gnutls_strerror(ret)); - return NULL; - } - - buf = conn->push_buf; - conn->push_buf = NULL; - return buf; -#else /* GNUTLS_IA */ - return NULL; -#endif /* GNUTLS_IA */ -} - - -int tls_connection_ia_final_phase_finished(void *tls_ctx, - struct tls_connection *conn) -{ - if (conn == NULL) - return -1; - - return conn->final_phase_finished; -} - - -int tls_connection_ia_permute_inner_secret(void *tls_ctx, - struct tls_connection *conn, - const u8 *key, size_t key_len) -{ -#ifdef GNUTLS_IA - if (conn == NULL || !conn->tls_ia) - return -1; - - if (conn->session_keys) { - os_memset(conn->session_keys, 0, conn->session_keys_len); - os_free(conn->session_keys); - } - conn->session_keys_len = 0; - - if (key) { - conn->session_keys = os_malloc(key_len); - if (conn->session_keys == NULL) - return -1; - os_memcpy(conn->session_keys, key, key_len); - conn->session_keys_len = key_len; - } else { - conn->session_keys = NULL; - conn->session_keys_len = 0; - } - return 0; -#else /* GNUTLS_IA */ - return -1; -#endif /* GNUTLS_IA */ } diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c index 64124d8a8e3ef..91f06900328a7 100644 --- a/src/crypto/tls_internal.c +++ b/src/crypto/tls_internal.c @@ -1,15 +1,9 @@ /* * TLS interface functions and an internal TLS implementation - * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file interface functions for hostapd/wpa_supplicant to use the * integrated TLSv1 implementation. @@ -211,6 +205,9 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } + tlsv1_client_set_time_checks( + conn->client, !(params->flags & TLS_CONN_DISABLE_TIME_CHECKS)); + return 0; #else /* CONFIG_TLS_INTERNAL_CLIENT */ return -1; @@ -287,13 +284,6 @@ int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, } -int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn, - int tls_ia) -{ - return -1; -} - - int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, struct tls_keys *keys) { @@ -336,6 +326,17 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx, const struct wpabuf *in_data, struct wpabuf **appl_data) { + return tls_connection_handshake2(tls_ctx, conn, in_data, appl_data, + NULL); +} + + +struct wpabuf * tls_connection_handshake2(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data, + int *need_more_data) +{ #ifdef CONFIG_TLS_INTERNAL_CLIENT u8 *res, *ad; size_t res_len, ad_len; @@ -348,7 +349,7 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx, res = tlsv1_client_handshake(conn->client, in_data ? wpabuf_head(in_data) : NULL, in_data ? wpabuf_len(in_data) : 0, - &res_len, &ad, &ad_len); + &res_len, &ad, &ad_len, need_more_data); if (res == NULL) return NULL; out = wpabuf_alloc_ext_data(res, res_len); @@ -459,23 +460,23 @@ struct wpabuf * tls_connection_decrypt(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data) { + return tls_connection_decrypt2(tls_ctx, conn, in_data, NULL); +} + + +struct wpabuf * tls_connection_decrypt2(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + int *need_more_data) +{ + if (need_more_data) + *need_more_data = 0; + #ifdef CONFIG_TLS_INTERNAL_CLIENT if (conn->client) { - struct wpabuf *buf; - int res; - buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3); - if (buf == NULL) - return NULL; - res = tlsv1_client_decrypt(conn->client, wpabuf_head(in_data), - wpabuf_len(in_data), - wpabuf_mhead(buf), - wpabuf_size(buf)); - if (res < 0) { - wpabuf_free(buf); - return NULL; - } - wpabuf_put(buf, res); - return buf; + return tlsv1_client_decrypt(conn->client, wpabuf_head(in_data), + wpabuf_len(in_data), + need_more_data); } #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER @@ -608,28 +609,6 @@ unsigned int tls_capabilities(void *tls_ctx) } -struct wpabuf * tls_connection_ia_send_phase_finished( - void *tls_ctx, struct tls_connection *conn, int final) -{ - return NULL; -} - - -int tls_connection_ia_final_phase_finished(void *tls_ctx, - struct tls_connection *conn) -{ - return -1; -} - - -int tls_connection_ia_permute_inner_secret(void *tls_ctx, - struct tls_connection *conn, - const u8 *key, size_t key_len) -{ - return -1; -} - - int tls_connection_set_session_ticket_cb(void *tls_ctx, struct tls_connection *conn, tls_session_ticket_cb cb, diff --git a/src/crypto/tls_none.c b/src/crypto/tls_none.c index 0c836bb631873..1a1092a184b51 100644 --- a/src/crypto/tls_none.c +++ b/src/crypto/tls_none.c @@ -2,14 +2,8 @@ * SSL/TLS interface functions for no TLS case * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -84,13 +78,6 @@ int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, } -int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn, - int tls_ia) -{ - return -1; -} - - int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, struct tls_keys *keys) { @@ -205,25 +192,3 @@ unsigned int tls_capabilities(void *tls_ctx) { return 0; } - - -struct wpabuf * tls_connection_ia_send_phase_finished( - void *tls_ctx, struct tls_connection *conn, int final) -{ - return NULL; -} - - -int tls_connection_ia_final_phase_finished(void *tls_ctx, - struct tls_connection *conn) -{ - return -1; -} - - -int tls_connection_ia_permute_inner_secret(void *tls_ctx, - struct tls_connection *conn, - const u8 *key, size_t key_len) -{ - return -1; -} diff --git a/src/crypto/tls_nss.c b/src/crypto/tls_nss.c index ad834b6493372..c53c192a1cdea 100644 --- a/src/crypto/tls_nss.c +++ b/src/crypto/tls_nss.c @@ -2,14 +2,8 @@ * SSL/TLS interface functions for NSS * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -419,13 +413,6 @@ int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, } -int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn, - int tls_ia) -{ - return -1; -} - - int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, struct tls_keys *keys) { @@ -649,28 +636,6 @@ unsigned int tls_capabilities(void *tls_ctx) } -struct wpabuf * tls_connection_ia_send_phase_finished( - void *tls_ctx, struct tls_connection *conn, int final) -{ - return NULL; -} - - -int tls_connection_ia_final_phase_finished(void *tls_ctx, - struct tls_connection *conn) -{ - return -1; -} - - -int tls_connection_ia_permute_inner_secret(void *tls_ctx, - struct tls_connection *conn, - const u8 *key, size_t key_len) -{ - return -1; -} - - int tls_connection_set_session_ticket_cb(void *tls_ctx, struct tls_connection *conn, tls_session_ticket_cb cb, diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c index c0a40f95617c6..2c3db473258bc 100644 --- a/src/crypto/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -1,15 +1,9 @@ /* * SSL/TLS interface functions for OpenSSL - * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -28,6 +22,11 @@ #include <openssl/engine.h> #endif /* OPENSSL_NO_ENGINE */ +#ifdef ANDROID +#include <openssl/pem.h> +#include "keystore_get.h" +#endif /* ANDROID */ + #include "common.h" #include "crypto.h" #include "tls.h" @@ -54,6 +53,7 @@ struct tls_global { void (*event_cb)(void *ctx, enum tls_event ev, union tls_event_data *data); void *cb_ctx; + int cert_in_cb; }; static struct tls_global *tls_global = NULL; @@ -81,6 +81,8 @@ struct tls_connection { unsigned int server_cert_only:1; u8 srv_cert_hash[32]; + + unsigned int flags; }; @@ -523,6 +525,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", @@ -687,6 +698,7 @@ void * tls_init(const struct tls_config *conf) if (conf) { tls_global->event_cb = conf->event_cb; tls_global->cb_ctx = conf->cb_ctx; + tls_global->cert_in_cb = conf->cert_in_cb; } #ifdef CONFIG_FIPS @@ -697,6 +709,8 @@ void * tls_init(const struct tls_config *conf) "mode"); ERR_load_crypto_strings(); ERR_print_errors_fp(stderr); + os_free(tls_global); + tls_global = NULL; return NULL; } else wpa_printf(MSG_INFO, "Running in FIPS mode"); @@ -705,13 +719,15 @@ void * tls_init(const struct tls_config *conf) if (conf && conf->fips_mode) { wpa_printf(MSG_ERROR, "FIPS mode requested, but not " "supported"); + os_free(tls_global); + tls_global = NULL; return NULL; } #endif /* OPENSSL_FIPS */ #endif /* CONFIG_FIPS */ SSL_load_error_strings(); SSL_library_init(); -#ifndef OPENSSL_NO_SHA256 +#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256) EVP_add_digest(EVP_sha256()); #endif /* OPENSSL_NO_SHA256 */ /* TODO: if /dev/urandom is available, PRNG is seeded @@ -1137,7 +1153,7 @@ static void openssl_tls_cert_event(struct tls_connection *conn, return; os_memset(&ev, 0, sizeof(ev)); - if (conn->cert_probe) { + if (conn->cert_probe || tls_global->cert_in_cb) { cert = get_x509_cert(err_cert); ev.peer_cert.cert = cert; } @@ -1178,13 +1194,22 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf)); conn = SSL_get_app_data(ssl); - match = conn ? conn->subject_match : NULL; - altmatch = conn ? conn->altsubject_match : NULL; + if (conn == NULL) + return 0; + match = conn->subject_match; + altmatch = conn->altsubject_match; if (!preverify_ok && !conn->ca_cert_verify) preverify_ok = 1; if (!preverify_ok && depth > 0 && conn->server_cert_only) preverify_ok = 1; + if (!preverify_ok && (conn->flags & TLS_CONN_DISABLE_TIME_CHECKS) && + (err == X509_V_ERR_CERT_HAS_EXPIRED || + err == X509_V_ERR_CERT_NOT_YET_VALID)) { + wpa_printf(MSG_DEBUG, "OpenSSL: Ignore certificate validity " + "time mismatch"); + preverify_ok = 1; + } err_str = X509_verify_cert_error_string(err); @@ -1253,6 +1278,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; } @@ -1290,6 +1319,19 @@ static int tls_load_ca_der(void *_ssl_ctx, const char *ca_cert) #endif /* OPENSSL_NO_STDIO */ +#ifdef ANDROID +static BIO * BIO_from_keystore(const char *key) +{ + BIO *bio = NULL; + char value[KEYSTORE_MESSAGE_SIZE]; + int length = keystore_get(key, strlen(key), value); + if (length != -1 && (bio = BIO_new(BIO_s_mem())) != NULL) + BIO_write(bio, value, length); + return bio; +} +#endif /* ANDROID */ + + static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, const char *ca_cert, const u8 *ca_cert_blob, size_t ca_cert_blob_len, const char *ca_path) @@ -1380,6 +1422,36 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, return 0; } +#ifdef ANDROID + if (ca_cert && os_strncmp("keystore://", ca_cert, 11) == 0) { + BIO *bio = BIO_from_keystore(&ca_cert[11]); + STACK_OF(X509_INFO) *stack = NULL; + int i; + + if (bio) { + stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL); + BIO_free(bio); + } + if (!stack) + return -1; + + for (i = 0; i < sk_X509_INFO_num(stack); ++i) { + X509_INFO *info = sk_X509_INFO_value(stack, i); + if (info->x509) { + X509_STORE_add_cert(ssl_ctx->cert_store, + info->x509); + } + if (info->crl) { + X509_STORE_add_crl(ssl_ctx->cert_store, + info->crl); + } + } + sk_X509_INFO_pop_free(stack, X509_INFO_free); + SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); + return 0; + } +#endif /* ANDROID */ + #ifdef CONFIG_NATIVE_WINDOWS if (ca_cert && tls_cryptoapi_ca_cert(ssl_ctx, conn->ssl, ca_cert) == 0) { @@ -1550,26 +1622,42 @@ static int tls_connection_client_cert(struct tls_connection *conn, if (client_cert == NULL) return -1; +#ifdef ANDROID + if (os_strncmp("keystore://", client_cert, 11) == 0) { + BIO *bio = BIO_from_keystore(&client_cert[11]); + X509 *x509 = NULL; + int ret = -1; + if (bio) { + x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL); + BIO_free(bio); + } + if (x509) { + if (SSL_use_certificate(conn->ssl, x509) == 1) + ret = 0; + X509_free(x509); + } + return ret; + } +#endif /* ANDROID */ + #ifndef OPENSSL_NO_STDIO if (SSL_use_certificate_file(conn->ssl, client_cert, SSL_FILETYPE_ASN1) == 1) { wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (DER)" " --> OK"); return 0; - } else { - tls_show_errors(MSG_DEBUG, __func__, - "SSL_use_certificate_file (DER) failed"); } if (SSL_use_certificate_file(conn->ssl, client_cert, SSL_FILETYPE_PEM) == 1) { + ERR_clear_error(); wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (PEM)" " --> OK"); return 0; - } else { - tls_show_errors(MSG_DEBUG, __func__, - "SSL_use_certificate_file (PEM) failed"); } + + tls_show_errors(MSG_DEBUG, __func__, + "SSL_use_certificate_file failed"); #else /* OPENSSL_NO_STDIO */ wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__); #endif /* OPENSSL_NO_STDIO */ @@ -1586,6 +1674,7 @@ static int tls_global_client_cert(SSL_CTX *ssl_ctx, const char *client_cert) if (SSL_CTX_use_certificate_file(ssl_ctx, client_cert, SSL_FILETYPE_ASN1) != 1 && + SSL_CTX_use_certificate_chain_file(ssl_ctx, client_cert) != 1 && SSL_CTX_use_certificate_file(ssl_ctx, client_cert, SSL_FILETYPE_PEM) != 1) { tls_show_errors(MSG_INFO, __func__, @@ -1837,6 +1926,8 @@ static int tls_connection_engine_ca_cert(void *_ssl_ctx, wpa_printf(MSG_DEBUG, "OpenSSL: %s - added CA certificate from engine " "to certificate store", __func__); SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); + conn->ca_cert_verify = 1; + return 0; #else /* OPENSSL_NO_ENGINE */ @@ -1900,10 +1991,6 @@ static int tls_connection_private_key(void *_ssl_ctx, "ASN1(EVP_PKEY_RSA) --> OK"); ok = 1; break; - } else { - tls_show_errors(MSG_DEBUG, __func__, - "SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA)" - " failed"); } if (SSL_use_PrivateKey_ASN1(EVP_PKEY_DSA, conn->ssl, @@ -1913,10 +2000,6 @@ static int tls_connection_private_key(void *_ssl_ctx, "ASN1(EVP_PKEY_DSA) --> OK"); ok = 1; break; - } else { - tls_show_errors(MSG_DEBUG, __func__, - "SSL_use_PrivateKey_ASN1(EVP_PKEY_DSA)" - " failed"); } if (SSL_use_RSAPrivateKey_ASN1(conn->ssl, @@ -1926,9 +2009,6 @@ static int tls_connection_private_key(void *_ssl_ctx, "SSL_use_RSAPrivateKey_ASN1 --> OK"); ok = 1; break; - } else { - tls_show_errors(MSG_DEBUG, __func__, - "SSL_use_RSAPrivateKey_ASN1 failed"); } if (tls_read_pkcs12_blob(ssl_ctx, conn->ssl, private_key_blob, @@ -1942,6 +2022,26 @@ static int tls_connection_private_key(void *_ssl_ctx, break; } +#ifdef ANDROID + if (!ok && private_key && + os_strncmp("keystore://", private_key, 11) == 0) { + BIO *bio = BIO_from_keystore(&private_key[11]); + EVP_PKEY *pkey = NULL; + if (bio) { + pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); + BIO_free(bio); + } + if (pkey) { + if (SSL_use_PrivateKey(conn->ssl, pkey) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: Private key " + "from keystore"); + ok = 1; + } + EVP_PKEY_free(pkey); + } + } +#endif /* ANDROID */ + while (!ok && private_key) { #ifndef OPENSSL_NO_STDIO if (SSL_use_PrivateKey_file(conn->ssl, private_key, @@ -1950,10 +2050,6 @@ static int tls_connection_private_key(void *_ssl_ctx, "SSL_use_PrivateKey_File (DER) --> OK"); ok = 1; break; - } else { - tls_show_errors(MSG_DEBUG, __func__, - "SSL_use_PrivateKey_File (DER) " - "failed"); } if (SSL_use_PrivateKey_file(conn->ssl, private_key, @@ -1962,10 +2058,6 @@ static int tls_connection_private_key(void *_ssl_ctx, "SSL_use_PrivateKey_File (PEM) --> OK"); ok = 1; break; - } else { - tls_show_errors(MSG_DEBUG, __func__, - "SSL_use_PrivateKey_File (PEM) " - "failed"); } #else /* OPENSSL_NO_STDIO */ wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", @@ -1991,15 +2083,15 @@ static int tls_connection_private_key(void *_ssl_ctx, } if (!ok) { - wpa_printf(MSG_INFO, "OpenSSL: Failed to load private key"); + tls_show_errors(MSG_INFO, __func__, + "Failed to load private key"); os_free(passwd); - ERR_clear_error(); return -1; } ERR_clear_error(); SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); os_free(passwd); - + if (!SSL_check_private_key(conn->ssl)) { tls_show_errors(MSG_INFO, __func__, "Private key failed " "verification"); @@ -2045,7 +2137,7 @@ static int tls_global_private_key(SSL_CTX *ssl_ctx, const char *private_key, os_free(passwd); ERR_clear_error(); SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); - + if (!SSL_CTX_check_private_key(ssl_ctx)) { tls_show_errors(MSG_INFO, __func__, "Private key failed verification"); @@ -2207,6 +2299,11 @@ static int tls_global_dh(SSL_CTX *ssl_ctx, const char *dh_file) int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, struct tls_keys *keys) { +#ifdef CONFIG_FIPS + wpa_printf(MSG_ERROR, "OpenSSL: TLS keys cannot be exported in FIPS " + "mode"); + return -1; +#else /* CONFIG_FIPS */ SSL *ssl; if (conn == NULL || keys == NULL) @@ -2224,6 +2321,7 @@ int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, keys->server_random_len = SSL3_RANDOM_SIZE; return 0; +#endif /* CONFIG_FIPS */ } @@ -2231,6 +2329,19 @@ int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, const char *label, int server_random_first, u8 *out, size_t out_len) { +#if OPENSSL_VERSION_NUMBER >= 0x10001000L + SSL *ssl; + if (conn == NULL) + return -1; + if (server_random_first) + return -1; + ssl = conn->ssl; + if (SSL_export_keying_material(ssl, out, out_len, label, + os_strlen(label), NULL, 0, 0) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: Using internal PRF"); + return 0; + } +#endif return -1; } @@ -2663,6 +2774,15 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } +#ifdef SSL_OP_NO_TICKET + if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET) + SSL_set_options(conn->ssl, SSL_OP_NO_TICKET); + else + SSL_clear_options(conn->ssl, SSL_OP_NO_TICKET); +#endif /* SSL_OP_NO_TICKET */ + + conn->flags = params->flags; + tls_get_errors(tls_ctx); return 0; @@ -2696,6 +2816,13 @@ int tls_global_set_params(void *tls_ctx, return -1; } +#ifdef SSL_OP_NO_TICKET + if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET) + SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET); + else + SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TICKET); +#endif /* SSL_OP_NO_TICKET */ + return 0; } @@ -2705,6 +2832,7 @@ int tls_connection_get_keyblock_size(void *tls_ctx, { const EVP_CIPHER *c; const EVP_MD *h; + int md_size; if (conn == NULL || conn->ssl == NULL || conn->ssl->enc_read_ctx == NULL || @@ -2718,9 +2846,20 @@ int tls_connection_get_keyblock_size(void *tls_ctx, #else h = conn->ssl->read_hash; #endif + if (h) + md_size = EVP_MD_size(h); +#if OPENSSL_VERSION_NUMBER >= 0x10000000L + else if (conn->ssl->s3) + md_size = conn->ssl->s3->tmp.new_mac_secret_size; +#endif + else + return -1; + wpa_printf(MSG_DEBUG, "OpenSSL: keyblock size: key_len=%d MD_size=%d " + "IV_len=%d", EVP_CIPHER_key_length(c), md_size, + EVP_CIPHER_iv_length(c)); return 2 * (EVP_CIPHER_key_length(c) + - EVP_MD_size(h) + + md_size + EVP_CIPHER_iv_length(c)); } @@ -2731,35 +2870,6 @@ unsigned int tls_capabilities(void *tls_ctx) } -int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn, - int tls_ia) -{ - return -1; -} - - -struct wpabuf * tls_connection_ia_send_phase_finished( - void *tls_ctx, struct tls_connection *conn, int final) -{ - return NULL; -} - - -int tls_connection_ia_final_phase_finished(void *tls_ctx, - struct tls_connection *conn) -{ - return -1; -} - - -int tls_connection_ia_permute_inner_secret(void *tls_ctx, - struct tls_connection *conn, - const u8 *key, size_t key_len) -{ - return -1; -} - - #if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) /* Pre-shared secred requires a patch to openssl, so this function is * commented out unless explicitly needed for EAP-FAST in order to be able to diff --git a/src/crypto/tls_schannel.c b/src/crypto/tls_schannel.c index 4a94e99119821..2c2daa8a804b7 100644 --- a/src/crypto/tls_schannel.c +++ b/src/crypto/tls_schannel.c @@ -2,14 +2,8 @@ * SSL/TLS interface functions for Microsoft Schannel * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ /* @@ -736,32 +730,3 @@ unsigned int tls_capabilities(void *tls_ctx) { return 0; } - - -int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn, - int tls_ia) -{ - return -1; -} - - -struct wpabuf * tls_connection_ia_send_phase_finished( - void *tls_ctx, struct tls_connection *conn, int final); -{ - return NULL; -} - - -int tls_connection_ia_final_phase_finished(void *tls_ctx, - struct tls_connection *conn) -{ - return -1; -} - - -int tls_connection_ia_permute_inner_secret(void *tls_ctx, - struct tls_connection *conn, - const u8 *key, size_t key_len) -{ - return -1; -} diff --git a/src/drivers/Apple80211.h b/src/drivers/Apple80211.h deleted file mode 100644 index 2a612e73083a6..0000000000000 --- a/src/drivers/Apple80211.h +++ /dev/null @@ -1,156 +0,0 @@ -#ifndef APPLE80211_H -#define APPLE80211_H - -/* - * Apple80211 framework definitions - * This is an undocumented interface and the definitions here are based on - * information from MacStumbler (http://www.macstumbler.com/Apple80211.h) and - * whatever related information can be found with google and experiments ;-). - */ - -typedef struct __WirelessRef *WirelessRef; -typedef SInt32 WirelessError; -#define errWirelessNoError 0 - -typedef struct WirelessInfo { - UInt16 link_qual; - UInt16 comms_qual; - UInt16 signal; - UInt16 noise; - UInt16 port_stat; - UInt16 client_mode; - UInt16 res1; - UInt16 power; - UInt16 res2; - UInt8 bssID[6]; - UInt8 ssid[34]; -} WirelessInfo; - -typedef struct WirelessInfo2 { - /* TODO - these are probably not in correct order or complete */ - WirelessInfo info1; - UInt8 macAddress[6]; -} WirelessInfo2; - -typedef struct WirelessNetworkInfo { - UInt16 channel; - UInt16 noise; - UInt16 signal; - UInt8 bssid[6]; - UInt16 beacon_int; - UInt16 capability; - UInt16 ssid_len; - UInt8 ssid[32]; -} WirelessNetworkInfo; - -typedef int wirelessKeyType; /* TODO */ - -int WirelessIsAvailable(void); -WirelessError WirelessAttach(WirelessRef *ref, UInt32 res); -WirelessError WirelessDetach(WirelessRef ref); -WirelessError WirelessPrivate(WirelessRef ref, void *in_ptr, int in_bytes, - void *out_ptr, int out_bytes); -WirelessError WirelessSetEnabled(WirelessRef ref, UInt8 enabled); -WirelessError WirelessGetEnabled(WirelessRef ref, UInt8 *enabled); -WirelessError WirelessSetPower(WirelessRef ref, UInt8 power); -WirelessError WirelessGetPower(WirelessRef ref, UInt8 *power); -WirelessError WirelessGetInfo(WirelessRef ref, WirelessInfo *info); -WirelessError WirelessGetInfo2(WirelessRef ref, WirelessInfo2 *info); -WirelessError WirelessScan(WirelessRef ref, CFArrayRef *results, - UInt32 strip_dups); -WirelessError WirelessScanSplit(WirelessRef ref, CFArrayRef *ap_results, - CFArrayRef *ibss_results, UInt32 strip_dups); -WirelessError WirelessDirectedScan(WirelessRef ref, CFArrayRef *results, - UInt32 strip_dups, CFStringRef ssid); -WirelessError WirelessDirectedScan2(WirelessRef ref, CFDataRef ssid, - UInt32 strip_dups, CFArrayRef *results); -WirelessError WirelessJoin(WirelessRef ref, CFStringRef ssid); -WirelessError WirelessJoinWEP(WirelessRef ref, CFStringRef ssid, - CFStringRef passwd); -WirelessError WirelessJoin8021x(WirelessRef ref, CFStringRef ssid); -/* - * Set WEP key - * ref: wireless reference from WirelessAttach() - * type: ? - * key_idx: 0..3 - * key_len: 13 for WEP-104 or 0 for clearing the key - * key: Pointer to the key or %NULL if key_len = 0 - */ -WirelessError WirelessSetKey(WirelessRef ref, wirelessKeyType type, - int key_idx, int key_len, - const unsigned char *key); -/* - * Set WPA key (e.g., PMK for 4-way handshake) - * ref: wireless reference from WirelessAttach() - * type: 0..4; 1 = PMK - * key_len: 16, 32, or 0 - * key: Pointer to the key or %NULL if key_len = 0 - */ -WirelessError WirelessSetWPAKey(WirelessRef ref, wirelessKeyType type, - int key_len, const unsigned char *key); -WirelessError WirelessAssociate(WirelessRef ref, int type, CFDataRef ssid, - CFStringRef key); -WirelessError WirelessAssociate2(WirelessRef ref, CFDictionaryRef scan_res, - CFStringRef key); -WirelessError WirelessDisassociate(WirelessRef ref); - -/* - * Get a copy of scan results for the given SSID - * The returned dictionary includes following entries: - * beaconInterval: CFNumber(kCFNumberSInt32Type) - * SSID: CFData buffer of the SSID - * isWPA: CFNumber(kCFNumberSInt32Type); 0 = not used, 1 = WPA, -128 = WPA2 - * name: Name of the network (SSID string) - * BSSID: CFData buffer of the BSSID - * channel: CFNumber(kCFNumberSInt32Type) - * signal: CFNumber(kCFNumberSInt32Type) - * appleIE: CFData - * WPSNOPINRequired: CFBoolean - * noise: CFNumber(kCFNumberSInt32Type) - * capability: CFNumber(kCFNumberSInt32Type) - * uniCipher: CFArray of CFNumber(kCFNumberSInt32Type) - * appleIE_Version: CFNumber(kCFNumberSInt32Type) - * appleIE_Robust: CFBoolean - * WPSConfigured: CFBoolean - * scanWasDirected: CFBoolean - * appleIE_Product: CFNumber(kCFNumberSInt32Type) - * authModes: CFArray of CFNumber(kCFNumberSInt32Type) - * multiCipher: CFNumber(kCFNumberSInt32Type) - */ -CFDictionaryRef WirelessSafeDirectedScanCopy(WirelessRef ref, CFDataRef ssid); - -/* - * Get information about the current association - * The returned dictionary includes following entries: - * keyData: CFData buffer of the key (e.g., 32-octet PSK) - * multiCipher: CFNumber(kCFNumberSInt32Type); 0 = none, 5 = CCMP? - * channel: CFNumber(kCFNumberSInt32Type) - * isIBSS: CFBoolean - * authMode: CFNumber(kCFNumberSInt32Type); 2 = WPA-Personal; 3 = open, - * 129 = WPA2-Enterprise - * isWPA: CFNumber(kCFNumberSInt32Type); 0 = not used, 1 = WPA, -128 == WPA2 - * SSID: CFData buffer of the SSID - * cipherMode: CFNumber(kCFNumberSInt32Type); 0 = none, 4 = CCMP? - */ -CFDictionaryRef WirelessGetAssociationInfo(WirelessRef ref); - -WirelessError WirelessConfigure(WirelessRef ref); - -/* - * Get ASP information - * The returned dictionary includes following entries: - * Version: version number (e.g., 3.0) - * Channel: channel (e.g., 1) - * Vendor: vendor (e.g., 2) - */ -CFDictionaryRef WirelessGetInfoASP(void); - -/* - * Get a copy of the interface dictionary - * The returned dictionary has a key,value pairs for wireless interfaces. - * The key is the interface name and the value is the driver identifier, e.g., - * en1: com.apple.driver.AirPort.Atheros - */ -CFDictionaryRef WirelessCopyInterfaceDict(void); - -#endif /* APPLE80211_H */ diff --git a/src/drivers/MobileApple80211.c b/src/drivers/MobileApple80211.c deleted file mode 100644 index ce004fe4c96f5..0000000000000 --- a/src/drivers/MobileApple80211.c +++ /dev/null @@ -1,189 +0,0 @@ -#include "includes.h" -#include <dlfcn.h> - -#include "common.h" - -#include <CoreFoundation/CoreFoundation.h> -#include "MobileApple80211.h" - -/* - * Code for dynamically loading Apple80211 functions from Aeropuerto to avoid - * having to link with full Preferences.framework. - */ - -static void *aeropuerto = NULL; - - -int _Apple80211Initialized(void) -{ - return aeropuerto ? 1 : 0; -} - - -static int (*__Apple80211Open)(Apple80211Ref *ctx) = NULL; - -int Apple80211Open(Apple80211Ref *ctx) -{ - return __Apple80211Open(ctx); -} - - -static int (*__Apple80211Close)(Apple80211Ref ctx) = NULL; - -int Apple80211Close(Apple80211Ref ctx) -{ - return __Apple80211Close(ctx); -} - - -static int (*__Apple80211GetIfListCopy)(Apple80211Ref handle, CFArrayRef *list) - = NULL; - -int Apple80211GetIfListCopy(Apple80211Ref handle, CFArrayRef *list) -{ - return __Apple80211GetIfListCopy(handle, list); -} - - -static int (*__Apple80211BindToInterface)(Apple80211Ref handle, - CFStringRef interface) = NULL; - -int Apple80211BindToInterface(Apple80211Ref handle, - CFStringRef interface) -{ - return __Apple80211BindToInterface(handle, interface); -} - - -static int (*__Apple80211GetInterfaceNameCopy)(Apple80211Ref handle, - CFStringRef *name) = NULL; - -int Apple80211GetInterfaceNameCopy(Apple80211Ref handle, - CFStringRef *name) -{ - return __Apple80211GetInterfaceNameCopy(handle, name); -} - - -static int (*__Apple80211GetInfoCopy)(Apple80211Ref handle, - CFDictionaryRef *info) = NULL; - -int Apple80211GetInfoCopy(Apple80211Ref handle, - CFDictionaryRef *info) -{ - return __Apple80211GetInfoCopy(handle, info); -} - - -static int (*__Apple80211GetPower)(Apple80211Ref handle, char *pwr) = NULL; - -int Apple80211GetPower(Apple80211Ref handle, char *pwr) -{ - return __Apple80211GetPower(handle, pwr); -} - - -static int (*__Apple80211SetPower)(Apple80211Ref handle, char pwr) = NULL; - -int Apple80211SetPower(Apple80211Ref handle, char pwr) -{ - return __Apple80211SetPower(handle, pwr); -} - - -static int (*__Apple80211Scan)(Apple80211Ref handle, CFArrayRef *list, - CFDictionaryRef parameters) = NULL; - -int Apple80211Scan(Apple80211Ref handle, CFArrayRef *list, - CFDictionaryRef parameters) -{ - return __Apple80211Scan(handle, list, parameters); -} - - -static int (*__Apple80211Associate)(Apple80211Ref handle, CFDictionaryRef bss, - CFStringRef password) = NULL; - -int Apple80211Associate(Apple80211Ref handle, CFDictionaryRef bss, - CFStringRef password) -{ - return __Apple80211Associate(handle, bss, password); -} - - -static int (*__Apple80211AssociateAndCopyInfo)(Apple80211Ref handle, - CFDictionaryRef bss, - CFStringRef password, - CFDictionaryRef *info) = - NULL; - -int Apple80211AssociateAndCopyInfo(Apple80211Ref handle, CFDictionaryRef bss, - CFStringRef password, CFDictionaryRef *info) -{ - return __Apple80211AssociateAndCopyInfo(handle, bss, password, info); -} - - -static int (*__Apple80211CopyValue)(Apple80211Ref handle, int field, - CFDictionaryRef arg2, void *value) = NULL; - -int Apple80211CopyValue(Apple80211Ref handle, int field, CFDictionaryRef arg2, - void *value) -{ - return __Apple80211CopyValue(handle, field, arg2, value); -} - - -#define DLSYM(s) \ -do { \ - __ ## s = dlsym(aeropuerto, #s); \ - if (__ ## s == NULL) { \ - wpa_printf(MSG_ERROR, "MobileApple80211: Could not resolve " \ - "symbol '" #s "' (%s)", dlerror()); \ - err = 1; \ - } \ -} while (0) - - -__attribute__ ((constructor)) -void _Apple80211_constructor(void) -{ - const char *fname = "/System/Library/SystemConfiguration/" - "Aeropuerto.bundle/Aeropuerto"; - int err = 0; - - aeropuerto = dlopen(fname, RTLD_LAZY); - if (!aeropuerto) { - wpa_printf(MSG_ERROR, "MobileApple80211: Failed to open %s " - "for symbols", fname); - return; - } - - DLSYM(Apple80211Open); - DLSYM(Apple80211Close); - DLSYM(Apple80211GetIfListCopy); - DLSYM(Apple80211BindToInterface); - DLSYM(Apple80211GetInterfaceNameCopy); - DLSYM(Apple80211GetInfoCopy); - DLSYM(Apple80211GetPower); - DLSYM(Apple80211SetPower); - DLSYM(Apple80211Scan); - DLSYM(Apple80211Associate); - DLSYM(Apple80211AssociateAndCopyInfo); - DLSYM(Apple80211CopyValue); - - if (err) { - dlclose(aeropuerto); - aeropuerto = NULL; - } -} - - -__attribute__ ((destructor)) -void _Apple80211_destructor(void) -{ - if (aeropuerto) { - dlclose(aeropuerto); - aeropuerto = NULL; - } -} diff --git a/src/drivers/MobileApple80211.h b/src/drivers/MobileApple80211.h deleted file mode 100644 index 64d439d660c8d..0000000000000 --- a/src/drivers/MobileApple80211.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef MOBILEAPPLE80211_H -#define MOBILEAPPLE80211_H - -/* - * MobileApple80211 interface for iPhone/iPod touch - * These functions are available from Aeropuerto. - */ - -struct Apple80211; -typedef struct Apple80211 *Apple80211Ref; - -int Apple80211Open(Apple80211Ref *ctx); -int Apple80211Close(Apple80211Ref ctx); -int Apple80211GetIfListCopy(Apple80211Ref handle, CFArrayRef *list); -int Apple80211BindToInterface(Apple80211Ref handle, - CFStringRef interface); -int Apple80211GetInterfaceNameCopy(Apple80211Ref handle, - CFStringRef *name); -int Apple80211GetInfoCopy(Apple80211Ref handle, - CFDictionaryRef *info); -int Apple80211GetPower(Apple80211Ref handle, char *pwr); -int Apple80211SetPower(Apple80211Ref handle, char pwr); - -/* parameters can be NULL; returns scan results in CFArrayRef *list; - * caller will need to free with CFRelease() */ -int Apple80211Scan(Apple80211Ref handle, CFArrayRef *list, - CFDictionaryRef parameters); - -int Apple80211Associate(Apple80211Ref handle, CFDictionaryRef bss, - CFStringRef password); -int Apple80211AssociateAndCopyInfo(Apple80211Ref handle, CFDictionaryRef bss, - CFStringRef password, - CFDictionaryRef *info); - -enum { - APPLE80211_VALUE_SSID = 1, - APPLE80211_VALUE_BSSID = 9 -}; - -int Apple80211CopyValue(Apple80211Ref handle, int field, CFDictionaryRef arg2, - void *value); - -#endif /* MOBILEAPPLE80211_H */ diff --git a/src/drivers/android_drv.h b/src/drivers/android_drv.h new file mode 100644 index 0000000000000..5906527a017e5 --- /dev/null +++ b/src/drivers/android_drv.h @@ -0,0 +1,60 @@ +/* + * Android driver interface + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + */ + +#ifndef ANDROID_DRV_H +#define ANDROID_DRV_H + +#define WPA_EVENT_DRIVER_STATE "CTRL-EVENT-DRIVER-STATE " + +#define MAX_SSID_LEN 32 + +#define MAX_DRV_CMD_SIZE 248 +#define DRV_NUMBER_SEQUENTIAL_ERRORS 4 + +#define WEXT_PNOSETUP_HEADER "PNOSETUP " +#define WEXT_PNOSETUP_HEADER_SIZE 9 +#define WEXT_PNO_TLV_PREFIX 'S' +#define WEXT_PNO_TLV_VERSION '1' +#define WEXT_PNO_TLV_SUBVERSION '2' +#define WEXT_PNO_TLV_RESERVED '0' +#define WEXT_PNO_VERSION_SIZE 4 +#define WEXT_PNO_AMOUNT 16 +#define WEXT_PNO_SSID_SECTION 'S' +/* SSID header size is SSID section type above + SSID length */ +#define WEXT_PNO_SSID_HEADER_SIZE 2 +#define WEXT_PNO_SCAN_INTERVAL_SECTION 'T' +#define WEXT_PNO_SCAN_INTERVAL_LENGTH 2 +#define WEXT_PNO_SCAN_INTERVAL 30 +/* Scan interval size is scan interval section type + scan interval length + * above */ +#define WEXT_PNO_SCAN_INTERVAL_SIZE (1 + WEXT_PNO_SCAN_INTERVAL_LENGTH) +#define WEXT_PNO_REPEAT_SECTION 'R' +#define WEXT_PNO_REPEAT_LENGTH 1 +#define WEXT_PNO_REPEAT 4 +/* Repeat section size is Repeat section type + Repeat value length above */ +#define WEXT_PNO_REPEAT_SIZE (1 + WEXT_PNO_REPEAT_LENGTH) +#define WEXT_PNO_MAX_REPEAT_SECTION 'M' +#define WEXT_PNO_MAX_REPEAT_LENGTH 1 +#define WEXT_PNO_MAX_REPEAT 3 +/* Max Repeat section size is Max Repeat section type + Max Repeat value length + * above */ +#define WEXT_PNO_MAX_REPEAT_SIZE (1 + WEXT_PNO_MAX_REPEAT_LENGTH) +/* This corresponds to the size of all sections expect SSIDs */ +#define WEXT_PNO_NONSSID_SECTIONS_SIZE \ +(WEXT_PNO_SCAN_INTERVAL_SIZE + WEXT_PNO_REPEAT_SIZE + WEXT_PNO_MAX_REPEAT_SIZE) +/* PNO Max command size is total of header, version, ssid and other sections + + * Null termination */ +#define WEXT_PNO_MAX_COMMAND_SIZE \ + (WEXT_PNOSETUP_HEADER_SIZE + WEXT_PNO_VERSION_SIZE \ + + WEXT_PNO_AMOUNT * (WEXT_PNO_SSID_HEADER_SIZE + MAX_SSID_LEN) \ + + WEXT_PNO_NONSSID_SECTIONS_SIZE + 1) + +#endif /* ANDROID_DRV_H */ diff --git a/src/drivers/driver.h b/src/drivers/driver.h index fa49da454e83a..7ee71aaac0579 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -1,15 +1,9 @@ /* * Driver interface definition - * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-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 - * published 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. * * This file defines a driver interface used by both %wpa_supplicant and * hostapd. The first part of the file defines data structures used in various @@ -31,6 +25,9 @@ #define HOSTAPD_CHAN_PASSIVE_SCAN 0x00000002 #define HOSTAPD_CHAN_NO_IBSS 0x00000004 #define HOSTAPD_CHAN_RADAR 0x00000008 +#define HOSTAPD_CHAN_HT40PLUS 0x00000010 +#define HOSTAPD_CHAN_HT40MINUS 0x00000020 +#define HOSTAPD_CHAN_HT40 0x00000040 /** * struct hostapd_channel_data - Channel information @@ -44,7 +41,7 @@ struct hostapd_channel_data { /** * freq - Frequency in MHz */ - short freq; + int freq; /** * flag - Channel flags (HOSTAPD_CHAN_*) @@ -57,6 +54,8 @@ struct hostapd_channel_data { u8 max_tx_power; }; +#define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0) + /** * struct hostapd_hw_modes - Supported hardware mode information */ @@ -100,6 +99,18 @@ struct hostapd_hw_modes { * a_mpdu_params - A-MPDU (IEEE 802.11n) parameters */ u8 a_mpdu_params; + + /** + * vht_capab - VHT (IEEE 802.11ac) capabilities + */ + u32 vht_capab; + + /** + * vht_mcs_set - VHT MCS (IEEE 802.11ac) rate parameters + */ + u8 vht_mcs_set[8]; + + unsigned int flags; /* HOSTAPD_MODE_FLAG_* */ }; @@ -192,7 +203,7 @@ struct wpa_interface_info { const char *drv_name; }; -#define WPAS_MAX_SCAN_SSIDS 4 +#define WPAS_MAX_SCAN_SSIDS 16 /** * struct wpa_driver_scan_params - Scan parameters @@ -261,6 +272,24 @@ struct wpa_driver_scan_params { * num_filter_ssids - Number of entries in filter_ssids array */ size_t num_filter_ssids; + + /** + * filter_rssi - Filter by RSSI + * + * The driver may filter scan results in firmware to reduce host + * wakeups and thereby save power. Specify the RSSI threshold in s32 + * dBm. + */ + s32 filter_rssi; + + /** + * p2p_probe - Used to disable CCK (802.11b) rates for P2P probes + * + * When set, the driver is expected to remove rates 1, 2, 5.5, and 11 + * Mbps from the support rates element(s) in the Probe Request frames + * and not to transmit the frames at any of those rates. + */ + u8 p2p_probe; }; /** @@ -279,6 +308,22 @@ struct wpa_driver_auth_params { size_t wep_key_len[4]; int wep_tx_keyidx; int local_state_change; + + /** + * p2p - Whether this connection is a P2P group + */ + int p2p; + + const u8 *sae_data; + size_t sae_data_len; + +}; + +enum wps_mode { + WPS_MODE_NONE /* no WPS provisioning being used */, + WPS_MODE_OPEN /* WPS provisioning with AP that is in open mode */, + WPS_MODE_PRIVACY /* WPS provisioning with AP that is using protection + */ }; /** @@ -310,6 +355,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 @@ -335,6 +387,11 @@ struct wpa_driver_associate_params { size_t wpa_ie_len; /** + * wpa_proto - Bitfield of WPA_PROTO_* values to indicate WPA/WPA2 + */ + unsigned int wpa_proto; + + /** * pairwise_suite - Selected pairwise cipher suite * * This is usually ignored if @wpa_ie is used. @@ -460,6 +517,237 @@ struct wpa_driver_associate_params { * association. */ const u8 *prev_bssid; + + /** + * wps - WPS mode + * + * If the driver needs to do special configuration for WPS association, + * this variable provides more information on what type of association + * is being requested. Most drivers should not need ot use this. + */ + enum wps_mode wps; + + /** + * p2p - Whether this connection is a P2P group + */ + int p2p; + + /** + * uapsd - UAPSD parameters for the network + * -1 = do not change defaults + * AP mode: 1 = enabled, 0 = disabled + * STA mode: bits 0..3 UAPSD enabled for VO,VI,BK,BE + */ + int uapsd; + + /** + * fixed_bssid - Whether to force this BSSID in IBSS mode + * 1 = Fix this BSSID and prevent merges. + * 0 = Do not fix BSSID. + */ + int fixed_bssid; + + /** + * disable_ht - Disable HT (IEEE 802.11n) for this connection + */ + int disable_ht; + + /** + * HT Capabilities over-rides. Only bits set in the mask will be used, + * and not all values are used by the kernel anyway. Currently, MCS, + * MPDU and MSDU fields are used. + */ + const u8 *htcaps; /* struct ieee80211_ht_capabilities * */ + const u8 *htcaps_mask; /* struct ieee80211_ht_capabilities * */ +}; + +enum hide_ssid { + NO_SSID_HIDING, + HIDDEN_SSID_ZERO_LEN, + HIDDEN_SSID_ZERO_CONTENTS +}; + +struct wpa_driver_ap_params { + /** + * head - Beacon head from IEEE 802.11 header to IEs before TIM IE + */ + const u8 *head; + + /** + * head_len - Length of the head buffer in octets + */ + size_t head_len; + + /** + * tail - Beacon tail following TIM IE + */ + const u8 *tail; + + /** + * tail_len - Length of the tail buffer in octets + */ + size_t tail_len; + + /** + * dtim_period - DTIM period + */ + int dtim_period; + + /** + * beacon_int - Beacon interval + */ + int beacon_int; + + /** + * basic_rates: -1 terminated array of basic rates in 100 kbps + * + * This parameter can be used to set a specific basic rate set for the + * BSS. If %NULL, default basic rate set is used. + */ + int *basic_rates; + + /** + * proberesp - Probe Response template + * + * This is used by drivers that reply to Probe Requests internally in + * AP mode and require the full Probe Response template. + */ + const u8 *proberesp; + + /** + * proberesp_len - Length of the proberesp buffer in octets + */ + size_t proberesp_len; + + /** + * ssid - The SSID to use in Beacon/Probe Response frames + */ + const u8 *ssid; + + /** + * ssid_len - Length of the SSID (1..32) + */ + size_t ssid_len; + + /** + * hide_ssid - Whether to hide the SSID + */ + enum hide_ssid hide_ssid; + + /** + * pairwise_ciphers - WPA_CIPHER_* bitfield + */ + unsigned int pairwise_ciphers; + + /** + * group_cipher - WPA_CIPHER_* + */ + unsigned int group_cipher; + + /** + * key_mgmt_suites - WPA_KEY_MGMT_* bitfield + */ + unsigned int key_mgmt_suites; + + /** + * auth_algs - WPA_AUTH_ALG_* bitfield + */ + unsigned int auth_algs; + + /** + * wpa_version - WPA_PROTO_* bitfield + */ + unsigned int wpa_version; + + /** + * privacy - Whether privacy is used in the BSS + */ + int privacy; + + /** + * beacon_ies - WPS/P2P IE(s) for Beacon frames + * + * This is used to add IEs like WPS IE and P2P IE by drivers that do + * not use the full Beacon template. + */ + const struct wpabuf *beacon_ies; + + /** + * proberesp_ies - P2P/WPS IE(s) for Probe Response frames + * + * This is used to add IEs like WPS IE and P2P IE by drivers that + * reply to Probe Request frames internally. + */ + const struct wpabuf *proberesp_ies; + + /** + * assocresp_ies - WPS IE(s) for (Re)Association Response frames + * + * This is used to add IEs like WPS IE by drivers that reply to + * (Re)Association Request frames internally. + */ + const struct wpabuf *assocresp_ies; + + /** + * isolate - Whether to isolate frames between associated stations + * + * If this is non-zero, the AP is requested to disable forwarding of + * frames between associated stations. + */ + int isolate; + + /** + * cts_protect - Whether CTS protection is enabled + */ + int cts_protect; + + /** + * preamble - Whether short preamble is enabled + */ + int preamble; + + /** + * short_slot_time - Whether short slot time is enabled + * + * 0 = short slot time disable, 1 = short slot time enabled, -1 = do + * not set (e.g., when 802.11g mode is not in use) + */ + int short_slot_time; + + /** + * ht_opmode - HT operation mode or -1 if HT not in use + */ + int ht_opmode; + + /** + * interworking - Whether Interworking is enabled + */ + int interworking; + + /** + * hessid - Homogeneous ESS identifier or %NULL if not set + */ + const u8 *hessid; + + /** + * access_network_type - Access Network Type (0..15) + * + * This is used for filtering Probe Request frames when Interworking is + * enabled. + */ + u8 access_network_type; + + /** + * ap_max_inactivity - Timeout in seconds to detect STA's inactivity + * + * This is used by driver which advertises this capability. + */ + int ap_max_inactivity; + + /** + * disable_dgaf - Whether group-addressed frames are disabled + */ + int disable_dgaf; }; /** @@ -473,12 +761,15 @@ struct wpa_driver_capa { #define WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE 0x00000010 #define WPA_DRIVER_CAPA_KEY_MGMT_FT 0x00000020 #define WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK 0x00000040 +#define WPA_DRIVER_CAPA_KEY_MGMT_WAPI_PSK 0x00000080 unsigned int key_mgmt; #define WPA_DRIVER_CAPA_ENC_WEP40 0x00000001 #define WPA_DRIVER_CAPA_ENC_WEP104 0x00000002 #define WPA_DRIVER_CAPA_ENC_TKIP 0x00000004 #define WPA_DRIVER_CAPA_ENC_CCMP 0x00000008 +#define WPA_DRIVER_CAPA_ENC_WEP128 0x00000010 +#define WPA_DRIVER_CAPA_ENC_GCMP 0x00000020 unsigned int enc; #define WPA_DRIVER_AUTH_OPEN 0x00000001 @@ -490,7 +781,7 @@ struct wpa_driver_capa { #define WPA_DRIVER_FLAGS_DRIVER_IE 0x00000001 /* Driver needs static WEP key setup after association command */ #define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC 0x00000002 -#define WPA_DRIVER_FLAGS_USER_SPACE_MLME 0x00000004 +/* unused: 0x00000004 */ /* Driver takes care of RSN 4-way handshake internally; PMK is configured with * struct wpa_driver_ops::set_key using alg = WPA_ALG_PMK */ #define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE 0x00000008 @@ -502,14 +793,86 @@ struct wpa_driver_capa { #define WPA_DRIVER_FLAGS_AP 0x00000040 /* Driver needs static WEP key setup after association has been completed */ #define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE 0x00000080 +/* Driver takes care of P2P management operations */ +#define WPA_DRIVER_FLAGS_P2P_MGMT 0x00000100 +/* Driver supports concurrent P2P operations */ +#define WPA_DRIVER_FLAGS_P2P_CONCURRENT 0x00000200 +/* + * Driver uses the initial interface as a dedicated management interface, i.e., + * it cannot be used for P2P group operations or non-P2P purposes. + */ +#define WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE 0x00000400 +/* This interface is P2P capable (P2P Device, GO, or P2P Client */ +#define WPA_DRIVER_FLAGS_P2P_CAPABLE 0x00000800 +/* Driver supports concurrent operations on multiple channels */ +#define WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT 0x00001000 +/* + * Driver uses the initial interface for P2P management interface and non-P2P + * purposes (e.g., connect to infra AP), but this interface cannot be used for + * P2P group operations. + */ +#define WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P 0x00002000 +/* + * Driver is known to use sane error codes, i.e., when it indicates that + * something (e.g., association) fails, there was indeed a failure and the + * operation does not end up getting completed successfully later. + */ +#define WPA_DRIVER_FLAGS_SANE_ERROR_CODES 0x00004000 +/* Driver supports off-channel TX */ +#define WPA_DRIVER_FLAGS_OFFCHANNEL_TX 0x00008000 +/* Driver indicates TX status events for EAPOL Data frames */ +#define WPA_DRIVER_FLAGS_EAPOL_TX_STATUS 0x00010000 +/* Driver indicates TX status events for Deauth/Disassoc frames */ +#define WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS 0x00020000 +/* Driver supports roaming (BSS selection) in firmware */ +#define WPA_DRIVER_FLAGS_BSS_SELECTION 0x00040000 +/* Driver supports operating as a TDLS peer */ +#define WPA_DRIVER_FLAGS_TDLS_SUPPORT 0x00080000 +/* Driver requires external TDLS setup/teardown/discovery */ +#define WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP 0x00100000 +/* Driver indicates support for Probe Response offloading in AP mode */ +#define WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD 0x00200000 +/* Driver supports U-APSD in AP mode */ +#define WPA_DRIVER_FLAGS_AP_UAPSD 0x00400000 +/* Driver supports inactivity timer in AP mode */ +#define WPA_DRIVER_FLAGS_INACTIVITY_TIMER 0x00800000 +/* Driver expects user space implementation of MLME in AP mode */ +#define WPA_DRIVER_FLAGS_AP_MLME 0x01000000 +/* Driver supports SAE with user space SME */ +#define WPA_DRIVER_FLAGS_SAE 0x02000000 +/* Driver makes use of OBSS scan mechanism in wpa_supplicant */ +#define WPA_DRIVER_FLAGS_OBSS_SCAN 0x04000000 unsigned int flags; int max_scan_ssids; + int max_sched_scan_ssids; + int sched_scan_supported; + int max_match_sets; /** * max_remain_on_chan - Maximum remain-on-channel duration in msec */ unsigned int max_remain_on_chan; + + /** + * max_stations - Maximum number of associated stations the driver + * supports in AP mode + */ + unsigned int max_stations; + + /** + * probe_resp_offloads - Bitmap of supported protocols by the driver + * for Probe Response offloading. + */ +/* Driver Probe Response offloading support for WPS ver. 1 */ +#define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS 0x00000001 +/* Driver Probe Response offloading support for WPS ver. 2 */ +#define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2 0x00000002 +/* Driver Probe Response offloading support for P2P */ +#define WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P 0x00000004 +/* Driver Probe Response offloading support for IEEE 802.11u (Interworking) */ +#define WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING 0x00000008 + unsigned int probe_resp_offloads; }; @@ -535,6 +898,9 @@ struct hostapd_sta_add_params { size_t supp_rates_len; u16 listen_interval; const struct ieee80211_ht_capabilities *ht_capabilities; + u32 flags; /* bitmask of WPA_STA_* flags */ + int set; /* Set STA parameters instead of add */ + u8 qosinfo; }; struct hostapd_freq_params { @@ -567,9 +933,26 @@ enum wpa_driver_if_type { * This interface has its own address and Beacon frame. */ WPA_IF_AP_BSS, + + /** + * WPA_IF_P2P_GO - P2P Group Owner + */ + WPA_IF_P2P_GO, + + /** + * WPA_IF_P2P_CLIENT - P2P Client + */ + WPA_IF_P2P_CLIENT, + + /** + * WPA_IF_P2P_GROUP - P2P Group interface (will become either + * WPA_IF_P2P_GO or WPA_IF_P2P_CLIENT, but the role is not yet known) + */ + WPA_IF_P2P_GROUP }; struct wpa_init_params { + void *global_priv; const u8 *bssid; const char *ifname; const u8 *ssid; @@ -595,12 +978,63 @@ struct wpa_bss_params { int wpa_pairwise; int wpa_key_mgmt; int rsn_preauth; + enum mfp_options ieee80211w; }; #define WPA_STA_AUTHORIZED BIT(0) #define WPA_STA_WMM BIT(1) #define WPA_STA_SHORT_PREAMBLE BIT(2) #define WPA_STA_MFP BIT(3) +#define WPA_STA_TDLS_PEER BIT(4) + +/** + * struct p2p_params - P2P parameters for driver-based P2P management + */ +struct p2p_params { + const char *dev_name; + u8 pri_dev_type[8]; +#define DRV_MAX_SEC_DEV_TYPES 5 + u8 sec_dev_type[DRV_MAX_SEC_DEV_TYPES][8]; + size_t num_sec_dev_types; +}; + +enum tdls_oper { + TDLS_DISCOVERY_REQ, + TDLS_SETUP, + TDLS_TEARDOWN, + TDLS_ENABLE_LINK, + TDLS_DISABLE_LINK, + TDLS_ENABLE, + TDLS_DISABLE +}; + +enum wnm_oper { + WNM_SLEEP_ENTER_CONFIRM, + WNM_SLEEP_ENTER_FAIL, + WNM_SLEEP_EXIT_CONFIRM, + WNM_SLEEP_EXIT_FAIL, + WNM_SLEEP_TFS_REQ_IE_ADD, /* STA requests driver to add TFS req IE */ + WNM_SLEEP_TFS_REQ_IE_NONE, /* STA requests empty TFS req IE */ + WNM_SLEEP_TFS_REQ_IE_SET, /* AP requests driver to set TFS req IE for + * a STA */ + WNM_SLEEP_TFS_RESP_IE_ADD, /* AP requests driver to add TFS resp IE + * for a STA */ + WNM_SLEEP_TFS_RESP_IE_NONE, /* AP requests empty TFS resp IE */ + WNM_SLEEP_TFS_RESP_IE_SET, /* AP requests driver to set TFS resp IE + * for a STA */ + WNM_SLEEP_TFS_IE_DEL /* AP delete the TFS IE */ +}; + +/** + * struct wpa_signal_info - Information about channel signal quality + */ +struct wpa_signal_info { + u32 frequency; + int above_threshold; + int current_signal; + int current_noise; + int current_txrate; +}; /** * struct wpa_driver_ops - Driver interface API definition @@ -650,10 +1084,15 @@ struct wpa_driver_ops { * @ifname: Interface name (for multi-SSID/VLAN support) * @priv: private driver interface data * @alg: encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP, - * %WPA_ALG_TKIP, %WPA_ALG_CCMP, %WPA_ALG_IGTK, %WPA_ALG_PMK); + * %WPA_ALG_TKIP, %WPA_ALG_CCMP, %WPA_ALG_IGTK, %WPA_ALG_PMK, + * %WPA_ALG_GCMP); * %WPA_ALG_NONE clears the key. - * @addr: address of the peer STA or ff:ff:ff:ff:ff:ff for - * broadcast/default keys + * @addr: Address of the peer STA (BSSID of the current AP when setting + * pairwise key in station mode), ff:ff:ff:ff:ff:ff for + * broadcast keys, %NULL for default keys that are used both for + * broadcast and unicast; when clearing keys, %NULL is used to + * indicate that both the broadcast-only and default key of the + * specified key index is to be cleared * @key_idx: key index (0..3), usually 0 for unicast keys; 0..4095 for * IGTK * @set_tx: configure this key as the default Tx key (only used when @@ -661,13 +1100,13 @@ struct wpa_driver_ops { * @seq: sequence number/packet number, seq_len octets, the next * packet number to be used for in replay protection; configured * for Rx keys (in most cases, this is only used with broadcast - * keys and set to zero for unicast keys) + * keys and set to zero for unicast keys); %NULL if not set * @seq_len: length of the seq, depends on the algorithm: - * TKIP: 6 octets, CCMP: 6 octets, IGTK: 6 octets + * TKIP: 6 octets, CCMP/GCMP: 6 octets, IGTK: 6 octets * @key: key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key, * 8-byte Rx Mic Key * @key_len: length of the key buffer in octets (WEP: 5 or 13, - * TKIP: 32, CCMP: 16, IGTK: 16) + * TKIP: 32, CCMP/GCMP: 16, IGTK: 16) * * Returns: 0 on success, -1 on failure * @@ -684,7 +1123,7 @@ struct wpa_driver_ops { * Please note that TKIP keys include separate TX and RX MIC keys and * some drivers may expect them in different order than wpa_supplicant * is using. If the TX/RX keys are swapped, all TKIP encrypted packets - * will tricker Michael MIC errors. This can be fixed by changing the + * will trigger Michael MIC errors. This can be fixed by changing the * order of MIC keys by swapping te bytes 16..23 and 24..31 of the key * in driver_*.c set_key() implementation, see driver_ndis.c for an * example on how this can be done. @@ -764,17 +1203,6 @@ struct wpa_driver_ops { int (*deauthenticate)(void *priv, const u8 *addr, int reason_code); /** - * disassociate - Request driver to disassociate - * @priv: private driver interface data - * @addr: peer address (BSSID of the AP) - * @reason_code: 16-bit reason code to be sent in the disassociation - * frame - * - * Returns: 0 on success, -1 on failure - */ - int (*disassociate)(void *priv, const u8 *addr, int reason_code); - - /** * associate - Request driver to associate * @priv: private driver interface data * @params: association parameters @@ -951,91 +1379,21 @@ struct wpa_driver_ops { * flags: Variable for returning hardware feature flags * Returns: Pointer to allocated hardware data on success or %NULL on * failure. Caller is responsible for freeing this. - * - * This function is only needed for drivers that export MLME - * (management frame processing) to %wpa_supplicant or hostapd. */ struct hostapd_hw_modes * (*get_hw_feature_data)(void *priv, u16 *num_modes, u16 *flags); /** - * set_channel - Set channel - * @priv: Private driver interface data - * @phymode: HOSTAPD_MODE_IEEE80211B, .. - * @chan: IEEE 802.11 channel number - * @freq: Frequency of the channel in MHz - * Returns: 0 on success, -1 on failure - * - * This function is only needed for drivers that export MLME - * (management frame processing) to wpa_supplicant. - */ - int (*set_channel)(void *priv, enum hostapd_hw_mode phymode, int chan, - int freq); - - /** - * set_ssid - Set SSID - * @priv: Private driver interface data - * @ssid: SSID - * @ssid_len: SSID length - * Returns: 0 on success, -1 on failure - * - * This function is only needed for drivers that export MLME - * (management frame processing) to wpa_supplicant. - */ - int (*set_ssid)(void *priv, const u8 *ssid, size_t ssid_len); - - /** - * set_bssid - Set BSSID - * @priv: Private driver interface data - * @bssid: BSSID - * Returns: 0 on success, -1 on failure - * - * This function is only needed for drivers that export MLME - * (management frame processing) to wpa_supplicant. - */ - int (*set_bssid)(void *priv, const u8 *bssid); - - /** * send_mlme - Send management frame from MLME * @priv: Private driver interface data * @data: IEEE 802.11 management frame with IEEE 802.11 header * @data_len: Size of the management frame + * @noack: Do not wait for this frame to be acked (disable retries) * Returns: 0 on success, -1 on failure - * - * This function is only needed for drivers that export MLME - * (management frame processing) to wpa_supplicant. */ - int (*send_mlme)(void *priv, const u8 *data, size_t data_len); - - /** - * mlme_add_sta - Add a STA entry into the driver/netstack - * @priv: Private driver interface data - * @addr: MAC address of the STA (e.g., BSSID of the AP) - * @supp_rates: Supported rate set (from (Re)AssocResp); in IEEE 802.11 - * format (one octet per rate, 1 = 0.5 Mbps) - * @supp_rates_len: Number of entries in supp_rates - * Returns: 0 on success, -1 on failure - * - * This function is only needed for drivers that export MLME - * (management frame processing) to wpa_supplicant. When the MLME code - * completes association with an AP, this function is called to - * configure the driver/netstack with a STA entry for data frame - * processing (TX rate control, encryption/decryption). - */ - int (*mlme_add_sta)(void *priv, const u8 *addr, const u8 *supp_rates, - size_t supp_rates_len); - - /** - * mlme_remove_sta - Remove a STA entry from the driver/netstack - * @priv: Private driver interface data - * @addr: MAC address of the STA (e.g., BSSID of the AP) - * Returns: 0 on success, -1 on failure - * - * This function is only needed for drivers that export MLME - * (management frame processing) to wpa_supplicant. - */ - int (*mlme_remove_sta)(void *priv, const u8 *addr); + int (*send_mlme)(void *priv, const u8 *data, size_t data_len, + int noack); /** * update_ft_ies - Update FT (IEEE 802.11r) IEs @@ -1164,24 +1522,25 @@ struct wpa_driver_ops { struct wpa_driver_auth_params *params); /** - * set_beacon - Set Beacon frame template + * set_ap - Set Beacon and Probe Response information for AP mode * @priv: Private driver interface data - * @head: Beacon head from IEEE 802.11 header to IEs before TIM IE - * @head_len: Length of the head buffer in octets - * @tail: Beacon tail following TIM IE - * @tail_len: Length of the tail buffer in octets - * @dtim_period: DTIM period - * @beacon_int: Beacon interval - * Returns: 0 on success, -1 on failure + * @params: Parameters to use in AP mode * - * This function is used to configure Beacon template for the driver in + * This function is used to configure Beacon template and/or extra IEs + * to add for Beacon and Probe Response frames for the driver in * AP mode. The driver is responsible for building the full Beacon * frame by concatenating the head part with TIM IE generated by the - * driver/firmware and finishing with the tail part. + * driver/firmware and finishing with the tail part. Depending on the + * driver architectue, this can be done either by using the full + * template or the set of additional IEs (e.g., WPS and P2P IE). + * Similarly, Probe Response processing depends on the driver design. + * If the driver (or firmware) takes care of replying to Probe Request + * frames, the extra IEs provided here needs to be added to the Probe + * Response frames. + * + * Returns: 0 on success, -1 on failure */ - int (*set_beacon)(void *priv, const u8 *head, size_t head_len, - const u8 *tail, size_t tail_len, int dtim_period, - int beacon_int); + int (*set_ap)(void *priv, struct wpa_driver_ap_params *params); /** * hapd_init - Initialize driver interface (hostapd only) @@ -1190,7 +1549,7 @@ struct wpa_driver_ops { * Returns: Pointer to private data, %NULL on failure * * This function is used instead of init() or init2() when the driver - * wrapper is used withh hostapd. + * wrapper is used with hostapd. */ void * (*hapd_init)(struct hostapd_data *hapd, struct wpa_init_params *params); @@ -1210,8 +1569,10 @@ struct wpa_driver_ops { * This is an optional function to configure the kernel driver to * enable/disable IEEE 802.1X support and set WPA/WPA2 parameters. This * can be left undefined (set to %NULL) if IEEE 802.1X support is - * always enabled and the driver uses set_beacon() to set WPA/RSN IE + * always enabled and the driver uses set_ap() to set WPA/RSN IE * for Beacon frames. + * + * DEPRECATED - use set_ap() instead */ int (*set_ieee8021x)(void *priv, struct wpa_bss_params *params); @@ -1223,7 +1584,9 @@ struct wpa_driver_ops { * * This is an optional function to configure privacy field in the * kernel driver for Beacon frames. This can be left undefined (set to - * %NULL) if the driver uses the Beacon template from set_beacon(). + * %NULL) if the driver uses the Beacon template from set_ap(). + * + * DEPRECATED - use set_ap() instead */ int (*set_privacy)(void *priv, int enabled); @@ -1237,9 +1600,9 @@ struct wpa_driver_ops { * Returns: 0 on success, -1 on failure * * This function is used to fetch the last used TSC/packet number for - * a TKIP, CCMP, or BIP/IGTK key. It is mainly used with group keys, so - * there is no strict requirement on implementing support for unicast - * keys (i.e., addr != %NULL). + * a TKIP, CCMP, GCMP, or BIP/IGTK key. It is mainly used with group + * keys, so there is no strict requirement on implementing support for + * unicast keys (i.e., addr != %NULL). */ int (*get_seqnum)(const char *ifname, void *priv, const u8 *addr, int idx, u8 *seq); @@ -1265,12 +1628,14 @@ struct wpa_driver_ops { * This is an optional function to add information elements in the * kernel driver for Beacon and Probe Response frames. This can be left * undefined (set to %NULL) if the driver uses the Beacon template from - * set_beacon(). + * set_ap(). + * + * DEPRECATED - use set_ap() instead */ int (*set_generic_elem)(void *priv, const u8 *elem, size_t elem_len); /** - * read_sta_data - Fetch station data (AP only) + * read_sta_data - Fetch station data * @priv: Private driver interface data * @data: Buffer for returning station information * @addr: MAC address of the station @@ -1287,12 +1652,13 @@ struct wpa_driver_ops { * @data_len: Length of the EAPOL packet in octets * @encrypt: Whether the frame should be encrypted * @own_addr: Source MAC address + * @flags: WPA_STA_* flags for the destination station * * Returns: 0 on success, -1 on failure */ int (*hapd_send_eapol)(void *priv, const u8 *addr, const u8 *data, size_t data_len, int encrypt, - const u8 *own_addr); + const u8 *own_addr, u32 flags); /** * sta_deauth - Deauthenticate a station (AP only) @@ -1338,8 +1704,7 @@ struct wpa_driver_ops { * Returns: Length of the SSID on success, -1 on failure * * This function need not be implemented if the driver uses Beacon - * template from set_beacon() and does not reply to Probe Request - * frames. + * template from set_ap() and does not reply to Probe Request frames. */ int (*hapd_get_ssid)(void *priv, u8 *buf, int len); @@ -1349,6 +1714,8 @@ struct wpa_driver_ops { * @buf: SSID * @len: Length of the SSID in octets * Returns: 0 on success, -1 on failure + * + * DEPRECATED - use set_ap() instead */ int (*hapd_set_ssid)(void *priv, const u8 *buf, int len); @@ -1372,6 +1739,9 @@ struct wpa_driver_ops { * This function is used to add a station entry to the driver once the * station has completed association. This is only used if the driver * does not take care of association processing. + * + * With TDLS, this function is also used to add or set (params->set 1) + * TDLS peer entries. */ int (*sta_add)(void *priv, struct hostapd_sta_add_params *params); @@ -1428,44 +1798,9 @@ struct wpa_driver_ops { int total_flags, int flags_or, int flags_and); /** - * set_rate_sets - Set supported and basic rate sets (AP only) - * @priv: Private driver interface data - * @supp_rates: -1 terminated array of supported rates in 100 kbps - * @basic_rates: -1 terminated array of basic rates in 100 kbps - * @mode: hardware mode (HOSTAPD_MODE_*) - * Returns: 0 on success, -1 on failure - */ - int (*set_rate_sets)(void *priv, int *supp_rates, int *basic_rates, - int mode); - - /** - * set_cts_protect - Set CTS protection mode (AP only) - * @priv: Private driver interface data - * @value: Whether CTS protection is enabled - * Returns: 0 on success, -1 on failure - */ - int (*set_cts_protect)(void *priv, int value); - - /** - * set_preamble - Set preamble mode (AP only) - * @priv: Private driver interface data - * @value: Whether short preamble is enabled - * Returns: 0 on success, -1 on failure - */ - int (*set_preamble)(void *priv, int value); - - /** - * set_short_slot_time - Set short slot time (AP only) - * @priv: Private driver interface data - * @value: Whether short slot time is enabled - * Returns: 0 on success, -1 on failure - */ - int (*set_short_slot_time)(void *priv, int value); - - /** * set_tx_queue_params - Set TX queue parameters * @priv: Private driver interface data - * @queue: Queue number + * @queue: Queue number (0 = VO, 1 = VI, 2 = BE, 3 = BK) * @aifs: AIFS * @cw_min: cwMin * @cw_max: cwMax @@ -1475,17 +1810,6 @@ struct wpa_driver_ops { int cw_max, int burst_time); /** - * valid_bss_mask - Validate BSSID mask - * @priv: Private driver interface data - * @addr: Address - * @mask: Mask - * Returns: 0 if mask is valid, -1 if mask is not valid, 1 if mask can - * be used, but the main interface address must be the first address in - * the block if mask is applied - */ - int (*valid_bss_mask)(void *priv, const u8 *addr, const u8 *mask); - - /** * if_add - Add a virtual interface * @priv: Private driver interface data * @type: Interface type @@ -1500,11 +1824,13 @@ struct wpa_driver_ops { * @if_addr: Buffer for returning the allocated interface address * (this may differ from the requested addr if the driver cannot * change interface address) + * @bridge: Bridge interface to use or %NULL if no bridge configured * Returns: 0 on success, -1 on failure */ int (*if_add)(void *priv, enum wpa_driver_if_type type, const char *ifname, const u8 *addr, void *bss_ctx, - void **drv_priv, char *force_ifname, u8 *if_addr); + void **drv_priv, char *force_ifname, u8 *if_addr, + const char *bridge); /** * if_remove - Remove a virtual interface @@ -1578,33 +1904,36 @@ struct wpa_driver_ops { int (*set_radius_acl_expire)(void *priv, const u8 *mac); /** - * set_ht_params - Set HT parameters (AP only) - * @priv: Private driver interface data - * @ht_capab: HT Capabilities IE - * @ht_capab_len: Length of ht_capab in octets - * @ht_oper: HT Operation IE - * @ht_oper_len: Length of ht_oper in octets - * Returns: 0 on success, -1 on failure - */ - int (*set_ht_params)(void *priv, - const u8 *ht_capab, size_t ht_capab_len, - const u8 *ht_oper, size_t ht_oper_len); - - /** * set_ap_wps_ie - Add WPS IE(s) into Beacon/Probe Response frames (AP) * @priv: Private driver interface data * @beacon: WPS IE(s) for Beacon frames or %NULL to remove extra IE(s) * @proberesp: WPS IE(s) for Probe Response frames or %NULL to remove * extra IE(s) + * @assocresp: WPS IE(s) for (Re)Association Response frames or %NULL + * to remove extra IE(s) * Returns: 0 on success, -1 on failure * * This is an optional function to add WPS IE in the kernel driver for * Beacon and Probe Response frames. This can be left undefined (set - * to %NULL) if the driver uses the Beacon template from set_beacon() - * and does not process Probe Request frames. + * to %NULL) if the driver uses the Beacon template from set_ap() + * and does not process Probe Request frames. If the driver takes care + * of (Re)Association frame processing, the assocresp buffer includes + * WPS IE(s) that need to be added to (Re)Association Response frames + * whenever a (Re)Association Request frame indicated use of WPS. + * + * This will also be used to add P2P IE(s) into Beacon/Probe Response + * frames when operating as a GO. The driver is responsible for adding + * timing related attributes (e.g., NoA) in addition to the IEs + * included here by appending them after these buffers. This call is + * also used to provide Probe Response IEs for P2P Listen state + * operations for drivers that generate the Probe Response frames + * internally. + * + * DEPRECATED - use set_ap() instead */ int (*set_ap_wps_ie)(void *priv, const struct wpabuf *beacon, - const struct wpabuf *proberesp); + const struct wpabuf *proberesp, + const struct wpabuf *assocresp); /** * set_supp_port - Set IEEE 802.1X Supplicant Port status @@ -1620,31 +1949,52 @@ struct wpa_driver_ops { * @addr: MAC address of the associated station * @aid: Association ID * @val: 1 = bind to 4-address WDS; 0 = unbind + * @bridge_ifname: Bridge interface to use for the WDS station or %NULL + * to indicate that bridge is not to be used * Returns: 0 on success, -1 on failure */ - int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val); + int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val, + const char *bridge_ifname); /** * send_action - Transmit an Action frame * @priv: Private driver interface data * @freq: Frequency (in MHz) of the channel + * @wait: Time to wait off-channel for a response (in ms), or zero * @dst: Destination MAC address (Address 1) * @src: Source MAC address (Address 2) * @bssid: BSSID (Address 3) * @data: Frame body * @data_len: data length in octets + @ @no_cck: Whether CCK rates must not be used to transmit this frame * Returns: 0 on success, -1 on failure * * This command can be used to request the driver to transmit an action - * frame to the specified destination. If a remain-on-channel duration - * is in progress, the frame is transmitted on that channel. Otherwise, - * the frame is transmitted on the current operational channel if in - * associated state in station mode or if operating as an AP. If none - * of these conditions is in effect, send_action() cannot be used. + * frame to the specified destination. + * + * If the %WPA_DRIVER_FLAGS_OFFCHANNEL_TX flag is set, the frame will + * be transmitted on the given channel and the device will wait for a + * response on that channel for the given wait time. + * + * If the flag is not set, the wait time will be ignored. In this case, + * if a remain-on-channel duration is in progress, the frame must be + * transmitted on that channel; alternatively the frame may be sent on + * the current operational channel (if in associated state in station + * mode or while operating as an AP.) */ - int (*send_action)(void *priv, unsigned int freq, + int (*send_action)(void *priv, unsigned int freq, unsigned int wait, const u8 *dst, const u8 *src, const u8 *bssid, - const u8 *data, size_t data_len); + const u8 *data, size_t data_len, int no_cck); + + /** + * send_action_cancel_wait - Cancel action frame TX wait + * @priv: Private driver interface data + * + * This command cancels the wait time associated with sending an action + * frame. It is only available when %WPA_DRIVER_FLAGS_OFFCHANNEL_TX is + * set in the driver flags. + */ + void (*send_action_cancel_wait)(void *priv); /** * remain_on_channel - Remain awake on a channel @@ -1701,28 +2051,25 @@ struct wpa_driver_ops { int (*probe_req_report)(void *priv, int report); /** - * disable_11b_rates - Set whether IEEE 802.11b rates are used for TX + * deinit_ap - Deinitialize AP mode * @priv: Private driver interface data - * @disabled: Whether IEEE 802.11b rates are disabled * Returns: 0 on success, -1 on failure (or if not supported) * - * This command is used to disable IEEE 802.11b rates (1, 2, 5.5, and - * 11 Mbps) as TX rates for data and management frames. This can be - * used to optimize channel use when there is no need to support IEEE - * 802.11b-only devices. + * This optional function can be used to disable AP mode related + * configuration and change the driver mode to station mode to allow + * normal station operations like scanning to be completed. */ - int (*disable_11b_rates)(void *priv, int disabled); + int (*deinit_ap)(void *priv); /** - * deinit_ap - Deinitialize AP mode + * 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 AP mode related - * configuration and change the driver mode to station mode to allow - * normal station operations like scanning to be completed. + * 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_ap)(void *priv); + int (*deinit_p2p_cli)(void *priv); /** * suspend - Notification on system suspend/hibernate event @@ -1765,6 +2112,496 @@ struct wpa_driver_ops { */ int (*send_frame)(void *priv, const u8 *data, size_t data_len, int encrypt); + + /** + * shared_freq - Get operating frequency of shared interface(s) + * @priv: Private driver interface data + * Returns: Operating frequency in MHz, 0 if no shared operation in + * use, or -1 on failure + * + * This command can be used to request the current operating frequency + * of any virtual interface that shares the same radio to provide + * information for channel selection for other virtual interfaces. + */ + int (*shared_freq)(void *priv); + + /** + * get_noa - Get current Notice of Absence attribute payload + * @priv: Private driver interface data + * @buf: Buffer for returning NoA + * @buf_len: Buffer length in octets + * Returns: Number of octets used in buf, 0 to indicate no NoA is being + * advertized, or -1 on failure + * + * This function is used to fetch the current Notice of Absence + * attribute value from GO. + */ + int (*get_noa)(void *priv, u8 *buf, size_t buf_len); + + /** + * set_noa - Set Notice of Absence parameters for GO (testing) + * @priv: Private driver interface data + * @count: Count + * @start: Start time in ms from next TBTT + * @duration: Duration in ms + * Returns: 0 on success or -1 on failure + * + * This function is used to set Notice of Absence parameters for GO. It + * is used only for testing. To disable NoA, all parameters are set to + * 0. + */ + int (*set_noa)(void *priv, u8 count, int start, int duration); + + /** + * set_p2p_powersave - Set P2P power save options + * @priv: Private driver interface data + * @legacy_ps: 0 = disable, 1 = enable, 2 = maximum PS, -1 = no change + * @opp_ps: 0 = disable, 1 = enable, -1 = no change + * @ctwindow: 0.. = change (msec), -1 = no change + * Returns: 0 on success or -1 on failure + */ + int (*set_p2p_powersave)(void *priv, int legacy_ps, int opp_ps, + int ctwindow); + + /** + * ampdu - Enable/disable aggregation + * @priv: Private driver interface data + * @ampdu: 1/0 = enable/disable A-MPDU aggregation + * Returns: 0 on success or -1 on failure + */ + int (*ampdu)(void *priv, int ampdu); + + /** + * get_radio_name - Get physical radio name for the device + * @priv: Private driver interface data + * Returns: Radio name or %NULL if not known + * + * The returned data must not be modified by the caller. It is assumed + * that any interface that has the same radio name as another is + * sharing the same physical radio. This information can be used to + * share scan results etc. information between the virtual interfaces + * to speed up various operations. + */ + const char * (*get_radio_name)(void *priv); + + /** + * p2p_find - Start P2P Device Discovery + * @priv: Private driver interface data + * @timeout: Timeout for find operation in seconds or 0 for no timeout + * @type: Device Discovery type (enum p2p_discovery_type) + * Returns: 0 on success, -1 on failure + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*p2p_find)(void *priv, unsigned int timeout, int type); + + /** + * p2p_stop_find - Stop P2P Device Discovery + * @priv: Private driver interface data + * Returns: 0 on success, -1 on failure + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*p2p_stop_find)(void *priv); + + /** + * p2p_listen - Start P2P Listen state for specified duration + * @priv: Private driver interface data + * @timeout: Listen state duration in milliseconds + * Returns: 0 on success, -1 on failure + * + * This function can be used to request the P2P module to keep the + * device discoverable on the listen channel for an extended set of + * time. At least in its current form, this is mainly used for testing + * purposes and may not be of much use for normal P2P operations. + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*p2p_listen)(void *priv, unsigned int timeout); + + /** + * p2p_connect - Start P2P group formation (GO negotiation) + * @priv: Private driver interface data + * @peer_addr: MAC address of the peer P2P client + * @wps_method: enum p2p_wps_method value indicating config method + * @go_intent: Local GO intent value (1..15) + * @own_interface_addr: Intended interface address to use with the + * group + * @force_freq: The only allowed channel frequency in MHz or 0 + * @persistent_group: Whether to create persistent group + * Returns: 0 on success, -1 on failure + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*p2p_connect)(void *priv, const u8 *peer_addr, int wps_method, + int go_intent, const u8 *own_interface_addr, + unsigned int force_freq, int persistent_group); + + /** + * wps_success_cb - Report successfully completed WPS provisioning + * @priv: Private driver interface data + * @peer_addr: Peer address + * Returns: 0 on success, -1 on failure + * + * This function is used to report successfully completed WPS + * provisioning during group formation in both GO/Registrar and + * client/Enrollee roles. + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*wps_success_cb)(void *priv, const u8 *peer_addr); + + /** + * p2p_group_formation_failed - Report failed WPS provisioning + * @priv: Private driver interface data + * Returns: 0 on success, -1 on failure + * + * This function is used to report failed group formation. This can + * happen either due to failed WPS provisioning or due to 15 second + * timeout during the provisioning phase. + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*p2p_group_formation_failed)(void *priv); + + /** + * p2p_set_params - Set P2P parameters + * @priv: Private driver interface data + * @params: P2P parameters + * Returns: 0 on success, -1 on failure + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*p2p_set_params)(void *priv, const struct p2p_params *params); + + /** + * p2p_prov_disc_req - Send Provision Discovery Request + * @priv: Private driver interface data + * @peer_addr: MAC address of the peer P2P client + * @config_methods: WPS Config Methods value (only one bit set) + * Returns: 0 on success, -1 on failure + * + * This function can be used to request a discovered P2P peer to + * display a PIN (config_methods = WPS_CONFIG_DISPLAY) or be prepared + * to enter a PIN from us (config_methods = WPS_CONFIG_KEYPAD). The + * Provision Discovery Request frame is transmitted once immediately + * and if no response is received, the frame will be sent again + * whenever the target device is discovered during device dsicovery + * (start with a p2p_find() call). Response from the peer is indicated + * with the EVENT_P2P_PROV_DISC_RESPONSE event. + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*p2p_prov_disc_req)(void *priv, const u8 *peer_addr, + u16 config_methods, int join); + + /** + * p2p_sd_request - Schedule a service discovery query + * @priv: Private driver interface data + * @dst: Destination peer or %NULL to apply for all peers + * @tlvs: P2P Service Query TLV(s) + * Returns: Reference to the query or 0 on failure + * + * Response to the query is indicated with the + * EVENT_P2P_SD_RESPONSE driver event. + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + u64 (*p2p_sd_request)(void *priv, const u8 *dst, + const struct wpabuf *tlvs); + + /** + * p2p_sd_cancel_request - Cancel a pending service discovery query + * @priv: Private driver interface data + * @req: Query reference from p2p_sd_request() + * Returns: 0 on success, -1 on failure + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*p2p_sd_cancel_request)(void *priv, u64 req); + + /** + * p2p_sd_response - Send response to a service discovery query + * @priv: Private driver interface data + * @freq: Frequency from EVENT_P2P_SD_REQUEST event + * @dst: Destination address from EVENT_P2P_SD_REQUEST event + * @dialog_token: Dialog token from EVENT_P2P_SD_REQUEST event + * @resp_tlvs: P2P Service Response TLV(s) + * Returns: 0 on success, -1 on failure + * + * This function is called as a response to the request indicated with + * the EVENT_P2P_SD_REQUEST driver event. + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*p2p_sd_response)(void *priv, int freq, const u8 *dst, + u8 dialog_token, + const struct wpabuf *resp_tlvs); + + /** + * p2p_service_update - Indicate a change in local services + * @priv: Private driver interface data + * Returns: 0 on success, -1 on failure + * + * This function needs to be called whenever there is a change in + * availability of the local services. This will increment the + * Service Update Indicator value which will be used in SD Request and + * Response frames. + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*p2p_service_update)(void *priv); + + /** + * p2p_reject - Reject peer device (explicitly block connections) + * @priv: Private driver interface data + * @addr: MAC address of the peer + * Returns: 0 on success, -1 on failure + */ + int (*p2p_reject)(void *priv, const u8 *addr); + + /** + * p2p_invite - Invite a P2P Device into a group + * @priv: Private driver interface data + * @peer: Device Address of the peer P2P Device + * @role: Local role in the group + * @bssid: Group BSSID or %NULL if not known + * @ssid: Group SSID + * @ssid_len: Length of ssid in octets + * @go_dev_addr: Forced GO Device Address or %NULL if none + * @persistent_group: Whether this is to reinvoke a persistent group + * Returns: 0 on success, -1 on failure + */ + int (*p2p_invite)(void *priv, const u8 *peer, int role, + const u8 *bssid, const u8 *ssid, size_t ssid_len, + const u8 *go_dev_addr, int persistent_group); + + /** + * send_tdls_mgmt - for sending TDLS management packets + * @priv: private driver interface data + * @dst: Destination (peer) MAC address + * @action_code: TDLS action code for the mssage + * @dialog_token: Dialog Token to use in the message (if needed) + * @status_code: Status Code or Reason Code to use (if needed) + * @buf: TDLS IEs to add to the message + * @len: Length of buf in octets + * Returns: 0 on success, negative (<0) on failure + * + * This optional function can be used to send packet to driver which is + * responsible for receiving and sending all TDLS packets. + */ + int (*send_tdls_mgmt)(void *priv, const u8 *dst, u8 action_code, + u8 dialog_token, u16 status_code, + const u8 *buf, size_t len); + + /** + * tdls_oper - Ask the driver to perform high-level TDLS operations + * @priv: Private driver interface data + * @oper: TDLS high-level operation. See %enum tdls_oper + * @peer: Destination (peer) MAC address + * Returns: 0 on success, negative (<0) on failure + * + * This optional function can be used to send high-level TDLS commands + * to the driver. + */ + int (*tdls_oper)(void *priv, enum tdls_oper oper, const u8 *peer); + + /** + * wnm_oper - Notify driver of the WNM frame reception + * @priv: Private driver interface data + * @oper: WNM operation. See %enum wnm_oper + * @peer: Destination (peer) MAC address + * @buf: Buffer for the driver to fill in (for getting IE) + * @buf_len: Return the len of buf + * Returns: 0 on success, negative (<0) on failure + */ + int (*wnm_oper)(void *priv, enum wnm_oper oper, const u8 *peer, + u8 *buf, u16 *buf_len); + + /** + * signal_poll - Get current connection information + * @priv: Private driver interface data + * @signal_info: Connection info structure + */ + int (*signal_poll)(void *priv, struct wpa_signal_info *signal_info); + + /** + * set_authmode - Set authentication algorithm(s) for static WEP + * @priv: Private driver interface data + * @authmode: 1=Open System, 2=Shared Key, 3=both + * Returns: 0 on success, -1 on failure + * + * This function can be used to set authentication algorithms for AP + * mode when static WEP is used. If the driver uses user space MLME/SME + * implementation, there is no need to implement this function. + * + * DEPRECATED - use set_ap() instead + */ + int (*set_authmode)(void *priv, int authmode); + + /** + * set_rekey_info - Set rekey information + * @priv: Private driver interface data + * @kek: Current KEK + * @kck: Current KCK + * @replay_ctr: Current EAPOL-Key Replay Counter + * + * This optional function can be used to provide information for the + * driver/firmware to process EAPOL-Key frames in Group Key Handshake + * while the host (including wpa_supplicant) is sleeping. + */ + void (*set_rekey_info)(void *priv, const u8 *kek, const u8 *kck, + const u8 *replay_ctr); + + /** + * sta_assoc - Station association indication + * @priv: Private driver interface data + * @own_addr: Source address and BSSID for association frame + * @addr: MAC address of the station to associate + * @reassoc: flag to indicate re-association + * @status: association response status code + * @ie: assoc response ie buffer + * @len: ie buffer length + * Returns: 0 on success, -1 on failure + * + * This function indicates the driver to send (Re)Association + * Response frame to the station. + */ + int (*sta_assoc)(void *priv, const u8 *own_addr, const u8 *addr, + int reassoc, u16 status, const u8 *ie, size_t len); + + /** + * sta_auth - Station authentication indication + * @priv: Private driver interface data + * @own_addr: Source address and BSSID for authentication frame + * @addr: MAC address of the station to associate + * @seq: authentication sequence number + * @status: authentication response status code + * @ie: authentication frame ie buffer + * @len: ie buffer length + * + * This function indicates the driver to send Authentication frame + * to the station. + */ + int (*sta_auth)(void *priv, const u8 *own_addr, const u8 *addr, + u16 seq, u16 status, const u8 *ie, size_t len); + + /** + * add_tspec - Add traffic stream + * @priv: Private driver interface data + * @addr: MAC address of the station to associate + * @tspec_ie: tspec ie buffer + * @tspec_ielen: tspec ie length + * Returns: 0 on success, -1 on failure + * + * This function adds the traffic steam for the station + * and fills the medium_time in tspec_ie. + */ + int (*add_tspec)(void *priv, const u8 *addr, u8 *tspec_ie, + size_t tspec_ielen); + + /** + * add_sta_node - Add a station node in the driver + * @priv: Private driver interface data + * @addr: MAC address of the station to add + * @auth_alg: authentication algorithm used by the station + * Returns: 0 on success, -1 on failure + * + * This function adds the station node in the driver, when + * the station gets added by FT-over-DS. + */ + int (*add_sta_node)(void *priv, const u8 *addr, u16 auth_alg); + + /** + * sched_scan - Request the driver to initiate scheduled scan + * @priv: Private driver interface data + * @params: Scan parameters + * @interval: Interval between scan cycles in milliseconds + * Returns: 0 on success, -1 on failure + * + * This operation should be used for scheduled scan offload to + * the hardware. Every time scan results are available, the + * driver should report scan results event for wpa_supplicant + * which will eventually request the results with + * wpa_driver_get_scan_results2(). This operation is optional + * and if not provided or if it returns -1, we fall back to + * normal host-scheduled scans. + */ + int (*sched_scan)(void *priv, struct wpa_driver_scan_params *params, + u32 interval); + + /** + * stop_sched_scan - Request the driver to stop a scheduled scan + * @priv: Private driver interface data + * Returns: 0 on success, -1 on failure + * + * This should cause the scheduled scan to be stopped and + * results should stop being sent. Must be supported if + * sched_scan is supported. + */ + int (*stop_sched_scan)(void *priv); + + /** + * poll_client - Probe (null data or such) the given station + * @priv: Private driver interface data + * @own_addr: MAC address of sending interface + * @addr: MAC address of the station to probe + * @qos: Indicates whether station is QoS station + * + * This function is used to verify whether an associated station is + * still present. This function does not need to be implemented if the + * driver provides such inactivity polling mechanism. + */ + void (*poll_client)(void *priv, const u8 *own_addr, + const u8 *addr, int qos); + + /** + * radio_disable - Disable/enable radio + * @priv: Private driver interface data + * @disabled: 1=disable 0=enable radio + * Returns: 0 on success, -1 on failure + * + * This optional command is for testing purposes. It can be used to + * disable the radio on a testbed device to simulate out-of-radio-range + * conditions. + */ + int (*radio_disable)(void *priv, int disabled); + + /** + * switch_channel - Announce channel switch and migrate the GO to the + * given frequency + * @priv: Private driver interface data + * @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); }; @@ -1887,6 +2724,13 @@ enum wpa_event_type { EVENT_STKSTART, /** + * EVENT_TDLS - Request TDLS operation + * + * This event can be used to request a TDLS operation to be performed. + */ + EVENT_TDLS, + + /** * EVENT_FT_RESPONSE - Report FT (IEEE 802.11r) response IEs * * The driver is expected to report the received FT IEs from @@ -1930,7 +2774,7 @@ enum wpa_event_type { * EVENT_ASSOC_REJECT - Association rejected * * This event should be called when (re)association attempt has been - * rejected by the AP. Information about authentication result is + * rejected by the AP. Information about the association response is * included in union wpa_event_data::assoc_reject. */ EVENT_ASSOC_REJECT, @@ -2046,7 +2890,163 @@ enum wpa_event_type { * observed in frames received from the current AP if signal strength * monitoring has been enabled with signal_monitor(). */ - EVENT_SIGNAL_CHANGE + EVENT_SIGNAL_CHANGE, + + /** + * EVENT_INTERFACE_ENABLED - Notify that interface was enabled + * + * This event is used to indicate that the interface was enabled after + * having been previously disabled, e.g., due to rfkill. + */ + EVENT_INTERFACE_ENABLED, + + /** + * EVENT_INTERFACE_DISABLED - Notify that interface was disabled + * + * This event is used to indicate that the interface was disabled, + * e.g., due to rfkill. + */ + EVENT_INTERFACE_DISABLED, + + /** + * EVENT_CHANNEL_LIST_CHANGED - Channel list changed + * + * This event is used to indicate that the channel list has changed, + * e.g., because of a regulatory domain change triggered by scan + * results including an AP advertising a country code. + */ + EVENT_CHANNEL_LIST_CHANGED, + + /** + * EVENT_INTERFACE_UNAVAILABLE - Notify that interface is unavailable + * + * This event is used to indicate that the driver cannot maintain this + * interface in its operation mode anymore. The most likely use for + * this is to indicate that AP mode operation is not available due to + * operating channel would need to be changed to a DFS channel when + * the driver does not support radar detection and another virtual + * interfaces caused the operating channel to change. Other similar + * resource conflicts could also trigger this for station mode + * interfaces. + */ + EVENT_INTERFACE_UNAVAILABLE, + + /** + * EVENT_BEST_CHANNEL + * + * Driver generates this event whenever it detects a better channel + * (e.g., based on RSSI or channel use). This information can be used + * to improve channel selection for a new AP/P2P group. + */ + EVENT_BEST_CHANNEL, + + /** + * EVENT_UNPROT_DEAUTH - Unprotected Deauthentication frame received + * + * This event should be called when a Deauthentication frame is dropped + * due to it not being protected (MFP/IEEE 802.11w). + * union wpa_event_data::unprot_deauth is required to provide more + * details of the frame. + */ + EVENT_UNPROT_DEAUTH, + + /** + * EVENT_UNPROT_DISASSOC - Unprotected Disassociation frame received + * + * This event should be called when a Disassociation frame is dropped + * due to it not being protected (MFP/IEEE 802.11w). + * union wpa_event_data::unprot_disassoc is required to provide more + * details of the frame. + */ + EVENT_UNPROT_DISASSOC, + + /** + * EVENT_STATION_LOW_ACK + * + * Driver generates this event whenever it detected that a particular + * station was lost. Detection can be through massive transmission + * failures for example. + */ + EVENT_STATION_LOW_ACK, + + /** + * EVENT_P2P_DEV_FOUND - Report a discovered P2P device + * + * This event is used only if the driver implements P2P management + * internally. Event data is stored in + * union wpa_event_data::p2p_dev_found. + */ + EVENT_P2P_DEV_FOUND, + + /** + * EVENT_P2P_GO_NEG_REQ_RX - Report reception of GO Negotiation Request + * + * This event is used only if the driver implements P2P management + * internally. Event data is stored in + * union wpa_event_data::p2p_go_neg_req_rx. + */ + EVENT_P2P_GO_NEG_REQ_RX, + + /** + * EVENT_P2P_GO_NEG_COMPLETED - Report completion of GO Negotiation + * + * This event is used only if the driver implements P2P management + * internally. Event data is stored in + * union wpa_event_data::p2p_go_neg_completed. + */ + EVENT_P2P_GO_NEG_COMPLETED, + + EVENT_P2P_PROV_DISC_REQUEST, + EVENT_P2P_PROV_DISC_RESPONSE, + EVENT_P2P_SD_REQUEST, + EVENT_P2P_SD_RESPONSE, + + /** + * EVENT_IBSS_PEER_LOST - IBSS peer not reachable anymore + */ + EVENT_IBSS_PEER_LOST, + + /** + * EVENT_DRIVER_GTK_REKEY - Device/driver did GTK rekey + * + * This event carries the new replay counter to notify wpa_supplicant + * of the current EAPOL-Key Replay Counter in case the driver/firmware + * completed Group Key Handshake while the host (including + * wpa_supplicant was sleeping). + */ + EVENT_DRIVER_GTK_REKEY, + + /** + * EVENT_SCHED_SCAN_STOPPED - Scheduled scan was stopped + */ + EVENT_SCHED_SCAN_STOPPED, + + /** + * EVENT_DRIVER_CLIENT_POLL_OK - Station responded to poll + * + * This event indicates that the station responded to the poll + * initiated with @poll_client. + */ + EVENT_DRIVER_CLIENT_POLL_OK, + + /** + * EVENT_EAPOL_TX_STATUS - notify of EAPOL TX status + */ + EVENT_EAPOL_TX_STATUS, + + /** + * EVENT_CH_SWITCH - AP or GO decided to switch channels + * + * Described in wpa_event_data.ch_switch + * */ + EVENT_CH_SWITCH, + + /** + * EVENT_WNM - Request WNM operation + * + * This event can be used to request a WNM operation to be performed. + */ + EVENT_WNM }; @@ -2064,6 +3064,11 @@ union wpa_event_data { */ struct assoc_info { /** + * reassoc - Flag to indicate association or reassociation + */ + int reassoc; + + /** * req_ies - (Re)Association Request IEs * * If the driver generates WPA/RSN IE, this event data must be @@ -2146,6 +3151,21 @@ union wpa_event_data { * Deauthentication frame */ u16 reason_code; + + /** + * ie - Optional IE(s) in Disassociation frame + */ + const u8 *ie; + + /** + * ie_len - Length of ie buffer in octets + */ + size_t ie_len; + + /** + * locally_generated - Whether the frame was locally generated + */ + int locally_generated; } disassoc_info; /** @@ -2162,6 +3182,21 @@ union wpa_event_data { * Deauthentication frame */ u16 reason_code; + + /** + * ie - Optional IE(s) in Deauthentication frame + */ + const u8 *ie; + + /** + * ie_len - Length of ie buffer in octets + */ + size_t ie_len; + + /** + * locally_generated - Whether the frame was locally generated + */ + int locally_generated; } deauth_info; /** @@ -2202,6 +3237,36 @@ union wpa_event_data { } stkstart; /** + * struct tdls - Data for EVENT_TDLS + */ + struct tdls { + u8 peer[ETH_ALEN]; + enum { + TDLS_REQUEST_SETUP, + TDLS_REQUEST_TEARDOWN + } oper; + u16 reason_code; /* for teardown */ + } tdls; + + /** + * struct wnm - Data for EVENT_WNM + */ + struct wnm { + u8 addr[ETH_ALEN]; + enum { + WNM_OPER_SLEEP, + } oper; + enum { + WNM_SLEEP_ENTER, + WNM_SLEEP_EXIT + } sleep_action; + int sleep_intval; + u16 reason_code; + u8 *buf; + u16 buf_len; + } wnm; + + /** * struct ft_ies - FT information elements (EVENT_FT_RESPONSE) * * During FT (IEEE 802.11r) authentication sequence, the driver is @@ -2233,7 +3298,9 @@ union wpa_event_data { */ struct auth_info { u8 peer[ETH_ALEN]; + u8 bssid[ETH_ALEN]; u16 auth_type; + u16 auth_transaction; u16 status_code; const u8 *ies; size_t ies_len; @@ -2244,6 +3311,11 @@ union wpa_event_data { */ struct assoc_reject { /** + * bssid - BSSID of the AP that rejected association + */ + const u8 *bssid; + + /** * resp_ies - (Re)Association Response IEs * * Optional association data from the driver. This data is not @@ -2254,7 +3326,7 @@ union wpa_event_data { * This should start with the first IE (fixed fields before IEs * are not included). */ - u8 *resp_ies; + const u8 *resp_ies; /** * resp_ies_len - Length of resp_ies in bytes @@ -2296,8 +3368,9 @@ union wpa_event_data { * struct rx_from_unknown - Data for EVENT_RX_FROM_UNKNOWN events */ struct rx_from_unknown { - const u8 *frame; - size_t len; + const u8 *bssid; + const u8 *addr; + int wds; } rx_from_unknown; /** @@ -2307,7 +3380,7 @@ union wpa_event_data { const u8 *frame; size_t frame_len; u32 datarate; - u32 ssi_signal; + int ssi_signal; /* dBm */ } rx_mgmt; /** @@ -2405,6 +3478,18 @@ union wpa_event_data { const u8 *sa; /** + * da - Destination address of the received Probe Request frame + * or %NULL if not available + */ + const u8 *da; + + /** + * bssid - BSSID of the received Probe Request frame or %NULL + * if not available + */ + const u8 *bssid; + + /** * ie - IEs from the Probe Request body */ const u8 *ie; @@ -2413,6 +3498,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; /** @@ -2432,11 +3522,155 @@ union wpa_event_data { } eapol_rx; /** - * struct signal_change - Data for EVENT_SIGNAL_CHANGE events + * signal_change - Data for EVENT_SIGNAL_CHANGE events + */ + struct wpa_signal_info signal_change; + + /** + * struct best_channel - Data for EVENT_BEST_CHANNEL events + * @freq_24: Best 2.4 GHz band channel frequency in MHz + * @freq_5: Best 5 GHz band channel frequency in MHz + * @freq_overall: Best channel frequency in MHz + * + * 0 can be used to indicate no preference in either band. + */ + struct best_channel { + int freq_24; + int freq_5; + int freq_overall; + } best_chan; + + struct unprot_deauth { + const u8 *sa; + const u8 *da; + u16 reason_code; + } unprot_deauth; + + struct unprot_disassoc { + const u8 *sa; + const u8 *da; + u16 reason_code; + } unprot_disassoc; + + /** + * struct low_ack - Data for EVENT_STATION_LOW_ACK events + * @addr: station address + */ + struct low_ack { + u8 addr[ETH_ALEN]; + } low_ack; + + /** + * struct p2p_dev_found - Data for EVENT_P2P_DEV_FOUND + */ + struct p2p_dev_found { + const u8 *addr; + const u8 *dev_addr; + const u8 *pri_dev_type; + const char *dev_name; + u16 config_methods; + u8 dev_capab; + u8 group_capab; + } p2p_dev_found; + + /** + * struct p2p_go_neg_req_rx - Data for EVENT_P2P_GO_NEG_REQ_RX + */ + struct p2p_go_neg_req_rx { + const u8 *src; + u16 dev_passwd_id; + } p2p_go_neg_req_rx; + + /** + * struct p2p_go_neg_completed - Data for EVENT_P2P_GO_NEG_COMPLETED + */ + struct p2p_go_neg_completed { + struct p2p_go_neg_results *res; + } p2p_go_neg_completed; + + struct p2p_prov_disc_req { + const u8 *peer; + u16 config_methods; + const u8 *dev_addr; + const u8 *pri_dev_type; + const char *dev_name; + u16 supp_config_methods; + u8 dev_capab; + u8 group_capab; + } p2p_prov_disc_req; + + struct p2p_prov_disc_resp { + const u8 *peer; + u16 config_methods; + } p2p_prov_disc_resp; + + struct p2p_sd_req { + int freq; + const u8 *sa; + u8 dialog_token; + u16 update_indic; + const u8 *tlvs; + size_t tlvs_len; + } p2p_sd_req; + + struct p2p_sd_resp { + const u8 *sa; + u16 update_indic; + const u8 *tlvs; + size_t tlvs_len; + } p2p_sd_resp; + + /** + * struct ibss_peer_lost - Data for EVENT_IBSS_PEER_LOST + */ + struct ibss_peer_lost { + u8 peer[ETH_ALEN]; + } ibss_peer_lost; + + /** + * struct driver_gtk_rekey - Data for EVENT_DRIVER_GTK_REKEY */ - struct signal_change { - int above_threshold; - } signal_change; + struct driver_gtk_rekey { + const u8 *bssid; + const u8 *replay_ctr; + } driver_gtk_rekey; + + /** + * struct client_poll - Data for EVENT_DRIVER_CLIENT_POLL_OK events + * @addr: station address + */ + struct client_poll { + u8 addr[ETH_ALEN]; + } client_poll; + + /** + * struct eapol_tx_status + * @dst: Original destination + * @data: Data starting with IEEE 802.1X header (!) + * @data_len: Length of data + * @ack: Indicates ack or lost frame + * + * This corresponds to hapd_send_eapol if the frame sent + * there isn't just reported as EVENT_TX_STATUS. + */ + struct eapol_tx_status { + const u8 *dst; + const u8 *data; + int data_len; + int ack; + } eapol_tx_status; + + /** + * struct ch_switch + * @freq: Frequency of new channel in MHz + * @ht_enabled: Whether this is an HT channel + * @ch_offset: Secondary channel offset + */ + struct ch_switch { + int freq; + int ht_enabled; + int ch_offset; + } ch_switch; }; /** @@ -2459,10 +3693,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, */ static inline void drv_event_assoc(void *ctx, const u8 *addr, const u8 *ie, - size_t ielen) + size_t ielen, int reassoc) { union wpa_event_data event; os_memset(&event, 0, sizeof(event)); + event.assoc_info.reassoc = reassoc; event.assoc_info.req_ies = ie; event.assoc_info.req_ies_len = ielen; event.assoc_info.addr = addr; @@ -2488,4 +3723,10 @@ static inline void drv_event_eapol_rx(void *ctx, const u8 *src, const u8 *data, wpa_supplicant_event(ctx, EVENT_EAPOL_RX, &event); } +/* driver_common.c */ +void wpa_scan_results_free(struct wpa_scan_results *res); + +/* Convert wpa_event_type to a string for logging */ +const char * event_to_string(enum wpa_event_type event); + #endif /* DRIVER_H */ diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c index 5c25f00b16431..c2f59344af5ab 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" @@ -20,6 +14,12 @@ #include <sys/ioctl.h> #include "common.h" +#include "eloop.h" +#include "common/ieee802_11_defs.h" +#include "l2_packet/l2_packet.h" +#include "p2p/p2p.h" + +#include "common.h" #ifndef _BYTE_ORDER #ifdef WORDS_BIGENDIAN #define _BYTE_ORDER _BIG_ENDIAN @@ -34,18 +34,18 @@ */ #define ATH_WPS_IE -#include "os/linux/include/ieee80211_external.h" +#include "ieee80211_external.h" #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 "wireless_copy.h" +#include "linux_wext.h" #include "driver.h" #include "eloop.h" @@ -56,7 +56,7 @@ #include "linux_ioctl.h" -struct madwifi_driver_data { +struct atheros_driver_data { struct hostapd_data *hapd; /* back pointer */ char iface[IFNAMSIZ + 1]; @@ -70,11 +70,15 @@ struct madwifi_driver_data { struct hostap_sta_driver_data acct_data; struct l2_packet_data *sock_raw; /* raw 802.11 management frames */ + struct wpabuf *wpa_ie; + struct wpabuf *wps_beacon_ie; + struct wpabuf *wps_probe_resp_ie; + u8 own_addr[ETH_ALEN]; }; -static int madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, +static int atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, int reason_code); -static int madwifi_set_privacy(void *priv, int enabled); +static int atheros_set_privacy(void *priv, int enabled); static const char * athr_get_ioctl_name(int op) { @@ -125,16 +129,8 @@ static const char * athr_get_ioctl_name(int op) return "FILTERFRAME"; case IEEE80211_IOCTL_SET_RTPARAMS: return "SET_RTPARAMS"; - case IEEE80211_IOCTL_SENDADDBA: - return "SENDADDBA"; - case IEEE80211_IOCTL_GETADDBASTATUS: - return "GETADDBASTATUS"; - case IEEE80211_IOCTL_SENDDELBA: - return "SENDDELBA"; case IEEE80211_IOCTL_SET_MEDENYENTRY: return "SET_MEDENYENTRY"; - case IEEE80211_IOCTL_SET_ADDBARESP: - return "SET_ADDBARESP"; case IEEE80211_IOCTL_GET_MACADDR: return "GET_MACADDR"; case IEEE80211_IOCTL_SET_HBRPARAMS: @@ -179,7 +175,7 @@ static const char * athr_get_param_name(int op) static int -set80211priv(struct madwifi_driver_data *drv, int op, void *data, int len) +set80211priv(struct atheros_driver_data *drv, int op, void *data, int len) { struct iwreq iwr; int do_inline = len < IFNAMSIZ; @@ -218,7 +214,7 @@ set80211priv(struct madwifi_driver_data *drv, int op, void *data, int len) } static int -set80211param(struct madwifi_driver_data *drv, int op, int arg) +set80211param(struct atheros_driver_data *drv, int op, int arg) { struct iwreq iwr; @@ -255,7 +251,7 @@ ether_sprintf(const u8 *addr) * Configure WPA parameters. */ static int -madwifi_configure_wpa(struct madwifi_driver_data *drv, +atheros_configure_wpa(struct atheros_driver_data *drv, struct wpa_bss_params *params) { int v; @@ -320,8 +316,15 @@ madwifi_configure_wpa(struct madwifi_driver_data *drv, v = 0; if (params->rsn_preauth) v |= BIT(0); - wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", - __func__, params->rsn_preauth); +#ifdef CONFIG_IEEE80211W + if (params->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + v |= BIT(7); + if (params->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) + v |= BIT(6); + } +#endif /* CONFIG_IEEE80211W */ + + wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", __func__, v); if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) { printf("Unable to set RSN capabilities to 0x%x\n", v); return -1; @@ -336,9 +339,9 @@ madwifi_configure_wpa(struct madwifi_driver_data *drv, } static int -madwifi_set_ieee8021x(void *priv, struct wpa_bss_params *params) +atheros_set_ieee8021x(void *priv, struct wpa_bss_params *params) { - struct madwifi_driver_data *drv = priv; + struct atheros_driver_data *drv = priv; wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled); @@ -348,14 +351,14 @@ madwifi_set_ieee8021x(void *priv, struct wpa_bss_params *params) IEEE80211_AUTH_AUTO) < 0) return -1; /* IEEE80211_AUTH_AUTO ends up enabling Privacy; clear that */ - return madwifi_set_privacy(drv, 0); + return atheros_set_privacy(drv, 0); } if (!params->wpa && !params->ieee802_1x) { hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER, HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!"); return -1; } - if (params->wpa && madwifi_configure_wpa(drv, params) != 0) { + if (params->wpa && atheros_configure_wpa(drv, params) != 0) { hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER, HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!"); return -1; @@ -371,9 +374,9 @@ madwifi_set_ieee8021x(void *priv, struct wpa_bss_params *params) } static int -madwifi_set_privacy(void *priv, int enabled) +atheros_set_privacy(void *priv, int enabled) { - struct madwifi_driver_data *drv = priv; + struct atheros_driver_data *drv = priv; wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); @@ -381,9 +384,9 @@ madwifi_set_privacy(void *priv, int enabled) } static int -madwifi_set_sta_authorized(void *priv, const u8 *addr, int authorized) +atheros_set_sta_authorized(void *priv, const u8 *addr, int authorized) { - struct madwifi_driver_data *drv = priv; + struct atheros_driver_data *drv = priv; struct ieee80211req_mlme mlme; int ret; @@ -406,21 +409,21 @@ madwifi_set_sta_authorized(void *priv, const u8 *addr, int authorized) } static int -madwifi_sta_set_flags(void *priv, const u8 *addr, +atheros_sta_set_flags(void *priv, const u8 *addr, int total_flags, int flags_or, int flags_and) { /* For now, only support setting Authorized flag */ if (flags_or & WPA_STA_AUTHORIZED) - return madwifi_set_sta_authorized(priv, addr, 1); + return atheros_set_sta_authorized(priv, addr, 1); if (!(flags_and & WPA_STA_AUTHORIZED)) - return madwifi_set_sta_authorized(priv, addr, 0); + return atheros_set_sta_authorized(priv, addr, 0); return 0; } static int -madwifi_del_key(void *priv, const u8 *addr, int key_idx) +atheros_del_key(void *priv, const u8 *addr, int key_idx) { - struct madwifi_driver_data *drv = priv; + struct atheros_driver_data *drv = priv; struct ieee80211req_del_key wk; int ret; @@ -446,17 +449,17 @@ madwifi_del_key(void *priv, const u8 *addr, int key_idx) } static int -madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg, +atheros_set_key(const char *ifname, void *priv, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) { - struct madwifi_driver_data *drv = priv; + struct atheros_driver_data *drv = priv; struct ieee80211req_key wk; u_int8_t cipher; int ret; if (alg == WPA_ALG_NONE) - return madwifi_del_key(drv, addr, key_idx); + return atheros_del_key(drv, addr, key_idx); wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%s key_idx=%d", __func__, alg, ether_sprintf(addr), key_idx); @@ -471,6 +474,11 @@ madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg, case WPA_ALG_CCMP: cipher = IEEE80211_CIPHER_AES_CCM; break; +#ifdef CONFIG_IEEE80211W + case WPA_ALG_IGTK: + cipher = IEEE80211_CIPHER_AES_CMAC; + break; +#endif /* CONFIG_IEEE80211W */ default: printf("%s: unknown/unsupported algorithm %d\n", __func__, alg); @@ -486,10 +494,11 @@ madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg, memset(&wk, 0, sizeof(wk)); wk.ik_type = cipher; wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT; - if (addr == NULL) { + if (addr == NULL || is_broadcast_ether_addr(addr)) { memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); wk.ik_keyix = key_idx; - wk.ik_flags |= IEEE80211_KEY_DEFAULT; + if (set_tx) + wk.ik_flags |= IEEE80211_KEY_DEFAULT; } else { memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); wk.ik_keyix = IEEE80211_KEYIX_NONE; @@ -510,10 +519,10 @@ madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg, static int -madwifi_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx, +atheros_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx, u8 *seq) { - struct madwifi_driver_data *drv = priv; + struct atheros_driver_data *drv = priv; struct ieee80211req_key wk; wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d", @@ -557,20 +566,20 @@ madwifi_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx, static int -madwifi_flush(void *priv) +atheros_flush(void *priv) { u8 allsta[IEEE80211_ADDR_LEN]; memset(allsta, 0xff, IEEE80211_ADDR_LEN); - return madwifi_sta_deauth(priv, NULL, allsta, + return atheros_sta_deauth(priv, NULL, allsta, IEEE80211_REASON_AUTH_LEAVE); } static int -madwifi_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, +atheros_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, const u8 *addr) { - struct madwifi_driver_data *drv = priv; + struct atheros_driver_data *drv = priv; struct ieee80211req_sta_stats stats; memset(data, 0, sizeof(*data)); @@ -602,9 +611,9 @@ madwifi_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, static int -madwifi_sta_clear_stats(void *priv, const u8 *addr) +atheros_sta_clear_stats(void *priv, const u8 *addr) { - struct madwifi_driver_data *drv = priv; + struct atheros_driver_data *drv = priv; struct ieee80211req_mlme mlme; int ret; @@ -624,20 +633,61 @@ madwifi_sta_clear_stats(void *priv, const u8 *addr) static int -madwifi_set_opt_ie(void *priv, const u8 *ie, size_t ie_len) +atheros_set_opt_ie(void *priv, const u8 *ie, size_t ie_len) { - /* - * Do nothing; we setup parameters at startup that define the - * contents of the beacon information element. - */ + struct atheros_driver_data *drv = priv; + u8 buf[512]; + struct ieee80211req_getset_appiebuf *app_ie; + + wpa_printf(MSG_DEBUG, "%s buflen = %lu", __func__, + (unsigned long) ie_len); + wpa_hexdump(MSG_DEBUG, "atheros: set_generic_elem", ie, ie_len); + + wpabuf_free(drv->wpa_ie); + drv->wpa_ie = wpabuf_alloc_copy(ie, ie_len); + + app_ie = (struct ieee80211req_getset_appiebuf *) buf; + os_memcpy(&(app_ie->app_buf[0]), ie, ie_len); + app_ie->app_buflen = ie_len; + + app_ie->app_frmtype = IEEE80211_APPIE_FRAME_BEACON; + + /* append WPS IE for Beacon */ + if (drv->wps_beacon_ie != NULL) { + os_memcpy(&(app_ie->app_buf[ie_len]), + wpabuf_head(drv->wps_beacon_ie), + wpabuf_len(drv->wps_beacon_ie)); + app_ie->app_buflen = ie_len + wpabuf_len(drv->wps_beacon_ie); + } + wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF(Beacon)", + app_ie->app_buf, app_ie->app_buflen); + set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, app_ie, + sizeof(struct ieee80211req_getset_appiebuf) + + app_ie->app_buflen); + + /* append WPS IE for Probe Response */ + app_ie->app_frmtype = IEEE80211_APPIE_FRAME_PROBE_RESP; + if (drv->wps_probe_resp_ie != NULL) { + os_memcpy(&(app_ie->app_buf[ie_len]), + wpabuf_head(drv->wps_probe_resp_ie), + wpabuf_len(drv->wps_probe_resp_ie)); + app_ie->app_buflen = ie_len + + wpabuf_len(drv->wps_probe_resp_ie); + } else + app_ie->app_buflen = ie_len; + wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF(ProbeResp)", + app_ie->app_buf, app_ie->app_buflen); + set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, app_ie, + sizeof(struct ieee80211req_getset_appiebuf) + + app_ie->app_buflen); return 0; } static int -madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, +atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, int reason_code) { - struct madwifi_driver_data *drv = priv; + struct atheros_driver_data *drv = priv; struct ieee80211req_mlme mlme; int ret; @@ -658,10 +708,10 @@ madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, } static int -madwifi_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, +atheros_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, int reason_code) { - struct madwifi_driver_data *drv = priv; + struct atheros_driver_data *drv = priv; struct ieee80211req_mlme mlme; int ret; @@ -682,10 +732,10 @@ madwifi_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, } #ifdef CONFIG_WPS -static void madwifi_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 madwifi_driver_data *drv = ctx; + struct atheros_driver_data *drv = ctx; const struct ieee80211_mgmt *mgmt; u16 fc; union wpa_event_data event; @@ -703,6 +753,8 @@ static void madwifi_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, os_memset(&event, 0, sizeof(event)); event.rx_probe_req.sa = mgmt->sa; + event.rx_probe_req.da = mgmt->da; + event.rx_probe_req.bssid = mgmt->bssid; event.rx_probe_req.ie = mgmt->u.probe_req.variable; event.rx_probe_req.ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); @@ -710,67 +762,379 @@ static void madwifi_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, } #endif /* CONFIG_WPS */ -static int madwifi_receive_probe_req(struct madwifi_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 */ + +#ifdef CONFIG_HS20 +static void atheros_raw_recv_hs20(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct atheros_driver_data *drv = ctx; + const struct ieee80211_mgmt *mgmt; + u16 fc; + union wpa_event_data event; + + /* Send the Action frame for HS20 processing */ + + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.action.category) + + sizeof(mgmt->u.action.u.public_action)) + return; + + mgmt = (const struct ieee80211_mgmt *) buf; + + fc = le_to_host16(mgmt->frame_control); + if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT || + WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION || + mgmt->u.action.category != WLAN_ACTION_PUBLIC) + return; + + wpa_printf(MSG_DEBUG, "%s:Received Public Action frame", __func__); + + os_memset(&event, 0, sizeof(event)); + event.rx_mgmt.frame = (const u8 *) mgmt; + event.rx_mgmt.frame_len = len; + wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event); +} +#endif /* CONFIG_HS20 */ + +#if defined(CONFIG_WNM) && !defined(CONFIG_IEEE80211R) +static void atheros_raw_recv_11v(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; + + /* Do 11R processing for WNM 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_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; + default: + break; + } +} +#endif /* CONFIG_WNM */ + +#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_WNM) +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 */ +#if defined(CONFIG_WNM) && !defined(CONFIG_IEEE80211R) + atheros_raw_recv_11v(ctx, src_addr, buf, len); +#endif /* CONFIG_WNM */ +#ifdef CONFIG_HS20 + atheros_raw_recv_hs20(ctx, src_addr, buf, len); +#endif /* CONFIG_HS20 */ +} +#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 | + IEEE80211_FILTER_TYPE_ACTION); +#endif +#ifdef CONFIG_WNM + filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION; +#endif /* CONFIG_WNM */ +#ifdef CONFIG_HS20 + filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION; +#endif /* CONFIG_HS20 */ + 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, - madwifi_raw_receive, drv, 1); + atheros_raw_receive, drv, 1); if (drv->sock_raw == NULL) return -1; -#endif /* CONFIG_WPS */ +#endif /* CONFIG_WPS || CONFIG_IEEE80211R */ 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 -madwifi_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype) +atheros_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype) { - struct madwifi_driver_data *drv = priv; - u8 buf[256]; + struct atheros_driver_data *drv = priv; + u8 buf[512]; struct ieee80211req_getset_appiebuf *beac_ie; - wpa_printf(MSG_DEBUG, "%s buflen = %lu", __func__, - (unsigned long) len); + wpa_printf(MSG_DEBUG, "%s buflen = %lu frametype=%u", __func__, + (unsigned long) len, frametype); + wpa_hexdump(MSG_DEBUG, "atheros: IE", ie, len); beac_ie = (struct ieee80211req_getset_appiebuf *) buf; beac_ie->app_frmtype = frametype; beac_ie->app_buflen = len; - memcpy(&(beac_ie->app_buf[0]), ie, len); + os_memcpy(&(beac_ie->app_buf[0]), ie, len); + + /* append the WPA/RSN IE if it is set already */ + if (((frametype == IEEE80211_APPIE_FRAME_BEACON) || + (frametype == IEEE80211_APPIE_FRAME_PROBE_RESP)) && + (drv->wpa_ie != NULL)) { + wpa_hexdump_buf(MSG_DEBUG, "atheros: Append WPA/RSN IE", + drv->wpa_ie); + os_memcpy(&(beac_ie->app_buf[len]), wpabuf_head(drv->wpa_ie), + wpabuf_len(drv->wpa_ie)); + beac_ie->app_buflen += wpabuf_len(drv->wpa_ie); + } + wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF", + beac_ie->app_buf, beac_ie->app_buflen); return set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, beac_ie, - sizeof(struct ieee80211req_getset_appiebuf) + len); + sizeof(struct ieee80211req_getset_appiebuf) + + beac_ie->app_buflen); } static int -madwifi_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, - const struct wpabuf *proberesp) +atheros_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, + const struct wpabuf *proberesp, + const struct wpabuf *assocresp) { - if (madwifi_set_wps_ie(priv, beacon ? wpabuf_head(beacon) : NULL, + struct atheros_driver_data *drv = priv; + + wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - beacon", beacon); + wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - proberesp", + proberesp); + wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - assocresp", + assocresp); + wpabuf_free(drv->wps_beacon_ie); + drv->wps_beacon_ie = beacon ? wpabuf_dup(beacon) : NULL; + wpabuf_free(drv->wps_probe_resp_ie); + drv->wps_probe_resp_ie = proberesp ? wpabuf_dup(proberesp) : NULL; + + atheros_set_wps_ie(priv, assocresp ? wpabuf_head(assocresp) : NULL, + assocresp ? wpabuf_len(assocresp) : 0, + IEEE80211_APPIE_FRAME_ASSOC_RESP); + if (atheros_set_wps_ie(priv, beacon ? wpabuf_head(beacon) : NULL, beacon ? wpabuf_len(beacon) : 0, IEEE80211_APPIE_FRAME_BEACON)) return -1; - return madwifi_set_wps_ie(priv, + return atheros_set_wps_ie(priv, proberesp ? wpabuf_head(proberesp) : NULL, proberesp ? wpabuf_len(proberesp): 0, IEEE80211_APPIE_FRAME_PROBE_RESP); } #else /* CONFIG_WPS */ -#define madwifi_set_ap_wps_ie NULL +#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 -madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) +atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) { struct hostapd_data *hapd = drv->hapd; struct ieee80211req_wpaie ie; @@ -791,17 +1155,21 @@ madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) __func__, strerror(errno)); goto no_ie; } - wpa_hexdump(MSG_MSGDUMP, "madwifi req WPA IE", + wpa_hexdump(MSG_MSGDUMP, "atheros req WPA IE", ie.wpa_ie, IEEE80211_MAX_OPT_IE); - wpa_hexdump(MSG_MSGDUMP, "madwifi req RSN IE", + wpa_hexdump(MSG_MSGDUMP, "atheros req RSN IE", ie.rsn_ie, IEEE80211_MAX_OPT_IE); +#ifdef ATH_WPS_IE + wpa_hexdump(MSG_MSGDUMP, "atheros req WPS IE", + ie.wps_ie, IEEE80211_MAX_OPT_IE); +#endif /* ATH_WPS_IE */ iebuf = ie.wpa_ie; - /* madwifi seems to return some random data if WPA/RSN IE is not set. + /* atheros seems to return some random data if WPA/RSN IE is not set. * Assume the IE was not included if the IE type is unknown. */ if (iebuf[0] != WLAN_EID_VENDOR_SPECIFIC) iebuf[1] = 0; if (iebuf[1] == 0 && ie.rsn_ie[1] > 0) { - /* madwifi-ng svn #1453 added rsn_ie. Use it, if wpa_ie was not + /* atheros-ng svn #1453 added rsn_ie. Use it, if wpa_ie was not * set. This is needed for WPA2. */ iebuf = ie.rsn_ie; if (iebuf[0] != WLAN_EID_RSN) @@ -809,13 +1177,23 @@ madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) } ielen = iebuf[1]; + +#ifdef ATH_WPS_IE + /* if WPS IE is present, preference is given to WPS */ + if (ie.wps_ie && + (ie.wps_ie[1] > 0 && (ie.wps_ie[0] == WLAN_EID_VENDOR_SPECIFIC))) { + iebuf = ie.wps_ie; + ielen = ie.wps_ie[1]; + } +#endif /* ATH_WPS_IE */ + if (ielen == 0) iebuf = NULL; else ielen += 2; no_ie: - drv_event_assoc(hapd, addr, iebuf, ielen); + drv_event_assoc(hapd, addr, iebuf, ielen, 0); if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { /* Cached accounting data is not valid anymore. */ @@ -825,7 +1203,7 @@ no_ie: } static void -madwifi_wireless_event_wireless_custom(struct madwifi_driver_data *drv, +atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv, char *custom, char *end) { wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); @@ -883,6 +1261,9 @@ madwifi_wireless_event_wireless_custom(struct madwifi_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) || defined(CONFIG_HS20) +#define MGMT_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 @@ -890,21 +1271,139 @@ madwifi_wireless_event_wireless_custom(struct madwifi_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) { + if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) { wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req event " "length %d", len); return; } - madwifi_raw_receive(drv, NULL, - (u8 *) custom + WPS_FRAM_TAG_SIZE, len); -#endif /* CONFIG_WPS */ + atheros_raw_receive(drv, NULL, + (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); + } 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 + MGMT_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 + MGMT_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 + MGMT_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 + MGMT_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 + MGMT_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 + MGMT_FRAM_TAG_SIZE, len); +#endif /* CONFIG_WPS or CONFIG_IEEE80211R */ + } +} + +/* +* Handle size of data problem. WEXT only allows data of 256 bytes for custom +* events, and p2p data can be much bigger. So the athr driver sends a small +* event telling me to collect the big data with an ioctl. +* On the first event, send all pending events to supplicant. +*/ +static void fetch_pending_big_events(struct atheros_driver_data *drv) +{ + union wpa_event_data event; + const struct ieee80211_mgmt *mgmt; + u8 tbuf[IW_PRIV_SIZE_MASK]; /* max size is 2047 bytes */ + u16 fc, stype; + struct iwreq iwr; + size_t data_len; + u32 freq, frame_type; + + while (1) { + os_memset(&iwr, 0, sizeof(iwr)); + os_strncpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + + iwr.u.data.pointer = (void *) tbuf; + iwr.u.data.length = sizeof(tbuf); + iwr.u.data.flags = IEEE80211_IOC_P2P_FETCH_FRAME; + + if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_P2P_BIG_PARAM, &iwr) + < 0) { + if (errno == ENOSPC) { + wpa_printf(MSG_DEBUG, "%s:%d exit", + __func__, __LINE__); + return; + } + wpa_printf(MSG_DEBUG, "athr: %s: P2P_BIG_PARAM[" + "P2P_FETCH_FRAME] failed: %s", + __func__, strerror(errno)); + return; + } + data_len = iwr.u.data.length; + wpa_hexdump(MSG_DEBUG, "athr: P2P_FETCH_FRAME data", + (u8 *) tbuf, data_len); + if (data_len < sizeof(freq) + sizeof(frame_type) + 24) { + wpa_printf(MSG_DEBUG, "athr: frame too short"); + continue; + } + os_memcpy(&freq, tbuf, sizeof(freq)); + os_memcpy(&frame_type, &tbuf[sizeof(freq)], + sizeof(frame_type)); + mgmt = (void *) &tbuf[sizeof(freq) + sizeof(frame_type)]; + data_len -= sizeof(freq) + sizeof(frame_type); + + if (frame_type == IEEE80211_EV_RX_MGMT) { + fc = le_to_host16(mgmt->frame_control); + stype = WLAN_FC_GET_STYPE(fc); + + wpa_printf(MSG_DEBUG, "athr: EV_RX_MGMT stype=%u " + "freq=%u len=%u", stype, freq, (int) data_len); + + if (stype == WLAN_FC_STYPE_ACTION) { + os_memset(&event, 0, sizeof(event)); + event.rx_mgmt.frame = (const u8 *) mgmt; + event.rx_mgmt.frame_len = data_len; + wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, + &event); + continue; + } + } else { + wpa_printf(MSG_DEBUG, "athr: %s unknown type %d", + __func__, frame_type); + continue; + } + } +} + +static void +atheros_wireless_event_atheros_custom(struct atheros_driver_data *drv, + int opcode, char *buf, int len) +{ + switch (opcode) { + case IEEE80211_EV_RX_MGMT: + wpa_printf(MSG_DEBUG, "WEXT: EV_RX_MGMT"); + fetch_pending_big_events(drv); + break; + default: + break; } } static void -madwifi_wireless_event_wireless(struct madwifi_driver_data *drv, +atheros_wireless_event_wireless(struct atheros_driver_data *drv, char *data, int len) { struct iw_event iwe_buf, *iwe = &iwe_buf; @@ -943,7 +1442,7 @@ madwifi_wireless_event_wireless(struct madwifi_driver_data *drv, (u8 *) iwe->u.addr.sa_data); break; case IWEVREGISTERED: - madwifi_new_sta(drv, (u8 *) iwe->u.addr.sa_data); + atheros_new_sta(drv, (u8 *) iwe->u.addr.sa_data); break; case IWEVASSOCREQIE: /* Driver hack.. Use IWEVASSOCREQIE to bypass @@ -958,8 +1457,15 @@ madwifi_wireless_event_wireless(struct madwifi_driver_data *drv, return; /* XXX */ memcpy(buf, custom, iwe->u.data.length); buf[iwe->u.data.length] = '\0'; - madwifi_wireless_event_wireless_custom( - drv, buf, buf + iwe->u.data.length); + + if (iwe->u.data.flags != 0) { + atheros_wireless_event_atheros_custom( + drv, (int) iwe->u.data.flags, + buf, len); + } else { + atheros_wireless_event_wireless_custom( + drv, buf, buf + iwe->u.data.length); + } free(buf); break; } @@ -970,10 +1476,10 @@ madwifi_wireless_event_wireless(struct madwifi_driver_data *drv, static void -madwifi_wireless_event_rtm_newlink(void *ctx, +atheros_wireless_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi, u8 *buf, size_t len) { - struct madwifi_driver_data *drv = ctx; + struct atheros_driver_data *drv = ctx; int attrlen, rta_len; struct rtattr *attr; @@ -986,7 +1492,7 @@ madwifi_wireless_event_rtm_newlink(void *ctx, rta_len = RTA_ALIGN(sizeof(struct rtattr)); while (RTA_OK(attr, attrlen)) { if (attr->rta_type == IFLA_WIRELESS) { - madwifi_wireless_event_wireless( + atheros_wireless_event_wireless( drv, ((char *) attr) + rta_len, attr->rta_len - rta_len); } @@ -996,7 +1502,7 @@ madwifi_wireless_event_rtm_newlink(void *ctx, static int -madwifi_get_we_version(struct madwifi_driver_data *drv) +atheros_get_we_version(struct atheros_driver_data *drv) { struct iw_range *range; struct iwreq iwr; @@ -1036,23 +1542,23 @@ madwifi_get_we_version(struct madwifi_driver_data *drv) drv->we_version = range->we_version_compiled; } - free(range); + os_free(range); return 0; } static int -madwifi_wireless_event_init(struct madwifi_driver_data *drv) +atheros_wireless_event_init(struct atheros_driver_data *drv) { struct netlink_config *cfg; - madwifi_get_we_version(drv); + atheros_get_we_version(drv); cfg = os_zalloc(sizeof(*cfg)); if (cfg == NULL) return -1; cfg->ctx = drv; - cfg->newlink_cb = madwifi_wireless_event_rtm_newlink; + cfg->newlink_cb = atheros_wireless_event_rtm_newlink; drv->netlink = netlink_init(cfg); if (drv->netlink == NULL) { os_free(cfg); @@ -1064,10 +1570,10 @@ madwifi_wireless_event_init(struct madwifi_driver_data *drv) static int -madwifi_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, - int encrypt, const u8 *own_addr) +atheros_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, + int encrypt, const u8 *own_addr, u32 flags) { - struct madwifi_driver_data *drv = priv; + struct atheros_driver_data *drv = priv; unsigned char buf[3000]; unsigned char *bp = buf; struct l2_ethhdr *eth; @@ -1107,22 +1613,22 @@ madwifi_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, static void handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { - struct madwifi_driver_data *drv = ctx; + struct atheros_driver_data *drv = ctx; drv_event_eapol_rx(drv->hapd, src_addr, buf + sizeof(struct l2_ethhdr), len - sizeof(struct l2_ethhdr)); } static void * -madwifi_init(struct hostapd_data *hapd, struct wpa_init_params *params) +atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params) { - struct madwifi_driver_data *drv; + struct atheros_driver_data *drv; struct ifreq ifr; struct iwreq iwr; char brname[IFNAMSIZ]; - drv = os_zalloc(sizeof(struct madwifi_driver_data)); + drv = os_zalloc(sizeof(struct atheros_driver_data)); if (drv == NULL) { - printf("Could not allocate memory for madwifi driver data\n"); + printf("Could not allocate memory for atheros driver data\n"); return NULL; } @@ -1148,6 +1654,7 @@ madwifi_init(struct hostapd_data *hapd, struct wpa_init_params *params) goto bad; if (l2_packet_get_own_addr(drv->sock_xmit, params->own_addr)) goto bad; + os_memcpy(drv->own_addr, params->own_addr, ETH_ALEN); if (params->bridge[0]) { wpa_printf(MSG_DEBUG, "Configure bridge %s for EAPOL traffic.", params->bridge[0]); @@ -1179,15 +1686,19 @@ madwifi_init(struct hostapd_data *hapd, struct wpa_init_params *params) /* mark down during setup */ linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0); - madwifi_set_privacy(drv, 0); /* default to no privacy */ + atheros_set_privacy(drv, 0); /* default to no privacy */ - madwifi_receive_probe_req(drv); + if (atheros_receive_pkt(drv)) + goto bad; - if (madwifi_wireless_event_init(drv)) + if (atheros_wireless_event_init(drv)) goto bad; return drv; bad: + atheros_reset_appfilter(drv); + if (drv->sock_raw) + l2_packet_deinit(drv->sock_raw); if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit) l2_packet_deinit(drv->sock_recv); if (drv->sock_xmit != NULL) @@ -1201,10 +1712,11 @@ bad: static void -madwifi_deinit(void *priv) +atheros_deinit(void *priv) { - struct madwifi_driver_data *drv = 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) @@ -1215,13 +1727,16 @@ madwifi_deinit(void *priv) l2_packet_deinit(drv->sock_xmit); if (drv->sock_raw) l2_packet_deinit(drv->sock_raw); + wpabuf_free(drv->wpa_ie); + wpabuf_free(drv->wps_beacon_ie); + wpabuf_free(drv->wps_probe_resp_ie); free(drv); } static int -madwifi_set_ssid(void *priv, const u8 *buf, int len) +atheros_set_ssid(void *priv, const u8 *buf, int len) { - struct madwifi_driver_data *drv = priv; + struct atheros_driver_data *drv = priv; struct iwreq iwr; memset(&iwr, 0, sizeof(iwr)); @@ -1239,16 +1754,17 @@ madwifi_set_ssid(void *priv, const u8 *buf, int len) } static int -madwifi_get_ssid(void *priv, u8 *buf, int len) +atheros_get_ssid(void *priv, u8 *buf, int len) { - struct madwifi_driver_data *drv = priv; + struct atheros_driver_data *drv = priv; struct iwreq iwr; int ret = 0; memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.essid.pointer = (caddr_t) buf; - iwr.u.essid.length = len; + iwr.u.essid.length = (len > IW_ESSID_MAX_SIZE) ? + IW_ESSID_MAX_SIZE : len; if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { perror("ioctl[SIOCGIWESSID]"); @@ -1260,39 +1776,381 @@ madwifi_get_ssid(void *priv, u8 *buf, int len) } static int -madwifi_set_countermeasures(void *priv, int enabled) +atheros_set_countermeasures(void *priv, int enabled) { - struct madwifi_driver_data *drv = priv; + struct atheros_driver_data *drv = priv; wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled); } static int -madwifi_commit(void *priv) +atheros_commit(void *priv) { - struct madwifi_driver_data *drv = priv; + struct atheros_driver_data *drv = priv; return linux_set_iface_flags(drv->ioctl_sock, drv->iface, 1); } +static int atheros_set_authmode(void *priv, int auth_algs) +{ + int authmode; + + if ((auth_algs & WPA_AUTH_ALG_OPEN) && + (auth_algs & WPA_AUTH_ALG_SHARED)) + authmode = IEEE80211_AUTH_AUTO; + else if (auth_algs & WPA_AUTH_ALG_OPEN) + authmode = IEEE80211_AUTH_OPEN; + else if (auth_algs & WPA_AUTH_ALG_SHARED) + authmode = IEEE80211_AUTH_SHARED; + else + return -1; + + return set80211param(priv, IEEE80211_PARAM_AUTHMODE, authmode); +} + +static int atheros_set_ap(void *priv, struct wpa_driver_ap_params *params) +{ + /* + * TODO: Use this to replace set_authmode, set_privacy, set_ieee8021x, + * set_generic_elem, and hapd_set_ssid. + */ + + wpa_printf(MSG_DEBUG, "atheros: set_ap - pairwise_ciphers=0x%x " + "group_cipher=0x%x key_mgmt_suites=0x%x auth_algs=0x%x " + "wpa_version=0x%x privacy=%d interworking=%d", + params->pairwise_ciphers, params->group_cipher, + params->key_mgmt_suites, params->auth_algs, + params->wpa_version, params->privacy, params->interworking); + wpa_hexdump_ascii(MSG_DEBUG, "atheros: SSID", + params->ssid, params->ssid_len); + if (params->hessid) + wpa_printf(MSG_DEBUG, "atheros: HESSID " MACSTR, + MAC2STR(params->hessid)); + wpa_hexdump_buf(MSG_DEBUG, "atheros: beacon_ies", + params->beacon_ies); + wpa_hexdump_buf(MSG_DEBUG, "atheros: proberesp_ies", + params->proberesp_ies); + wpa_hexdump_buf(MSG_DEBUG, "atheros: assocresp_ies", + params->assocresp_ies); + + 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 */ + + +/* Use only to set a big param, get will not work. */ +static int +set80211big(struct atheros_driver_data *drv, int op, const void *data, int len) +{ + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + + iwr.u.data.pointer = (void *) data; + iwr.u.data.length = len; + iwr.u.data.flags = op; + wpa_printf(MSG_DEBUG, "%s: op=0x%x=%d (%s) len=0x%x", + __func__, op, op, athr_get_param_name(op), len); + + if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_P2P_BIG_PARAM, &iwr) < 0) { + wpa_printf(MSG_DEBUG, "%s: op=0x%x (%s) subop=0x%x=%d " + "value=0x%x,0x%x failed: %d (%s)", + __func__, op, athr_get_ioctl_name(op), iwr.u.mode, + iwr.u.mode, iwr.u.data.length, + iwr.u.data.flags, errno, strerror(errno)); + return -1; + } + return 0; +} + + +static int atheros_send_action(void *priv, unsigned int freq, + unsigned int wait, + const u8 *dst, const u8 *src, + const u8 *bssid, + const u8 *data, size_t data_len, int no_cck) +{ + struct atheros_driver_data *drv = priv; + struct ieee80211_p2p_send_action *act; + int res; + + act = os_zalloc(sizeof(*act) + data_len); + if (act == NULL) + return -1; + act->freq = freq; + os_memcpy(act->dst_addr, dst, ETH_ALEN); + os_memcpy(act->src_addr, src, ETH_ALEN); + os_memcpy(act->bssid, bssid, ETH_ALEN); + os_memcpy(act + 1, data, data_len); + wpa_printf(MSG_DEBUG, "%s: freq=%d, wait=%u, dst=" MACSTR ", src=" + MACSTR ", bssid=" MACSTR, + __func__, act->freq, wait, MAC2STR(act->dst_addr), + MAC2STR(act->src_addr), MAC2STR(act->bssid)); + wpa_hexdump(MSG_MSGDUMP, "athr: act", (u8 *) act, sizeof(*act)); + wpa_hexdump(MSG_MSGDUMP, "athr: data", data, data_len); + + res = set80211big(drv, IEEE80211_IOC_P2P_SEND_ACTION, + act, sizeof(*act) + data_len); + os_free(act); + return res; +} + + +#ifdef CONFIG_WNM +static int athr_wnm_tfs(struct atheros_driver_data *drv, const u8* peer, + u8 *ie, u16 *len, enum wnm_oper oper) +{ +#define IEEE80211_APPIE_MAX 1024 /* max appie buffer size */ + u8 buf[IEEE80211_APPIE_MAX]; + struct ieee80211req_getset_appiebuf *tfs_ie; + u16 val; + + wpa_printf(MSG_DEBUG, "atheros: ifname=%s, WNM TFS IE oper=%d " MACSTR, + drv->iface, oper, MAC2STR(peer)); + + switch (oper) { + case WNM_SLEEP_TFS_REQ_IE_SET: + if (*len > IEEE80211_APPIE_MAX - + sizeof(struct ieee80211req_getset_appiebuf)) { + wpa_printf(MSG_DEBUG, "TFS Req IE(s) too large"); + return -1; + } + tfs_ie = (struct ieee80211req_getset_appiebuf *) buf; + tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM; + tfs_ie->app_buflen = ETH_ALEN + 2 + 2 + *len; + + /* Command header for driver */ + os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN); + val = oper; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2); + val = *len; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2); + + /* copy the ie */ + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2 + 2, ie, *len); + + if (set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, tfs_ie, + IEEE80211_APPIE_MAX)) { + wpa_printf(MSG_DEBUG, "%s: Failed to set WNM TFS IE: " + "%s", __func__, strerror(errno)); + return -1; + } + break; + case WNM_SLEEP_TFS_RESP_IE_ADD: + tfs_ie = (struct ieee80211req_getset_appiebuf *) buf; + tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM; + tfs_ie->app_buflen = IEEE80211_APPIE_MAX - + sizeof(struct ieee80211req_getset_appiebuf); + /* Command header for driver */ + os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN); + val = oper; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2); + val = 0; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2); + + if (set80211priv(drv, IEEE80211_IOCTL_GET_APPIEBUF, tfs_ie, + IEEE80211_APPIE_MAX)) { + wpa_printf(MSG_DEBUG, "%s: Failed to get WNM TFS IE: " + "%s", __func__, strerror(errno)); + return -1; + } + + *len = tfs_ie->app_buflen; + os_memcpy(ie, &(tfs_ie->app_buf[0]), *len); + wpa_printf(MSG_DEBUG, "atheros: %c len=%d", tfs_ie->app_buf[0], + *len); + break; + case WNM_SLEEP_TFS_RESP_IE_NONE: + *len = 0; + break; + case WNM_SLEEP_TFS_IE_DEL: + tfs_ie = (struct ieee80211req_getset_appiebuf *) buf; + tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM; + tfs_ie->app_buflen = IEEE80211_APPIE_MAX - + sizeof(struct ieee80211req_getset_appiebuf); + /* Command header for driver */ + os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN); + val = oper; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2); + val = 0; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2); + + if (set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, tfs_ie, + IEEE80211_APPIE_MAX)) { + wpa_printf(MSG_DEBUG, "%s: Failed to set WNM TFS IE: " + "%s", __func__, strerror(errno)); + return -1; + } + break; + default: + wpa_printf(MSG_DEBUG, "Unsupported TFS oper %d", oper); + break; + } + + return 0; +} + + +static int atheros_wnm_sleep(struct atheros_driver_data *drv, + const u8 *peer, enum wnm_oper oper) +{ + u8 *data, *pos; + size_t dlen; + int ret; + u16 val; + + wpa_printf(MSG_DEBUG, "atheros: WNM-Sleep Oper %d, " MACSTR, + oper, MAC2STR(peer)); + + dlen = ETH_ALEN + 2 + 2; + data = os_malloc(dlen); + if (data == NULL) + return -1; + + /* Command header for driver */ + pos = data; + os_memcpy(pos, peer, ETH_ALEN); + pos += ETH_ALEN; + + val = oper; + os_memcpy(pos, &val, 2); + pos += 2; + + val = 0; + os_memcpy(pos, &val, 2); + + ret = atheros_set_wps_ie(drv, data, dlen, IEEE80211_APPIE_FRAME_WNM); + + os_free(data); + + return ret; +} + + +static int atheros_wnm_oper(void *priv, enum wnm_oper oper, const u8 *peer, + u8 *buf, u16 *buf_len) +{ + struct atheros_driver_data *drv = priv; + + switch (oper) { + case WNM_SLEEP_ENTER_CONFIRM: + case WNM_SLEEP_ENTER_FAIL: + case WNM_SLEEP_EXIT_CONFIRM: + case WNM_SLEEP_EXIT_FAIL: + return atheros_wnm_sleep(drv, peer, oper); + case WNM_SLEEP_TFS_REQ_IE_SET: + case WNM_SLEEP_TFS_RESP_IE_ADD: + case WNM_SLEEP_TFS_RESP_IE_NONE: + case WNM_SLEEP_TFS_IE_DEL: + return athr_wnm_tfs(drv, peer, buf, buf_len, oper); + default: + wpa_printf(MSG_DEBUG, "atheros: Unsupported WNM operation %d", + oper); + return -1; + } +} +#endif /* CONFIG_WNM */ + + const struct wpa_driver_ops wpa_driver_atheros_ops = { .name = "atheros", - .hapd_init = madwifi_init, - .hapd_deinit = madwifi_deinit, - .set_ieee8021x = madwifi_set_ieee8021x, - .set_privacy = madwifi_set_privacy, - .set_key = madwifi_set_key, - .get_seqnum = madwifi_get_seqnum, - .flush = madwifi_flush, - .set_generic_elem = madwifi_set_opt_ie, - .sta_set_flags = madwifi_sta_set_flags, - .read_sta_data = madwifi_read_sta_driver_data, - .hapd_send_eapol = madwifi_send_eapol, - .sta_disassoc = madwifi_sta_disassoc, - .sta_deauth = madwifi_sta_deauth, - .hapd_set_ssid = madwifi_set_ssid, - .hapd_get_ssid = madwifi_get_ssid, - .set_countermeasures = madwifi_set_countermeasures, - .sta_clear_stats = madwifi_sta_clear_stats, - .commit = madwifi_commit, - .set_ap_wps_ie = madwifi_set_ap_wps_ie, + .hapd_init = atheros_init, + .hapd_deinit = atheros_deinit, + .set_ieee8021x = atheros_set_ieee8021x, + .set_privacy = atheros_set_privacy, + .set_key = atheros_set_key, + .get_seqnum = atheros_get_seqnum, + .flush = atheros_flush, + .set_generic_elem = atheros_set_opt_ie, + .sta_set_flags = atheros_sta_set_flags, + .read_sta_data = atheros_read_sta_driver_data, + .hapd_send_eapol = atheros_send_eapol, + .sta_disassoc = atheros_sta_disassoc, + .sta_deauth = atheros_sta_deauth, + .hapd_set_ssid = atheros_set_ssid, + .hapd_get_ssid = atheros_get_ssid, + .set_countermeasures = atheros_set_countermeasures, + .sta_clear_stats = atheros_sta_clear_stats, + .commit = atheros_commit, + .set_ap_wps_ie = atheros_set_ap_wps_ie, + .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 */ + .send_action = atheros_send_action, +#ifdef CONFIG_WNM + .wnm_oper = atheros_wnm_oper, +#endif /* CONFIG_WNM */ }; diff --git a/src/drivers/driver_atmel.c b/src/drivers/driver_atmel.c deleted file mode 100644 index cbec6c38d78d4..0000000000000 --- a/src/drivers/driver_atmel.c +++ /dev/null @@ -1,499 +0,0 @@ -/* - * WPA Supplicant - Driver interaction with Atmel Wireless LAN drivers - * Copyright (c) 2000-2005, ATMEL Corporation - * 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. - */ - -/****************************************************************************** - Copyright 2000-2001 ATMEL Corporation. - - WPA Supplicant - driver interaction with Atmel Wireless lan drivers. - - This is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Atmel wireless lan drivers; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -******************************************************************************/ - -/* - * Alternatively, this software may be distributed under the terms of BSD - * license. - */ - -#include "includes.h" -#include <sys/ioctl.h> - -#include "wireless_copy.h" -#include "common.h" -#include "driver.h" -#include "driver_wext.h" - -struct wpa_driver_atmel_data { - void *wext; /* private data for driver_wext */ - void *ctx; - char ifname[IFNAMSIZ + 1]; - int sock; -}; - - -#define ATMEL_WPA_IOCTL (SIOCIWFIRSTPRIV + 2) -#define ATMEL_WPA_IOCTL_PARAM (SIOCIWFIRSTPRIV + 3) -#define ATMEL_WPA_IOCTL_GET_PARAM (SIOCIWFIRSTPRIV + 4) - - -/* ATMEL_WPA_IOCTL ioctl() cmd: */ -enum { - SET_WPA_ENCRYPTION = 1, - SET_CIPHER_SUITES = 2, - MLME_STA_DEAUTH = 3, - MLME_STA_DISASSOC = 4 -}; - -/* ATMEL_WPA_IOCTL_PARAM ioctl() cmd: */ -enum { - ATMEL_PARAM_WPA = 1, - ATMEL_PARAM_PRIVACY_INVOKED = 2, - ATMEL_PARAM_WPA_TYPE = 3 -}; - -#define MAX_KEY_LENGTH 40 - -struct atmel_param{ - unsigned char sta_addr[6]; - int cmd; - u8 alg; - u8 key_idx; - u8 set_tx; - u8 seq[8]; - u8 seq_len; - u16 key_len; - u8 key[MAX_KEY_LENGTH]; - struct{ - int reason_code; - u8 state; - }mlme; - u8 pairwise_suite; - u8 group_suite; - u8 key_mgmt_suite; -}; - - - -static int atmel_ioctl(struct wpa_driver_atmel_data *drv, - struct atmel_param *param, - int len, int show_err) -{ - struct iwreq iwr; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - iwr.u.data.pointer = (caddr_t) param; - iwr.u.data.length = len; - - if (ioctl(drv->sock, ATMEL_WPA_IOCTL, &iwr) < 0) { - int ret; - ret = errno; - if (show_err) - perror("ioctl[ATMEL_WPA_IOCTL]"); - return ret; - } - - return 0; -} - - -static int atmel2param(struct wpa_driver_atmel_data *drv, int param, int value) -{ - struct iwreq iwr; - int *i, ret = 0; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - i = (int *) iwr.u.name; - *i++ = param; - *i++ = value; - - if (ioctl(drv->sock, ATMEL_WPA_IOCTL_PARAM, &iwr) < 0) { - perror("ioctl[ATMEL_WPA_IOCTL_PARAM]"); - ret = -1; - } - return ret; -} - - -#if 0 -static int wpa_driver_atmel_set_wpa_ie(struct wpa_driver_atmel_data *drv, - const char *wpa_ie, size_t wpa_ie_len) -{ - struct atmel_param *param; - int res; - size_t blen = ATMEL_GENERIC_ELEMENT_HDR_LEN + wpa_ie_len; - if (blen < sizeof(*param)) - blen = sizeof(*param); - - param = os_zalloc(blen); - if (param == NULL) - return -1; - - param->cmd = ATMEL_SET_GENERIC_ELEMENT; - param->u.generic_elem.len = wpa_ie_len; - os_memcpy(param->u.generic_elem.data, wpa_ie, wpa_ie_len); - res = atmel_ioctl(drv, param, blen, 1); - - os_free(param); - - return res; -} -#endif - - -static int wpa_driver_atmel_set_wpa(void *priv, int enabled) -{ - struct wpa_driver_atmel_data *drv = priv; - int ret = 0; - - printf("wpa_driver_atmel_set_wpa %s\n", drv->ifname); - - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); - -#if 0 - if (!enabled && wpa_driver_atmel_set_wpa_ie(drv, NULL, 0) < 0) - ret = -1; -#endif - if (atmel2param(drv, ATMEL_PARAM_PRIVACY_INVOKED, enabled) < 0) - ret = -1; - if (atmel2param(drv, ATMEL_PARAM_WPA, enabled) < 0) - ret = -1; - - return ret; -} - - -static int wpa_driver_atmel_set_key(const char *ifname, void *priv, - enum wpa_alg alg, const u8 *addr, - int key_idx, int set_tx, - const u8 *seq, size_t seq_len, - const u8 *key, size_t key_len) -{ - struct wpa_driver_atmel_data *drv = priv; - int ret = 0; - struct atmel_param *param; - u8 *buf; - u8 alg_type; - - size_t blen; - char *alg_name; - - switch (alg) { - case WPA_ALG_NONE: - alg_name = "none"; - alg_type = 0; - break; - case WPA_ALG_WEP: - alg_name = "WEP"; - alg_type = 1; - break; - case WPA_ALG_TKIP: - alg_name = "TKIP"; - alg_type = 2; - break; - case WPA_ALG_CCMP: - alg_name = "CCMP"; - alg_type = 3; - break; - default: - return -1; - } - - wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu " - "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx, - (unsigned long) seq_len, (unsigned long) key_len); - - if (seq_len > 8) - return -2; - - blen = sizeof(*param) + key_len; - buf = os_zalloc(blen); - if (buf == NULL) - return -1; - - param = (struct atmel_param *) buf; - - param->cmd = SET_WPA_ENCRYPTION; - - if (addr == NULL) - os_memset(param->sta_addr, 0xff, ETH_ALEN); - else - os_memcpy(param->sta_addr, addr, ETH_ALEN); - - param->alg = alg_type; - param->key_idx = key_idx; - param->set_tx = set_tx; - os_memcpy(param->seq, seq, seq_len); - param->seq_len = seq_len; - param->key_len = key_len; - os_memcpy((u8 *)param->key, key, key_len); - - if (atmel_ioctl(drv, param, blen, 1)) { - wpa_printf(MSG_WARNING, "Failed to set encryption."); - /* TODO: show key error*/ - ret = -1; - } - os_free(buf); - - return ret; -} - - -static int wpa_driver_atmel_set_countermeasures(void *priv, - int enabled) -{ - /* FIX */ - printf("wpa_driver_atmel_set_countermeasures - not yet " - "implemented\n"); - return 0; -} - - -static int wpa_driver_atmel_mlme(void *priv, const u8 *addr, int cmd, - int reason_code) -{ - struct wpa_driver_atmel_data *drv = priv; - struct atmel_param param; - int ret; - int mgmt_error = 0xaa; - - os_memset(¶m, 0, sizeof(param)); - os_memcpy(param.sta_addr, addr, ETH_ALEN); - param.cmd = cmd; - param.mlme.reason_code = reason_code; - param.mlme.state = mgmt_error; - ret = atmel_ioctl(drv, ¶m, sizeof(param), 1); - return ret; -} - - -#if 0 -static int wpa_driver_atmel_set_suites(struct wpa_driver_atmel_data *drv, - u8 pairwise_suite, u8 group_suite, - u8 key_mgmt_suite) -{ - struct atmel_param param; - int ret; - - os_memset(¶m, 0, sizeof(param)); - param.cmd = SET_CIPHER_SUITES; - param.pairwise_suite = pairwise_suite; - param.group_suite = group_suite; - param.key_mgmt_suite = key_mgmt_suite; - - ret = atmel_ioctl(drv, ¶m, sizeof(param), 1); - return ret; -} -#endif - - -static int wpa_driver_atmel_deauthenticate(void *priv, const u8 *addr, - int reason_code) -{ - struct wpa_driver_atmel_data *drv = priv; - printf("wpa_driver_atmel_deauthenticate\n"); - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - return wpa_driver_atmel_mlme(drv, addr, MLME_STA_DEAUTH, - reason_code); - -} - - -static int wpa_driver_atmel_disassociate(void *priv, const u8 *addr, - int reason_code) -{ - struct wpa_driver_atmel_data *drv = priv; - printf("wpa_driver_atmel_disassociate\n"); - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - return wpa_driver_atmel_mlme(drv, addr, MLME_STA_DISASSOC, - reason_code); - -} - - -#if 0 -/* Atmel driver uses specific values for each cipher suite */ -static int convertSuiteToDriver(enum wpa_cipher suite) -{ - u8 suite_type; - - switch(suite) { - case CIPHER_NONE: - suite_type = 0; - break; - case CIPHER_WEP40: - suite_type = 1; - break; - case CIPHER_TKIP: - suite_type = 2; - break; - case CIPHER_WEP104: - suite_type = 5; - break; - case CIPHER_CCMP: - suite_type = 3; - break; - default: - suite_type = 2; - } - - return suite_type; - -} -#endif - -static int -wpa_driver_atmel_associate(void *priv, - struct wpa_driver_associate_params *params) -{ - struct wpa_driver_atmel_data *drv = priv; - int ret = 0; -#if 0 - u8 pairwise_suite_driver; - u8 group_suite_driver; - u8 key_mgmt_suite_driver; - - pairwise_suite_driver = convertSuiteToDriver(params->pairwise_suite); - group_suite_driver = convertSuiteToDriver(params->group_suite); - key_mgmt_suite_driver = convertSuiteToDriver(params->key_mgmt_suite); - - if (wpa_driver_atmel_set_suites(drv, pairwise_suite_driver, - group_suite_driver, - key_mgmt_suite_driver) < 0){ - printf("wpa_driver_atmel_set_suites.\n"); - ret = -1; - } - if (wpa_driver_wext_set_freq(drv->wext, params->freq) < 0) { - printf("wpa_driver_atmel_set_freq.\n"); - ret = -1; - } -#endif - if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, params->ssid_len) - < 0) { - printf("FAILED : wpa_driver_atmel_set_ssid.\n"); - ret = -1; - } - if (wpa_driver_wext_set_bssid(drv->wext, params->bssid) < 0) { - printf("FAILED : wpa_driver_atmel_set_bssid.\n"); - ret = -1; - } - - return ret; -} - - -static int wpa_driver_atmel_get_bssid(void *priv, u8 *bssid) -{ - struct wpa_driver_atmel_data *drv = priv; - return wpa_driver_wext_get_bssid(drv->wext, bssid); -} - - -static int wpa_driver_atmel_get_ssid(void *priv, u8 *ssid) -{ - struct wpa_driver_atmel_data *drv = priv; - return wpa_driver_wext_get_ssid(drv->wext, ssid); -} - - -static int wpa_driver_atmel_scan(void *priv, - struct wpa_driver_scan_params *params) -{ - struct wpa_driver_atmel_data *drv = priv; - return wpa_driver_wext_scan(drv->wext, params); -} - - -static struct wpa_scan_results * wpa_driver_atmel_get_scan_results(void *priv) -{ - struct wpa_driver_atmel_data *drv = priv; - return wpa_driver_wext_get_scan_results(drv->wext); -} - - -static int wpa_driver_atmel_set_operstate(void *priv, int state) -{ - struct wpa_driver_atmel_data *drv = priv; - return wpa_driver_wext_set_operstate(drv->wext, state); -} - - -static void * wpa_driver_atmel_init(void *ctx, const char *ifname) -{ - struct wpa_driver_atmel_data *drv; - - drv = os_zalloc(sizeof(*drv)); - if (drv == NULL) - return NULL; - drv->wext = wpa_driver_wext_init(ctx, ifname); - if (drv->wext == NULL) { - os_free(drv); - return NULL; - } - - drv->ctx = ctx; - os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); - drv->sock = socket(PF_INET, SOCK_DGRAM, 0); - if (drv->sock < 0) { - wpa_driver_wext_deinit(drv->wext); - os_free(drv); - return NULL; - } - - wpa_driver_atmel_set_wpa(drv, 1); - - return drv; -} - - -static void wpa_driver_atmel_deinit(void *priv) -{ - struct wpa_driver_atmel_data *drv = priv; - wpa_driver_atmel_set_wpa(drv, 0); - wpa_driver_wext_deinit(drv->wext); - close(drv->sock); - os_free(drv); -} - - -const struct wpa_driver_ops wpa_driver_atmel_ops = { - .name = "atmel", - .desc = "ATMEL AT76C5XXx (USB, PCMCIA)", - .get_bssid = wpa_driver_atmel_get_bssid, - .get_ssid = wpa_driver_atmel_get_ssid, - .set_key = wpa_driver_atmel_set_key, - .init = wpa_driver_atmel_init, - .deinit = wpa_driver_atmel_deinit, - .set_countermeasures = wpa_driver_atmel_set_countermeasures, - .scan2 = wpa_driver_atmel_scan, - .get_scan_results2 = wpa_driver_atmel_get_scan_results, - .deauthenticate = wpa_driver_atmel_deauthenticate, - .disassociate = wpa_driver_atmel_disassociate, - .associate = wpa_driver_atmel_associate, - .set_operstate = wpa_driver_atmel_set_operstate, -}; diff --git a/src/drivers/driver_broadcom.c b/src/drivers/driver_broadcom.c deleted file mode 100644 index cb88543c2c36a..0000000000000 --- a/src/drivers/driver_broadcom.c +++ /dev/null @@ -1,599 +0,0 @@ -/* - * WPA Supplicant - driver interaction with old Broadcom wl.o driver - * Copyright (c) 2004, Nikki Chumkov <nikki@gattaca.ru> - * Copyright (c) 2004, Jouni Malinen <j@w1.fi> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - * - * Please note that the newer Broadcom driver ("hybrid Linux driver") supports - * Linux wireless extensions and does not need (or even work) with this old - * driver wrapper. Use driver_wext.c with that driver. - */ - -#include "includes.h" - -#include <sys/ioctl.h> - -#include "common.h" - -#if 0 -#include <netpacket/packet.h> -#include <net/ethernet.h> /* the L2 protocols */ -#else -#include <linux/if_packet.h> -#include <linux/if_ether.h> /* The L2 protocols */ -#endif -#include <net/if.h> -#include <typedefs.h> - -/* wlioctl.h is a Broadcom header file and it is available, e.g., from Linksys - * WRT54G GPL tarball. */ -#include <wlioctl.h> - -#include "driver.h" -#include "eloop.h" - -struct wpa_driver_broadcom_data { - void *ctx; - int ioctl_sock; - int event_sock; - char ifname[IFNAMSIZ + 1]; -}; - - -#ifndef WLC_DEAUTHENTICATE -#define WLC_DEAUTHENTICATE 143 -#endif -#ifndef WLC_DEAUTHENTICATE_WITH_REASON -#define WLC_DEAUTHENTICATE_WITH_REASON 201 -#endif -#ifndef WLC_SET_TKIP_COUNTERMEASURES -#define WLC_SET_TKIP_COUNTERMEASURES 202 -#endif - -#if !defined(PSK_ENABLED) /* NEW driver interface */ -#define WL_VERSION 360130 -/* wireless authentication bit vector */ -#define WPA_ENABLED 1 -#define PSK_ENABLED 2 - -#define WAUTH_WPA_ENABLED(wauth) ((wauth) & WPA_ENABLED) -#define WAUTH_PSK_ENABLED(wauth) ((wauth) & PSK_ENABLED) -#define WAUTH_ENABLED(wauth) ((wauth) & (WPA_ENABLED | PSK_ENABLED)) - -#define WSEC_PRIMARY_KEY WL_PRIMARY_KEY - -typedef wl_wsec_key_t wsec_key_t; -#endif - -typedef struct { - uint32 val; - struct ether_addr ea; - uint16 res; -} wlc_deauth_t; - - -static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx, - void *timeout_ctx); - -static int broadcom_ioctl(struct wpa_driver_broadcom_data *drv, int cmd, - void *buf, int len) -{ - struct ifreq ifr; - wl_ioctl_t ioc; - int ret = 0; - - wpa_printf(MSG_MSGDUMP, "BROADCOM: wlioctl(%s,%d,len=%d,val=%p)", - drv->ifname, cmd, len, buf); - /* wpa_hexdump(MSG_MSGDUMP, "BROADCOM: wlioctl buf", buf, len); */ - - ioc.cmd = cmd; - ioc.buf = buf; - ioc.len = len; - os_strlcpy(ifr.ifr_name, drv->ifname, IFNAMSIZ); - ifr.ifr_data = (caddr_t) &ioc; - if ((ret = ioctl(drv->ioctl_sock, SIOCDEVPRIVATE, &ifr)) < 0) { - if (cmd != WLC_GET_MAGIC) - perror(ifr.ifr_name); - wpa_printf(MSG_MSGDUMP, "BROADCOM: wlioctl cmd=%d res=%d", - cmd, ret); - } - - return ret; -} - -static int wpa_driver_broadcom_get_bssid(void *priv, u8 *bssid) -{ - struct wpa_driver_broadcom_data *drv = priv; - if (broadcom_ioctl(drv, WLC_GET_BSSID, bssid, ETH_ALEN) == 0) - return 0; - - os_memset(bssid, 0, ETH_ALEN); - return -1; -} - -static int wpa_driver_broadcom_get_ssid(void *priv, u8 *ssid) -{ - struct wpa_driver_broadcom_data *drv = priv; - wlc_ssid_t s; - - if (broadcom_ioctl(drv, WLC_GET_SSID, &s, sizeof(s)) == -1) - return -1; - - os_memcpy(ssid, s.SSID, s.SSID_len); - return s.SSID_len; -} - -static int wpa_driver_broadcom_set_wpa(void *priv, int enable) -{ - struct wpa_driver_broadcom_data *drv = priv; - unsigned int wauth, wsec; - struct ether_addr ea; - - os_memset(&ea, enable ? 0xff : 0, sizeof(ea)); - if (broadcom_ioctl(drv, WLC_GET_WPA_AUTH, &wauth, sizeof(wauth)) == - -1 || - broadcom_ioctl(drv, WLC_GET_WSEC, &wsec, sizeof(wsec)) == -1) - return -1; - - if (enable) { - wauth = PSK_ENABLED; - wsec = TKIP_ENABLED; - } else { - wauth = 255; - wsec &= ~(TKIP_ENABLED | AES_ENABLED); - } - - if (broadcom_ioctl(drv, WLC_SET_WPA_AUTH, &wauth, sizeof(wauth)) == - -1 || - broadcom_ioctl(drv, WLC_SET_WSEC, &wsec, sizeof(wsec)) == -1) - return -1; - - /* FIX: magic number / error handling? */ - broadcom_ioctl(drv, 122, &ea, sizeof(ea)); - - return 0; -} - -static int wpa_driver_broadcom_set_key(const char *ifname, void *priv, - enum wpa_alg alg, - const u8 *addr, int key_idx, int set_tx, - const u8 *seq, size_t seq_len, - const u8 *key, size_t key_len) -{ - struct wpa_driver_broadcom_data *drv = priv; - int ret; - wsec_key_t wkt; - - os_memset(&wkt, 0, sizeof wkt); - wpa_printf(MSG_MSGDUMP, "BROADCOM: SET %sKEY[%d] alg=%d", - set_tx ? "PRIMARY " : "", key_idx, alg); - if (key && key_len > 0) - wpa_hexdump_key(MSG_MSGDUMP, "BROADCOM: key", key, key_len); - - switch (alg) { - case WPA_ALG_NONE: - wkt.algo = CRYPTO_ALGO_OFF; - break; - case WPA_ALG_WEP: - wkt.algo = CRYPTO_ALGO_WEP128; /* CRYPTO_ALGO_WEP1? */ - break; - case WPA_ALG_TKIP: - wkt.algo = 0; /* CRYPTO_ALGO_TKIP? */ - break; - case WPA_ALG_CCMP: - wkt.algo = 0; /* CRYPTO_ALGO_AES_CCM; - * AES_OCB_MSDU, AES_OCB_MPDU? */ - break; - default: - wkt.algo = CRYPTO_ALGO_NALG; - break; - } - - if (seq && seq_len > 0) - wpa_hexdump(MSG_MSGDUMP, "BROADCOM: SEQ", seq, seq_len); - - if (addr) - wpa_hexdump(MSG_MSGDUMP, "BROADCOM: addr", addr, ETH_ALEN); - - wkt.index = key_idx; - wkt.len = key_len; - if (key && key_len > 0) { - os_memcpy(wkt.data, key, key_len); - if (key_len == 32) { - /* hack hack hack XXX */ - os_memcpy(&wkt.data[16], &key[24], 8); - os_memcpy(&wkt.data[24], &key[16], 8); - } - } - /* wkt.algo = CRYPTO_ALGO_...; */ - wkt.flags = set_tx ? 0 : WSEC_PRIMARY_KEY; - if (addr && set_tx) - os_memcpy(&wkt.ea, addr, sizeof(wkt.ea)); - ret = broadcom_ioctl(drv, WLC_SET_KEY, &wkt, sizeof(wkt)); - if (addr && set_tx) { - /* FIX: magic number / error handling? */ - broadcom_ioctl(drv, 121, &wkt.ea, sizeof(wkt.ea)); - } - return ret; -} - - -static void wpa_driver_broadcom_event_receive(int sock, void *ctx, - void *sock_ctx) -{ - char buf[8192]; - int left; - wl_wpa_header_t *wwh; - union wpa_event_data data; - u8 *resp_ies = NULL; - - if ((left = recv(sock, buf, sizeof buf, 0)) < 0) - return; - - wpa_hexdump(MSG_DEBUG, "RECEIVE EVENT", (u8 *) buf, left); - - if ((size_t) left < sizeof(wl_wpa_header_t)) - return; - - wwh = (wl_wpa_header_t *) buf; - - if (wwh->snap.type != WL_WPA_ETHER_TYPE) - return; - if (os_memcmp(&wwh->snap, wl_wpa_snap_template, 6) != 0) - return; - - os_memset(&data, 0, sizeof(data)); - - switch (wwh->type) { - case WLC_ASSOC_MSG: - left -= WL_WPA_HEADER_LEN; - wpa_printf(MSG_DEBUG, "BROADCOM: ASSOC MESSAGE (left: %d)", - left); - if (left > 0) { - resp_ies = os_malloc(left); - if (resp_ies == NULL) - return; - os_memcpy(resp_ies, buf + WL_WPA_HEADER_LEN, left); - data.assoc_info.resp_ies = resp_ies; - data.assoc_info.resp_ies_len = left; - } - - wpa_supplicant_event(ctx, EVENT_ASSOC, &data); - os_free(resp_ies); - break; - case WLC_DISASSOC_MSG: - wpa_printf(MSG_DEBUG, "BROADCOM: DISASSOC MESSAGE"); - wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL); - break; - case WLC_PTK_MIC_MSG: - wpa_printf(MSG_DEBUG, "BROADCOM: PTK MIC MSG MESSAGE"); - data.michael_mic_failure.unicast = 1; - wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); - break; - case WLC_GTK_MIC_MSG: - wpa_printf(MSG_DEBUG, "BROADCOM: GTK MIC MSG MESSAGE"); - data.michael_mic_failure.unicast = 0; - wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); - break; - default: - wpa_printf(MSG_DEBUG, "BROADCOM: UNKNOWN MESSAGE (%d)", - wwh->type); - break; - } -} - -static void * wpa_driver_broadcom_init(void *ctx, const char *ifname) -{ - int s; - struct sockaddr_ll ll; - struct wpa_driver_broadcom_data *drv; - struct ifreq ifr; - - /* open socket to kernel */ - if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { - perror("socket"); - return NULL; - } - /* do it */ - os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); - if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { - perror(ifr.ifr_name); - return NULL; - } - - - drv = os_zalloc(sizeof(*drv)); - if (drv == NULL) - return NULL; - drv->ctx = ctx; - os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); - drv->ioctl_sock = s; - - s = socket(PF_PACKET, SOCK_RAW, ntohs(ETH_P_802_2)); - if (s < 0) { - perror("socket(PF_PACKET, SOCK_RAW, ntohs(ETH_P_802_2))"); - close(drv->ioctl_sock); - os_free(drv); - return NULL; - } - - os_memset(&ll, 0, sizeof(ll)); - ll.sll_family = AF_PACKET; - ll.sll_protocol = ntohs(ETH_P_802_2); - ll.sll_ifindex = ifr.ifr_ifindex; - ll.sll_hatype = 0; - ll.sll_pkttype = PACKET_HOST; - ll.sll_halen = 0; - - if (bind(s, (struct sockaddr *) &ll, sizeof(ll)) < 0) { - perror("bind(netlink)"); - close(s); - close(drv->ioctl_sock); - os_free(drv); - return NULL; - } - - eloop_register_read_sock(s, wpa_driver_broadcom_event_receive, ctx, - NULL); - drv->event_sock = s; - wpa_driver_broadcom_set_wpa(drv, 1); - - return drv; -} - -static void wpa_driver_broadcom_deinit(void *priv) -{ - struct wpa_driver_broadcom_data *drv = priv; - wpa_driver_broadcom_set_wpa(drv, 0); - eloop_cancel_timeout(wpa_driver_broadcom_scan_timeout, drv, drv->ctx); - eloop_unregister_read_sock(drv->event_sock); - close(drv->event_sock); - close(drv->ioctl_sock); - os_free(drv); -} - -static int wpa_driver_broadcom_set_countermeasures(void *priv, - int enabled) -{ -#if 0 - struct wpa_driver_broadcom_data *drv = priv; - /* FIX: ? */ - return broadcom_ioctl(drv, WLC_SET_TKIP_COUNTERMEASURES, &enabled, - sizeof(enabled)); -#else - return 0; -#endif -} - -static int wpa_driver_broadcom_set_drop_unencrypted(void *priv, int enabled) -{ - struct wpa_driver_broadcom_data *drv = priv; - /* SET_EAP_RESTRICT, SET_WEP_RESTRICT */ - int _restrict = (enabled ? 1 : 0); - - if (broadcom_ioctl(drv, WLC_SET_WEP_RESTRICT, - &_restrict, sizeof(_restrict)) < 0 || - broadcom_ioctl(drv, WLC_SET_EAP_RESTRICT, - &_restrict, sizeof(_restrict)) < 0) - return -1; - - return 0; -} - -static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx, - void *timeout_ctx) -{ - wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); - wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); -} - -static int wpa_driver_broadcom_scan(void *priv, - struct wpa_driver_scan_params *params) -{ - struct wpa_driver_broadcom_data *drv = priv; - wlc_ssid_t wst = { 0, "" }; - const u8 *ssid = params->ssids[0].ssid; - size_t ssid_len = params->ssids[0].ssid_len; - - if (ssid && ssid_len > 0 && ssid_len <= sizeof(wst.SSID)) { - wst.SSID_len = ssid_len; - os_memcpy(wst.SSID, ssid, ssid_len); - } - - if (broadcom_ioctl(drv, WLC_SCAN, &wst, sizeof(wst)) < 0) - return -1; - - eloop_cancel_timeout(wpa_driver_broadcom_scan_timeout, drv, drv->ctx); - eloop_register_timeout(3, 0, wpa_driver_broadcom_scan_timeout, drv, - drv->ctx); - return 0; -} - - -static const int frequency_list[] = { - 2412, 2417, 2422, 2427, 2432, 2437, 2442, - 2447, 2452, 2457, 2462, 2467, 2472, 2484 -}; - -struct bss_ie_hdr { - u8 elem_id; - u8 len; - u8 oui[3]; - /* u8 oui_type; */ - /* u16 version; */ -} __attribute__ ((packed)); - -static struct wpa_scan_results * -wpa_driver_broadcom_get_scan_results(void *priv) -{ - struct wpa_driver_broadcom_data *drv = priv; - char *buf; - wl_scan_results_t *wsr; - wl_bss_info_t *wbi; - size_t ap_num; - struct wpa_scan_results *res; - - buf = os_malloc(WLC_IOCTL_MAXLEN); - if (buf == NULL) - return NULL; - - wsr = (wl_scan_results_t *) buf; - - wsr->buflen = WLC_IOCTL_MAXLEN - sizeof(wsr); - wsr->version = 107; - wsr->count = 0; - - if (broadcom_ioctl(drv, WLC_SCAN_RESULTS, buf, WLC_IOCTL_MAXLEN) < 0) { - os_free(buf); - return NULL; - } - - res = os_zalloc(sizeof(*res)); - if (res == NULL) { - os_free(buf); - return NULL; - } - - res->res = os_zalloc(wsr->count * sizeof(struct wpa_scan_res *)); - if (res->res == NULL) { - os_free(res); - os_free(buf); - return NULL; - } - - for (ap_num = 0, wbi = wsr->bss_info; ap_num < wsr->count; ++ap_num) { - struct wpa_scan_res *r; - r = os_malloc(sizeof(*r) + wbi->ie_length); - if (r == NULL) - break; - res->res[res->num++] = r; - - os_memcpy(r->bssid, &wbi->BSSID, ETH_ALEN); - r->freq = frequency_list[wbi->channel - 1]; - /* get ie's */ - os_memcpy(r + 1, wbi + 1, wbi->ie_length); - r->ie_len = wbi->ie_length; - - wbi = (wl_bss_info_t *) ((u8 *) wbi + wbi->length); - } - - wpa_printf(MSG_MSGDUMP, "Received %d bytes of scan results (%lu " - "BSSes)", - wsr->buflen, (unsigned long) ap_num); - - os_free(buf); - return res; - } - -static int wpa_driver_broadcom_deauthenticate(void *priv, const u8 *addr, - int reason_code) -{ - struct wpa_driver_broadcom_data *drv = priv; - wlc_deauth_t wdt; - wdt.val = reason_code; - os_memcpy(&wdt.ea, addr, sizeof wdt.ea); - wdt.res = 0x7fff; - return broadcom_ioctl(drv, WLC_DEAUTHENTICATE_WITH_REASON, &wdt, - sizeof(wdt)); -} - -static int wpa_driver_broadcom_disassociate(void *priv, const u8 *addr, - int reason_code) -{ - struct wpa_driver_broadcom_data *drv = priv; - return broadcom_ioctl(drv, WLC_DISASSOC, NULL, 0); -} - -static int -wpa_driver_broadcom_associate(void *priv, - struct wpa_driver_associate_params *params) -{ - struct wpa_driver_broadcom_data *drv = priv; - wlc_ssid_t s; - int infra = 1; - int auth = 0; - int wsec = 4; - int dummy; - int wpa_auth; - int ret; - - ret = wpa_driver_broadcom_set_drop_unencrypted( - drv, params->drop_unencrypted); - - s.SSID_len = params->ssid_len; - os_memcpy(s.SSID, params->ssid, params->ssid_len); - - switch (params->pairwise_suite) { - case CIPHER_WEP40: - case CIPHER_WEP104: - wsec = 1; - break; - - case CIPHER_TKIP: - wsec = 2; - break; - - case CIPHER_CCMP: - wsec = 4; - break; - - default: - wsec = 0; - break; - } - - switch (params->key_mgmt_suite) { - case KEY_MGMT_802_1X: - wpa_auth = 1; - break; - - case KEY_MGMT_PSK: - wpa_auth = 2; - break; - - default: - wpa_auth = 255; - break; - } - - /* printf("broadcom_associate: %u %u %u\n", pairwise_suite, - * group_suite, key_mgmt_suite); - * broadcom_ioctl(ifname, WLC_GET_WSEC, &wsec, sizeof(wsec)); - * wl join uses wlc_sec_wep here, not wlc_set_wsec */ - - if (broadcom_ioctl(drv, WLC_SET_WSEC, &wsec, sizeof(wsec)) < 0 || - broadcom_ioctl(drv, WLC_SET_WPA_AUTH, &wpa_auth, - sizeof(wpa_auth)) < 0 || - broadcom_ioctl(drv, WLC_GET_WEP, &dummy, sizeof(dummy)) < 0 || - broadcom_ioctl(drv, WLC_SET_INFRA, &infra, sizeof(infra)) < 0 || - broadcom_ioctl(drv, WLC_SET_AUTH, &auth, sizeof(auth)) < 0 || - broadcom_ioctl(drv, WLC_SET_WEP, &wsec, sizeof(wsec)) < 0 || - broadcom_ioctl(drv, WLC_SET_SSID, &s, sizeof(s)) < 0) - return -1; - - return ret; -} - -const struct wpa_driver_ops wpa_driver_broadcom_ops = { - .name = "broadcom", - .desc = "Broadcom wl.o driver", - .get_bssid = wpa_driver_broadcom_get_bssid, - .get_ssid = wpa_driver_broadcom_get_ssid, - .set_key = wpa_driver_broadcom_set_key, - .init = wpa_driver_broadcom_init, - .deinit = wpa_driver_broadcom_deinit, - .set_countermeasures = wpa_driver_broadcom_set_countermeasures, - .scan2 = wpa_driver_broadcom_scan, - .get_scan_results2 = wpa_driver_broadcom_get_scan_results, - .deauthenticate = wpa_driver_broadcom_deauthenticate, - .disassociate = wpa_driver_broadcom_disassociate, - .associate = wpa_driver_broadcom_associate, -}; diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c index 99de6c7ff48e4..9d869b1517a59 100644 --- a/src/drivers/driver_bsd.c +++ b/src/drivers/driver_bsd.c @@ -3,23 +3,19 @@ * 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" #include <sys/ioctl.h> +#include <sys/sysctl.h> #include "common.h" #include "driver.h" #include "eloop.h" #include "common/ieee802_11_defs.h" +#include "common/wpa_common.h" #include <net/if.h> #include <net/if_media.h> @@ -295,9 +291,7 @@ bsd_set_key(const char *ifname, void *priv, enum wpa_alg alg, if (alg == WPA_ALG_NONE) { #ifndef HOSTAPD - if (addr == NULL || - os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", - IEEE80211_ADDR_LEN) == 0) + if (addr == NULL || is_broadcast_ether_addr(addr)) return bsd_del_key(priv, NULL, key_idx); else #endif /* HOSTAPD */ @@ -334,8 +328,7 @@ bsd_set_key(const char *ifname, void *priv, enum wpa_alg alg, * the address (yech). Note also that we can only mark global * keys default; doing this for a unicast key is an error. */ - if (os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", - IEEE80211_ADDR_LEN) == 0) { + if (is_broadcast_ether_addr(addr)) { wk.ik_flags |= IEEE80211_KEY_GROUP; wk.ik_keyix = key_idx; } else { @@ -346,7 +339,20 @@ bsd_set_key(const char *ifname, void *priv, enum wpa_alg alg, if (wk.ik_keyix != IEEE80211_KEYIX_NONE && set_tx) wk.ik_flags |= IEEE80211_KEY_DEFAULT; wk.ik_keylen = key_len; - os_memcpy(&wk.ik_keyrsc, seq, seq_len); + if (seq) { +#ifdef WORDS_BIGENDIAN + /* + * wk.ik_keyrsc is in host byte order (big endian), need to + * swap it to match with the byte order used in WPA. + */ + int i; + u8 *keyrsc = (u8 *) &wk.ik_keyrsc; + for (i = 0; i < seq_len; i++) + keyrsc[WPA_KEY_RSC_LEN - i - 1] = seq[i]; +#else /* WORDS_BIGENDIAN */ + os_memcpy(&wk.ik_keyrsc, seq, seq_len); +#endif /* WORDS_BIGENDIAN */ + } os_memcpy(wk.ik_keydata, key, key_len); return set80211var(priv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk)); @@ -511,12 +517,12 @@ bsd_new_sta(void *priv, void *ctx, u8 addr[IEEE80211_ADDR_LEN]) ielen += 2; no_ie: - drv_event_assoc(ctx, addr, iebuf, ielen); + drv_event_assoc(ctx, addr, iebuf, ielen, 0); } static int bsd_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, - int encrypt, const u8 *own_addr) + int encrypt, const u8 *own_addr, u32 flags) { struct bsd_driver_data *drv = priv; @@ -527,20 +533,30 @@ bsd_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, } static int -bsd_set_freq(void *priv, u16 channel) +bsd_set_freq(void *priv, struct hostapd_freq_params *freq) { struct bsd_driver_data *drv = priv; #ifdef SIOCS80211CHANNEL struct ieee80211chanreq creq; #endif /* SIOCS80211CHANNEL */ u32 mode; - - if (channel < 14) - mode = IFM_IEEE80211_11G; - else if (channel == 14) + int channel = freq->channel; + + if (channel < 14) { + mode = +#ifdef CONFIG_IEEE80211N + freq->ht_enabled ? IFM_IEEE80211_11NG : +#endif /* CONFIG_IEEE80211N */ + IFM_IEEE80211_11G; + } else if (channel == 14) { mode = IFM_IEEE80211_11B; - else - mode = IFM_IEEE80211_11A; + } else { + mode = +#ifdef CONFIG_IEEE80211N + freq->ht_enabled ? IFM_IEEE80211_11NA : +#endif /* CONFIG_IEEE80211N */ + IFM_IEEE80211_11A; + } if (bsd_set_mediaopt(drv, IFM_MMASK, mode) < 0) { wpa_printf(MSG_ERROR, "%s: failed to set modulation mode", __func__); @@ -550,7 +566,7 @@ bsd_set_freq(void *priv, u16 channel) #ifdef SIOCS80211CHANNEL os_memset(&creq, 0, sizeof(creq)); os_strlcpy(creq.i_name, drv->ifname, sizeof(creq.i_name)); - creq.i_channel = channel; + creq.i_channel = (u_int16_t)channel; return ioctl(drv->sock, SIOCS80211CHANNEL, &creq); #else /* SIOCS80211CHANNEL */ return set80211param(priv, IEEE80211_IOC_CHANNEL, channel); @@ -569,6 +585,21 @@ bsd_set_opt_ie(void *priv, const u8 *ie, size_t ie_len) return 0; } +static int +rtbuf_len(void) +{ + size_t len; + + int mib[6] = {CTL_NET, AF_ROUTE, 0, AF_INET, NET_RT_DUMP, 0}; + + if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) { + wpa_printf(MSG_WARNING, "%s failed: %s\n", __func__, + strerror(errno)); + len = 2048; + } + + return len; +} #ifdef HOSTAPD @@ -691,26 +722,37 @@ static void bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx) { struct bsd_driver_data *drv = ctx; - char buf[2048]; + char *buf; struct if_announcemsghdr *ifan; struct rt_msghdr *rtm; struct ieee80211_michael_event *mic; struct ieee80211_join_event *join; struct ieee80211_leave_event *leave; - int n; + int n, len; union wpa_event_data data; - n = read(sock, buf, sizeof(buf)); + len = rtbuf_len(); + + buf = os_malloc(len); + if (buf == NULL) { + wpa_printf(MSG_ERROR, "%s os_malloc() failed\n", __func__); + return; + } + + n = read(sock, buf, len); if (n < 0) { if (errno != EINTR && errno != EAGAIN) - perror("read(PF_ROUTE)"); + wpa_printf(MSG_ERROR, "%s read() failed: %s\n", + __func__, strerror(errno)); + os_free(buf); return; } rtm = (struct rt_msghdr *) buf; if (rtm->rtm_version != RTM_VERSION) { - wpa_printf(MSG_DEBUG, "Routing message version %d not " - "understood\n", rtm->rtm_version); + wpa_printf(MSG_DEBUG, "Invalid routing message version=%d", + rtm->rtm_version); + os_free(buf); return; } ifan = (struct if_announcemsghdr *) rtm; @@ -751,6 +793,7 @@ bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx) } break; } + os_free(buf); } static void @@ -760,12 +803,6 @@ handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) drv_event_eapol_rx(drv->hapd, src_addr, buf, len); } -static int -hostapd_bsd_set_freq(void *priv, struct hostapd_freq_params *freq) -{ - return bsd_set_freq(priv, freq->channel); -} - static void * bsd_init(struct hostapd_data *hapd, struct wpa_init_params *params) { @@ -936,13 +973,6 @@ wpa_driver_bsd_deauthenticate(void *priv, const u8 *addr, int reason_code) } static int -wpa_driver_bsd_disassociate(void *priv, const u8 *addr, int reason_code) -{ - return bsd_send_mlme_param(priv, IEEE80211_MLME_DISASSOC, reason_code, - addr); -} - -static int wpa_driver_bsd_set_auth_alg(void *priv, int auth_alg) { int authmode; @@ -972,7 +1002,6 @@ wpa_driver_bsd_associate(void *priv, struct wpa_driver_associate_params *params) struct bsd_driver_data *drv = priv; struct ieee80211req_mlme mlme; u32 mode; - u16 channel; int privacy; int ret = 0; @@ -1007,18 +1036,6 @@ wpa_driver_bsd_associate(void *priv, struct wpa_driver_associate_params *params) } if (params->mode == IEEE80211_MODE_AP) { - if (params->freq >= 2412 && params->freq <= 2472) - channel = (params->freq - 2407) / 5; - else if (params->freq == 2484) - channel = 14; - else if ((params->freq >= 5180 && params->freq <= 5240) || - (params->freq >= 5745 && params->freq <= 5825)) - channel = (params->freq - 5000) / 5; - else - channel = 0; - if (bsd_set_freq(drv, channel) < 0) - return -1; - drv->sock_xmit = l2_packet_init(drv->ifname, NULL, ETH_P_EAPOL, handle_read, drv, 0); if (drv->sock_xmit == NULL) @@ -1134,7 +1151,7 @@ static void wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) { struct bsd_driver_data *drv = sock_ctx; - char buf[2048]; + char *buf; struct if_announcemsghdr *ifan; struct if_msghdr *ifm; struct rt_msghdr *rtm; @@ -1142,19 +1159,30 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) struct ieee80211_michael_event *mic; struct ieee80211_leave_event *leave; struct ieee80211_join_event *join; - int n; + int n, len; + + len = rtbuf_len(); + + buf = os_malloc(len); + if (buf == NULL) { + wpa_printf(MSG_ERROR, "%s os_malloc() failed\n", __func__); + return; + } - n = read(sock, buf, sizeof(buf)); + n = read(sock, buf, len); if (n < 0) { if (errno != EINTR && errno != EAGAIN) - perror("read(PF_ROUTE)"); + wpa_printf(MSG_ERROR, "%s read() failed: %s\n", + __func__, strerror(errno)); + os_free(buf); return; } rtm = (struct rt_msghdr *) buf; if (rtm->rtm_version != RTM_VERSION) { - wpa_printf(MSG_DEBUG, "Routing message version %d not " - "understood\n", rtm->rtm_version); + wpa_printf(MSG_DEBUG, "Invalid routing message version=%d", + rtm->rtm_version); + os_free(buf); return; } os_memset(&event, 0, sizeof(event)); @@ -1169,6 +1197,7 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) case IFAN_DEPARTURE: event.interface_status.ievent = EVENT_INTERFACE_REMOVED; default: + os_free(buf); return; } wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: Interface '%s' %s", @@ -1241,6 +1270,7 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) } break; } + os_free(buf); } static void @@ -1291,8 +1321,8 @@ wpa_driver_bsd_add_scan_entry(struct wpa_scan_results *res, result->ie_len = pos - (u8 *)(result + 1); - tmp = os_realloc(res->res, - (res->num + 1) * sizeof(struct wpa_scan_res *)); + tmp = os_realloc_array(res->res, res->num + 1, + sizeof(struct wpa_scan_res *)); if (tmp == NULL) { os_free(result); return; @@ -1518,7 +1548,6 @@ const struct wpa_driver_ops wpa_driver_bsd_ops = { .read_sta_data = bsd_read_sta_driver_data, .sta_disassoc = bsd_sta_disassoc, .sta_deauth = bsd_sta_deauth, - .set_freq = hostapd_bsd_set_freq, #else /* HOSTAPD */ .init = wpa_driver_bsd_init, .deinit = wpa_driver_bsd_deinit, @@ -1528,10 +1557,10 @@ const struct wpa_driver_ops wpa_driver_bsd_ops = { .scan2 = wpa_driver_bsd_scan, .get_scan_results2 = wpa_driver_bsd_get_scan_results2, .deauthenticate = wpa_driver_bsd_deauthenticate, - .disassociate = wpa_driver_bsd_disassociate, .associate = wpa_driver_bsd_associate, .get_capa = wpa_driver_bsd_get_capa, #endif /* HOSTAPD */ + .set_freq = bsd_set_freq, .set_key = bsd_set_key, .set_ieee8021x = bsd_set_ieee8021x, .hapd_set_ssid = bsd_set_ssid, diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c new file mode 100644 index 0000000000000..418cf1a064f7a --- /dev/null +++ b/src/drivers/driver_common.c @@ -0,0 +1,86 @@ +/* + * Common driver-related functions + * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include "utils/common.h" +#include "driver.h" + +void wpa_scan_results_free(struct wpa_scan_results *res) +{ + size_t i; + + if (res == NULL) + return; + + for (i = 0; i < res->num; i++) + os_free(res->res[i]); + os_free(res->res); + os_free(res); +} + + +const char * event_to_string(enum wpa_event_type event) +{ +#define E2S(n) case EVENT_ ## n: return #n + switch (event) { + E2S(ASSOC); + E2S(DISASSOC); + E2S(MICHAEL_MIC_FAILURE); + E2S(SCAN_RESULTS); + E2S(ASSOCINFO); + E2S(INTERFACE_STATUS); + E2S(PMKID_CANDIDATE); + E2S(STKSTART); + E2S(TDLS); + E2S(FT_RESPONSE); + E2S(IBSS_RSN_START); + E2S(AUTH); + E2S(DEAUTH); + E2S(ASSOC_REJECT); + E2S(AUTH_TIMED_OUT); + E2S(ASSOC_TIMED_OUT); + E2S(FT_RRB_RX); + E2S(WPS_BUTTON_PUSHED); + E2S(TX_STATUS); + E2S(RX_FROM_UNKNOWN); + E2S(RX_MGMT); + E2S(RX_ACTION); + E2S(REMAIN_ON_CHANNEL); + E2S(CANCEL_REMAIN_ON_CHANNEL); + E2S(MLME_RX); + E2S(RX_PROBE_REQ); + E2S(NEW_STA); + E2S(EAPOL_RX); + E2S(SIGNAL_CHANGE); + E2S(INTERFACE_ENABLED); + E2S(INTERFACE_DISABLED); + E2S(CHANNEL_LIST_CHANGED); + E2S(INTERFACE_UNAVAILABLE); + E2S(BEST_CHANNEL); + E2S(UNPROT_DEAUTH); + E2S(UNPROT_DISASSOC); + E2S(STATION_LOW_ACK); + E2S(P2P_DEV_FOUND); + E2S(P2P_GO_NEG_REQ_RX); + E2S(P2P_GO_NEG_COMPLETED); + E2S(P2P_PROV_DISC_REQUEST); + E2S(P2P_PROV_DISC_RESPONSE); + E2S(P2P_SD_REQUEST); + E2S(P2P_SD_RESPONSE); + E2S(IBSS_PEER_LOST); + E2S(DRIVER_GTK_REKEY); + E2S(SCHED_SCAN_STOPPED); + E2S(DRIVER_CLIENT_POLL_OK); + E2S(EAPOL_TX_STATUS); + E2S(CH_SWITCH); + E2S(WNM); + } + + return "UNKNOWN"; +#undef E2S +} diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c index 952f3891abdeb..16f5563af93b4 100644 --- a/src/drivers/driver_hostap.c +++ b/src/drivers/driver_hostap.c @@ -2,20 +2,14 @@ * Driver interaction with Linux Host AP driver * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include <sys/ioctl.h> -#include "wireless_copy.h" +#include "linux_wext.h" #include "common.h" #include "driver.h" #include "driver_wext.h" @@ -23,8 +17,6 @@ #include "driver_hostap.h" -#ifdef HOSTAPD - #include <net/if_arp.h> #include <netpacket/packet.h> @@ -32,6 +24,7 @@ #include "netlink.h" #include "linux_ioctl.h" #include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" /* MTU to be set for the wlan#ap device; this is mainly needed for IEEE 802.1X @@ -84,8 +77,8 @@ static void handle_data(struct hostap_driver_data *drv, u8 *buf, size_t len, sa = hdr->addr2; os_memset(&event, 0, sizeof(event)); - event.rx_from_unknown.frame = buf; - event.rx_from_unknown.len = len; + event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len); + event.rx_from_unknown.addr = sa; wpa_supplicant_event(drv->hapd, EVENT_RX_FROM_UNKNOWN, &event); pos = (u8 *) (hdr + 1); @@ -148,7 +141,6 @@ static void handle_frame(struct hostap_driver_data *drv, u8 *buf, size_t len) { struct ieee80211_hdr *hdr; u16 fc, extra_len, type, stype; - unsigned char *extra = NULL; size_t data_len = len; int ver; union wpa_event_data event; @@ -185,7 +177,6 @@ static void handle_frame(struct hostap_driver_data *drv, u8 *buf, size_t len) return; } len -= extra_len + 2; - extra = buf + len; } else if (ver == 1 || ver == 2) { handle_tx_callback(drv, buf, data_len, ver == 2 ? 1 : 0); return; @@ -273,7 +264,7 @@ static int hostap_init_sockets(struct hostap_driver_data *drv, u8 *own_addr) } -static int hostap_send_mlme(void *priv, const u8 *msg, size_t len) +static int hostap_send_mlme(void *priv, const u8 *msg, size_t len, int noack) { struct hostap_driver_data *drv = priv; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) msg; @@ -289,7 +280,8 @@ static int hostap_send_mlme(void *priv, const u8 *msg, size_t len) static int hostap_send_eapol(void *priv, const u8 *addr, const u8 *data, - size_t data_len, int encrypt, const u8 *own_addr) + size_t data_len, int encrypt, const u8 *own_addr, + u32 flags) { struct hostap_driver_data *drv = priv; struct ieee80211_hdr *hdr; @@ -321,7 +313,7 @@ static int hostap_send_eapol(void *priv, const u8 *addr, const u8 *data, pos += 2; memcpy(pos, data, data_len); - res = hostap_send_mlme(drv, (u8 *) hdr, len); + res = hostap_send_mlme(drv, (u8 *) hdr, len, 0); if (res < 0) { wpa_printf(MSG_ERROR, "hostap_send_eapol - packet len: %lu - " "failed: %d (%s)", @@ -763,7 +755,8 @@ static int hostap_set_generic_elem(void *priv, static int hostap_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, - const struct wpabuf *proberesp) + const struct wpabuf *proberesp, + const struct wpabuf *assocresp) { struct hostap_driver_data *drv = priv; @@ -1034,6 +1027,16 @@ static int hostap_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, struct hostap_driver_data *drv = priv; struct ieee80211_mgmt mgmt; + if (is_broadcast_ether_addr(addr)) { + /* + * New Prism2.5/3 STA firmware versions seem to have issues + * with this broadcast deauth frame. This gets the firmware in + * odd state where nothing works correctly, so let's skip + * sending this for the hostap driver. + */ + return 0; + } + memset(&mgmt, 0, sizeof(mgmt)); mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DEAUTH); @@ -1042,7 +1045,26 @@ static int hostap_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, memcpy(mgmt.bssid, own_addr, ETH_ALEN); mgmt.u.deauth.reason_code = host_to_le16(reason); return hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN + - sizeof(mgmt.u.deauth)); + sizeof(mgmt.u.deauth), 0); +} + + +static int hostap_set_freq(void *priv, struct hostapd_freq_params *freq) +{ + struct hostap_driver_data *drv = priv; + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.freq.m = freq->channel; + iwr.u.freq.e = 0; + + if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { + perror("ioctl[SIOCSIWFREQ]"); + return -1; + } + + return 0; } @@ -1060,7 +1082,7 @@ static int hostap_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, memcpy(mgmt.bssid, own_addr, ETH_ALEN); mgmt.u.disassoc.reason_code = host_to_le16(reason); return hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN + - sizeof(mgmt.u.disassoc)); + sizeof(mgmt.u.disassoc), 0); } @@ -1114,491 +1136,38 @@ static struct hostapd_hw_modes * hostap_get_hw_feature_data(void *priv, return mode; } -#else /* HOSTAPD */ - -struct wpa_driver_hostap_data { - void *wext; /* private data for driver_wext */ - void *ctx; - char ifname[IFNAMSIZ + 1]; - int sock; - int current_mode; /* infra/adhoc */ -}; - - -static int wpa_driver_hostap_set_auth_alg(void *priv, int auth_alg); - - -static int hostapd_ioctl(struct wpa_driver_hostap_data *drv, - struct prism2_hostapd_param *param, - int len, int show_err) -{ - struct iwreq iwr; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - iwr.u.data.pointer = (caddr_t) param; - iwr.u.data.length = len; - - if (ioctl(drv->sock, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) { - int ret = errno; - if (show_err) - perror("ioctl[PRISM2_IOCTL_HOSTAPD]"); - return ret; - } - - return 0; -} - -static int wpa_driver_hostap_set_wpa_ie(struct wpa_driver_hostap_data *drv, - const u8 *wpa_ie, size_t wpa_ie_len) +static void wpa_driver_hostap_poll_client(void *priv, const u8 *own_addr, + const u8 *addr, int qos) { - struct prism2_hostapd_param *param; - int res; - size_t blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN + wpa_ie_len; - if (blen < sizeof(*param)) - blen = sizeof(*param); + struct ieee80211_hdr hdr; - param = os_zalloc(blen); - if (param == NULL) - return -1; + os_memset(&hdr, 0, sizeof(hdr)); - param->cmd = PRISM2_HOSTAPD_SET_GENERIC_ELEMENT; - param->u.generic_elem.len = wpa_ie_len; - os_memcpy(param->u.generic_elem.data, wpa_ie, wpa_ie_len); - res = hostapd_ioctl(drv, param, blen, 1); - - os_free(param); - - return res; -} - - -static int prism2param(struct wpa_driver_hostap_data *drv, int param, - int value) -{ - struct iwreq iwr; - int *i, ret = 0; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - i = (int *) iwr.u.name; - *i++ = param; - *i++ = value; - - if (ioctl(drv->sock, PRISM2_IOCTL_PRISM2_PARAM, &iwr) < 0) { - perror("ioctl[PRISM2_IOCTL_PRISM2_PARAM]"); - ret = -1; - } - return ret; -} - - -static int wpa_driver_hostap_set_wpa(void *priv, int enabled) -{ - struct wpa_driver_hostap_data *drv = priv; - int ret = 0; - - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); - - if (!enabled && wpa_driver_hostap_set_wpa_ie(drv, NULL, 0) < 0) - ret = -1; - if (prism2param(drv, PRISM2_PARAM_HOST_ROAMING, enabled ? 2 : 0) < 0) - ret = -1; - if (prism2param(drv, PRISM2_PARAM_WPA, enabled) < 0) - ret = -1; - - return ret; -} - - -static void show_set_key_error(struct prism2_hostapd_param *param) -{ - switch (param->u.crypt.err) { - case HOSTAP_CRYPT_ERR_UNKNOWN_ALG: - wpa_printf(MSG_INFO, "Unknown algorithm '%s'.", - param->u.crypt.alg); - wpa_printf(MSG_INFO, "You may need to load kernel module to " - "register that algorithm."); - wpa_printf(MSG_INFO, "E.g., 'modprobe hostap_crypt_wep' for " - "WEP."); - break; - case HOSTAP_CRYPT_ERR_UNKNOWN_ADDR: - wpa_printf(MSG_INFO, "Unknown address " MACSTR ".", - MAC2STR(param->sta_addr)); - break; - case HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED: - wpa_printf(MSG_INFO, "Crypt algorithm initialization failed."); - break; - case HOSTAP_CRYPT_ERR_KEY_SET_FAILED: - wpa_printf(MSG_INFO, "Key setting failed."); - break; - case HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED: - wpa_printf(MSG_INFO, "TX key index setting failed."); - break; - case HOSTAP_CRYPT_ERR_CARD_CONF_FAILED: - wpa_printf(MSG_INFO, "Card configuration failed."); - break; - } -} - - -static int wpa_driver_hostap_set_key(const char *ifname, void *priv, - enum wpa_alg alg, const u8 *addr, - int key_idx, int set_tx, - const u8 *seq, size_t seq_len, - const u8 *key, size_t key_len) -{ - struct wpa_driver_hostap_data *drv = priv; - struct prism2_hostapd_param *param; - u8 *buf; - size_t blen; - int ret = 0; - char *alg_name; - - switch (alg) { - case WPA_ALG_NONE: - alg_name = "none"; - break; - case WPA_ALG_WEP: - alg_name = "WEP"; - break; - case WPA_ALG_TKIP: - alg_name = "TKIP"; - break; - case WPA_ALG_CCMP: - alg_name = "CCMP"; - break; - default: - return -1; - } - - wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu " - "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx, - (unsigned long) seq_len, (unsigned long) key_len); - - if (seq_len > 8) - return -2; - - blen = sizeof(*param) + key_len; - buf = os_zalloc(blen); - if (buf == NULL) - return -1; - - param = (struct prism2_hostapd_param *) buf; - param->cmd = PRISM2_SET_ENCRYPTION; - /* TODO: In theory, STA in client mode can use five keys; four default - * keys for receiving (with keyidx 0..3) and one individual key for - * both transmitting and receiving (keyidx 0) _unicast_ packets. Now, - * keyidx 0 is reserved for this unicast use and default keys can only - * use keyidx 1..3 (i.e., default key with keyidx 0 is not supported). - * This should be fine for more or less all cases, but for completeness - * sake, the driver could be enhanced to support the missing key. */ -#if 0 - if (addr == NULL) - os_memset(param->sta_addr, 0xff, ETH_ALEN); - else - os_memcpy(param->sta_addr, addr, ETH_ALEN); -#else - os_memset(param->sta_addr, 0xff, ETH_ALEN); -#endif - os_strlcpy((char *) param->u.crypt.alg, alg_name, - HOSTAP_CRYPT_ALG_NAME_LEN); - param->u.crypt.flags = set_tx ? HOSTAP_CRYPT_FLAG_SET_TX_KEY : 0; - param->u.crypt.idx = key_idx; - os_memcpy(param->u.crypt.seq, seq, seq_len); - param->u.crypt.key_len = key_len; - os_memcpy((u8 *) (param + 1), key, key_len); - - if (hostapd_ioctl(drv, param, blen, 1)) { - wpa_printf(MSG_WARNING, "Failed to set encryption."); - show_set_key_error(param); - ret = -1; - } - os_free(buf); - - return ret; -} - - -static int wpa_driver_hostap_set_countermeasures(void *priv, int enabled) -{ - struct wpa_driver_hostap_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); - return prism2param(drv, PRISM2_PARAM_TKIP_COUNTERMEASURES, enabled); -} - - -static int wpa_driver_hostap_reset(struct wpa_driver_hostap_data *drv, - int type) -{ - struct iwreq iwr; - int *i, ret = 0; - - wpa_printf(MSG_DEBUG, "%s: type=%d", __FUNCTION__, type); - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - i = (int *) iwr.u.name; - *i++ = type; - - if (ioctl(drv->sock, PRISM2_IOCTL_RESET, &iwr) < 0) { - perror("ioctl[PRISM2_IOCTL_RESET]"); - ret = -1; - } - return ret; -} - - -static int wpa_driver_hostap_mlme(struct wpa_driver_hostap_data *drv, - const u8 *addr, int cmd, int reason_code) -{ - struct prism2_hostapd_param param; - int ret; - - /* There does not seem to be a better way of deauthenticating or - * disassociating with Prism2/2.5/3 than sending the management frame - * and then resetting the Port0 to make sure both the AP and the STA - * end up in disconnected state. */ - os_memset(¶m, 0, sizeof(param)); - param.cmd = PRISM2_HOSTAPD_MLME; - os_memcpy(param.sta_addr, addr, ETH_ALEN); - param.u.mlme.cmd = cmd; - param.u.mlme.reason_code = reason_code; - ret = hostapd_ioctl(drv, ¶m, sizeof(param), 1); - if (ret == 0) { - os_sleep(0, 100000); - ret = wpa_driver_hostap_reset(drv, 2); - } - return ret; -} - - -static int wpa_driver_hostap_deauthenticate(void *priv, const u8 *addr, - int reason_code) -{ - struct wpa_driver_hostap_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - return wpa_driver_hostap_mlme(drv, addr, MLME_STA_DEAUTH, - reason_code); -} - - -static int wpa_driver_hostap_disassociate(void *priv, const u8 *addr, - int reason_code) -{ - struct wpa_driver_hostap_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - return wpa_driver_hostap_mlme(drv, addr, MLME_STA_DISASSOC, - reason_code); -} - - -static int -wpa_driver_hostap_associate(void *priv, - struct wpa_driver_associate_params *params) -{ - struct wpa_driver_hostap_data *drv = priv; - int ret = 0; - int allow_unencrypted_eapol; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - if (prism2param(drv, PRISM2_PARAM_DROP_UNENCRYPTED, - params->drop_unencrypted) < 0) - ret = -1; - if (wpa_driver_hostap_set_auth_alg(drv, params->auth_alg) < 0) - ret = -1; - if (params->mode != drv->current_mode) { - /* At the moment, Host AP driver requires host_roaming=2 for - * infrastructure mode and host_roaming=0 for adhoc. */ - if (prism2param(drv, PRISM2_PARAM_HOST_ROAMING, - params->mode == IEEE80211_MODE_IBSS ? 0 : 2) < - 0) { - wpa_printf(MSG_DEBUG, "%s: failed to set host_roaming", - __func__); - } - drv->current_mode = params->mode; - } - - if (prism2param(drv, PRISM2_PARAM_PRIVACY_INVOKED, - params->key_mgmt_suite != KEY_MGMT_NONE) < 0) - ret = -1; - if (wpa_driver_hostap_set_wpa_ie(drv, params->wpa_ie, - params->wpa_ie_len) < 0) - ret = -1; - if (wpa_driver_wext_set_mode(drv->wext, params->mode) < 0) - ret = -1; - if (params->freq && - wpa_driver_wext_set_freq(drv->wext, params->freq) < 0) - ret = -1; - if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, params->ssid_len) - < 0) - ret = -1; - if (wpa_driver_wext_set_bssid(drv->wext, params->bssid) < 0) - ret = -1; - - /* Allow unencrypted EAPOL messages even if pairwise keys are set when - * not using WPA. IEEE 802.1X specifies that these frames are not - * encrypted, but WPA encrypts them when pairwise keys are in use. */ - if (params->key_mgmt_suite == KEY_MGMT_802_1X || - params->key_mgmt_suite == KEY_MGMT_PSK) - allow_unencrypted_eapol = 0; - else - allow_unencrypted_eapol = 1; - - if (prism2param(drv, PRISM2_PARAM_IEEE_802_1X, - allow_unencrypted_eapol) < 0) { - wpa_printf(MSG_DEBUG, "hostap: Failed to configure " - "ieee_802_1x param"); - /* Ignore this error.. driver_hostap.c can also be used with - * other drivers that do not support this prism2_param. */ - } - - return ret; -} - - -static int wpa_driver_hostap_scan(void *priv, - struct wpa_driver_scan_params *params) -{ - struct wpa_driver_hostap_data *drv = priv; - struct prism2_hostapd_param param; - int ret; - const u8 *ssid = params->ssids[0].ssid; - size_t ssid_len = params->ssids[0].ssid_len; - - if (ssid == NULL) { - /* Use standard Linux Wireless Extensions ioctl if possible - * because some drivers using hostap code in wpa_supplicant - * might not support Host AP specific scan request (with SSID - * info). */ - return wpa_driver_wext_scan(drv->wext, params); - } - - if (ssid_len > 32) - ssid_len = 32; - - os_memset(¶m, 0, sizeof(param)); - param.cmd = PRISM2_HOSTAPD_SCAN_REQ; - param.u.scan_req.ssid_len = ssid_len; - os_memcpy(param.u.scan_req.ssid, ssid, ssid_len); - ret = hostapd_ioctl(drv, ¶m, sizeof(param), 1); - - /* Not all drivers generate "scan completed" wireless event, so try to - * read results after a timeout. */ - eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv->wext, - drv->ctx); - eloop_register_timeout(3, 0, wpa_driver_wext_scan_timeout, drv->wext, - drv->ctx); - - return ret; -} - - -static int wpa_driver_hostap_set_auth_alg(void *priv, int auth_alg) -{ - struct wpa_driver_hostap_data *drv = priv; - int algs = 0; - - if (auth_alg & WPA_AUTH_ALG_OPEN) - algs |= 1; - if (auth_alg & WPA_AUTH_ALG_SHARED) - algs |= 2; - if (auth_alg & WPA_AUTH_ALG_LEAP) - algs |= 4; - if (algs == 0) - algs = 1; /* at least one algorithm should be set */ - - return prism2param(drv, PRISM2_PARAM_AP_AUTH_ALGS, algs); -} - - -static int wpa_driver_hostap_get_bssid(void *priv, u8 *bssid) -{ - struct wpa_driver_hostap_data *drv = priv; - return wpa_driver_wext_get_bssid(drv->wext, bssid); -} - - -static int wpa_driver_hostap_get_ssid(void *priv, u8 *ssid) -{ - struct wpa_driver_hostap_data *drv = priv; - return wpa_driver_wext_get_ssid(drv->wext, ssid); -} - - -static struct wpa_scan_results * wpa_driver_hostap_get_scan_results(void *priv) -{ - struct wpa_driver_hostap_data *drv = priv; - return wpa_driver_wext_get_scan_results(drv->wext); -} - - -static int wpa_driver_hostap_set_operstate(void *priv, int state) -{ - struct wpa_driver_hostap_data *drv = priv; - return wpa_driver_wext_set_operstate(drv->wext, state); -} - - -static void * wpa_driver_hostap_init(void *ctx, const char *ifname) -{ - struct wpa_driver_hostap_data *drv; - - drv = os_zalloc(sizeof(*drv)); - if (drv == NULL) - return NULL; - drv->wext = wpa_driver_wext_init(ctx, ifname); - if (drv->wext == NULL) { - os_free(drv); - return NULL; - } - - drv->ctx = ctx; - os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); - drv->sock = socket(PF_INET, SOCK_DGRAM, 0); - if (drv->sock < 0) { - perror("socket"); - wpa_driver_wext_deinit(drv->wext); - os_free(drv); - return NULL; - } - - if (os_strncmp(ifname, "wlan", 4) == 0) { - /* - * Host AP driver may use both wlan# and wifi# interface in - * wireless events. - */ - char ifname2[IFNAMSIZ + 1]; - os_strlcpy(ifname2, ifname, sizeof(ifname2)); - os_memcpy(ifname2, "wifi", 4); - wpa_driver_wext_alternative_ifindex(drv->wext, ifname2); - } - - wpa_driver_hostap_set_wpa(drv, 1); - - return drv; -} + /* + * WLAN_FC_STYPE_NULLFUNC would be more appropriate, + * but it is apparently not retried so TX Exc events + * are not received for it. + * This is the reason the driver overrides the default + * handling. + */ + hdr.frame_control = IEEE80211_FC(WLAN_FC_TYPE_DATA, + WLAN_FC_STYPE_DATA); + hdr.frame_control |= + host_to_le16(WLAN_FC_FROMDS); + os_memcpy(hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN); + os_memcpy(hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); + os_memcpy(hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); -static void wpa_driver_hostap_deinit(void *priv) -{ - struct wpa_driver_hostap_data *drv = priv; - wpa_driver_hostap_set_wpa(drv, 0); - wpa_driver_wext_deinit(drv->wext); - close(drv->sock); - os_free(drv); + hostap_send_mlme(priv, (u8 *)&hdr, sizeof(hdr), 0); } -#endif /* HOSTAPD */ - const struct wpa_driver_ops wpa_driver_hostap_ops = { .name = "hostap", .desc = "Host AP driver (Intersil Prism2/2.5/3)", .set_key = wpa_driver_hostap_set_key, -#ifdef HOSTAPD .hapd_init = hostap_init, .hapd_deinit = hostap_driver_deinit, .set_ieee8021x = hostap_set_ieee8021x, @@ -1619,17 +1188,6 @@ const struct wpa_driver_ops wpa_driver_hostap_ops = { .sta_clear_stats = hostap_sta_clear_stats, .get_hw_feature_data = hostap_get_hw_feature_data, .set_ap_wps_ie = hostap_set_ap_wps_ie, -#else /* HOSTAPD */ - .get_bssid = wpa_driver_hostap_get_bssid, - .get_ssid = wpa_driver_hostap_get_ssid, - .set_countermeasures = wpa_driver_hostap_set_countermeasures, - .scan2 = wpa_driver_hostap_scan, - .get_scan_results2 = wpa_driver_hostap_get_scan_results, - .deauthenticate = wpa_driver_hostap_deauthenticate, - .disassociate = wpa_driver_hostap_disassociate, - .associate = wpa_driver_hostap_associate, - .init = wpa_driver_hostap_init, - .deinit = wpa_driver_hostap_deinit, - .set_operstate = wpa_driver_hostap_set_operstate, -#endif /* HOSTAPD */ + .set_freq = hostap_set_freq, + .poll_client = wpa_driver_hostap_poll_client, }; diff --git a/src/drivers/driver_hostap.h b/src/drivers/driver_hostap.h index 66b2bb39b849c..a9d3e76cbe8f3 100644 --- a/src/drivers/driver_hostap.h +++ b/src/drivers/driver_hostap.h @@ -2,14 +2,8 @@ * Driver interaction with Linux Host AP driver * Copyright (c) 2002-2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef HOSTAP_DRIVER_H diff --git a/src/drivers/driver_iphone.m b/src/drivers/driver_iphone.m deleted file mode 100644 index 8213fdacc32e8..0000000000000 --- a/src/drivers/driver_iphone.m +++ /dev/null @@ -1,466 +0,0 @@ -/* - * WPA Supplicant - iPhone/iPod touch Apple80211 driver interface - * Copyright (c) 2007, Jouni Malinen <j@w1.fi> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" -#define Boolean __DummyBoolean -#include <CoreFoundation/CoreFoundation.h> -#undef Boolean - -#include "common.h" -#include "driver.h" -#include "eloop.h" -#include "common/ieee802_11_defs.h" - -#include "MobileApple80211.h" - -struct wpa_driver_iphone_data { - void *ctx; - Apple80211Ref wireless_ctx; - CFArrayRef scan_results; - int ctrl_power; -}; - - -static const void * cfdict_get_key_str(CFDictionaryRef dict, const char *key) -{ - const void *res; - CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, key, - kCFStringEncodingMacRoman); - if (str == NULL) - return NULL; - - res = CFDictionaryGetValue(dict, str); - CFRelease(str); - return res; -} - - -static int wpa_driver_iphone_get_ssid(void *priv, u8 *ssid) -{ - struct wpa_driver_iphone_data *drv = priv; - CFDataRef data; - int err, len; - - err = Apple80211CopyValue(drv->wireless_ctx, APPLE80211_VALUE_SSID, 0, - &data); - if (err != 0) { - wpa_printf(MSG_DEBUG, "iPhone: Apple80211CopyValue(SSID) " - "failed: %d", err); - return -1; - } - - len = CFDataGetLength(data); - if (len > 32) { - CFRelease(data); - return -1; - } - os_memcpy(ssid, CFDataGetBytePtr(data), len); - CFRelease(data); - - return len; -} - - -static int wpa_driver_iphone_get_bssid(void *priv, u8 *bssid) -{ - struct wpa_driver_iphone_data *drv = priv; - CFStringRef data; - int err; - int a1, a2, a3, a4, a5, a6; - - err = Apple80211CopyValue(drv->wireless_ctx, APPLE80211_VALUE_BSSID, 0, - &data); - if (err != 0) { - wpa_printf(MSG_DEBUG, "iPhone: Apple80211CopyValue(BSSID) " - "failed: %d", err); - return -1; - } - - sscanf(CFStringGetCStringPtr(data, kCFStringEncodingMacRoman), - "%x:%x:%x:%x:%x:%x", &a1, &a2, &a3, &a4, &a5, &a6); - bssid[0] = a1; - bssid[1] = a2; - bssid[2] = a3; - bssid[3] = a4; - bssid[4] = a5; - bssid[5] = a6; - - CFRelease(data); - - return 0; -} - - -static void wpa_driver_iphone_scan_timeout(void *eloop_ctx, void *timeout_ctx) -{ - wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); -} - - -static int wpa_driver_iphone_scan(void *priv, const u8 *ssid, size_t ssid_len) -{ - struct wpa_driver_iphone_data *drv = priv; - int err; - - if (drv->scan_results) { - CFRelease(drv->scan_results); - drv->scan_results = NULL; - } - - err = Apple80211Scan(drv->wireless_ctx, &drv->scan_results, NULL); - if (err) { - wpa_printf(MSG_DEBUG, "iPhone: Apple80211Scan failed: %d", - err); - return -1; - } - - eloop_register_timeout(0, 0, wpa_driver_iphone_scan_timeout, drv, - drv->ctx); - return 0; -} - - -static int wpa_driver_iphone_get_scan_results(void *priv, - struct wpa_scan_result *results, - size_t max_size) -{ - struct wpa_driver_iphone_data *drv = priv; - size_t i, num; - - if (drv->scan_results == NULL) - return 0; - - num = CFArrayGetCount(drv->scan_results); - if (num > max_size) - num = max_size; - os_memset(results, 0, num * sizeof(struct wpa_scan_result)); - - for (i = 0; i < num; i++) { - struct wpa_scan_result *res = &results[i]; - CFDictionaryRef dict = - CFArrayGetValueAtIndex(drv->scan_results, i); - CFDataRef data; - CFStringRef str; - CFNumberRef num; - int val; - - data = cfdict_get_key_str(dict, "SSID"); - if (data) { - res->ssid_len = CFDataGetLength(data); - if (res->ssid_len > 32) - res->ssid_len = 32; - os_memcpy(res->ssid, CFDataGetBytePtr(data), - res->ssid_len); - } - - str = cfdict_get_key_str(dict, "BSSID"); - if (str) { - int a1, a2, a3, a4, a5, a6; - sscanf(CFStringGetCStringPtr( - str, kCFStringEncodingMacRoman), - "%x:%x:%x:%x:%x:%x", - &a1, &a2, &a3, &a4, &a5, &a6); - res->bssid[0] = a1; - res->bssid[1] = a2; - res->bssid[2] = a3; - res->bssid[3] = a4; - res->bssid[4] = a5; - res->bssid[5] = a6; - } - - num = cfdict_get_key_str(dict, "CAPABILITIES"); - if (num) { - if (CFNumberGetValue(num, kCFNumberSInt32Type, &val)) - res->caps = val; - } - - num = cfdict_get_key_str(dict, "CHANNEL"); - if (num) { - if (CFNumberGetValue(num, kCFNumberSInt32Type, &val)) - res->freq = 2407 + val * 5; - } - - num = cfdict_get_key_str(dict, "RSSI"); - if (num) { - if (CFNumberGetValue(num, kCFNumberSInt32Type, &val)) - res->level = val; - } - - num = cfdict_get_key_str(dict, "NOISE"); - if (num) { - if (CFNumberGetValue(num, kCFNumberSInt32Type, &val)) - res->noise = val; - } - - data = cfdict_get_key_str(dict, "IE"); - if (data) { - u8 *ptr = (u8 *) CFDataGetBytePtr(data); - int len = CFDataGetLength(data); - u8 *pos = ptr, *end = ptr + len; - - while (pos + 2 < end) { - if (pos + 2 + pos[1] > end) - break; - if (pos[0] == WLAN_EID_RSN && - pos[1] <= SSID_MAX_WPA_IE_LEN) { - os_memcpy(res->rsn_ie, pos, - 2 + pos[1]); - res->rsn_ie_len = 2 + pos[1]; - } - if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && - pos[1] > 4 && pos[2] == 0x00 && - pos[3] == 0x50 && pos[4] == 0xf2 && - pos[5] == 0x01) { - os_memcpy(res->wpa_ie, pos, - 2 + pos[1]); - res->wpa_ie_len = 2 + pos[1]; - } - - pos = pos + 2 + pos[1]; - } - } - } - - return num; -} - - -static void wpa_driver_iphone_assoc_timeout(void *eloop_ctx, void *timeout_ctx) -{ - struct wpa_driver_iphone_data *drv = eloop_ctx; - u8 bssid[ETH_ALEN]; - - if (wpa_driver_iphone_get_bssid(drv, bssid) != 0) { - eloop_register_timeout(1, 0, wpa_driver_iphone_assoc_timeout, - drv, drv->ctx); - return; - } - - wpa_supplicant_event(timeout_ctx, EVENT_ASSOC, NULL); -} - - -static int wpa_driver_iphone_associate( - void *priv, struct wpa_driver_associate_params *params) -{ - struct wpa_driver_iphone_data *drv = priv; - int i, num, err; - size_t ssid_len; - CFDictionaryRef bss = NULL; - - /* - * TODO: Consider generating parameters instead of just using an entry - * from scan results in order to support ap_scan=2. - */ - - if (drv->scan_results == NULL) { - wpa_printf(MSG_DEBUG, "iPhone: No scan results - cannot " - "associate"); - return -1; - } - - num = CFArrayGetCount(drv->scan_results); - - for (i = 0; i < num; i++) { - CFDictionaryRef dict = - CFArrayGetValueAtIndex(drv->scan_results, i); - CFDataRef data; - - data = cfdict_get_key_str(dict, "SSID"); - if (data == NULL) - continue; - - ssid_len = CFDataGetLength(data); - if (ssid_len != params->ssid_len || - os_memcmp(CFDataGetBytePtr(data), params->ssid, ssid_len) - != 0) - continue; - - bss = dict; - break; - } - - if (bss == NULL) { - wpa_printf(MSG_DEBUG, "iPhone: Could not find SSID from scan " - "results - cannot associate"); - return -1; - } - - wpa_printf(MSG_DEBUG, "iPhone: Trying to associate with a BSS found " - "from scan results"); - - err = Apple80211Associate(drv->wireless_ctx, bss, NULL); - if (err) { - wpa_printf(MSG_DEBUG, "iPhone: Apple80211Associate() failed: " - "%d", err); - return -1; - } - - /* - * Driver is actually already associated; report association from an - * eloop callback. - */ - eloop_cancel_timeout(wpa_driver_iphone_assoc_timeout, drv, drv->ctx); - eloop_register_timeout(0, 0, wpa_driver_iphone_assoc_timeout, drv, - drv->ctx); - - return 0; -} - - -static int wpa_driver_iphone_set_key(void *priv, wpa_alg alg, const u8 *addr, - int key_idx, int set_tx, const u8 *seq, - size_t seq_len, const u8 *key, - size_t key_len) -{ - /* - * TODO: Need to either support configuring PMK for 4-way handshake or - * PTK for TKIP/CCMP. - */ - return -1; -} - - -static int wpa_driver_iphone_get_capa(void *priv, struct wpa_driver_capa *capa) -{ - os_memset(capa, 0, sizeof(*capa)); - - capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | - WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | - WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | - WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; - capa->enc = WPA_DRIVER_CAPA_ENC_WEP40 | WPA_DRIVER_CAPA_ENC_WEP104 | - WPA_DRIVER_CAPA_ENC_TKIP | WPA_DRIVER_CAPA_ENC_CCMP; - capa->auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED | - WPA_DRIVER_AUTH_LEAP; - capa->flags = WPA_DRIVER_FLAGS_4WAY_HANDSHAKE; - - return 0; -} - - -static void * wpa_driver_iphone_init(void *ctx, const char *ifname) -{ - struct wpa_driver_iphone_data *drv; - int err; - char power; - CFStringRef name; - CFDictionaryRef dict; - - drv = os_zalloc(sizeof(*drv)); - if (drv == NULL) - return NULL; - drv->ctx = ctx; - err = Apple80211Open(&drv->wireless_ctx); - if (err) { - wpa_printf(MSG_ERROR, "iPhone: Apple80211Open failed: %d", - err); - os_free(drv); - return NULL; - } - - name = CFStringCreateWithCString(kCFAllocatorDefault, ifname, - kCFStringEncodingISOLatin1); - if (name == NULL) { - wpa_printf(MSG_ERROR, "iPhone: ifname -> CFString failed"); - Apple80211Close(drv->wireless_ctx); - os_free(drv); - return NULL; - } - - err = Apple80211BindToInterface(drv->wireless_ctx, name); - CFRelease(name); - - if (err) { - wpa_printf(MSG_ERROR, "iPhone: Apple80211BindToInterface " - "failed: %d", err); - Apple80211Close(drv->wireless_ctx); - os_free(drv); - return NULL; - } - - err = Apple80211GetPower(drv->wireless_ctx, &power); - if (err) - wpa_printf(MSG_DEBUG, "iPhone: Apple80211GetPower failed: %d", - err); - - wpa_printf(MSG_DEBUG, "iPhone: Power=%d", power); - - if (!power) { - drv->ctrl_power = 1; - err = Apple80211SetPower(drv->wireless_ctx, 1); - if (err) { - wpa_printf(MSG_DEBUG, "iPhone: Apple80211SetPower " - "failed: %d", err); - Apple80211Close(drv->wireless_ctx); - os_free(drv); - return NULL; - } - } - - err = Apple80211GetInfoCopy(drv->wireless_ctx, &dict); - if (err == 0) { - CFShow(dict); - CFRelease(dict); - } else { - printf("Apple80211GetInfoCopy: %d\n", err); - } - - return drv; -} - - -static void wpa_driver_iphone_deinit(void *priv) -{ - struct wpa_driver_iphone_data *drv = priv; - int err; - - eloop_cancel_timeout(wpa_driver_iphone_scan_timeout, drv, drv->ctx); - eloop_cancel_timeout(wpa_driver_iphone_assoc_timeout, drv, drv->ctx); - - if (drv->ctrl_power) { - wpa_printf(MSG_DEBUG, "iPhone: Power down the interface"); - err = Apple80211SetPower(drv->wireless_ctx, 0); - if (err) { - wpa_printf(MSG_DEBUG, "iPhone: Apple80211SetPower(0) " - "failed: %d", err); - } - } - - err = Apple80211Close(drv->wireless_ctx); - if (err) { - wpa_printf(MSG_DEBUG, "iPhone: Apple80211Close failed: %d", - err); - } - - if (drv->scan_results) - CFRelease(drv->scan_results); - - os_free(drv); -} - - -const struct wpa_driver_ops wpa_driver_iphone_ops = { - .name = "iphone", - .desc = "iPhone/iPod touch Apple80211 driver", - .get_ssid = wpa_driver_iphone_get_ssid, - .get_bssid = wpa_driver_iphone_get_bssid, - .init = wpa_driver_iphone_init, - .deinit = wpa_driver_iphone_deinit, - .scan = wpa_driver_iphone_scan, - .get_scan_results = wpa_driver_iphone_get_scan_results, - .associate = wpa_driver_iphone_associate, - .set_key = wpa_driver_iphone_set_key, - .get_capa = wpa_driver_iphone_get_capa, -}; diff --git a/src/drivers/driver_ipw.c b/src/drivers/driver_ipw.c deleted file mode 100644 index 77984f9e5c4cc..0000000000000 --- a/src/drivers/driver_ipw.c +++ /dev/null @@ -1,472 +0,0 @@ -/* - * WPA Supplicant - driver interaction with Linux ipw2100/2200 drivers - * Copyright (c) 2005 Zhu Yi <yi.zhu@intel.com> - * Copyright (c) 2004 Lubomir Gelo <lgelo@cnc.sk> - * Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - * - * Please note that ipw2100/2200 drivers change to use generic Linux wireless - * extensions if the kernel includes support for WE-18 or newer (Linux 2.6.13 - * or newer). driver_wext.c should be used in those cases. - */ - -#include "includes.h" -#include <sys/ioctl.h> - -#include "wireless_copy.h" -#include "common.h" -#include "driver.h" -#include "driver_wext.h" - -struct wpa_driver_ipw_data { - void *wext; /* private data for driver_wext */ - void *ctx; - char ifname[IFNAMSIZ + 1]; - int sock; -}; - -/* following definitions must be kept in sync with ipw2100.c and ipw2200.c */ - -#define IPW_IOCTL_WPA_SUPPLICANT SIOCIWFIRSTPRIV+30 - -#define IPW_CMD_SET_WPA_PARAM 1 -#define IPW_CMD_SET_WPA_IE 2 -#define IPW_CMD_SET_ENCRYPTION 3 -#define IPW_CMD_MLME 4 - -#define IPW_PARAM_WPA_ENABLED 1 -#define IPW_PARAM_TKIP_COUNTERMEASURES 2 -#define IPW_PARAM_DROP_UNENCRYPTED 3 -#define IPW_PARAM_PRIVACY_INVOKED 4 -#define IPW_PARAM_AUTH_ALGS 5 -#define IPW_PARAM_IEEE_802_1X 6 - -#define IPW_MLME_STA_DEAUTH 1 -#define IPW_MLME_STA_DISASSOC 2 - -#define IPW_CRYPT_ERR_UNKNOWN_ALG 2 -#define IPW_CRYPT_ERR_UNKNOWN_ADDR 3 -#define IPW_CRYPT_ERR_CRYPT_INIT_FAILED 4 -#define IPW_CRYPT_ERR_KEY_SET_FAILED 5 -#define IPW_CRYPT_ERR_TX_KEY_SET_FAILED 6 -#define IPW_CRYPT_ERR_CARD_CONF_FAILED 7 - -#define IPW_CRYPT_ALG_NAME_LEN 16 - -struct ipw_param { - u32 cmd; - u8 sta_addr[ETH_ALEN]; - union { - struct { - u8 name; - u32 value; - } wpa_param; - struct { - u32 len; - u8 reserved[32]; - u8 data[0]; - } wpa_ie; - struct{ - u32 command; - u32 reason_code; - } mlme; - struct { - u8 alg[IPW_CRYPT_ALG_NAME_LEN]; - u8 set_tx; - u32 err; - u8 idx; - u8 seq[8]; - u16 key_len; - u8 key[0]; - } crypt; - - } u; -}; - -/* end of ipw2100.c and ipw2200.c code */ - -static int wpa_driver_ipw_set_auth_alg(void *priv, int auth_alg); - -static int ipw_ioctl(struct wpa_driver_ipw_data *drv, - struct ipw_param *param, int len, int show_err) -{ - struct iwreq iwr; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - iwr.u.data.pointer = (caddr_t) param; - iwr.u.data.length = len; - - if (ioctl(drv->sock, IPW_IOCTL_WPA_SUPPLICANT, &iwr) < 0) { - int ret = errno; - if (show_err) - perror("ioctl[IPW_IOCTL_WPA_SUPPLICANT]"); - return ret; - } - - return 0; -} - - -static void ipw_show_set_key_error(struct ipw_param *param) -{ - switch (param->u.crypt.err) { - case IPW_CRYPT_ERR_UNKNOWN_ALG: - wpa_printf(MSG_INFO, "Unknown algorithm '%s'.", - param->u.crypt.alg); - wpa_printf(MSG_INFO, "You may need to load kernel module to " - "register that algorithm."); - wpa_printf(MSG_INFO, "E.g., 'modprobe ieee80211_crypt_wep' for" - " WEP."); - break; - case IPW_CRYPT_ERR_UNKNOWN_ADDR: - wpa_printf(MSG_INFO, "Unknown address " MACSTR ".", - MAC2STR(param->sta_addr)); - break; - case IPW_CRYPT_ERR_CRYPT_INIT_FAILED: - wpa_printf(MSG_INFO, "Crypt algorithm initialization failed."); - break; - case IPW_CRYPT_ERR_KEY_SET_FAILED: - wpa_printf(MSG_INFO, "Key setting failed."); - break; - case IPW_CRYPT_ERR_TX_KEY_SET_FAILED: - wpa_printf(MSG_INFO, "TX key index setting failed."); - break; - case IPW_CRYPT_ERR_CARD_CONF_FAILED: - wpa_printf(MSG_INFO, "Card configuration failed."); - break; - } -} - - -static int ipw_set_wpa_ie(struct wpa_driver_ipw_data *drv, - const u8 *wpa_ie, size_t wpa_ie_len) -{ - struct ipw_param *param; - int ret; - size_t blen = sizeof(*param) + wpa_ie_len; - - param = os_zalloc(blen); - if (param == NULL) - return -1; - - param->cmd = IPW_CMD_SET_WPA_IE; - param->u.wpa_ie.len = wpa_ie_len; - os_memcpy(param->u.wpa_ie.data, wpa_ie, wpa_ie_len); - - ret = ipw_ioctl(drv, param, blen, 1); - - os_free(param); - return ret; -} - - -static int ipw_set_wpa_param(struct wpa_driver_ipw_data *drv, u8 name, - u32 value) -{ - struct ipw_param param; - - os_memset(¶m, 0, sizeof(param)); - param.cmd = IPW_CMD_SET_WPA_PARAM; - param.u.wpa_param.name = name; - param.u.wpa_param.value = value; - - return ipw_ioctl(drv, ¶m, sizeof(param), 1); -} - - -static int ipw_mlme(struct wpa_driver_ipw_data *drv, const u8 *addr, - int cmd, int reason) -{ - struct ipw_param param; - - os_memset(¶m, 0, sizeof(param)); - os_memcpy(param.sta_addr, addr, ETH_ALEN); - param.cmd = IPW_CMD_MLME; - param.u.mlme.command = cmd; - param.u.mlme.reason_code = reason; - - return ipw_ioctl(drv, ¶m, sizeof(param), 1); -} - - -static int wpa_driver_ipw_set_wpa(void *priv, int enabled) -{ - struct wpa_driver_ipw_data *drv = priv; - int ret = 0; - - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); - - if (!enabled && ipw_set_wpa_ie(drv, NULL, 0) < 0) - ret = -1; - - if (ipw_set_wpa_param(drv, IPW_PARAM_WPA_ENABLED, enabled) < 0) - ret = -1; - - return ret; -} - - -static int wpa_driver_ipw_set_key(const char *ifname, void *priv, - enum wpa_alg alg, const u8 *addr, - int key_idx, int set_tx, - const u8 *seq, size_t seq_len, - const u8 *key, size_t key_len) -{ - struct wpa_driver_ipw_data *drv = priv; - struct ipw_param *param; - u8 *buf; - size_t blen; - int ret = 0; - char *alg_name; - - switch (alg) { - case WPA_ALG_NONE: - alg_name = "none"; - break; - case WPA_ALG_WEP: - alg_name = "WEP"; - break; - case WPA_ALG_TKIP: - alg_name = "TKIP"; - break; - case WPA_ALG_CCMP: - alg_name = "CCMP"; - break; - default: - return -1; - } - - wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu " - "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx, - (unsigned long) seq_len, (unsigned long) key_len); - - if (seq_len > 8) - return -2; - - blen = sizeof(*param) + key_len; - buf = os_zalloc(blen); - if (buf == NULL) - return -1; - - param = (struct ipw_param *) buf; - param->cmd = IPW_CMD_SET_ENCRYPTION; - os_memset(param->sta_addr, 0xff, ETH_ALEN); - os_strlcpy((char *) param->u.crypt.alg, alg_name, - IPW_CRYPT_ALG_NAME_LEN); - param->u.crypt.set_tx = set_tx ? 1 : 0; - param->u.crypt.idx = key_idx; - os_memcpy(param->u.crypt.seq, seq, seq_len); - param->u.crypt.key_len = key_len; - os_memcpy((u8 *) (param + 1), key, key_len); - - if (ipw_ioctl(drv, param, blen, 1)) { - wpa_printf(MSG_WARNING, "Failed to set encryption."); - ipw_show_set_key_error(param); - ret = -1; - } - os_free(buf); - - return ret; -} - - -static int wpa_driver_ipw_set_countermeasures(void *priv, int enabled) -{ - struct wpa_driver_ipw_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); - return ipw_set_wpa_param(drv, IPW_PARAM_TKIP_COUNTERMEASURES, - enabled); - -} - - -static int wpa_driver_ipw_set_drop_unencrypted(void *priv, int enabled) -{ - struct wpa_driver_ipw_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); - return ipw_set_wpa_param(drv, IPW_PARAM_DROP_UNENCRYPTED, - enabled); -} - - -static int wpa_driver_ipw_deauthenticate(void *priv, const u8 *addr, - int reason_code) -{ - struct wpa_driver_ipw_data *drv = priv; - return ipw_mlme(drv, addr, IPW_MLME_STA_DEAUTH, reason_code); -} - - -static int wpa_driver_ipw_disassociate(void *priv, const u8 *addr, - int reason_code) -{ - struct wpa_driver_ipw_data *drv = priv; - return ipw_mlme(drv, addr, IPW_MLME_STA_DISASSOC, reason_code); -} - - -static int -wpa_driver_ipw_associate(void *priv, struct wpa_driver_associate_params *params) -{ - struct wpa_driver_ipw_data *drv = priv; - int ret = 0; - int unencrypted_eapol; - - if (wpa_driver_ipw_set_auth_alg(drv, params->auth_alg) < 0) - ret = -1; - if (wpa_driver_ipw_set_drop_unencrypted(drv, params->drop_unencrypted) - < 0) - ret = -1; - if (ipw_set_wpa_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0) - ret = -1; - if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, - params->ssid_len) < 0) - ret = -1; - if (wpa_driver_wext_set_bssid(drv->wext, params->bssid) < 0) - ret = -1; - - if (params->key_mgmt_suite == KEY_MGMT_802_1X || - params->key_mgmt_suite == KEY_MGMT_PSK) - unencrypted_eapol = 0; - else - unencrypted_eapol = 1; - - if (ipw_set_wpa_param(drv, IPW_PARAM_IEEE_802_1X, - unencrypted_eapol) < 0) { - wpa_printf(MSG_DEBUG, "ipw: Failed to configure " - "ieee_802_1x param"); - } - - return ret; -} - - -static int wpa_driver_ipw_set_auth_alg(void *priv, int auth_alg) -{ - struct wpa_driver_ipw_data *drv = priv; - int algs = 0; - - if (auth_alg & WPA_AUTH_ALG_OPEN) - algs |= 1; - if (auth_alg & WPA_AUTH_ALG_SHARED) - algs |= 2; - if (auth_alg & WPA_AUTH_ALG_LEAP) - algs |= 4; - if (algs == 0) - algs = 1; /* at least one algorithm should be set */ - - wpa_printf(MSG_DEBUG, "%s: auth_alg=0x%x", __FUNCTION__, algs); - return ipw_set_wpa_param(drv, IPW_PARAM_AUTH_ALGS, algs); -} - - -static int wpa_driver_ipw_get_bssid(void *priv, u8 *bssid) -{ - struct wpa_driver_ipw_data *drv = priv; - return wpa_driver_wext_get_bssid(drv->wext, bssid); -} - - -static int wpa_driver_ipw_get_ssid(void *priv, u8 *ssid) -{ - struct wpa_driver_ipw_data *drv = priv; - return wpa_driver_wext_get_ssid(drv->wext, ssid); -} - - -static int wpa_driver_ipw_scan(void *priv, - struct wpa_driver_scan_params *params) -{ - struct wpa_driver_ipw_data *drv = priv; - return wpa_driver_wext_scan(drv->wext, params); -} - - -static struct wpa_scan_results * wpa_driver_ipw_get_scan_results(void *priv) -{ - struct wpa_driver_ipw_data *drv = priv; - return wpa_driver_wext_get_scan_results(drv->wext); -} - - -static int wpa_driver_ipw_set_operstate(void *priv, int state) -{ - struct wpa_driver_ipw_data *drv = priv; - return wpa_driver_wext_set_operstate(drv->wext, state); -} - - -static void * wpa_driver_ipw_init(void *ctx, const char *ifname) -{ - struct wpa_driver_ipw_data *drv; - int ver; - - wpa_printf(MSG_DEBUG, "%s is called", __FUNCTION__); - drv = os_zalloc(sizeof(*drv)); - if (drv == NULL) - return NULL; - drv->wext = wpa_driver_wext_init(ctx, ifname); - if (drv->wext == NULL) { - os_free(drv); - return NULL; - } - - ver = wpa_driver_wext_get_version(drv->wext); - if (ver >= 18) { - wpa_printf(MSG_WARNING, "Linux wireless extensions version %d " - "detected.", ver); - wpa_printf(MSG_WARNING, "ipw2x00 driver uses driver_wext " - "(-Dwext) instead of driver_ipw."); - } - - drv->ctx = ctx; - os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); - drv->sock = socket(PF_INET, SOCK_DGRAM, 0); - if (drv->sock < 0) { - wpa_driver_wext_deinit(drv->wext); - os_free(drv); - return NULL; - } - - wpa_driver_ipw_set_wpa(drv, 1); - - return drv; -} - - -static void wpa_driver_ipw_deinit(void *priv) -{ - struct wpa_driver_ipw_data *drv = priv; - wpa_driver_ipw_set_wpa(drv, 0); - wpa_driver_wext_deinit(drv->wext); - close(drv->sock); - os_free(drv); -} - - -const struct wpa_driver_ops wpa_driver_ipw_ops = { - .name = "ipw", - .desc = "Intel ipw2100/2200 driver (old; use wext with Linux 2.6.13 " - "or newer)", - .get_bssid = wpa_driver_ipw_get_bssid, - .get_ssid = wpa_driver_ipw_get_ssid, - .set_key = wpa_driver_ipw_set_key, - .set_countermeasures = wpa_driver_ipw_set_countermeasures, - .scan2 = wpa_driver_ipw_scan, - .get_scan_results2 = wpa_driver_ipw_get_scan_results, - .deauthenticate = wpa_driver_ipw_deauthenticate, - .disassociate = wpa_driver_ipw_disassociate, - .associate = wpa_driver_ipw_associate, - .init = wpa_driver_ipw_init, - .deinit = wpa_driver_ipw_deinit, - .set_operstate = wpa_driver_ipw_set_operstate, -}; diff --git a/src/drivers/driver_madwifi.c b/src/drivers/driver_madwifi.c index 8687404db9026..bb48011dfe579 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 @@ -27,7 +21,7 @@ #include "driver_wext.h" #include "eloop.h" #include "common/ieee802_11_defs.h" -#include "wireless_copy.h" +#include "linux_wext.h" /* * Avoid conflicts with wpa_supplicant definitions by undefining a definition. @@ -69,8 +63,7 @@ #define MADWIFI_NG #endif /* IEEE80211_IOCTL_SETWMMPARAMS */ - -#ifdef HOSTAPD +#define WPA_KEY_RSC_LEN 8 #include "priv_netlink.h" #include "netlink.h" @@ -461,7 +454,7 @@ wpa_driver_madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg, memset(&wk, 0, sizeof(wk)); wk.ik_type = cipher; wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT; - if (addr == NULL) { + if (addr == NULL || is_broadcast_ether_addr(addr)) { memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); wk.ik_keyix = key_idx; wk.ik_flags |= IEEE80211_KEY_DEFAULT; @@ -733,6 +726,8 @@ static void madwifi_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, os_memset(&event, 0, sizeof(event)); event.rx_probe_req.sa = mgmt->sa; + event.rx_probe_req.da = mgmt->da; + event.rx_probe_req.bssid = mgmt->bssid; event.rx_probe_req.ie = mgmt->u.probe_req.variable; event.rx_probe_req.ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); @@ -787,7 +782,8 @@ madwifi_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype) static int madwifi_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, - const struct wpabuf *proberesp) + const struct wpabuf *proberesp, + const struct wpabuf *assocresp) { if (madwifi_set_wps_ie(priv, beacon ? wpabuf_head(beacon) : NULL, beacon ? wpabuf_len(beacon) : 0, @@ -802,6 +798,24 @@ madwifi_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, #define madwifi_set_ap_wps_ie NULL #endif /* CONFIG_WPS */ +static int madwifi_set_freq(void *priv, struct hostapd_freq_params *freq) +{ + struct madwifi_driver_data *drv = priv; + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.freq.m = freq->channel; + iwr.u.freq.e = 0; + + if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { + perror("ioctl[SIOCSIWFREQ]"); + return -1; + } + + return 0; +} + static void madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) { @@ -846,7 +860,7 @@ madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) ielen += 2; no_ie: - drv_event_assoc(hapd, addr, iebuf, ielen); + drv_event_assoc(hapd, addr, iebuf, ielen, 0); if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { /* Cached accounting data is not valid anymore. */ @@ -1065,7 +1079,7 @@ madwifi_wireless_event_init(struct madwifi_driver_data *drv) static int madwifi_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, - int encrypt, const u8 *own_addr) + int encrypt, const u8 *own_addr, u32 flags) { struct madwifi_driver_data *drv = priv; unsigned char buf[3000]; @@ -1272,554 +1286,11 @@ madwifi_commit(void *priv) return linux_set_iface_flags(drv->ioctl_sock, drv->iface, 1); } -#else /* HOSTAPD */ - -struct wpa_driver_madwifi_data { - void *wext; /* private data for driver_wext */ - void *ctx; - char ifname[IFNAMSIZ + 1]; - int sock; -}; - -static int wpa_driver_madwifi_set_auth_alg(void *priv, int auth_alg); -static int wpa_driver_madwifi_set_probe_req_ie(void *priv, const u8 *ies, - size_t ies_len); - - -static int -set80211priv(struct wpa_driver_madwifi_data *drv, int op, void *data, int len, - int show_err) -{ - struct iwreq iwr; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - if (len < IFNAMSIZ && - op != IEEE80211_IOCTL_SET_APPIEBUF) { - /* - * Argument data fits inline; put it there. - */ - os_memcpy(iwr.u.name, data, len); - } else { - /* - * Argument data too big for inline transfer; setup a - * parameter block instead; the kernel will transfer - * the data for the driver. - */ - iwr.u.data.pointer = data; - iwr.u.data.length = len; - } - - if (ioctl(drv->sock, op, &iwr) < 0) { - if (show_err) { -#ifdef MADWIFI_NG - int first = IEEE80211_IOCTL_SETPARAM; - int last = IEEE80211_IOCTL_KICKMAC; - static const char *opnames[] = { - "ioctl[IEEE80211_IOCTL_SETPARAM]", - "ioctl[IEEE80211_IOCTL_GETPARAM]", - "ioctl[IEEE80211_IOCTL_SETMODE]", - "ioctl[IEEE80211_IOCTL_GETMODE]", - "ioctl[IEEE80211_IOCTL_SETWMMPARAMS]", - "ioctl[IEEE80211_IOCTL_GETWMMPARAMS]", - "ioctl[IEEE80211_IOCTL_SETCHANLIST]", - "ioctl[IEEE80211_IOCTL_GETCHANLIST]", - "ioctl[IEEE80211_IOCTL_CHANSWITCH]", - NULL, - "ioctl[IEEE80211_IOCTL_SET_APPIEBUF]", - "ioctl[IEEE80211_IOCTL_GETSCANRESULTS]", - NULL, - "ioctl[IEEE80211_IOCTL_GETCHANINFO]", - "ioctl[IEEE80211_IOCTL_SETOPTIE]", - "ioctl[IEEE80211_IOCTL_GETOPTIE]", - "ioctl[IEEE80211_IOCTL_SETMLME]", - NULL, - "ioctl[IEEE80211_IOCTL_SETKEY]", - NULL, - "ioctl[IEEE80211_IOCTL_DELKEY]", - NULL, - "ioctl[IEEE80211_IOCTL_ADDMAC]", - NULL, - "ioctl[IEEE80211_IOCTL_DELMAC]", - NULL, - "ioctl[IEEE80211_IOCTL_WDSMAC]", - NULL, - "ioctl[IEEE80211_IOCTL_WDSDELMAC]", - NULL, - "ioctl[IEEE80211_IOCTL_KICKMAC]", - }; -#else /* MADWIFI_NG */ - int first = IEEE80211_IOCTL_SETPARAM; - int last = IEEE80211_IOCTL_CHANLIST; - static const char *opnames[] = { - "ioctl[IEEE80211_IOCTL_SETPARAM]", - "ioctl[IEEE80211_IOCTL_GETPARAM]", - "ioctl[IEEE80211_IOCTL_SETKEY]", - "ioctl[IEEE80211_IOCTL_GETKEY]", - "ioctl[IEEE80211_IOCTL_DELKEY]", - NULL, - "ioctl[IEEE80211_IOCTL_SETMLME]", - NULL, - "ioctl[IEEE80211_IOCTL_SETOPTIE]", - "ioctl[IEEE80211_IOCTL_GETOPTIE]", - "ioctl[IEEE80211_IOCTL_ADDMAC]", - NULL, - "ioctl[IEEE80211_IOCTL_DELMAC]", - NULL, - "ioctl[IEEE80211_IOCTL_CHANLIST]", - }; -#endif /* MADWIFI_NG */ - int idx = op - first; - if (first <= op && op <= last && - idx < (int) (sizeof(opnames) / sizeof(opnames[0])) - && opnames[idx]) - perror(opnames[idx]); - else - perror("ioctl[unknown???]"); - } - return -1; - } - return 0; -} - -static int -set80211param(struct wpa_driver_madwifi_data *drv, int op, int arg, - int show_err) -{ - struct iwreq iwr; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - iwr.u.mode = op; - os_memcpy(iwr.u.name+sizeof(u32), &arg, sizeof(arg)); - - if (ioctl(drv->sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) { - if (show_err) - perror("ioctl[IEEE80211_IOCTL_SETPARAM]"); - return -1; - } - return 0; -} - -static int -wpa_driver_madwifi_set_wpa_ie(struct wpa_driver_madwifi_data *drv, - const u8 *wpa_ie, size_t wpa_ie_len) -{ - struct iwreq iwr; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - /* NB: SETOPTIE is not fixed-size so must not be inlined */ - iwr.u.data.pointer = (void *) wpa_ie; - iwr.u.data.length = wpa_ie_len; - - if (ioctl(drv->sock, IEEE80211_IOCTL_SETOPTIE, &iwr) < 0) { - perror("ioctl[IEEE80211_IOCTL_SETOPTIE]"); - return -1; - } - return 0; -} - -static int -wpa_driver_madwifi_del_key(struct wpa_driver_madwifi_data *drv, int key_idx, - const u8 *addr) -{ - struct ieee80211req_del_key wk; - - wpa_printf(MSG_DEBUG, "%s: keyidx=%d", __FUNCTION__, key_idx); - os_memset(&wk, 0, sizeof(wk)); - wk.idk_keyix = key_idx; - if (addr != NULL) - os_memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN); - - return set80211priv(drv, IEEE80211_IOCTL_DELKEY, &wk, sizeof(wk), 1); -} - -static int -wpa_driver_madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg, - const u8 *addr, int key_idx, int set_tx, - const u8 *seq, size_t seq_len, - const u8 *key, size_t key_len) -{ - struct wpa_driver_madwifi_data *drv = priv; - struct ieee80211req_key wk; - char *alg_name; - u_int8_t cipher; - - if (alg == WPA_ALG_NONE) - return wpa_driver_madwifi_del_key(drv, key_idx, addr); - - switch (alg) { - case WPA_ALG_WEP: - if (addr == NULL || os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", - ETH_ALEN) == 0) { - /* - * madwifi did not seem to like static WEP key - * configuration with IEEE80211_IOCTL_SETKEY, so use - * Linux wireless extensions ioctl for this. - */ - return wpa_driver_wext_set_key(ifname, drv->wext, alg, - addr, key_idx, set_tx, - seq, seq_len, - key, key_len); - } - alg_name = "WEP"; - cipher = IEEE80211_CIPHER_WEP; - break; - case WPA_ALG_TKIP: - alg_name = "TKIP"; - cipher = IEEE80211_CIPHER_TKIP; - break; - case WPA_ALG_CCMP: - alg_name = "CCMP"; - cipher = IEEE80211_CIPHER_AES_CCM; - break; - default: - wpa_printf(MSG_DEBUG, "%s: unknown/unsupported algorithm %d", - __FUNCTION__, alg); - return -1; - } - - wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu " - "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx, - (unsigned long) seq_len, (unsigned long) key_len); - - if (seq_len > sizeof(u_int64_t)) { - wpa_printf(MSG_DEBUG, "%s: seq_len %lu too big", - __FUNCTION__, (unsigned long) seq_len); - return -2; - } - if (key_len > sizeof(wk.ik_keydata)) { - wpa_printf(MSG_DEBUG, "%s: key length %lu too big", - __FUNCTION__, (unsigned long) key_len); - return -3; - } - - os_memset(&wk, 0, sizeof(wk)); - wk.ik_type = cipher; - wk.ik_flags = IEEE80211_KEY_RECV; - if (addr == NULL || - os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0) - wk.ik_flags |= IEEE80211_KEY_GROUP; - if (set_tx) { - wk.ik_flags |= IEEE80211_KEY_XMIT | IEEE80211_KEY_DEFAULT; - os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); - } else - os_memset(wk.ik_macaddr, 0, IEEE80211_ADDR_LEN); - wk.ik_keyix = key_idx; - wk.ik_keylen = key_len; -#ifdef WORDS_BIGENDIAN -#define WPA_KEY_RSC_LEN 8 - { - size_t i; - u8 tmp[WPA_KEY_RSC_LEN]; - os_memset(tmp, 0, sizeof(tmp)); - for (i = 0; i < seq_len; i++) - tmp[WPA_KEY_RSC_LEN - i - 1] = seq[i]; - os_memcpy(&wk.ik_keyrsc, tmp, WPA_KEY_RSC_LEN); - } -#else /* WORDS_BIGENDIAN */ - os_memcpy(&wk.ik_keyrsc, seq, seq_len); -#endif /* WORDS_BIGENDIAN */ - os_memcpy(wk.ik_keydata, key, key_len); - - return set80211priv(drv, IEEE80211_IOCTL_SETKEY, &wk, sizeof(wk), 1); -} - -static int -wpa_driver_madwifi_set_countermeasures(void *priv, int enabled) -{ - struct wpa_driver_madwifi_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); - return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled, 1); -} - -static int -wpa_driver_madwifi_deauthenticate(void *priv, const u8 *addr, int reason_code) -{ - struct wpa_driver_madwifi_data *drv = priv; - struct ieee80211req_mlme mlme; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - mlme.im_op = IEEE80211_MLME_DEAUTH; - mlme.im_reason = reason_code; - os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); - return set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme), 1); -} - -static int -wpa_driver_madwifi_disassociate(void *priv, const u8 *addr, int reason_code) -{ - struct wpa_driver_madwifi_data *drv = priv; - struct ieee80211req_mlme mlme; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - mlme.im_op = IEEE80211_MLME_DISASSOC; - mlme.im_reason = reason_code; - os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); - return set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme), 1); -} - -static int -wpa_driver_madwifi_associate(void *priv, - struct wpa_driver_associate_params *params) -{ - struct wpa_driver_madwifi_data *drv = priv; - struct ieee80211req_mlme mlme; - int ret = 0, privacy = 1; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - if (set80211param(drv, IEEE80211_PARAM_DROPUNENCRYPTED, - params->drop_unencrypted, 1) < 0) - ret = -1; - if (wpa_driver_madwifi_set_auth_alg(drv, params->auth_alg) < 0) - ret = -1; - - /* - * NB: Don't need to set the freq or cipher-related state as - * this is implied by the bssid which is used to locate - * the scanned node state which holds it. The ssid is - * needed to disambiguate an AP that broadcasts multiple - * ssid's but uses the same bssid. - */ - /* XXX error handling is wrong but unclear what to do... */ - if (wpa_driver_madwifi_set_wpa_ie(drv, params->wpa_ie, - params->wpa_ie_len) < 0) - ret = -1; - - if (params->pairwise_suite == CIPHER_NONE && - params->group_suite == CIPHER_NONE && - params->key_mgmt_suite == KEY_MGMT_NONE && - params->wpa_ie_len == 0) - privacy = 0; - - if (set80211param(drv, IEEE80211_PARAM_PRIVACY, privacy, 1) < 0) - ret = -1; - - if (params->wpa_ie_len && - set80211param(drv, IEEE80211_PARAM_WPA, - params->wpa_ie[0] == WLAN_EID_RSN ? 2 : 1, 1) < 0) - ret = -1; - - if (params->bssid == NULL) { - /* ap_scan=2 mode - driver takes care of AP selection and - * roaming */ - /* FIX: this does not seem to work; would probably need to - * change something in the driver */ - if (set80211param(drv, IEEE80211_PARAM_ROAMING, 0, 1) < 0) - ret = -1; - - if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, - params->ssid_len) < 0) - ret = -1; - } else { - if (set80211param(drv, IEEE80211_PARAM_ROAMING, 2, 1) < 0) - ret = -1; - if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, - params->ssid_len) < 0) - ret = -1; - os_memset(&mlme, 0, sizeof(mlme)); - mlme.im_op = IEEE80211_MLME_ASSOC; - os_memcpy(mlme.im_macaddr, params->bssid, IEEE80211_ADDR_LEN); - if (set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, - sizeof(mlme), 1) < 0) { - wpa_printf(MSG_DEBUG, "%s: SETMLME[ASSOC] failed", - __func__); - ret = -1; - } - } - - return ret; -} - -static int -wpa_driver_madwifi_set_auth_alg(void *priv, int auth_alg) -{ - struct wpa_driver_madwifi_data *drv = priv; - int authmode; - - if ((auth_alg & WPA_AUTH_ALG_OPEN) && - (auth_alg & WPA_AUTH_ALG_SHARED)) - authmode = IEEE80211_AUTH_AUTO; - else if (auth_alg & WPA_AUTH_ALG_SHARED) - authmode = IEEE80211_AUTH_SHARED; - else - authmode = IEEE80211_AUTH_OPEN; - - return set80211param(drv, IEEE80211_PARAM_AUTHMODE, authmode, 1); -} - -static int -wpa_driver_madwifi_scan(void *priv, struct wpa_driver_scan_params *params) -{ - struct wpa_driver_madwifi_data *drv = priv; - struct iwreq iwr; - int ret = 0; - const u8 *ssid = params->ssids[0].ssid; - size_t ssid_len = params->ssids[0].ssid_len; - - wpa_driver_madwifi_set_probe_req_ie(drv, params->extra_ies, - params->extra_ies_len); - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - - /* set desired ssid before scan */ - /* FIX: scan should not break the current association, so using - * set_ssid may not be the best way of doing this.. */ - if (wpa_driver_wext_set_ssid(drv->wext, ssid, ssid_len) < 0) - ret = -1; - - if (ioctl(drv->sock, SIOCSIWSCAN, &iwr) < 0) { - perror("ioctl[SIOCSIWSCAN]"); - ret = -1; - } - - /* - * madwifi delivers a scan complete event so no need to poll, but - * register a backup timeout anyway to make sure that we recover even - * if the driver does not send this event for any reason. This timeout - * will only be used if the event is not delivered (event handler will - * cancel the timeout). - */ - eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv->wext, - drv->ctx); - eloop_register_timeout(30, 0, wpa_driver_wext_scan_timeout, drv->wext, - drv->ctx); - - return ret; -} - -static int wpa_driver_madwifi_get_bssid(void *priv, u8 *bssid) -{ - struct wpa_driver_madwifi_data *drv = priv; - return wpa_driver_wext_get_bssid(drv->wext, bssid); -} - - -static int wpa_driver_madwifi_get_ssid(void *priv, u8 *ssid) -{ - struct wpa_driver_madwifi_data *drv = priv; - return wpa_driver_wext_get_ssid(drv->wext, ssid); -} - - -static struct wpa_scan_results * -wpa_driver_madwifi_get_scan_results(void *priv) -{ - struct wpa_driver_madwifi_data *drv = priv; - return wpa_driver_wext_get_scan_results(drv->wext); -} - - -static int wpa_driver_madwifi_set_operstate(void *priv, int state) -{ - struct wpa_driver_madwifi_data *drv = priv; - return wpa_driver_wext_set_operstate(drv->wext, state); -} - - -static int wpa_driver_madwifi_set_probe_req_ie(void *priv, const u8 *ies, - size_t ies_len) -{ - struct ieee80211req_getset_appiebuf *probe_req_ie; - int ret; - - probe_req_ie = os_malloc(sizeof(*probe_req_ie) + ies_len); - if (probe_req_ie == NULL) - return -1; - - probe_req_ie->app_frmtype = IEEE80211_APPIE_FRAME_PROBE_REQ; - probe_req_ie->app_buflen = ies_len; - os_memcpy(probe_req_ie->app_buf, ies, ies_len); - - ret = set80211priv(priv, IEEE80211_IOCTL_SET_APPIEBUF, probe_req_ie, - sizeof(struct ieee80211req_getset_appiebuf) + - ies_len, 1); - - os_free(probe_req_ie); - - return ret; -} - - -static void * wpa_driver_madwifi_init(void *ctx, const char *ifname) -{ - struct wpa_driver_madwifi_data *drv; - - drv = os_zalloc(sizeof(*drv)); - if (drv == NULL) - return NULL; - drv->wext = wpa_driver_wext_init(ctx, ifname); - if (drv->wext == NULL) - goto fail; - - drv->ctx = ctx; - os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); - drv->sock = socket(PF_INET, SOCK_DGRAM, 0); - if (drv->sock < 0) - goto fail2; - - if (set80211param(drv, IEEE80211_PARAM_ROAMING, 2, 1) < 0) { - wpa_printf(MSG_DEBUG, "%s: failed to set wpa_supplicant-based " - "roaming", __FUNCTION__); - goto fail3; - } - - if (set80211param(drv, IEEE80211_PARAM_WPA, 3, 1) < 0) { - wpa_printf(MSG_DEBUG, "%s: failed to enable WPA support", - __FUNCTION__); - goto fail3; - } - - return drv; - -fail3: - close(drv->sock); -fail2: - wpa_driver_wext_deinit(drv->wext); -fail: - os_free(drv); - return NULL; -} - - -static void wpa_driver_madwifi_deinit(void *priv) -{ - struct wpa_driver_madwifi_data *drv = priv; - - if (wpa_driver_madwifi_set_wpa_ie(drv, NULL, 0) < 0) { - wpa_printf(MSG_DEBUG, "%s: failed to clear WPA IE", - __FUNCTION__); - } - if (set80211param(drv, IEEE80211_PARAM_ROAMING, 0, 1) < 0) { - wpa_printf(MSG_DEBUG, "%s: failed to enable driver-based " - "roaming", __FUNCTION__); - } - if (set80211param(drv, IEEE80211_PARAM_PRIVACY, 0, 1) < 0) { - wpa_printf(MSG_DEBUG, "%s: failed to disable forced Privacy " - "flag", __FUNCTION__); - } - if (set80211param(drv, IEEE80211_PARAM_WPA, 0, 1) < 0) { - wpa_printf(MSG_DEBUG, "%s: failed to disable WPA", - __FUNCTION__); - } - - wpa_driver_wext_deinit(drv->wext); - - close(drv->sock); - os_free(drv); -} - -#endif /* HOSTAPD */ - const struct wpa_driver_ops wpa_driver_madwifi_ops = { .name = "madwifi", .desc = "MADWIFI 802.11 support (Atheros, etc.)", .set_key = wpa_driver_madwifi_set_key, -#ifdef HOSTAPD .hapd_init = madwifi_init, .hapd_deinit = madwifi_deinit, .set_ieee8021x = madwifi_set_ieee8021x, @@ -1838,17 +1309,5 @@ const struct wpa_driver_ops wpa_driver_madwifi_ops = { .sta_clear_stats = madwifi_sta_clear_stats, .commit = madwifi_commit, .set_ap_wps_ie = madwifi_set_ap_wps_ie, -#else /* HOSTAPD */ - .get_bssid = wpa_driver_madwifi_get_bssid, - .get_ssid = wpa_driver_madwifi_get_ssid, - .init = wpa_driver_madwifi_init, - .deinit = wpa_driver_madwifi_deinit, - .set_countermeasures = wpa_driver_madwifi_set_countermeasures, - .scan2 = wpa_driver_madwifi_scan, - .get_scan_results2 = wpa_driver_madwifi_get_scan_results, - .deauthenticate = wpa_driver_madwifi_deauthenticate, - .disassociate = wpa_driver_madwifi_disassociate, - .associate = wpa_driver_madwifi_associate, - .set_operstate = wpa_driver_madwifi_set_operstate, -#endif /* HOSTAPD */ + .set_freq = madwifi_set_freq, }; diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c index 462dd81f58af3..7af3317775ab6 100644 --- a/src/drivers/driver_ndis.c +++ b/src/drivers/driver_ndis.c @@ -2,14 +2,8 @@ * WPA Supplicant - Windows/NDIS driver interface * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifdef __CYGWIN__ @@ -731,14 +725,6 @@ static int wpa_driver_ndis_deauthenticate(void *priv, const u8 *addr, } -static int wpa_driver_ndis_disassociate(void *priv, const u8 *addr, - int reason_code) -{ - struct wpa_driver_ndis_data *drv = priv; - return wpa_driver_ndis_disconnect(drv); -} - - static void wpa_driver_ndis_scan_timeout(void *eloop_ctx, void *timeout_ctx) { wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); @@ -864,7 +850,7 @@ static struct wpa_scan_results * wpa_driver_ndis_get_scan_results(void *priv) os_free(b); return NULL; } - results->res = os_zalloc(count * sizeof(struct wpa_scan_res *)); + results->res = os_calloc(count, sizeof(struct wpa_scan_res *)); if (results->res == NULL) { os_free(results); os_free(b); @@ -1001,8 +987,7 @@ static int wpa_driver_ndis_set_key(const char *ifname, void *priv, int res, pairwise; u8 bssid[ETH_ALEN]; - if (addr == NULL || os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", - ETH_ALEN) == 0) { + if (addr == NULL || is_broadcast_ether_addr(addr)) { /* Group Key */ pairwise = 0; if (wpa_driver_ndis_get_bssid(drv, bssid) < 0) @@ -1066,6 +1051,7 @@ wpa_driver_ndis_associate(void *priv, { struct wpa_driver_ndis_data *drv = priv; u32 auth_mode, encr, priv_mode, mode; + u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; drv->mode = params->mode; @@ -1091,7 +1077,6 @@ wpa_driver_ndis_associate(void *priv, if (params->key_mgmt_suite == KEY_MGMT_NONE || params->key_mgmt_suite == KEY_MGMT_802_1X_NO_WPA) { /* Re-set WEP keys if static WEP configuration is used. */ - u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; int i; for (i = 0; i < 4; i++) { if (!params->wep_key[i]) @@ -1125,6 +1110,22 @@ wpa_driver_ndis_associate(void *priv, } else if (params->key_mgmt_suite == KEY_MGMT_WPS) { auth_mode = Ndis802_11AuthModeOpen; priv_mode = Ndis802_11PrivFilterAcceptAll; + if (params->wps == WPS_MODE_PRIVACY) { + u8 dummy_key[5] = { 0x11, 0x22, 0x33, 0x44, 0x55 }; + /* + * Some NDIS drivers refuse to associate in open mode + * configuration due to Privacy field mismatch, so use + * a workaround to make the configuration look like + * matching one for WPS provisioning. + */ + wpa_printf(MSG_DEBUG, "NDIS: Set dummy WEP key as a " + "workaround to allow driver to associate " + "for WPS"); + wpa_driver_ndis_set_key(drv->ifname, drv, WPA_ALG_WEP, + bcast, 0, 1, + NULL, 0, dummy_key, + sizeof(dummy_key)); + } #endif /* CONFIG_WPS */ } else { priv_mode = Ndis802_11PrivFilter8021xWEP; @@ -1148,6 +1149,12 @@ wpa_driver_ndis_associate(void *priv, encr = Ndis802_11Encryption1Enabled; break; case CIPHER_NONE: +#ifdef CONFIG_WPS + if (params->wps == WPS_MODE_PRIVACY) { + encr = Ndis802_11Encryption1Enabled; + break; + } +#endif /* CONFIG_WPS */ if (params->group_suite == CIPHER_CCMP) encr = Ndis802_11Encryption3Enabled; else if (params->group_suite == CIPHER_TKIP) @@ -1156,7 +1163,14 @@ wpa_driver_ndis_associate(void *priv, encr = Ndis802_11EncryptionDisabled; break; default: +#ifdef CONFIG_WPS + if (params->wps == WPS_MODE_PRIVACY) { + encr = Ndis802_11Encryption1Enabled; + break; + } +#endif /* CONFIG_WPS */ encr = Ndis802_11EncryptionDisabled; + break; }; if (ndis_set_oid(drv, OID_802_11_PRIVACY_FILTER, @@ -3185,94 +3199,32 @@ wpa_driver_ndis_get_interfaces(void *global_priv) } -const struct wpa_driver_ops wpa_driver_ndis_ops = { - "ndis", - "Windows NDIS driver", - wpa_driver_ndis_get_bssid, - wpa_driver_ndis_get_ssid, - wpa_driver_ndis_set_key, - wpa_driver_ndis_init, - wpa_driver_ndis_deinit, - NULL /* set_param */, - NULL /* set_countermeasures */, - wpa_driver_ndis_deauthenticate, - wpa_driver_ndis_disassociate, - wpa_driver_ndis_associate, - wpa_driver_ndis_add_pmkid, - wpa_driver_ndis_remove_pmkid, - wpa_driver_ndis_flush_pmkid, - wpa_driver_ndis_get_capa, - wpa_driver_ndis_poll, - wpa_driver_ndis_get_ifname, - wpa_driver_ndis_get_mac_addr, - NULL /* send_eapol */, - NULL /* set_operstate */, - NULL /* mlme_setprotection */, - NULL /* get_hw_feature_data */, - NULL /* set_channel */, - NULL /* set_ssid */, - NULL /* set_bssid */, - NULL /* send_mlme */, - NULL /* mlme_add_sta */, - NULL /* mlme_remove_sta */, - NULL /* update_ft_ies */, - NULL /* send_ft_action */, - wpa_driver_ndis_get_scan_results, - NULL /* set_country */, - NULL /* global_init */, - NULL /* global_deinit */, - NULL /* init2 */, - wpa_driver_ndis_get_interfaces, - wpa_driver_ndis_scan, - NULL /* authenticate */, - NULL /* set_beacon */, - NULL /* hapd_init */, - NULL /* hapd_deinit */, - NULL /* set_ieee8021x */, - NULL /* set_privacy */, - NULL /* get_seqnum */, - NULL /* flush */, - NULL /* set_generic_elem */, - NULL /* read_sta_data */, - NULL /* hapd_send_eapol */, - NULL /* sta_deauth */, - NULL /* sta_disassoc */, - NULL /* sta_remove */, - NULL /* hapd_get_ssid */, - NULL /* hapd_set_ssid */, - NULL /* hapd_set_countermeasures */, - NULL /* sta_add */, - NULL /* get_inact_sec */, - NULL /* sta_clear_stats */, - NULL /* set_freq */, - NULL /* set_rts */, - NULL /* set_frag */, - NULL /* sta_set_flags */, - NULL /* set_rate_sets */, - NULL /* set_cts_protect */, - NULL /* set_preamble */, - NULL /* set_short_slot_time */, - NULL /* set_tx_queue_params */, - NULL /* valid_bss_mask */, - NULL /* if_add */, - NULL /* if_remove */, - NULL /* set_sta_vlan */, - NULL /* commit */, - NULL /* send_ether */, - NULL /* set_radius_acl_auth */, - NULL /* set_radius_acl_expire */, - NULL /* set_ht_params */, - NULL /* set_ap_wps_ie */, - NULL /* set_supp_port */, - NULL /* set_wds_sta */, - NULL /* send_action */, - NULL /* remain_on_channel */, - NULL /* cancel_remain_on_channel */, - NULL /* probe_req_report */, - NULL /* disable_11b_rates */, - NULL /* deinit_ap */, - NULL /* suspend */, - NULL /* resume */, - NULL /* signal_monitor */, - NULL /* send_frame */ -}; +static const char *ndis_drv_name = "ndis"; +static const char *ndis_drv_desc = "Windows NDIS driver"; + +struct wpa_driver_ops wpa_driver_ndis_ops; + +void driver_ndis_init_ops(void) +{ + os_memset(&wpa_driver_ndis_ops, 0, sizeof(wpa_driver_ndis_ops)); + wpa_driver_ndis_ops.name = ndis_drv_name; + wpa_driver_ndis_ops.desc = ndis_drv_desc; + wpa_driver_ndis_ops.get_bssid = wpa_driver_ndis_get_bssid; + wpa_driver_ndis_ops.get_ssid = wpa_driver_ndis_get_ssid; + wpa_driver_ndis_ops.set_key = wpa_driver_ndis_set_key; + wpa_driver_ndis_ops.init = wpa_driver_ndis_init; + wpa_driver_ndis_ops.deinit = wpa_driver_ndis_deinit; + wpa_driver_ndis_ops.deauthenticate = wpa_driver_ndis_deauthenticate; + wpa_driver_ndis_ops.associate = wpa_driver_ndis_associate; + wpa_driver_ndis_ops.add_pmkid = wpa_driver_ndis_add_pmkid; + wpa_driver_ndis_ops.remove_pmkid = wpa_driver_ndis_remove_pmkid; + wpa_driver_ndis_ops.flush_pmkid = wpa_driver_ndis_flush_pmkid; + wpa_driver_ndis_ops.get_capa = wpa_driver_ndis_get_capa; + wpa_driver_ndis_ops.poll = wpa_driver_ndis_poll; + wpa_driver_ndis_ops.get_ifname = wpa_driver_ndis_get_ifname; + wpa_driver_ndis_ops.get_mac_addr = wpa_driver_ndis_get_mac_addr; + wpa_driver_ndis_ops.get_scan_results2 = + wpa_driver_ndis_get_scan_results; + wpa_driver_ndis_ops.get_interfaces = wpa_driver_ndis_get_interfaces; + wpa_driver_ndis_ops.scan2 = wpa_driver_ndis_scan; +} diff --git a/src/drivers/driver_ndis.h b/src/drivers/driver_ndis.h index f263f0e435858..89d136d3b4289 100644 --- a/src/drivers/driver_ndis.h +++ b/src/drivers/driver_ndis.h @@ -2,14 +2,8 @@ * WPA Supplicant - Windows/NDIS driver interface * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef DRIVER_NDIS_H diff --git a/src/drivers/driver_ndis_.c b/src/drivers/driver_ndis_.c index 4bee9aa543eb5..4d23001964b04 100644 --- a/src/drivers/driver_ndis_.c +++ b/src/drivers/driver_ndis_.c @@ -2,14 +2,8 @@ * WPA Supplicant - Windows/NDIS driver interface - event processing * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * 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_ndiswrapper.c b/src/drivers/driver_ndiswrapper.c deleted file mode 100644 index cd2f61e468a04..0000000000000 --- a/src/drivers/driver_ndiswrapper.c +++ /dev/null @@ -1,378 +0,0 @@ -/* - * WPA Supplicant - driver interaction with Linux ndiswrapper - * Copyright (c) 2004-2006, Giridhar Pemmasani <giri@lmc.cs.sunysb.edu> - * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - * - * Please note that ndiswrapper supports WPA configuration via Linux wireless - * extensions and if the kernel includes support for this, driver_wext.c should - * be used instead of this driver wrapper. - */ - -#include "includes.h" -#include <sys/ioctl.h> - -#include "wireless_copy.h" -#include "common.h" -#include "driver.h" -#include "driver_wext.h" - -struct wpa_driver_ndiswrapper_data { - void *wext; /* private data for driver_wext */ - void *ctx; - char ifname[IFNAMSIZ + 1]; - int sock; -}; - - -struct wpa_key { - enum wpa_alg alg; - const u8 *addr; - int key_index; - int set_tx; - const u8 *seq; - size_t seq_len; - const u8 *key; - size_t key_len; -}; - -struct wpa_assoc_info { - const u8 *bssid; - const u8 *ssid; - size_t ssid_len; - int freq; - const u8 *wpa_ie; - size_t wpa_ie_len; - enum wpa_cipher pairwise_suite; - enum wpa_cipher group_suite; - enum wpa_key_mgmt key_mgmt_suite; - int auth_alg; - int mode; -}; - -#define PRIV_RESET SIOCIWFIRSTPRIV+0 -#define WPA_SET_WPA SIOCIWFIRSTPRIV+1 -#define WPA_SET_KEY SIOCIWFIRSTPRIV+2 -#define WPA_ASSOCIATE SIOCIWFIRSTPRIV+3 -#define WPA_DISASSOCIATE SIOCIWFIRSTPRIV+4 -#define WPA_DROP_UNENCRYPTED SIOCIWFIRSTPRIV+5 -#define WPA_SET_COUNTERMEASURES SIOCIWFIRSTPRIV+6 -#define WPA_DEAUTHENTICATE SIOCIWFIRSTPRIV+7 -#define WPA_SET_AUTH_ALG SIOCIWFIRSTPRIV+8 -#define WPA_INIT SIOCIWFIRSTPRIV+9 -#define WPA_DEINIT SIOCIWFIRSTPRIV+10 -#define WPA_GET_CAPA SIOCIWFIRSTPRIV+11 - -static int wpa_ndiswrapper_set_auth_alg(void *priv, int auth_alg); - -static int get_socket(void) -{ - static const int families[] = { - AF_INET, AF_IPX, AF_AX25, AF_APPLETALK - }; - unsigned int i; - int sock; - - for (i = 0; i < sizeof(families) / sizeof(int); ++i) { - sock = socket(families[i], SOCK_DGRAM, 0); - if (sock >= 0) - return sock; - } - - return -1; -} - -static int iw_set_ext(struct wpa_driver_ndiswrapper_data *drv, int request, - struct iwreq *pwrq) -{ - os_strlcpy(pwrq->ifr_name, drv->ifname, IFNAMSIZ); - return ioctl(drv->sock, request, pwrq); -} - -static int wpa_ndiswrapper_set_wpa(void *priv, int enabled) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - struct iwreq priv_req; - int ret = 0; - - os_memset(&priv_req, 0, sizeof(priv_req)); - - priv_req.u.data.flags = enabled; - if (iw_set_ext(drv, WPA_SET_WPA, &priv_req) < 0) - ret = -1; - return ret; -} - -static int wpa_ndiswrapper_set_key(const char *ifname, void *priv, - enum wpa_alg alg, const u8 *addr, - int key_idx, int set_tx, - const u8 *seq, size_t seq_len, - const u8 *key, size_t key_len) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - struct wpa_key wpa_key; - int ret = 0; - struct iwreq priv_req; - - os_memset(&priv_req, 0, sizeof(priv_req)); - - wpa_key.alg = alg; - wpa_key.addr = addr; - wpa_key.key_index = key_idx; - wpa_key.set_tx = set_tx; - wpa_key.seq = seq; - wpa_key.seq_len = seq_len; - wpa_key.key = key; - wpa_key.key_len = key_len; - - priv_req.u.data.pointer = (void *)&wpa_key; - priv_req.u.data.length = sizeof(wpa_key); - - if (iw_set_ext(drv, WPA_SET_KEY, &priv_req) < 0) - ret = -1; - - if (alg == WPA_ALG_NONE) { - /* - * ndiswrapper did not seem to be clearing keys properly in - * some cases with WPA_SET_KEY. For example, roaming from WPA - * enabled AP to plaintext one seemed to fail since the driver - * did not associate. Try to make sure the keys are cleared so - * that plaintext APs can be used in all cases. - */ - wpa_driver_wext_set_key(ifname, drv->wext, alg, addr, key_idx, - set_tx, seq, seq_len, key, key_len); - } - - return ret; -} - -static int wpa_ndiswrapper_set_countermeasures(void *priv, int enabled) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - int ret = 0; - struct iwreq priv_req; - - os_memset(&priv_req, 0, sizeof(priv_req)); - - priv_req.u.param.value = enabled; - if (iw_set_ext(drv, WPA_SET_COUNTERMEASURES, &priv_req) < 0) - ret = -1; - - return ret; -} - -static int wpa_ndiswrapper_set_drop_unencrypted(void *priv, - int enabled) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - int ret = 0; - struct iwreq priv_req; - - os_memset(&priv_req, 0, sizeof(priv_req)); - - priv_req.u.param.value = enabled; - if (iw_set_ext(drv, WPA_DROP_UNENCRYPTED, &priv_req) < 0) - ret = -1; - return ret; -} - -static int wpa_ndiswrapper_deauthenticate(void *priv, const u8 *addr, - int reason_code) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - int ret = 0; - struct iwreq priv_req; - - os_memset(&priv_req, 0, sizeof(priv_req)); - - priv_req.u.param.value = reason_code; - os_memcpy(&priv_req.u.ap_addr.sa_data, addr, ETH_ALEN); - if (iw_set_ext(drv, WPA_DEAUTHENTICATE, &priv_req) < 0) - ret = -1; - return ret; -} - -static int wpa_ndiswrapper_disassociate(void *priv, const u8 *addr, - int reason_code) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - int ret = 0; - struct iwreq priv_req; - - os_memset(&priv_req, 0, sizeof(priv_req)); - - os_memcpy(&priv_req.u.ap_addr.sa_data, addr, ETH_ALEN); - if (iw_set_ext(drv, WPA_DISASSOCIATE, &priv_req) < 0) - ret = -1; - return ret; -} - -static int -wpa_ndiswrapper_associate(void *priv, - struct wpa_driver_associate_params *params) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - int ret = 0; - struct wpa_assoc_info wpa_assoc_info; - struct iwreq priv_req; - - if (wpa_ndiswrapper_set_drop_unencrypted(drv, - params->drop_unencrypted) < 0) - ret = -1; - if (wpa_ndiswrapper_set_auth_alg(drv, params->auth_alg) < 0) - ret = -1; - - os_memset(&priv_req, 0, sizeof(priv_req)); - os_memset(&wpa_assoc_info, 0, sizeof(wpa_assoc_info)); - - wpa_assoc_info.bssid = params->bssid; - wpa_assoc_info.ssid = params->ssid; - wpa_assoc_info.ssid_len = params->ssid_len; - wpa_assoc_info.freq = params->freq; - wpa_assoc_info.wpa_ie = params->wpa_ie; - wpa_assoc_info.wpa_ie_len = params->wpa_ie_len; - wpa_assoc_info.pairwise_suite = params->pairwise_suite; - wpa_assoc_info.group_suite = params->group_suite; - wpa_assoc_info.key_mgmt_suite = params->key_mgmt_suite; - wpa_assoc_info.auth_alg = params->auth_alg; - wpa_assoc_info.mode = params->mode; - - priv_req.u.data.pointer = (void *)&wpa_assoc_info; - priv_req.u.data.length = sizeof(wpa_assoc_info); - - if (iw_set_ext(drv, WPA_ASSOCIATE, &priv_req) < 0) - ret = -1; - return ret; -} - -static int wpa_ndiswrapper_set_auth_alg(void *priv, int auth_alg) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - int ret = 0; - struct iwreq priv_req; - - os_memset(&priv_req, 0, sizeof(priv_req)); - - priv_req.u.param.value = auth_alg; - if (iw_set_ext(drv, WPA_SET_AUTH_ALG, &priv_req) < 0) - ret = -1; - return ret; -} - -static int wpa_ndiswrapper_get_bssid(void *priv, u8 *bssid) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - return wpa_driver_wext_get_bssid(drv->wext, bssid); -} - - -static int wpa_ndiswrapper_get_ssid(void *priv, u8 *ssid) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - return wpa_driver_wext_get_ssid(drv->wext, ssid); -} - - -static int wpa_ndiswrapper_scan(void *priv, - struct wpa_driver_scan_params *params) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - return wpa_driver_wext_scan(drv->wext, params); -} - - -static struct wpa_scan_results * wpa_ndiswrapper_get_scan_results(void *priv) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - return wpa_driver_wext_get_scan_results(drv->wext); -} - - -static int wpa_ndiswrapper_get_capa(void *priv, struct wpa_driver_capa *capa) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - int ret = 0; - struct iwreq priv_req; - - os_memset(&priv_req, 0, sizeof(priv_req)); - - priv_req.u.data.pointer = (void *) capa; - priv_req.u.data.length = sizeof(*capa); - if (iw_set_ext(drv, WPA_GET_CAPA, &priv_req) < 0) - ret = -1; - return ret; - -} - - -static int wpa_ndiswrapper_set_operstate(void *priv, int state) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - return wpa_driver_wext_set_operstate(drv->wext, state); -} - - -static void * wpa_ndiswrapper_init(void *ctx, const char *ifname) -{ - struct wpa_driver_ndiswrapper_data *drv; - - drv = os_zalloc(sizeof(*drv)); - if (drv == NULL) - return NULL; - drv->wext = wpa_driver_wext_init(ctx, ifname); - if (drv->wext == NULL) { - os_free(drv); - return NULL; - } - - drv->ctx = ctx; - os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); - drv->sock = get_socket(); - if (drv->sock < 0) { - wpa_driver_wext_deinit(drv->wext); - os_free(drv); - return NULL; - } - - wpa_ndiswrapper_set_wpa(drv, 1); - - return drv; -} - - -static void wpa_ndiswrapper_deinit(void *priv) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - wpa_ndiswrapper_set_wpa(drv, 0); - wpa_driver_wext_deinit(drv->wext); - close(drv->sock); - os_free(drv); -} - - -const struct wpa_driver_ops wpa_driver_ndiswrapper_ops = { - .name = "ndiswrapper", - .desc = "Linux ndiswrapper (deprecated; use wext)", - .set_key = wpa_ndiswrapper_set_key, - .set_countermeasures = wpa_ndiswrapper_set_countermeasures, - .deauthenticate = wpa_ndiswrapper_deauthenticate, - .disassociate = wpa_ndiswrapper_disassociate, - .associate = wpa_ndiswrapper_associate, - - .get_bssid = wpa_ndiswrapper_get_bssid, - .get_ssid = wpa_ndiswrapper_get_ssid, - .scan2 = wpa_ndiswrapper_scan, - .get_scan_results2 = wpa_ndiswrapper_get_scan_results, - .init = wpa_ndiswrapper_init, - .deinit = wpa_ndiswrapper_deinit, - .get_capa = wpa_ndiswrapper_get_capa, - .set_operstate = wpa_ndiswrapper_set_operstate, -}; diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 364158c7d8e25..37b6be9951f4c 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -1,48 +1,151 @@ /* * Driver interaction with Linux nl80211/cfg80211 - * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> * Copyright (c) 2003-2004, Instant802 Networks, Inc. * Copyright (c) 2005-2006, Devicescape Software, Inc. * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net> * Copyright (c) 2009-2010, Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> #include <net/if.h> #include <netlink/genl/genl.h> #include <netlink/genl/family.h> #include <netlink/genl/ctrl.h> +#include <linux/rtnetlink.h> #include <netpacket/packet.h> #include <linux/filter.h> +#include <linux/errqueue.h> #include "nl80211_copy.h" #include "common.h" #include "eloop.h" +#include "utils/list.h" #include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "l2_packet/l2_packet.h" #include "netlink.h" #include "linux_ioctl.h" #include "radiotap.h" #include "radiotap_iter.h" +#include "rfkill.h" #include "driver.h" +#ifndef SO_WIFI_STATUS +# if defined(__sparc__) +# define SO_WIFI_STATUS 0x0025 +# elif defined(__parisc__) +# define SO_WIFI_STATUS 0x4022 +# else +# define SO_WIFI_STATUS 41 +# endif + +# define SCM_WIFI_STATUS SO_WIFI_STATUS +#endif + +#ifndef SO_EE_ORIGIN_TXSTATUS +#define SO_EE_ORIGIN_TXSTATUS 4 +#endif + +#ifndef PACKET_TX_TIMESTAMP +#define PACKET_TX_TIMESTAMP 16 +#endif + +#ifdef ANDROID +#include "android_drv.h" + +/* system/core/libnl_2 in AOSP does not include nla_put_u32() */ +int nla_put_u32(struct nl_msg *msg, int attrtype, uint32_t value) +{ + return nla_put(msg, attrtype, sizeof(uint32_t), &value); +} +#endif /* ANDROID */ #ifdef CONFIG_LIBNL20 /* libnl 2.0 compatibility code */ #define nl_handle nl_sock -#define nl_handle_alloc_cb nl_socket_alloc_cb -#define nl_handle_destroy nl_socket_free +#define nl80211_handle_alloc nl_socket_alloc_cb +#define nl80211_handle_destroy nl_socket_free +#else +/* + * libnl 1.1 has a bug, it tries to allocate socket numbers densely + * but when you free a socket again it will mess up its bitmap and + * and use the wrong number the next time it needs a socket ID. + * Therefore, we wrap the handle alloc/destroy and add our own pid + * accounting. + */ +static uint32_t port_bitmap[32] = { 0 }; + +static struct nl_handle *nl80211_handle_alloc(void *cb) +{ + struct nl_handle *handle; + uint32_t pid = getpid() & 0x3FFFFF; + int i; + + handle = nl_handle_alloc_cb(cb); + + for (i = 0; i < 1024; i++) { + if (port_bitmap[i / 32] & (1 << (i % 32))) + continue; + port_bitmap[i / 32] |= 1 << (i % 32); + pid += i << 22; + break; + } + + nl_socket_set_local_port(handle, pid); + + return handle; +} + +static void nl80211_handle_destroy(struct nl_handle *handle) +{ + uint32_t port = nl_socket_get_local_port(handle); + + port >>= 22; + port_bitmap[port / 32] &= ~(1 << (port % 32)); + + nl_handle_destroy(handle); +} #endif /* CONFIG_LIBNL20 */ +static struct nl_handle * nl_create_handle(struct nl_cb *cb, const char *dbg) +{ + struct nl_handle *handle; + + handle = nl80211_handle_alloc(cb); + if (handle == NULL) { + wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " + "callbacks (%s)", dbg); + return NULL; + } + + if (genl_connect(handle)) { + wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic " + "netlink (%s)", dbg); + nl80211_handle_destroy(handle); + return NULL; + } + + return handle; +} + + +static void nl_destroy_handles(struct nl_handle **handle) +{ + if (*handle == NULL) + return; + nl80211_handle_destroy(*handle); + *handle = NULL; +} + + #ifndef IFF_LOWER_UP #define IFF_LOWER_UP 0x10000 /* driver signals L1 up */ #endif @@ -57,21 +160,66 @@ #define IF_OPER_UP 6 #endif +struct nl80211_global { + struct dl_list interfaces; + int if_add_ifindex; + struct netlink_data *netlink; + struct nl_cb *nl_cb; + struct nl_handle *nl; + int nl80211_id; + int ioctl_sock; /* socket for ioctl() use */ + + struct nl_handle *nl_event; +}; + +struct nl80211_wiphy_data { + struct dl_list list; + struct dl_list bsss; + struct dl_list drvs; + + struct nl_handle *nl_beacons; + struct nl_cb *nl_cb; + + int wiphy_idx; +}; + +static void nl80211_global_deinit(void *priv); +static void wpa_driver_nl80211_deinit(void *priv); + struct i802_bss { struct wpa_driver_nl80211_data *drv; struct i802_bss *next; int ifindex; char ifname[IFNAMSIZ + 1]; + char brname[IFNAMSIZ]; unsigned int beacon_set:1; + unsigned int added_if_into_bridge:1; + unsigned int added_bridge:1; + unsigned int in_deinit:1; + + u8 addr[ETH_ALEN]; + + int freq; + + void *ctx; + struct nl_handle *nl_preq, *nl_mgmt; + struct nl_cb *nl_cb; + + struct nl80211_wiphy_data *wiphy_data; + struct dl_list wiphy_list; }; struct wpa_driver_nl80211_data { + struct nl80211_global *global; + struct dl_list list; + struct dl_list wiphy_list; + char phyname[32]; void *ctx; - struct netlink_data *netlink; - int ioctl_sock; /* socket for ioctl() use */ - char brname[IFNAMSIZ]; int ifindex; int if_removed; + int if_disabled; + int ignore_if_down_event; + struct rfkill_data *rfkill; struct wpa_driver_capa capa; int has_capability; @@ -79,39 +227,44 @@ struct wpa_driver_nl80211_data { int scan_complete_events; - struct nl_handle *nl_handle; - struct nl_handle *nl_handle_event; - struct nl_cache *nl_cache; - struct nl_cache *nl_cache_event; struct nl_cb *nl_cb; - struct genl_family *nl80211; u8 auth_bssid[ETH_ALEN]; u8 bssid[ETH_ALEN]; int associated; u8 ssid[32]; size_t ssid_len; - int nlmode; - int ap_scan_as_station; + enum nl80211_iftype nlmode; + enum nl80211_iftype ap_scan_as_station; unsigned int assoc_freq; int monitor_sock; int monitor_ifidx; - int probe_req_report; - int disable_11b_rates; + int monitor_refcount; + unsigned int disabled_11b_rates:1; unsigned int pending_remain_on_chan:1; - unsigned int added_bridge:1; - unsigned int added_if_into_bridge:1; + unsigned int in_interface_list:1; + unsigned int device_ap_sme:1; + unsigned int poll_command_supported:1; + unsigned int data_tx_status:1; + unsigned int scan_for_auth:1; + unsigned int retry_auth:1; + unsigned int use_monitor:1; + unsigned int ignore_next_local_disconnect:1; u64 remain_on_chan_cookie; u64 send_action_cookie; + unsigned int last_mgmt_freq; + struct wpa_driver_scan_filter *filter_ssids; size_t num_filter_ssids; struct i802_bss first_bss; + int eapol_tx_sock; + #ifdef HOSTAPD int eapol_sock; /* socket for EAPOL frames */ @@ -122,12 +275,27 @@ struct wpa_driver_nl80211_data { int last_freq; int last_freq_ht; #endif /* HOSTAPD */ + + /* From failed authentication command */ + int auth_freq; + u8 auth_bssid_[ETH_ALEN]; + u8 auth_ssid[32]; + size_t auth_ssid_len; + int auth_alg; + u8 *auth_ie; + size_t auth_ie_len; + u8 auth_wep_key[4][16]; + size_t auth_wep_key_len[4]; + int auth_wep_tx_keyidx; + int auth_local_state_change; + int auth_p2p; }; static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx); -static int wpa_driver_nl80211_set_mode(void *priv, int mode); +static int wpa_driver_nl80211_set_mode(struct i802_bss *bss, + enum nl80211_iftype nlmode); static int wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv); static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, @@ -135,6 +303,16 @@ static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, int local_state_change); static void nl80211_remove_monitor_interface( struct wpa_driver_nl80211_data *drv); +static int nl80211_send_frame_cmd(struct i802_bss *bss, + unsigned int freq, unsigned int wait, + const u8 *buf, size_t buf_len, u64 *cookie, + int no_cck, int no_ack, int offchanok); +static int wpa_driver_nl80211_probe_req_report(void *priv, int report); +#ifdef ANDROID +static int android_pno_start(struct i802_bss *bss, + struct wpa_driver_scan_params *params); +static int android_pno_stop(struct i802_bss *bss); +#endif /* ANDROID */ #ifdef HOSTAPD static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx); @@ -144,18 +322,59 @@ static int wpa_driver_nl80211_if_remove(void *priv, enum wpa_driver_if_type type, const char *ifname); #else /* HOSTAPD */ -static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +static inline void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +{ +} + +static inline void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +{ +} + +static inline int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) { return 0; } #endif /* HOSTAPD */ static int i802_set_freq(void *priv, struct hostapd_freq_params *freq); -static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx, - void *timeout_ctx); static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv, int ifindex, int disabled); +static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv); +static int wpa_driver_nl80211_authenticate_retry( + struct wpa_driver_nl80211_data *drv); + + +static int is_ap_interface(enum nl80211_iftype nlmode) +{ + return (nlmode == NL80211_IFTYPE_AP || + nlmode == NL80211_IFTYPE_P2P_GO); +} + + +static int is_sta_interface(enum nl80211_iftype nlmode) +{ + return (nlmode == NL80211_IFTYPE_STATION || + nlmode == NL80211_IFTYPE_P2P_CLIENT); +} + + +static int is_p2p_interface(enum nl80211_iftype nlmode) +{ + return (nlmode == NL80211_IFTYPE_P2P_CLIENT || + nlmode == NL80211_IFTYPE_P2P_GO); +} + + +struct nl80211_bss_info_arg { + struct wpa_driver_nl80211_data *drv; + struct wpa_scan_results *res; + unsigned int assoc_freq; + u8 assoc_bssid[ETH_ALEN]; +}; + +static int bss_info_handler(struct nl_msg *msg, void *arg); + /* nl80211 code */ static int ack_handler(struct nl_msg *msg, void *arg) @@ -187,7 +406,7 @@ static int no_seq_check(struct nl_msg *msg, void *arg) } -static int send_and_recv(struct wpa_driver_nl80211_data *drv, +static int send_and_recv(struct nl80211_global *global, struct nl_handle *nl_handle, struct nl_msg *msg, int (*valid_handler)(struct nl_msg *, void *), void *valid_data) @@ -195,7 +414,7 @@ static int send_and_recv(struct wpa_driver_nl80211_data *drv, struct nl_cb *cb; int err = -ENOMEM; - cb = nl_cb_clone(drv->nl_cb); + cb = nl_cb_clone(global->nl_cb); if (!cb) goto out; @@ -222,13 +441,23 @@ static int send_and_recv(struct wpa_driver_nl80211_data *drv, } +static int send_and_recv_msgs_global(struct nl80211_global *global, + struct nl_msg *msg, + int (*valid_handler)(struct nl_msg *, void *), + void *valid_data) +{ + return send_and_recv(global, global->nl, msg, valid_handler, + valid_data); +} + + static int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, struct nl_msg *msg, int (*valid_handler)(struct nl_msg *, void *), void *valid_data) { - return send_and_recv(drv, drv->nl_handle, msg, valid_handler, - valid_data); + return send_and_recv(drv->global, drv->global->nl, msg, + valid_handler, valid_data); } @@ -269,7 +498,7 @@ static int family_handler(struct nl_msg *msg, void *arg) } -static int nl_get_multicast_id(struct wpa_driver_nl80211_data *drv, +static int nl_get_multicast_id(struct nl80211_global *global, const char *family, const char *group) { struct nl_msg *msg; @@ -279,11 +508,11 @@ static int nl_get_multicast_id(struct wpa_driver_nl80211_data *drv, msg = nlmsg_alloc(); if (!msg) return -ENOMEM; - genlmsg_put(msg, 0, 0, genl_ctrl_resolve(drv->nl_handle, "nlctrl"), + genlmsg_put(msg, 0, 0, genl_ctrl_resolve(global->nl, "nlctrl"), 0, 0, CTRL_CMD_GETFAMILY, 0); NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family); - ret = send_and_recv_msgs(drv, msg, family_handler, &res); + ret = send_and_recv_msgs_global(global, msg, family_handler, &res); msg = NULL; if (ret == 0) ret = res.id; @@ -294,6 +523,235 @@ nla_put_failure: } +static void * nl80211_cmd(struct wpa_driver_nl80211_data *drv, + struct nl_msg *msg, int flags, uint8_t cmd) +{ + return genlmsg_put(msg, 0, 0, drv->global->nl80211_id, + 0, flags, cmd, 0); +} + + +struct wiphy_idx_data { + int wiphy_idx; +}; + + +static int netdev_info_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct wiphy_idx_data *info = arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb[NL80211_ATTR_WIPHY]) + info->wiphy_idx = nla_get_u32(tb[NL80211_ATTR_WIPHY]); + + return NL_SKIP; +} + + +static int nl80211_get_wiphy_index(struct i802_bss *bss) +{ + struct nl_msg *msg; + struct wiphy_idx_data data = { + .wiphy_idx = -1, + }; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_GET_INTERFACE); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); + + if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data) == 0) + return data.wiphy_idx; + msg = NULL; +nla_put_failure: + nlmsg_free(msg); + return -1; +} + + +static int nl80211_register_beacons(struct wpa_driver_nl80211_data *drv, + struct nl80211_wiphy_data *w) +{ + struct nl_msg *msg; + int ret = -1; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_BEACONS); + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, w->wiphy_idx); + + ret = send_and_recv(drv->global, w->nl_beacons, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Register beacons command " + "failed: ret=%d (%s)", + ret, strerror(-ret)); + goto nla_put_failure; + } + ret = 0; +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +static void nl80211_recv_beacons(int sock, void *eloop_ctx, void *handle) +{ + struct nl80211_wiphy_data *w = eloop_ctx; + + wpa_printf(MSG_EXCESSIVE, "nl80211: Beacon event message available"); + + nl_recvmsgs(handle, w->nl_cb); +} + + +static int process_beacon_event(struct nl_msg *msg, void *arg) +{ + struct nl80211_wiphy_data *w = arg; + struct wpa_driver_nl80211_data *drv; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + union wpa_event_data event; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (gnlh->cmd != NL80211_CMD_FRAME) { + wpa_printf(MSG_DEBUG, "nl80211: Unexpected beacon event? (%d)", + gnlh->cmd); + return NL_SKIP; + } + + if (!tb[NL80211_ATTR_FRAME]) + return NL_SKIP; + + dl_list_for_each(drv, &w->drvs, struct wpa_driver_nl80211_data, + wiphy_list) { + os_memset(&event, 0, sizeof(event)); + event.rx_mgmt.frame = nla_data(tb[NL80211_ATTR_FRAME]); + event.rx_mgmt.frame_len = nla_len(tb[NL80211_ATTR_FRAME]); + wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event); + } + + return NL_SKIP; +} + + +static struct nl80211_wiphy_data * +nl80211_get_wiphy_data_ap(struct i802_bss *bss) +{ + static DEFINE_DL_LIST(nl80211_wiphys); + struct nl80211_wiphy_data *w; + int wiphy_idx, found = 0; + struct i802_bss *tmp_bss; + + if (bss->wiphy_data != NULL) + return bss->wiphy_data; + + wiphy_idx = nl80211_get_wiphy_index(bss); + + dl_list_for_each(w, &nl80211_wiphys, struct nl80211_wiphy_data, list) { + if (w->wiphy_idx == wiphy_idx) + goto add; + } + + /* alloc new one */ + w = os_zalloc(sizeof(*w)); + if (w == NULL) + return NULL; + w->wiphy_idx = wiphy_idx; + dl_list_init(&w->bsss); + dl_list_init(&w->drvs); + + w->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!w->nl_cb) { + os_free(w); + return NULL; + } + nl_cb_set(w->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); + nl_cb_set(w->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, process_beacon_event, + w); + + w->nl_beacons = nl_create_handle(bss->drv->global->nl_cb, + "wiphy beacons"); + if (w->nl_beacons == NULL) { + os_free(w); + return NULL; + } + + if (nl80211_register_beacons(bss->drv, w)) { + nl_destroy_handles(&w->nl_beacons); + os_free(w); + return NULL; + } + + eloop_register_read_sock(nl_socket_get_fd(w->nl_beacons), + nl80211_recv_beacons, w, w->nl_beacons); + + dl_list_add(&nl80211_wiphys, &w->list); + +add: + /* drv entry for this bss already there? */ + dl_list_for_each(tmp_bss, &w->bsss, struct i802_bss, wiphy_list) { + if (tmp_bss->drv == bss->drv) { + found = 1; + break; + } + } + /* if not add it */ + if (!found) + dl_list_add(&w->drvs, &bss->drv->wiphy_list); + + dl_list_add(&w->bsss, &bss->wiphy_list); + bss->wiphy_data = w; + return w; +} + + +static void nl80211_put_wiphy_data_ap(struct i802_bss *bss) +{ + struct nl80211_wiphy_data *w = bss->wiphy_data; + struct i802_bss *tmp_bss; + int found = 0; + + if (w == NULL) + return; + bss->wiphy_data = NULL; + dl_list_del(&bss->wiphy_list); + + /* still any for this drv present? */ + dl_list_for_each(tmp_bss, &w->bsss, struct i802_bss, wiphy_list) { + if (tmp_bss->drv == bss->drv) { + found = 1; + break; + } + } + /* if not remove it */ + if (!found) + dl_list_del(&bss->drv->wiphy_list); + + if (!dl_list_empty(&w->bsss)) + return; + + eloop_unregister_read_sock(nl_socket_get_fd(w->nl_beacons)); + + nl_cb_put(w->nl_cb); + nl_destroy_handles(&w->nl_beacons); + dl_list_del(&w->list); + os_free(w); +} + + static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid) { struct i802_bss *bss = priv; @@ -334,10 +792,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); @@ -387,17 +863,33 @@ static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv, } +static struct wpa_driver_nl80211_data * +nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len) +{ + struct wpa_driver_nl80211_data *drv; + dl_list_for_each(drv, &global->interfaces, + struct wpa_driver_nl80211_data, list) { + if (wpa_driver_nl80211_own_ifindex(drv, idx, buf, len) || + have_ifidx(drv, idx)) + return drv; + } + return NULL; +} + + static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi, u8 *buf, size_t len) { - struct wpa_driver_nl80211_data *drv = ctx; + struct nl80211_global *global = ctx; + struct wpa_driver_nl80211_data *drv; int attrlen, rta_len; struct rtattr *attr; u32 brid = 0; + char namebuf[IFNAMSIZ]; - if (!wpa_driver_nl80211_own_ifindex(drv, ifi->ifi_index, buf, len) && - !have_ifidx(drv, ifi->ifi_index)) { + drv = nl80211_find_drv(global, ifi->ifi_index, buf, len); + if (!drv) { wpa_printf(MSG_DEBUG, "nl80211: Ignore event for foreign " "ifindex %d", ifi->ifi_index); return; @@ -410,6 +902,50 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); + + if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) { + if (if_indextoname(ifi->ifi_index, namebuf) && + linux_iface_up(drv->global->ioctl_sock, + drv->first_bss.ifname) > 0) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down " + "event since interface %s is up", namebuf); + return; + } + wpa_printf(MSG_DEBUG, "nl80211: Interface down"); + if (drv->ignore_if_down_event) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down " + "event generated by mode change"); + drv->ignore_if_down_event = 0; + } else { + drv->if_disabled = 1; + wpa_supplicant_event(drv->ctx, + EVENT_INTERFACE_DISABLED, NULL); + } + } + + if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) { + if (if_indextoname(ifi->ifi_index, namebuf) && + linux_iface_up(drv->global->ioctl_sock, + drv->first_bss.ifname) == 0) { + 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; + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, + NULL); + } + } + /* * Some drivers send the association event before the operup event--in * this case, lifting operstate in wpa_driver_nl80211_set_operstate() @@ -419,7 +955,7 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, if (drv->operstate == 1 && (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP && !(ifi->ifi_flags & IFF_RUNNING)) - netlink_send_oper_ifla(drv->netlink, drv->ifindex, + netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, -1, IF_OPER_UP); attrlen = len; @@ -436,16 +972,13 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, attr = RTA_NEXT(attr, attrlen); } -#ifdef HOSTAPD if (ifi->ifi_family == AF_BRIDGE && brid) { /* device has been added to bridge */ - char namebuf[IFNAMSIZ]; if_indextoname(brid, namebuf); wpa_printf(MSG_DEBUG, "nl80211: Add ifindex %u for bridge %s", brid, namebuf); add_ifidx(drv, brid); } -#endif /* HOSTAPD */ } @@ -453,11 +986,19 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, struct ifinfomsg *ifi, u8 *buf, size_t len) { - struct wpa_driver_nl80211_data *drv = ctx; + struct nl80211_global *global = ctx; + struct wpa_driver_nl80211_data *drv; int attrlen, rta_len; struct rtattr *attr; u32 brid = 0; + drv = nl80211_find_drv(global, ifi->ifi_index, buf, len); + if (!drv) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore dellink event for " + "foreign ifindex %d", ifi->ifi_index); + return; + } + attrlen = len; attr = (struct rtattr *) buf; @@ -473,7 +1014,6 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, attr = RTA_NEXT(attr, attrlen); } -#ifdef HOSTAPD if (ifi->ifi_family == AF_BRIDGE && brid) { /* device has been removed from bridge */ char namebuf[IFNAMSIZ]; @@ -482,7 +1022,6 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, "%s", brid, namebuf); del_ifidx(drv, brid); } -#endif /* HOSTAPD */ } @@ -492,6 +1031,7 @@ static void mlme_event_auth(struct wpa_driver_nl80211_data *drv, const struct ieee80211_mgmt *mgmt; union wpa_event_data event; + wpa_printf(MSG_DEBUG, "nl80211: Authenticate event"); mgmt = (const struct ieee80211_mgmt *) frame; if (len < 24 + sizeof(mgmt->u.auth)) { wpa_printf(MSG_DEBUG, "nl80211: Too short association event " @@ -503,6 +1043,8 @@ static void mlme_event_auth(struct wpa_driver_nl80211_data *drv, os_memset(&event, 0, sizeof(event)); os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN); event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg); + event.auth.auth_transaction = + le_to_host16(mgmt->u.auth.auth_transaction); event.auth.status_code = le_to_host16(mgmt->u.auth.status_code); if (len > 24 + sizeof(mgmt->u.auth)) { event.auth.ies = mgmt->u.auth.variable; @@ -513,6 +1055,37 @@ static void mlme_event_auth(struct wpa_driver_nl80211_data *drv, } +static unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv) +{ + struct nl_msg *msg; + int ret; + struct nl80211_bss_info_arg arg; + + os_memset(&arg, 0, sizeof(arg)); + msg = nlmsg_alloc(); + if (!msg) + goto nla_put_failure; + + nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + arg.drv = drv; + ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg); + msg = NULL; + if (ret == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Operating frequency for the " + "associated BSS from scan results: %u MHz", + arg.assoc_freq); + return arg.assoc_freq ? arg.assoc_freq : drv->assoc_freq; + } + wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d " + "(%s)", ret, strerror(-ret)); +nla_put_failure: + nlmsg_free(msg); + return drv->assoc_freq; +} + + static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, const u8 *frame, size_t len) { @@ -520,6 +1093,7 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, union wpa_event_data event; u16 status; + wpa_printf(MSG_DEBUG, "nl80211: Associate event"); mgmt = (const struct ieee80211_mgmt *) frame; if (len < 24 + sizeof(mgmt->u.assoc_resp)) { wpa_printf(MSG_DEBUG, "nl80211: Too short association event " @@ -530,6 +1104,7 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, status = le_to_host16(mgmt->u.assoc_resp.status_code); if (status != WLAN_STATUS_SUCCESS) { os_memset(&event, 0, sizeof(event)); + event.assoc_reject.bssid = mgmt->bssid; if (len > 24 + sizeof(mgmt->u.assoc_resp)) { event.assoc_reject.resp_ies = (u8 *) mgmt->u.assoc_resp.variable; @@ -575,9 +1150,16 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, return; } + if (cmd == NL80211_CMD_CONNECT) + wpa_printf(MSG_DEBUG, "nl80211: Connect event"); + else if (cmd == NL80211_CMD_ROAM) + wpa_printf(MSG_DEBUG, "nl80211: Roam event"); + os_memset(&event, 0, sizeof(event)); if (cmd == NL80211_CMD_CONNECT && nla_get_u16(status) != WLAN_STATUS_SUCCESS) { + if (addr) + event.assoc_reject.bssid = nla_data(addr); if (resp_ie) { event.assoc_reject.resp_ies = nla_data(resp_ie); event.assoc_reject.resp_ies_len = nla_len(resp_ie); @@ -600,10 +1182,85 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, event.assoc_info.resp_ies_len = nla_len(resp_ie); } + event.assoc_info.freq = nl80211_get_assoc_freq(drv); + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); } +static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv, + struct nlattr *reason, struct nlattr *addr, + struct nlattr *by_ap) +{ + union wpa_event_data data; + unsigned int locally_generated = by_ap == NULL; + + if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { + /* + * Avoid reporting two disassociation events that could + * confuse the core code. + */ + wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect " + "event when using userspace SME"); + return; + } + + if (drv->ignore_next_local_disconnect) { + drv->ignore_next_local_disconnect = 0; + if (locally_generated) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect " + "event triggered during reassociation"); + return; + } + wpa_printf(MSG_WARNING, "nl80211: Was expecting local " + "disconnect but got another disconnect " + "event first"); + } + + wpa_printf(MSG_DEBUG, "nl80211: Disconnect event"); + drv->associated = 0; + os_memset(&data, 0, sizeof(data)); + if (reason) + 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); +} + + static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv, enum nl80211_commands cmd, struct nlattr *addr) { @@ -629,13 +1286,16 @@ static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv, } -static void mlme_event_action(struct wpa_driver_nl80211_data *drv, - struct nlattr *freq, const u8 *frame, size_t len) +static void mlme_event_mgmt(struct wpa_driver_nl80211_data *drv, + 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; + wpa_printf(MSG_DEBUG, "nl80211: Frame event"); mgmt = (const struct ieee80211_mgmt *) frame; if (len < 24) { wpa_printf(MSG_DEBUG, "nl80211: Too short action frame"); @@ -645,38 +1305,55 @@ static void mlme_event_action(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)); - event.rx_action.da = mgmt->da; - event.rx_action.sa = mgmt->sa; - event.rx_action.bssid = mgmt->bssid; - event.rx_action.category = mgmt->u.action.category; - event.rx_action.data = &mgmt->u.action.category + 1; - event.rx_action.len = frame + len - event.rx_action.data; - if (freq) + if (freq) { event.rx_action.freq = nla_get_u32(freq); - wpa_supplicant_event(drv->ctx, EVENT_RX_ACTION, &event); + drv->last_mgmt_freq = event.rx_action.freq; + } + if (stype == WLAN_FC_STYPE_ACTION) { + event.rx_action.da = mgmt->da; + event.rx_action.sa = mgmt->sa; + event.rx_action.bssid = mgmt->bssid; + event.rx_action.category = mgmt->u.action.category; + event.rx_action.data = &mgmt->u.action.category + 1; + event.rx_action.len = frame + len - event.rx_action.data; + wpa_supplicant_event(drv->ctx, EVENT_RX_ACTION, &event); + } else { + event.rx_mgmt.frame = frame; + event.rx_mgmt.frame_len = len; + event.rx_mgmt.ssi_signal = ssi_signal; + wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event); + } } -static void mlme_event_action_tx_status(struct wpa_driver_nl80211_data *drv, - struct nlattr *cookie, const u8 *frame, - size_t len, struct nlattr *ack) +static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv, + struct nlattr *cookie, const u8 *frame, + size_t len, struct nlattr *ack) { union wpa_event_data event; const struct ieee80211_hdr *hdr; u16 fc; - u64 cookie_val; - if (!cookie) - return; + wpa_printf(MSG_DEBUG, "nl80211: Frame TX status event"); + if (!is_ap_interface(drv->nlmode)) { + u64 cookie_val; - cookie_val = nla_get_u64(cookie); - wpa_printf(MSG_DEBUG, "nl80211: Action TX status: cookie=0%llx%s", - (long long unsigned int) cookie_val, - cookie_val == drv->send_action_cookie ? - " (match)" : " (unknown)"); - if (cookie_val != drv->send_action_cookie) - return; + if (!cookie) + return; + + cookie_val = nla_get_u64(cookie); + wpa_printf(MSG_DEBUG, "nl80211: Action TX status:" + " cookie=0%llx%s (ack=%d)", + (long long unsigned int) cookie_val, + cookie_val == drv->send_action_cookie ? + " (match)" : " (unknown)", ack != NULL); + if (cookie_val != drv->send_action_cookie) + return; + } hdr = (const struct ieee80211_hdr *) frame; fc = le_to_host16(hdr->frame_control); @@ -701,6 +1378,11 @@ static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv, const u8 *bssid = NULL; u16 reason_code = 0; + if (type == EVENT_DEAUTH) + wpa_printf(MSG_DEBUG, "nl80211: Deauthenticate event"); + else + wpa_printf(MSG_DEBUG, "nl80211: Disassociate event"); + mgmt = (const struct ieee80211_mgmt *) frame; if (len >= 24) { bssid = mgmt->bssid; @@ -728,11 +1410,62 @@ static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv, reason_code = le_to_host16(mgmt->u.deauth.reason_code); if (type == EVENT_DISASSOC) { + event.disassoc_info.locally_generated = + !os_memcmp(mgmt->sa, drv->first_bss.addr, ETH_ALEN); event.disassoc_info.addr = bssid; event.disassoc_info.reason_code = reason_code; + if (frame + len > mgmt->u.disassoc.variable) { + event.disassoc_info.ie = mgmt->u.disassoc.variable; + event.disassoc_info.ie_len = frame + len - + mgmt->u.disassoc.variable; + } } else { + event.deauth_info.locally_generated = + !os_memcmp(mgmt->sa, drv->first_bss.addr, ETH_ALEN); event.deauth_info.addr = bssid; event.deauth_info.reason_code = reason_code; + if (frame + len > mgmt->u.deauth.variable) { + event.deauth_info.ie = mgmt->u.deauth.variable; + event.deauth_info.ie_len = frame + len - + mgmt->u.deauth.variable; + } + } + + wpa_supplicant_event(drv->ctx, type, &event); +} + + +static void mlme_event_unprot_disconnect(struct wpa_driver_nl80211_data *drv, + enum wpa_event_type type, + const u8 *frame, size_t len) +{ + const struct ieee80211_mgmt *mgmt; + union wpa_event_data event; + u16 reason_code = 0; + + if (type == EVENT_UNPROT_DEAUTH) + wpa_printf(MSG_DEBUG, "nl80211: Unprot Deauthenticate event"); + else + wpa_printf(MSG_DEBUG, "nl80211: Unprot Disassociate event"); + + if (len < 24) + return; + + mgmt = (const struct ieee80211_mgmt *) frame; + + os_memset(&event, 0, sizeof(event)); + /* Note: Same offset for Reason Code in both frame subtypes */ + if (len >= 24 + sizeof(mgmt->u.deauth)) + reason_code = le_to_host16(mgmt->u.deauth.reason_code); + + if (type == EVENT_UNPROT_DISASSOC) { + event.unprot_disassoc.sa = mgmt->sa; + event.unprot_disassoc.da = mgmt->da; + event.unprot_disassoc.reason_code = reason_code; + } else { + event.unprot_deauth.sa = mgmt->sa; + event.unprot_deauth.da = mgmt->da; + event.unprot_deauth.reason_code = reason_code; } wpa_supplicant_event(drv->ctx, type, &event); @@ -743,7 +1476,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); @@ -775,12 +1508,21 @@ static void mlme_event(struct wpa_driver_nl80211_data *drv, mlme_event_deauth_disassoc(drv, EVENT_DISASSOC, nla_data(frame), nla_len(frame)); break; - case NL80211_CMD_ACTION: - mlme_event_action(drv, freq, nla_data(frame), nla_len(frame)); + case NL80211_CMD_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), + nla_len(frame), ack); break; - case NL80211_CMD_ACTION_TX_STATUS: - mlme_event_action_tx_status(drv, cookie, nla_data(frame), - nla_len(frame), ack); + case NL80211_CMD_UNPROT_DEAUTHENTICATE: + mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DEAUTH, + nla_data(frame), nla_len(frame)); + break; + case NL80211_CMD_UNPROT_DISASSOCIATE: + mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DISASSOC, + nla_data(frame), nla_len(frame)); break; default: break; @@ -788,7 +1530,7 @@ static void mlme_event(struct wpa_driver_nl80211_data *drv, } -static void mlme_event_michael_mic_failure(struct wpa_driver_nl80211_data *drv, +static void mlme_event_michael_mic_failure(struct i802_bss *bss, struct nlattr *tb[]) { union wpa_event_data data; @@ -820,7 +1562,7 @@ static void mlme_event_michael_mic_failure(struct wpa_driver_nl80211_data *drv, wpa_printf(MSG_DEBUG, "nl80211: Key Id %d", key_id); } - wpa_supplicant_event(drv->ctx, EVENT_MICHAEL_MIC_FAILURE, &data); + wpa_supplicant_event(bss->ctx, EVENT_MICHAEL_MIC_FAILURE, &data); } @@ -877,7 +1619,8 @@ static void mlme_event_remain_on_channel(struct wpa_driver_nl80211_data *drv, if (cookie != drv->remain_on_chan_cookie) return; /* not for us */ - drv->pending_remain_on_chan = !cancel_event; + if (cancel_event) + drv->pending_remain_on_chan = 0; os_memset(&data, 0, sizeof(data)); data.remain_on_channel.freq = freq; @@ -899,6 +1642,14 @@ static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, int freqs[MAX_REPORT_FREQS]; int num_freqs = 0; + if (drv->scan_for_auth) { + drv->scan_for_auth = 0; + wpa_printf(MSG_DEBUG, "nl80211: Scan results for missing " + "cfg80211 BSS entry"); + wpa_driver_nl80211_authenticate_retry(drv); + return; + } + os_memset(&event, 0, sizeof(event)); info = &event.scan_info; info->aborted = aborted; @@ -929,6 +1680,219 @@ static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, } +static int get_link_signal(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1]; + static struct nla_policy policy[NL80211_STA_INFO_MAX + 1] = { + [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 }, + }; + struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1]; + static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = { + [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 }, + [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 }, + [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG }, + [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG }, + }; + struct wpa_signal_info *sig_change = arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (!tb[NL80211_ATTR_STA_INFO] || + nla_parse_nested(sinfo, NL80211_STA_INFO_MAX, + tb[NL80211_ATTR_STA_INFO], policy)) + return NL_SKIP; + if (!sinfo[NL80211_STA_INFO_SIGNAL]) + return NL_SKIP; + + sig_change->current_signal = + (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]); + + if (sinfo[NL80211_STA_INFO_TX_BITRATE]) { + if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX, + sinfo[NL80211_STA_INFO_TX_BITRATE], + rate_policy)) { + sig_change->current_txrate = 0; + } else { + if (rinfo[NL80211_RATE_INFO_BITRATE]) { + sig_change->current_txrate = + nla_get_u16(rinfo[ + NL80211_RATE_INFO_BITRATE]) * 100; + } + } + } + + return NL_SKIP; +} + + +static int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv, + struct wpa_signal_info *sig) +{ + struct nl_msg *msg; + + sig->current_signal = -9999; + sig->current_txrate = 0; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_STATION); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid); + + return send_and_recv_msgs(drv, msg, get_link_signal, sig); + nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + + +static int get_link_noise(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1]; + static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = { + [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 }, + [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 }, + }; + struct wpa_signal_info *sig_change = arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[NL80211_ATTR_SURVEY_INFO]) { + wpa_printf(MSG_DEBUG, "nl80211: survey data missing!"); + return NL_SKIP; + } + + if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX, + tb[NL80211_ATTR_SURVEY_INFO], + survey_policy)) { + wpa_printf(MSG_DEBUG, "nl80211: failed to parse nested " + "attributes!"); + return NL_SKIP; + } + + if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY]) + return NL_SKIP; + + if (nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) != + sig_change->frequency) + return NL_SKIP; + + if (!sinfo[NL80211_SURVEY_INFO_NOISE]) + return NL_SKIP; + + sig_change->current_noise = + (s8) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]); + + return NL_SKIP; +} + + +static int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv, + struct wpa_signal_info *sig_change) +{ + struct nl_msg *msg; + + sig_change->current_noise = 9999; + sig_change->frequency = drv->assoc_freq; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + return send_and_recv_msgs(drv, msg, get_link_noise, sig_change); + nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + + +static int get_noise_for_scan_results(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1]; + static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = { + [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 }, + [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 }, + }; + struct wpa_scan_results *scan_results = arg; + struct wpa_scan_res *scan_res; + size_t i; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[NL80211_ATTR_SURVEY_INFO]) { + wpa_printf(MSG_DEBUG, "nl80211: Survey data missing"); + return NL_SKIP; + } + + if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX, + tb[NL80211_ATTR_SURVEY_INFO], + survey_policy)) { + wpa_printf(MSG_DEBUG, "nl80211: Failed to parse nested " + "attributes"); + return NL_SKIP; + } + + if (!sinfo[NL80211_SURVEY_INFO_NOISE]) + return NL_SKIP; + + if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY]) + return NL_SKIP; + + for (i = 0; i < scan_results->num; ++i) { + scan_res = scan_results->res[i]; + if (!scan_res) + continue; + if ((int) nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) != + scan_res->freq) + continue; + if (!(scan_res->flags & WPA_SCAN_NOISE_INVALID)) + continue; + scan_res->noise = (s8) + nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]); + scan_res->flags &= ~WPA_SCAN_NOISE_INVALID; + } + + return NL_SKIP; +} + + +static int nl80211_get_noise_for_scan_results( + struct wpa_driver_nl80211_data *drv, + struct wpa_scan_results *scan_res) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + return send_and_recv_msgs(drv, msg, get_noise_for_scan_results, + scan_res); + nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + + static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, struct nlattr *tb[]) { @@ -936,10 +1900,13 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 }, [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 }, [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_PKT_LOSS_EVENT] = { .type = NLA_U32 }, }; struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1]; enum nl80211_cqm_rssi_threshold_event event; union wpa_event_data ed; + struct wpa_signal_info sig; + int res; if (tb[NL80211_ATTR_CQM] == NULL || nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, tb[NL80211_ATTR_CQM], @@ -948,12 +1915,21 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, return; } + os_memset(&ed, 0, sizeof(ed)); + + if (cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]) { + if (!tb[NL80211_ATTR_MAC]) + return; + os_memcpy(ed.low_ack.addr, nla_data(tb[NL80211_ATTR_MAC]), + ETH_ALEN); + wpa_supplicant_event(drv->ctx, EVENT_STATION_LOW_ACK, &ed); + return; + } + if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL) return; event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]); - os_memset(&ed, 0, sizeof(ed)); - if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) { wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor " "event: RSSI high"); @@ -965,42 +1941,258 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, } else return; + res = nl80211_get_link_signal(drv, &sig); + if (res == 0) { + ed.signal_change.current_signal = sig.current_signal; + ed.signal_change.current_txrate = sig.current_txrate; + wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm txrate: %d", + sig.current_signal, sig.current_txrate); + } + + res = nl80211_get_link_noise(drv, &sig); + if (res == 0) { + ed.signal_change.current_noise = sig.current_noise; + wpa_printf(MSG_DEBUG, "nl80211: Noise: %d dBm", + sig.current_noise); + } + wpa_supplicant_event(drv->ctx, EVENT_SIGNAL_CHANGE, &ed); } -static int process_event(struct nl_msg *msg, void *arg) +static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) { - struct wpa_driver_nl80211_data *drv = arg; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - struct nlattr *tb[NL80211_ATTR_MAX + 1]; + u8 *addr; union wpa_event_data data; - nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); - - if (tb[NL80211_ATTR_IFINDEX]) { - int ifindex = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); - if (ifindex != drv->ifindex && !have_ifidx(drv, ifindex)) { - wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d)" - " for foreign interface (ifindex %d)", - gnlh->cmd, ifindex); - return NL_SKIP; + if (tb[NL80211_ATTR_MAC] == NULL) + return; + addr = nla_data(tb[NL80211_ATTR_MAC]); + wpa_printf(MSG_DEBUG, "nl80211: New station " MACSTR, MAC2STR(addr)); + + if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) { + u8 *ies = NULL; + size_t ies_len = 0; + if (tb[NL80211_ATTR_IE]) { + ies = nla_data(tb[NL80211_ATTR_IE]); + ies_len = nla_len(tb[NL80211_ATTR_IE]); } + wpa_hexdump(MSG_DEBUG, "nl80211: Assoc Req IEs", ies, ies_len); + drv_event_assoc(drv->ctx, addr, ies, ies_len, 0); + return; } - if (drv->ap_scan_as_station && - (gnlh->cmd == NL80211_CMD_NEW_SCAN_RESULTS || - gnlh->cmd == NL80211_CMD_SCAN_ABORTED)) { + if (drv->nlmode != NL80211_IFTYPE_ADHOC) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.ibss_rsn_start.peer, addr, ETH_ALEN); + wpa_supplicant_event(drv->ctx, EVENT_IBSS_RSN_START, &data); +} + + +static void nl80211_del_station_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + u8 *addr; + union wpa_event_data data; + + if (tb[NL80211_ATTR_MAC] == NULL) + return; + addr = nla_data(tb[NL80211_ATTR_MAC]); + wpa_printf(MSG_DEBUG, "nl80211: Delete station " MACSTR, + MAC2STR(addr)); + + if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) { + drv_event_disassoc(drv->ctx, addr); + return; + } + + if (drv->nlmode != NL80211_IFTYPE_ADHOC) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.ibss_peer_lost.peer, addr, ETH_ALEN); + wpa_supplicant_event(drv->ctx, EVENT_IBSS_PEER_LOST, &data); +} + + +static void nl80211_rekey_offload_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + struct nlattr *rekey_info[NUM_NL80211_REKEY_DATA]; + static struct nla_policy rekey_policy[NUM_NL80211_REKEY_DATA] = { + [NL80211_REKEY_DATA_KEK] = { + .minlen = NL80211_KEK_LEN, + .maxlen = NL80211_KEK_LEN, + }, + [NL80211_REKEY_DATA_KCK] = { + .minlen = NL80211_KCK_LEN, + .maxlen = NL80211_KCK_LEN, + }, + [NL80211_REKEY_DATA_REPLAY_CTR] = { + .minlen = NL80211_REPLAY_CTR_LEN, + .maxlen = NL80211_REPLAY_CTR_LEN, + }, + }; + union wpa_event_data data; + + if (!tb[NL80211_ATTR_MAC]) + return; + if (!tb[NL80211_ATTR_REKEY_DATA]) + return; + if (nla_parse_nested(rekey_info, MAX_NL80211_REKEY_DATA, + tb[NL80211_ATTR_REKEY_DATA], rekey_policy)) + return; + if (!rekey_info[NL80211_REKEY_DATA_REPLAY_CTR]) + return; + + os_memset(&data, 0, sizeof(data)); + data.driver_gtk_rekey.bssid = nla_data(tb[NL80211_ATTR_MAC]); + wpa_printf(MSG_DEBUG, "nl80211: Rekey offload event for BSSID " MACSTR, + MAC2STR(data.driver_gtk_rekey.bssid)); + data.driver_gtk_rekey.replay_ctr = + nla_data(rekey_info[NL80211_REKEY_DATA_REPLAY_CTR]); + wpa_hexdump(MSG_DEBUG, "nl80211: Rekey offload - Replay Counter", + data.driver_gtk_rekey.replay_ctr, NL80211_REPLAY_CTR_LEN); + wpa_supplicant_event(drv->ctx, EVENT_DRIVER_GTK_REKEY, &data); +} + + +static void nl80211_pmksa_candidate_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + struct nlattr *cand[NUM_NL80211_PMKSA_CANDIDATE]; + static struct nla_policy cand_policy[NUM_NL80211_PMKSA_CANDIDATE] = { + [NL80211_PMKSA_CANDIDATE_INDEX] = { .type = NLA_U32 }, + [NL80211_PMKSA_CANDIDATE_BSSID] = { + .minlen = ETH_ALEN, + .maxlen = ETH_ALEN, + }, + [NL80211_PMKSA_CANDIDATE_PREAUTH] = { .type = NLA_FLAG }, + }; + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "nl80211: PMKSA candidate event"); + + if (!tb[NL80211_ATTR_PMKSA_CANDIDATE]) + return; + if (nla_parse_nested(cand, MAX_NL80211_PMKSA_CANDIDATE, + tb[NL80211_ATTR_PMKSA_CANDIDATE], cand_policy)) + return; + if (!cand[NL80211_PMKSA_CANDIDATE_INDEX] || + !cand[NL80211_PMKSA_CANDIDATE_BSSID]) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.pmkid_candidate.bssid, + nla_data(cand[NL80211_PMKSA_CANDIDATE_BSSID]), ETH_ALEN); + data.pmkid_candidate.index = + nla_get_u32(cand[NL80211_PMKSA_CANDIDATE_INDEX]); + data.pmkid_candidate.preauth = + cand[NL80211_PMKSA_CANDIDATE_PREAUTH] != NULL; + wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data); +} + + +static void nl80211_client_probe_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "nl80211: Probe client event"); + + if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_ACK]) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.client_poll.addr, + nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); + + wpa_supplicant_event(drv->ctx, EVENT_DRIVER_CLIENT_POLL_OK, &data); +} + + +static void nl80211_tdls_oper_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "nl80211: TDLS operation event"); + + if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_TDLS_OPERATION]) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.tdls.peer, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); + switch (nla_get_u8(tb[NL80211_ATTR_TDLS_OPERATION])) { + case NL80211_TDLS_SETUP: + wpa_printf(MSG_DEBUG, "nl80211: TDLS setup request for peer " + MACSTR, MAC2STR(data.tdls.peer)); + data.tdls.oper = TDLS_REQUEST_SETUP; + break; + case NL80211_TDLS_TEARDOWN: + wpa_printf(MSG_DEBUG, "nl80211: TDLS teardown request for peer " + MACSTR, MAC2STR(data.tdls.peer)); + data.tdls.oper = TDLS_REQUEST_TEARDOWN; + break; + default: + wpa_printf(MSG_DEBUG, "nl80211: Unsupported TDLS operatione " + "event"); + return; + } + if (tb[NL80211_ATTR_REASON_CODE]) { + data.tdls.reason_code = + nla_get_u16(tb[NL80211_ATTR_REASON_CODE]); + } + + wpa_supplicant_event(drv->ctx, EVENT_TDLS, &data); +} + + +static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb, + int wds) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + union wpa_event_data event; + + if (!tb[NL80211_ATTR_MAC]) + return; + + os_memset(&event, 0, sizeof(event)); + event.rx_from_unknown.bssid = bss->addr; + event.rx_from_unknown.addr = nla_data(tb[NL80211_ATTR_MAC]); + event.rx_from_unknown.wds = wds; + + wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event); +} + + +static void do_process_drv_event(struct i802_bss *bss, int cmd, + struct nlattr **tb) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + + if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED && + (cmd == NL80211_CMD_NEW_SCAN_RESULTS || + cmd == NL80211_CMD_SCAN_ABORTED)) { wpa_driver_nl80211_set_mode(&drv->first_bss, - IEEE80211_MODE_AP); - drv->ap_scan_as_station = 0; + drv->ap_scan_as_station); + drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED; } - switch (gnlh->cmd) { + switch (cmd) { case NL80211_CMD_TRIGGER_SCAN: wpa_printf(MSG_DEBUG, "nl80211: Scan trigger"); break; + case NL80211_CMD_START_SCHED_SCAN: + wpa_printf(MSG_DEBUG, "nl80211: Sched scan started"); + break; + case NL80211_CMD_SCHED_SCAN_STOPPED: + wpa_printf(MSG_DEBUG, "nl80211: Sched scan stopped"); + wpa_supplicant_event(drv->ctx, EVENT_SCHED_SCAN_STOPPED, NULL); + break; case NL80211_CMD_NEW_SCAN_RESULTS: wpa_printf(MSG_DEBUG, "nl80211: New scan results available"); drv->scan_complete_events = 1; @@ -1008,6 +2200,11 @@ static int process_event(struct nl_msg *msg, void *arg) drv->ctx); send_scan_event(drv, 0, tb); break; + case NL80211_CMD_SCHED_SCAN_RESULTS: + wpa_printf(MSG_DEBUG, + "nl80211: New sched scan results available"); + send_scan_event(drv, 0, tb); + break; case NL80211_CMD_SCAN_ABORTED: wpa_printf(MSG_DEBUG, "nl80211: Scan aborted"); /* @@ -1022,40 +2219,34 @@ static int process_event(struct nl_msg *msg, void *arg) case NL80211_CMD_ASSOCIATE: case NL80211_CMD_DEAUTHENTICATE: case NL80211_CMD_DISASSOCIATE: - case NL80211_CMD_ACTION: - case NL80211_CMD_ACTION_TX_STATUS: - mlme_event(drv, gnlh->cmd, tb[NL80211_ATTR_FRAME], + case NL80211_CMD_FRAME_TX_STATUS: + case NL80211_CMD_UNPROT_DEAUTHENTICATE: + case NL80211_CMD_UNPROT_DISASSOCIATE: + 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: - mlme_event_connect(drv, gnlh->cmd, + mlme_event_connect(drv, cmd, tb[NL80211_ATTR_STATUS_CODE], tb[NL80211_ATTR_MAC], 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: - if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { - /* - * Avoid reporting two disassociation events that could - * confuse the core code. - */ - wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect " - "event when using userspace SME"); - break; - } - drv->associated = 0; - os_memset(&data, 0, sizeof(data)); - if (tb[NL80211_ATTR_REASON_CODE]) - data.disassoc_info.reason_code = - nla_get_u16(tb[NL80211_ATTR_REASON_CODE]); - wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, &data); + mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE], + tb[NL80211_ATTR_MAC], + tb[NL80211_ATTR_DISCONNECTED_BY_AP]); break; case NL80211_CMD_MICHAEL_MIC_FAILURE: - mlme_event_michael_mic_failure(drv, tb); + mlme_event_michael_mic_failure(bss, tb); break; case NL80211_CMD_JOIN_IBSS: mlme_event_join_ibss(drv, tb); @@ -1069,6 +2260,123 @@ static int process_event(struct nl_msg *msg, void *arg) case NL80211_CMD_NOTIFY_CQM: nl80211_cqm_event(drv, tb); break; + case NL80211_CMD_REG_CHANGE: + wpa_printf(MSG_DEBUG, "nl80211: Regulatory domain change"); + wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, + NULL); + break; + case NL80211_CMD_REG_BEACON_HINT: + wpa_printf(MSG_DEBUG, "nl80211: Regulatory beacon hint"); + wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, + NULL); + break; + case NL80211_CMD_NEW_STATION: + nl80211_new_station_event(drv, tb); + break; + case NL80211_CMD_DEL_STATION: + nl80211_del_station_event(drv, tb); + break; + case NL80211_CMD_SET_REKEY_OFFLOAD: + nl80211_rekey_offload_event(drv, tb); + break; + case NL80211_CMD_PMKSA_CANDIDATE: + nl80211_pmksa_candidate_event(drv, tb); + break; + case NL80211_CMD_PROBE_CLIENT: + nl80211_client_probe_event(drv, tb); + break; + case NL80211_CMD_TDLS_OPER: + nl80211_tdls_oper_event(drv, tb); + break; + default: + wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event " + "(cmd=%d)", cmd); + break; + } +} + + +static int process_drv_event(struct nl_msg *msg, void *arg) +{ + struct wpa_driver_nl80211_data *drv = arg; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct i802_bss *bss; + int ifidx = -1; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb[NL80211_ATTR_IFINDEX]) + ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); + + for (bss = &drv->first_bss; bss; bss = bss->next) { + if (ifidx == -1 || ifidx == bss->ifindex) { + do_process_drv_event(bss, gnlh->cmd, tb); + return NL_SKIP; + } + } + + wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d) for foreign " + "interface (ifindex %d)", gnlh->cmd, ifidx); + + return NL_SKIP; +} + + +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, *tmp; + int ifidx = -1; + struct i802_bss *bss; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb[NL80211_ATTR_IFINDEX]) + ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); + + dl_list_for_each_safe(drv, tmp, &global->interfaces, + struct wpa_driver_nl80211_data, list) { + for (bss = &drv->first_bss; bss; bss = bss->next) { + if (ifidx == -1 || ifidx == bss->ifindex) { + do_process_drv_event(bss, gnlh->cmd, tb); + return NL_SKIP; + } + } + } + + return NL_SKIP; +} + + +static int process_bss_event(struct nl_msg *msg, void *arg) +{ + struct i802_bss *bss = arg; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + switch (gnlh->cmd) { + case NL80211_CMD_FRAME: + case NL80211_CMD_FRAME_TX_STATUS: + 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_RX_SIGNAL_DBM]); + break; + case NL80211_CMD_UNEXPECTED_FRAME: + nl80211_spurious_frame(bss, tb, 0); + break; + case NL80211_CMD_UNEXPECTED_4ADDR_FRAME: + nl80211_spurious_frame(bss, tb, 1); + break; default: wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event " "(cmd=%d)", gnlh->cmd); @@ -1080,20 +2388,13 @@ static int process_event(struct nl_msg *msg, void *arg) static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx, - void *sock_ctx) + void *handle) { - struct nl_cb *cb; - struct wpa_driver_nl80211_data *drv = eloop_ctx; + struct nl_cb *cb = eloop_ctx; wpa_printf(MSG_DEBUG, "nl80211: Event message available"); - cb = nl_cb_clone(drv->nl_cb); - if (!cb) - return; - nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); - nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, process_event, drv); - nl_recvmsgs(drv->nl_handle_event, cb); - nl_cb_put(cb); + nl_recvmsgs(handle, cb); } @@ -1121,49 +2422,160 @@ static int wpa_driver_nl80211_set_country(void *priv, const char *alpha2_arg) alpha2[1] = alpha2_arg[1]; alpha2[2] = '\0'; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_REQ_SET_REG, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_REQ_SET_REG); NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, alpha2); if (send_and_recv_msgs(drv, msg, NULL, NULL)) return -EINVAL; return 0; nla_put_failure: + nlmsg_free(msg); return -EINVAL; } -#ifndef HOSTAPD struct wiphy_info_data { - int max_scan_ssids; - int ap_supported; - int auth_supported; - int connect_supported; + struct wpa_driver_capa *capa; + + unsigned int error:1; + unsigned int device_ap_sme:1; + unsigned int poll_command_supported:1; + unsigned int data_tx_status:1; + unsigned int monitor_supported:1; }; +static unsigned int probe_resp_offload_support(int supp_protocols) +{ + unsigned int prot = 0; + + if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS) + prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS; + if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2) + prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2; + if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P) + prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P; + if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U) + prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING; + + return prot; +} + + static int wiphy_info_handler(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct wiphy_info_data *info = arg; + int p2p_go_supported = 0, p2p_client_supported = 0; + int p2p_concurrent = 0, p2p_multichan_concurrent = 0; + int auth_supported = 0, connect_supported = 0; + struct wpa_driver_capa *capa = info->capa; + static struct nla_policy + iface_combination_policy[NUM_NL80211_IFACE_COMB] = { + [NL80211_IFACE_COMB_LIMITS] = { .type = NLA_NESTED }, + [NL80211_IFACE_COMB_MAXNUM] = { .type = NLA_U32 }, + [NL80211_IFACE_COMB_STA_AP_BI_MATCH] = { .type = NLA_FLAG }, + [NL80211_IFACE_COMB_NUM_CHANNELS] = { .type = NLA_U32 }, + }, + iface_limit_policy[NUM_NL80211_IFACE_LIMIT] = { + [NL80211_IFACE_LIMIT_TYPES] = { .type = NLA_NESTED }, + [NL80211_IFACE_LIMIT_MAX] = { .type = NLA_U32 }, + }; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]) - info->max_scan_ssids = + capa->max_scan_ssids = nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]); + if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]) + capa->max_sched_scan_ssids = + nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]); + + if (tb[NL80211_ATTR_MAX_MATCH_SETS]) + capa->max_match_sets = + nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]); + if (tb[NL80211_ATTR_SUPPORTED_IFTYPES]) { struct nlattr *nl_mode; int i; nla_for_each_nested(nl_mode, tb[NL80211_ATTR_SUPPORTED_IFTYPES], i) { - if (nl_mode->nla_type == NL80211_IFTYPE_AP) { - info->ap_supported = 1; + switch (nla_type(nl_mode)) { + case NL80211_IFTYPE_AP: + capa->flags |= WPA_DRIVER_FLAGS_AP; + break; + case NL80211_IFTYPE_P2P_GO: + p2p_go_supported = 1; + break; + case NL80211_IFTYPE_P2P_CLIENT: + p2p_client_supported = 1; break; + case NL80211_IFTYPE_MONITOR: + info->monitor_supported = 1; + break; + } + } + } + + if (tb[NL80211_ATTR_INTERFACE_COMBINATIONS]) { + struct nlattr *nl_combi; + int rem_combi; + + nla_for_each_nested(nl_combi, + tb[NL80211_ATTR_INTERFACE_COMBINATIONS], + rem_combi) { + struct nlattr *tb_comb[NUM_NL80211_IFACE_COMB]; + struct nlattr *tb_limit[NUM_NL80211_IFACE_LIMIT]; + struct nlattr *nl_limit, *nl_mode; + int err, rem_limit, rem_mode; + int combination_has_p2p = 0, combination_has_mgd = 0; + + err = nla_parse_nested(tb_comb, MAX_NL80211_IFACE_COMB, + nl_combi, + iface_combination_policy); + if (err || !tb_comb[NL80211_IFACE_COMB_LIMITS] || + !tb_comb[NL80211_IFACE_COMB_MAXNUM] || + !tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]) + goto broken_combination; + + nla_for_each_nested(nl_limit, + tb_comb[NL80211_IFACE_COMB_LIMITS], + rem_limit) { + err = nla_parse_nested(tb_limit, + MAX_NL80211_IFACE_LIMIT, + nl_limit, + iface_limit_policy); + if (err || + !tb_limit[NL80211_IFACE_LIMIT_TYPES]) + goto broken_combination; + + nla_for_each_nested( + nl_mode, + tb_limit[NL80211_IFACE_LIMIT_TYPES], + rem_mode) { + int ift = nla_type(nl_mode); + if (ift == NL80211_IFTYPE_P2P_GO || + ift == NL80211_IFTYPE_P2P_CLIENT) + combination_has_p2p = 1; + if (ift == NL80211_IFTYPE_STATION) + combination_has_mgd = 1; + } + if (combination_has_p2p && combination_has_mgd) + break; } + + if (combination_has_p2p && combination_has_mgd) { + p2p_concurrent = 1; + if (nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]) > 1) + p2p_multichan_concurrent = 1; + break; + } + +broken_combination: + ; } } @@ -1173,14 +2585,108 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) nla_for_each_nested(nl_cmd, tb[NL80211_ATTR_SUPPORTED_COMMANDS], i) { - u32 cmd = nla_get_u32(nl_cmd); - if (cmd == NL80211_CMD_AUTHENTICATE) - info->auth_supported = 1; - else if (cmd == NL80211_CMD_CONNECT) - info->connect_supported = 1; + switch (nla_get_u32(nl_cmd)) { + case NL80211_CMD_AUTHENTICATE: + auth_supported = 1; + break; + case NL80211_CMD_CONNECT: + connect_supported = 1; + break; + case NL80211_CMD_START_SCHED_SCAN: + capa->sched_scan_supported = 1; + break; + case NL80211_CMD_PROBE_CLIENT: + info->poll_command_supported = 1; + break; + } + } + } + + if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK]) { + wpa_printf(MSG_DEBUG, "nl80211: Using driver-based " + "off-channel TX"); + capa->flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_TX; + } + + if (tb[NL80211_ATTR_ROAM_SUPPORT]) { + wpa_printf(MSG_DEBUG, "nl80211: Using driver-based roaming"); + capa->flags |= WPA_DRIVER_FLAGS_BSS_SELECTION; + } + + /* default to 5000 since early versions of mac80211 don't set it */ + capa->max_remain_on_chan = 5000; + + if (tb[NL80211_ATTR_SUPPORT_AP_UAPSD]) + capa->flags |= WPA_DRIVER_FLAGS_AP_UAPSD; + + if (tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]) + capa->max_remain_on_chan = + nla_get_u32(tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]); + + if (auth_supported) + capa->flags |= WPA_DRIVER_FLAGS_SME; + else if (!connect_supported) { + wpa_printf(MSG_INFO, "nl80211: Driver does not support " + "authentication/association or connect commands"); + info->error = 1; + } + + if (p2p_go_supported && p2p_client_supported) + capa->flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE; + if (p2p_concurrent) { + wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group " + "interface (driver advertised support)"); + capa->flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT; + capa->flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P; + + if (p2p_multichan_concurrent) { + wpa_printf(MSG_DEBUG, "nl80211: Enable multi-channel " + "concurrent (driver advertised support)"); + capa->flags |= + WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT; + } + } + + if (tb[NL80211_ATTR_TDLS_SUPPORT]) { + wpa_printf(MSG_DEBUG, "nl80211: TDLS supported"); + capa->flags |= WPA_DRIVER_FLAGS_TDLS_SUPPORT; + + if (tb[NL80211_ATTR_TDLS_EXTERNAL_SETUP]) { + wpa_printf(MSG_DEBUG, "nl80211: TDLS external setup"); + capa->flags |= + WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP; } } + if (tb[NL80211_ATTR_DEVICE_AP_SME]) + info->device_ap_sme = 1; + + if (tb[NL80211_ATTR_FEATURE_FLAGS]) { + u32 flags = nla_get_u32(tb[NL80211_ATTR_FEATURE_FLAGS]); + + 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 (flags & NL80211_FEATURE_SAE) + capa->flags |= WPA_DRIVER_FLAGS_SAE; + + if (flags & NL80211_FEATURE_NEED_OBSS_SCAN) + capa->flags |= WPA_DRIVER_FLAGS_OBSS_SCAN; + } + + if (tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]) { + int protocols = + nla_get_u32(tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]); + wpa_printf(MSG_DEBUG, "nl80211: Supports Probe Response " + "offload in AP mode"); + capa->flags |= WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD; + capa->probe_resp_offloads = + probe_resp_offload_support(protocols); + } + return NL_SKIP; } @@ -1191,12 +2697,13 @@ static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv, struct nl_msg *msg; os_memset(info, 0, sizeof(*info)); + info->capa = &drv->capa; + msg = nlmsg_alloc(); if (!msg) return -1; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_GET_WIPHY, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->first_bss.ifindex); @@ -1214,6 +2721,10 @@ static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) struct wiphy_info_data info; if (wpa_driver_nl80211_get_info(drv, &info)) return -1; + + if (info.error) + return -1; + drv->has_capability = 1; /* For now, assume TKIP, CCMP, WPA, WPA2 are supported */ drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | @@ -1228,210 +2739,421 @@ static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) WPA_DRIVER_AUTH_SHARED | WPA_DRIVER_AUTH_LEAP; - drv->capa.max_scan_ssids = info.max_scan_ssids; - if (info.ap_supported) - drv->capa.flags |= WPA_DRIVER_FLAGS_AP; + 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; - if (info.auth_supported) - drv->capa.flags |= WPA_DRIVER_FLAGS_SME; - else if (!info.connect_supported) { - wpa_printf(MSG_INFO, "nl80211: Driver does not support " - "authentication/association or connect commands"); - return -1; + if (!info.device_ap_sme) { + drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS; + + /* + * No AP SME is currently assumed to also indicate no AP MLME + * in the driver/firmware. + */ + drv->capa.flags |= WPA_DRIVER_FLAGS_AP_MLME; } - drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE; - drv->capa.max_remain_on_chan = 5000; + drv->device_ap_sme = info.device_ap_sme; + drv->poll_command_supported = info.poll_command_supported; + drv->data_tx_status = info.data_tx_status; + + /* + * If poll command and tx status are supported, mac80211 is new enough + * to have everything we need to not need monitor interfaces. + */ + drv->use_monitor = !info.poll_command_supported || !info.data_tx_status; + + if (drv->device_ap_sme && drv->use_monitor) { + /* + * Non-mac80211 drivers may not support monitor interface. + * Make sure we do not get stuck with incorrect capability here + * by explicitly testing this. + */ + if (!info.monitor_supported) { + wpa_printf(MSG_DEBUG, "nl80211: Disable use_monitor " + "with device_ap_sme since no monitor mode " + "support detected"); + drv->use_monitor = 0; + } + } + + /* + * If we aren't going to use monitor interfaces, but the + * driver doesn't support data TX status, we won't get TX + * status for EAPOL frames. + */ + if (!drv->use_monitor && !info.data_tx_status) + drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; return 0; } -#endif /* HOSTAPD */ -static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv, - void *ctx) +#ifdef ANDROID +static int android_genl_ctrl_resolve(struct nl_handle *handle, + const char *name) { - int ret; - - /* Initialize generic netlink and nl80211 */ + /* + * Android ICS has very minimal genl_ctrl_resolve() implementation, so + * need to work around that. + */ + struct nl_cache *cache = NULL; + struct genl_family *nl80211 = NULL; + int id = -1; - drv->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); - if (drv->nl_cb == NULL) { - wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " - "callbacks"); - goto err1; + if (genl_ctrl_alloc_cache(handle, &cache) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " + "netlink cache"); + goto fail; } - drv->nl_handle = nl_handle_alloc_cb(drv->nl_cb); - if (drv->nl_handle == NULL) { - wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " - "callbacks"); - goto err2; - } + nl80211 = genl_ctrl_search_by_name(cache, name); + if (nl80211 == NULL) + goto fail; - drv->nl_handle_event = nl_handle_alloc_cb(drv->nl_cb); - if (drv->nl_handle_event == NULL) { - wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " - "callbacks (event)"); - goto err2b; - } + id = genl_family_get_id(nl80211); - if (genl_connect(drv->nl_handle)) { - wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic " - "netlink"); - goto err3; - } +fail: + if (nl80211) + genl_family_put(nl80211); + if (cache) + nl_cache_free(cache); - if (genl_connect(drv->nl_handle_event)) { - wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic " - "netlink (event)"); - goto err3; - } + return id; +} +#define genl_ctrl_resolve android_genl_ctrl_resolve +#endif /* ANDROID */ -#ifdef CONFIG_LIBNL20 - if (genl_ctrl_alloc_cache(drv->nl_handle, &drv->nl_cache) < 0) { - wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " - "netlink cache"); - goto err3; - } - if (genl_ctrl_alloc_cache(drv->nl_handle_event, &drv->nl_cache_event) < - 0) { - wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " - "netlink cache (event)"); - goto err3b; - } -#else /* CONFIG_LIBNL20 */ - drv->nl_cache = genl_ctrl_alloc_cache(drv->nl_handle); - if (drv->nl_cache == NULL) { - wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " - "netlink cache"); - goto err3; - } - drv->nl_cache_event = genl_ctrl_alloc_cache(drv->nl_handle_event); - if (drv->nl_cache_event == NULL) { - wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " - "netlink cache (event)"); - goto err3b; + +static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global) +{ + int ret; + + global->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); + if (global->nl_cb == NULL) { + wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " + "callbacks"); + return -1; } -#endif /* CONFIG_LIBNL20 */ - drv->nl80211 = genl_ctrl_search_by_name(drv->nl_cache, "nl80211"); - if (drv->nl80211 == NULL) { + global->nl = nl_create_handle(global->nl_cb, "nl"); + if (global->nl == NULL) + goto err; + + global->nl80211_id = genl_ctrl_resolve(global->nl, "nl80211"); + if (global->nl80211_id < 0) { wpa_printf(MSG_ERROR, "nl80211: 'nl80211' generic netlink not " "found"); - goto err4; + goto err; } - ret = nl_get_multicast_id(drv, "nl80211", "scan"); + global->nl_event = nl_create_handle(global->nl_cb, "event"); + if (global->nl_event == NULL) + goto err; + + ret = nl_get_multicast_id(global, "nl80211", "scan"); if (ret >= 0) - ret = nl_socket_add_membership(drv->nl_handle_event, ret); + ret = nl_socket_add_membership(global->nl_event, ret); if (ret < 0) { wpa_printf(MSG_ERROR, "nl80211: Could not add multicast " "membership for scan events: %d (%s)", ret, strerror(-ret)); - goto err4; + goto err; } - ret = nl_get_multicast_id(drv, "nl80211", "mlme"); + ret = nl_get_multicast_id(global, "nl80211", "mlme"); if (ret >= 0) - ret = nl_socket_add_membership(drv->nl_handle_event, ret); + ret = nl_socket_add_membership(global->nl_event, ret); if (ret < 0) { wpa_printf(MSG_ERROR, "nl80211: Could not add multicast " "membership for mlme events: %d (%s)", ret, strerror(-ret)); - goto err4; + goto err; + } + + ret = nl_get_multicast_id(global, "nl80211", "regulatory"); + if (ret >= 0) + ret = nl_socket_add_membership(global->nl_event, ret); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "nl80211: Could not add multicast " + "membership for regulatory events: %d (%s)", + ret, strerror(-ret)); + /* Continue without regulatory events */ } - eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle_event), - wpa_driver_nl80211_event_receive, drv, ctx); + nl_cb_set(global->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, + no_seq_check, NULL); + nl_cb_set(global->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, + process_global_event, global); + + eloop_register_read_sock(nl_socket_get_fd(global->nl_event), + wpa_driver_nl80211_event_receive, + global->nl_cb, global->nl_event); return 0; -err4: - nl_cache_free(drv->nl_cache_event); -err3b: - nl_cache_free(drv->nl_cache); -err3: - nl_handle_destroy(drv->nl_handle_event); -err2b: - nl_handle_destroy(drv->nl_handle); -err2: - nl_cb_put(drv->nl_cb); -err1: +err: + nl_destroy_handles(&global->nl_event); + nl_destroy_handles(&global->nl); + nl_cb_put(global->nl_cb); + global->nl_cb = NULL; return -1; } +static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv) +{ + drv->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!drv->nl_cb) { + wpa_printf(MSG_ERROR, "nl80211: Failed to alloc cb struct"); + return -1; + } + + nl_cb_set(drv->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, + no_seq_check, NULL); + nl_cb_set(drv->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, + process_drv_event, drv); + + return 0; +} + + +static void wpa_driver_nl80211_rfkill_blocked(void *ctx) +{ + wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked"); + /* + * This may be for any interface; use ifdown event to disable + * interface. + */ +} + + +static void wpa_driver_nl80211_rfkill_unblocked(void *ctx) +{ + struct wpa_driver_nl80211_data *drv = ctx; + wpa_printf(MSG_DEBUG, "nl80211: RFKILL unblocked"); + if (linux_set_iface_flags(drv->global->ioctl_sock, + drv->first_bss.ifname, 1)) { + wpa_printf(MSG_DEBUG, "nl80211: Could not set interface UP " + "after rfkill unblock"); + return; + } + /* rtnetlink ifup handler will report interface as enabled */ +} + + +static void nl80211_get_phy_name(struct wpa_driver_nl80211_data *drv) +{ + /* Find phy (radio) to which this interface belongs */ + char buf[90], *pos; + int f, rv; + + drv->phyname[0] = '\0'; + snprintf(buf, sizeof(buf) - 1, "/sys/class/net/%s/phy80211/name", + drv->first_bss.ifname); + f = open(buf, O_RDONLY); + if (f < 0) { + wpa_printf(MSG_DEBUG, "Could not open file %s: %s", + buf, strerror(errno)); + return; + } + + rv = read(f, drv->phyname, sizeof(drv->phyname) - 1); + close(f); + if (rv < 0) { + wpa_printf(MSG_DEBUG, "Could not read file %s: %s", + buf, strerror(errno)); + return; + } + + drv->phyname[rv] = '\0'; + pos = os_strchr(drv->phyname, '\n'); + if (pos) + *pos = '\0'; + wpa_printf(MSG_DEBUG, "nl80211: interface %s in phy %s", + drv->first_bss.ifname, drv->phyname); +} + + +static void wpa_driver_nl80211_handle_eapol_tx_status(int sock, + void *eloop_ctx, + void *handle) +{ + struct wpa_driver_nl80211_data *drv = eloop_ctx; + u8 data[2048]; + struct msghdr msg; + struct iovec entry; + u8 control[512]; + struct cmsghdr *cmsg; + int res, found_ee = 0, found_wifi = 0, acked = 0; + union wpa_event_data event; + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = &entry; + msg.msg_iovlen = 1; + entry.iov_base = data; + entry.iov_len = sizeof(data); + msg.msg_control = &control; + msg.msg_controllen = sizeof(control); + + res = recvmsg(sock, &msg, MSG_ERRQUEUE); + /* if error or not fitting 802.3 header, return */ + if (res < 14) + return; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) + { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_WIFI_STATUS) { + int *ack; + + found_wifi = 1; + ack = (void *)CMSG_DATA(cmsg); + acked = *ack; + } + + if (cmsg->cmsg_level == SOL_PACKET && + cmsg->cmsg_type == PACKET_TX_TIMESTAMP) { + struct sock_extended_err *err = + (struct sock_extended_err *)CMSG_DATA(cmsg); + + if (err->ee_origin == SO_EE_ORIGIN_TXSTATUS) + found_ee = 1; + } + } + + if (!found_ee || !found_wifi) + return; + + memset(&event, 0, sizeof(event)); + event.eapol_tx_status.dst = data; + event.eapol_tx_status.data = data + 14; + event.eapol_tx_status.data_len = res - 14; + event.eapol_tx_status.ack = acked; + wpa_supplicant_event(drv->ctx, EVENT_EAPOL_TX_STATUS, &event); +} + + +static int nl80211_init_bss(struct i802_bss *bss) +{ + bss->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!bss->nl_cb) + return -1; + + nl_cb_set(bss->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, + no_seq_check, NULL); + nl_cb_set(bss->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, + process_bss_event, bss); + + return 0; +} + + +static void nl80211_destroy_bss(struct i802_bss *bss) +{ + nl_cb_put(bss->nl_cb); + bss->nl_cb = NULL; +} + + /** * wpa_driver_nl80211_init - Initialize nl80211 driver interface * @ctx: context to be used when calling wpa_supplicant functions, * e.g., wpa_supplicant_event() * @ifname: interface name, e.g., wlan0 + * @global_priv: private driver global data from global_init() * Returns: Pointer to private data, %NULL on failure */ -static void * wpa_driver_nl80211_init(void *ctx, const char *ifname) +static void * wpa_driver_nl80211_init(void *ctx, const char *ifname, + void *global_priv) { struct wpa_driver_nl80211_data *drv; - struct netlink_config *cfg; + struct rfkill_config *rcfg; struct i802_bss *bss; + if (global_priv == NULL) + return NULL; drv = os_zalloc(sizeof(*drv)); if (drv == NULL) return NULL; + drv->global = global_priv; drv->ctx = ctx; bss = &drv->first_bss; bss->drv = drv; + bss->ctx = ctx; + os_strlcpy(bss->ifname, ifname, sizeof(bss->ifname)); drv->monitor_ifidx = -1; drv->monitor_sock = -1; - drv->ioctl_sock = -1; + drv->eapol_tx_sock = -1; + drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED; - if (wpa_driver_nl80211_init_nl(drv, ctx)) { + if (wpa_driver_nl80211_init_nl(drv)) { os_free(drv); return NULL; } - drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); - if (drv->ioctl_sock < 0) { - perror("socket(PF_INET,SOCK_DGRAM)"); + if (nl80211_init_bss(bss)) goto failed; - } - cfg = os_zalloc(sizeof(*cfg)); - if (cfg == NULL) - goto failed; - cfg->ctx = drv; - cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink; - cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink; - drv->netlink = netlink_init(cfg); - if (drv->netlink == NULL) { - os_free(cfg); + nl80211_get_phy_name(drv); + + rcfg = os_zalloc(sizeof(*rcfg)); + if (rcfg == NULL) goto failed; + rcfg->ctx = drv; + os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname)); + rcfg->blocked_cb = wpa_driver_nl80211_rfkill_blocked; + rcfg->unblocked_cb = wpa_driver_nl80211_rfkill_unblocked; + drv->rfkill = rfkill_init(rcfg); + if (drv->rfkill == NULL) { + wpa_printf(MSG_DEBUG, "nl80211: RFKILL status not available"); + os_free(rcfg); } + if (wpa_driver_nl80211_finish_drv_init(drv)) goto failed; - return bss; + drv->eapol_tx_sock = socket(PF_PACKET, SOCK_DGRAM, 0); + if (drv->eapol_tx_sock < 0) + goto failed; -failed: - netlink_deinit(drv->netlink); - if (drv->ioctl_sock >= 0) - close(drv->ioctl_sock); + if (drv->data_tx_status) { + int enabled = 1; + + if (setsockopt(drv->eapol_tx_sock, SOL_SOCKET, SO_WIFI_STATUS, + &enabled, sizeof(enabled)) < 0) { + wpa_printf(MSG_DEBUG, + "nl80211: wifi status sockopt failed\n"); + drv->data_tx_status = 0; + if (!drv->use_monitor) + drv->capa.flags &= + ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; + } else { + eloop_register_read_sock(drv->eapol_tx_sock, + wpa_driver_nl80211_handle_eapol_tx_status, + drv, NULL); + } + } - genl_family_put(drv->nl80211); - nl_cache_free(drv->nl_cache); - nl_handle_destroy(drv->nl_handle); - nl_cb_put(drv->nl_cb); - eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle_event)); + if (drv->global) { + dl_list_add(&drv->global->interfaces, &drv->list); + drv->in_interface_list = 1; + } - os_free(drv); + return bss; + +failed: + wpa_driver_nl80211_deinit(bss); return NULL; } -static int nl80211_register_action_frame(struct wpa_driver_nl80211_data *drv, - const u8 *match, size_t match_len) +static int nl80211_register_frame(struct i802_bss *bss, + struct nl_handle *nl_handle, + u16 type, const u8 *match, size_t match_len) { + struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret = -1; @@ -1439,18 +3161,24 @@ static int nl80211_register_action_frame(struct wpa_driver_nl80211_data *drv, if (!msg) return -1; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_REGISTER_ACTION, 0); + wpa_printf(MSG_DEBUG, "nl80211: Register frame type=0x%x nl_handle=%p", + type, nl_handle); + wpa_hexdump(MSG_DEBUG, "nl80211: Register frame match", + match, match_len); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_ACTION); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); + NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE, type); NLA_PUT(msg, NL80211_ATTR_FRAME_MATCH, match_len, match); - ret = send_and_recv(drv, drv->nl_handle_event, msg, NULL, NULL); + ret = send_and_recv(drv->global, nl_handle, msg, NULL, NULL); msg = NULL; if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: Register Action command " - "failed: ret=%d (%s)", ret, strerror(-ret)); - wpa_hexdump(MSG_DEBUG, "nl80211: Register Action match", + wpa_printf(MSG_DEBUG, "nl80211: Register frame command " + "failed (type=%u): ret=%d (%s)", + type, ret, strerror(-ret)); + wpa_hexdump(MSG_DEBUG, "nl80211: Register frame match", match, match_len); goto nla_put_failure; } @@ -1461,54 +3189,272 @@ nla_put_failure: } -static int nl80211_register_action_frames(struct wpa_driver_nl80211_data *drv) +static int nl80211_alloc_mgmt_handle(struct i802_bss *bss) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + + if (bss->nl_mgmt) { + wpa_printf(MSG_DEBUG, "nl80211: Mgmt reporting " + "already on! (nl_mgmt=%p)", bss->nl_mgmt); + return -1; + } + + bss->nl_mgmt = nl_create_handle(drv->nl_cb, "mgmt"); + if (bss->nl_mgmt == NULL) + return -1; + + eloop_register_read_sock(nl_socket_get_fd(bss->nl_mgmt), + wpa_driver_nl80211_event_receive, bss->nl_cb, + bss->nl_mgmt); + + return 0; +} + + +static int nl80211_register_action_frame(struct i802_bss *bss, + const u8 *match, size_t match_len) { + u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_ACTION << 4); + return nl80211_register_frame(bss, bss->nl_mgmt, + type, match, match_len); +} + + +static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + + if (nl80211_alloc_mgmt_handle(bss)) + return -1; + wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with non-AP " + "handle %p", bss->nl_mgmt); + +#if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING) + /* GAS Initial Request */ + if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0a", 2) < 0) + return -1; + /* GAS Initial Response */ + if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0b", 2) < 0) + return -1; + /* GAS Comeback Request */ + if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0c", 2) < 0) + return -1; + /* GAS Comeback Response */ + if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0d", 2) < 0) + return -1; +#endif /* CONFIG_P2P || CONFIG_INTERWORKING */ +#ifdef CONFIG_P2P + /* P2P Public Action */ + if (nl80211_register_action_frame(bss, + (u8 *) "\x04\x09\x50\x6f\x9a\x09", + 6) < 0) + return -1; + /* P2P Action */ + if (nl80211_register_action_frame(bss, + (u8 *) "\x7f\x50\x6f\x9a\x09", + 5) < 0) + return -1; +#endif /* CONFIG_P2P */ +#ifdef CONFIG_IEEE80211W + /* SA Query Response */ + if (nl80211_register_action_frame(bss, (u8 *) "\x08\x01", 2) < 0) + return -1; +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_TDLS + if ((drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) { + /* TDLS Discovery Response */ + if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0e", 2) < + 0) + return -1; + } +#endif /* CONFIG_TDLS */ + /* FT Action frames */ - if (nl80211_register_action_frame(drv, (u8 *) "\x06", 1) < 0) + if (nl80211_register_action_frame(bss, (u8 *) "\x06", 1) < 0) return -1; else drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT | WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK; + /* WNM - BSS Transition Management Request */ + if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x07", 2) < 0) + return -1; + /* WNM-Sleep Mode Response */ + if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x11", 2) < 0) + return -1; + return 0; } +static int nl80211_register_spurious_class3(struct i802_bss *bss) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret = -1; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_UNEXPECTED_FRAME); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); + + ret = send_and_recv(drv->global, bss->nl_mgmt, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Register spurious class3 " + "failed: ret=%d (%s)", + ret, strerror(-ret)); + goto nla_put_failure; + } + ret = 0; +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +static int nl80211_mgmt_subscribe_ap(struct i802_bss *bss) +{ + static const int stypes[] = { + WLAN_FC_STYPE_AUTH, + WLAN_FC_STYPE_ASSOC_REQ, + WLAN_FC_STYPE_REASSOC_REQ, + WLAN_FC_STYPE_DISASSOC, + WLAN_FC_STYPE_DEAUTH, + WLAN_FC_STYPE_ACTION, + WLAN_FC_STYPE_PROBE_REQ, +/* Beacon doesn't work as mac80211 doesn't currently allow + * it, but it wouldn't really be the right thing anyway as + * it isn't per interface ... maybe just dump the scan + * results periodically for OLBC? + */ +// WLAN_FC_STYPE_BEACON, + }; + unsigned int i; + + if (nl80211_alloc_mgmt_handle(bss)) + return -1; + wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP " + "handle %p", bss->nl_mgmt); + + for (i = 0; i < sizeof(stypes) / sizeof(stypes[0]); i++) { + if (nl80211_register_frame(bss, bss->nl_mgmt, + (WLAN_FC_TYPE_MGMT << 2) | + (stypes[i] << 4), + NULL, 0) < 0) { + goto out_err; + } + } + + if (nl80211_register_spurious_class3(bss)) + goto out_err; + + if (nl80211_get_wiphy_data_ap(bss) == NULL) + goto out_err; + + return 0; + +out_err: + eloop_unregister_read_sock(nl_socket_get_fd(bss->nl_mgmt)); + nl_destroy_handles(&bss->nl_mgmt); + return -1; +} + + +static int nl80211_mgmt_subscribe_ap_dev_sme(struct i802_bss *bss) +{ + if (nl80211_alloc_mgmt_handle(bss)) + return -1; + wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP " + "handle %p (device SME)", bss->nl_mgmt); + + if (nl80211_register_frame(bss, bss->nl_mgmt, + (WLAN_FC_TYPE_MGMT << 2) | + (WLAN_FC_STYPE_ACTION << 4), + NULL, 0) < 0) + goto out_err; + + return 0; + +out_err: + eloop_unregister_read_sock(nl_socket_get_fd(bss->nl_mgmt)); + nl_destroy_handles(&bss->nl_mgmt); + return -1; +} + + +static void nl80211_mgmt_unsubscribe(struct i802_bss *bss, const char *reason) +{ + if (bss->nl_mgmt == NULL) + return; + wpa_printf(MSG_DEBUG, "nl80211: Unsubscribe mgmt frames handle %p " + "(%s)", bss->nl_mgmt, reason); + eloop_unregister_read_sock(nl_socket_get_fd(bss->nl_mgmt)); + nl_destroy_handles(&bss->nl_mgmt); + + nl80211_put_wiphy_data_ap(bss); +} + + +static void wpa_driver_nl80211_send_rfkill(void *eloop_ctx, void *timeout_ctx) +{ + wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL); +} + + static int wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv) { struct i802_bss *bss = &drv->first_bss; + int send_rfkill_event = 0; drv->ifindex = if_nametoindex(bss->ifname); drv->first_bss.ifindex = drv->ifindex; #ifndef HOSTAPD - if (wpa_driver_nl80211_set_mode(bss, IEEE80211_MODE_INFRA) < 0) { - wpa_printf(MSG_DEBUG, "nl80211: Could not configure driver to " + /* + * Make sure the interface starts up in station mode unless this is a + * dynamically added interface (e.g., P2P) that was already configured + * with proper iftype. + */ + if (drv->ifindex != drv->global->if_add_ifindex && + wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Could not configure driver to " "use managed mode"); + return -1; } - if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) { - wpa_printf(MSG_ERROR, "Could not set interface '%s' UP", - bss->ifname); - return -1; + if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1)) { + if (rfkill_is_blocked(drv->rfkill)) { + wpa_printf(MSG_DEBUG, "nl80211: Could not yet enable " + "interface '%s' due to rfkill", + bss->ifname); + drv->if_disabled = 1; + send_rfkill_event = 1; + } else { + wpa_printf(MSG_ERROR, "nl80211: Could not set " + "interface '%s' UP", bss->ifname); + return -1; + } } + netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, + 1, IF_OPER_DORMANT); +#endif /* HOSTAPD */ + if (wpa_driver_nl80211_capa(drv)) return -1; - netlink_send_oper_ifla(drv->netlink, drv->ifindex, - 1, IF_OPER_DORMANT); -#endif /* HOSTAPD */ + if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, + bss->addr)) + return -1; - if (nl80211_register_action_frames(drv) < 0) { - wpa_printf(MSG_DEBUG, "nl80211: Failed to register Action " - "frame processing - ignore for now"); - /* - * Older kernel versions did not support this, so ignore the - * error for now. Some functionality may not be available - * because of this. - */ + if (send_rfkill_event) { + eloop_register_timeout(0, 0, wpa_driver_nl80211_send_rfkill, + drv, drv->ctx); } return 0; @@ -1523,12 +3469,12 @@ static int wpa_driver_nl80211_del_beacon(struct wpa_driver_nl80211_data *drv) if (!msg) return -ENOMEM; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_DEL_BEACON, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_BEACON); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); return send_and_recv_msgs(drv, msg, NULL, NULL); nla_put_failure: + nlmsg_free(msg); return -ENOBUFS; } @@ -1545,23 +3491,31 @@ static void wpa_driver_nl80211_deinit(void *priv) struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - if (drv->added_if_into_bridge) { - if (linux_br_del_if(drv->ioctl_sock, drv->brname, bss->ifname) - < 0) + bss->in_deinit = 1; + if (drv->data_tx_status) + eloop_unregister_read_sock(drv->eapol_tx_sock); + if (drv->eapol_tx_sock >= 0) + close(drv->eapol_tx_sock); + + if (bss->nl_preq) + wpa_driver_nl80211_probe_req_report(bss, 0); + if (bss->added_if_into_bridge) { + if (linux_br_del_if(drv->global->ioctl_sock, bss->brname, + bss->ifname) < 0) wpa_printf(MSG_INFO, "nl80211: Failed to remove " "interface %s from bridge %s: %s", - bss->ifname, drv->brname, strerror(errno)); + bss->ifname, bss->brname, strerror(errno)); } - if (drv->added_bridge) { - if (linux_br_del(drv->ioctl_sock, drv->brname) < 0) + if (bss->added_bridge) { + if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0) wpa_printf(MSG_INFO, "nl80211: Failed to remove " "bridge %s: %s", - drv->brname, strerror(errno)); + bss->brname, strerror(errno)); } nl80211_remove_monitor_interface(drv); - if (drv->nlmode == NL80211_IFTYPE_AP) + if (is_ap_interface(drv->nlmode)) wpa_driver_nl80211_del_beacon(drv); #ifdef HOSTAPD @@ -1582,33 +3536,30 @@ static void wpa_driver_nl80211_deinit(void *priv) os_free(drv->if_indices); #endif /* HOSTAPD */ - if (drv->disable_11b_rates) + if (drv->disabled_11b_rates) nl80211_disable_11b_rates(drv, drv->ifindex, 0); - netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP); - netlink_deinit(drv->netlink); + netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, 0, + IF_OPER_UP); + rfkill_deinit(drv->rfkill); eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); - (void) linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0); - wpa_driver_nl80211_set_mode(bss, IEEE80211_MODE_INFRA); - - if (drv->ioctl_sock >= 0) - close(drv->ioctl_sock); + (void) linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0); + wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION); + nl80211_mgmt_unsubscribe(bss, "deinit"); - eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle_event)); - genl_family_put(drv->nl80211); - nl_cache_free(drv->nl_cache); - nl_cache_free(drv->nl_cache_event); - nl_handle_destroy(drv->nl_handle); - nl_handle_destroy(drv->nl_handle_event); nl_cb_put(drv->nl_cb); - eloop_cancel_timeout(wpa_driver_nl80211_probe_req_report_timeout, - drv, NULL); + nl80211_destroy_bss(&drv->first_bss); os_free(drv->filter_ssids); + os_free(drv->auth_ie); + + if (drv->in_interface_list) + dl_list_del(&drv->list); + os_free(drv); } @@ -1624,39 +3575,78 @@ static void wpa_driver_nl80211_deinit(void *priv) static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_driver_nl80211_data *drv = eloop_ctx; - if (drv->ap_scan_as_station) { + if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED) { wpa_driver_nl80211_set_mode(&drv->first_bss, - IEEE80211_MODE_AP); - drv->ap_scan_as_station = 0; + drv->ap_scan_as_station); + drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED; } wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); } -/** - * wpa_driver_nl80211_scan - Request the driver to initiate scan - * @priv: Pointer to private driver data from wpa_driver_nl80211_init() - * @params: Scan parameters - * Returns: 0 on success, -1 on failure - */ -static int wpa_driver_nl80211_scan(void *priv, - struct wpa_driver_scan_params *params) +static struct nl_msg * +nl80211_scan_common(struct wpa_driver_nl80211_data *drv, u8 cmd, + struct wpa_driver_scan_params *params) { - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - int ret = 0, timeout; - struct nl_msg *msg, *ssids, *freqs; + struct nl_msg *msg; + int err; size_t i; msg = nlmsg_alloc(); - ssids = nlmsg_alloc(); - freqs = nlmsg_alloc(); - if (!msg || !ssids || !freqs) { - nlmsg_free(msg); + if (!msg) + return NULL; + + nl80211_cmd(drv, msg, 0, cmd); + + if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, drv->ifindex) < 0) + goto fail; + + if (params->num_ssids) { + struct nl_msg *ssids = nlmsg_alloc(); + if (ssids == NULL) + goto fail; + for (i = 0; i < params->num_ssids; i++) { + wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID", + params->ssids[i].ssid, + params->ssids[i].ssid_len); + if (nla_put(ssids, i + 1, params->ssids[i].ssid_len, + params->ssids[i].ssid) < 0) { + nlmsg_free(ssids); + goto fail; + } + } + err = nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids); nlmsg_free(ssids); + if (err < 0) + goto fail; + } + + if (params->extra_ies) { + wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs", + params->extra_ies, params->extra_ies_len); + if (nla_put(msg, NL80211_ATTR_IE, params->extra_ies_len, + params->extra_ies) < 0) + goto fail; + } + + if (params->freqs) { + struct nl_msg *freqs = nlmsg_alloc(); + if (freqs == NULL) + goto fail; + for (i = 0; params->freqs[i]; i++) { + wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u " + "MHz", params->freqs[i]); + if (nla_put_u32(freqs, i + 1, params->freqs[i]) < 0) { + nlmsg_free(freqs); + goto fail; + } + } + err = nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, + freqs); nlmsg_free(freqs); - return -1; + if (err < 0) + goto fail; } os_free(drv->filter_ssids); @@ -1664,35 +3654,54 @@ static int wpa_driver_nl80211_scan(void *priv, params->filter_ssids = NULL; drv->num_filter_ssids = params->num_filter_ssids; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_TRIGGER_SCAN, 0); + return msg; - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); +fail: + nlmsg_free(msg); + return NULL; +} - for (i = 0; i < params->num_ssids; i++) { - wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID", - params->ssids[i].ssid, - params->ssids[i].ssid_len); - NLA_PUT(ssids, i + 1, params->ssids[i].ssid_len, - params->ssids[i].ssid); - } - if (params->num_ssids) - nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids); - if (params->extra_ies) { - wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan extra IEs", - params->extra_ies, params->extra_ies_len); - NLA_PUT(msg, NL80211_ATTR_IE, params->extra_ies_len, - params->extra_ies); - } +/** + * wpa_driver_nl80211_scan - Request the driver to initiate scan + * @priv: Pointer to private driver data from wpa_driver_nl80211_init() + * @params: Scan parameters + * Returns: 0 on success, -1 on failure + */ +static int wpa_driver_nl80211_scan(void *priv, + struct wpa_driver_scan_params *params) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret = -1, timeout; + struct nl_msg *msg, *rates = NULL; - if (params->freqs) { - for (i = 0; params->freqs[i]; i++) { - wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u " - "MHz", params->freqs[i]); - NLA_PUT_U32(freqs, i + 1, params->freqs[i]); - } - nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs); + drv->scan_for_auth = 0; + + msg = nl80211_scan_common(drv, NL80211_CMD_TRIGGER_SCAN, params); + if (!msg) + return -1; + + if (params->p2p_probe) { + wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates"); + + rates = nlmsg_alloc(); + if (rates == NULL) + goto nla_put_failure; + + /* + * 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, + * 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS rates. All 5 GHz + * rates are left enabled. + */ + NLA_PUT(rates, NL80211_BAND_2GHZ, 8, + "\x0c\x12\x18\x24\x30\x48\x60\x6c"); + if (nla_put_nested(msg, NL80211_ATTR_SCAN_SUPP_RATES, rates) < + 0) + goto nla_put_failure; + + NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE); } ret = send_and_recv_msgs(drv, msg, NULL, NULL); @@ -1701,23 +3710,22 @@ static int wpa_driver_nl80211_scan(void *priv, wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d " "(%s)", ret, strerror(-ret)); #ifdef HOSTAPD - if (drv->nlmode == NL80211_IFTYPE_AP) { + if (is_ap_interface(drv->nlmode)) { /* * mac80211 does not allow scan requests in AP mode, so * try to do this in station mode. */ - if (wpa_driver_nl80211_set_mode(bss, - IEEE80211_MODE_INFRA)) + if (wpa_driver_nl80211_set_mode( + bss, NL80211_IFTYPE_STATION)) goto nla_put_failure; if (wpa_driver_nl80211_scan(drv, params)) { - wpa_driver_nl80211_set_mode(bss, - IEEE80211_MODE_AP); + wpa_driver_nl80211_set_mode(bss, drv->nlmode); goto nla_put_failure; } /* Restore AP mode when processing scan results */ - drv->ap_scan_as_station = 1; + drv->ap_scan_as_station = drv->nlmode; ret = 0; } else goto nla_put_failure; @@ -1744,9 +3752,147 @@ static int wpa_driver_nl80211_scan(void *priv, drv, drv->ctx); nla_put_failure: - nlmsg_free(ssids); nlmsg_free(msg); - nlmsg_free(freqs); + nlmsg_free(rates); + return ret; +} + + +/** + * wpa_driver_nl80211_sched_scan - Initiate a scheduled scan + * @priv: Pointer to private driver data from wpa_driver_nl80211_init() + * @params: Scan parameters + * @interval: Interval between scan cycles in milliseconds + * Returns: 0 on success, -1 on failure or if not supported + */ +static int wpa_driver_nl80211_sched_scan(void *priv, + struct wpa_driver_scan_params *params, + u32 interval) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret = -1; + struct nl_msg *msg; + struct nl_msg *match_set_ssid = NULL, *match_sets = NULL; + struct nl_msg *match_set_rssi = NULL; + size_t i; + +#ifdef ANDROID + if (!drv->capa.sched_scan_supported) + return android_pno_start(bss, params); +#endif /* ANDROID */ + + msg = nl80211_scan_common(drv, NL80211_CMD_START_SCHED_SCAN, params); + if (!msg) + goto nla_put_failure; + + NLA_PUT_U32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, interval); + + if ((drv->num_filter_ssids && + (int) drv->num_filter_ssids <= drv->capa.max_match_sets) || + params->filter_rssi) { + match_sets = nlmsg_alloc(); + if (match_sets == NULL) + goto nla_put_failure; + + for (i = 0; i < drv->num_filter_ssids; i++) { + wpa_hexdump_ascii(MSG_MSGDUMP, + "nl80211: Sched scan filter SSID", + drv->filter_ssids[i].ssid, + drv->filter_ssids[i].ssid_len); + + match_set_ssid = nlmsg_alloc(); + if (match_set_ssid == NULL) + goto nla_put_failure; + NLA_PUT(match_set_ssid, + NL80211_ATTR_SCHED_SCAN_MATCH_SSID, + drv->filter_ssids[i].ssid_len, + drv->filter_ssids[i].ssid); + + if (nla_put_nested(match_sets, i + 1, match_set_ssid) < + 0) + goto nla_put_failure; + } + + if (params->filter_rssi) { + match_set_rssi = nlmsg_alloc(); + if (match_set_rssi == NULL) + goto nla_put_failure; + NLA_PUT_U32(match_set_rssi, + NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, + params->filter_rssi); + wpa_printf(MSG_MSGDUMP, + "nl80211: Sched scan RSSI filter %d dBm", + params->filter_rssi); + if (nla_put_nested(match_sets, 0, match_set_rssi) < 0) + goto nla_put_failure; + } + + if (nla_put_nested(msg, NL80211_ATTR_SCHED_SCAN_MATCH, + match_sets) < 0) + goto nla_put_failure; + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + + /* TODO: if we get an error here, we should fall back to normal scan */ + + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Sched scan start failed: " + "ret=%d (%s)", ret, strerror(-ret)); + goto nla_put_failure; + } + + wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d) - " + "scan interval %d msec", ret, interval); + +nla_put_failure: + nlmsg_free(match_set_ssid); + nlmsg_free(match_sets); + nlmsg_free(match_set_rssi); + nlmsg_free(msg); + return ret; +} + + +/** + * wpa_driver_nl80211_stop_sched_scan - Stop a scheduled scan + * @priv: Pointer to private driver data from wpa_driver_nl80211_init() + * Returns: 0 on success, -1 on failure or if not supported + */ +static int wpa_driver_nl80211_stop_sched_scan(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret = 0; + struct nl_msg *msg; + +#ifdef ANDROID + if (!drv->capa.sched_scan_supported) + return android_pno_stop(bss); +#endif /* ANDROID */ + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_STOP_SCHED_SCAN); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Sched scan stop failed: " + "ret=%d (%s)", ret, strerror(-ret)); + goto nla_put_failure; + } + + wpa_printf(MSG_DEBUG, "nl80211: Sched scan stop sent (ret=%d)", ret); + +nla_put_failure: + nlmsg_free(msg); return ret; } @@ -1797,11 +3943,6 @@ static int nl80211_scan_filtered(struct wpa_driver_nl80211_data *drv, } -struct nl80211_bss_info_arg { - struct wpa_driver_nl80211_data *drv; - struct wpa_scan_results *res; -}; - static int bss_info_handler(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; @@ -1827,6 +3968,7 @@ static int bss_info_handler(struct nl_msg *msg, void *arg) const u8 *ie, *beacon_ie; size_t ie_len, beacon_ie_len; u8 *pos; + size_t i; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); @@ -1835,6 +3977,26 @@ static int bss_info_handler(struct nl_msg *msg, void *arg) if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], bss_policy)) return NL_SKIP; + if (bss[NL80211_BSS_STATUS]) { + enum nl80211_bss_status status; + status = nla_get_u32(bss[NL80211_BSS_STATUS]); + if (status == NL80211_BSS_STATUS_ASSOCIATED && + bss[NL80211_BSS_FREQUENCY]) { + _arg->assoc_freq = + nla_get_u32(bss[NL80211_BSS_FREQUENCY]); + wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz", + _arg->assoc_freq); + } + if (status == NL80211_BSS_STATUS_ASSOCIATED && + bss[NL80211_BSS_BSSID]) { + os_memcpy(_arg->assoc_bssid, + nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN); + wpa_printf(MSG_DEBUG, "nl80211: Associated with " + MACSTR, MAC2STR(_arg->assoc_bssid)); + } + } + if (!res) + return NL_SKIP; if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) { ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]); ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]); @@ -1873,7 +4035,7 @@ static int bss_info_handler(struct nl_msg *msg, void *arg) r->flags |= WPA_SCAN_LEVEL_DBM | WPA_SCAN_QUAL_INVALID; } else if (bss[NL80211_BSS_SIGNAL_UNSPEC]) { r->level = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]); - r->flags |= WPA_SCAN_LEVEL_INVALID; + r->flags |= WPA_SCAN_QUAL_INVALID; } else r->flags |= WPA_SCAN_LEVEL_INVALID | WPA_SCAN_QUAL_INVALID; if (bss[NL80211_BSS_TSF]) @@ -1905,8 +4067,40 @@ static int bss_info_handler(struct nl_msg *msg, void *arg) } } - tmp = os_realloc(res->res, - (res->num + 1) * sizeof(struct wpa_scan_res *)); + /* + * cfg80211 maintains separate BSS table entries for APs if the same + * BSSID,SSID pair is seen on multiple channels. wpa_supplicant does + * not use frequency as a separate key in the BSS table, so filter out + * duplicated entries. Prefer associated BSS entry in such a case in + * order to get the correct frequency into the BSS table. + */ + for (i = 0; i < res->num; i++) { + const u8 *s1, *s2; + if (os_memcmp(res->res[i]->bssid, r->bssid, ETH_ALEN) != 0) + continue; + + s1 = nl80211_get_ie((u8 *) (res->res[i] + 1), + res->res[i]->ie_len, WLAN_EID_SSID); + s2 = nl80211_get_ie((u8 *) (r + 1), r->ie_len, WLAN_EID_SSID); + if (s1 == NULL || s2 == NULL || s1[1] != s2[1] || + os_memcmp(s1, s2, 2 + s1[1]) != 0) + continue; + + /* Same BSSID,SSID was already included in scan results */ + wpa_printf(MSG_DEBUG, "nl80211: Remove duplicated scan result " + "for " MACSTR, MAC2STR(r->bssid)); + + if ((r->flags & WPA_SCAN_ASSOCIATED) && + !(res->res[i]->flags & WPA_SCAN_ASSOCIATED)) { + os_free(res->res[i]); + res->res[i] = r; + } else + os_free(r); + return NL_SKIP; + } + + tmp = os_realloc_array(res->res, res->num + 1, + sizeof(struct wpa_scan_res *)); if (tmp == NULL) { os_free(r); return NL_SKIP; @@ -1943,7 +4137,7 @@ static void wpa_driver_nl80211_check_bss_status( "indicates BSS status with " MACSTR " as authenticated", MAC2STR(r->bssid)); - if (drv->nlmode == NL80211_IFTYPE_STATION && + if (is_sta_interface(drv->nlmode) && os_memcmp(r->bssid, drv->bssid, ETH_ALEN) != 0 && os_memcmp(r->bssid, drv->auth_bssid, ETH_ALEN) != 0) { @@ -1961,13 +4155,13 @@ static void wpa_driver_nl80211_check_bss_status( "indicate BSS status with " MACSTR " as associated", MAC2STR(r->bssid)); - if (drv->nlmode == NL80211_IFTYPE_STATION && + if (is_sta_interface(drv->nlmode) && !drv->associated) { wpa_printf(MSG_DEBUG, "nl80211: Local state " "(not associated) does not match " "with BSS state"); clear_state_mismatch(drv, r->bssid); - } else if (drv->nlmode == NL80211_IFTYPE_STATION && + } else if (is_sta_interface(drv->nlmode) && os_memcmp(drv->bssid, r->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, "nl80211: Local state " @@ -1982,20 +4176,6 @@ static void wpa_driver_nl80211_check_bss_status( } -static void wpa_scan_results_free(struct wpa_scan_results *res) -{ - size_t i; - - if (res == NULL) - return; - - for (i = 0; i < res->num; i++) - os_free(res->res[i]); - os_free(res->res); - os_free(res); -} - - static struct wpa_scan_results * nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv) { @@ -2011,8 +4191,7 @@ nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv) if (!msg) goto nla_put_failure; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, NLM_F_DUMP, - NL80211_CMD_GET_SCAN, 0); + nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); arg.drv = drv; @@ -2020,8 +4199,9 @@ nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv) ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg); msg = NULL; if (ret == 0) { - wpa_printf(MSG_DEBUG, "Received scan results (%lu BSSes)", - (unsigned long) res->num); + wpa_printf(MSG_DEBUG, "nl80211: Received scan results (%lu " + "BSSes)", (unsigned long) res->num); + nl80211_get_noise_for_scan_results(drv, res); return res; } wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d " @@ -2092,17 +4272,19 @@ static int wpa_driver_nl80211_set_key(const char *ifname, void *priv, "set_tx=%d seq_len=%lu key_len=%lu", __func__, ifindex, alg, addr, key_idx, set_tx, (unsigned long) seq_len, (unsigned long) key_len); +#ifdef CONFIG_TDLS + if (key_idx == -1) + key_idx = 0; +#endif /* CONFIG_TDLS */ msg = nlmsg_alloc(); if (!msg) return -ENOMEM; if (alg == WPA_ALG_NONE) { - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_DEL_KEY, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_KEY); } else { - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_NEW_KEY, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_NEW_KEY); NLA_PUT(msg, NL80211_ATTR_KEY_DATA, key_len, key); switch (alg) { case WPA_ALG_WEP: @@ -2121,10 +4303,22 @@ static int wpa_driver_nl80211_set_key(const char *ifname, void *priv, NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, WLAN_CIPHER_SUITE_CCMP); break; + case WPA_ALG_GCMP: + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, + WLAN_CIPHER_SUITE_GCMP); + break; case WPA_ALG_IGTK: NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, WLAN_CIPHER_SUITE_AES_CMAC); break; + case WPA_ALG_SMS4: + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, + WLAN_CIPHER_SUITE_SMS4); + break; + case WPA_ALG_KRK: + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, + WLAN_CIPHER_SUITE_KRK); + break; default: wpa_printf(MSG_ERROR, "%s: Unsupported encryption " "algorithm %d", __func__, alg); @@ -2136,10 +4330,28 @@ static int wpa_driver_nl80211_set_key(const char *ifname, void *priv, if (seq && seq_len) NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, seq_len, seq); - if (addr && os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0) - { + if (addr && !is_broadcast_ether_addr(addr)) { wpa_printf(MSG_DEBUG, " addr=" MACSTR, MAC2STR(addr)); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + + if (alg != WPA_ALG_WEP && key_idx && !set_tx) { + wpa_printf(MSG_DEBUG, " RSN IBSS RX GTK"); + NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, + NL80211_KEYTYPE_GROUP); + } + } else if (addr && is_broadcast_ether_addr(addr)) { + struct nl_msg *types; + int err; + wpa_printf(MSG_DEBUG, " broadcast key"); + types = nlmsg_alloc(); + if (!types) + goto nla_put_failure; + NLA_PUT_FLAG(types, NL80211_KEY_DEFAULT_TYPE_MULTICAST); + err = nla_put_nested(msg, NL80211_ATTR_KEY_DEFAULT_TYPES, + types); + nlmsg_free(types); + if (err) + goto nla_put_failure; } NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); @@ -2157,26 +4369,46 @@ static int wpa_driver_nl80211_set_key(const char *ifname, void *priv, */ if (ret || !set_tx || alg == WPA_ALG_NONE) return ret; -#ifdef HOSTAPD - if (addr) - return ret; -#else /* HOSTAPD */ - if (drv->nlmode == NL80211_IFTYPE_AP && addr) + if (is_ap_interface(drv->nlmode) && addr && + !is_broadcast_ether_addr(addr)) return ret; -#endif /* HOSTAPD */ msg = nlmsg_alloc(); if (!msg) return -ENOMEM; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_SET_KEY, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_KEY); NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); if (alg == WPA_ALG_IGTK) NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT_MGMT); else NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT); + if (addr && is_broadcast_ether_addr(addr)) { + struct nl_msg *types; + int err; + types = nlmsg_alloc(); + if (!types) + goto nla_put_failure; + NLA_PUT_FLAG(types, NL80211_KEY_DEFAULT_TYPE_MULTICAST); + err = nla_put_nested(msg, NL80211_ATTR_KEY_DEFAULT_TYPES, + types); + nlmsg_free(types); + if (err) + goto nla_put_failure; + } else if (addr) { + struct nl_msg *types; + int err; + types = nlmsg_alloc(); + if (!types) + goto nla_put_failure; + NLA_PUT_FLAG(types, NL80211_KEY_DEFAULT_TYPE_UNICAST); + err = nla_put_nested(msg, NL80211_ATTR_KEY_DEFAULT_TYPES, + types); + nlmsg_free(types); + if (err) + goto nla_put_failure; + } ret = send_and_recv_msgs(drv, msg, NULL, NULL); if (ret == -ENOENT) @@ -2187,6 +4419,7 @@ static int wpa_driver_nl80211_set_key(const char *ifname, void *priv, return ret; nla_put_failure: + nlmsg_free(msg); return -ENOBUFS; } @@ -2222,6 +4455,9 @@ static int nl_add_key(struct nl_msg *msg, enum wpa_alg alg, case WPA_ALG_CCMP: NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_CCMP); break; + case WPA_ALG_GCMP: + NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_GCMP); + break; case WPA_ALG_IGTK: NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_AES_CMAC); @@ -2257,6 +4493,12 @@ static int nl80211_set_conn_keys(struct wpa_driver_associate_params *params, privacy = 1; break; } + if (params->wps == WPS_MODE_PRIVACY) + privacy = 1; + if (params->pairwise_suite && + params->pairwise_suite != WPA_CIPHER_NONE) + privacy = 1; + if (!privacy) return 0; @@ -2310,19 +4552,21 @@ static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, if (!msg) return -1; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, cmd, 0); + nl80211_cmd(drv, msg, 0, cmd); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason_code); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + if (addr) + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); if (local_state_change) NLA_PUT_FLAG(msg, NL80211_ATTR_LOCAL_STATE_CHANGE); ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: MLME command failed: ret=%d " - "(%s)", ret, strerror(-ret)); + wpa_dbg(drv->ctx, MSG_DEBUG, + "nl80211: MLME command failed: reason=%u ret=%d (%s)", + reason_code, ret, strerror(-ret)); goto nla_put_failure; } ret = 0; @@ -2334,11 +4578,13 @@ nla_put_failure: static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv, - const u8 *addr, int reason_code) + int reason_code) { - wpa_printf(MSG_DEBUG, "%s", __func__); + wpa_printf(MSG_DEBUG, "%s(reason_code=%d)", __func__, reason_code); drv->associated = 0; - return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DISCONNECT, + drv->ignore_next_local_disconnect = 0; + /* Disconnect command doesn't need BSSID - it uses cached value */ + return wpa_driver_nl80211_mlme(drv, NULL, NL80211_CMD_DISCONNECT, reason_code, 0); } @@ -2349,25 +4595,60 @@ static int wpa_driver_nl80211_deauthenticate(void *priv, const u8 *addr, struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) - return wpa_driver_nl80211_disconnect(drv, addr, reason_code); - wpa_printf(MSG_DEBUG, "%s", __func__); + return wpa_driver_nl80211_disconnect(drv, reason_code); + wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)", + __func__, MAC2STR(addr), reason_code); drv->associated = 0; + if (drv->nlmode == NL80211_IFTYPE_ADHOC) + return nl80211_leave_ibss(drv); return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE, reason_code, 0); } -static int wpa_driver_nl80211_disassociate(void *priv, const u8 *addr, - int reason_code) +static void nl80211_copy_auth_params(struct wpa_driver_nl80211_data *drv, + struct wpa_driver_auth_params *params) { - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) - return wpa_driver_nl80211_disconnect(drv, addr, reason_code); - wpa_printf(MSG_DEBUG, "%s", __func__); - drv->associated = 0; - return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DISASSOCIATE, - reason_code, 0); + int i; + + drv->auth_freq = params->freq; + drv->auth_alg = params->auth_alg; + drv->auth_wep_tx_keyidx = params->wep_tx_keyidx; + drv->auth_local_state_change = params->local_state_change; + drv->auth_p2p = params->p2p; + + if (params->bssid) + os_memcpy(drv->auth_bssid_, params->bssid, ETH_ALEN); + else + os_memset(drv->auth_bssid_, 0, ETH_ALEN); + + if (params->ssid) { + os_memcpy(drv->auth_ssid, params->ssid, params->ssid_len); + drv->auth_ssid_len = params->ssid_len; + } else + drv->auth_ssid_len = 0; + + + os_free(drv->auth_ie); + drv->auth_ie = NULL; + drv->auth_ie_len = 0; + if (params->ie) { + drv->auth_ie = os_malloc(params->ie_len); + if (drv->auth_ie) { + os_memcpy(drv->auth_ie, params->ie, params->ie_len); + drv->auth_ie_len = params->ie_len; + } + } + + for (i = 0; i < 4; i++) { + if (params->wep_key[i] && params->wep_key_len[i] && + params->wep_key_len[i] <= 16) { + os_memcpy(drv->auth_wep_key[i], params->wep_key[i], + params->wep_key_len[i]); + drv->auth_wep_key_len[i] = params->wep_key_len[i]; + } else + drv->auth_wep_key_len[i] = 0; + } } @@ -2379,15 +4660,20 @@ static int wpa_driver_nl80211_authenticate( int ret = -1, i; struct nl_msg *msg; enum nl80211_auth_type type; + enum nl80211_iftype nlmode; int count = 0; + int is_retry; + + is_retry = drv->retry_auth; + drv->retry_auth = 0; drv->associated = 0; os_memset(drv->auth_bssid, 0, ETH_ALEN); /* FIX: IBSS mode */ - if (drv->nlmode != NL80211_IFTYPE_STATION) - wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA); - - if (wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA) < 0) + nlmode = params->p2p ? + NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION; + if (drv->nlmode != nlmode && + wpa_driver_nl80211_set_mode(priv, nlmode) < 0) return -1; retry: @@ -2398,8 +4684,7 @@ retry: wpa_printf(MSG_DEBUG, "nl80211: Authenticate (ifindex=%d)", drv->ifindex); - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_AUTHENTICATE, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_AUTHENTICATE); for (i = 0; i < 4; i++) { if (!params->wep_key[i]) @@ -2437,6 +4722,12 @@ retry: wpa_hexdump(MSG_DEBUG, " * IEs", params->ie, params->ie_len); if (params->ie) NLA_PUT(msg, NL80211_ATTR_IE, params->ie_len, params->ie); + if (params->sae_data) { + wpa_hexdump(MSG_DEBUG, " * SAE data", params->sae_data, + params->sae_data_len); + NLA_PUT(msg, NL80211_ATTR_SAE_DATA, params->sae_data_len, + params->sae_data); + } if (params->auth_alg & WPA_AUTH_ALG_OPEN) type = NL80211_AUTHTYPE_OPEN_SYSTEM; else if (params->auth_alg & WPA_AUTH_ALG_SHARED) @@ -2445,6 +4736,8 @@ retry: type = NL80211_AUTHTYPE_NETWORK_EAP; else if (params->auth_alg & WPA_AUTH_ALG_FT) type = NL80211_AUTHTYPE_FT; + else if (params->auth_alg & WPA_AUTH_ALG_SAE) + type = NL80211_AUTHTYPE_SAE; else goto nla_put_failure; wpa_printf(MSG_DEBUG, " * Auth Type %d", type); @@ -2457,8 +4750,9 @@ retry: ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: MLME command failed: ret=%d " - "(%s)", ret, strerror(-ret)); + wpa_dbg(drv->ctx, MSG_DEBUG, + "nl80211: MLME command failed (auth): ret=%d (%s)", + ret, strerror(-ret)); count++; if (ret == -EALREADY && count == 1 && params->bssid && !params->local_state_change) { @@ -2475,6 +4769,49 @@ retry: nlmsg_free(msg); goto retry; } + + if (ret == -ENOENT && params->freq && !is_retry) { + /* + * cfg80211 has likely expired the BSS entry even + * though it was previously available in our internal + * BSS table. To recover quickly, start a single + * channel scan on the specified channel. + */ + struct wpa_driver_scan_params scan; + int freqs[2]; + + os_memset(&scan, 0, sizeof(scan)); + scan.num_ssids = 1; + if (params->ssid) { + scan.ssids[0].ssid = params->ssid; + scan.ssids[0].ssid_len = params->ssid_len; + } + freqs[0] = params->freq; + freqs[1] = 0; + scan.freqs = freqs; + wpa_printf(MSG_DEBUG, "nl80211: Trigger single " + "channel scan to refresh cfg80211 BSS " + "entry"); + ret = wpa_driver_nl80211_scan(bss, &scan); + if (ret == 0) { + nl80211_copy_auth_params(drv, params); + drv->scan_for_auth = 1; + } + } else if (is_retry) { + /* + * Need to indicate this with an event since the return + * value from the retry is not delivered to core code. + */ + union wpa_event_data event; + wpa_printf(MSG_DEBUG, "nl80211: Authentication retry " + "failed"); + os_memset(&event, 0, sizeof(event)); + os_memcpy(event.timeout_event.addr, drv->auth_bssid_, + ETH_ALEN); + wpa_supplicant_event(drv->ctx, EVENT_AUTH_TIMED_OUT, + &event); + } + goto nla_put_failure; } ret = 0; @@ -2487,6 +4824,45 @@ nla_put_failure: } +static int wpa_driver_nl80211_authenticate_retry( + struct wpa_driver_nl80211_data *drv) +{ + struct wpa_driver_auth_params params; + struct i802_bss *bss = &drv->first_bss; + int i; + + wpa_printf(MSG_DEBUG, "nl80211: Try to authenticate again"); + + os_memset(¶ms, 0, sizeof(params)); + params.freq = drv->auth_freq; + params.auth_alg = drv->auth_alg; + params.wep_tx_keyidx = drv->auth_wep_tx_keyidx; + params.local_state_change = drv->auth_local_state_change; + params.p2p = drv->auth_p2p; + + if (!is_zero_ether_addr(drv->auth_bssid_)) + params.bssid = drv->auth_bssid_; + + if (drv->auth_ssid_len) { + params.ssid = drv->auth_ssid; + params.ssid_len = drv->auth_ssid_len; + } + + params.ie = drv->auth_ie; + params.ie_len = drv->auth_ie_len; + + for (i = 0; i < 4; i++) { + if (drv->auth_wep_key_len[i]) { + params.wep_key[i] = drv->auth_wep_key[i]; + params.wep_key_len[i] = drv->auth_wep_key_len[i]; + } + } + + drv->retry_auth = 1; + return wpa_driver_nl80211_authenticate(bss, ¶ms); +} + + struct phy_info_arg { u16 *num_modes; struct hostapd_hw_modes *modes; @@ -2530,7 +4906,9 @@ static int phy_info_handler(struct nl_msg *msg, void *arg) return NL_SKIP; nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) { - mode = os_realloc(phy_info->modes, (*phy_info->num_modes + 1) * sizeof(*mode)); + mode = os_realloc_array(phy_info->modes, + *phy_info->num_modes + 1, + sizeof(*mode)); if (!mode) return NL_SKIP; phy_info->modes = mode; @@ -2539,6 +4917,7 @@ static int phy_info_handler(struct nl_msg *msg, void *arg) mode = &phy_info->modes[*(phy_info->num_modes)]; memset(mode, 0, sizeof(*mode)); + mode->flags = HOSTAPD_MODE_FLAG_HT_INFO_KNOWN; *(phy_info->num_modes) += 1; nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band), @@ -2568,6 +4947,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); @@ -2576,7 +4967,8 @@ static int phy_info_handler(struct nl_msg *msg, void *arg) mode->num_channels++; } - mode->channels = os_zalloc(mode->num_channels * sizeof(struct hostapd_channel_data)); + mode->channels = os_calloc(mode->num_channels, + sizeof(struct hostapd_channel_data)); if (!mode->channels) return NL_SKIP; @@ -2595,19 +4987,35 @@ static int phy_info_handler(struct nl_msg *msg, void *arg) /* crude heuristic */ if (mode->channels[idx].freq < 4000) mode->mode = HOSTAPD_MODE_IEEE80211B; + else if (mode->channels[idx].freq > 50000) + mode->mode = HOSTAPD_MODE_IEEE80211AD; else mode->mode = HOSTAPD_MODE_IEEE80211A; mode_is_set = 1; } - /* crude heuristic */ - if (mode->channels[idx].freq < 4000) + switch (mode->mode) { + case HOSTAPD_MODE_IEEE80211AD: + mode->channels[idx].chan = + (mode->channels[idx].freq - 56160) / + 2160; + break; + case HOSTAPD_MODE_IEEE80211A: + mode->channels[idx].chan = + mode->channels[idx].freq / 5 - 1000; + break; + case HOSTAPD_MODE_IEEE80211B: + case HOSTAPD_MODE_IEEE80211G: if (mode->channels[idx].freq == 2484) mode->channels[idx].chan = 14; else - mode->channels[idx].chan = (mode->channels[idx].freq - 2407) / 5; - else - mode->channels[idx].chan = mode->channels[idx].freq/5 - 1000; + mode->channels[idx].chan = + (mode->channels[idx].freq - + 2407) / 5; + break; + default: + break; + } if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) mode->channels[idx].flag |= @@ -2638,7 +5046,7 @@ static int phy_info_handler(struct nl_msg *msg, void *arg) mode->num_rates++; } - mode->rates = os_zalloc(mode->num_rates * sizeof(int)); + mode->rates = os_calloc(mode->num_rates, sizeof(int)); if (!mode->rates) return NL_SKIP; @@ -2683,7 +5091,7 @@ wpa_driver_nl80211_add_11b(struct hostapd_hw_modes *modes, u16 *num_modes) if (mode11g_idx < 0) return modes; /* 2.4 GHz band not supported at all */ - nmodes = os_realloc(modes, (*num_modes + 1) * sizeof(*nmodes)); + nmodes = os_realloc_array(modes, *num_modes + 1, sizeof(*nmodes)); if (nmodes == NULL) return modes; /* Could not add 802.11b mode */ @@ -2737,6 +5145,153 @@ wpa_driver_nl80211_add_11b(struct hostapd_hw_modes *modes, u16 *num_modes) } +static void nl80211_set_ht40_mode(struct hostapd_hw_modes *mode, int start, + int end) +{ + int c; + + for (c = 0; c < mode->num_channels; c++) { + struct hostapd_channel_data *chan = &mode->channels[c]; + if (chan->freq - 10 >= start && chan->freq + 10 <= end) + chan->flag |= HOSTAPD_CHAN_HT40; + } +} + + +static void nl80211_set_ht40_mode_sec(struct hostapd_hw_modes *mode, int start, + int end) +{ + int c; + + for (c = 0; c < mode->num_channels; c++) { + struct hostapd_channel_data *chan = &mode->channels[c]; + if (!(chan->flag & HOSTAPD_CHAN_HT40)) + continue; + if (chan->freq - 30 >= start && chan->freq - 10 <= end) + chan->flag |= HOSTAPD_CHAN_HT40MINUS; + if (chan->freq + 10 >= start && chan->freq + 30 <= end) + chan->flag |= HOSTAPD_CHAN_HT40PLUS; + } +} + + +static void nl80211_reg_rule_ht40(struct nlattr *tb[], + struct phy_info_arg *results) +{ + u32 start, end, max_bw; + u16 m; + + if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL || + tb[NL80211_ATTR_FREQ_RANGE_END] == NULL || + tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL) + return; + + start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000; + end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000; + max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000; + + wpa_printf(MSG_DEBUG, "nl80211: %u-%u @ %u MHz", + start, end, max_bw); + if (max_bw < 40) + return; + + for (m = 0; m < *results->num_modes; m++) { + if (!(results->modes[m].ht_capab & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) + continue; + nl80211_set_ht40_mode(&results->modes[m], start, end); + } +} + + +static void nl80211_reg_rule_sec(struct nlattr *tb[], + struct phy_info_arg *results) +{ + u32 start, end, max_bw; + u16 m; + + if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL || + tb[NL80211_ATTR_FREQ_RANGE_END] == NULL || + tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL) + return; + + start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000; + end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000; + max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000; + + if (max_bw < 20) + return; + + for (m = 0; m < *results->num_modes; m++) { + if (!(results->modes[m].ht_capab & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) + continue; + nl80211_set_ht40_mode_sec(&results->modes[m], start, end); + } +} + + +static int nl80211_get_reg(struct nl_msg *msg, void *arg) +{ + struct phy_info_arg *results = arg; + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *nl_rule; + struct nlattr *tb_rule[NL80211_FREQUENCY_ATTR_MAX + 1]; + int rem_rule; + static struct nla_policy reg_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { + [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 }, + [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 }, + [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 }, + [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 }, + [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 }, + [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 }, + }; + + nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (!tb_msg[NL80211_ATTR_REG_ALPHA2] || + !tb_msg[NL80211_ATTR_REG_RULES]) { + wpa_printf(MSG_DEBUG, "nl80211: No regulatory information " + "available"); + return NL_SKIP; + } + + wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s", + (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2])); + + nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule) + { + nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX, + nla_data(nl_rule), nla_len(nl_rule), reg_policy); + nl80211_reg_rule_ht40(tb_rule, results); + } + + nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule) + { + nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX, + nla_data(nl_rule), nla_len(nl_rule), reg_policy); + nl80211_reg_rule_sec(tb_rule, results); + } + + return NL_SKIP; +} + + +static int nl80211_set_ht40_flags(struct wpa_driver_nl80211_data *drv, + struct phy_info_arg *results) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG); + return send_and_recv_msgs(drv, msg, nl80211_get_reg, results); +} + + static struct hostapd_hw_modes * wpa_driver_nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) { @@ -2755,21 +5310,24 @@ wpa_driver_nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) if (!msg) return NULL; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_GET_WIPHY, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) + if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) { + nl80211_set_ht40_flags(drv, &result); return wpa_driver_nl80211_add_11b(result.modes, num_modes); + } + msg = NULL; nla_put_failure: + nlmsg_free(msg); return NULL; } -static int wpa_driver_nl80211_send_frame(struct wpa_driver_nl80211_data *drv, - const void *data, size_t len, - int encrypt) +static int wpa_driver_nl80211_send_mntr(struct wpa_driver_nl80211_data *drv, + const void *data, size_t len, + int encrypt, int noack) { __u8 rtap_hdr[] = { 0x00, 0x00, /* radiotap version */ @@ -2799,18 +5357,59 @@ static int wpa_driver_nl80211_send_frame(struct wpa_driver_nl80211_data *drv, .msg_controllen = 0, .msg_flags = 0, }; + int res; + u16 txflags = 0; if (encrypt) rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP; - return sendmsg(drv->monitor_sock, &msg, 0); + if (drv->monitor_sock < 0) { + wpa_printf(MSG_DEBUG, "nl80211: No monitor socket available " + "for %s", __func__); + return -1; + } + + if (noack) + txflags |= IEEE80211_RADIOTAP_F_TX_NOACK; + WPA_PUT_LE16(&rtap_hdr[12], txflags); + + res = sendmsg(drv->monitor_sock, &msg, 0); + if (res < 0) { + wpa_printf(MSG_INFO, "nl80211: sendmsg: %s", strerror(errno)); + return -1; + } + return 0; } -static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data, - size_t data_len) +static int wpa_driver_nl80211_send_frame(struct i802_bss *bss, + const void *data, size_t len, + int encrypt, int noack, + unsigned int freq, int no_cck, + int offchanok, unsigned int wait_time) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + u64 cookie; + + if (freq == 0) + freq = bss->freq; + + if (drv->use_monitor) + return wpa_driver_nl80211_send_mntr(drv, data, len, + encrypt, noack); + + return nl80211_send_frame_cmd(bss, freq, wait_time, data, len, + &cookie, no_cck, noack, offchanok); +} + + +static int wpa_driver_nl80211_send_mlme_freq(struct i802_bss *bss, + const u8 *data, + size_t data_len, int noack, + unsigned int freq, int no_cck, + int offchanok, + unsigned int wait_time) { - struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct ieee80211_mgmt *mgmt; int encrypt = 1; @@ -2819,6 +5418,32 @@ static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data, mgmt = (struct ieee80211_mgmt *) data; fc = le_to_host16(mgmt->frame_control); + if (is_sta_interface(drv->nlmode) && + WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_RESP) { + /* + * The use of last_mgmt_freq is a bit of a hack, + * but it works due to the single-threaded nature + * of wpa_supplicant. + */ + if (freq == 0) + freq = drv->last_mgmt_freq; + return nl80211_send_frame_cmd(bss, freq, 0, + data, data_len, NULL, 1, noack, + 1); + } + + if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) { + if (freq == 0) + freq = bss->freq; + 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); + } + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) { /* @@ -2833,14 +5458,68 @@ static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data, encrypt = 0; } - return wpa_driver_nl80211_send_frame(drv, data, data_len, encrypt); + return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt, + noack, freq, no_cck, offchanok, + wait_time); +} + + +static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data, + size_t data_len, int noack) +{ + struct i802_bss *bss = priv; + return wpa_driver_nl80211_send_mlme_freq(bss, data, data_len, noack, + 0, 0, 0, 0); +} + + +static int nl80211_set_bss(struct i802_bss *bss, int cts, int preamble, + int slot, int ht_opmode, int ap_isolate, + int *basic_rates) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_BSS); + + if (cts >= 0) + NLA_PUT_U8(msg, NL80211_ATTR_BSS_CTS_PROT, cts); + if (preamble >= 0) + NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble); + if (slot >= 0) + NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot); + if (ht_opmode >= 0) + NLA_PUT_U16(msg, NL80211_ATTR_BSS_HT_OPMODE, ht_opmode); + if (ap_isolate >= 0) + NLA_PUT_U8(msg, NL80211_ATTR_AP_ISOLATE, ap_isolate); + + if (basic_rates) { + u8 rates[NL80211_MAX_SUPP_RATES]; + u8 rates_len = 0; + int i; + + for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0; + i++) + rates[rates_len++] = basic_rates[i] / 5; + + NLA_PUT(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, rates); + } + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; } -static int wpa_driver_nl80211_set_beacon(void *priv, - const u8 *head, size_t head_len, - const u8 *tail, size_t tail_len, - int dtim_period, int beacon_int) +static int wpa_driver_nl80211_set_ap(void *priv, + struct wpa_driver_ap_params *params) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; @@ -2849,6 +5528,9 @@ static int wpa_driver_nl80211_set_beacon(void *priv, int ret; int beacon_set; int ifindex = if_nametoindex(bss->ifname); + int num_suites; + u32 suites[10]; + u32 ver; beacon_set = bss->beacon_set; @@ -2861,13 +5543,123 @@ static int wpa_driver_nl80211_set_beacon(void *priv, if (beacon_set) cmd = NL80211_CMD_SET_BEACON; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, cmd, 0); - NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, head_len, head); - NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL, tail_len, tail); + nl80211_cmd(drv, msg, 0, cmd); + NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, params->head_len, params->head); + NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL, params->tail_len, params->tail); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, beacon_int); - NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, dtim_period); + NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, params->beacon_int); + NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, params->dtim_period); + NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len, + params->ssid); + if (params->proberesp && params->proberesp_len) + NLA_PUT(msg, NL80211_ATTR_PROBE_RESP, params->proberesp_len, + params->proberesp); + switch (params->hide_ssid) { + case NO_SSID_HIDING: + NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID, + NL80211_HIDDEN_SSID_NOT_IN_USE); + break; + case HIDDEN_SSID_ZERO_LEN: + NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID, + NL80211_HIDDEN_SSID_ZERO_LEN); + break; + case HIDDEN_SSID_ZERO_CONTENTS: + NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID, + NL80211_HIDDEN_SSID_ZERO_CONTENTS); + break; + } + if (params->privacy) + NLA_PUT_FLAG(msg, NL80211_ATTR_PRIVACY); + if ((params->auth_algs & (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) == + (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) { + /* Leave out the attribute */ + } else if (params->auth_algs & WPA_AUTH_ALG_SHARED) + NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, + NL80211_AUTHTYPE_SHARED_KEY); + else + NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, + NL80211_AUTHTYPE_OPEN_SYSTEM); + + ver = 0; + if (params->wpa_version & WPA_PROTO_WPA) + ver |= NL80211_WPA_VERSION_1; + if (params->wpa_version & WPA_PROTO_RSN) + ver |= NL80211_WPA_VERSION_2; + if (ver) + NLA_PUT_U32(msg, NL80211_ATTR_WPA_VERSIONS, ver); + + num_suites = 0; + if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X) + suites[num_suites++] = WLAN_AKM_SUITE_8021X; + if (params->key_mgmt_suites & WPA_KEY_MGMT_PSK) + suites[num_suites++] = WLAN_AKM_SUITE_PSK; + if (num_suites) { + NLA_PUT(msg, NL80211_ATTR_AKM_SUITES, + num_suites * sizeof(u32), suites); + } + + if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X && + params->pairwise_ciphers & (WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40)) + NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT); + + num_suites = 0; + if (params->pairwise_ciphers & WPA_CIPHER_CCMP) + suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP; + if (params->pairwise_ciphers & WPA_CIPHER_GCMP) + suites[num_suites++] = WLAN_CIPHER_SUITE_GCMP; + if (params->pairwise_ciphers & WPA_CIPHER_TKIP) + suites[num_suites++] = WLAN_CIPHER_SUITE_TKIP; + if (params->pairwise_ciphers & WPA_CIPHER_WEP104) + suites[num_suites++] = WLAN_CIPHER_SUITE_WEP104; + if (params->pairwise_ciphers & WPA_CIPHER_WEP40) + suites[num_suites++] = WLAN_CIPHER_SUITE_WEP40; + if (num_suites) { + NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, + num_suites * sizeof(u32), suites); + } + + switch (params->group_cipher) { + case WPA_CIPHER_CCMP: + NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, + WLAN_CIPHER_SUITE_CCMP); + break; + case WPA_CIPHER_GCMP: + NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, + WLAN_CIPHER_SUITE_GCMP); + break; + case WPA_CIPHER_TKIP: + NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, + WLAN_CIPHER_SUITE_TKIP); + break; + case WPA_CIPHER_WEP104: + NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, + WLAN_CIPHER_SUITE_WEP104); + break; + case WPA_CIPHER_WEP40: + NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, + WLAN_CIPHER_SUITE_WEP40); + break; + } + + if (params->beacon_ies) { + NLA_PUT(msg, NL80211_ATTR_IE, wpabuf_len(params->beacon_ies), + wpabuf_head(params->beacon_ies)); + } + if (params->proberesp_ies) { + NLA_PUT(msg, NL80211_ATTR_IE_PROBE_RESP, + wpabuf_len(params->proberesp_ies), + wpabuf_head(params->proberesp_ies)); + } + if (params->assocresp_ies) { + NLA_PUT(msg, NL80211_ATTR_IE_ASSOC_RESP, + wpabuf_len(params->assocresp_ies), + 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) { @@ -2875,26 +5667,33 @@ static int wpa_driver_nl80211_set_beacon(void *priv, ret, strerror(-ret)); } else { bss->beacon_set = 1; + nl80211_set_bss(bss, params->cts_protect, params->preamble, + params->short_slot_time, params->ht_opmode, + params->isolate, params->basic_rates); } return ret; nla_put_failure: + nlmsg_free(msg); return -ENOBUFS; } -static int wpa_driver_nl80211_set_freq(struct wpa_driver_nl80211_data *drv, +static int wpa_driver_nl80211_set_freq(struct i802_bss *bss, int freq, int ht_enabled, int sec_channel_offset) { + struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret; + wpa_printf(MSG_DEBUG, "nl80211: Set freq %d (ht_enabled=%d " + "sec_channel_offset=%d)", + freq, ht_enabled, sec_channel_offset); msg = nlmsg_alloc(); if (!msg) return -1; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_SET_WIPHY, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); @@ -2916,50 +5715,103 @@ static int wpa_driver_nl80211_set_freq(struct wpa_driver_nl80211_data *drv, } ret = send_and_recv_msgs(drv, msg, NULL, NULL); - if (ret == 0) + msg = NULL; + if (ret == 0) { + bss->freq = freq; return 0; + } wpa_printf(MSG_DEBUG, "nl80211: Failed to set channel (freq=%d): " "%d (%s)", freq, ret, strerror(-ret)); nla_put_failure: + nlmsg_free(msg); return -1; } +static u32 sta_flags_nl80211(int flags) +{ + u32 f = 0; + + if (flags & WPA_STA_AUTHORIZED) + f |= BIT(NL80211_STA_FLAG_AUTHORIZED); + if (flags & WPA_STA_WMM) + f |= BIT(NL80211_STA_FLAG_WME); + if (flags & WPA_STA_SHORT_PREAMBLE) + f |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE); + if (flags & WPA_STA_MFP) + f |= BIT(NL80211_STA_FLAG_MFP); + if (flags & WPA_STA_TDLS_PEER) + f |= BIT(NL80211_STA_FLAG_TDLS_PEER); + + return f; +} + + static int wpa_driver_nl80211_sta_add(void *priv, struct hostapd_sta_add_params *params) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - struct nl_msg *msg; + struct nl_msg *msg, *wme = NULL; + struct nl80211_sta_flag_update upd; int ret = -ENOBUFS; + if ((params->flags & WPA_STA_TDLS_PEER) && + !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) + return -EOPNOTSUPP; + msg = nlmsg_alloc(); if (!msg) return -ENOMEM; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_NEW_STATION, 0); + nl80211_cmd(drv, msg, 0, params->set ? NL80211_CMD_SET_STATION : + NL80211_CMD_NEW_STATION); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr); - NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid); NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, params->supp_rates_len, params->supp_rates); - NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, - params->listen_interval); + if (!params->set) { + NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid); + NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, + params->listen_interval); + } if (params->ht_capabilities) { NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sizeof(*params->ht_capabilities), params->ht_capabilities); } + os_memset(&upd, 0, sizeof(upd)); + upd.mask = sta_flags_nl80211(params->flags); + upd.set = upd.mask; + NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd); + + if (params->flags & WPA_STA_WMM) { + wme = nlmsg_alloc(); + if (!wme) + goto nla_put_failure; + + NLA_PUT_U8(wme, NL80211_STA_WME_UAPSD_QUEUES, + params->qosinfo & WMM_QOSINFO_STA_AC_MASK); + NLA_PUT_U8(wme, NL80211_STA_WME_MAX_SP, + (params->qosinfo > WMM_QOSINFO_STA_SP_SHIFT) & + WMM_QOSINFO_STA_SP_MASK); + if (nla_put_nested(msg, NL80211_ATTR_STA_WME, wme) < 0) + goto nla_put_failure; + } + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; if (ret) - wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_NEW_STATION " - "result: %d (%s)", ret, strerror(-ret)); + wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_%s_STATION " + "result: %d (%s)", params->set ? "SET" : "NEW", ret, + strerror(-ret)); if (ret == -EEXIST) ret = 0; nla_put_failure: + nlmsg_free(wme); + nlmsg_free(msg); return ret; } @@ -2975,8 +5827,7 @@ static int wpa_driver_nl80211_sta_remove(void *priv, const u8 *addr) if (!msg) return -ENOMEM; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_DEL_STATION, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_STATION); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); @@ -2987,6 +5838,7 @@ static int wpa_driver_nl80211_sta_remove(void *priv, const u8 *addr) return 0; return ret; nla_put_failure: + nlmsg_free(msg); return -ENOBUFS; } @@ -2998,26 +5850,46 @@ static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, wpa_printf(MSG_DEBUG, "nl80211: Remove interface ifindex=%d", ifidx); -#ifdef HOSTAPD /* stop listening for EAPOL on this interface */ del_ifidx(drv, ifidx); -#endif /* HOSTAPD */ msg = nlmsg_alloc(); if (!msg) goto nla_put_failure; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_DEL_INTERFACE, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_INTERFACE); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifidx); if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) return; + msg = NULL; nla_put_failure: + nlmsg_free(msg); wpa_printf(MSG_ERROR, "Failed to remove interface (ifidx=%d)", ifidx); } +static const char * nl80211_iftype_str(enum nl80211_iftype mode) +{ + switch (mode) { + case NL80211_IFTYPE_ADHOC: + return "ADHOC"; + case NL80211_IFTYPE_STATION: + return "STATION"; + case NL80211_IFTYPE_AP: + return "AP"; + case NL80211_IFTYPE_MONITOR: + return "MONITOR"; + case NL80211_IFTYPE_P2P_CLIENT: + return "P2P_CLIENT"; + case NL80211_IFTYPE_P2P_GO: + return "P2P_GO"; + default: + return "unknown"; + } +} + + static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, const char *ifname, enum nl80211_iftype iftype, @@ -3027,12 +5899,14 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, int ifidx; int ret = -ENOBUFS; + wpa_printf(MSG_DEBUG, "nl80211: Create interface iftype %d (%s)", + iftype, nl80211_iftype_str(iftype)); + msg = nlmsg_alloc(); if (!msg) return -1; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_NEW_INTERFACE, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_NEW_INTERFACE); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, ifname); NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, iftype); @@ -3057,8 +5931,10 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, } ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; if (ret) { nla_put_failure: + nlmsg_free(msg); wpa_printf(MSG_ERROR, "Failed to create interface %s: %d (%s)", ifname, ret, strerror(-ret)); return ret; @@ -3071,13 +5947,11 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, if (ifidx <= 0) return -1; -#ifdef HOSTAPD /* start listening for EAPOL on this interface */ add_ifidx(drv, ifidx); -#endif /* HOSTAPD */ if (addr && iftype != NL80211_IFTYPE_MONITOR && - linux_set_ifhwaddr(drv->ioctl_sock, ifname, addr)) { + linux_set_ifhwaddr(drv->global->ioctl_sock, ifname, addr)) { nl80211_remove_iface(drv, ifidx); return -1; } @@ -3094,7 +5968,7 @@ static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv, ret = nl80211_create_iface_once(drv, ifname, iftype, addr, wds); - /* if error occured and interface exists already */ + /* if error occurred and interface exists already */ if (ret == -ENFILE && if_nametoindex(ifname)) { wpa_printf(MSG_INFO, "Try to remove and re-create %s", ifname); @@ -3106,7 +5980,7 @@ static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv, wds); } - if (ret >= 0 && drv->disable_11b_rates) + if (ret >= 0 && is_p2p_interface(iftype)) nl80211_disable_11b_rates(drv, ret, 1); return ret; @@ -3136,10 +6010,20 @@ static void handle_tx_callback(void *ctx, u8 *buf, size_t len, int ok) static void from_unknown_sta(struct wpa_driver_nl80211_data *drv, u8 *buf, size_t len) { + struct ieee80211_hdr *hdr = (void *)buf; + u16 fc; union wpa_event_data event; + + if (len < sizeof(*hdr)) + return; + + fc = le_to_host16(hdr->frame_control); + os_memset(&event, 0, sizeof(event)); - event.rx_from_unknown.frame = buf; - event.rx_from_unknown.len = len; + event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len); + event.rx_from_unknown.addr = hdr->addr2; + event.rx_from_unknown.wds = (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) == + (WLAN_FC_FROMDS | WLAN_FC_TODS); wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event); } @@ -3191,12 +6075,6 @@ static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx) return; } - if (drv->nlmode == NL80211_IFTYPE_STATION && !drv->probe_req_report) { - wpa_printf(MSG_DEBUG, "nl80211: Ignore monitor interface " - "frame since Probe Request reporting is disabled"); - return; - } - if (ieee80211_radiotap_iterator_init(&iter, (void*)buf, len)) { printf("received invalid radiotap frame\n"); return; @@ -3231,8 +6109,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; } } @@ -3290,8 +6168,15 @@ static struct sock_filter msock_filter_insns[] = { * add a filter here that filters on our DA and that flag * to allow us to deauth frames to that bad station. * - * Not a regression -- we didn't do it before either. + * For now allow all To DS data frames through. */ + /* load the IEEE 802.11 frame control field */ + BPF_STMT(BPF_LD | BPF_H | BPF_IND, 0), + /* mask off frame type, version and DS status */ + BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0F03), + /* accept frame if version 0, type 2 and To DS, fall through otherwise + */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0801, PASS, 0), #if 0 /* @@ -3396,6 +6281,10 @@ static int add_monitor_filter(int s) static void nl80211_remove_monitor_interface( struct wpa_driver_nl80211_data *drv) { + drv->monitor_refcount--; + if (drv->monitor_refcount > 0) + return; + if (drv->monitor_ifidx >= 0) { nl80211_remove_iface(drv, drv->monitor_ifidx); drv->monitor_ifidx = -1; @@ -3416,17 +6305,46 @@ nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv) int optval; socklen_t optlen; - snprintf(buf, IFNAMSIZ, "mon.%s", drv->first_bss.ifname); + if (drv->monitor_ifidx >= 0) { + drv->monitor_refcount++; + return 0; + } + + if (os_strncmp(drv->first_bss.ifname, "p2p-", 4) == 0) { + /* + * P2P interface name is of the format p2p-%s-%d. For monitor + * interface name corresponding to P2P GO, replace "p2p-" with + * "mon-" to retain the same interface name length and to + * indicate that it is a monitor interface. + */ + snprintf(buf, IFNAMSIZ, "mon-%s", drv->first_bss.ifname + 4); + } else { + /* Non-P2P interface with AP functionality. */ + snprintf(buf, IFNAMSIZ, "mon.%s", drv->first_bss.ifname); + } + buf[IFNAMSIZ - 1] = '\0'; drv->monitor_ifidx = nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL, 0); + if (drv->monitor_ifidx == -EOPNOTSUPP) { + /* + * This is backward compatibility for a few versions of + * the kernel only that didn't advertise the right + * attributes for the only driver that then supported + * AP mode w/o monitor -- ath6kl. + */ + wpa_printf(MSG_DEBUG, "nl80211: Driver does not support " + "monitor interface type - try to run without it"); + drv->device_ap_sme = 1; + } + if (drv->monitor_ifidx < 0) return -1; - if (linux_set_iface_flags(drv->ioctl_sock, buf, 1)) + if (linux_set_iface_flags(drv->global->ioctl_sock, buf, 1)) goto error; memset(&ll, 0, sizeof(ll)); @@ -3470,11 +6388,95 @@ nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv) } +static int nl80211_setup_ap(struct i802_bss *bss) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + + wpa_printf(MSG_DEBUG, "nl80211: Setup AP - device_ap_sme=%d " + "use_monitor=%d", drv->device_ap_sme, drv->use_monitor); + + /* + * Disable Probe Request reporting unless we need it in this way for + * devices that include the AP SME, in the other case (unless using + * monitor iface) we'll get it through the nl_mgmt socket instead. + */ + if (!drv->device_ap_sme) + wpa_driver_nl80211_probe_req_report(bss, 0); + + if (!drv->device_ap_sme && !drv->use_monitor) + if (nl80211_mgmt_subscribe_ap(bss)) + return -1; + + if (drv->device_ap_sme && !drv->use_monitor) + if (nl80211_mgmt_subscribe_ap_dev_sme(bss)) + return -1; + + if (!drv->device_ap_sme && drv->use_monitor && + nl80211_create_monitor_interface(drv) && + !drv->device_ap_sme) + return -1; + + if (drv->device_ap_sme && + wpa_driver_nl80211_probe_req_report(bss, 1) < 0) { + wpa_printf(MSG_DEBUG, "nl80211: Failed to enable " + "Probe Request frame reporting in AP mode"); + /* Try to survive without this */ + } + + return 0; +} + + +static void nl80211_teardown_ap(struct i802_bss *bss) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + + if (drv->device_ap_sme) { + wpa_driver_nl80211_probe_req_report(bss, 0); + if (!drv->use_monitor) + nl80211_mgmt_unsubscribe(bss, "AP teardown (dev SME)"); + } else if (drv->use_monitor) + nl80211_remove_monitor_interface(drv); + else + nl80211_mgmt_unsubscribe(bss, "AP teardown"); + + bss->beacon_set = 0; +} + + +static int nl80211_send_eapol_data(struct i802_bss *bss, + const u8 *addr, const u8 *data, + size_t data_len) +{ + struct sockaddr_ll ll; + int ret; + + if (bss->drv->eapol_tx_sock < 0) { + wpa_printf(MSG_DEBUG, "nl80211: No socket to send EAPOL"); + return -1; + } + + os_memset(&ll, 0, sizeof(ll)); + ll.sll_family = AF_PACKET; + ll.sll_ifindex = bss->ifindex; + ll.sll_protocol = htons(ETH_P_PAE); + ll.sll_halen = ETH_ALEN; + os_memcpy(ll.sll_addr, addr, ETH_ALEN); + ret = sendto(bss->drv->eapol_tx_sock, data, data_len, 0, + (struct sockaddr *) &ll, sizeof(ll)); + if (ret < 0) + wpa_printf(MSG_ERROR, "nl80211: EAPOL TX: %s", + strerror(errno)); + + return ret; +} + + static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; static int wpa_driver_nl80211_hapd_send_eapol( void *priv, const u8 *addr, const u8 *data, - size_t data_len, int encrypt, const u8 *own_addr) + size_t data_len, int encrypt, const u8 *own_addr, u32 flags) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; @@ -3482,11 +6484,10 @@ static int wpa_driver_nl80211_hapd_send_eapol( size_t len; u8 *pos; int res; -#if 0 /* FIX */ - int qos = sta->flags & WPA_STA_WMM; -#else - int qos = 0; -#endif + int qos = flags & WPA_STA_WMM; + + if (drv->device_ap_sme || !drv->use_monitor) + return nl80211_send_eapol_data(bss, addr, data, data_len); len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 + data_len; @@ -3502,26 +6503,22 @@ static int wpa_driver_nl80211_hapd_send_eapol( hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS); if (encrypt) hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP); -#if 0 /* To be enabled if qos determination is added above */ if (qos) { hdr->frame_control |= host_to_le16(WLAN_FC_STYPE_QOS_DATA << 4); } -#endif memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN); memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); pos = (u8 *) (hdr + 1); -#if 0 /* To be enabled if qos determination is added above */ if (qos) { - /* add an empty QoS header if needed */ - pos[0] = 0; + /* Set highest priority in QoS header */ + pos[0] = 7; pos[1] = 0; pos += 2; } -#endif memcpy(pos, rfc1042_header, sizeof(rfc1042_header)); pos += sizeof(rfc1042_header); @@ -3529,7 +6526,8 @@ static int wpa_driver_nl80211_hapd_send_eapol( pos += 2; memcpy(pos, data, data_len); - res = wpa_driver_nl80211_send_frame(drv, (u8 *) hdr, len, encrypt); + res = wpa_driver_nl80211_send_frame(bss, (u8 *) hdr, len, encrypt, 0, + 0, 0, 0, 0); if (res < 0) { wpa_printf(MSG_ERROR, "i802_send_eapol - packet len: %lu - " "failed: %d (%s)", @@ -3541,23 +6539,6 @@ static int wpa_driver_nl80211_hapd_send_eapol( } -static u32 sta_flags_nl80211(int flags) -{ - u32 f = 0; - - if (flags & WPA_STA_AUTHORIZED) - f |= BIT(NL80211_STA_FLAG_AUTHORIZED); - if (flags & WPA_STA_WMM) - f |= BIT(NL80211_STA_FLAG_WME); - if (flags & WPA_STA_SHORT_PREAMBLE) - f |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE); - if (flags & WPA_STA_MFP) - f |= BIT(NL80211_STA_FLAG_MFP); - - return f; -} - - static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr, int total_flags, int flags_or, int flags_and) @@ -3577,8 +6558,7 @@ static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr, return -ENOMEM; } - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_SET_STATION, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); @@ -3600,6 +6580,9 @@ static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr, if (total_flags & WPA_STA_MFP) NLA_PUT_FLAG(flags, NL80211_STA_FLAG_MFP); + if (total_flags & WPA_STA_TDLS_PEER) + NLA_PUT_FLAG(flags, NL80211_STA_FLAG_TDLS_PEER); + if (nla_put_nested(msg, NL80211_ATTR_STA_FLAGS, flags)) goto nla_put_failure; @@ -3612,6 +6595,7 @@ static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr, return send_and_recv_msgs(drv, msg, NULL, NULL); nla_put_failure: + nlmsg_free(msg); nlmsg_free(flags); return -ENOBUFS; } @@ -3620,14 +6604,27 @@ static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr, static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv, struct wpa_driver_associate_params *params) { - if (wpa_driver_nl80211_set_mode(&drv->first_bss, params->mode) || - wpa_driver_nl80211_set_freq(drv, params->freq, 0, 0)) { + enum nl80211_iftype nlmode, old_mode; + + if (params->p2p) { + wpa_printf(MSG_DEBUG, "nl80211: Setup AP operations for P2P " + "group (GO)"); + nlmode = NL80211_IFTYPE_P2P_GO; + } else + nlmode = NL80211_IFTYPE_AP; + + old_mode = drv->nlmode; + if (wpa_driver_nl80211_set_mode(&drv->first_bss, nlmode)) { nl80211_remove_monitor_interface(drv); return -1; } - /* TODO: setup monitor interface (and add code somewhere to remove this - * when AP mode is stopped; associate with mode != 2 or drv_deinit) */ + if (wpa_driver_nl80211_set_freq(&drv->first_bss, params->freq, 0, 0)) { + if (old_mode != nlmode) + wpa_driver_nl80211_set_mode(&drv->first_bss, old_mode); + nl80211_remove_monitor_interface(drv); + return -1; + } return 0; } @@ -3642,8 +6639,7 @@ static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv) if (!msg) return -1; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_LEAVE_IBSS, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_LEAVE_IBSS); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; @@ -3671,7 +6667,8 @@ static int wpa_driver_nl80211_ibss(struct wpa_driver_nl80211_data *drv, wpa_printf(MSG_DEBUG, "nl80211: Join IBSS (ifindex=%d)", drv->ifindex); - if (wpa_driver_nl80211_set_mode(&drv->first_bss, params->mode)) { + if (wpa_driver_nl80211_set_mode(&drv->first_bss, + NL80211_IFTYPE_ADHOC)) { wpa_printf(MSG_INFO, "nl80211: Failed to set interface into " "IBSS mode"); return -1; @@ -3682,8 +6679,7 @@ retry: if (!msg) return -1; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_JOIN_IBSS, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_JOIN_IBSS); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); if (params->ssid == NULL || params->ssid_len > sizeof(drv->ssid)) @@ -3703,6 +6699,20 @@ retry: if (ret) goto nla_put_failure; + if (params->bssid && params->fixed_bssid) { + wpa_printf(MSG_DEBUG, " * BSSID=" MACSTR, + MAC2STR(params->bssid)); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid); + } + + if (params->key_mgmt_suite == KEY_MGMT_802_1X || + params->key_mgmt_suite == KEY_MGMT_PSK || + params->key_mgmt_suite == KEY_MGMT_802_1X_SHA256 || + params->key_mgmt_suite == KEY_MGMT_PSK_SHA256) { + wpa_printf(MSG_DEBUG, " * control port"); + NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT); + } + if (params->wpa_ie) { wpa_hexdump(MSG_DEBUG, " * Extra IEs for Beacon/Probe Response frames", @@ -3736,21 +6746,21 @@ nla_put_failure: } -static int wpa_driver_nl80211_connect( +static int wpa_driver_nl80211_try_connect( struct wpa_driver_nl80211_data *drv, struct wpa_driver_associate_params *params) { struct nl_msg *msg; enum nl80211_auth_type type; int ret = 0; + int algs; msg = nlmsg_alloc(); if (!msg) return -1; wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex); - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_CONNECT, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_CONNECT); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); if (params->bssid) { @@ -3762,6 +6772,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); @@ -3777,6 +6793,19 @@ static int wpa_driver_nl80211_connect( NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len, params->wpa_ie); + algs = 0; + if (params->auth_alg & WPA_AUTH_ALG_OPEN) + algs++; + if (params->auth_alg & WPA_AUTH_ALG_SHARED) + algs++; + if (params->auth_alg & WPA_AUTH_ALG_LEAP) + algs++; + if (algs > 1) { + wpa_printf(MSG_DEBUG, " * Leave out Auth Type for automatic " + "selection"); + goto skip_auth_type; + } + if (params->auth_alg & WPA_AUTH_ALG_OPEN) type = NL80211_AUTHTYPE_OPEN_SYSTEM; else if (params->auth_alg & WPA_AUTH_ALG_SHARED) @@ -3791,15 +6820,16 @@ static int wpa_driver_nl80211_connect( wpa_printf(MSG_DEBUG, " * Auth Type %d", type); NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type); - if (params->wpa_ie && params->wpa_ie_len) { - enum nl80211_wpa_versions ver; +skip_auth_type: + if (params->wpa_proto) { + enum nl80211_wpa_versions ver = 0; - if (params->wpa_ie[0] == WLAN_EID_RSN) - ver = NL80211_WPA_VERSION_2; - else - ver = NL80211_WPA_VERSION_1; + if (params->wpa_proto & WPA_PROTO_WPA) + ver |= NL80211_WPA_VERSION_1; + if (params->wpa_proto & WPA_PROTO_RSN) + ver |= NL80211_WPA_VERSION_2; - wpa_printf(MSG_DEBUG, " * WPA Version %d", ver); + wpa_printf(MSG_DEBUG, " * WPA Versions 0x%x", ver); NLA_PUT_U32(msg, NL80211_ATTR_WPA_VERSIONS, ver); } @@ -3807,6 +6837,9 @@ static int wpa_driver_nl80211_connect( int cipher; switch (params->pairwise_suite) { + case CIPHER_SMS4: + cipher = WLAN_CIPHER_SUITE_SMS4; + break; case CIPHER_WEP40: cipher = WLAN_CIPHER_SUITE_WEP40; break; @@ -3816,6 +6849,9 @@ static int wpa_driver_nl80211_connect( case CIPHER_CCMP: cipher = WLAN_CIPHER_SUITE_CCMP; break; + case CIPHER_GCMP: + cipher = WLAN_CIPHER_SUITE_GCMP; + break; case CIPHER_TKIP: default: cipher = WLAN_CIPHER_SUITE_TKIP; @@ -3828,6 +6864,9 @@ static int wpa_driver_nl80211_connect( int cipher; switch (params->group_suite) { + case CIPHER_SMS4: + cipher = WLAN_CIPHER_SUITE_SMS4; + break; case CIPHER_WEP40: cipher = WLAN_CIPHER_SUITE_WEP40; break; @@ -3837,6 +6876,9 @@ static int wpa_driver_nl80211_connect( case CIPHER_CCMP: cipher = WLAN_CIPHER_SUITE_CCMP; break; + case CIPHER_GCMP: + cipher = WLAN_CIPHER_SUITE_GCMP; + break; case CIPHER_TKIP: default: cipher = WLAN_CIPHER_SUITE_TKIP; @@ -3846,10 +6888,14 @@ static int wpa_driver_nl80211_connect( } if (params->key_mgmt_suite == KEY_MGMT_802_1X || - params->key_mgmt_suite == KEY_MGMT_PSK) { + params->key_mgmt_suite == KEY_MGMT_PSK || + params->key_mgmt_suite == KEY_MGMT_CCKM) { int mgmt = WLAN_AKM_SUITE_PSK; switch (params->key_mgmt_suite) { + case KEY_MGMT_CCKM: + mgmt = WLAN_AKM_SUITE_CCKM; + break; case KEY_MGMT_802_1X: mgmt = WLAN_AKM_SUITE_8021X; break; @@ -3861,6 +6907,16 @@ static int wpa_driver_nl80211_connect( NLA_PUT_U32(msg, NL80211_ATTR_AKM_SUITES, mgmt); } + if (params->disable_ht) + NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT); + + if (params->htcaps && params->htcaps_mask) { + int sz = sizeof(struct ieee80211_ht_capabilities); + NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sz, params->htcaps); + NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz, + params->htcaps_mask); + } + ret = nl80211_set_conn_keys(params, msg); if (ret) goto nla_put_failure; @@ -3882,6 +6938,31 @@ nla_put_failure: } +static int wpa_driver_nl80211_connect( + struct wpa_driver_nl80211_data *drv, + struct wpa_driver_associate_params *params) +{ + int ret = wpa_driver_nl80211_try_connect(drv, params); + if (ret == -EALREADY) { + /* + * cfg80211 does not currently accept new connections if + * we are already connected. As a workaround, force + * disconnection and try again. + */ + wpa_printf(MSG_DEBUG, "nl80211: Explicitly " + "disconnecting before reassociation " + "attempt"); + if (wpa_driver_nl80211_disconnect( + drv, WLAN_REASON_PREV_AUTH_NOT_VALID)) + return -1; + /* Ignore the next local disconnect message. */ + drv->ignore_next_local_disconnect = 1; + ret = wpa_driver_nl80211_try_connect(drv, params); + } + return ret; +} + + static int wpa_driver_nl80211_associate( void *priv, struct wpa_driver_associate_params *params) { @@ -3897,7 +6978,10 @@ static int wpa_driver_nl80211_associate( return wpa_driver_nl80211_ibss(drv, params); if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) { - if (wpa_driver_nl80211_set_mode(priv, params->mode) < 0) + enum nl80211_iftype nlmode = params->p2p ? + NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION; + + if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0) return -1; return wpa_driver_nl80211_connect(drv, params); } @@ -3910,8 +6994,7 @@ static int wpa_driver_nl80211_associate( wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)", drv->ifindex); - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_ASSOCIATE, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_ASSOCIATE); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); if (params->bssid) { @@ -3925,6 +7008,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); @@ -3940,6 +7029,56 @@ static int wpa_driver_nl80211_associate( NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len, params->wpa_ie); + if (params->pairwise_suite != CIPHER_NONE) { + int cipher; + + switch (params->pairwise_suite) { + case CIPHER_WEP40: + cipher = WLAN_CIPHER_SUITE_WEP40; + break; + case CIPHER_WEP104: + cipher = WLAN_CIPHER_SUITE_WEP104; + break; + case CIPHER_CCMP: + cipher = WLAN_CIPHER_SUITE_CCMP; + break; + case CIPHER_GCMP: + cipher = WLAN_CIPHER_SUITE_GCMP; + break; + case CIPHER_TKIP: + default: + cipher = WLAN_CIPHER_SUITE_TKIP; + break; + } + wpa_printf(MSG_DEBUG, " * pairwise=0x%x", cipher); + NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, cipher); + } + + if (params->group_suite != CIPHER_NONE) { + int cipher; + + switch (params->group_suite) { + case CIPHER_WEP40: + cipher = WLAN_CIPHER_SUITE_WEP40; + break; + case CIPHER_WEP104: + cipher = WLAN_CIPHER_SUITE_WEP104; + break; + case CIPHER_CCMP: + cipher = WLAN_CIPHER_SUITE_CCMP; + break; + case CIPHER_GCMP: + cipher = WLAN_CIPHER_SUITE_GCMP; + break; + case CIPHER_TKIP: + default: + cipher = WLAN_CIPHER_SUITE_TKIP; + break; + } + wpa_printf(MSG_DEBUG, " * group=0x%x", cipher); + NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher); + } + #ifdef CONFIG_IEEE80211W if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED) NLA_PUT_U32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED); @@ -3954,11 +7093,25 @@ static int wpa_driver_nl80211_associate( params->prev_bssid); } + if (params->disable_ht) + NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT); + + if (params->htcaps && params->htcaps_mask) { + int sz = sizeof(struct ieee80211_ht_capabilities); + NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sz, params->htcaps); + NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz, + params->htcaps_mask); + } + + if (params->p2p) + wpa_printf(MSG_DEBUG, " * P2P group"); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: MLME command failed: ret=%d " - "(%s)", ret, strerror(-ret)); + wpa_dbg(drv->ctx, MSG_DEBUG, + "nl80211: MLME command failed (assoc): ret=%d (%s)", + ret, strerror(-ret)); nl80211_dump_scan(drv); goto nla_put_failure; } @@ -3973,57 +7126,53 @@ nla_put_failure: static int nl80211_set_mode(struct wpa_driver_nl80211_data *drv, - int ifindex, int mode) + int ifindex, enum nl80211_iftype mode) { struct nl_msg *msg; int ret = -ENOBUFS; + wpa_printf(MSG_DEBUG, "nl80211: Set mode ifindex %d iftype %d (%s)", + ifindex, mode, nl80211_iftype_str(mode)); + msg = nlmsg_alloc(); if (!msg) return -ENOMEM; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_SET_INTERFACE, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_INTERFACE); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, mode); ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; if (!ret) return 0; nla_put_failure: + nlmsg_free(msg); wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface %d to mode %d:" " %d (%s)", ifindex, mode, ret, strerror(-ret)); return ret; } -static int wpa_driver_nl80211_set_mode(void *priv, int mode) +static int wpa_driver_nl80211_set_mode(struct i802_bss *bss, + enum nl80211_iftype nlmode) { - struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; int ret = -1; - int nlmode; - - switch (mode) { - case 0: - nlmode = NL80211_IFTYPE_STATION; - break; - case 1: - nlmode = NL80211_IFTYPE_ADHOC; - break; - case 2: - nlmode = NL80211_IFTYPE_AP; - break; - default: - return -1; - } + int i; + int was_ap = is_ap_interface(drv->nlmode); + int res; - if (nl80211_set_mode(drv, drv->ifindex, nlmode) == 0) { + res = nl80211_set_mode(drv, drv->ifindex, nlmode); + if (res == 0) { drv->nlmode = nlmode; ret = 0; goto done; } + if (res == -ENODEV) + return -1; + if (nlmode == drv->nlmode) { wpa_printf(MSG_DEBUG, "nl80211: Interface already in " "requested mode - ignore error"); @@ -4035,36 +7184,68 @@ static int wpa_driver_nl80211_set_mode(void *priv, int mode) * take the device down, try to set the mode again, and bring the * device back up. */ - if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0) == 0) { - /* Try to set the mode again while the interface is down */ - ret = nl80211_set_mode(drv, drv->ifindex, nlmode); - if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) - ret = -1; + wpa_printf(MSG_DEBUG, "nl80211: Try mode change after setting " + "interface down"); + for (i = 0; i < 10; i++) { + res = linux_set_iface_flags(drv->global->ioctl_sock, + bss->ifname, 0); + if (res == -EACCES || res == -ENODEV) + break; + if (res == 0) { + /* Try to set the mode again while the interface is + * down */ + ret = nl80211_set_mode(drv, drv->ifindex, nlmode); + if (ret == -EACCES) + break; + res = linux_set_iface_flags(drv->global->ioctl_sock, + bss->ifname, 1); + if (res && !ret) + ret = -1; + else if (ret != -EBUSY) + break; + } else + wpa_printf(MSG_DEBUG, "nl80211: Failed to set " + "interface down"); + os_sleep(0, 100000); } if (!ret) { wpa_printf(MSG_DEBUG, "nl80211: Mode change succeeded while " "interface is down"); drv->nlmode = nlmode; + drv->ignore_if_down_event = 1; } done: - if (!ret && nlmode == NL80211_IFTYPE_AP) { + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Interface mode change to %d " + "from %d failed", nlmode, drv->nlmode); + return ret; + } + + if (is_p2p_interface(nlmode)) + nl80211_disable_11b_rates(drv, drv->ifindex, 1); + else if (drv->disabled_11b_rates) + nl80211_disable_11b_rates(drv, drv->ifindex, 0); + + if (is_ap_interface(nlmode)) { + nl80211_mgmt_unsubscribe(bss, "start AP"); /* Setup additional AP mode functionality if needed */ - if (drv->monitor_ifidx < 0 && - nl80211_create_monitor_interface(drv)) + if (nl80211_setup_ap(bss)) return -1; - } else if (!ret && nlmode != NL80211_IFTYPE_AP) { + } else if (was_ap) { /* Remove additional AP mode functionality */ - nl80211_remove_monitor_interface(drv); - bss->beacon_set = 0; + nl80211_teardown_ap(bss); + } else { + nl80211_mgmt_unsubscribe(bss, "mode change"); } - if (ret) - wpa_printf(MSG_DEBUG, "nl80211: Interface mode change to %d " - "from %d failed", nlmode, drv->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"); - return ret; + return 0; } @@ -4088,7 +7269,7 @@ static int wpa_driver_nl80211_set_operstate(void *priv, int state) wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)", __func__, drv->operstate, state, state ? "UP" : "DORMANT"); drv->operstate = state; - return netlink_send_oper_ifla(drv->netlink, drv->ifindex, -1, + return netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, -1, state ? IF_OPER_UP : IF_OPER_DORMANT); } @@ -4104,8 +7285,7 @@ static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized) if (!msg) return -ENOMEM; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_SET_STATION, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); @@ -4119,74 +7299,21 @@ static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized) return send_and_recv_msgs(drv, msg, NULL, NULL); nla_put_failure: + nlmsg_free(msg); return -ENOBUFS; } -#ifdef HOSTAPD - -static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) -{ - int i; - int *old; - - wpa_printf(MSG_DEBUG, "nl80211: Add own interface ifindex %d", - ifidx); - for (i = 0; i < drv->num_if_indices; i++) { - if (drv->if_indices[i] == 0) { - drv->if_indices[i] = ifidx; - return; - } - } - - if (drv->if_indices != drv->default_if_indices) - old = drv->if_indices; - else - old = NULL; - - drv->if_indices = os_realloc(old, - sizeof(int) * (drv->num_if_indices + 1)); - if (!drv->if_indices) { - if (!old) - drv->if_indices = drv->default_if_indices; - else - drv->if_indices = old; - wpa_printf(MSG_ERROR, "Failed to reallocate memory for " - "interfaces"); - wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx); - return; - } else if (!old) - os_memcpy(drv->if_indices, drv->default_if_indices, - sizeof(drv->default_if_indices)); - drv->if_indices[drv->num_if_indices] = ifidx; - drv->num_if_indices++; -} - - -static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +/* Set kernel driver on given frequency (MHz) */ +static int i802_set_freq(void *priv, struct hostapd_freq_params *freq) { - int i; - - for (i = 0; i < drv->num_if_indices; i++) { - if (drv->if_indices[i] == ifidx) { - drv->if_indices[i] = 0; - break; - } - } + struct i802_bss *bss = priv; + return wpa_driver_nl80211_set_freq(bss, freq->freq, freq->ht_enabled, + freq->sec_channel_offset); } -static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) -{ - int i; - - for (i = 0; i < drv->num_if_indices; i++) - if (drv->if_indices[i] == ifidx) - return 1; - - return 0; -} - +#if defined(HOSTAPD) || defined(CONFIG_AP) static inline int min_int(int a, int b) { @@ -4228,8 +7355,7 @@ static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr, if (!msg) return -ENOMEM; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_GET_KEY, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_KEY); if (addr) NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); @@ -4240,54 +7366,11 @@ static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr, return send_and_recv_msgs(drv, msg, get_key_handler, seq); nla_put_failure: + nlmsg_free(msg); return -ENOBUFS; } -static int i802_set_rate_sets(void *priv, int *supp_rates, int *basic_rates, - int mode) -{ - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - struct nl_msg *msg; - u8 rates[NL80211_MAX_SUPP_RATES]; - u8 rates_len = 0; - int i; - - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_SET_BSS, 0); - - for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0; i++) - rates[rates_len++] = basic_rates[i] / 5; - - NLA_PUT(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, rates); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); - - return send_and_recv_msgs(drv, msg, NULL, NULL); - nla_put_failure: - return -ENOBUFS; -} - -#endif /* HOSTAPD */ - - -/* Set kernel driver on given frequency (MHz) */ -static int i802_set_freq(void *priv, struct hostapd_freq_params *freq) -{ - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - return wpa_driver_nl80211_set_freq(drv, freq->freq, freq->ht_enabled, - freq->sec_channel_offset); -} - - -#ifdef HOSTAPD - static int i802_set_rts(void *priv, int rts) { struct i802_bss *bss = priv; @@ -4305,15 +7388,16 @@ static int i802_set_rts(void *priv, int rts) else val = rts; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_SET_WIPHY, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, val); ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; if (!ret) return 0; nla_put_failure: + nlmsg_free(msg); wpa_printf(MSG_DEBUG, "nl80211: Failed to set RTS threshold %d: " "%d (%s)", rts, ret, strerror(-ret)); return ret; @@ -4337,15 +7421,16 @@ static int i802_set_frag(void *priv, int frag) else val = frag; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_SET_WIPHY, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, val); ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; if (!ret) return 0; nla_put_failure: + nlmsg_free(msg); wpa_printf(MSG_DEBUG, "nl80211: Failed to set fragmentation threshold " "%d: %d (%s)", frag, ret, strerror(-ret)); return ret; @@ -4357,13 +7442,13 @@ static int i802_flush(void *priv) struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; + int res; msg = nlmsg_alloc(); if (!msg) return -1; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_DEL_STATION, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_STATION); /* * XXX: FIX! this needs to flush all VLANs too @@ -4371,11 +7456,19 @@ static int i802_flush(void *priv) NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); - return send_and_recv_msgs(drv, msg, NULL, NULL); + res = send_and_recv_msgs(drv, msg, NULL, NULL); + if (res) { + wpa_printf(MSG_DEBUG, "nl80211: Station flush failed: ret=%d " + "(%s)", res, strerror(-res)); + } + return res; nla_put_failure: + nlmsg_free(msg); return -ENOBUFS; } +#endif /* HOSTAPD || CONFIG_AP */ + static int get_sta_handler(struct nl_msg *msg, void *arg) { @@ -4389,6 +7482,7 @@ static int get_sta_handler(struct nl_msg *msg, void *arg) [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 }, [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 }, [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 }, + [NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 }, }; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), @@ -4424,6 +7518,9 @@ static int get_sta_handler(struct nl_msg *msg, void *arg) if (stats[NL80211_STA_INFO_TX_PACKETS]) data->tx_packets = nla_get_u32(stats[NL80211_STA_INFO_TX_PACKETS]); + if (stats[NL80211_STA_INFO_TX_FAILED]) + data->tx_retry_failed = + nla_get_u32(stats[NL80211_STA_INFO_TX_FAILED]); return NL_SKIP; } @@ -4440,18 +7537,20 @@ static int i802_read_sta_data(void *priv, struct hostap_sta_driver_data *data, if (!msg) return -ENOMEM; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_GET_STATION, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_STATION); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); return send_and_recv_msgs(drv, msg, get_sta_handler, data); nla_put_failure: + nlmsg_free(msg); return -ENOBUFS; } +#if defined(HOSTAPD) || defined(CONFIG_AP) + static int i802_set_tx_queue_params(void *priv, int queue, int aifs, int cw_min, int cw_max, int burst_time) { @@ -4464,8 +7563,7 @@ static int i802_set_tx_queue_params(void *priv, int queue, int aifs, if (!msg) return -1; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_SET_WIPHY, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); @@ -4478,7 +7576,20 @@ static int i802_set_tx_queue_params(void *priv, int queue, int aifs, if (!params) goto nla_put_failure; - NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, queue); + switch (queue) { + case 0: + NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VO); + break; + case 1: + NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VI); + break; + case 2: + NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BE); + break; + case 3: + NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BK); + break; + } /* Burst time is configured in units of 0.1 msec and TXOP parameter in * 32 usec, so need to convert the value here. */ NLA_PUT_U16(msg, NL80211_TXQ_ATTR_TXOP, (burst_time * 100 + 16) / 32); @@ -4492,57 +7603,13 @@ static int i802_set_tx_queue_params(void *priv, int queue, int aifs, if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) return 0; + msg = NULL; nla_put_failure: + nlmsg_free(msg); return -1; } -static int i802_set_bss(void *priv, int cts, int preamble, int slot) -{ - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - struct nl_msg *msg; - - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_SET_BSS, 0); - - if (cts >= 0) - NLA_PUT_U8(msg, NL80211_ATTR_BSS_CTS_PROT, cts); - if (preamble >= 0) - NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble); - if (slot >= 0) - NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); - - return send_and_recv_msgs(drv, msg, NULL, NULL); - nla_put_failure: - return -ENOBUFS; -} - - -static int i802_set_cts_protect(void *priv, int value) -{ - return i802_set_bss(priv, value, -1, -1); -} - - -static int i802_set_preamble(void *priv, int value) -{ - return i802_set_bss(priv, -1, value, -1); -} - - -static int i802_set_short_slot_time(void *priv, int value) -{ - return i802_set_bss(priv, -1, -1, value); -} - - static int i802_set_sta_vlan(void *priv, const u8 *addr, const char *ifname, int vlan_id) { @@ -4555,8 +7622,7 @@ static int i802_set_sta_vlan(void *priv, const u8 *addr, if (!msg) return -ENOMEM; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_SET_STATION, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); @@ -4565,6 +7631,7 @@ static int i802_set_sta_vlan(void *priv, const u8 *addr, if_nametoindex(ifname)); ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; if (ret < 0) { wpa_printf(MSG_ERROR, "nl80211: NL80211_ATTR_STA_VLAN (addr=" MACSTR " ifname=%s vlan_id=%d) failed: %d (%s)", @@ -4572,53 +7639,11 @@ static int i802_set_sta_vlan(void *priv, const u8 *addr, strerror(-ret)); } nla_put_failure: + nlmsg_free(msg); return ret; } -static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val) -{ - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - char name[IFNAMSIZ + 1]; - - os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid); - wpa_printf(MSG_DEBUG, "nl80211: Set WDS STA addr=" MACSTR - " aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name); - if (val) { - if (nl80211_create_iface(drv, name, NL80211_IFTYPE_AP_VLAN, - NULL, 1) < 0) - return -1; - linux_set_iface_flags(drv->ioctl_sock, name, 1); - return i802_set_sta_vlan(priv, addr, name, 0); - } else { - i802_set_sta_vlan(priv, addr, bss->ifname, 0); - return wpa_driver_nl80211_if_remove(priv, WPA_IF_AP_VLAN, - name); - } -} - - -static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx) -{ - struct wpa_driver_nl80211_data *drv = eloop_ctx; - struct sockaddr_ll lladdr; - unsigned char buf[3000]; - int len; - socklen_t fromlen = sizeof(lladdr); - - len = recvfrom(sock, buf, sizeof(buf), 0, - (struct sockaddr *)&lladdr, &fromlen); - if (len < 0) { - perror("recv"); - return; - } - - if (have_ifidx(drv, lladdr.sll_ifindex)) - drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len); -} - - static int i802_get_inact_sec(void *priv, const u8 *addr) { struct hostap_sta_driver_data data; @@ -4645,8 +7670,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); @@ -4656,7 +7685,7 @@ static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, mgmt.u.deauth.reason_code = host_to_le16(reason); return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt, IEEE80211_HDRLEN + - sizeof(mgmt.u.deauth)); + sizeof(mgmt.u.deauth), 0); } @@ -4664,8 +7693,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); @@ -4675,30 +7708,155 @@ static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, mgmt.u.disassoc.reason_code = host_to_le16(reason); return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt, IEEE80211_HDRLEN + - sizeof(mgmt.u.disassoc)); + sizeof(mgmt.u.disassoc), 0); +} + +#endif /* HOSTAPD || CONFIG_AP */ + +#ifdef HOSTAPD + +static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +{ + int i; + int *old; + + wpa_printf(MSG_DEBUG, "nl80211: Add own interface ifindex %d", + ifidx); + for (i = 0; i < drv->num_if_indices; i++) { + if (drv->if_indices[i] == 0) { + drv->if_indices[i] = ifidx; + return; + } + } + + if (drv->if_indices != drv->default_if_indices) + old = drv->if_indices; + else + old = NULL; + + drv->if_indices = os_realloc_array(old, drv->num_if_indices + 1, + sizeof(int)); + if (!drv->if_indices) { + if (!old) + drv->if_indices = drv->default_if_indices; + else + drv->if_indices = old; + wpa_printf(MSG_ERROR, "Failed to reallocate memory for " + "interfaces"); + wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx); + return; + } else if (!old) + os_memcpy(drv->if_indices, drv->default_if_indices, + sizeof(drv->default_if_indices)); + drv->if_indices[drv->num_if_indices] = ifidx; + drv->num_if_indices++; +} + + +static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +{ + int i; + + for (i = 0; i < drv->num_if_indices; i++) { + if (drv->if_indices[i] == ifidx) { + drv->if_indices[i] = 0; + break; + } + } +} + + +static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +{ + int i; + + for (i = 0; i < drv->num_if_indices; i++) + if (drv->if_indices[i] == ifidx) + return 1; + + return 0; +} + + +static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val, + const char *bridge_ifname) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + char name[IFNAMSIZ + 1]; + + os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid); + wpa_printf(MSG_DEBUG, "nl80211: Set WDS STA addr=" MACSTR + " aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name); + if (val) { + if (!if_nametoindex(name)) { + if (nl80211_create_iface(drv, name, + NL80211_IFTYPE_AP_VLAN, + NULL, 1) < 0) + return -1; + if (bridge_ifname && + linux_br_add_if(drv->global->ioctl_sock, + bridge_ifname, name) < 0) + return -1; + } + if (linux_set_iface_flags(drv->global->ioctl_sock, name, 1)) { + wpa_printf(MSG_ERROR, "nl80211: Failed to set WDS STA " + "interface %s up", name); + } + return i802_set_sta_vlan(priv, addr, name, 0); + } else { + if (bridge_ifname) + linux_br_del_if(drv->global->ioctl_sock, bridge_ifname, + name); + + i802_set_sta_vlan(priv, addr, bss->ifname, 0); + return wpa_driver_nl80211_if_remove(priv, WPA_IF_AP_VLAN, + name); + } +} + + +static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct wpa_driver_nl80211_data *drv = eloop_ctx; + struct sockaddr_ll lladdr; + unsigned char buf[3000]; + int len; + socklen_t fromlen = sizeof(lladdr); + + len = recvfrom(sock, buf, sizeof(buf), 0, + (struct sockaddr *)&lladdr, &fromlen); + if (len < 0) { + perror("recv"); + return; + } + + if (have_ifidx(drv, lladdr.sll_ifindex)) + drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len); } static int i802_check_bridge(struct wpa_driver_nl80211_data *drv, + struct i802_bss *bss, const char *brname, const char *ifname) { int ifindex; char in_br[IFNAMSIZ]; - os_strlcpy(drv->brname, brname, IFNAMSIZ); + os_strlcpy(bss->brname, brname, IFNAMSIZ); ifindex = if_nametoindex(brname); if (ifindex == 0) { /* * Bridge was configured, but the bridge device does * not exist. Try to add it now. */ - if (linux_br_add(drv->ioctl_sock, brname) < 0) { + if (linux_br_add(drv->global->ioctl_sock, brname) < 0) { wpa_printf(MSG_ERROR, "nl80211: Failed to add the " "bridge interface %s: %s", brname, strerror(errno)); return -1; } - drv->added_bridge = 1; + bss->added_bridge = 1; add_ifidx(drv, if_nametoindex(brname)); } @@ -4708,7 +7866,8 @@ static int i802_check_bridge(struct wpa_driver_nl80211_data *drv, wpa_printf(MSG_DEBUG, "nl80211: Removing interface %s from " "bridge %s", ifname, in_br); - if (linux_br_del_if(drv->ioctl_sock, in_br, ifname) < 0) { + if (linux_br_del_if(drv->global->ioctl_sock, in_br, ifname) < + 0) { wpa_printf(MSG_ERROR, "nl80211: Failed to " "remove interface %s from bridge " "%s: %s", @@ -4719,13 +7878,13 @@ static int i802_check_bridge(struct wpa_driver_nl80211_data *drv, wpa_printf(MSG_DEBUG, "nl80211: Adding interface %s into bridge %s", ifname, brname); - if (linux_br_add_if(drv->ioctl_sock, brname, ifname) < 0) { + if (linux_br_add_if(drv->global->ioctl_sock, brname, ifname) < 0) { wpa_printf(MSG_ERROR, "nl80211: Failed to add interface %s " "into bridge %s: %s", ifname, brname, strerror(errno)); return -1; } - drv->added_if_into_bridge = 1; + bss->added_if_into_bridge = 1; return 0; } @@ -4741,11 +7900,15 @@ static void *i802_init(struct hostapd_data *hapd, int ifindex, br_ifindex; int br_added = 0; - bss = wpa_driver_nl80211_init(hapd, params->ifname); + bss = wpa_driver_nl80211_init(hapd, params->ifname, + params->global_priv); if (bss == NULL) return NULL; drv = bss->drv; + drv->nlmode = NL80211_IFTYPE_AP; + drv->eapol_sock = -1; + if (linux_br_get(brname, params->ifname) == 0) { wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s", params->ifname, brname); @@ -4773,26 +7936,26 @@ static void *i802_init(struct hostapd_data *hapd, /* start listening for EAPOL on the default AP interface */ add_ifidx(drv, drv->ifindex); - if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0)) + if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0)) goto failed; if (params->bssid) { - if (linux_set_ifhwaddr(drv->ioctl_sock, bss->ifname, + if (linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname, params->bssid)) goto failed; } - if (wpa_driver_nl80211_set_mode(bss, IEEE80211_MODE_AP)) { + if (wpa_driver_nl80211_set_mode(bss, drv->nlmode)) { wpa_printf(MSG_ERROR, "nl80211: Failed to set interface %s " "into AP mode", bss->ifname); goto failed; } if (params->num_bridge && params->bridge[0] && - i802_check_bridge(drv, params->bridge[0], params->ifname) < 0) + i802_check_bridge(drv, bss, params->bridge[0], params->ifname) < 0) goto failed; - if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) + if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1)) goto failed; drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE)); @@ -4807,22 +7970,16 @@ static void *i802_init(struct hostapd_data *hapd, goto failed; } - if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, params->own_addr)) + if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, + params->own_addr)) goto failed; + memcpy(bss->addr, params->own_addr, ETH_ALEN); + return bss; failed: - nl80211_remove_monitor_interface(drv); - if (drv->ioctl_sock >= 0) - close(drv->ioctl_sock); - - genl_family_put(drv->nl80211); - nl_cache_free(drv->nl_cache); - nl_handle_destroy(drv->nl_handle); - nl_cb_put(drv->nl_cb); - - os_free(drv); + wpa_driver_nl80211_deinit(bss); return NULL; } @@ -4841,19 +7998,66 @@ static enum nl80211_iftype wpa_driver_nl80211_if_type( switch (type) { case WPA_IF_STATION: return NL80211_IFTYPE_STATION; + case WPA_IF_P2P_CLIENT: + case WPA_IF_P2P_GROUP: + return NL80211_IFTYPE_P2P_CLIENT; case WPA_IF_AP_VLAN: return NL80211_IFTYPE_AP_VLAN; case WPA_IF_AP_BSS: return NL80211_IFTYPE_AP; + case WPA_IF_P2P_GO: + return NL80211_IFTYPE_P2P_GO; } return -1; } +#ifdef CONFIG_P2P + +static int nl80211_addr_in_use(struct nl80211_global *global, const u8 *addr) +{ + struct wpa_driver_nl80211_data *drv; + dl_list_for_each(drv, &global->interfaces, + struct wpa_driver_nl80211_data, list) { + if (os_memcmp(addr, drv->first_bss.addr, ETH_ALEN) == 0) + return 1; + } + return 0; +} + + +static int nl80211_p2p_interface_addr(struct wpa_driver_nl80211_data *drv, + u8 *new_addr) +{ + unsigned int idx; + + if (!drv->global) + return -1; + + os_memcpy(new_addr, drv->first_bss.addr, ETH_ALEN); + for (idx = 0; idx < 64; idx++) { + new_addr[0] = drv->first_bss.addr[0] | 0x02; + new_addr[0] ^= idx << 2; + if (!nl80211_addr_in_use(drv->global, new_addr)) + break; + } + if (idx == 64) + return -1; + + wpa_printf(MSG_DEBUG, "nl80211: Assigned new P2P Interface Address " + MACSTR, MAC2STR(new_addr)); + + return 0; +} + +#endif /* CONFIG_P2P */ + + static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, const char *ifname, const u8 *addr, void *bss_ctx, void **drv_priv, - char *force_ifname, u8 *if_addr) + char *force_ifname, u8 *if_addr, + const char *bridge) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; @@ -4881,26 +8085,81 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, } if (!addr && - linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, if_addr) < 0) + linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, + if_addr) < 0) { + nl80211_remove_iface(drv, ifidx); return -1; + } + +#ifdef CONFIG_P2P + if (!addr && + (type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP || + type == WPA_IF_P2P_GO)) { + /* Enforce unique P2P Interface Address */ + u8 new_addr[ETH_ALEN], own_addr[ETH_ALEN]; + + if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, + own_addr) < 0 || + linux_get_ifhwaddr(drv->global->ioctl_sock, ifname, + new_addr) < 0) { + nl80211_remove_iface(drv, ifidx); + return -1; + } + if (os_memcmp(own_addr, new_addr, ETH_ALEN) == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Allocate new address " + "for P2P group interface"); + if (nl80211_p2p_interface_addr(drv, new_addr) < 0) { + nl80211_remove_iface(drv, ifidx); + return -1; + } + if (linux_set_ifhwaddr(drv->global->ioctl_sock, ifname, + new_addr) < 0) { + nl80211_remove_iface(drv, ifidx); + return -1; + } + } + os_memcpy(if_addr, new_addr, ETH_ALEN); + } +#endif /* CONFIG_P2P */ #ifdef HOSTAPD + if (bridge && + i802_check_bridge(drv, new_bss, bridge, ifname) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to add the new " + "interface %s to a bridge %s", ifname, bridge); + nl80211_remove_iface(drv, ifidx); + os_free(new_bss); + return -1; + } + if (type == WPA_IF_AP_BSS) { - if (linux_set_iface_flags(drv->ioctl_sock, ifname, 1)) { + if (linux_set_iface_flags(drv->global->ioctl_sock, ifname, 1)) + { nl80211_remove_iface(drv, ifidx); os_free(new_bss); return -1; } os_strlcpy(new_bss->ifname, ifname, IFNAMSIZ); + os_memcpy(new_bss->addr, if_addr, ETH_ALEN); new_bss->ifindex = ifidx; new_bss->drv = drv; new_bss->next = drv->first_bss.next; + new_bss->freq = drv->first_bss.freq; + new_bss->ctx = bss_ctx; drv->first_bss.next = new_bss; if (drv_priv) *drv_priv = new_bss; + nl80211_init_bss(new_bss); + + /* Subscribe management frames for this WPA_IF_AP_BSS */ + if (nl80211_setup_ap(new_bss)) + return -1; } #endif /* HOSTAPD */ + if (drv->global) + drv->global->if_add_ifindex = ifidx; + return 0; } @@ -4917,23 +8176,44 @@ static int wpa_driver_nl80211_if_remove(void *priv, __func__, type, ifname, ifindex); if (ifindex <= 0) return -1; + nl80211_remove_iface(drv, ifindex); #ifdef HOSTAPD if (type != WPA_IF_AP_BSS) return 0; - if (bss != &drv->first_bss) { - struct i802_bss *tbss = &drv->first_bss; - - while (tbss) { - if (tbss->next != bss) - continue; + if (bss->added_if_into_bridge) { + if (linux_br_del_if(drv->global->ioctl_sock, bss->brname, + bss->ifname) < 0) + wpa_printf(MSG_INFO, "nl80211: Failed to remove " + "interface %s from bridge %s: %s", + bss->ifname, bss->brname, strerror(errno)); + } + if (bss->added_bridge) { + if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0) + wpa_printf(MSG_INFO, "nl80211: Failed to remove " + "bridge %s: %s", + bss->brname, strerror(errno)); + } - tbss->next = bss->next; - os_free(bss); - break; + if (bss != &drv->first_bss) { + struct i802_bss *tbss; + + for (tbss = &drv->first_bss; tbss; tbss = tbss->next) { + if (tbss->next == bss) { + tbss->next = bss->next; + /* Unsubscribe management frames */ + nl80211_teardown_ap(bss); + nl80211_destroy_bss(bss); + os_free(bss); + bss = NULL; + break; + } } + if (bss) + wpa_printf(MSG_INFO, "nl80211: %s - could not find " + "BSS %p in the list", __func__, bss); } #endif /* HOSTAPD */ @@ -4954,21 +8234,77 @@ static int cookie_handler(struct nl_msg *msg, void *arg) } +static int nl80211_send_frame_cmd(struct i802_bss *bss, + unsigned int freq, unsigned int wait, + const u8 *buf, size_t buf_len, + u64 *cookie_out, int no_cck, int no_ack, + int offchanok) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + u64 cookie; + int ret = -1; + + msg = nlmsg_alloc(); + 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); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); + if (wait) + NLA_PUT_U32(msg, NL80211_ATTR_DURATION, wait); + if (offchanok && (drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX)) + NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK); + if (no_cck) + NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE); + if (no_ack) + NLA_PUT_FLAG(msg, NL80211_ATTR_DONT_WAIT_FOR_ACK); + + NLA_PUT(msg, NL80211_ATTR_FRAME, buf_len, buf); + + cookie = 0; + ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Frame command failed: ret=%d " + "(%s) (freq=%u wait=%u)", ret, strerror(-ret), + freq, wait); + goto nla_put_failure; + } + wpa_printf(MSG_DEBUG, "nl80211: Frame TX command accepted%s; " + "cookie 0x%llx", no_ack ? " (no ACK)" : "", + (long long unsigned int) cookie); + + if (cookie_out) + *cookie_out = no_ack ? (u64) -1 : cookie; + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + static int wpa_driver_nl80211_send_action(void *priv, unsigned int freq, + unsigned int wait_time, const u8 *dst, const u8 *src, const u8 *bssid, - const u8 *data, size_t data_len) + const u8 *data, size_t data_len, + int no_cck) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; int ret = -1; - struct nl_msg *msg; u8 *buf; struct ieee80211_hdr *hdr; - u64 cookie; - wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d)", - drv->ifindex); + wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d, " + "freq=%u MHz wait=%d ms no_cck=%d)", + drv->ifindex, freq, wait_time, no_cck); buf = os_zalloc(24 + data_len); if (buf == NULL) @@ -4981,44 +8317,46 @@ static int wpa_driver_nl80211_send_action(void *priv, unsigned int freq, os_memcpy(hdr->addr2, src, ETH_ALEN); os_memcpy(hdr->addr3, bssid, ETH_ALEN); - if (drv->nlmode == NL80211_IFTYPE_AP) { - ret = wpa_driver_nl80211_send_mlme(priv, buf, 24 + data_len); - os_free(buf); - return ret; - } + if (is_ap_interface(drv->nlmode)) + ret = wpa_driver_nl80211_send_mlme_freq(priv, buf, + 24 + data_len, + 0, freq, no_cck, 1, + wait_time); + else + ret = nl80211_send_frame_cmd(bss, freq, wait_time, buf, + 24 + data_len, + &drv->send_action_cookie, + no_cck, 0, 1); + + os_free(buf); + return ret; +} + + +static void wpa_driver_nl80211_send_action_cancel_wait(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; msg = nlmsg_alloc(); - if (!msg) { - os_free(buf); - return -1; - } + if (!msg) + return; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_ACTION, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_FRAME_WAIT_CANCEL); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); - NLA_PUT(msg, NL80211_ATTR_FRAME, 24 + data_len, buf); - os_free(buf); - buf = NULL; + NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->send_action_cookie); - cookie = 0; - ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; - if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: Action command failed: ret=%d " + if (ret) + wpa_printf(MSG_DEBUG, "nl80211: wait cancel failed: ret=%d " "(%s)", ret, strerror(-ret)); - goto nla_put_failure; - } - wpa_printf(MSG_DEBUG, "nl80211: Action TX command accepted; " - "cookie 0x%llx", (long long unsigned int) cookie); - drv->send_action_cookie = cookie; - ret = 0; -nla_put_failure: - os_free(buf); + nla_put_failure: nlmsg_free(msg); - return ret; } @@ -5035,8 +8373,7 @@ static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq, if (!msg) return -1; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_REMAIN_ON_CHANNEL, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_REMAIN_ON_CHANNEL); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); @@ -5044,16 +8381,20 @@ static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq, cookie = 0; ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie); + msg = NULL; if (ret == 0) { wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie " "0x%llx for freq=%u MHz duration=%u", (long long unsigned int) cookie, freq, duration); drv->remain_on_chan_cookie = cookie; + drv->pending_remain_on_chan = 1; return 0; } wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel " - "(freq=%d): %d (%s)", freq, ret, strerror(-ret)); + "(freq=%d duration=%u): %d (%s)", + freq, duration, ret, strerror(-ret)); nla_put_failure: + nlmsg_free(msg); return -1; } @@ -5079,76 +8420,75 @@ static int wpa_driver_nl80211_cancel_remain_on_channel(void *priv) if (!msg) return -1; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie); ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; if (ret == 0) return 0; wpa_printf(MSG_DEBUG, "nl80211: Failed to cancel remain-on-channel: " "%d (%s)", ret, strerror(-ret)); nla_put_failure: + nlmsg_free(msg); return -1; } -static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx, - void *timeout_ctx) -{ - struct wpa_driver_nl80211_data *drv = eloop_ctx; - if (drv->monitor_ifidx < 0) - return; /* monitor interface already removed */ - - if (drv->nlmode != NL80211_IFTYPE_STATION) - return; /* not in station mode anymore */ - - if (drv->probe_req_report) - return; /* reporting enabled */ - - wpa_printf(MSG_DEBUG, "nl80211: Remove monitor interface due to no " - "Probe Request reporting needed anymore"); - nl80211_remove_monitor_interface(drv); -} - - static int wpa_driver_nl80211_probe_req_report(void *priv, int report) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - if (drv->nlmode != NL80211_IFTYPE_STATION) { - wpa_printf(MSG_DEBUG, "nl80211: probe_req_report control only " - "allowed in station mode (iftype=%d)", - drv->nlmode); - return -1; + if (!report) { + if (bss->nl_preq && drv->device_ap_sme && + is_ap_interface(drv->nlmode)) { + /* + * Do not disable Probe Request reporting that was + * enabled in nl80211_setup_ap(). + */ + wpa_printf(MSG_DEBUG, "nl80211: Skip disabling of " + "Probe Request reporting nl_preq=%p while " + "in AP mode", bss->nl_preq); + } else if (bss->nl_preq) { + wpa_printf(MSG_DEBUG, "nl80211: Disable Probe Request " + "reporting nl_preq=%p", bss->nl_preq); + eloop_unregister_read_sock( + nl_socket_get_fd(bss->nl_preq)); + nl_destroy_handles(&bss->nl_preq); + } + return 0; } - drv->probe_req_report = report; - if (report) { - eloop_cancel_timeout( - wpa_driver_nl80211_probe_req_report_timeout, - drv, NULL); - if (drv->monitor_ifidx < 0 && - nl80211_create_monitor_interface(drv)) - return -1; - } else { - /* - * It takes a while to remove the monitor interface, so try to - * avoid doing this if it is needed again shortly. Instead, - * schedule the interface to be removed later if no need for it - * is seen. - */ - wpa_printf(MSG_DEBUG, "nl80211: Scheduling monitor interface " - "to be removed after 10 seconds of no use"); - eloop_register_timeout( - 10, 0, wpa_driver_nl80211_probe_req_report_timeout, - drv, NULL); + if (bss->nl_preq) { + wpa_printf(MSG_DEBUG, "nl80211: Probe Request reporting " + "already on! nl_preq=%p", bss->nl_preq); + return 0; } + bss->nl_preq = nl_create_handle(drv->global->nl_cb, "preq"); + if (bss->nl_preq == NULL) + return -1; + wpa_printf(MSG_DEBUG, "nl80211: Enable Probe Request " + "reporting nl_preq=%p", bss->nl_preq); + + if (nl80211_register_frame(bss, bss->nl_preq, + (WLAN_FC_TYPE_MGMT << 2) | + (WLAN_FC_STYPE_PROBE_REQ << 4), + NULL, 0) < 0) + goto out_err; + + eloop_register_read_sock(nl_socket_get_fd(bss->nl_preq), + wpa_driver_nl80211_event_receive, bss->nl_cb, + bss->nl_preq); + return 0; + + out_err: + nl_destroy_handles(&bss->nl_preq); + return -1; } @@ -5163,8 +8503,7 @@ static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv, if (!msg) return -1; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_SET_TX_BITRATE_MASK, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_TX_BITRATE_MASK); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES); @@ -5179,8 +8518,10 @@ static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv, band = nla_nest_start(msg, NL80211_BAND_2GHZ); if (!band) goto nla_put_failure; - NLA_PUT(msg, NL80211_TXRATE_LEGACY, 8, - "\x0c\x12\x18\x24\x30\x48\x60\x6c"); + if (disabled) { + NLA_PUT(msg, NL80211_TXRATE_LEGACY, 8, + "\x0c\x12\x18\x24\x30\x48\x60\x6c"); + } nla_nest_end(msg, band); nla_nest_end(msg, bands); @@ -5190,7 +8531,8 @@ static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv, if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Set TX rates failed: ret=%d " "(%s)", ret, strerror(-ret)); - } + } else + drv->disabled_11b_rates = disabled; return ret; @@ -5200,23 +8542,24 @@ nla_put_failure: } -static int wpa_driver_nl80211_disable_11b_rates(void *priv, int disabled) +static int wpa_driver_nl80211_deinit_ap(void *priv) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - drv->disable_11b_rates = disabled; - return nl80211_disable_11b_rates(drv, drv->ifindex, disabled); + if (!is_ap_interface(drv->nlmode)) + return -1; + wpa_driver_nl80211_del_beacon(drv); + return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION); } -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_AP) + if (drv->nlmode != NL80211_IFTYPE_P2P_CLIENT) return -1; - wpa_driver_nl80211_del_beacon(drv); - return wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA); + return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION); } @@ -5224,7 +8567,7 @@ static void wpa_driver_nl80211_resume(void *priv) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) { + if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1)) { wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on " "resume event"); } @@ -5239,10 +8582,7 @@ static int nl80211_send_ft_action(void *priv, u8 action, const u8 *target_ap, int ret; u8 *data, *pos; size_t data_len; - u8 own_addr[ETH_ALEN]; - - if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, own_addr) < 0) - return -1; + const u8 *own_addr = bss->addr; if (action != 1) { wpa_printf(MSG_ERROR, "nl80211: Unsupported send_ft_action " @@ -5272,9 +8612,9 @@ static int nl80211_send_ft_action(void *priv, u8 action, const u8 *target_ap, pos += ETH_ALEN; os_memcpy(pos, ies, ies_len); - ret = wpa_driver_nl80211_send_action(bss, drv->assoc_freq, drv->bssid, - own_addr, drv->bssid, - data, data_len); + ret = wpa_driver_nl80211_send_action(bss, drv->assoc_freq, 0, + drv->bssid, own_addr, drv->bssid, + data, data_len, 0); os_free(data); return ret; @@ -5286,6 +8626,7 @@ static int nl80211_signal_monitor(void *priv, int threshold, int hysteresis) struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg, *cqm = NULL; + int ret = -1; wpa_printf(MSG_DEBUG, "nl80211: Signal monitor threshold=%d " "hysteresis=%d", threshold, hysteresis); @@ -5294,28 +8635,82 @@ static int nl80211_signal_monitor(void *priv, int threshold, int hysteresis) if (!msg) return -1; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_SET_CQM, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_CQM); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); cqm = nlmsg_alloc(); if (cqm == NULL) - return -1; + goto nla_put_failure; NLA_PUT_U32(cqm, NL80211_ATTR_CQM_RSSI_THOLD, threshold); NLA_PUT_U32(cqm, NL80211_ATTR_CQM_RSSI_HYST, hysteresis); - nla_put_nested(msg, NL80211_ATTR_CQM, cqm); + if (nla_put_nested(msg, NL80211_ATTR_CQM, cqm) < 0) + goto nla_put_failure; - if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) - return 0; + ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; nla_put_failure: - if (cqm) - nlmsg_free(cqm); + nlmsg_free(cqm); nlmsg_free(msg); - return -1; + return ret; +} + + +static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int res; + + os_memset(si, 0, sizeof(*si)); + res = nl80211_get_link_signal(drv, si); + if (res != 0) + return res; + + return nl80211_get_link_noise(drv, si); +} + + +static int wpa_driver_nl80211_shared_freq(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct wpa_driver_nl80211_data *driver; + int freq = 0; + + /* + * If the same PHY is in connected state with some other interface, + * then retrieve the assoc freq. + */ + wpa_printf(MSG_DEBUG, "nl80211: Get shared freq for PHY %s", + drv->phyname); + + dl_list_for_each(driver, &drv->global->interfaces, + struct wpa_driver_nl80211_data, list) { + if (drv == driver || + os_strcmp(drv->phyname, driver->phyname) != 0 || + !driver->associated) + continue; + + wpa_printf(MSG_DEBUG, "nl80211: Found a match for PHY %s - %s " + MACSTR, + driver->phyname, driver->first_bss.ifname, + MAC2STR(driver->first_bss.addr)); + if (is_ap_interface(driver->nlmode)) + freq = driver->first_bss.freq; + else + freq = nl80211_get_assoc_freq(driver); + wpa_printf(MSG_DEBUG, "nl80211: Shared freq for PHY %s: %d", + drv->phyname, freq); + } + + if (!freq) + wpa_printf(MSG_DEBUG, "nl80211: No shared interface for " + "PHY (%s) in associated state", drv->phyname); + + return freq; } @@ -5323,11 +8718,523 @@ static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len, int encrypt) { struct i802_bss *bss = priv; + return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt, 0, + 0, 0, 0, 0); +} + + +static int nl80211_set_param(void *priv, const char *param) +{ + wpa_printf(MSG_DEBUG, "nl80211: driver param='%s'", param); + if (param == NULL) + return 0; + +#ifdef CONFIG_P2P + if (os_strstr(param, "use_p2p_group_interface=1")) { + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + + wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group " + "interface"); + drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT; + drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P; + } +#endif /* CONFIG_P2P */ + + return 0; +} + + +static void * nl80211_global_init(void) +{ + struct nl80211_global *global; + struct netlink_config *cfg; + + global = os_zalloc(sizeof(*global)); + if (global == NULL) + return NULL; + global->ioctl_sock = -1; + dl_list_init(&global->interfaces); + global->if_add_ifindex = -1; + + cfg = os_zalloc(sizeof(*cfg)); + if (cfg == NULL) + goto err; + + cfg->ctx = global; + cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink; + cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink; + global->netlink = netlink_init(cfg); + if (global->netlink == NULL) { + os_free(cfg); + goto err; + } + + if (wpa_driver_nl80211_init_nl_global(global) < 0) + goto err; + + global->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (global->ioctl_sock < 0) { + perror("socket(PF_INET,SOCK_DGRAM)"); + goto err; + } + + return global; + +err: + nl80211_global_deinit(global); + return NULL; +} + + +static void nl80211_global_deinit(void *priv) +{ + struct nl80211_global *global = priv; + if (global == NULL) + return; + if (!dl_list_empty(&global->interfaces)) { + wpa_printf(MSG_ERROR, "nl80211: %u interface(s) remain at " + "nl80211_global_deinit", + dl_list_len(&global->interfaces)); + } + + if (global->netlink) + netlink_deinit(global->netlink); + + nl_destroy_handles(&global->nl); + + if (global->nl_event) { + eloop_unregister_read_sock( + nl_socket_get_fd(global->nl_event)); + nl_destroy_handles(&global->nl_event); + } + + nl_cb_put(global->nl_cb); + + if (global->ioctl_sock >= 0) + close(global->ioctl_sock); + + os_free(global); +} + + +static const char * nl80211_get_radio_name(void *priv) +{ + struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - return wpa_driver_nl80211_send_frame(drv, data, data_len, encrypt); + return drv->phyname; +} + + +static int nl80211_pmkid(struct i802_bss *bss, int cmd, const u8 *bssid, + const u8 *pmkid) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(bss->drv, msg, 0, cmd); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); + if (pmkid) + NLA_PUT(msg, NL80211_ATTR_PMKID, 16, pmkid); + if (bssid) + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid); + + return send_and_recv_msgs(bss->drv, msg, NULL, NULL); + nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + + +static int nl80211_add_pmkid(void *priv, const u8 *bssid, const u8 *pmkid) +{ + struct i802_bss *bss = priv; + wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR, MAC2STR(bssid)); + return nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, bssid, pmkid); +} + + +static int nl80211_remove_pmkid(void *priv, const u8 *bssid, const u8 *pmkid) +{ + struct i802_bss *bss = priv; + wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR, + MAC2STR(bssid)); + return nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, bssid, pmkid); } +static int nl80211_flush_pmkid(void *priv) +{ + struct i802_bss *bss = priv; + wpa_printf(MSG_DEBUG, "nl80211: Flush PMKIDs"); + return nl80211_pmkid(bss, NL80211_CMD_FLUSH_PMKSA, NULL, NULL); +} + + +static void nl80211_set_rekey_info(void *priv, const u8 *kek, const u8 *kck, + const u8 *replay_ctr) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nlattr *replay_nested; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_REKEY_OFFLOAD); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); + + replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA); + if (!replay_nested) + goto nla_put_failure; + + NLA_PUT(msg, NL80211_REKEY_DATA_KEK, NL80211_KEK_LEN, kek); + NLA_PUT(msg, NL80211_REKEY_DATA_KCK, NL80211_KCK_LEN, kck); + NLA_PUT(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN, + replay_ctr); + + nla_nest_end(msg, replay_nested); + + send_and_recv_msgs(drv, msg, NULL, NULL); + return; + nla_put_failure: + nlmsg_free(msg); +} + + +static void nl80211_send_null_frame(struct i802_bss *bss, const u8 *own_addr, + const u8 *addr, int qos) +{ + /* send data frame to poll STA and check whether + * this frame is ACKed */ + struct { + struct ieee80211_hdr hdr; + u16 qos_ctl; + } STRUCT_PACKED nulldata; + size_t size; + + /* Send data frame to poll STA and check whether this frame is ACKed */ + + os_memset(&nulldata, 0, sizeof(nulldata)); + + if (qos) { + nulldata.hdr.frame_control = + IEEE80211_FC(WLAN_FC_TYPE_DATA, + WLAN_FC_STYPE_QOS_NULL); + size = sizeof(nulldata); + } else { + nulldata.hdr.frame_control = + IEEE80211_FC(WLAN_FC_TYPE_DATA, + WLAN_FC_STYPE_NULLFUNC); + size = sizeof(struct ieee80211_hdr); + } + + nulldata.hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS); + os_memcpy(nulldata.hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN); + os_memcpy(nulldata.hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); + os_memcpy(nulldata.hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); + + if (wpa_driver_nl80211_send_mlme(bss, (u8 *) &nulldata, size, 0) < 0) + wpa_printf(MSG_DEBUG, "nl80211_send_null_frame: Failed to " + "send poll frame"); +} + +static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr, + int qos) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + + if (!drv->poll_command_supported) { + nl80211_send_null_frame(bss, own_addr, addr, qos); + return; + } + + msg = nlmsg_alloc(); + if (!msg) + return; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_PROBE_CLIENT); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + + send_and_recv_msgs(drv, msg, NULL, NULL); + return; + nla_put_failure: + nlmsg_free(msg); +} + + +static int nl80211_set_power_save(struct i802_bss *bss, int enabled) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_SET_POWER_SAVE); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, + enabled ? NL80211_PS_ENABLED : NL80211_PS_DISABLED); + return send_and_recv_msgs(bss->drv, msg, NULL, NULL); +nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + + +static int nl80211_set_p2p_powersave(void *priv, int legacy_ps, int opp_ps, + int ctwindow) +{ + struct i802_bss *bss = priv; + + wpa_printf(MSG_DEBUG, "nl80211: set_p2p_powersave (legacy_ps=%d " + "opp_ps=%d ctwindow=%d)", legacy_ps, opp_ps, ctwindow); + + if (opp_ps != -1 || ctwindow != -1) + return -1; /* Not yet supported */ + + if (legacy_ps == -1) + return 0; + if (legacy_ps != 0 && legacy_ps != 1) + return -1; /* Not yet supported */ + + return nl80211_set_power_save(bss, legacy_ps); +} + + +#ifdef CONFIG_TDLS + +static int nl80211_send_tdls_mgmt(void *priv, const u8 *dst, u8 action_code, + u8 dialog_token, u16 status_code, + const u8 *buf, size_t len) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) + return -EOPNOTSUPP; + + if (!dst) + return -EINVAL; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_TDLS_MGMT); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst); + NLA_PUT_U8(msg, NL80211_ATTR_TDLS_ACTION, action_code); + NLA_PUT_U8(msg, NL80211_ATTR_TDLS_DIALOG_TOKEN, dialog_token); + NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status_code); + NLA_PUT(msg, NL80211_ATTR_IE, len, buf); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + +nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + + +static int nl80211_tdls_oper(void *priv, enum tdls_oper oper, const u8 *peer) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + enum nl80211_tdls_operation nl80211_oper; + + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) + return -EOPNOTSUPP; + + switch (oper) { + case TDLS_DISCOVERY_REQ: + nl80211_oper = NL80211_TDLS_DISCOVERY_REQ; + break; + case TDLS_SETUP: + nl80211_oper = NL80211_TDLS_SETUP; + break; + case TDLS_TEARDOWN: + nl80211_oper = NL80211_TDLS_TEARDOWN; + break; + case TDLS_ENABLE_LINK: + nl80211_oper = NL80211_TDLS_ENABLE_LINK; + break; + case TDLS_DISABLE_LINK: + nl80211_oper = NL80211_TDLS_DISABLE_LINK; + break; + case TDLS_ENABLE: + return 0; + case TDLS_DISABLE: + return 0; + default: + return -EINVAL; + } + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_TDLS_OPER); + NLA_PUT_U8(msg, NL80211_ATTR_TDLS_OPERATION, nl80211_oper); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, peer); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + +nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + +#endif /* CONFIG TDLS */ + + +#ifdef ANDROID + +typedef struct android_wifi_priv_cmd { + char *buf; + int used_len; + int total_len; +} android_wifi_priv_cmd; + +static int drv_errors = 0; + +static void wpa_driver_send_hang_msg(struct wpa_driver_nl80211_data *drv) +{ + drv_errors++; + if (drv_errors > DRV_NUMBER_SEQUENTIAL_ERRORS) { + drv_errors = 0; + wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED"); + } +} + + +static int android_priv_cmd(struct i802_bss *bss, const char *cmd) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct ifreq ifr; + android_wifi_priv_cmd priv_cmd; + char buf[MAX_DRV_CMD_SIZE]; + int ret; + + os_memset(&ifr, 0, sizeof(ifr)); + os_memset(&priv_cmd, 0, sizeof(priv_cmd)); + os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ); + + os_memset(buf, 0, sizeof(buf)); + os_strlcpy(buf, cmd, sizeof(buf)); + + priv_cmd.buf = buf; + priv_cmd.used_len = sizeof(buf); + priv_cmd.total_len = sizeof(buf); + ifr.ifr_data = &priv_cmd; + + ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: failed to issue private commands", + __func__); + wpa_driver_send_hang_msg(drv); + return ret; + } + + drv_errors = 0; + return 0; +} + + +static int android_pno_start(struct i802_bss *bss, + struct wpa_driver_scan_params *params) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct ifreq ifr; + android_wifi_priv_cmd priv_cmd; + int ret = 0, i = 0, bp; + char buf[WEXT_PNO_MAX_COMMAND_SIZE]; + + bp = WEXT_PNOSETUP_HEADER_SIZE; + os_memcpy(buf, WEXT_PNOSETUP_HEADER, bp); + buf[bp++] = WEXT_PNO_TLV_PREFIX; + buf[bp++] = WEXT_PNO_TLV_VERSION; + buf[bp++] = WEXT_PNO_TLV_SUBVERSION; + buf[bp++] = WEXT_PNO_TLV_RESERVED; + + while (i < WEXT_PNO_AMOUNT && (size_t) i < params->num_ssids) { + /* Check that there is enough space needed for 1 more SSID, the + * other sections and null termination */ + if ((bp + WEXT_PNO_SSID_HEADER_SIZE + MAX_SSID_LEN + + WEXT_PNO_NONSSID_SECTIONS_SIZE + 1) >= (int) sizeof(buf)) + break; + wpa_hexdump_ascii(MSG_DEBUG, "For PNO Scan", + params->ssids[i].ssid, + params->ssids[i].ssid_len); + buf[bp++] = WEXT_PNO_SSID_SECTION; + buf[bp++] = params->ssids[i].ssid_len; + os_memcpy(&buf[bp], params->ssids[i].ssid, + params->ssids[i].ssid_len); + bp += params->ssids[i].ssid_len; + i++; + } + + buf[bp++] = WEXT_PNO_SCAN_INTERVAL_SECTION; + os_snprintf(&buf[bp], WEXT_PNO_SCAN_INTERVAL_LENGTH + 1, "%x", + WEXT_PNO_SCAN_INTERVAL); + bp += WEXT_PNO_SCAN_INTERVAL_LENGTH; + + buf[bp++] = WEXT_PNO_REPEAT_SECTION; + os_snprintf(&buf[bp], WEXT_PNO_REPEAT_LENGTH + 1, "%x", + WEXT_PNO_REPEAT); + bp += WEXT_PNO_REPEAT_LENGTH; + + buf[bp++] = WEXT_PNO_MAX_REPEAT_SECTION; + os_snprintf(&buf[bp], WEXT_PNO_MAX_REPEAT_LENGTH + 1, "%x", + WEXT_PNO_MAX_REPEAT); + bp += WEXT_PNO_MAX_REPEAT_LENGTH + 1; + + memset(&ifr, 0, sizeof(ifr)); + memset(&priv_cmd, 0, sizeof(priv_cmd)); + os_strncpy(ifr.ifr_name, bss->ifname, IFNAMSIZ); + + priv_cmd.buf = buf; + priv_cmd.used_len = bp; + priv_cmd.total_len = bp; + ifr.ifr_data = &priv_cmd; + + ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr); + + if (ret < 0) { + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (pnosetup): %d", + ret); + wpa_driver_send_hang_msg(drv); + return ret; + } + + drv_errors = 0; + + return android_priv_cmd(bss, "PNOFORCE 1"); +} + + +static int android_pno_stop(struct i802_bss *bss) +{ + return android_priv_cmd(bss, "PNOFORCE 0"); +} + +#endif /* ANDROID */ + + const struct wpa_driver_ops wpa_driver_nl80211_ops = { .name = "nl80211", .desc = "Linux nl80211/cfg80211", @@ -5335,18 +9242,21 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .get_ssid = wpa_driver_nl80211_get_ssid, .set_key = wpa_driver_nl80211_set_key, .scan2 = wpa_driver_nl80211_scan, + .sched_scan = wpa_driver_nl80211_sched_scan, + .stop_sched_scan = wpa_driver_nl80211_stop_sched_scan, .get_scan_results2 = wpa_driver_nl80211_get_scan_results, .deauthenticate = wpa_driver_nl80211_deauthenticate, - .disassociate = wpa_driver_nl80211_disassociate, .authenticate = wpa_driver_nl80211_authenticate, .associate = wpa_driver_nl80211_associate, - .init = wpa_driver_nl80211_init, + .global_init = nl80211_global_init, + .global_deinit = nl80211_global_deinit, + .init2 = wpa_driver_nl80211_init, .deinit = wpa_driver_nl80211_deinit, .get_capa = wpa_driver_nl80211_get_capa, .set_operstate = wpa_driver_nl80211_set_operstate, .set_supp_port = wpa_driver_nl80211_set_supp_port, .set_country = wpa_driver_nl80211_set_country, - .set_beacon = wpa_driver_nl80211_set_beacon, + .set_ap = wpa_driver_nl80211_set_ap, .if_add = wpa_driver_nl80211_if_add, .if_remove = wpa_driver_nl80211_if_remove, .send_mlme = wpa_driver_nl80211_send_mlme, @@ -5358,33 +9268,46 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { #ifdef HOSTAPD .hapd_init = i802_init, .hapd_deinit = i802_deinit, + .set_wds_sta = i802_set_wds_sta, +#endif /* HOSTAPD */ +#if defined(HOSTAPD) || defined(CONFIG_AP) .get_seqnum = i802_get_seqnum, .flush = i802_flush, - .read_sta_data = i802_read_sta_data, - .sta_deauth = i802_sta_deauth, - .sta_disassoc = i802_sta_disassoc, .get_inact_sec = i802_get_inact_sec, .sta_clear_stats = i802_sta_clear_stats, .set_rts = i802_set_rts, .set_frag = i802_set_frag, - .set_rate_sets = i802_set_rate_sets, - .set_cts_protect = i802_set_cts_protect, - .set_preamble = i802_set_preamble, - .set_short_slot_time = i802_set_short_slot_time, .set_tx_queue_params = i802_set_tx_queue_params, .set_sta_vlan = i802_set_sta_vlan, - .set_wds_sta = i802_set_wds_sta, -#endif /* HOSTAPD */ + .sta_deauth = i802_sta_deauth, + .sta_disassoc = i802_sta_disassoc, +#endif /* HOSTAPD || CONFIG_AP */ + .read_sta_data = i802_read_sta_data, .set_freq = i802_set_freq, .send_action = wpa_driver_nl80211_send_action, + .send_action_cancel_wait = wpa_driver_nl80211_send_action_cancel_wait, .remain_on_channel = wpa_driver_nl80211_remain_on_channel, .cancel_remain_on_channel = wpa_driver_nl80211_cancel_remain_on_channel, .probe_req_report = wpa_driver_nl80211_probe_req_report, - .disable_11b_rates = wpa_driver_nl80211_disable_11b_rates, .deinit_ap = wpa_driver_nl80211_deinit_ap, + .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, + .signal_poll = nl80211_signal_poll, .send_frame = nl80211_send_frame, + .shared_freq = wpa_driver_nl80211_shared_freq, + .set_param = nl80211_set_param, + .get_radio_name = nl80211_get_radio_name, + .add_pmkid = nl80211_add_pmkid, + .remove_pmkid = nl80211_remove_pmkid, + .flush_pmkid = nl80211_flush_pmkid, + .set_rekey_info = nl80211_set_rekey_info, + .poll_client = nl80211_poll_client, + .set_p2p_powersave = nl80211_set_p2p_powersave, +#ifdef CONFIG_TDLS + .send_tdls_mgmt = nl80211_send_tdls_mgmt, + .tdls_oper = nl80211_tdls_oper, +#endif /* CONFIG_TDLS */ }; diff --git a/src/drivers/driver_none.c b/src/drivers/driver_none.c index aaeacd66435dd..d75c14b182f93 100644 --- a/src/drivers/driver_none.c +++ b/src/drivers/driver_none.c @@ -2,14 +2,8 @@ * Driver interface for RADIUS server or WPS ER only (no driver) * Copyright (c) 2008, Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * 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_osx.m b/src/drivers/driver_osx.m deleted file mode 100644 index 69ca4b576c3c0..0000000000000 --- a/src/drivers/driver_osx.m +++ /dev/null @@ -1,459 +0,0 @@ -/* - * WPA Supplicant - Mac OS X Apple80211 driver interface - * Copyright (c) 2007, Jouni Malinen <j@w1.fi> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" -#define Boolean __DummyBoolean -#include <CoreFoundation/CoreFoundation.h> -#undef Boolean - -#include "common.h" -#include "driver.h" -#include "eloop.h" -#include "common/ieee802_11_defs.h" - -#include "Apple80211.h" - -struct wpa_driver_osx_data { - void *ctx; - WirelessRef wireless_ctx; - CFArrayRef scan_results; -}; - - -#ifndef CONFIG_NO_STDOUT_DEBUG -extern int wpa_debug_level; - -static void dump_dict_cb(const void *key, const void *value, void *context) -{ - if (MSG_DEBUG < wpa_debug_level) - return; - - wpa_printf(MSG_DEBUG, "Key:"); - CFShow(key); - wpa_printf(MSG_DEBUG, "Value:"); - CFShow(value); -} -#endif /* CONFIG_NO_STDOUT_DEBUG */ - - -static void wpa_driver_osx_dump_dict(CFDictionaryRef dict, const char *title) -{ -#ifndef CONFIG_NO_STDOUT_DEBUG - wpa_printf(MSG_DEBUG, "OSX: Dump dictionary %s - %u entries", - title, (unsigned int) CFDictionaryGetCount(dict)); - CFDictionaryApplyFunction(dict, dump_dict_cb, NULL); -#endif /* CONFIG_NO_STDOUT_DEBUG */ -} - - -static int wpa_driver_osx_get_ssid(void *priv, u8 *ssid) -{ - struct wpa_driver_osx_data *drv = priv; - WirelessError err; - WirelessInfo info; - int len; - - err = WirelessGetInfo(drv->wireless_ctx, &info); - if (err) { - wpa_printf(MSG_DEBUG, "OSX: WirelessGetInfo failed: %d", - (int) err); - return -1; - } - if (!info.power) { - wpa_printf(MSG_DEBUG, "OSX: Wireless device power off"); - return -1; - } - - for (len = 0; len < 32; len++) - if (info.ssid[len] == 0) - break; - - os_memcpy(ssid, info.ssid, len); - return len; -} - - -static int wpa_driver_osx_get_bssid(void *priv, u8 *bssid) -{ - struct wpa_driver_osx_data *drv = priv; - WirelessError err; - WirelessInfo info; - - err = WirelessGetInfo(drv->wireless_ctx, &info); - if (err) { - wpa_printf(MSG_DEBUG, "OSX: WirelessGetInfo failed: %d", - (int) err); - return -1; - } - if (!info.power) { - wpa_printf(MSG_DEBUG, "OSX: Wireless device power off"); - return -1; - } - - os_memcpy(bssid, info.bssID, ETH_ALEN); - return 0; -} - - -static void wpa_driver_osx_scan_timeout(void *eloop_ctx, void *timeout_ctx) -{ - wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); -} - - -static int wpa_driver_osx_scan(void *priv, struct wpa_driver_scan_params *params) -{ - struct wpa_driver_osx_data *drv = priv; - WirelessError err; - const u8 *ssid = params->ssids[0].ssid; - size_t ssid_len = params->ssids[0].ssid_len; - - if (drv->scan_results) { - CFRelease(drv->scan_results); - drv->scan_results = NULL; - } - - if (ssid) { - CFStringRef data; - data = CFStringCreateWithBytes(kCFAllocatorDefault, - ssid, ssid_len, - kCFStringEncodingISOLatin1, - FALSE); - if (data == NULL) { - wpa_printf(MSG_DEBUG, "CFStringCreateWithBytes " - "failed"); - return -1; - } - - err = WirelessDirectedScan(drv->wireless_ctx, - &drv->scan_results, 0, data); - CFRelease(data); - if (err) { - wpa_printf(MSG_DEBUG, "OSX: WirelessDirectedScan " - "failed: 0x%08x", (unsigned int) err); - return -1; - } - } else { - err = WirelessScan(drv->wireless_ctx, &drv->scan_results, 0); - if (err) { - wpa_printf(MSG_DEBUG, "OSX: WirelessScan failed: " - "0x%08x", (unsigned int) err); - return -1; - } - } - - eloop_register_timeout(0, 0, wpa_driver_osx_scan_timeout, drv, - drv->ctx); - return 0; -} - - -static void wpa_driver_osx_add_scan_entry(struct wpa_scan_results *res, - WirelessNetworkInfo *info) -{ - struct wpa_scan_res *result, **tmp; - size_t extra_len; - u8 *pos; - - extra_len = 2 + info->ssid_len; - - result = os_zalloc(sizeof(*result) + extra_len); - if (result == NULL) - return; - os_memcpy(result->bssid, info->bssid, ETH_ALEN); - result->freq = 2407 + info->channel * 5; - //result->beacon_int =; - result->caps = info->capability; - //result->qual = info->signal; - result->noise = info->noise; - - pos = (u8 *)(result + 1); - - *pos++ = WLAN_EID_SSID; - *pos++ = info->ssid_len; - os_memcpy(pos, info->ssid, info->ssid_len); - pos += info->ssid_len; - - result->ie_len = pos - (u8 *)(result + 1); - - tmp = os_realloc(res->res, - (res->num + 1) * sizeof(struct wpa_scan_res *)); - if (tmp == NULL) { - os_free(result); - return; - } - tmp[res->num++] = result; - res->res = tmp; -} - - -static struct wpa_scan_results * wpa_driver_osx_get_scan_results(void *priv) -{ - struct wpa_driver_osx_data *drv = priv; - struct wpa_scan_results *res; - size_t i, num; - - if (drv->scan_results == NULL) - return 0; - - num = CFArrayGetCount(drv->scan_results); - - res = os_zalloc(sizeof(*res)); - if (res == NULL) - return NULL; - - for (i = 0; i < num; i++) - wpa_driver_osx_add_scan_entry(res, (WirelessNetworkInfo *) - CFDataGetBytePtr(CFArrayGetValueAtIndex( - drv->scan_results, i))); - - return res; -} - - -static void wpa_driver_osx_assoc_timeout(void *eloop_ctx, void *timeout_ctx) -{ - struct wpa_driver_osx_data *drv = eloop_ctx; - u8 bssid[ETH_ALEN]; - CFDictionaryRef ai; - - if (wpa_driver_osx_get_bssid(drv, bssid) != 0) { - eloop_register_timeout(1, 0, wpa_driver_osx_assoc_timeout, - drv, drv->ctx); - return; - } - - ai = WirelessGetAssociationInfo(drv->wireless_ctx); - if (ai) { - wpa_driver_osx_dump_dict(ai, "WirelessGetAssociationInfo"); - CFRelease(ai); - } else { - wpa_printf(MSG_DEBUG, "OSX: Failed to get association info"); - } - - wpa_supplicant_event(timeout_ctx, EVENT_ASSOC, NULL); -} - - -static int wpa_driver_osx_associate(void *priv, - struct wpa_driver_associate_params *params) -{ - struct wpa_driver_osx_data *drv = priv; - WirelessError err; - CFDataRef ssid; - CFStringRef key; - int assoc_type; - - ssid = CFDataCreate(kCFAllocatorDefault, params->ssid, - params->ssid_len); - if (ssid == NULL) - return -1; - - /* TODO: support for WEP */ - if (params->key_mgmt_suite == KEY_MGMT_PSK) { - if (params->passphrase == NULL) - return -1; - key = CFStringCreateWithCString(kCFAllocatorDefault, - params->passphrase, - kCFStringEncodingISOLatin1); - if (key == NULL) { - CFRelease(ssid); - return -1; - } - } else - key = NULL; - - if (params->key_mgmt_suite == KEY_MGMT_NONE) - assoc_type = 0; - else - assoc_type = 4; - - wpa_printf(MSG_DEBUG, "OSX: WirelessAssociate(type=%d key=%p)", - assoc_type, key); - err = WirelessAssociate(drv->wireless_ctx, assoc_type, ssid, key); - CFRelease(ssid); - if (key) - CFRelease(key); - if (err) { - wpa_printf(MSG_DEBUG, "OSX: WirelessAssociate failed: 0x%08x", - (unsigned int) err); - return -1; - } - - /* - * Driver is actually already associated; report association from an - * eloop callback. - */ - eloop_cancel_timeout(wpa_driver_osx_assoc_timeout, drv, drv->ctx); - eloop_register_timeout(0, 0, wpa_driver_osx_assoc_timeout, drv, - drv->ctx); - - return 0; -} - - -static int wpa_driver_osx_set_key(const char *ifname, void *priv, - enum wpa_alg alg, const u8 *addr, - int key_idx, int set_tx, const u8 *seq, - size_t seq_len, const u8 *key, - size_t key_len) -{ - struct wpa_driver_osx_data *drv = priv; - WirelessError err; - - if (alg == WPA_ALG_WEP) { - err = WirelessSetKey(drv->wireless_ctx, 1, key_idx, key_len, - key); - if (err != 0) { - wpa_printf(MSG_DEBUG, "OSX: WirelessSetKey failed: " - "0x%08x", (unsigned int) err); - return -1; - } - - return 0; - } - - if (alg == WPA_ALG_PMK) { - err = WirelessSetWPAKey(drv->wireless_ctx, 1, key_len, key); - if (err != 0) { - wpa_printf(MSG_DEBUG, "OSX: WirelessSetWPAKey failed: " - "0x%08x", (unsigned int) err); - return -1; - } - return 0; - } - - wpa_printf(MSG_DEBUG, "OSX: Unsupported set_key alg %d", alg); - return -1; -} - - -static int wpa_driver_osx_get_capa(void *priv, struct wpa_driver_capa *capa) -{ - os_memset(capa, 0, sizeof(*capa)); - - capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | - WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | - WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | - WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; - capa->enc = WPA_DRIVER_CAPA_ENC_WEP40 | WPA_DRIVER_CAPA_ENC_WEP104 | - WPA_DRIVER_CAPA_ENC_TKIP | WPA_DRIVER_CAPA_ENC_CCMP; - capa->auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED | - WPA_DRIVER_AUTH_LEAP; - capa->flags = WPA_DRIVER_FLAGS_4WAY_HANDSHAKE; - - return 0; -} - - -static void * wpa_driver_osx_init(void *ctx, const char *ifname) -{ - struct wpa_driver_osx_data *drv; - WirelessError err; - u8 enabled, power; - - if (!WirelessIsAvailable()) { - wpa_printf(MSG_ERROR, "OSX: No wireless interface available"); - return NULL; - } - - drv = os_zalloc(sizeof(*drv)); - if (drv == NULL) - return NULL; - drv->ctx = ctx; - err = WirelessAttach(&drv->wireless_ctx, 0); - if (err) { - wpa_printf(MSG_ERROR, "OSX: WirelessAttach failed: %d", - (int) err); - os_free(drv); - return NULL; - } - - err = WirelessGetEnabled(drv->wireless_ctx, &enabled); - if (err) - wpa_printf(MSG_DEBUG, "OSX: WirelessGetEnabled failed: 0x%08x", - (unsigned int) err); - err = WirelessGetPower(drv->wireless_ctx, &power); - if (err) - wpa_printf(MSG_DEBUG, "OSX: WirelessGetPower failed: 0x%08x", - (unsigned int) err); - - wpa_printf(MSG_DEBUG, "OSX: Enabled=%d Power=%d", enabled, power); - - if (!enabled) { - err = WirelessSetEnabled(drv->wireless_ctx, 1); - if (err) { - wpa_printf(MSG_DEBUG, "OSX: WirelessSetEnabled failed:" - " 0x%08x", (unsigned int) err); - WirelessDetach(drv->wireless_ctx); - os_free(drv); - return NULL; - } - } - - if (!power) { - err = WirelessSetPower(drv->wireless_ctx, 1); - if (err) { - wpa_printf(MSG_DEBUG, "OSX: WirelessSetPower failed: " - "0x%08x", (unsigned int) err); - WirelessDetach(drv->wireless_ctx); - os_free(drv); - return NULL; - } - } - - return drv; -} - - -static void wpa_driver_osx_deinit(void *priv) -{ - struct wpa_driver_osx_data *drv = priv; - WirelessError err; - - eloop_cancel_timeout(wpa_driver_osx_scan_timeout, drv, drv->ctx); - eloop_cancel_timeout(wpa_driver_osx_assoc_timeout, drv, drv->ctx); - - err = WirelessSetPower(drv->wireless_ctx, 0); - if (err) { - wpa_printf(MSG_DEBUG, "OSX: WirelessSetPower(0) failed: " - "0x%08x", (unsigned int) err); - } - - err = WirelessDetach(drv->wireless_ctx); - if (err) { - wpa_printf(MSG_DEBUG, "OSX: WirelessDetach failed: 0x%08x", - (unsigned int) err); - } - - if (drv->scan_results) - CFRelease(drv->scan_results); - - os_free(drv); -} - - -const struct wpa_driver_ops wpa_driver_osx_ops = { - .name = "osx", - .desc = "Mac OS X Apple80211 driver", - .get_ssid = wpa_driver_osx_get_ssid, - .get_bssid = wpa_driver_osx_get_bssid, - .init = wpa_driver_osx_init, - .deinit = wpa_driver_osx_deinit, - .scan2 = wpa_driver_osx_scan, - .get_scan_results2 = wpa_driver_osx_get_scan_results, - .associate = wpa_driver_osx_associate, - .set_key = wpa_driver_osx_set_key, - .get_capa = wpa_driver_osx_get_capa, -}; diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c index 28485215e2b28..ed88e71c3a77c 100644 --- a/src/drivers/driver_privsep.c +++ b/src/drivers/driver_privsep.c @@ -2,14 +2,8 @@ * WPA Supplicant - privilege separated driver interface * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -158,7 +152,7 @@ wpa_driver_privsep_get_scan_results2(void *priv) return NULL; } - results->res = os_zalloc(num * sizeof(struct wpa_scan_res *)); + results->res = os_calloc(num, sizeof(struct wpa_scan_res *)); if (results->res == NULL) { os_free(results); os_free(buf); @@ -310,17 +304,6 @@ static int wpa_driver_privsep_deauthenticate(void *priv, const u8 *addr, } -static int wpa_driver_privsep_disassociate(void *priv, const u8 *addr, - int reason_code) -{ - //struct wpa_driver_privsep_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d", - __func__, MAC2STR(addr), reason_code); - wpa_printf(MSG_DEBUG, "%s - TODO", __func__); - return 0; -} - - static void wpa_driver_privsep_event_assoc(void *ctx, enum wpa_event_type event, u8 *buf, size_t len) @@ -657,7 +640,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); @@ -682,7 +665,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); @@ -742,7 +725,6 @@ struct wpa_driver_ops wpa_driver_privsep_ops = { .set_param = wpa_driver_privsep_set_param, .scan2 = wpa_driver_privsep_scan, .deauthenticate = wpa_driver_privsep_deauthenticate, - .disassociate = wpa_driver_privsep_disassociate, .associate = wpa_driver_privsep_associate, .get_capa = wpa_driver_privsep_get_capa, .get_mac_addr = wpa_driver_privsep_get_mac_addr, diff --git a/src/drivers/driver_ralink.c b/src/drivers/driver_ralink.c deleted file mode 100644 index 09d1ef5405090..0000000000000 --- a/src/drivers/driver_ralink.c +++ /dev/null @@ -1,1499 +0,0 @@ -/* - * WPA Supplicant - driver interaction with Ralink Wireless Client - * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> - * Copyright (c) 2007, Snowpin Lee <snowpin_lee@ralinktech.com.tw> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - * - */ - -#include "includes.h" -#include <sys/ioctl.h> - -#include "wireless_copy.h" -#include "common.h" -#include "driver.h" -#include "l2_packet/l2_packet.h" -#include "eloop.h" -#include "common/ieee802_11_defs.h" -#include "priv_netlink.h" -#include "netlink.h" -#include "linux_ioctl.h" -#include "driver_ralink.h" - -static void wpa_driver_ralink_scan_timeout(void *eloop_ctx, void *timeout_ctx); - -#define MAX_SSID_LEN 32 - -struct wpa_driver_ralink_data { - void *ctx; - int ioctl_sock; - struct netlink_data *netlink; - char ifname[IFNAMSIZ + 1]; - u8 *assoc_req_ies; - size_t assoc_req_ies_len; - u8 *assoc_resp_ies; - size_t assoc_resp_ies_len; - int no_of_pmkid; - struct ndis_pmkid_entry *pmkid; - int we_version_compiled; - int ap_scan; - int scanning_done; - u8 g_driver_down; - BOOLEAN bAddWepKey; -}; - -static int ralink_set_oid(struct wpa_driver_ralink_data *drv, - unsigned short oid, char *data, int len) -{ - char *buf; - struct iwreq iwr; - - buf = os_zalloc(len); - if (buf == NULL) - return -1; - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - iwr.u.data.flags = oid; - iwr.u.data.flags |= OID_GET_SET_TOGGLE; - - if (data) - os_memcpy(buf, data, len); - - iwr.u.data.pointer = (caddr_t) buf; - iwr.u.data.length = len; - - if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) { - wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%d) failed", - __func__, oid, len); - os_free(buf); - return -1; - } - os_free(buf); - return 0; -} - -static int -ralink_get_new_driver_flag(struct wpa_driver_ralink_data *drv) -{ - struct iwreq iwr; - UCHAR enabled = 0; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - iwr.u.data.pointer = (UCHAR*) &enabled; - iwr.u.data.flags = RT_OID_NEW_DRIVER; - - if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) { - wpa_printf(MSG_DEBUG, "%s: failed", __func__); - return 0; - } - - return (enabled == 1) ? 1 : 0; -} - -static int wpa_driver_ralink_get_bssid(void *priv, u8 *bssid) -{ - struct wpa_driver_ralink_data *drv = priv; - struct iwreq iwr; - int ret = 0; - - if (drv->g_driver_down == 1) - return -1; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - - if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) { - perror("ioctl[SIOCGIWAP]"); - ret = -1; - } - os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN); - - return ret; -} - -static int wpa_driver_ralink_get_ssid(void *priv, u8 *ssid) -{ - struct wpa_driver_ralink_data *drv = priv; -#if 0 - struct wpa_supplicant *wpa_s = drv->ctx; - struct wpa_ssid *entry; -#endif - int ssid_len; - u8 bssid[ETH_ALEN]; - u8 ssid_str[MAX_SSID_LEN]; - struct iwreq iwr; -#if 0 - int result = 0; -#endif - int ret = 0; -#if 0 - BOOLEAN ieee8021x_mode = FALSE; - BOOLEAN ieee8021x_required_key = FALSE; -#endif - - if (drv->g_driver_down == 1) - return -1; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - iwr.u.essid.pointer = (caddr_t) ssid; - iwr.u.essid.length = 32; - - if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { - perror("ioctl[SIOCGIWESSID]"); - ret = -1; - } else - ret = iwr.u.essid.length; - - if (ret <= 0) - return ret; - - ssid_len = ret; - os_memset(ssid_str, 0, MAX_SSID_LEN); - os_memcpy(ssid_str, ssid, ssid_len); - - if (drv->ap_scan == 0) { - /* Read BSSID form driver */ - if (wpa_driver_ralink_get_bssid(priv, bssid) < 0) { - wpa_printf(MSG_WARNING, "Could not read BSSID from " - "driver."); - return ret; - } - -#if 0 - entry = wpa_s->conf->ssid; - while (entry) { - if (!entry->disabled && ssid_len == entry->ssid_len && - os_memcmp(ssid_str, entry->ssid, ssid_len) == 0 && - (!entry->bssid_set || - os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)) { - /* match the config of driver */ - result = 1; - break; - } - entry = entry->next; - } - - if (result) { - wpa_printf(MSG_DEBUG, "Ready to set 802.1x mode and " - "ieee_required_keys parameters to driver"); - - /* set 802.1x mode and ieee_required_keys parameter */ - if (entry->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { - if ((entry->eapol_flags & (EAPOL_FLAG_REQUIRE_KEY_UNICAST | EAPOL_FLAG_REQUIRE_KEY_BROADCAST))) - ieee8021x_required_key = TRUE; - ieee8021x_mode = TRUE; - } - - if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X, (char *) &ieee8021x_mode, sizeof(BOOLEAN)) < 0) - { - wpa_printf(MSG_DEBUG, "RALINK: Failed to set OID_802_11_SET_IEEE8021X(%d)", (int) ieee8021x_mode); - } - else - { - wpa_printf(MSG_DEBUG, "ieee8021x_mode is %s", ieee8021x_mode ? "TRUE" : "FALSE"); - } - - if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X_REQUIRE_KEY, (char *) &ieee8021x_required_key, sizeof(BOOLEAN)) < 0) - { - wpa_printf(MSG_DEBUG, "ERROR: Failed to set OID_802_11_SET_IEEE8021X_REQUIRE_KEY(%d)", (int) ieee8021x_required_key); - } - else - { - wpa_printf(MSG_DEBUG, "ieee8021x_required_key is %s and eapol_flag(%d)", ieee8021x_required_key ? "TRUE" : "FALSE", - entry->eapol_flags); - } - } -#endif - } - - return ret; -} - -static int wpa_driver_ralink_set_ssid(struct wpa_driver_ralink_data *drv, - const u8 *ssid, size_t ssid_len) -{ - NDIS_802_11_SSID *buf; - int ret = 0; - struct iwreq iwr; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - buf = os_zalloc(sizeof(NDIS_802_11_SSID)); - if (buf == NULL) - return -1; - os_memset(buf, 0, sizeof(buf)); - buf->SsidLength = ssid_len; - os_memcpy(buf->Ssid, ssid, ssid_len); - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - - iwr.u.data.flags = OID_802_11_SSID; - iwr.u.data.flags |= OID_GET_SET_TOGGLE; - iwr.u.data.pointer = (caddr_t) buf; - iwr.u.data.length = sizeof(NDIS_802_11_SSID); - - if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) { - perror("ioctl[RT_PRIV_IOCTL] -- OID_802_11_SSID"); - ret = -1; - } - os_free(buf); - return ret; -} - -static void wpa_driver_ralink_event_pmkid(struct wpa_driver_ralink_data *drv, - const u8 *data, size_t data_len) -{ - NDIS_802_11_PMKID_CANDIDATE_LIST *pmkid; - size_t i; - union wpa_event_data event; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - if (data_len < 8) { - wpa_printf(MSG_DEBUG, "RALINK: Too short PMKID Candidate List " - "Event (len=%lu)", (unsigned long) data_len); - return; - } - pmkid = (NDIS_802_11_PMKID_CANDIDATE_LIST *) data; - wpa_printf(MSG_DEBUG, "RALINK: PMKID Candidate List Event - Version %d" - " NumCandidates %d", - (int) pmkid->Version, (int) pmkid->NumCandidates); - - if (pmkid->Version != 1) { - wpa_printf(MSG_DEBUG, "RALINK: Unsupported PMKID Candidate " - "List Version %d", (int) pmkid->Version); - return; - } - - if (data_len < 8 + pmkid->NumCandidates * sizeof(PMKID_CANDIDATE)) { - wpa_printf(MSG_DEBUG, "RALINK: PMKID Candidate List " - "underflow"); - - return; - } - - - - os_memset(&event, 0, sizeof(event)); - for (i = 0; i < pmkid->NumCandidates; i++) { - PMKID_CANDIDATE *p = &pmkid->CandidateList[i]; - wpa_printf(MSG_DEBUG, "RALINK: %lu: " MACSTR " Flags 0x%x", - (unsigned long) i, MAC2STR(p->BSSID), - (int) p->Flags); - os_memcpy(event.pmkid_candidate.bssid, p->BSSID, ETH_ALEN); - event.pmkid_candidate.index = i; - event.pmkid_candidate.preauth = - p->Flags & NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED; - wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, - &event); - } -} - -static int wpa_driver_ralink_set_pmkid(struct wpa_driver_ralink_data *drv) -{ - int len, count, i, ret; - struct ndis_pmkid_entry *entry; - NDIS_802_11_PMKID *p; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - count = 0; - entry = drv->pmkid; - while (entry) { - count++; - if (count >= drv->no_of_pmkid) - break; - entry = entry->next; - } - len = 8 + count * sizeof(BSSID_INFO); - p = os_zalloc(len); - if (p == NULL) - return -1; - p->Length = len; - p->BSSIDInfoCount = count; - entry = drv->pmkid; - for (i = 0; i < count; i++) { - os_memcpy(&p->BSSIDInfo[i].BSSID, entry->bssid, ETH_ALEN); - os_memcpy(&p->BSSIDInfo[i].PMKID, entry->pmkid, 16); - entry = entry->next; - } - wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID", - (const u8 *) p, len); - ret = ralink_set_oid(drv, OID_802_11_PMKID, (char *) p, len); - os_free(p); - return ret; -} - -static int wpa_driver_ralink_add_pmkid(void *priv, const u8 *bssid, - const u8 *pmkid) -{ - struct wpa_driver_ralink_data *drv = priv; - struct ndis_pmkid_entry *entry, *prev; - - if (drv->g_driver_down == 1) - return -1; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - if (drv->no_of_pmkid == 0) - return 0; - - prev = NULL; - entry = drv->pmkid; - while (entry) { - if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0) - break; - prev = entry; - entry = entry->next; - } - - if (entry) { - /* Replace existing entry for this BSSID and move it into the - * beginning of the list. */ - os_memcpy(entry->pmkid, pmkid, 16); - if (prev) { - prev->next = entry->next; - entry->next = drv->pmkid; - drv->pmkid = entry; - } - } else { - entry = os_malloc(sizeof(*entry)); - if (entry) { - os_memcpy(entry->bssid, bssid, ETH_ALEN); - os_memcpy(entry->pmkid, pmkid, 16); - entry->next = drv->pmkid; - drv->pmkid = entry; - } - } - - return wpa_driver_ralink_set_pmkid(drv); -} - - -static int wpa_driver_ralink_remove_pmkid(void *priv, const u8 *bssid, - const u8 *pmkid) -{ - struct wpa_driver_ralink_data *drv = priv; - struct ndis_pmkid_entry *entry, *prev; - - if (drv->g_driver_down == 1) - return -1; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - if (drv->no_of_pmkid == 0) - return 0; - - entry = drv->pmkid; - prev = NULL; - drv->pmkid = NULL; - while (entry) { - if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0 && - os_memcmp(entry->pmkid, pmkid, 16) == 0) { - if (prev) - prev->next = entry->next; - else - drv->pmkid = entry->next; - os_free(entry); - break; - } - prev = entry; - entry = entry->next; - } - return wpa_driver_ralink_set_pmkid(drv); -} - - -static int wpa_driver_ralink_flush_pmkid(void *priv) -{ - struct wpa_driver_ralink_data *drv = priv; - NDIS_802_11_PMKID p; - struct ndis_pmkid_entry *pmkid, *prev; - - if (drv->g_driver_down == 1) - return -1; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - if (drv->no_of_pmkid == 0) - return 0; - - pmkid = drv->pmkid; - drv->pmkid = NULL; - while (pmkid) { - prev = pmkid; - pmkid = pmkid->next; - os_free(prev); - } - - os_memset(&p, 0, sizeof(p)); - p.Length = 8; - p.BSSIDInfoCount = 0; - wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID (flush)", - (const u8 *) &p, 8); - return ralink_set_oid(drv, OID_802_11_PMKID, (char *) &p, 8); -} - -static void -wpa_driver_ralink_event_wireless_custom(struct wpa_driver_ralink_data *drv, - void *ctx, char *custom) -{ - union wpa_event_data data; - u8 *req_ies = NULL, *resp_ies = NULL; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); - - os_memset(&data, 0, sizeof(data)); - /* Host AP driver */ - if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { - /* receive a MICFAILURE report */ - data.michael_mic_failure.unicast = - os_strstr(custom, " unicast") != NULL; - /* TODO: parse parameters(?) */ - wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); - } else if (os_strncmp(custom, "ASSOCINFO_ReqIEs=", 17) == 0) { - /* receive assoc. req. IEs */ - char *spos; - int bytes; - - spos = custom + 17; - /*get IE's length */ - /* - * bytes = strlen(spos); ==> bug, bytes may less than original - * size by using this way to get size. snowpin 20070312 - * if (!bytes) - * return; - */ - bytes = drv->assoc_req_ies_len; - - req_ies = os_malloc(bytes); - if (req_ies == NULL) - return; - os_memcpy(req_ies, spos, bytes); - data.assoc_info.req_ies = req_ies; - data.assoc_info.req_ies_len = bytes; - - /* skip the '\0' byte */ - spos += bytes + 1; - - data.assoc_info.resp_ies = NULL; - data.assoc_info.resp_ies_len = 0; - - if (os_strncmp(spos, " RespIEs=", 9) == 0) { - /* receive assoc. resp. IEs */ - spos += 9; - /* get IE's length */ - bytes = os_strlen(spos); - if (!bytes) - goto done; - - resp_ies = os_malloc(bytes); - if (resp_ies == NULL) - goto done; - os_memcpy(resp_ies, spos, bytes); - data.assoc_info.resp_ies = resp_ies; - data.assoc_info.resp_ies_len = bytes; - } - - wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data); - - done: - /* free allocated memory */ - os_free(resp_ies); - os_free(req_ies); - } -} - -static void ralink_interface_up(struct wpa_driver_ralink_data *drv) -{ - union wpa_event_data event; - int enable_wpa_supplicant = 0; - drv->g_driver_down = 0; - os_memset(&event, 0, sizeof(event)); - os_snprintf(event.interface_status.ifname, - sizeof(event.interface_status.ifname), "%s", drv->ifname); - - event.interface_status.ievent = EVENT_INTERFACE_ADDED; - wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); - - if (drv->ap_scan == 1) - enable_wpa_supplicant = 1; - else - enable_wpa_supplicant = 2; - /* trigger driver support wpa_supplicant */ - if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT, - (PCHAR) &enable_wpa_supplicant, sizeof(UCHAR)) < 0) - { - wpa_printf(MSG_INFO, "RALINK: Failed to set " - "RT_OID_WPA_SUPPLICANT_SUPPORT(%d)", - (int) enable_wpa_supplicant); - wpa_printf(MSG_ERROR, "ralink. Driver does not support " - "wpa_supplicant"); - } -} - -static void -wpa_driver_ralink_event_wireless(struct wpa_driver_ralink_data *drv, - void *ctx, char *data, int len) -{ - struct iw_event iwe_buf, *iwe = &iwe_buf; - char *pos, *end, *custom, *buf, *assoc_info_buf, *info_pos; -#if 0 - BOOLEAN ieee8021x_required_key = FALSE; -#endif - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - assoc_info_buf = info_pos = NULL; - pos = data; - end = data + len; - - while (pos + IW_EV_LCP_LEN <= end) { - /* Event data may be unaligned, so make a local, aligned copy - * before processing. */ - os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); - wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d", - iwe->cmd, iwe->len); - if (iwe->len <= IW_EV_LCP_LEN) - return; - - custom = pos + IW_EV_POINT_LEN; - - if (drv->we_version_compiled > 18 && iwe->cmd == IWEVCUSTOM) { - /* WE-19 removed the pointer from struct iw_point */ - char *dpos = (char *) &iwe_buf.u.data.length; - int dlen = dpos - (char *) &iwe_buf; - os_memcpy(dpos, pos + IW_EV_LCP_LEN, - sizeof(struct iw_event) - dlen); - } else { - os_memcpy(&iwe_buf, pos, sizeof(struct iw_event)); - custom += IW_EV_POINT_OFF; - } - - switch (iwe->cmd) { - case IWEVCUSTOM: - if (custom + iwe->u.data.length > end) - return; - buf = os_malloc(iwe->u.data.length + 1); - if (buf == NULL) - return; - os_memcpy(buf, custom, iwe->u.data.length); - buf[iwe->u.data.length] = '\0'; - - if (drv->ap_scan == 1) { - if ((iwe->u.data.flags == RT_ASSOC_EVENT_FLAG) - || (iwe->u.data.flags == - RT_REQIE_EVENT_FLAG) || - (iwe->u.data.flags == RT_RESPIE_EVENT_FLAG) - || (iwe->u.data.flags == - RT_ASSOCINFO_EVENT_FLAG)) { - if (drv->scanning_done == 0) { - os_free(buf); - return; - } - } - } - - if (iwe->u.data.flags == RT_ASSOC_EVENT_FLAG) { - wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); - wpa_printf(MSG_DEBUG, "Custom wireless event: " - "receive ASSOCIATED_EVENT !!!"); - } else if (iwe->u.data.flags == RT_REQIE_EVENT_FLAG) { - wpa_printf(MSG_DEBUG, "Custom wireless event: " - "receive ReqIEs !!!"); - drv->assoc_req_ies = - os_malloc(iwe->u.data.length); - if (drv->assoc_req_ies == NULL) { - os_free(buf); - return; - } - - drv->assoc_req_ies_len = iwe->u.data.length; - os_memcpy(drv->assoc_req_ies, custom, - iwe->u.data.length); - } else if (iwe->u.data.flags == RT_RESPIE_EVENT_FLAG) { - wpa_printf(MSG_DEBUG, "Custom wireless event: " - "receive RespIEs !!!"); - drv->assoc_resp_ies = - os_malloc(iwe->u.data.length); - if (drv->assoc_resp_ies == NULL) { - os_free(drv->assoc_req_ies); - drv->assoc_req_ies = NULL; - os_free(buf); - return; - } - - drv->assoc_resp_ies_len = iwe->u.data.length; - os_memcpy(drv->assoc_resp_ies, custom, - iwe->u.data.length); - } else if (iwe->u.data.flags == - RT_ASSOCINFO_EVENT_FLAG) { - wpa_printf(MSG_DEBUG, "Custom wireless event: " - "receive ASSOCINFO_EVENT !!!"); - - assoc_info_buf = - os_zalloc(drv->assoc_req_ies_len + - drv->assoc_resp_ies_len + 1); - - if (assoc_info_buf == NULL) { - os_free(drv->assoc_req_ies); - drv->assoc_req_ies = NULL; - os_free(drv->assoc_resp_ies); - drv->assoc_resp_ies = NULL; - os_free(buf); - return; - } - - if (drv->assoc_req_ies) { - os_memcpy(assoc_info_buf, - drv->assoc_req_ies, - drv->assoc_req_ies_len); - } - info_pos = assoc_info_buf + - drv->assoc_req_ies_len; - if (drv->assoc_resp_ies) { - os_memcpy(info_pos, - drv->assoc_resp_ies, - drv->assoc_resp_ies_len); - } - assoc_info_buf[drv->assoc_req_ies_len + - drv->assoc_resp_ies_len] = '\0'; - wpa_driver_ralink_event_wireless_custom( - drv, ctx, assoc_info_buf); - os_free(drv->assoc_req_ies); - drv->assoc_req_ies = NULL; - os_free(drv->assoc_resp_ies); - drv->assoc_resp_ies = NULL; - os_free(assoc_info_buf); - } else if (iwe->u.data.flags == RT_DISASSOC_EVENT_FLAG) - { - wpa_printf(MSG_DEBUG, "Custom wireless event: " - "receive DISASSOCIATED_EVENT !!!"); - wpa_supplicant_event(ctx, EVENT_DISASSOC, - NULL); - } else if (iwe->u.data.flags == RT_PMKIDCAND_FLAG) { - wpa_printf(MSG_DEBUG, "Custom wireless event: " - "receive PMKIDCAND_EVENT !!!"); - wpa_driver_ralink_event_pmkid( - drv, (const u8 *) custom, - iwe->u.data.length); - } else if (iwe->u.data.flags == RT_INTERFACE_DOWN) { - drv->g_driver_down = 1; - eloop_terminate(); - } else if (iwe->u.data.flags == RT_INTERFACE_UP) { - ralink_interface_up(drv); - } else { - wpa_driver_ralink_event_wireless_custom( - drv, ctx, buf); - } - os_free(buf); - break; - } - - pos += iwe->len; - } -} - -static void -wpa_driver_ralink_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi, - u8 *buf, size_t len) -{ - struct wpa_driver_ralink_data *drv = ctx; - int attrlen, rta_len; - struct rtattr *attr; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - wpa_hexdump(MSG_DEBUG, "ifi: ", (u8 *) ifi, sizeof(struct ifinfomsg)); - - attrlen = len; - wpa_printf(MSG_DEBUG, "attrlen=%d", attrlen); - attr = (struct rtattr *) buf; - wpa_hexdump(MSG_DEBUG, "attr1: ", (u8 *) attr, sizeof(struct rtattr)); - rta_len = RTA_ALIGN(sizeof(struct rtattr)); - wpa_hexdump(MSG_DEBUG, "attr2: ", (u8 *)attr,rta_len); - while (RTA_OK(attr, attrlen)) { - wpa_printf(MSG_DEBUG, "rta_type=%02x\n", attr->rta_type); - if (attr->rta_type == IFLA_WIRELESS) { - wpa_driver_ralink_event_wireless( - drv, ctx, - ((char *) attr) + rta_len, - attr->rta_len - rta_len); - } - attr = RTA_NEXT(attr, attrlen); - wpa_hexdump(MSG_DEBUG, "attr3: ", - (u8 *) attr, sizeof(struct rtattr)); - } -} - -static int -ralink_get_we_version_compiled(struct wpa_driver_ralink_data *drv) -{ - struct iwreq iwr; - UINT we_version_compiled = 0; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - iwr.u.data.pointer = (caddr_t) &we_version_compiled; - iwr.u.data.flags = RT_OID_WE_VERSION_COMPILED; - - if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) { - wpa_printf(MSG_DEBUG, "%s: failed", __func__); - return -1; - } - - drv->we_version_compiled = we_version_compiled; - - return 0; -} - -static void * wpa_driver_ralink_init(void *ctx, const char *ifname) -{ - int s; - struct wpa_driver_ralink_data *drv; - struct ifreq ifr; - UCHAR enable_wpa_supplicant = 0; - struct netlink_config *cfg; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - /* open socket to kernel */ - if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { - perror("socket"); - return NULL; - } - /* do it */ - os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); - - if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { - perror(ifr.ifr_name); - return NULL; - } - - drv = os_zalloc(sizeof(*drv)); - if (drv == NULL) - return NULL; - - drv->scanning_done = 1; - drv->ap_scan = 1; /* for now - let's assume ap_scan=1 is used */ - drv->ctx = ctx; - os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); - drv->ioctl_sock = s; - drv->g_driver_down = 0; - - cfg = os_zalloc(sizeof(*cfg)); - if (cfg == NULL) { - close(drv->ioctl_sock); - os_free(drv); - return NULL; - } - cfg->ctx = drv; - cfg->newlink_cb = wpa_driver_ralink_event_rtm_newlink; - drv->netlink = netlink_init(cfg); - if (drv->netlink == NULL) { - os_free(cfg); - close(drv->ioctl_sock); - os_free(drv); - return NULL; - } - - drv->no_of_pmkid = 4; /* Number of PMKID saved supported */ - - linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1); - ralink_get_we_version_compiled(drv); - wpa_driver_ralink_flush_pmkid(drv); - - if (drv->ap_scan == 1) - enable_wpa_supplicant = 1; - else - enable_wpa_supplicant = 2; - /* trigger driver support wpa_supplicant */ - if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT, - (PCHAR) &enable_wpa_supplicant, sizeof(UCHAR)) < 0) - { - wpa_printf(MSG_DEBUG, "RALINK: Failed to set " - "RT_OID_WPA_SUPPLICANT_SUPPORT(%d)", - (int) enable_wpa_supplicant); - wpa_printf(MSG_ERROR, "RALINK: Driver does not support " - "wpa_supplicant"); - close(s); - close(drv->ioctl_sock); - os_free(drv); - return NULL; - } - - if (drv->ap_scan == 1) - drv->scanning_done = 0; - - return drv; -} - -static void wpa_driver_ralink_deinit(void *priv) -{ - struct wpa_driver_ralink_data *drv = priv; - UCHAR enable_wpa_supplicant; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - enable_wpa_supplicant = 0; - - if (drv->g_driver_down == 0) { - /* trigger driver disable wpa_supplicant support */ - if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT, - (char *) &enable_wpa_supplicant, - sizeof(BOOLEAN)) < 0) { - wpa_printf(MSG_DEBUG, "RALINK: Failed to set " - "RT_OID_WPA_SUPPLICANT_SUPPORT(%d)", - (int) enable_wpa_supplicant); - } - - wpa_driver_ralink_flush_pmkid(drv); - - sleep(1); - /* linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0); */ - } - - eloop_cancel_timeout(wpa_driver_ralink_scan_timeout, drv, drv->ctx); - netlink_deinit(drv->netlink); - close(drv->ioctl_sock); - os_free(drv); -} - -static void wpa_driver_ralink_scan_timeout(void *eloop_ctx, void *timeout_ctx) -{ - struct wpa_driver_ralink_data *drv = eloop_ctx; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); - wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); - - drv->scanning_done = 1; - -} - -static int wpa_driver_ralink_scan(void *priv, - struct wpa_driver_scan_params *params) -{ - struct wpa_driver_ralink_data *drv = priv; - struct iwreq iwr; - int ret = 0; - - if (drv->g_driver_down == 1) - return -1; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - -#if 0 - if (ssid_len > IW_ESSID_MAX_SIZE) { - wpa_printf(MSG_DEBUG, "%s: too long SSID (%lu)", - __FUNCTION__, (unsigned long) ssid_len); - return -1; - } - - /* wpa_driver_ralink_set_ssid(drv, ssid, ssid_len); */ -#endif - - if (ralink_set_oid(drv, RT_OID_WPS_PROBE_REQ_IE, - (char *) params->extra_ies, params->extra_ies_len) < - 0) { - wpa_printf(MSG_DEBUG, "RALINK: Failed to set " - "RT_OID_WPS_PROBE_REQ_IE"); - } - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - - if (ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr) < 0) { - perror("ioctl[SIOCSIWSCAN]"); - ret = -1; - } - - /* Not all drivers generate "scan completed" wireless event, so try to - * read results after a timeout. */ - eloop_cancel_timeout(wpa_driver_ralink_scan_timeout, drv, drv->ctx); - eloop_register_timeout(4, 0, wpa_driver_ralink_scan_timeout, drv, - drv->ctx); - - drv->scanning_done = 0; - - return ret; -} - -static struct wpa_scan_results * -wpa_driver_ralink_get_scan_results(void *priv) -{ - struct wpa_driver_ralink_data *drv = priv; - UCHAR *buf = NULL; - size_t buf_len; - NDIS_802_11_BSSID_LIST_EX *wsr; - NDIS_WLAN_BSSID_EX *wbi; - struct iwreq iwr; - size_t ap_num; - u8 *pos; - struct wpa_scan_results *res; - - if (drv->g_driver_down == 1) - return NULL; - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - if (drv->we_version_compiled >= 17) - buf_len = 8192; - else - buf_len = 4096; - - for (;;) { - buf = os_zalloc(buf_len); - iwr.u.data.length = buf_len; - if (buf == NULL) - return NULL; - - wsr = (NDIS_802_11_BSSID_LIST_EX *) buf; - - wsr->NumberOfItems = 0; - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - iwr.u.data.pointer = (void *) buf; - iwr.u.data.flags = OID_802_11_BSSID_LIST; - - if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) == 0) - break; - - if (errno == E2BIG && buf_len < 65535) { - os_free(buf); - buf = NULL; - buf_len *= 2; - if (buf_len > 65535) - buf_len = 65535; /* 16-bit length field */ - wpa_printf(MSG_DEBUG, "Scan results did not fit - " - "trying larger buffer (%lu bytes)", - (unsigned long) buf_len); - } else { - perror("ioctl[RT_PRIV_IOCTL]"); - os_free(buf); - return NULL; - } - } - - res = os_zalloc(sizeof(*res)); - if (res == NULL) { - os_free(buf); - return NULL; - } - - res->res = os_zalloc(wsr->NumberOfItems * - sizeof(struct wpa_scan_res *)); - if (res->res == NULL) { - os_free(res); - os_free(buf); - return NULL; - } - - for (ap_num = 0, wbi = wsr->Bssid; ap_num < wsr->NumberOfItems; - ++ap_num) { - struct wpa_scan_res *r = NULL; - size_t extra_len = 0, var_ie_len = 0; - u8 *pos2; - - /* SSID data element */ - extra_len += 2 + wbi->Ssid.SsidLength; - var_ie_len = wbi->IELength - sizeof(NDIS_802_11_FIXED_IEs); - r = os_zalloc(sizeof(*r) + extra_len + var_ie_len); - if (r == NULL) - break; - res->res[res->num++] = r; - - wpa_printf(MSG_DEBUG, "SSID - %s", wbi->Ssid.Ssid); - /* get ie's */ - wpa_hexdump(MSG_DEBUG, "RALINK: AP IEs", - (u8 *) &wbi->IEs[0], wbi->IELength); - - os_memcpy(r->bssid, wbi->MacAddress, ETH_ALEN); - - extra_len += (2 + wbi->Ssid.SsidLength); - r->ie_len = extra_len + var_ie_len; - pos2 = (u8 *) (r + 1); - - /* - * Generate a fake SSID IE since the driver did not report - * a full IE list. - */ - *pos2++ = WLAN_EID_SSID; - *pos2++ = wbi->Ssid.SsidLength; - os_memcpy(pos2, wbi->Ssid.Ssid, wbi->Ssid.SsidLength); - pos2 += wbi->Ssid.SsidLength; - - r->freq = (wbi->Configuration.DSConfig / 1000); - - pos = (u8 *) wbi + sizeof(*wbi) - 1; - - pos += sizeof(NDIS_802_11_FIXED_IEs) - 2; - os_memcpy(&(r->caps), pos, 2); - pos += 2; - - if (wbi->IELength > sizeof(NDIS_802_11_FIXED_IEs)) - os_memcpy(pos2, pos, var_ie_len); - - wbi = (NDIS_WLAN_BSSID_EX *) ((u8 *) wbi + wbi->Length); - } - - os_free(buf); - return res; -} - -static int ralink_set_auth_mode(struct wpa_driver_ralink_data *drv, - NDIS_802_11_AUTHENTICATION_MODE mode) -{ - NDIS_802_11_AUTHENTICATION_MODE auth_mode = mode; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - if (ralink_set_oid(drv, OID_802_11_AUTHENTICATION_MODE, - (char *) &auth_mode, sizeof(auth_mode)) < 0) { - wpa_printf(MSG_DEBUG, "RALINK: Failed to set " - "OID_802_11_AUTHENTICATION_MODE (%d)", - (int) auth_mode); - return -1; - } - return 0; -} - -static int ralink_set_encr_type(struct wpa_driver_ralink_data *drv, - NDIS_802_11_WEP_STATUS encr_type) -{ - NDIS_802_11_WEP_STATUS wep_status = encr_type; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - if (ralink_set_oid(drv, OID_802_11_WEP_STATUS, - (char *) &wep_status, sizeof(wep_status)) < 0) { - wpa_printf(MSG_DEBUG, "RALINK: Failed to set " - "OID_802_11_WEP_STATUS (%d)", - (int) wep_status); - return -1; - } - return 0; -} - - -static int wpa_driver_ralink_remove_key(struct wpa_driver_ralink_data *drv, - int key_idx, const u8 *addr, - const u8 *bssid, int pairwise) -{ - NDIS_802_11_REMOVE_KEY rkey; - NDIS_802_11_KEY_INDEX _index; - int res, res2; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - os_memset(&rkey, 0, sizeof(rkey)); - - rkey.Length = sizeof(rkey); - rkey.KeyIndex = key_idx; - - if (pairwise) - rkey.KeyIndex |= 1 << 30; - - os_memcpy(rkey.BSSID, bssid, ETH_ALEN); - - res = ralink_set_oid(drv, OID_802_11_REMOVE_KEY, (char *) &rkey, - sizeof(rkey)); - - /* AlbertY@20060210 removed it */ - if (0 /* !pairwise */) { - res2 = ralink_set_oid(drv, OID_802_11_REMOVE_WEP, - (char *) &_index, sizeof(_index)); - } else - res2 = 0; - - if (res < 0 && res2 < 0) - return res; - return 0; -} - -static int wpa_driver_ralink_add_wep(struct wpa_driver_ralink_data *drv, - int pairwise, int key_idx, int set_tx, - const u8 *key, size_t key_len) -{ - NDIS_802_11_WEP *wep; - size_t len; - int res; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - len = 12 + key_len; - wep = os_zalloc(len); - if (wep == NULL) - return -1; - - wep->Length = len; - wep->KeyIndex = key_idx; - - if (set_tx) - wep->KeyIndex |= 0x80000000; - - wep->KeyLength = key_len; - os_memcpy(wep->KeyMaterial, key, key_len); - - wpa_hexdump_key(MSG_MSGDUMP, "RALINK: OID_802_11_ADD_WEP", - (const u8 *) wep, len); - res = ralink_set_oid(drv, OID_802_11_ADD_WEP, (char *) wep, len); - - os_free(wep); - - return res; -} - -static int wpa_driver_ralink_set_key(const char *ifname, void *priv, - enum wpa_alg alg, const u8 *addr, - int key_idx, int set_tx, - const u8 *seq, size_t seq_len, - const u8 *key, size_t key_len) -{ - struct wpa_driver_ralink_data *drv = priv; - size_t len, i; - NDIS_802_11_KEY *nkey; - int res, pairwise; - u8 bssid[ETH_ALEN]; - - if (drv->g_driver_down == 1) - return -1; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - drv->bAddWepKey = FALSE; - - if (addr == NULL || os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", - ETH_ALEN) == 0) { - /* Group Key */ - pairwise = 0; - wpa_driver_ralink_get_bssid(drv, bssid); - } else { - /* Pairwise Key */ - pairwise = 1; - os_memcpy(bssid, addr, ETH_ALEN); - } - - if (alg == WPA_ALG_NONE || key_len == 0) { - return wpa_driver_ralink_remove_key(drv, key_idx, addr, bssid, - pairwise); - } - - if (alg == WPA_ALG_WEP) { - drv->bAddWepKey = TRUE; - return wpa_driver_ralink_add_wep(drv, pairwise, key_idx, - set_tx, key, key_len); - } - - len = 12 + 6 + 6 + 8 + key_len; - - nkey = os_zalloc(len); - if (nkey == NULL) - return -1; - - nkey->Length = len; - nkey->KeyIndex = key_idx; - - if (set_tx) - nkey->KeyIndex |= 1 << 31; - - if (pairwise) - nkey->KeyIndex |= 1 << 30; - - if (seq && seq_len) - nkey->KeyIndex |= 1 << 29; - - nkey->KeyLength = key_len; - os_memcpy(nkey->BSSID, bssid, ETH_ALEN); - - if (seq && seq_len) { - for (i = 0; i < seq_len; i++) - nkey->KeyRSC |= seq[i] << (i * 8); - } - if (alg == WPA_ALG_TKIP && key_len == 32) { - os_memcpy(nkey->KeyMaterial, key, 16); - os_memcpy(nkey->KeyMaterial + 16, key + 24, 8); - os_memcpy(nkey->KeyMaterial + 24, key + 16, 8); - } else { - os_memcpy(nkey->KeyMaterial, key, key_len); - } - - wpa_printf(MSG_DEBUG, "%s: alg=%d key_idx=%d set_tx=%d seq_len=%lu " - "key_len=%lu", __FUNCTION__, alg, key_idx, set_tx, - (unsigned long) seq_len, (unsigned long) key_len); - - wpa_hexdump_key(MSG_MSGDUMP, "RALINK: OID_802_11_ADD_KEY", - (const u8 *) nkey, len); - res = ralink_set_oid(drv, OID_802_11_ADD_KEY, (char *) nkey, len); - os_free(nkey); - - return res; -} - -static int wpa_driver_ralink_disassociate(void *priv, const u8 *addr, - int reason_code) -{ - struct wpa_driver_ralink_data *drv = priv; - - if (drv->g_driver_down == 1) - return -1; - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - if (ralink_set_oid(drv, OID_802_11_DISASSOCIATE, " ", 4) < 0) { - wpa_printf(MSG_DEBUG, "RALINK: Failed to set " - "OID_802_11_DISASSOCIATE"); - } - - return 0; -} - -static int wpa_driver_ralink_deauthenticate(void *priv, const u8 *addr, - int reason_code) -{ - struct wpa_driver_ralink_data *drv = priv; - - wpa_printf(MSG_DEBUG, "g_driver_down = %d", drv->g_driver_down); - - if (drv->g_driver_down == 1) - return -1; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - if (ralink_get_new_driver_flag(drv) == 0) { - return wpa_driver_ralink_disassociate(priv, addr, reason_code); - } else { - MLME_DEAUTH_REQ_STRUCT mlme; - os_memset(&mlme, 0, sizeof(MLME_DEAUTH_REQ_STRUCT)); - mlme.Reason = reason_code; - os_memcpy(mlme.Addr, addr, MAC_ADDR_LEN); - return ralink_set_oid(drv, OID_802_11_DEAUTHENTICATION, - (char *) &mlme, - sizeof(MLME_DEAUTH_REQ_STRUCT)); - } -} - -static int wpa_driver_ralink_set_gen_ie(void *priv, const u8 *ie, - size_t ie_len) -{ - struct wpa_driver_ralink_data *drv = priv; - struct iwreq iwr; - int ret = 0; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - iwr.u.data.pointer = (caddr_t) ie; - iwr.u.data.length = ie_len; - - wpa_hexdump(MSG_DEBUG, "wpa_driver_ralink_set_gen_ie: ", - (u8 *) ie, ie_len); - - if (ioctl(drv->ioctl_sock, SIOCSIWGENIE, &iwr) < 0) { - perror("ioctl[SIOCSIWGENIE]"); - ret = -1; - } - - return ret; -} - -static int -wpa_driver_ralink_associate(void *priv, - struct wpa_driver_associate_params *params) -{ - struct wpa_driver_ralink_data *drv = priv; - - NDIS_802_11_NETWORK_INFRASTRUCTURE mode; - NDIS_802_11_AUTHENTICATION_MODE auth_mode; - NDIS_802_11_WEP_STATUS encr; - BOOLEAN ieee8021xMode; - BOOLEAN ieee8021x_required_key = TRUE; - - if (drv->g_driver_down == 1) - return -1; - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - if (params->mode == IEEE80211_MODE_IBSS) - mode = Ndis802_11IBSS; - else - mode = Ndis802_11Infrastructure; - - if (ralink_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE, - (char *) &mode, sizeof(mode)) < 0) { - wpa_printf(MSG_DEBUG, "RALINK: Failed to set " - "OID_802_11_INFRASTRUCTURE_MODE (%d)", - (int) mode); - /* Try to continue anyway */ - } - - if (params->key_mgmt_suite == KEY_MGMT_WPS) { - UCHAR enable_wps = 0x80; - /* trigger driver support wpa_supplicant */ - if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT, - (PCHAR) &enable_wps, sizeof(UCHAR)) < 0) { - wpa_printf(MSG_INFO, "RALINK: Failed to set " - "RT_OID_WPA_SUPPLICANT_SUPPORT (%d)", - (int) enable_wps); - } - - wpa_driver_ralink_set_gen_ie(priv, params->wpa_ie, - params->wpa_ie_len); - - ralink_set_auth_mode(drv, Ndis802_11AuthModeOpen); - - ralink_set_encr_type(drv, Ndis802_11EncryptionDisabled); - } else { -#ifdef CONFIG_WPS - UCHAR enable_wpa_supplicant; - - if (drv->ap_scan == 1) - enable_wpa_supplicant = 0x01; - else - enable_wpa_supplicant = 0x02; - - /* trigger driver support wpa_supplicant */ - if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT, - (PCHAR) &enable_wpa_supplicant, - sizeof(UCHAR)) < 0) { - wpa_printf(MSG_DEBUG, "RALINK: Failed to set " - "RT_OID_WPA_SUPPLICANT_SUPPORT (%d)", - (int) enable_wpa_supplicant); - } - - wpa_driver_ralink_set_gen_ie(priv, (u8 *) "", 0); -#endif /* CONFIG_WPS */ - - if (params->wpa_ie == NULL || params->wpa_ie_len == 0) { - if (params->auth_alg & WPA_AUTH_ALG_SHARED) { - if (params->auth_alg & WPA_AUTH_ALG_OPEN) - auth_mode = Ndis802_11AuthModeAutoSwitch; - else - auth_mode = Ndis802_11AuthModeShared; - } else - auth_mode = Ndis802_11AuthModeOpen; - } else if (params->wpa_ie[0] == WLAN_EID_RSN) { - if (params->key_mgmt_suite == KEY_MGMT_PSK) - auth_mode = Ndis802_11AuthModeWPA2PSK; - else - auth_mode = Ndis802_11AuthModeWPA2; - } else { - if (params->key_mgmt_suite == KEY_MGMT_WPA_NONE) - auth_mode = Ndis802_11AuthModeWPANone; - else if (params->key_mgmt_suite == KEY_MGMT_PSK) - auth_mode = Ndis802_11AuthModeWPAPSK; - else - auth_mode = Ndis802_11AuthModeWPA; - } - - switch (params->pairwise_suite) { - case CIPHER_CCMP: - encr = Ndis802_11Encryption3Enabled; - break; - case CIPHER_TKIP: - encr = Ndis802_11Encryption2Enabled; - break; - case CIPHER_WEP40: - case CIPHER_WEP104: - encr = Ndis802_11Encryption1Enabled; - break; - case CIPHER_NONE: - if (params->group_suite == CIPHER_CCMP) - encr = Ndis802_11Encryption3Enabled; - else if (params->group_suite == CIPHER_TKIP) - encr = Ndis802_11Encryption2Enabled; - else - encr = Ndis802_11EncryptionDisabled; - break; - default: - encr = Ndis802_11EncryptionDisabled; - break; - } - - ralink_set_auth_mode(drv, auth_mode); - - /* notify driver that IEEE8021x mode is enabled */ - if (params->key_mgmt_suite == KEY_MGMT_802_1X_NO_WPA) { - ieee8021xMode = TRUE; - if (drv->bAddWepKey) - ieee8021x_required_key = FALSE; - } else - ieee8021xMode = FALSE; - - if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X_REQUIRE_KEY, - (char *) &ieee8021x_required_key, - sizeof(BOOLEAN)) < 0) { - wpa_printf(MSG_DEBUG, "ERROR: Failed to set " - "OID_802_11_SET_IEEE8021X_REQUIRE_KEY(%d)", - (int) ieee8021x_required_key); - } else { - wpa_printf(MSG_DEBUG, "ieee8021x_required_key is %s", - ieee8021x_required_key ? "TRUE" : "FALSE"); - } - - if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X, - (char *) &ieee8021xMode, sizeof(BOOLEAN)) < - 0) { - wpa_printf(MSG_DEBUG, "RALINK: Failed to set " - "OID_802_11_SET_IEEE8021X(%d)", - (int) ieee8021xMode); - } - - ralink_set_encr_type(drv, encr); - - if ((ieee8021xMode == FALSE) && - (encr == Ndis802_11Encryption1Enabled)) { - /* static WEP */ - int enabled = 0; - if (ralink_set_oid(drv, OID_802_11_DROP_UNENCRYPTED, - (char *) &enabled, sizeof(enabled)) - < 0) { - wpa_printf(MSG_DEBUG, "RALINK: Failed to set " - "OID_802_11_DROP_UNENCRYPTED(%d)", - (int) encr); - } - } - } - - return wpa_driver_ralink_set_ssid(drv, params->ssid, params->ssid_len); -} - -static int -wpa_driver_ralink_set_countermeasures(void *priv, int enabled) -{ - struct wpa_driver_ralink_data *drv = priv; - if (drv->g_driver_down == 1) - return -1; - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); - return ralink_set_oid(drv, OID_SET_COUNTERMEASURES, (char *) &enabled, - sizeof(int)); -} - -const struct wpa_driver_ops wpa_driver_ralink_ops = { - .name = "ralink", - .desc = "Ralink Wireless Client driver", - .get_bssid = wpa_driver_ralink_get_bssid, - .get_ssid = wpa_driver_ralink_get_ssid, - .set_key = wpa_driver_ralink_set_key, - .init = wpa_driver_ralink_init, - .deinit = wpa_driver_ralink_deinit, - .set_countermeasures = wpa_driver_ralink_set_countermeasures, - .scan2 = wpa_driver_ralink_scan, - .get_scan_results2 = wpa_driver_ralink_get_scan_results, - .deauthenticate = wpa_driver_ralink_deauthenticate, - .disassociate = wpa_driver_ralink_disassociate, - .associate = wpa_driver_ralink_associate, - .add_pmkid = wpa_driver_ralink_add_pmkid, - .remove_pmkid = wpa_driver_ralink_remove_pmkid, - .flush_pmkid = wpa_driver_ralink_flush_pmkid, -}; diff --git a/src/drivers/driver_ralink.h b/src/drivers/driver_ralink.h deleted file mode 100644 index d13df28de4564..0000000000000 --- a/src/drivers/driver_ralink.h +++ /dev/null @@ -1,383 +0,0 @@ -/* - * WPA Supplicant - driver_ralink exported functions - * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> - * Copyright (c) 2007, Snowpin Lee <snowpin_lee@ralinktech.com.tw> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -// Ralink defined OIDs -#if WIRELESS_EXT <= 11 -#ifndef SIOCDEVPRIVATE -#define SIOCDEVPRIVATE 0x8BE0 -#endif -#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE -#endif - -#define RT_PRIV_IOCTL (SIOCIWFIRSTPRIV + 0x0E) -#define RTPRIV_IOCTL_SET (SIOCIWFIRSTPRIV + 0x02) - -// IEEE 802.11 OIDs & Ralink defined OIDs ****** - -// (RaConfig Set/QueryInform) ==> -#define OID_GET_SET_TOGGLE 0x8000 - -#define OID_802_11_ADD_WEP 0x0112 -#define OID_802_11_REMOVE_WEP 0x0113 -#define OID_802_11_DISASSOCIATE 0x0114 -#define OID_802_11_PRIVACY_FILTER 0x0118 -#define OID_802_11_ASSOCIATION_INFORMATION 0x011E -#define OID_802_11_BSSID_LIST_SCAN 0x0508 -#define OID_802_11_SSID 0x0509 -#define OID_802_11_BSSID 0x050A -#define OID_802_11_WEP_STATUS 0x0510 -#define OID_802_11_AUTHENTICATION_MODE 0x0511 -#define OID_802_11_INFRASTRUCTURE_MODE 0x0512 -#define OID_802_11_TX_POWER_LEVEL 0x0517 -#define OID_802_11_REMOVE_KEY 0x0519 -#define OID_802_11_ADD_KEY 0x0520 -#define OID_802_11_DEAUTHENTICATION 0x0526 -#define OID_802_11_DROP_UNENCRYPTED 0x0527 -#define OID_802_11_BSSID_LIST 0x0609 -#define OID_802_3_CURRENT_ADDRESS 0x060A -#define OID_SET_COUNTERMEASURES 0x0616 -#define OID_802_11_SET_IEEE8021X 0x0617 // For IEEE8021x mode -#define OID_802_11_SET_IEEE8021X_REQUIRE_KEY 0x0618 // For DynamicWEP in IEEE802.1x mode -#define OID_802_11_PMKID 0x0620 -#define RT_OID_WPA_SUPPLICANT_SUPPORT 0x0621 // for trigger driver enable/disable wpa_supplicant support -#define RT_OID_WE_VERSION_COMPILED 0x0622 -#define RT_OID_NEW_DRIVER 0x0623 -#define RT_OID_WPS_PROBE_REQ_IE 0x0625 - -#define PACKED __attribute__ ((packed)) - -//wpa_supplicant event flags -#define RT_ASSOC_EVENT_FLAG 0x0101 -#define RT_DISASSOC_EVENT_FLAG 0x0102 -#define RT_REQIE_EVENT_FLAG 0x0103 -#define RT_RESPIE_EVENT_FLAG 0x0104 -#define RT_ASSOCINFO_EVENT_FLAG 0x0105 -#define RT_PMKIDCAND_FLAG 0x0106 -#define RT_INTERFACE_DOWN 0x0107 -#define RT_INTERFACE_UP 0x0108 - -// -// IEEE 802.11 Structures and definitions -// -// new types for Media Specific Indications - -#ifndef ULONG -#define CHAR char -#define INT int -#define SHORT int -#define UINT u32 -#undef ULONG -//#define ULONG u32 -#define ULONG unsigned long /* 32-bit in 32-bit CPU or 64-bit in 64-bit CPU */ -#define USHORT unsigned short -#define UCHAR unsigned char - -#define uint32 u32 -#define uint8 u8 - - -#define BOOLEAN u8 -//#define LARGE_INTEGER s64 -#define VOID void -#define LONG long -#define LONGLONG s64 -#define ULONGLONG u64 -typedef VOID *PVOID; -typedef CHAR *PCHAR; -typedef UCHAR *PUCHAR; -typedef USHORT *PUSHORT; -typedef LONG *PLONG; -typedef ULONG *PULONG; - -typedef union _LARGE_INTEGER { - struct { - ULONG LowPart; - LONG HighPart; - }vv; - struct { - ULONG LowPart; - LONG HighPart; - } u; - s64 QuadPart; -} LARGE_INTEGER; - -#endif - -#define NDIS_802_11_LENGTH_SSID 32 -#define NDIS_802_11_LENGTH_RATES 8 -#define NDIS_802_11_LENGTH_RATES_EX 16 -#define MAX_LEN_OF_SSID 32 -#define MAC_ADDR_LEN 6 - -typedef UCHAR NDIS_802_11_MAC_ADDRESS[6]; - -// mask for authentication/integrity fields -#define NDIS_802_11_AUTH_REQUEST_AUTH_FIELDS 0x0f - -#define NDIS_802_11_AUTH_REQUEST_REAUTH 0x01 -#define NDIS_802_11_AUTH_REQUEST_KEYUPDATE 0x02 -#define NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR 0x06 -#define NDIS_802_11_AUTH_REQUEST_GROUP_ERROR 0x0E - -// Added new types for OFDM 5G and 2.4G -typedef enum _NDIS_802_11_NETWORK_TYPE -{ - Ndis802_11FH, - Ndis802_11DS, - Ndis802_11OFDM5, - Ndis802_11OFDM24, - Ndis802_11Automode, - Ndis802_11NetworkTypeMax // not a real type, defined as an upper bound -} NDIS_802_11_NETWORK_TYPE, *PNDIS_802_11_NETWORK_TYPE; - -// -// Received Signal Strength Indication -// -typedef LONG NDIS_802_11_RSSI; // in dBm - -typedef struct _NDIS_802_11_CONFIGURATION_FH -{ - ULONG Length; // Length of structure - ULONG HopPattern; // As defined by 802.11, MSB set - ULONG HopSet; // to one if non-802.11 - ULONG DwellTime; // units are Kusec -} NDIS_802_11_CONFIGURATION_FH, *PNDIS_802_11_CONFIGURATION_FH; - -typedef struct _NDIS_802_11_CONFIGURATION -{ - ULONG Length; // Length of structure - ULONG BeaconPeriod; // units are Kusec - ULONG ATIMWindow; // units are Kusec - ULONG DSConfig; // Frequency, units are kHz - NDIS_802_11_CONFIGURATION_FH FHConfig; -} NDIS_802_11_CONFIGURATION, *PNDIS_802_11_CONFIGURATION; - -typedef ULONG NDIS_802_11_KEY_INDEX; -typedef ULONGLONG NDIS_802_11_KEY_RSC; - -// Key mapping keys require a BSSID -typedef struct _NDIS_802_11_KEY -{ - UINT Length; // Length of this structure - UINT KeyIndex; - UINT KeyLength; // length of key in bytes - NDIS_802_11_MAC_ADDRESS BSSID; - NDIS_802_11_KEY_RSC KeyRSC; - UCHAR KeyMaterial[1]; // variable length depending on above field -} NDIS_802_11_KEY, *PNDIS_802_11_KEY; - -typedef struct _NDIS_802_11_REMOVE_KEY -{ - UINT Length; // Length of this structure - UINT KeyIndex; - NDIS_802_11_MAC_ADDRESS BSSID; -} NDIS_802_11_REMOVE_KEY, *PNDIS_802_11_REMOVE_KEY; - -typedef struct PACKED _NDIS_802_11_WEP -{ - UINT Length; // Length of this structure - UINT KeyIndex; // 0 is the per-client key, 1-N are the - // global keys - UINT KeyLength; // length of key in bytes - UCHAR KeyMaterial[1];// variable length depending on above field -} NDIS_802_11_WEP, *PNDIS_802_11_WEP; - - -typedef enum _NDIS_802_11_NETWORK_INFRASTRUCTURE -{ - Ndis802_11IBSS, - Ndis802_11Infrastructure, - Ndis802_11AutoUnknown, - Ndis802_11InfrastructureMax // Not a real value, defined as upper bound -} NDIS_802_11_NETWORK_INFRASTRUCTURE, *PNDIS_802_11_NETWORK_INFRASTRUCTURE; - -// PMKID Structures -typedef UCHAR NDIS_802_11_PMKID_VALUE[16]; - -typedef struct _BSSID_INFO -{ - NDIS_802_11_MAC_ADDRESS BSSID; - NDIS_802_11_PMKID_VALUE PMKID; -} BSSID_INFO, *PBSSID_INFO; - -typedef struct _NDIS_802_11_PMKID -{ - ULONG Length; - ULONG BSSIDInfoCount; - BSSID_INFO BSSIDInfo[1]; -} NDIS_802_11_PMKID, *PNDIS_802_11_PMKID; - -//Added new types for PMKID Candidate lists. -typedef struct _PMKID_CANDIDATE { - NDIS_802_11_MAC_ADDRESS BSSID; - ULONG Flags; -} PMKID_CANDIDATE, *PPMKID_CANDIDATE; - -typedef struct _NDIS_802_11_PMKID_CANDIDATE_LIST -{ - ULONG Version; // Version of the structure - ULONG NumCandidates; // No. of pmkid candidates - PMKID_CANDIDATE CandidateList[1]; -} NDIS_802_11_PMKID_CANDIDATE_LIST, *PNDIS_802_11_PMKID_CANDIDATE_LIST; - -//Flags for PMKID Candidate list structure -#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED 0x01 - -// Add new authentication modes -typedef enum _NDIS_802_11_AUTHENTICATION_MODE -{ - Ndis802_11AuthModeOpen, - Ndis802_11AuthModeShared, - Ndis802_11AuthModeAutoSwitch, - Ndis802_11AuthModeWPA, - Ndis802_11AuthModeWPAPSK, - Ndis802_11AuthModeWPANone, - Ndis802_11AuthModeWPA2, - Ndis802_11AuthModeWPA2PSK, - Ndis802_11AuthModeMax // Not a real mode, defined as upper bound -} NDIS_802_11_AUTHENTICATION_MODE, *PNDIS_802_11_AUTHENTICATION_MODE; - -typedef UCHAR NDIS_802_11_RATES[NDIS_802_11_LENGTH_RATES]; // Set of 8 data rates -typedef UCHAR NDIS_802_11_RATES_EX[NDIS_802_11_LENGTH_RATES_EX]; // Set of 16 data rates - -typedef struct PACKED _NDIS_802_11_SSID -{ - INT SsidLength; // length of SSID field below, in bytes; - // this can be zero. - UCHAR Ssid[NDIS_802_11_LENGTH_SSID]; // SSID information field -} NDIS_802_11_SSID, *PNDIS_802_11_SSID; - - -typedef struct PACKED _NDIS_WLAN_BSSID -{ - ULONG Length; // Length of this structure - NDIS_802_11_MAC_ADDRESS MacAddress; // BSSID - UCHAR Reserved[2]; - NDIS_802_11_SSID Ssid; // SSID - ULONG Privacy; // WEP encryption requirement - NDIS_802_11_RSSI Rssi; // receive signal - // strength in dBm - NDIS_802_11_NETWORK_TYPE NetworkTypeInUse; - NDIS_802_11_CONFIGURATION Configuration; - NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode; - NDIS_802_11_RATES SupportedRates; -} NDIS_WLAN_BSSID, *PNDIS_WLAN_BSSID; - -typedef struct PACKED _NDIS_802_11_BSSID_LIST -{ - UINT NumberOfItems; // in list below, at least 1 - NDIS_WLAN_BSSID Bssid[1]; -} NDIS_802_11_BSSID_LIST, *PNDIS_802_11_BSSID_LIST; - -// Added Capabilities, IELength and IEs for each BSSID -typedef struct PACKED _NDIS_WLAN_BSSID_EX -{ - ULONG Length; // Length of this structure - NDIS_802_11_MAC_ADDRESS MacAddress; // BSSID - UCHAR Reserved[2]; - NDIS_802_11_SSID Ssid; // SSID - UINT Privacy; // WEP encryption requirement - NDIS_802_11_RSSI Rssi; // receive signal - // strength in dBm - NDIS_802_11_NETWORK_TYPE NetworkTypeInUse; - NDIS_802_11_CONFIGURATION Configuration; - NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode; - NDIS_802_11_RATES_EX SupportedRates; - ULONG IELength; - UCHAR IEs[1]; -} NDIS_WLAN_BSSID_EX, *PNDIS_WLAN_BSSID_EX; - -typedef struct PACKED _NDIS_802_11_BSSID_LIST_EX -{ - UINT NumberOfItems; // in list below, at least 1 - NDIS_WLAN_BSSID_EX Bssid[1]; -} NDIS_802_11_BSSID_LIST_EX, *PNDIS_802_11_BSSID_LIST_EX; - -typedef struct PACKED _NDIS_802_11_FIXED_IEs -{ - UCHAR Timestamp[8]; - USHORT BeaconInterval; - USHORT Capabilities; -} NDIS_802_11_FIXED_IEs, *PNDIS_802_11_FIXED_IEs; - -// Added new encryption types -// Also aliased typedef to new name -typedef enum _NDIS_802_11_WEP_STATUS -{ - Ndis802_11WEPEnabled, - Ndis802_11Encryption1Enabled = Ndis802_11WEPEnabled, - Ndis802_11WEPDisabled, - Ndis802_11EncryptionDisabled = Ndis802_11WEPDisabled, - Ndis802_11WEPKeyAbsent, - Ndis802_11Encryption1KeyAbsent = Ndis802_11WEPKeyAbsent, - Ndis802_11WEPNotSupported, - Ndis802_11EncryptionNotSupported = Ndis802_11WEPNotSupported, - Ndis802_11Encryption2Enabled, - Ndis802_11Encryption2KeyAbsent, - Ndis802_11Encryption3Enabled, - Ndis802_11Encryption3KeyAbsent -} NDIS_802_11_WEP_STATUS, *PNDIS_802_11_WEP_STATUS, - NDIS_802_11_ENCRYPTION_STATUS, *PNDIS_802_11_ENCRYPTION_STATUS; - -typedef enum _NDIS_802_11_RELOAD_DEFAULTS -{ - Ndis802_11ReloadWEPKeys -} NDIS_802_11_RELOAD_DEFAULTS, *PNDIS_802_11_RELOAD_DEFAULTS; - -#define NDIS_802_11_AI_REQFI_CAPABILITIES 1 -#define NDIS_802_11_AI_REQFI_LISTENINTERVAL 2 -#define NDIS_802_11_AI_REQFI_CURRENTAPADDRESS 4 - -#define NDIS_802_11_AI_RESFI_CAPABILITIES 1 -#define NDIS_802_11_AI_RESFI_STATUSCODE 2 -#define NDIS_802_11_AI_RESFI_ASSOCIATIONID 4 - -typedef struct _NDIS_802_11_AI_REQFI -{ - USHORT Capabilities; - USHORT ListenInterval; - NDIS_802_11_MAC_ADDRESS CurrentAPAddress; -} NDIS_802_11_AI_REQFI, *PNDIS_802_11_AI_REQFI; - -typedef struct _NDIS_802_11_AI_RESFI -{ - USHORT Capabilities; - USHORT StatusCode; - USHORT AssociationId; -} NDIS_802_11_AI_RESFI, *PNDIS_802_11_AI_RESFI; - -typedef struct _NDIS_802_11_ASSOCIATION_INFORMATION -{ - ULONG Length; - USHORT AvailableRequestFixedIEs; - NDIS_802_11_AI_REQFI RequestFixedIEs; - ULONG RequestIELength; - ULONG OffsetRequestIEs; - USHORT AvailableResponseFixedIEs; - NDIS_802_11_AI_RESFI ResponseFixedIEs; - ULONG ResponseIELength; - ULONG OffsetResponseIEs; -} NDIS_802_11_ASSOCIATION_INFORMATION, *PNDIS_802_11_ASSOCIATION_INFORMATION; - -struct ndis_pmkid_entry { - struct ndis_pmkid_entry *next; - u8 bssid[ETH_ALEN]; - u8 pmkid[16]; -}; - -typedef struct _MLME_DEAUTH_REQ_STRUCT { - UCHAR Addr[MAC_ADDR_LEN]; - USHORT Reason; -} MLME_DEAUTH_REQ_STRUCT, *PMLME_DEAUTH_REQ_STRUCT; diff --git a/src/drivers/driver_roboswitch.c b/src/drivers/driver_roboswitch.c index 6877eda3ed22c..0a9078a4ab065 100644 --- a/src/drivers/driver_roboswitch.c +++ b/src/drivers/driver_roboswitch.c @@ -2,22 +2,16 @@ * 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" #include <sys/ioctl.h> -#include <linux/if.h> #include <linux/sockios.h> #include <linux/if_ether.h> #include <linux/mii.h> +#include <net/if.h> #include "common.h" #include "driver.h" @@ -364,7 +358,7 @@ static void * wpa_driver_roboswitch_init(void *ctx, const char *ifname) /* copy ifname and take a pointer to the second to last character */ sep = drv->ifname + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)) - 2; - /* find the '.' seperating <interface> and <vlan> */ + /* find the '.' separating <interface> and <vlan> */ while (sep > drv->ifname && *sep != '.') sep--; if (sep <= drv->ifname) { wpa_printf(MSG_INFO, "%s: No <interface>.<vlan> pair in " diff --git a/src/drivers/driver_test.c b/src/drivers/driver_test.c index fb2467350f1ee..bd65dd874d2ed 100644 --- a/src/drivers/driver_test.c +++ b/src/drivers/driver_test.c @@ -2,14 +2,8 @@ * Testing driver interface for a simulated network driver * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ /* Make sure we get winsock2.h for Windows build to get sockaddr_storage */ @@ -34,6 +28,8 @@ #include "common/ieee802_11_defs.h" #include "crypto/sha1.h" #include "l2_packet/l2_packet.h" +#include "p2p/p2p.h" +#include "wps/wps.h" #include "driver.h" @@ -87,7 +83,6 @@ struct wpa_driver_test_data { int use_associnfo; u8 assoc_wpa_ie[80]; size_t assoc_wpa_ie_len; - int use_mlme; int associated; u8 *probe_req_ie; size_t probe_req_ie_len; @@ -107,6 +102,20 @@ struct wpa_driver_test_data { unsigned int remain_on_channel_duration; int current_freq; + + struct p2p_data *p2p; + unsigned int off_channel_freq; + struct wpabuf *pending_action_tx; + u8 pending_action_src[ETH_ALEN]; + u8 pending_action_dst[ETH_ALEN]; + u8 pending_action_bssid[ETH_ALEN]; + unsigned int pending_action_freq; + unsigned int pending_action_no_cck; + unsigned int pending_listen_freq; + unsigned int pending_listen_duration; + int pending_p2p_scan; + struct sockaddr *probe_from; + socklen_t probe_from_len; }; @@ -116,6 +125,7 @@ static int wpa_driver_test_attach(struct wpa_driver_test_data *drv, static void wpa_driver_test_close_test_socket( struct wpa_driver_test_data *drv); static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx); +static int wpa_driver_test_init_p2p(struct wpa_driver_test_data *drv); static void test_driver_free_bss(struct test_driver_bss *bss) @@ -159,7 +169,7 @@ test_driver_get_cli(struct wpa_driver_test_data *drv, struct sockaddr_un *from, static int test_driver_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, int encrypt, - const u8 *own_addr) + const u8 *own_addr, u32 flags) { struct test_driver_bss *dbss = priv; struct wpa_driver_test_data *drv = dbss->drv; @@ -293,7 +303,7 @@ static int test_driver_send_ether(void *priv, const u8 *dst, const u8 *src, static int wpa_driver_test_send_mlme(void *priv, const u8 *data, - size_t data_len) + size_t data_len, int noack) { struct test_driver_bss *dbss = priv; struct wpa_driver_test_data *drv = dbss->drv; @@ -469,6 +479,34 @@ static int wpa_driver_test_send_mlme(void *priv, const u8 *data, event.tx_status.ack = ret >= 0; wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event); +#ifdef CONFIG_P2P + if (drv->p2p && + WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) { + if (drv->pending_action_tx == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - " + "no pending operation"); + return ret; + } + + if (os_memcmp(hdr->addr1, drv->pending_action_dst, ETH_ALEN) != + 0) { + wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - " + "unknown destination address"); + return ret; + } + + wpabuf_free(drv->pending_action_tx); + drv->pending_action_tx = NULL; + + p2p_send_action_cb(drv->p2p, drv->pending_action_freq, + drv->pending_action_dst, + drv->pending_action_src, + drv->pending_action_bssid, + ret >= 0); + } +#endif /* CONFIG_P2P */ + return ret; } @@ -515,6 +553,10 @@ static void test_driver_scan(struct wpa_driver_test_data *drv, event.rx_probe_req.ie = ie; event.rx_probe_req.ie_len = ielen; wpa_supplicant_event(drv->ctx, EVENT_RX_PROBE_REQ, &event); +#ifdef CONFIG_P2P + if (drv->p2p) + p2p_probe_req_rx(drv->p2p, sa, NULL, NULL, ie, ielen); +#endif /* CONFIG_P2P */ } dl_list_for_each(bss, &drv->bss, struct test_driver_bss, list) { @@ -624,7 +666,7 @@ static void test_driver_assoc(struct wpa_driver_test_data *drv, sendto(drv->test_socket, cmd, strlen(cmd), 0, (struct sockaddr *) from, fromlen); - drv_event_assoc(bss->bss_ctx, cli->addr, ie, ielen); + drv_event_assoc(bss->bss_ctx, cli->addr, ie, ielen, 0); } @@ -841,7 +883,8 @@ static int test_driver_set_generic_elem(void *priv, static int test_driver_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, - const struct wpabuf *proberesp) + const struct wpabuf *proberesp, + const struct wpabuf *assocresp) { struct test_driver_bss *bss = priv; @@ -1015,7 +1058,8 @@ static int test_driver_bss_remove(void *priv, const char *ifname) static int test_driver_if_add(void *priv, enum wpa_driver_if_type type, const char *ifname, const u8 *addr, void *bss_ctx, void **drv_priv, - char *force_ifname, u8 *if_addr) + char *force_ifname, u8 *if_addr, + const char *bridge) { struct test_driver_bss *dbss = priv; struct wpa_driver_test_data *drv = dbss->drv; @@ -1033,7 +1077,8 @@ static int test_driver_if_add(void *priv, enum wpa_driver_if_type type, sizeof(drv->alloc_iface_idx), if_addr + 1, ETH_ALEN - 1); } - if (type == WPA_IF_AP_BSS) + if (type == WPA_IF_AP_BSS || type == WPA_IF_P2P_GO || + type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP) return test_driver_bss_add(priv, ifname, if_addr, bss_ctx, drv_priv); return 0; @@ -1044,27 +1089,23 @@ static int test_driver_if_remove(void *priv, enum wpa_driver_if_type type, const char *ifname) { wpa_printf(MSG_DEBUG, "%s(type=%d ifname=%s)", __func__, type, ifname); - if (type == WPA_IF_AP_BSS) + if (type == WPA_IF_AP_BSS || type == WPA_IF_P2P_GO || + type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP) return test_driver_bss_remove(priv, ifname); return 0; } -static int test_driver_valid_bss_mask(void *priv, const u8 *addr, - const u8 *mask) -{ - return 0; -} - - static int test_driver_set_ssid(void *priv, const u8 *buf, int len) { struct test_driver_bss *bss = priv; wpa_printf(MSG_DEBUG, "%s(ifname=%s)", __func__, bss->ifname); + if (len < 0) + return -1; wpa_hexdump_ascii(MSG_DEBUG, "test_driver_set_ssid: SSID", buf, len); - if (len < 0 || (size_t) len > sizeof(bss->ssid)) + if ((size_t) len > sizeof(bss->ssid)) return -1; os_memcpy(bss->ssid, buf, len); @@ -1178,6 +1219,7 @@ static void * test_driver_init(struct hostapd_data *hapd, return NULL; drv->ap = 1; bss = dl_list_first(&drv->bss, struct test_driver_bss, list); + drv->global = params->global_priv; bss->bss_ctx = hapd; os_memcpy(bss->bssid, drv->own_addr, ETH_ALEN); @@ -1233,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); @@ -1271,7 +1313,24 @@ static void wpa_driver_test_poll(void *eloop_ctx, void *timeout_ctx) static void wpa_driver_test_scan_timeout(void *eloop_ctx, void *timeout_ctx) { + struct wpa_driver_test_data *drv = eloop_ctx; wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + if (drv->pending_p2p_scan && drv->p2p) { +#ifdef CONFIG_P2P + size_t i; + for (i = 0; i < drv->num_scanres; i++) { + struct wpa_scan_res *bss = drv->scanres[i]; + if (p2p_scan_res_handler(drv->p2p, bss->bssid, + bss->freq, bss->age, + bss->level, + (const u8 *) (bss + 1), + bss->ie_len) > 0) + return; + } + p2p_scan_res_handled(drv->p2p); +#endif /* CONFIG_P2P */ + return; + } wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); } @@ -1420,7 +1479,7 @@ static struct wpa_scan_results * wpa_driver_test_get_scan_results2(void *priv) if (res == NULL) return NULL; - res->res = os_zalloc(drv->num_scanres * sizeof(struct wpa_scan_res *)); + res->res = os_calloc(drv->num_scanres, sizeof(struct wpa_scan_res *)); if (res->res == NULL) { os_free(res); return NULL; @@ -1487,6 +1546,7 @@ static int wpa_driver_test_associate( __func__, priv, params->freq, params->pairwise_suite, params->group_suite, params->key_mgmt_suite, params->auth_alg, params->mode); + wpa_driver_update_mode(drv, params->mode == IEEE80211_MODE_AP); if (params->bssid) { wpa_printf(MSG_DEBUG, " bssid=" MACSTR, MAC2STR(params->bssid)); @@ -1655,20 +1715,6 @@ static int wpa_driver_test_deauthenticate(void *priv, const u8 *addr, } -static int wpa_driver_test_disassociate(void *priv, const u8 *addr, - int reason_code) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d", - __func__, MAC2STR(addr), reason_code); - os_memset(dbss->bssid, 0, ETH_ALEN); - drv->associated = 0; - wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); - return wpa_driver_test_send_disassoc(drv); -} - - static const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie) { const u8 *end, *pos; @@ -1848,6 +1894,8 @@ static void wpa_driver_test_mlme(struct wpa_driver_test_data *drv, { int freq = 0, own_freq; union wpa_event_data event; + const struct ieee80211_mgmt *mgmt; + u16 fc; struct test_driver_bss *bss; bss = dl_list_first(&drv->bss, struct test_driver_bss, list); @@ -1885,23 +1933,45 @@ static void wpa_driver_test_mlme(struct wpa_driver_test_data *drv, event.mlme_rx.freq = freq; wpa_supplicant_event(drv->ctx, EVENT_MLME_RX, &event); - if (drv->probe_req_report && data_len >= 24) { - const struct ieee80211_mgmt *mgmt; - u16 fc; + mgmt = (const struct ieee80211_mgmt *) data; + fc = le_to_host16(mgmt->frame_control); - mgmt = (const struct ieee80211_mgmt *) data; - fc = le_to_host16(mgmt->frame_control); + if (drv->probe_req_report && data_len >= 24) { if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_REQ) { os_memset(&event, 0, sizeof(event)); event.rx_probe_req.sa = mgmt->sa; + event.rx_probe_req.da = mgmt->da; + event.rx_probe_req.bssid = mgmt->bssid; event.rx_probe_req.ie = mgmt->u.probe_req.variable; event.rx_probe_req.ie_len = data_len - (mgmt->u.probe_req.variable - data); wpa_supplicant_event(drv->ctx, EVENT_RX_PROBE_REQ, &event); +#ifdef CONFIG_P2P + if (drv->p2p) + p2p_probe_req_rx(drv->p2p, mgmt->sa, + mgmt->da, mgmt->bssid, + event.rx_probe_req.ie, + event.rx_probe_req.ie_len); +#endif /* CONFIG_P2P */ } } + +#ifdef CONFIG_P2P + if (drv->p2p && + WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) { + size_t hdr_len; + hdr_len = (const u8 *) + &mgmt->u.action.u.vs_public_action.action - data; + p2p_rx_action(drv->p2p, mgmt->da, mgmt->sa, mgmt->bssid, + mgmt->u.action.category, + &mgmt->u.action.u.vs_public_action.action, + data_len - hdr_len, freq); + } +#endif /* CONFIG_P2P */ + } @@ -1917,6 +1987,29 @@ static void wpa_driver_test_scan_cmd(struct wpa_driver_test_data *drv, bss = dl_list_first(&drv->bss, struct test_driver_bss, list); /* data: optional [ STA-addr | ' ' | IEs(hex) ] */ +#ifdef CONFIG_P2P + if (drv->probe_req_report && drv->p2p && data_len) { + const char *d = (const char *) data; + u8 sa[ETH_ALEN]; + u8 ie[512]; + size_t ielen; + + if (hwaddr_aton(d, sa)) + return; + d += 18; + while (*d == ' ') + d++; + ielen = os_strlen(d) / 2; + if (ielen > sizeof(ie)) + ielen = sizeof(ie); + if (hexstr2bin(d, ie, ielen) < 0) + ielen = 0; + drv->probe_from = from; + drv->probe_from_len = fromlen; + p2p_probe_req_rx(drv->p2p, sa, NULL, NULL, ie, ielen); + drv->probe_from = NULL; + } +#endif /* CONFIG_P2P */ if (!drv->ibss) return; @@ -2073,6 +2166,12 @@ static void wpa_driver_test_deinit(void *priv) struct test_client_socket *cli, *prev; int i; +#ifdef CONFIG_P2P + if (drv->p2p) + p2p_deinit(drv->p2p); + wpabuf_free(drv->pending_action_tx); +#endif /* CONFIG_P2P */ + cli = drv->cli; while (cli) { prev = cli; @@ -2140,7 +2239,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); @@ -2269,12 +2368,12 @@ static int wpa_driver_test_set_param(void *priv, const char *param) drv->use_associnfo = 1; } -#ifdef CONFIG_CLIENT_MLME - if (os_strstr(param, "use_mlme=1")) { - wpa_printf(MSG_DEBUG, "test_driver: Use internal MLME"); - drv->use_mlme = 1; + if (os_strstr(param, "p2p_mgmt=1")) { + wpa_printf(MSG_DEBUG, "test_driver: Use internal P2P " + "management"); + if (wpa_driver_test_init_p2p(drv) < 0) + return -1; } -#endif /* CONFIG_CLIENT_MLME */ return 0; } @@ -2382,9 +2481,12 @@ static int wpa_driver_test_get_capa(void *priv, struct wpa_driver_capa *capa) capa->auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED | WPA_DRIVER_AUTH_LEAP; - if (drv->use_mlme) - capa->flags |= WPA_DRIVER_FLAGS_USER_SPACE_MLME; + if (drv->p2p) + capa->flags |= WPA_DRIVER_FLAGS_P2P_MGMT; capa->flags |= WPA_DRIVER_FLAGS_AP; + capa->flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT; + capa->flags |= WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE; + capa->flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE; capa->max_scan_ssids = 2; capa->max_remain_on_chan = 60000; @@ -2408,50 +2510,6 @@ static int wpa_driver_test_mlme_setprotection(void *priv, const u8 *addr, } -static int wpa_driver_test_set_channel(void *priv, - enum hostapd_hw_mode phymode, - int chan, int freq) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - wpa_printf(MSG_DEBUG, "%s: phymode=%d chan=%d freq=%d", - __func__, phymode, chan, freq); - drv->current_freq = freq; - return 0; -} - - -static int wpa_driver_test_mlme_add_sta(void *priv, const u8 *addr, - const u8 *supp_rates, - size_t supp_rates_len) -{ - wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__, MAC2STR(addr)); - return 0; -} - - -static int wpa_driver_test_mlme_remove_sta(void *priv, const u8 *addr) -{ - wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__, MAC2STR(addr)); - return 0; -} - - -static int wpa_driver_test_set_ssid(void *priv, const u8 *ssid, - size_t ssid_len) -{ - wpa_printf(MSG_DEBUG, "%s", __func__); - return 0; -} - - -static int wpa_driver_test_set_bssid(void *priv, const u8 *bssid) -{ - wpa_printf(MSG_DEBUG, "%s: bssid=" MACSTR, __func__, MAC2STR(bssid)); - return 0; -} - - static void * wpa_driver_test_global_init(void) { struct wpa_driver_test_global *global; @@ -2499,15 +2557,14 @@ wpa_driver_test_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) *num_modes = 3; *flags = 0; - modes = os_zalloc(*num_modes * sizeof(struct hostapd_hw_modes)); + modes = os_calloc(*num_modes, sizeof(struct hostapd_hw_modes)); if (modes == NULL) return NULL; modes[0].mode = HOSTAPD_MODE_IEEE80211G; modes[0].num_channels = 11; modes[0].num_rates = 12; - modes[0].channels = - os_zalloc(11 * sizeof(struct hostapd_channel_data)); - modes[0].rates = os_zalloc(modes[0].num_rates * sizeof(int)); + modes[0].channels = os_calloc(11, sizeof(struct hostapd_channel_data)); + modes[0].rates = os_calloc(modes[0].num_rates, sizeof(int)); if (modes[0].channels == NULL || modes[0].rates == NULL) goto fail; for (i = 0; i < 11; i++) { @@ -2531,9 +2588,8 @@ wpa_driver_test_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) modes[1].mode = HOSTAPD_MODE_IEEE80211B; modes[1].num_channels = 11; modes[1].num_rates = 4; - modes[1].channels = - os_zalloc(11 * sizeof(struct hostapd_channel_data)); - modes[1].rates = os_zalloc(modes[1].num_rates * sizeof(int)); + modes[1].channels = os_calloc(11, sizeof(struct hostapd_channel_data)); + modes[1].rates = os_calloc(modes[1].num_rates, sizeof(int)); if (modes[1].channels == NULL || modes[1].rates == NULL) goto fail; for (i = 0; i < 11; i++) { @@ -2549,8 +2605,8 @@ wpa_driver_test_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) modes[2].mode = HOSTAPD_MODE_IEEE80211A; modes[2].num_channels = 1; modes[2].num_rates = 8; - modes[2].channels = os_zalloc(sizeof(struct hostapd_channel_data)); - modes[2].rates = os_zalloc(modes[2].num_rates * sizeof(int)); + modes[2].channels = os_calloc(1, sizeof(struct hostapd_channel_data)); + modes[2].rates = os_calloc(modes[2].num_rates, sizeof(int)); if (modes[2].channels == NULL || modes[2].rates == NULL) goto fail; modes[2].channels[0].chan = 60; @@ -2591,9 +2647,11 @@ static int wpa_driver_test_set_freq(void *priv, static int wpa_driver_test_send_action(void *priv, unsigned int freq, + unsigned int wait, const u8 *dst, const u8 *src, const u8 *bssid, - const u8 *data, size_t data_len) + const u8 *data, size_t data_len, + int no_cck) { struct test_driver_bss *dbss = priv; struct wpa_driver_test_data *drv = dbss->drv; @@ -2626,12 +2684,39 @@ static int wpa_driver_test_send_action(void *priv, unsigned int freq, os_memcpy(hdr->addr2, src, ETH_ALEN); os_memcpy(hdr->addr3, bssid, ETH_ALEN); - ret = wpa_driver_test_send_mlme(priv, buf, 24 + data_len); + ret = wpa_driver_test_send_mlme(priv, buf, 24 + data_len, 0); os_free(buf); return ret; } +#ifdef CONFIG_P2P +static void test_send_action_cb(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_driver_test_data *drv = eloop_ctx; + + if (drv->pending_action_tx == NULL) + return; + + if (drv->off_channel_freq != drv->pending_action_freq) { + wpa_printf(MSG_DEBUG, "P2P: Pending Action frame TX " + "waiting for another freq=%u", + drv->pending_action_freq); + return; + } + wpa_printf(MSG_DEBUG, "P2P: Sending pending Action frame to " + MACSTR, MAC2STR(drv->pending_action_dst)); + wpa_driver_test_send_action(drv, drv->pending_action_freq, 0, + drv->pending_action_dst, + drv->pending_action_src, + drv->pending_action_bssid, + wpabuf_head(drv->pending_action_tx), + wpabuf_len(drv->pending_action_tx), + drv->pending_action_no_cck); +} +#endif /* CONFIG_P2P */ + + static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_driver_test_data *drv = eloop_ctx; @@ -2642,9 +2727,13 @@ static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx) os_memset(&data, 0, sizeof(data)); data.remain_on_channel.freq = drv->remain_on_channel_freq; data.remain_on_channel.duration = drv->remain_on_channel_duration; - wpa_supplicant_event(drv->ctx, EVENT_CANCEL_REMAIN_ON_CHANNEL, &data); + + if (drv->p2p) + drv->off_channel_freq = 0; drv->remain_on_channel_freq = 0; + + wpa_supplicant_event(drv->ctx, EVENT_CANCEL_REMAIN_ON_CHANNEL, &data); } @@ -2675,6 +2764,18 @@ static int wpa_driver_test_remain_on_channel(void *priv, unsigned int freq, data.remain_on_channel.duration = duration; wpa_supplicant_event(drv->ctx, EVENT_REMAIN_ON_CHANNEL, &data); +#ifdef CONFIG_P2P + if (drv->p2p) { + drv->off_channel_freq = drv->remain_on_channel_freq; + test_send_action_cb(drv, NULL); + if (drv->off_channel_freq == drv->pending_listen_freq) { + p2p_listen_cb(drv->p2p, drv->pending_listen_freq, + drv->pending_listen_duration); + drv->pending_listen_freq = 0; + } + } +#endif /* CONFIG_P2P */ + return 0; } @@ -2702,6 +2803,464 @@ static int wpa_driver_test_probe_req_report(void *priv, int report) } +#ifdef CONFIG_P2P + +static int wpa_driver_test_p2p_find(void *priv, unsigned int timeout, int type) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + wpa_printf(MSG_DEBUG, "%s(timeout=%u)", __func__, timeout); + if (!drv->p2p) + return -1; + return p2p_find(drv->p2p, timeout, type, 0, NULL, NULL, 0); +} + + +static int wpa_driver_test_p2p_stop_find(void *priv) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + wpa_printf(MSG_DEBUG, "%s", __func__); + if (!drv->p2p) + return -1; + p2p_stop_find(drv->p2p); + return 0; +} + + +static int wpa_driver_test_p2p_listen(void *priv, unsigned int timeout) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + wpa_printf(MSG_DEBUG, "%s(timeout=%u)", __func__, timeout); + if (!drv->p2p) + return -1; + return p2p_listen(drv->p2p, timeout); +} + + +static int wpa_driver_test_p2p_connect(void *priv, const u8 *peer_addr, + int wps_method, int go_intent, + const u8 *own_interface_addr, + unsigned int force_freq, + int persistent_group) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + wpa_printf(MSG_DEBUG, "%s(peer_addr=" MACSTR " wps_method=%d " + "go_intent=%d " + "own_interface_addr=" MACSTR " force_freq=%u " + "persistent_group=%d)", + __func__, MAC2STR(peer_addr), wps_method, go_intent, + MAC2STR(own_interface_addr), force_freq, persistent_group); + if (!drv->p2p) + return -1; + return p2p_connect(drv->p2p, peer_addr, wps_method, go_intent, + own_interface_addr, force_freq, persistent_group, + NULL, 0, 0, 0); +} + + +static int wpa_driver_test_wps_success_cb(void *priv, const u8 *peer_addr) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + wpa_printf(MSG_DEBUG, "%s(peer_addr=" MACSTR ")", + __func__, MAC2STR(peer_addr)); + if (!drv->p2p) + return -1; + p2p_wps_success_cb(drv->p2p, peer_addr); + return 0; +} + + +static int wpa_driver_test_p2p_group_formation_failed(void *priv) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + wpa_printf(MSG_DEBUG, "%s", __func__); + if (!drv->p2p) + return -1; + p2p_group_formation_failed(drv->p2p); + return 0; +} + + +static int wpa_driver_test_p2p_set_params(void *priv, + const struct p2p_params *params) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + wpa_printf(MSG_DEBUG, "%s", __func__); + if (!drv->p2p) + return -1; + if (p2p_set_dev_name(drv->p2p, params->dev_name) < 0 || + p2p_set_pri_dev_type(drv->p2p, params->pri_dev_type) < 0 || + p2p_set_sec_dev_types(drv->p2p, params->sec_dev_type, + params->num_sec_dev_types) < 0) + return -1; + return 0; +} + + +static int test_p2p_scan(void *ctx, enum p2p_scan_type type, int freq, + unsigned int num_req_dev_types, + const u8 *req_dev_types, const u8 *dev_id, u16 pw_id) +{ + struct wpa_driver_test_data *drv = ctx; + struct wpa_driver_scan_params params; + int ret; + struct wpabuf *wps_ie, *ies; + int social_channels[] = { 2412, 2437, 2462, 0, 0 }; + size_t ielen; + + wpa_printf(MSG_DEBUG, "%s(type=%d freq=%d)", + __func__, type, freq); + + os_memset(¶ms, 0, sizeof(params)); + + /* P2P Wildcard SSID */ + params.num_ssids = 1; + params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID; + params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN; + +#if 0 /* TODO: WPS IE */ + wpa_s->wps->dev.p2p = 1; + wps_ie = wps_build_probe_req_ie(pw_id, &wpa_s->wps->dev, + wpa_s->wps->uuid, WPS_REQ_ENROLLEE); +#else + wps_ie = wpabuf_alloc(1); +#endif + if (wps_ie == NULL) + return -1; + + ielen = p2p_scan_ie_buf_len(drv->p2p); + ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen); + if (ies == NULL) { + wpabuf_free(wps_ie); + return -1; + } + wpabuf_put_buf(ies, wps_ie); + wpabuf_free(wps_ie); + + p2p_scan_ie(drv->p2p, ies, dev_id); + + params.extra_ies = wpabuf_head(ies); + params.extra_ies_len = wpabuf_len(ies); + + switch (type) { + case P2P_SCAN_SOCIAL: + params.freqs = social_channels; + break; + case P2P_SCAN_FULL: + break; + case P2P_SCAN_SOCIAL_PLUS_ONE: + social_channels[3] = freq; + params.freqs = social_channels; + break; + } + + drv->pending_p2p_scan = 1; + ret = wpa_driver_test_scan(drv, ¶ms); + + wpabuf_free(ies); + + return ret; +} + + +static int test_send_action(void *ctx, unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, const u8 *buf, + size_t len, unsigned int wait_time) +{ + struct wpa_driver_test_data *drv = ctx; + + wpa_printf(MSG_DEBUG, "%s(freq=%u dst=" MACSTR " src=" MACSTR + " bssid=" MACSTR " len=%d", + __func__, freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid), + (int) len); + if (freq <= 0) { + wpa_printf(MSG_WARNING, "P2P: No frequency specified for " + "action frame TX"); + return -1; + } + + if (drv->pending_action_tx) { + wpa_printf(MSG_DEBUG, "P2P: Dropped pending Action frame TX " + "to " MACSTR, MAC2STR(drv->pending_action_dst)); + wpabuf_free(drv->pending_action_tx); + } + drv->pending_action_tx = wpabuf_alloc(len); + if (drv->pending_action_tx == NULL) + return -1; + wpabuf_put_data(drv->pending_action_tx, buf, len); + os_memcpy(drv->pending_action_src, src, ETH_ALEN); + os_memcpy(drv->pending_action_dst, dst, ETH_ALEN); + os_memcpy(drv->pending_action_bssid, bssid, ETH_ALEN); + drv->pending_action_freq = freq; + drv->pending_action_no_cck = 1; + + if (drv->off_channel_freq == freq) { + /* Already on requested channel; send immediately */ + /* TODO: Would there ever be need to extend the current + * duration on the channel? */ + eloop_cancel_timeout(test_send_action_cb, drv, NULL); + eloop_register_timeout(0, 0, test_send_action_cb, drv, NULL); + return 0; + } + + wpa_printf(MSG_DEBUG, "P2P: Schedule Action frame to be transmitted " + "once the driver gets to the requested channel"); + if (wpa_driver_test_remain_on_channel(drv, freq, wait_time) < 0) { + wpa_printf(MSG_DEBUG, "P2P: Failed to request driver " + "to remain on channel (%u MHz) for Action " + "Frame TX", freq); + return -1; + } + + return 0; +} + + +static void test_send_action_done(void *ctx) +{ + wpa_printf(MSG_DEBUG, "%s", __func__); + /* TODO */ +} + + +static void test_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) +{ + struct wpa_driver_test_data *drv = ctx; + union wpa_event_data event; + wpa_printf(MSG_DEBUG, "%s", __func__); + os_memset(&event, 0, sizeof(event)); + event.p2p_go_neg_completed.res = res; + wpa_supplicant_event(drv->ctx, EVENT_P2P_GO_NEG_COMPLETED, &event); +} + + +static void test_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id) +{ + struct wpa_driver_test_data *drv = ctx; + union wpa_event_data event; + wpa_printf(MSG_DEBUG, "%s(src=" MACSTR ")", __func__, MAC2STR(src)); + os_memset(&event, 0, sizeof(event)); + event.p2p_go_neg_req_rx.src = src; + event.p2p_go_neg_req_rx.dev_passwd_id = dev_passwd_id; + wpa_supplicant_event(drv->ctx, EVENT_P2P_GO_NEG_REQ_RX, &event); +} + + +static void test_dev_found(void *ctx, const u8 *addr, + const struct p2p_peer_info *info, int new_device) +{ + struct wpa_driver_test_data *drv = ctx; + union wpa_event_data event; + char devtype[WPS_DEV_TYPE_BUFSIZE]; + wpa_printf(MSG_DEBUG, "%s(" MACSTR " p2p_dev_addr=" MACSTR + " pri_dev_type=%s name='%s' config_methods=0x%x " + "dev_capab=0x%x group_capab=0x%x)", + __func__, MAC2STR(addr), MAC2STR(info->p2p_device_addr), + wps_dev_type_bin2str(info->pri_dev_type, devtype, + sizeof(devtype)), + info->device_name, info->config_methods, info->dev_capab, + info->group_capab); + + os_memset(&event, 0, sizeof(event)); + event.p2p_dev_found.addr = addr; + event.p2p_dev_found.dev_addr = info->p2p_device_addr; + event.p2p_dev_found.pri_dev_type = info->pri_dev_type; + event.p2p_dev_found.dev_name = info->device_name; + event.p2p_dev_found.config_methods = info->config_methods; + event.p2p_dev_found.dev_capab = info->dev_capab; + event.p2p_dev_found.group_capab = info->group_capab; + wpa_supplicant_event(drv->ctx, EVENT_P2P_DEV_FOUND, &event); +} + + +static int test_start_listen(void *ctx, unsigned int freq, + unsigned int duration, + const struct wpabuf *probe_resp_ie) +{ + struct wpa_driver_test_data *drv = ctx; + + wpa_printf(MSG_DEBUG, "%s(freq=%u duration=%u)", + __func__, freq, duration); + + if (wpa_driver_test_probe_req_report(drv, 1) < 0) + return -1; + + drv->pending_listen_freq = freq; + drv->pending_listen_duration = duration; + + if (wpa_driver_test_remain_on_channel(drv, freq, duration) < 0) { + drv->pending_listen_freq = 0; + return -1; + } + + return 0; +} + + +static void test_stop_listen(void *ctx) +{ + wpa_printf(MSG_DEBUG, "%s", __func__); + /* TODO */ +} + + +static int test_send_probe_resp(void *ctx, const struct wpabuf *buf) +{ + struct wpa_driver_test_data *drv = ctx; + char resp[512], *pos, *end; + int ret; + const struct ieee80211_mgmt *mgmt; + const u8 *ie, *ie_end; + + wpa_printf(MSG_DEBUG, "%s", __func__); + wpa_hexdump_buf(MSG_MSGDUMP, "Probe Response", buf); + if (wpabuf_len(buf) < 24) + return -1; + if (!drv->probe_from) { + wpa_printf(MSG_DEBUG, "%s: probe_from not set", __func__); + return -1; + } + + pos = resp; + end = resp + sizeof(resp); + + mgmt = wpabuf_head(buf); + + /* reply: SCANRESP BSSID SSID IEs */ + ret = os_snprintf(pos, end - pos, "SCANRESP " MACSTR " ", + MAC2STR(mgmt->bssid)); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + + ie = mgmt->u.probe_resp.variable; + ie_end = wpabuf_head_u8(buf) + wpabuf_len(buf); + if (ie_end - ie < 2 || ie[0] != WLAN_EID_SSID || + ie + 2 + ie[1] > ie_end) + return -1; + pos += wpa_snprintf_hex(pos, end - pos, ie + 2, ie[1]); + + ret = os_snprintf(pos, end - pos, " "); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + pos += wpa_snprintf_hex(pos, end - pos, ie, ie_end - ie); + + sendto(drv->test_socket, resp, pos - resp, 0, + drv->probe_from, drv->probe_from_len); + + return 0; +} + + +static void test_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token, + u16 update_indic, const u8 *tlvs, size_t tlvs_len) +{ + wpa_printf(MSG_DEBUG, "%s", __func__); + /* TODO */ +} + + +static void test_sd_response(void *ctx, const u8 *sa, u16 update_indic, + const u8 *tlvs, size_t tlvs_len) +{ + wpa_printf(MSG_DEBUG, "%s", __func__); + /* TODO */ +} + + +static void test_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods, + const u8 *dev_addr, const u8 *pri_dev_type, + const char *dev_name, u16 supp_config_methods, + u8 dev_capab, u8 group_capab, + const u8 *group_id, size_t group_id_len) +{ + wpa_printf(MSG_DEBUG, "%s(peer=" MACSTR " config_methods=0x%x)", + __func__, MAC2STR(peer), config_methods); + /* TODO */ +} + + +static void test_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods) +{ + wpa_printf(MSG_DEBUG, "%s(peer=" MACSTR " config_methods=0x%x)", + __func__, MAC2STR(peer), config_methods); + /* TODO */ +} + +#endif /* CONFIG_P2P */ + + +static int wpa_driver_test_init_p2p(struct wpa_driver_test_data *drv) +{ +#ifdef CONFIG_P2P + struct p2p_config p2p; + unsigned int r; + int i; + + os_memset(&p2p, 0, sizeof(p2p)); + p2p.msg_ctx = drv->ctx; + p2p.cb_ctx = drv; + p2p.p2p_scan = test_p2p_scan; + p2p.send_action = test_send_action; + p2p.send_action_done = test_send_action_done; + p2p.go_neg_completed = test_go_neg_completed; + p2p.go_neg_req_rx = test_go_neg_req_rx; + p2p.dev_found = test_dev_found; + p2p.start_listen = test_start_listen; + p2p.stop_listen = test_stop_listen; + p2p.send_probe_resp = test_send_probe_resp; + p2p.sd_request = test_sd_request; + p2p.sd_response = test_sd_response; + p2p.prov_disc_req = test_prov_disc_req; + p2p.prov_disc_resp = test_prov_disc_resp; + + os_memcpy(p2p.dev_addr, drv->own_addr, ETH_ALEN); + + p2p.reg_class = 12; /* TODO: change depending on location */ + /* + * Pick one of the social channels randomly as the listen + * channel. + */ + os_get_random((u8 *) &r, sizeof(r)); + p2p.channel = 1 + (r % 3) * 5; + + /* TODO: change depending on location */ + p2p.op_reg_class = 12; + /* + * For initial tests, pick the operation channel randomly. + * TODO: Use scan results (etc.) to select the best channel. + */ + p2p.op_channel = 1 + r % 11; + + os_memcpy(p2p.country, "US ", 3); + + /* FIX: fetch available channels from the driver */ + p2p.channels.reg_classes = 1; + p2p.channels.reg_class[0].reg_class = 12; /* US/12 = 2.4 GHz band */ + p2p.channels.reg_class[0].channels = 11; + for (i = 0; i < 11; i++) + p2p.channels.reg_class[0].channel[i] = i + 1; + + p2p.max_peers = 100; + + drv->p2p = p2p_init(&p2p); + if (drv->p2p == NULL) + return -1; + return 0; +#else /* CONFIG_P2P */ + wpa_printf(MSG_INFO, "driver_test: P2P support not included"); + return -1; +#endif /* CONFIG_P2P */ +} + + const struct wpa_driver_ops wpa_driver_test_ops = { "test", "wpa_supplicant test driver", @@ -2715,7 +3274,6 @@ const struct wpa_driver_ops wpa_driver_test_ops = { .get_hw_feature_data = wpa_driver_test_get_hw_feature_data, .if_add = test_driver_if_add, .if_remove = test_driver_if_remove, - .valid_bss_mask = test_driver_valid_bss_mask, .hapd_set_ssid = test_driver_set_ssid, .set_privacy = test_driver_set_privacy, .set_sta_vlan = test_driver_set_sta_vlan, @@ -2728,17 +3286,11 @@ const struct wpa_driver_ops wpa_driver_test_ops = { .deinit = wpa_driver_test_deinit, .set_param = wpa_driver_test_set_param, .deauthenticate = wpa_driver_test_deauthenticate, - .disassociate = wpa_driver_test_disassociate, .associate = wpa_driver_test_associate, .get_capa = wpa_driver_test_get_capa, .get_mac_addr = wpa_driver_test_get_mac_addr, .send_eapol = wpa_driver_test_send_eapol, .mlme_setprotection = wpa_driver_test_mlme_setprotection, - .set_channel = wpa_driver_test_set_channel, - .set_ssid = wpa_driver_test_set_ssid, - .set_bssid = wpa_driver_test_set_bssid, - .mlme_add_sta = wpa_driver_test_mlme_add_sta, - .mlme_remove_sta = wpa_driver_test_mlme_remove_sta, .get_scan_results2 = wpa_driver_test_get_scan_results2, .global_init = wpa_driver_test_global_init, .global_deinit = wpa_driver_test_global_deinit, @@ -2750,4 +3302,14 @@ const struct wpa_driver_ops wpa_driver_test_ops = { .remain_on_channel = wpa_driver_test_remain_on_channel, .cancel_remain_on_channel = wpa_driver_test_cancel_remain_on_channel, .probe_req_report = wpa_driver_test_probe_req_report, +#ifdef CONFIG_P2P + .p2p_find = wpa_driver_test_p2p_find, + .p2p_stop_find = wpa_driver_test_p2p_stop_find, + .p2p_listen = wpa_driver_test_p2p_listen, + .p2p_connect = wpa_driver_test_p2p_connect, + .wps_success_cb = wpa_driver_test_wps_success_cb, + .p2p_group_formation_failed = + wpa_driver_test_p2p_group_formation_failed, + .p2p_set_params = wpa_driver_test_p2p_set_params, +#endif /* CONFIG_P2P */ }; diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c index 2614f23093fac..9733e015638d5 100644 --- a/src/drivers/driver_wext.c +++ b/src/drivers/driver_wext.c @@ -2,14 +2,8 @@ * Driver interaction with generic Linux Wireless Extensions * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file implements a driver interface for the Linux Wireless Extensions. * When used with WE-18 or newer, this interface can be used as-is with number @@ -20,10 +14,12 @@ #include "includes.h" #include <sys/ioctl.h> +#include <sys/types.h> #include <sys/stat.h> +#include <fcntl.h> #include <net/if_arp.h> -#include "wireless_copy.h" +#include "linux_wext.h" #include "common.h" #include "eloop.h" #include "common/ieee802_11_defs.h" @@ -31,9 +27,13 @@ #include "priv_netlink.h" #include "netlink.h" #include "linux_ioctl.h" +#include "rfkill.h" #include "driver.h" #include "driver_wext.h" +#ifdef ANDROID +#include "android_drv.h" +#endif /* ANDROID */ static int wpa_driver_wext_flush_pmkid(void *priv); static int wpa_driver_wext_get_range(void *priv); @@ -299,6 +299,14 @@ wpa_driver_wext_event_wireless_custom(void *ctx, char *custom) } wpa_supplicant_event(ctx, EVENT_STKSTART, &data); #endif /* CONFIG_PEERKEY */ +#ifdef ANDROID + } else if (os_strncmp(custom, "STOP", 4) == 0) { + wpa_msg(ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STOPPED"); + } else if (os_strncmp(custom, "START", 5) == 0) { + wpa_msg(ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STARTED"); + } else if (os_strncmp(custom, "HANG", 4) == 0) { + wpa_msg(ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED"); +#endif /* ANDROID */ } } @@ -561,10 +569,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); @@ -620,6 +646,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", @@ -634,6 +661,35 @@ static void wpa_driver_wext_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi, (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); + + if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) { + wpa_printf(MSG_DEBUG, "WEXT: Interface down"); + drv->if_disabled = 1; + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, NULL); + } + + if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) { + 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); + } + } + /* * Some drivers send the association event before the operup event--in * this case, lifting operstate in wpa_driver_wext_set_operstate() @@ -687,6 +743,62 @@ static void wpa_driver_wext_event_rtm_dellink(void *ctx, struct ifinfomsg *ifi, } +static void wpa_driver_wext_rfkill_blocked(void *ctx) +{ + wpa_printf(MSG_DEBUG, "WEXT: RFKILL blocked"); + /* + * This may be for any interface; use ifdown event to disable + * interface. + */ +} + + +static void wpa_driver_wext_rfkill_unblocked(void *ctx) +{ + struct wpa_driver_wext_data *drv = ctx; + wpa_printf(MSG_DEBUG, "WEXT: RFKILL unblocked"); + if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1)) { + wpa_printf(MSG_DEBUG, "WEXT: Could not set interface UP " + "after rfkill unblock"); + return; + } + /* rtnetlink ifup handler will report interface as enabled */ +} + + +static void wext_get_phy_name(struct wpa_driver_wext_data *drv) +{ + /* Find phy (radio) to which this interface belongs */ + char buf[90], *pos; + int f, rv; + + drv->phyname[0] = '\0'; + snprintf(buf, sizeof(buf) - 1, "/sys/class/net/%s/phy80211/name", + drv->ifname); + f = open(buf, O_RDONLY); + if (f < 0) { + wpa_printf(MSG_DEBUG, "Could not open file %s: %s", + buf, strerror(errno)); + return; + } + + rv = read(f, drv->phyname, sizeof(drv->phyname) - 1); + close(f); + if (rv < 0) { + wpa_printf(MSG_DEBUG, "Could not read file %s: %s", + buf, strerror(errno)); + return; + } + + drv->phyname[rv] = '\0'; + pos = os_strchr(drv->phyname, '\n'); + if (pos) + *pos = '\0'; + wpa_printf(MSG_DEBUG, "wext: interface %s phy: %s", + drv->ifname, drv->phyname); +} + + /** * wpa_driver_wext_init - Initialize WE driver interface * @ctx: context to be used when calling wpa_supplicant functions, @@ -698,6 +810,7 @@ void * wpa_driver_wext_init(void *ctx, const char *ifname) { struct wpa_driver_wext_data *drv; struct netlink_config *cfg; + struct rfkill_config *rcfg; char path[128]; struct stat buf; @@ -711,6 +824,7 @@ void * wpa_driver_wext_init(void *ctx, const char *ifname) if (stat(path, &buf) == 0) { wpa_printf(MSG_DEBUG, "WEXT: cfg80211-based driver detected"); drv->cfg80211 = 1; + wext_get_phy_name(drv); } drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); @@ -731,8 +845,27 @@ void * wpa_driver_wext_init(void *ctx, const char *ifname) goto err2; } + rcfg = os_zalloc(sizeof(*rcfg)); + if (rcfg == NULL) + goto err3; + rcfg->ctx = drv; + os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname)); + rcfg->blocked_cb = wpa_driver_wext_rfkill_blocked; + rcfg->unblocked_cb = wpa_driver_wext_rfkill_unblocked; + drv->rfkill = rfkill_init(rcfg); + if (drv->rfkill == NULL) { + wpa_printf(MSG_DEBUG, "WEXT: RFKILL status not available"); + os_free(rcfg); + } + drv->mlme_sock = -1; +#ifdef ANDROID + drv->errors = 0; + drv->driver_is_started = TRUE; + drv->bgscan_enabled = 0; +#endif /* ANDROID */ + if (wpa_driver_wext_finish_drv_init(drv) < 0) goto err3; @@ -741,6 +874,7 @@ void * wpa_driver_wext_init(void *ctx, const char *ifname) return drv; err3: + rfkill_deinit(drv->rfkill); netlink_deinit(drv->netlink); err2: close(drv->ioctl_sock); @@ -750,10 +884,29 @@ err1: } +static void wpa_driver_wext_send_rfkill(void *eloop_ctx, void *timeout_ctx) +{ + wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL); +} + + static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv) { - if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1) < 0) - return -1; + int send_rfkill_event = 0; + + if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1) < 0) { + if (rfkill_is_blocked(drv->rfkill)) { + wpa_printf(MSG_DEBUG, "WEXT: Could not yet enable " + "interface '%s' due to rfkill", + drv->ifname); + drv->if_disabled = 1; + send_rfkill_event = 1; + } else { + wpa_printf(MSG_ERROR, "WEXT: Could not set " + "interface '%s' UP", drv->ifname); + return -1; + } + } /* * Make sure that the driver does not have any obsolete PMKID entries. @@ -795,6 +948,11 @@ static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv) netlink_send_oper_ifla(drv->netlink, drv->ifindex, 1, IF_OPER_DORMANT); + if (send_rfkill_event) { + eloop_register_timeout(0, 0, wpa_driver_wext_send_rfkill, + drv, drv->ctx); + } + return 0; } @@ -822,6 +980,7 @@ void wpa_driver_wext_deinit(void *priv) netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP); netlink_deinit(drv->netlink); + rfkill_deinit(drv->rfkill); if (drv->mlme_sock >= 0) eloop_unregister_read_sock(drv->mlme_sock); @@ -894,7 +1053,7 @@ int wpa_driver_wext_scan(void *priv, struct wpa_driver_scan_params *params) /* Not all drivers generate "scan completed" wireless event, so try to * read results after a timeout. */ - timeout = 5; + timeout = 10; if (drv->scan_complete_events) { /* * The driver seems to deliver SIOCGIWSCAN events to notify @@ -1040,7 +1199,8 @@ static void wext_get_scan_freq(struct iw_event *iwe, } -static void wext_get_scan_qual(struct iw_event *iwe, +static void wext_get_scan_qual(struct wpa_driver_wext_data *drv, + struct iw_event *iwe, struct wext_scan_data *res) { res->res.qual = iwe->u.qual.qual; @@ -1054,6 +1214,14 @@ static void wext_get_scan_qual(struct iw_event *iwe, res->res.flags |= WPA_SCAN_NOISE_INVALID; if (iwe->u.qual.updated & IW_QUAL_DBM) res->res.flags |= WPA_SCAN_LEVEL_DBM; + if ((iwe->u.qual.updated & IW_QUAL_DBM) || + ((iwe->u.qual.level != 0) && + (iwe->u.qual.level > drv->max_level))) { + if (iwe->u.qual.level >= 64) + res->res.level -= 0x100; + if (iwe->u.qual.noise >= 64) + res->res.noise -= 0x100; + } } @@ -1246,8 +1414,8 @@ static void wpa_driver_wext_add_scan_entry(struct wpa_scan_results *res, if (data->ie) os_memcpy(pos, data->ie, data->ie_len); - tmp = os_realloc(res->res, - (res->num + 1) * sizeof(struct wpa_scan_res *)); + tmp = os_realloc_array(res->res, res->num + 1, + sizeof(struct wpa_scan_res *)); if (tmp == NULL) { os_free(r); return; @@ -1255,7 +1423,7 @@ static void wpa_driver_wext_add_scan_entry(struct wpa_scan_results *res, tmp[res->num++] = r; res->res = tmp; } - + /** * wpa_driver_wext_get_scan_results - Fetch the latest scan results @@ -1265,7 +1433,7 @@ static void wpa_driver_wext_add_scan_entry(struct wpa_scan_results *res, struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv) { struct wpa_driver_wext_data *drv = priv; - size_t ap_num = 0, len; + size_t len; int first; u8 *res_buf; struct iw_event iwe_buf, *iwe = &iwe_buf; @@ -1277,7 +1445,6 @@ struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv) if (res_buf == NULL) return NULL; - ap_num = 0; first = 1; res = os_zalloc(sizeof(*res)); @@ -1329,7 +1496,7 @@ struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv) wext_get_scan_freq(iwe, &data); break; case IWEVQUAL: - wext_get_scan_qual(iwe, &data); + wext_get_scan_qual(drv, iwe, &data); break; case SIOCGIWENCODE: wext_get_scan_encode(iwe, &data); @@ -1408,6 +1575,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) @@ -1427,6 +1595,8 @@ static int wpa_driver_wext_get_range(void *priv) "assuming WPA is not supported"); } + drv->max_level = range->max_qual.level; + os_free(range); return 0; } @@ -1498,8 +1668,7 @@ static int wpa_driver_wext_set_key_ext(void *priv, enum wpa_alg alg, iwr.u.encoding.pointer = (caddr_t) ext; iwr.u.encoding.length = sizeof(*ext) + key_len; - if (addr == NULL || - os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0) + if (addr == NULL || is_broadcast_ether_addr(addr)) ext->ext_flags |= IW_ENCODE_EXT_GROUP_KEY; if (set_tx) ext->ext_flags |= IW_ENCODE_EXT_SET_TX_KEY; @@ -1702,8 +1871,10 @@ static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv) { struct iwreq iwr; const u8 null_bssid[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; +#ifndef ANDROID u8 ssid[32]; int i; +#endif /* ANDROID */ /* * Only force-disconnect when the card is in infrastructure mode, @@ -1718,32 +1889,39 @@ static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv) } if (iwr.u.mode == IW_MODE_INFRA) { + /* Clear the BSSID selection */ + if (wpa_driver_wext_set_bssid(drv, null_bssid) < 0) { + wpa_printf(MSG_DEBUG, "WEXT: Failed to clear BSSID " + "selection on disconnect"); + } + +#ifndef ANDROID if (drv->cfg80211) { /* * cfg80211 supports SIOCSIWMLME commands, so there is * no need for the random SSID hack, but clear the - * BSSID and SSID. + * SSID. */ - if (wpa_driver_wext_set_bssid(drv, null_bssid) < 0 || - wpa_driver_wext_set_ssid(drv, (u8 *) "", 0) < 0) { + if (wpa_driver_wext_set_ssid(drv, (u8 *) "", 0) < 0) { wpa_printf(MSG_DEBUG, "WEXT: Failed to clear " - "to disconnect"); + "SSID on disconnect"); } return; } + /* - * Clear the BSSID selection and set a random SSID to make sure - * the driver will not be trying to associate with something - * even if it does not understand SIOCSIWMLME commands (or - * tries to associate automatically after deauth/disassoc). + * Set a random SSID to make sure the driver will not be trying + * to associate with something even if it does not understand + * SIOCSIWMLME commands (or tries to associate automatically + * after deauth/disassoc). */ for (i = 0; i < 32; i++) ssid[i] = rand() & 0xFF; - if (wpa_driver_wext_set_bssid(drv, null_bssid) < 0 || - wpa_driver_wext_set_ssid(drv, ssid, 32) < 0) { + if (wpa_driver_wext_set_ssid(drv, ssid, 32) < 0) { wpa_printf(MSG_DEBUG, "WEXT: Failed to set bogus " - "BSSID/SSID to disconnect"); + "SSID to disconnect"); } +#endif /* ANDROID */ } } @@ -1760,18 +1938,6 @@ static int wpa_driver_wext_deauthenticate(void *priv, const u8 *addr, } -static int wpa_driver_wext_disassociate(void *priv, const u8 *addr, - int reason_code) -{ - struct wpa_driver_wext_data *drv = priv; - int ret; - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - ret = wpa_driver_wext_mlme(drv, addr, IW_MLME_DISASSOC, reason_code); - wpa_driver_wext_disconnect(drv); - return ret; -} - - static int wpa_driver_wext_set_gen_ie(void *priv, const u8 *ie, size_t ie_len) { @@ -2167,6 +2333,136 @@ int wpa_driver_wext_get_version(struct wpa_driver_wext_data *drv) } +static const char * wext_get_radio_name(void *priv) +{ + struct wpa_driver_wext_data *drv = priv; + return drv->phyname; +} + + +#ifdef ANDROID + +static int android_wext_cmd(struct wpa_driver_wext_data *drv, const char *cmd) +{ + struct iwreq iwr; + char buf[MAX_DRV_CMD_SIZE]; + int ret; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + + os_memset(buf, 0, sizeof(buf)); + os_strlcpy(buf, cmd, sizeof(buf)); + + iwr.u.data.pointer = buf; + iwr.u.data.length = sizeof(buf); + + ret = ioctl(drv->ioctl_sock, SIOCSIWPRIV, &iwr); + + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s failed (%d): %s", __func__, ret, + cmd); + drv->errors++; + if (drv->errors > DRV_NUMBER_SEQUENTIAL_ERRORS) { + drv->errors = 0; + wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE + "HANGED"); + } + return ret; + } + + drv->errors = 0; + return 0; +} + + +static int wext_sched_scan(void *priv, struct wpa_driver_scan_params *params, + u32 interval) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0, i = 0, bp; + char buf[WEXT_PNO_MAX_COMMAND_SIZE]; + + bp = WEXT_PNOSETUP_HEADER_SIZE; + os_memcpy(buf, WEXT_PNOSETUP_HEADER, bp); + buf[bp++] = WEXT_PNO_TLV_PREFIX; + buf[bp++] = WEXT_PNO_TLV_VERSION; + buf[bp++] = WEXT_PNO_TLV_SUBVERSION; + buf[bp++] = WEXT_PNO_TLV_RESERVED; + + while (i < WEXT_PNO_AMOUNT && (size_t) i < params->num_ssids) { + /* + * Check that there is enough space needed for 1 more SSID, the + * other sections and null termination. + */ + if ((bp + WEXT_PNO_SSID_HEADER_SIZE + IW_ESSID_MAX_SIZE + + WEXT_PNO_NONSSID_SECTIONS_SIZE + 1) >= (int) sizeof(buf)) + break; + + wpa_hexdump_ascii(MSG_DEBUG, "For PNO Scan", + params->ssids[i].ssid, + params->ssids[i].ssid_len); + buf[bp++] = WEXT_PNO_SSID_SECTION; + buf[bp++] = params->ssids[i].ssid_len; + os_memcpy(&buf[bp], params->ssids[i].ssid, + params->ssids[i].ssid_len); + bp += params->ssids[i].ssid_len; + i++; + } + + buf[bp++] = WEXT_PNO_SCAN_INTERVAL_SECTION; + /* TODO: consider using interval parameter (interval in msec) instead + * of hardcoded value here */ + os_snprintf(&buf[bp], WEXT_PNO_SCAN_INTERVAL_LENGTH + 1, "%x", + WEXT_PNO_SCAN_INTERVAL); + bp += WEXT_PNO_SCAN_INTERVAL_LENGTH; + + buf[bp++] = WEXT_PNO_REPEAT_SECTION; + os_snprintf(&buf[bp], WEXT_PNO_REPEAT_LENGTH + 1, "%x", + WEXT_PNO_REPEAT); + bp += WEXT_PNO_REPEAT_LENGTH; + + buf[bp++] = WEXT_PNO_MAX_REPEAT_SECTION; + os_snprintf(&buf[bp], WEXT_PNO_MAX_REPEAT_LENGTH + 1, "%x", + WEXT_PNO_MAX_REPEAT); + bp += WEXT_PNO_MAX_REPEAT_LENGTH + 1; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = buf; + iwr.u.data.length = bp; + + ret = ioctl(drv->ioctl_sock, SIOCSIWPRIV, &iwr); + if (ret < 0) { + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (pnosetup): %d", + ret); + drv->errors++; + if (drv->errors > DRV_NUMBER_SEQUENTIAL_ERRORS) { + drv->errors = 0; + wpa_msg(drv->ctx, MSG_INFO, + WPA_EVENT_DRIVER_STATE "HANGED"); + } + return ret; + } + + drv->errors = 0; + drv->bgscan_enabled = 1; + + return android_wext_cmd(drv, "PNOFORCE 1"); +} + + +static int wext_stop_sched_scan(void *priv) +{ + struct wpa_driver_wext_data *drv = priv; + drv->bgscan_enabled = 0; + return android_wext_cmd(drv, "PNOFORCE 0"); +} + +#endif /* ANDROID */ + + const struct wpa_driver_ops wpa_driver_wext_ops = { .name = "wext", .desc = "Linux wireless extensions (generic)", @@ -2177,7 +2473,6 @@ const struct wpa_driver_ops wpa_driver_wext_ops = { .scan2 = wpa_driver_wext_scan, .get_scan_results2 = wpa_driver_wext_get_scan_results, .deauthenticate = wpa_driver_wext_deauthenticate, - .disassociate = wpa_driver_wext_disassociate, .associate = wpa_driver_wext_associate, .init = wpa_driver_wext_init, .deinit = wpa_driver_wext_deinit, @@ -2186,4 +2481,9 @@ const struct wpa_driver_ops wpa_driver_wext_ops = { .flush_pmkid = wpa_driver_wext_flush_pmkid, .get_capa = wpa_driver_wext_get_capa, .set_operstate = wpa_driver_wext_set_operstate, + .get_radio_name = wext_get_radio_name, +#ifdef ANDROID + .sched_scan = wext_sched_scan, + .stop_sched_scan = wext_stop_sched_scan, +#endif /* ANDROID */ }; diff --git a/src/drivers/driver_wext.h b/src/drivers/driver_wext.h index 602c7e1f689c0..c4a5bc99c8039 100644 --- a/src/drivers/driver_wext.h +++ b/src/drivers/driver_wext.h @@ -2,14 +2,8 @@ * WPA Supplicant - driver_wext exported functions * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef DRIVER_WEXT_H @@ -23,9 +17,12 @@ struct wpa_driver_wext_data { int ioctl_sock; int mlme_sock; char ifname[IFNAMSIZ + 1]; + char phyname[32]; int ifindex; int ifindex2; int if_removed; + int if_disabled; + struct rfkill_data *rfkill; u8 *assoc_req_ies; size_t assoc_req_ies_len; u8 *assoc_resp_ies; @@ -45,6 +42,14 @@ struct wpa_driver_wext_data { int scan_complete_events; int cfg80211; /* whether driver is using cfg80211 */ + + u8 max_level; + +#ifdef ANDROID + int errors; + int driver_is_started; + int bgscan_enabled; +#endif /* ANDROID */ }; int wpa_driver_wext_get_bssid(void *priv, u8 *bssid); diff --git a/src/drivers/driver_wired.c b/src/drivers/driver_wired.c index 2b197f0ab6c72..e0f0f2289bce9 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" @@ -24,6 +18,9 @@ #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) #include <net/if_dl.h> #endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */ +#ifdef __sun__ +#include <sys/sockio.h> +#endif /* __sun__ */ #include "common.h" #include "eloop.h" @@ -311,7 +308,7 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr) static int wired_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, int encrypt, - const u8 *own_addr) + const u8 *own_addr, u32 flags) { struct wpa_driver_wired_data *drv = priv; struct ieee8023_hdr *hdr; @@ -462,6 +459,10 @@ static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add) struct ifreq ifr; int s; +#ifdef __sun__ + return -1; +#endif /* __sun__ */ + s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { perror("socket"); diff --git a/src/drivers/drivers.c b/src/drivers/drivers.c index bffbbde72b77b..a92eddf302950 100644 --- a/src/drivers/drivers.c +++ b/src/drivers/drivers.c @@ -2,14 +2,8 @@ * Driver interface list * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -24,25 +18,9 @@ extern struct wpa_driver_ops wpa_driver_nl80211_ops; /* driver_nl80211.c */ #ifdef CONFIG_DRIVER_HOSTAP extern struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */ #endif /* CONFIG_DRIVER_HOSTAP */ -#ifdef CONFIG_DRIVER_HERMES -extern struct wpa_driver_ops wpa_driver_hermes_ops; /* driver_hermes.c */ -#endif /* CONFIG_DRIVER_HERMES */ #ifdef CONFIG_DRIVER_MADWIFI extern struct wpa_driver_ops wpa_driver_madwifi_ops; /* driver_madwifi.c */ #endif /* CONFIG_DRIVER_MADWIFI */ -#ifdef CONFIG_DRIVER_ATMEL -extern struct wpa_driver_ops wpa_driver_atmel_ops; /* driver_atmel.c */ -#endif /* CONFIG_DRIVER_ATMEL */ -#ifdef CONFIG_DRIVER_NDISWRAPPER -/* driver_ndiswrapper.c */ -extern struct wpa_driver_ops wpa_driver_ndiswrapper_ops; -#endif /* CONFIG_DRIVER_NDISWRAPPER */ -#ifdef CONFIG_DRIVER_BROADCOM -extern struct wpa_driver_ops wpa_driver_broadcom_ops; /* driver_broadcom.c */ -#endif /* CONFIG_DRIVER_BROADCOM */ -#ifdef CONFIG_DRIVER_IPW -extern struct wpa_driver_ops wpa_driver_ipw_ops; /* driver_ipw.c */ -#endif /* CONFIG_DRIVER_IPW */ #ifdef CONFIG_DRIVER_BSD extern struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */ #endif /* CONFIG_DRIVER_BSD */ @@ -55,15 +33,6 @@ extern struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */ #ifdef CONFIG_DRIVER_TEST extern struct wpa_driver_ops wpa_driver_test_ops; /* driver_test.c */ #endif /* CONFIG_DRIVER_TEST */ -#ifdef CONFIG_DRIVER_RALINK -extern struct wpa_driver_ops wpa_driver_ralink_ops; /* driver_ralink.c */ -#endif /* CONFIG_DRIVER_RALINK */ -#ifdef CONFIG_DRIVER_OSX -extern struct wpa_driver_ops wpa_driver_osx_ops; /* driver_osx.m */ -#endif /* CONFIG_DRIVER_OSX */ -#ifdef CONFIG_DRIVER_IPHONE -extern struct wpa_driver_ops wpa_driver_iphone_ops; /* driver_iphone.m */ -#endif /* CONFIG_DRIVER_IPHONE */ #ifdef CONFIG_DRIVER_ROBOSWITCH /* driver_roboswitch.c */ extern struct wpa_driver_ops wpa_driver_roboswitch_ops; @@ -87,24 +56,9 @@ struct wpa_driver_ops *wpa_drivers[] = #ifdef CONFIG_DRIVER_HOSTAP &wpa_driver_hostap_ops, #endif /* CONFIG_DRIVER_HOSTAP */ -#ifdef CONFIG_DRIVER_HERMES - &wpa_driver_hermes_ops, -#endif /* CONFIG_DRIVER_HERMES */ #ifdef CONFIG_DRIVER_MADWIFI &wpa_driver_madwifi_ops, #endif /* CONFIG_DRIVER_MADWIFI */ -#ifdef CONFIG_DRIVER_ATMEL - &wpa_driver_atmel_ops, -#endif /* CONFIG_DRIVER_ATMEL */ -#ifdef CONFIG_DRIVER_NDISWRAPPER - &wpa_driver_ndiswrapper_ops, -#endif /* CONFIG_DRIVER_NDISWRAPPER */ -#ifdef CONFIG_DRIVER_BROADCOM - &wpa_driver_broadcom_ops, -#endif /* CONFIG_DRIVER_BROADCOM */ -#ifdef CONFIG_DRIVER_IPW - &wpa_driver_ipw_ops, -#endif /* CONFIG_DRIVER_IPW */ #ifdef CONFIG_DRIVER_BSD &wpa_driver_bsd_ops, #endif /* CONFIG_DRIVER_BSD */ @@ -117,15 +71,6 @@ struct wpa_driver_ops *wpa_drivers[] = #ifdef CONFIG_DRIVER_TEST &wpa_driver_test_ops, #endif /* CONFIG_DRIVER_TEST */ -#ifdef CONFIG_DRIVER_RALINK - &wpa_driver_ralink_ops, -#endif /* CONFIG_DRIVER_RALINK */ -#ifdef CONFIG_DRIVER_OSX - &wpa_driver_osx_ops, -#endif /* CONFIG_DRIVER_OSX */ -#ifdef CONFIG_DRIVER_IPHONE - &wpa_driver_iphone_ops, -#endif /* CONFIG_DRIVER_IPHONE */ #ifdef CONFIG_DRIVER_ROBOSWITCH &wpa_driver_roboswitch_ops, #endif /* CONFIG_DRIVER_ROBOSWITCH */ diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak index b76b22953a38e..c7a98d31dc209 100644 --- a/src/drivers/drivers.mak +++ b/src/drivers/drivers.mak @@ -1,28 +1,22 @@ -##### COMMON DRIVERS +##### CLEAR VARS + +DRV_CFLAGS = +DRV_WPA_CFLAGS = +DRV_AP_CFLAGS = +DRV_OBJS = +DRV_WPA_OBJS = +DRV_AP_OBJS = +DRV_LIBS = +DRV_WPA_LIBS = +DRV_AP_LIBS = -ifdef CONFIG_DRIVER_HOSTAP -DRV_CFLAGS += -DCONFIG_DRIVER_HOSTAP -DRV_OBJS += ../src/drivers/driver_hostap.o -CONFIG_WIRELESS_EXTENSION=y -NEED_AP_MLME=y -NEED_NETLINK=y -NEED_LINUX_IOCTL=y -endif +##### COMMON DRIVERS ifdef CONFIG_DRIVER_WIRED DRV_CFLAGS += -DCONFIG_DRIVER_WIRED DRV_OBJS += ../src/drivers/driver_wired.o endif -ifdef CONFIG_DRIVER_MADWIFI -DRV_CFLAGS += -DCONFIG_DRIVER_MADWIFI -DRV_OBJS += ../src/drivers/driver_madwifi.o -CONFIG_WIRELESS_EXTENSION=y -CONFIG_L2_PACKET=linux -NEED_NETLINK=y -NEED_LINUX_IOCTL=y -endif - ifdef CONFIG_DRIVER_NL80211 DRV_CFLAGS += -DCONFIG_DRIVER_NL80211 DRV_OBJS += ../src/drivers/driver_nl80211.o @@ -31,11 +25,23 @@ NEED_SME=y NEED_AP_MLME=y NEED_NETLINK=y NEED_LINUX_IOCTL=y -DRV_LIBS += -lnl +NEED_RFKILL=y -ifdef CONFIG_LIBNL20 -DRV_LIBS += -lnl-genl -DRV_CFLAGS += -DCONFIG_LIBNL20 +ifdef CONFIG_LIBNL32 + DRV_LIBS += -lnl-3 + DRV_LIBS += -lnl-genl-3 + DRV_CFLAGS += -DCONFIG_LIBNL20 -I/usr/include/libnl3 +else + ifdef CONFIG_LIBNL_TINY + DRV_LIBS += -lnl-tiny + else + DRV_LIBS += -lnl + endif + + ifdef CONFIG_LIBNL20 + DRV_LIBS += -lnl-genl + DRV_CFLAGS += -DCONFIG_LIBNL20 + endif endif endif @@ -62,57 +68,40 @@ endif ##### PURE AP DRIVERS -ifdef CONFIG_DRIVER_ATHEROS -DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS -DRV_AP_OBJS += ../src/drivers/driver_atheros.o -CONFIG_L2_PACKET=linux +ifdef CONFIG_DRIVER_HOSTAP +DRV_AP_CFLAGS += -DCONFIG_DRIVER_HOSTAP +DRV_AP_OBJS += ../src/drivers/driver_hostap.o +CONFIG_WIRELESS_EXTENSION=y +NEED_AP_MLME=y NEED_NETLINK=y NEED_LINUX_IOCTL=y endif -##### PURE CLIENT DRIVERS - -ifdef CONFIG_DRIVER_WEXT -DRV_WPA_CFLAGS += -DCONFIG_DRIVER_WEXT +ifdef CONFIG_DRIVER_MADWIFI +DRV_AP_CFLAGS += -DCONFIG_DRIVER_MADWIFI +DRV_AP_OBJS += ../src/drivers/driver_madwifi.o CONFIG_WIRELESS_EXTENSION=y +CONFIG_L2_PACKET=linux NEED_NETLINK=y NEED_LINUX_IOCTL=y endif -ifdef CONFIG_DRIVER_HERMES -DRV_WPA_CFLAGS += -DCONFIG_DRIVER_HERMES -DRV_WPA_OBJS += ../src/drivers/driver_hermes.o -CONFIG_WIRELESS_EXTENSION=y -endif - -ifdef CONFIG_DRIVER_ATMEL -DRV_WPA_CFLAGS += -DCONFIG_DRIVER_ATMEL -DRV_WPA_OBJS += ../src/drivers/driver_atmel.o -CONFIG_WIRELESS_EXTENSION=y -endif - -ifdef CONFIG_DRIVER_NDISWRAPPER -DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDISWRAPPER -DRV_WPA_OBJS += ../src/drivers/driver_ndiswrapper.o -CONFIG_WIRELESS_EXTENSION=y -endif - -ifdef CONFIG_DRIVER_RALINK -DRV_WPA_CFLAGS += -DCONFIG_DRIVER_RALINK -DRV_WPA_OBJS += ../src/drivers/driver_ralink.o +ifdef CONFIG_DRIVER_ATHEROS +DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS +DRV_AP_OBJS += ../src/drivers/driver_atheros.o +CONFIG_L2_PACKET=linux NEED_NETLINK=y NEED_LINUX_IOCTL=y endif -ifdef CONFIG_DRIVER_BROADCOM -DRV_WPA_CFLAGS += -DCONFIG_DRIVER_BROADCOM -DRV_WPA_OBJS += ../src/drivers/driver_broadcom.o -endif +##### PURE CLIENT DRIVERS -ifdef CONFIG_DRIVER_IPW -DRV_WPA_CFLAGS += -DCONFIG_DRIVER_IPW -DRV_WPA_OBJS += ../src/drivers/driver_ipw.o +ifdef CONFIG_DRIVER_WEXT +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_WEXT CONFIG_WIRELESS_EXTENSION=y +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +NEED_RFKILL=y endif ifdef CONFIG_DRIVER_NDIS @@ -130,20 +119,6 @@ DRV_WPA_CFLAGS += -DCONFIG_USE_NDISUIO endif endif -ifdef CONFIG_DRIVER_OSX -DRV_WPA_CFLAGS += -DCONFIG_DRIVER_OSX -DRV_WPA_OBJS += ../src/drivers/driver_osx.o -DRV_WPA_LDFLAGS += -framework CoreFoundation -DRV_WPA_LDFLAGS += -F/System/Library/PrivateFrameworks -framework Apple80211 -endif - -ifdef CONFIG_DRIVER_IPHONE -DRV_WPA_CFLAGS += -DCONFIG_DRIVER_IPHONE -DRV_WPA_OBJS += ../src/drivers/driver_iphone.o -DRV_WPA_OBJS += ../src/drivers/MobileApple80211.o -DRV_WPA_LDFLAGS += -framework CoreFoundation -endif - ifdef CONFIG_DRIVER_ROBOSWITCH DRV_WPA_CFLAGS += -DCONFIG_DRIVER_ROBOSWITCH DRV_WPA_OBJS += ../src/drivers/driver_roboswitch.o @@ -152,6 +127,7 @@ endif ifdef CONFIG_WIRELESS_EXTENSION DRV_WPA_CFLAGS += -DCONFIG_WIRELESS_EXTENSION DRV_WPA_OBJS += ../src/drivers/driver_wext.o +NEED_RFKILL=y endif ifdef NEED_NETLINK @@ -162,6 +138,32 @@ ifdef NEED_LINUX_IOCTL DRV_OBJS += ../src/drivers/linux_ioctl.o endif +ifdef NEED_RFKILL +DRV_OBJS += ../src/drivers/rfkill.o +endif + +ifdef CONFIG_VLAN_NETLINK +ifdef CONFIG_FULL_DYNAMIC_VLAN +ifdef CONFIG_LIBNL32 + DRV_LIBS += -lnl-3 + DRV_LIBS += -lnl-genl-3 + DRV_LIBS += -lnl-route-3 + DRV_CFLAGS += -DCONFIG_LIBNL20 +else + ifdef CONFIG_LIBNL_TINY + DRV_LIBS += -lnl-tiny + else + DRV_LIBS += -lnl + endif + + ifdef CONFIG_LIBNL20 + DRV_LIBS += -lnl-genl + DRV_LIBS += -lnl-route + DRV_CFLAGS += -DCONFIG_LIBNL20 + endif +endif +endif +endif ##### COMMON VARS DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS) diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk new file mode 100644 index 0000000000000..23fcbb7c5b228 --- /dev/null +++ b/src/drivers/drivers.mk @@ -0,0 +1,187 @@ +##### CLEAR VARS + +DRV_CFLAGS = +DRV_WPA_CFLAGS = +DRV_AP_CFLAGS = +DRV_OBJS = +DRV_WPA_OBJS = +DRV_AP_OBJS = +DRV_LIBS = +DRV_WPA_LIBS = +DRV_AP_LIBS = + +##### COMMON DRIVERS + +ifdef CONFIG_DRIVER_WIRED +DRV_CFLAGS += -DCONFIG_DRIVER_WIRED +DRV_OBJS += src/drivers/driver_wired.c +endif + +ifdef CONFIG_DRIVER_NL80211 +DRV_CFLAGS += -DCONFIG_DRIVER_NL80211 +DRV_OBJS += src/drivers/driver_nl80211.c +DRV_OBJS += src/utils/radiotap.c +NEED_SME=y +NEED_AP_MLME=y +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +NEED_RFKILL=y + +ifdef CONFIG_LIBNL32 + DRV_LIBS += -lnl-3 + DRV_LIBS += -lnl-genl-3 + DRV_CFLAGS += -DCONFIG_LIBNL20 -I/usr/include/libnl3 +else + ifdef CONFIG_LIBNL_TINY + DRV_LIBS += -lnl-tiny + else + DRV_LIBS += -lnl + endif + + ifdef CONFIG_LIBNL20 + DRV_LIBS += -lnl-genl + DRV_CFLAGS += -DCONFIG_LIBNL20 + endif +endif +endif + +ifdef CONFIG_DRIVER_BSD +ifndef CONFIG_L2_PACKET +CONFIG_L2_PACKET=freebsd +endif +DRV_CFLAGS += -DCONFIG_DRIVER_BSD +DRV_OBJS += src/drivers/driver_bsd.c +CONFIG_L2_FREEBSD=y +CONFIG_DNET_PCAP=y +endif + +ifdef CONFIG_DRIVER_TEST +DRV_CFLAGS += -DCONFIG_DRIVER_TEST +DRV_OBJS += src/drivers/driver_test.c +NEED_AP_MLME=y +endif + +ifdef CONFIG_DRIVER_NONE +DRV_CFLAGS += -DCONFIG_DRIVER_NONE +DRV_OBJS += src/drivers/driver_none.c +endif + +##### PURE AP DRIVERS + +ifdef CONFIG_DRIVER_HOSTAP +DRV_AP_CFLAGS += -DCONFIG_DRIVER_HOSTAP +DRV_AP_OBJS += src/drivers/driver_hostap.c +CONFIG_WIRELESS_EXTENSION=y +NEED_AP_MLME=y +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +endif + +ifdef CONFIG_DRIVER_MADWIFI +DRV_AP_CFLAGS += -DCONFIG_DRIVER_MADWIFI +DRV_AP_OBJS += src/drivers/driver_madwifi.c +CONFIG_WIRELESS_EXTENSION=y +CONFIG_L2_PACKET=linux +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +endif + +ifdef CONFIG_DRIVER_ATHEROS +DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS +DRV_AP_OBJS += src/drivers/driver_atheros.c +CONFIG_L2_PACKET=linux +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +endif + +##### PURE CLIENT DRIVERS + +ifdef CONFIG_DRIVER_WEXT +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_WEXT +CONFIG_WIRELESS_EXTENSION=y +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +NEED_RFKILL=y +endif + +ifdef CONFIG_DRIVER_NDIS +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDIS +DRV_WPA_OBJS += src/drivers/driver_ndis.c +ifdef CONFIG_NDIS_EVENTS_INTEGRATED +DRV_WPA_OBJS += src/drivers/driver_ndis_.c +endif +ifndef CONFIG_L2_PACKET +CONFIG_L2_PACKET=pcap +endif +CONFIG_WINPCAP=y +ifdef CONFIG_USE_NDISUIO +DRV_WPA_CFLAGS += -DCONFIG_USE_NDISUIO +endif +endif + +ifdef CONFIG_DRIVER_ROBOSWITCH +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_ROBOSWITCH +DRV_WPA_OBJS += src/drivers/driver_roboswitch.c +endif + +ifdef CONFIG_WIRELESS_EXTENSION +DRV_WPA_CFLAGS += -DCONFIG_WIRELESS_EXTENSION +DRV_WPA_OBJS += src/drivers/driver_wext.c +NEED_RFKILL=y +endif + +ifdef NEED_NETLINK +DRV_OBJS += src/drivers/netlink.c +endif + +ifdef NEED_LINUX_IOCTL +DRV_OBJS += src/drivers/linux_ioctl.c +endif + +ifdef NEED_RFKILL +DRV_OBJS += src/drivers/rfkill.c +endif + +ifdef CONFIG_DRIVER_CUSTOM +DRV_CFLAGS += -DCONFIG_DRIVER_CUSTOM +endif + +ifdef CONFIG_VLAN_NETLINK +ifdef CONFIG_FULL_DYNAMIC_VLAN +ifdef CONFIG_LIBNL32 + DRV_LIBS += -lnl-3 + DRV_LIBS += -lnl-genl-3 + DRV_LIBS += -lnl-route-3 + DRV_CFLAGS += -DCONFIG_LIBNL20 +else + ifdef CONFIG_LIBNL_TINY + DRV_LIBS += -lnl-tiny + else + DRV_LIBS += -lnl + endif + + ifdef CONFIG_LIBNL20 + DRV_LIBS += -lnl-genl + DRV_LIBS += -lnl-route + DRV_CFLAGS += -DCONFIG_LIBNL20 + endif +endif +endif +endif + +##### COMMON VARS +DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS) +DRV_WPA_CFLAGS += $(DRV_CFLAGS) +DRV_AP_CFLAGS += $(DRV_CFLAGS) + +DRV_BOTH_LIBS := $(DRV_LIBS) $(DRV_WPA_LIBS) $(DRV_AP_LIBS) +DRV_WPA_LIBS += $(DRV_LIBS) +DRV_AP_LIBS += $(DRV_LIBS) + +DRV_BOTH_OBJS := $(DRV_OBJS) $(DRV_WPA_OBJS) $(DRV_AP_OBJS) +DRV_WPA_OBJS += $(DRV_OBJS) +DRV_AP_OBJS += $(DRV_OBJS) + +DRV_BOTH_LDFLAGS := $(DRV_LDFLAGS) $(DRV_WPA_LDFLAGS) $(DRV_AP_LDFLAGS) +DRV_WPA_LDFLAGS += $(DRV_LDFLAGS) +DRV_AP_LDFLAGS += $(DRV_LDFLAGS) diff --git a/src/drivers/linux_ioctl.c b/src/drivers/linux_ioctl.c index 0d6cf5416beec..4380428f979c7 100644 --- a/src/drivers/linux_ioctl.c +++ b/src/drivers/linux_ioctl.c @@ -2,14 +2,8 @@ * Linux ioctl helper functions for driver wrappers * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -24,6 +18,7 @@ int linux_set_iface_flags(int sock, const char *ifname, int dev_up) { struct ifreq ifr; + int ret; if (sock < 0) return -1; @@ -32,9 +27,10 @@ int linux_set_iface_flags(int sock, const char *ifname, int dev_up) os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) { + ret = errno ? -errno : -999; wpa_printf(MSG_ERROR, "Could not read interface %s flags: %s", ifname, strerror(errno)); - return -1; + return ret; } if (dev_up) { @@ -48,15 +44,39 @@ int linux_set_iface_flags(int sock, const char *ifname, int dev_up) } if (ioctl(sock, SIOCSIFFLAGS, &ifr) != 0) { - wpa_printf(MSG_ERROR, "Could not set interface %s flags: %s", - ifname, strerror(errno)); - return -1; + ret = errno ? -errno : -999; + wpa_printf(MSG_ERROR, "Could not set interface %s flags (%s): " + "%s", + ifname, dev_up ? "UP" : "DOWN", strerror(errno)); + return ret; } return 0; } +int linux_iface_up(int sock, const char *ifname) +{ + struct ifreq ifr; + int ret; + + if (sock < 0) + return -1; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + + if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) { + ret = errno ? -errno : -999; + wpa_printf(MSG_ERROR, "Could not read interface %s flags: %s", + ifname, strerror(errno)); + return ret; + } + + return !!(ifr.ifr_flags & IFF_UP); +} + + int linux_get_ifhwaddr(int sock, const char *ifname, u8 *addr) { struct ifreq ifr; diff --git a/src/drivers/linux_ioctl.h b/src/drivers/linux_ioctl.h index a5557383d8fe1..c03fe6e9a3fd2 100644 --- a/src/drivers/linux_ioctl.h +++ b/src/drivers/linux_ioctl.h @@ -2,20 +2,15 @@ * Linux ioctl helper functions for driver wrappers * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef LINUX_IOCTL_H #define LINUX_IOCTL_H int linux_set_iface_flags(int sock, const char *ifname, int dev_up); +int linux_iface_up(int sock, const char *ifname); int linux_get_ifhwaddr(int sock, const char *ifname, u8 *addr); int linux_set_ifhwaddr(int sock, const char *ifname, const u8 *addr); int linux_br_add(int sock, const char *brname); diff --git a/src/drivers/linux_wext.h b/src/drivers/linux_wext.h new file mode 100644 index 0000000000000..55cf95531808a --- /dev/null +++ b/src/drivers/linux_wext.h @@ -0,0 +1,45 @@ +/* + * Driver interaction with generic Linux Wireless Extensions + * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef LINUX_WEXT_H +#define LINUX_WEXT_H + +#ifndef ANDROID + +/* + * Avoid including other kernel header to avoid conflicts with C library + * headers. + */ +#define _LINUX_TYPES_H +#define _LINUX_SOCKET_H +#define _LINUX_IF_H + +#include <sys/types.h> +#include <net/if.h> +typedef __uint32_t __u32; +typedef __int32_t __s32; +typedef __uint16_t __u16; +typedef __int16_t __s16; +typedef __uint8_t __u8; +#ifndef __user +#define __user +#endif /* __user */ + +#endif /* ANDROID */ + +#include <linux/wireless.h> + +#ifndef IW_ENCODE_ALG_PMK +#define IW_ENCODE_ALG_PMK 4 +#endif + +#ifndef IW_ENC_CAPA_4WAY_HANDSHAKE +#define IW_ENC_CAPA_4WAY_HANDSHAKE 0x00000010 +#endif + +#endif /* LINUX_WEXT_H */ diff --git a/src/drivers/ndis_events.c b/src/drivers/ndis_events.c index f6eaa7c9f7ee2..93673a36320cf 100644 --- a/src/drivers/ndis_events.c +++ b/src/drivers/ndis_events.c @@ -2,14 +2,8 @@ * ndis_events - Receive NdisMIndicateStatus() events using WMI * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #define _WIN32_WINNT 0x0400 diff --git a/src/drivers/netlink.c b/src/drivers/netlink.c index ad15b1d62a8d8..6c60550fd613d 100644 --- a/src/drivers/netlink.c +++ b/src/drivers/netlink.c @@ -2,14 +2,8 @@ * Netlink helper functions for driver wrappers * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -34,7 +28,7 @@ static void netlink_receive_link(struct netlink_data *netlink, if (cb == NULL || NLMSG_PAYLOAD(h, 0) < sizeof(struct ifinfomsg)) return; cb(netlink->cfg->ctx, NLMSG_DATA(h), - NLMSG_DATA(h) + NLMSG_ALIGN(sizeof(struct ifinfomsg)), + (u8 *) NLMSG_DATA(h) + NLMSG_ALIGN(sizeof(struct ifinfomsg)), NLMSG_PAYLOAD(h, sizeof(struct ifinfomsg))); } @@ -103,8 +97,6 @@ struct netlink_data * netlink_init(struct netlink_config *cfg) if (netlink == NULL) return NULL; - netlink->cfg = cfg; - netlink->sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (netlink->sock < 0) { wpa_printf(MSG_ERROR, "netlink: Failed to open netlink " @@ -127,6 +119,8 @@ struct netlink_data * netlink_init(struct netlink_config *cfg) eloop_register_read_sock(netlink->sock, netlink_receive, netlink, NULL); + netlink->cfg = cfg; + return netlink; } diff --git a/src/drivers/netlink.h b/src/drivers/netlink.h index bcbfbb51fdd70..3a7340e515346 100644 --- a/src/drivers/netlink.h +++ b/src/drivers/netlink.h @@ -2,20 +2,15 @@ * Netlink helper functions for driver wrappers * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef NETLINK_H #define NETLINK_H struct netlink_data; +struct ifinfomsg; struct netlink_config { void *ctx; diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h index 2ea3edeee7aae..1a9a819cfab08 100644 --- a/src/drivers/nl80211_copy.h +++ b/src/drivers/nl80211_copy.h @@ -6,7 +6,7 @@ * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> * Copyright 2008 Michael Wu <flamingice@sourmilk.net> * Copyright 2008 Luis Carlos Cobo <luisca@cozybit.com> - * Copyright 2008 Michael Buesch <mb@bu3sch.de> + * Copyright 2008 Michael Buesch <m@bues.ch> * Copyright 2008, 2009 Luis R. Rodriguez <lrodriguez@atheros.com> * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com> * Copyright 2008 Colin McCabe <colin@cozybit.com> @@ -40,6 +40,76 @@ */ /** + * DOC: Frame transmission/registration support + * + * Frame transmission and registration support exists to allow userspace + * management entities such as wpa_supplicant react to management frames + * that are not being handled by the kernel. This includes, for example, + * certain classes of action frames that cannot be handled in the kernel + * for various reasons. + * + * Frame registration is done on a per-interface basis and registrations + * cannot be removed other than by closing the socket. It is possible to + * specify a registration filter to register, for example, only for a + * certain type of action frame. In particular with action frames, those + * that userspace registers for will not be returned as unhandled by the + * driver, so that the registered application has to take responsibility + * for doing that. + * + * The type of frame that can be registered for is also dependent on the + * driver and interface type. The frame types are advertised in wiphy + * attributes so applications know what to expect. + * + * NOTE: When an interface changes type while registrations are active, + * these registrations are ignored until the interface type is + * changed again. This means that changing the interface type can + * lead to a situation that couldn't otherwise be produced, but + * any such registrations will be dormant in the sense that they + * will not be serviced, i.e. they will not receive any frames. + * + * Frame transmission allows userspace to send for example the required + * responses to action frames. It is subject to some sanity checking, + * but many frames can be transmitted. When a frame was transmitted, its + * status is indicated to the sending socket. + * + * For more technical details, see the corresponding command descriptions + * below. + */ + +/** + * DOC: Virtual interface / concurrency capabilities + * + * Some devices are able to operate with virtual MACs, they can have + * more than one virtual interface. The capability handling for this + * is a bit complex though, as there may be a number of restrictions + * on the types of concurrency that are supported. + * + * To start with, each device supports the interface types listed in + * the %NL80211_ATTR_SUPPORTED_IFTYPES attribute, but by listing the + * types there no concurrency is implied. + * + * Once concurrency is desired, more attributes must be observed: + * To start with, since some interface types are purely managed in + * software, like the AP-VLAN type in mac80211 for example, there's + * an additional list of these, they can be added at any time and + * are only restricted by some semantic restrictions (e.g. AP-VLAN + * cannot be added without a corresponding AP interface). This list + * is exported in the %NL80211_ATTR_SOFTWARE_IFTYPES attribute. + * + * Further, the list of supported combinations is exported. This is + * in the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute. Basically, + * it exports a list of "groups", and at any point in time the + * interfaces that are currently active must fall into any one of + * the advertised groups. Within each group, there are restrictions + * on the number of interfaces of different types that are supported + * and also the number of different channels, along with potentially + * some other restrictions. See &enum nl80211_if_combination_attrs. + * + * All together, these attributes define the concurrency of virtual + * interfaces that a given device supports. + */ + +/** * enum nl80211_commands - supported nl80211 commands * * @NL80211_CMD_UNSPEC: unspecified command to catch errors @@ -52,6 +122,8 @@ * %NL80211_ATTR_WIPHY_CHANNEL_TYPE, %NL80211_ATTR_WIPHY_RETRY_SHORT, * %NL80211_ATTR_WIPHY_RETRY_LONG, %NL80211_ATTR_WIPHY_FRAG_THRESHOLD, * and/or %NL80211_ATTR_WIPHY_RTS_THRESHOLD. + * However, for setting the channel, see %NL80211_CMD_SET_CHANNEL + * instead, the support here is for backward compatibility only. * @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request * or rename notification. Has attributes %NL80211_ATTR_WIPHY and * %NL80211_ATTR_WIPHY_NAME. @@ -84,14 +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. - * @NL80211_CMD_NEW_BEACON: add a new beacon to an access point interface, - * parameters are like for %NL80211_CMD_SET_BEACON. - * @NL80211_CMD_DEL_BEACON: remove the beacon, stop sending it + * @NL80211_CMD_GET_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 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. @@ -109,6 +192,10 @@ * @NL80211_CMD_SET_MPATH: Set mesh path attributes for mesh path to * destination %NL80211_ATTR_MAC on the interface identified by * %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_NEW_MPATH: Create a new mesh path for the destination given by + * %NL80211_ATTR_MAC via %NL80211_ATTR_MPATH_NEXT_HOP. + * @NL80211_CMD_DEL_MPATH: Delete a mesh path to the destination given by + * %NL80211_ATTR_MAC. * @NL80211_CMD_NEW_PATH: Add a mesh path with given attributes to the * the interface identified by %NL80211_ATTR_IFINDEX. * @NL80211_CMD_DEL_PATH: Remove a mesh path identified by %NL80211_ATTR_MAC @@ -130,13 +217,13 @@ * %NL80211_ATTR_REG_RULE_POWER_MAX_ANT_GAIN and * %NL80211_ATTR_REG_RULE_POWER_MAX_EIRP. * @NL80211_CMD_REQ_SET_REG: ask the wireless core to set the regulatory domain - * to the the specified ISO/IEC 3166-1 alpha2 country code. The core will + * to the specified ISO/IEC 3166-1 alpha2 country code. The core will * store this as a valid request and then query userspace for it. * - * @NL80211_CMD_GET_MESH_PARAMS: Get mesh networking properties for the + * @NL80211_CMD_GET_MESH_CONFIG: Get mesh networking properties for the * interface identified by %NL80211_ATTR_IFINDEX * - * @NL80211_CMD_SET_MESH_PARAMS: Set mesh networking properties for the + * @NL80211_CMD_SET_MESH_CONFIG: Set mesh networking properties for the * interface identified by %NL80211_ATTR_IFINDEX * * @NL80211_CMD_SET_MGMT_EXTRA_IE: Set extra IEs for management frames. The @@ -155,16 +242,47 @@ * * @NL80211_CMD_GET_SCAN: get scan results * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters + * %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the + * probe requests at CCK rate or not. * @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to * NL80211_CMD_GET_SCAN and on the "scan" multicast group) * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons, * partial scan results may be available * + * @NL80211_CMD_START_SCHED_SCAN: start a scheduled scan at certain + * intervals, as specified by %NL80211_ATTR_SCHED_SCAN_INTERVAL. + * Like with normal scans, if SSIDs (%NL80211_ATTR_SCAN_SSIDS) + * are passed, they are used in the probe requests. For + * broadcast, a broadcast SSID must be passed (ie. an empty + * string). If no SSID is passed, no probe requests are sent and + * a passive scan is performed. %NL80211_ATTR_SCAN_FREQUENCIES, + * if passed, define which channels should be scanned; if not + * passed, all channels allowed for the current regulatory domain + * are used. Extra IEs can also be passed from the userspace by + * using the %NL80211_ATTR_IE attribute. + * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT + * if scheduled scan is not running. + * @NL80211_CMD_SCHED_SCAN_RESULTS: indicates that there are scheduled scan + * results available. + * @NL80211_CMD_SCHED_SCAN_STOPPED: indicates that the scheduled scan has + * stopped. The driver may issue this event at any time during a + * scheduled scan. One reason for stopping the scan is if the hardware + * does not support starting an association or a normal scan while running + * a scheduled scan. This event is also sent when the + * %NL80211_CMD_STOP_SCHED_SCAN command is received or when the interface + * is brought down while a scheduled scan was running. + * * @NL80211_CMD_GET_SURVEY: get survey resuls, e.g. channel occupation * or noise level * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to * NL80211_CMD_GET_SURVEY and on the "scan" multicast group) * + * @NL80211_CMD_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 @@ -256,7 +374,14 @@ * auth and assoc steps. For this, you need to specify the SSID in a * %NL80211_ATTR_SSID attribute, and can optionally specify the association * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_MAC, - * %NL80211_ATTR_WIPHY_FREQ and %NL80211_ATTR_CONTROL_PORT. + * %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. @@ -274,8 +399,8 @@ * channel for the specified amount of time. This can be used to do * off-channel operations like transmit a Public Action frame and wait for * a response while being associated to an AP on another channel. - * %NL80211_ATTR_WIPHY or %NL80211_ATTR_IFINDEX is used to specify which - * radio is used. %NL80211_ATTR_WIPHY_FREQ is used to specify the + * %NL80211_ATTR_IFINDEX is used to specify which interface (and thus + * radio) is used. %NL80211_ATTR_WIPHY_FREQ is used to specify the * frequency for the operation and %NL80211_ATTR_WIPHY_CHANNEL_TYPE may be * optionally used to specify additional channel parameters. * %NL80211_ATTR_DURATION is used to specify the duration in milliseconds @@ -299,42 +424,174 @@ * rate selection. %NL80211_ATTR_IFINDEX is used to specify the interface * and @NL80211_ATTR_TX_RATES the set of allowed rates. * - * @NL80211_CMD_REGISTER_ACTION: Register for receiving certain action frames - * (via @NL80211_CMD_ACTION) for processing in userspace. This command - * requires an interface index and a match attribute containing the first - * few bytes of the frame that should match, e.g. a single byte for only - * a category match or four bytes for vendor frames including the OUI. - * The registration cannot be dropped, but is removed automatically - * when the netlink socket is closed. Multiple registrations can be made. - * @NL80211_CMD_ACTION: Action frame TX request and RX notification. This - * command is used both as a request to transmit an Action frame and as an - * event indicating reception of an Action frame that was not processed in + * @NL80211_CMD_REGISTER_FRAME: Register for receiving certain mgmt frames + * (via @NL80211_CMD_FRAME) for processing in userspace. This command + * requires an interface index, a frame type attribute (optional for + * backward compatibility reasons, if not given assumes action frames) + * and a match attribute containing the first few bytes of the frame + * that should match, e.g. a single byte for only a category match or + * four bytes for vendor frames including the OUI. The registration + * cannot be dropped, but is removed automatically when the netlink + * socket is closed. Multiple registrations can be made. + * @NL80211_CMD_REGISTER_ACTION: Alias for @NL80211_CMD_REGISTER_FRAME for + * backward compatibility + * @NL80211_CMD_FRAME: Management frame TX request and RX notification. This + * command is used both as a request to transmit a management frame and + * as an event indicating reception of a frame that was not processed in * kernel code, but is for us (i.e., which may need to be processed in a * user space application). %NL80211_ATTR_FRAME is used to specify the * frame contents (including header). %NL80211_ATTR_WIPHY_FREQ (and * optionally %NL80211_ATTR_WIPHY_CHANNEL_TYPE) is used to indicate on - * which channel the frame is to be transmitted or was received. This - * channel has to be the current channel (remain-on-channel or the - * operational channel). When called, this operation returns a cookie - * (%NL80211_ATTR_COOKIE) that will be included with the TX status event - * pertaining to the TX request. - * @NL80211_CMD_ACTION_TX_STATUS: Report TX status of an Action frame - * transmitted with %NL80211_CMD_ACTION. %NL80211_ATTR_COOKIE identifies + * which channel the frame is to be transmitted or was received. If this + * channel is not the current channel (remain-on-channel or the + * operational channel) the device will switch to the given channel and + * transmit the frame, optionally waiting for a response for the time + * specified using %NL80211_ATTR_DURATION. When called, this operation + * returns a cookie (%NL80211_ATTR_COOKIE) that will be included with the + * TX status event pertaining to the TX request. + * %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the + * management frames at CCK rate or not in 2GHz band. + * @NL80211_CMD_FRAME_WAIT_CANCEL: When an off-channel TX was requested, this + * command may be used with the corresponding cookie to cancel the wait + * time if it is known that it is no longer necessary. + * @NL80211_CMD_ACTION: Alias for @NL80211_CMD_FRAME for backward compatibility. + * @NL80211_CMD_FRAME_TX_STATUS: Report TX status of a management frame + * transmitted with %NL80211_CMD_FRAME. %NL80211_ATTR_COOKIE identifies * the TX command and %NL80211_ATTR_FRAME includes the contents of the * frame. %NL80211_ATTR_ACK flag is included if the recipient acknowledged * the frame. + * @NL80211_CMD_ACTION_TX_STATUS: Alias for @NL80211_CMD_FRAME_TX_STATUS for + * backward compatibility. + * + * @NL80211_CMD_SET_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. * @NL80211_CMD_NOTIFY_CQM: Connection quality monitor notification. This * command is used as an event to indicate the that a trigger level was * reached. + * @NL80211_CMD_SET_CHANNEL: Set the channel (using %NL80211_ATTR_WIPHY_FREQ + * and %NL80211_ATTR_WIPHY_CHANNEL_TYPE) the given interface (identifed + * by %NL80211_ATTR_IFINDEX) shall operate on. + * In case multiple channels are supported by the device, the mechanism + * with which it switches channels is implementation-defined. + * When a monitor interface is given, it can only switch channel while + * no other interfaces are operating to avoid disturbing the operation + * of any other interfaces, and other interfaces will again take + * precedence when they are used. + * + * @NL80211_CMD_SET_WDS_PEER: Set the MAC address of the peer on a WDS interface. + * + * @NL80211_CMD_JOIN_MESH: Join a mesh. The mesh ID must be given, and initial + * mesh config parameters may be given. + * @NL80211_CMD_LEAVE_MESH: Leave the mesh network -- no special arguments, the + * network is determined by the network interface. + * + * @NL80211_CMD_UNPROT_DEAUTHENTICATE: Unprotected deauthentication frame + * notification. This event is used to indicate that an unprotected + * deauthentication frame was dropped when MFP is in use. + * @NL80211_CMD_UNPROT_DISASSOCIATE: Unprotected disassociation frame + * notification. This event is used to indicate that an unprotected + * disassociation frame was dropped when MFP is in use. + * + * @NL80211_CMD_NEW_PEER_CANDIDATE: Notification on the reception of a + * beacon or probe response from a compatible mesh peer. This is only + * sent while no station information (sta_info) exists for the new peer + * candidate and when @NL80211_MESH_SETUP_USERSPACE_AUTH is set. On + * reception of this notification, userspace may decide to create a new + * station (@NL80211_CMD_NEW_STATION). To stop this notification from + * reoccurring, the userspace authentication daemon may want to create the + * new station with the AUTHENTICATED flag unset and maybe change it later + * depending on the authentication result. + * + * @NL80211_CMD_GET_WOWLAN: get Wake-on-Wireless-LAN (WoWLAN) settings. + * @NL80211_CMD_SET_WOWLAN: set Wake-on-Wireless-LAN (WoWLAN) settings. + * Since wireless is more complex than wired ethernet, it supports + * various triggers. These triggers can be configured through this + * command with the %NL80211_ATTR_WOWLAN_TRIGGERS attribute. For + * more background information, see + * http://wireless.kernel.org/en/users/Documentation/WoWLAN. + * + * @NL80211_CMD_SET_REKEY_OFFLOAD: This command is used give the driver + * the necessary information for supporting GTK rekey offload. This + * feature is typically used during WoWLAN. The configuration data + * is contained in %NL80211_ATTR_REKEY_DATA (which is nested and + * contains the data in sub-attributes). After rekeying happened, + * this command may also be sent by the driver as an MLME event to + * inform userspace of the new replay counter. + * + * @NL80211_CMD_PMKSA_CANDIDATE: This is used as an event to inform userspace + * of PMKSA caching dandidates. + * + * @NL80211_CMD_TDLS_OPER: Perform a high-level TDLS command (e.g. link setup). + * In addition, this can be used as an event to request userspace to take + * actions on TDLS links (set up a new link or tear down an existing one). + * In such events, %NL80211_ATTR_TDLS_OPERATION indicates the requested + * operation, %NL80211_ATTR_MAC contains the peer MAC address, and + * %NL80211_ATTR_REASON_CODE the reason code to be used (only with + * %NL80211_TDLS_TEARDOWN). + * @NL80211_CMD_TDLS_MGMT: Send a TDLS management frame. + * + * @NL80211_CMD_UNEXPECTED_FRAME: Used by an application controlling an AP + * (or GO) interface (i.e. hostapd) to ask for unexpected frames to + * implement sending deauth to stations that send unexpected class 3 + * frames. Also used as the event sent by the kernel when such a frame + * is received. + * For the event, the %NL80211_ATTR_MAC attribute carries the TA and + * other attributes like the interface index are present. + * If used as the command it must have an interface index and you can + * only unsubscribe from the event by closing the socket. Subscription + * is also for %NL80211_CMD_UNEXPECTED_4ADDR_FRAME events. + * + * @NL80211_CMD_UNEXPECTED_4ADDR_FRAME: Sent as an event indicating that the + * associated station identified by %NL80211_ATTR_MAC sent a 4addr frame + * and wasn't already in a 4-addr VLAN. The event will be sent similarly + * to the %NL80211_CMD_UNEXPECTED_FRAME event, to the same listener. + * + * @NL80211_CMD_PROBE_CLIENT: Probe an associated station on an AP interface + * by sending a null data frame to it and reporting when the frame is + * acknowleged. This is used to allow timing out inactive clients. Uses + * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_MAC. The command returns a + * direct reply with an %NL80211_ATTR_COOKIE that is later used to match + * up the event with the request. The event includes the same data and + * has %NL80211_ATTR_ACK set if the frame was ACKed. + * + * @NL80211_CMD_REGISTER_BEACONS: Register this socket to receive beacons from + * other BSSes when any interfaces are in AP mode. This helps implement + * OLBC handling in hostapd. Beacons are reported in %NL80211_CMD_FRAME + * messages. Note that per PHY only one application may register. + * + * @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_START_P2P_DEVICE: Start the given P2P Device, identified by + * its %NL80211_ATTR_WDEV identifier. It must have been created with + * %NL80211_CMD_NEW_INTERFACE previously. After it has been started, the + * P2P Device can be used for P2P operations, e.g. remain-on-channel and + * public action frame TX. + * @NL80211_CMD_STOP_P2P_DEVICE: Stop the given P2P Device, identified by + * its %NL80211_ATTR_WDEV identifier. + * + * @NL80211_CMD_CONN_FAILED: connection request to an AP failed; used to + * notify userspace that AP has rejected the connection request from a + * station, due to particular reason. %NL80211_ATTR_CONN_FAILED_REASON + * is used for this. + * + * @NL80211_CMD_SET_MCAST_RATE: Change the rate used to send multicast frames + * for IBSS or MESH vif. * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ enum nl80211_commands { -/* don't change the order or add anything inbetween, this is ABI! */ +/* don't change the order or add anything between, this is ABI! */ NL80211_CMD_UNSPEC, NL80211_CMD_GET_WIPHY, /* can dump */ @@ -354,8 +611,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, @@ -372,8 +631,8 @@ enum nl80211_commands { NL80211_CMD_SET_REG, NL80211_CMD_REQ_SET_REG, - NL80211_CMD_GET_MESH_PARAMS, - NL80211_CMD_SET_MESH_PARAMS, + NL80211_CMD_GET_MESH_CONFIG, + NL80211_CMD_SET_MESH_CONFIG, NL80211_CMD_SET_MGMT_EXTRA_IE /* reserved; not used */, @@ -418,9 +677,12 @@ enum nl80211_commands { NL80211_CMD_SET_TX_BITRATE_MASK, - NL80211_CMD_REGISTER_ACTION, - NL80211_CMD_ACTION, - NL80211_CMD_ACTION_TX_STATUS, + NL80211_CMD_REGISTER_FRAME, + NL80211_CMD_REGISTER_ACTION = NL80211_CMD_REGISTER_FRAME, + NL80211_CMD_FRAME, + NL80211_CMD_ACTION = NL80211_CMD_FRAME, + NL80211_CMD_FRAME_TX_STATUS, + NL80211_CMD_ACTION_TX_STATUS = NL80211_CMD_FRAME_TX_STATUS, NL80211_CMD_SET_POWER_SAVE, NL80211_CMD_GET_POWER_SAVE, @@ -428,6 +690,53 @@ enum nl80211_commands { NL80211_CMD_SET_CQM, NL80211_CMD_NOTIFY_CQM, + NL80211_CMD_SET_CHANNEL, + NL80211_CMD_SET_WDS_PEER, + + NL80211_CMD_FRAME_WAIT_CANCEL, + + NL80211_CMD_JOIN_MESH, + NL80211_CMD_LEAVE_MESH, + + NL80211_CMD_UNPROT_DEAUTHENTICATE, + NL80211_CMD_UNPROT_DISASSOCIATE, + + NL80211_CMD_NEW_PEER_CANDIDATE, + + NL80211_CMD_GET_WOWLAN, + NL80211_CMD_SET_WOWLAN, + + NL80211_CMD_START_SCHED_SCAN, + NL80211_CMD_STOP_SCHED_SCAN, + NL80211_CMD_SCHED_SCAN_RESULTS, + NL80211_CMD_SCHED_SCAN_STOPPED, + + NL80211_CMD_SET_REKEY_OFFLOAD, + + NL80211_CMD_PMKSA_CANDIDATE, + + NL80211_CMD_TDLS_OPER, + NL80211_CMD_TDLS_MGMT, + + NL80211_CMD_UNEXPECTED_FRAME, + + NL80211_CMD_PROBE_CLIENT, + + NL80211_CMD_REGISTER_BEACONS, + + NL80211_CMD_UNEXPECTED_4ADDR_FRAME, + + NL80211_CMD_SET_NOACK_MAP, + + NL80211_CMD_CH_SWITCH_NOTIFY, + + NL80211_CMD_START_P2P_DEVICE, + NL80211_CMD_STOP_P2P_DEVICE, + + NL80211_CMD_CONN_FAILED, + + NL80211_CMD_SET_MCAST_RATE, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -448,6 +757,13 @@ enum nl80211_commands { #define NL80211_CMD_DISASSOCIATE NL80211_CMD_DISASSOCIATE #define NL80211_CMD_REG_BEACON_HINT NL80211_CMD_REG_BEACON_HINT +#define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS + +/* source-level API compatibility */ +#define NL80211_CMD_GET_MESH_PARAMS NL80211_CMD_GET_MESH_CONFIG +#define NL80211_CMD_SET_MESH_PARAMS NL80211_CMD_SET_MESH_CONFIG +#define NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE NL80211_MESH_SETUP_IE + /** * enum nl80211_attrs - nl80211 netlink attributes * @@ -484,6 +800,9 @@ enum nl80211_commands { * @NL80211_ATTR_IFNAME: network interface name * @NL80211_ATTR_IFTYPE: type of virtual interface, see &enum nl80211_iftype * + * @NL80211_ATTR_WDEV: wireless device identifier, used for pseudo-devices + * that don't have a netdev (u64) + * * @NL80211_ATTR_MAC: MAC address (various uses) * * @NL80211_ATTR_KEY_DATA: (temporal) key data; for TKIP this consists of @@ -494,6 +813,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 @@ -518,7 +844,7 @@ enum nl80211_commands { * consisting of a nested array. * * @NL80211_ATTR_MESH_ID: mesh id (1-32 bytes). - * @NL80211_ATTR_PLINK_ACTION: action to perform on the mesh peer link. + * @NL80211_ATTR_STA_PLINK_ACTION: action to perform on the mesh peer link. * @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path. * @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path * info given for %NL80211_CMD_GET_MPATH, nested attribute described at @@ -563,8 +889,14 @@ enum nl80211_commands { * * @NL80211_ATTR_MAX_NUM_SCAN_SSIDS: number of SSIDs you can scan with * a single scan request, a wiphy attribute. + * @NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS: number of SSIDs you can + * scan with a single scheduled scan request, a wiphy attribute. * @NL80211_ATTR_MAX_SCAN_IE_LEN: maximum length of information elements * that can be added to a scan request + * @NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN: maximum length of information + * elements that can be added to a scheduled scan request + * @NL80211_ATTR_MAX_MATCH_SETS: maximum number of sets that can be + * used with @NL80211_ATTR_SCHED_SCAN_MATCH, a wiphy attribute. * * @NL80211_ATTR_SCAN_FREQUENCIES: nested attribute with frequencies (in MHz) * @NL80211_ATTR_SCAN_SSIDS: nested attribute with SSIDs, leave out for passive @@ -626,6 +958,15 @@ enum nl80211_commands { * request, the driver will assume that the port is unauthorized until * authorized by user space. Otherwise, port is marked authorized by * default in station mode. + * @NL80211_ATTR_CONTROL_PORT_ETHERTYPE: A 16-bit value indicating the + * ethertype that will be used for key negotiation. It can be + * specified with the associate and connect commands. If it is not + * specified, the value defaults to 0x888E (PAE, 802.1X). This + * attribute is also used as a flag in the wiphy information to + * indicate that protocols other than PAE are supported. + * @NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT: When included along with + * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE, indicates that the custom + * ethertype frames used for key negotiation must not be encrypted. * * @NL80211_ATTR_TESTDATA: Testmode data blob, passed through to the driver. * We recommend using nested, driver-specific attributes within this. @@ -636,18 +977,20 @@ enum nl80211_commands { * @NL80211_ATTR_STATUS_CODE: StatusCode for the %NL80211_CMD_CONNECT * event (u16) * @NL80211_ATTR_PRIVACY: Flag attribute, used with connect(), indicating - * that protected APs should be used. + * that protected APs should be used. This is also used with NEW_BEACON to + * indicate that the BSS is to use protection. * - * @NL80211_ATTR_CIPHERS_PAIRWISE: Used with CONNECT and ASSOCIATE to - * indicate which unicast key ciphers will be used with the connection + * @NL80211_ATTR_CIPHERS_PAIRWISE: Used with CONNECT, ASSOCIATE, and NEW_BEACON + * to indicate which unicast key ciphers will be used with the connection * (an array of u32). - * @NL80211_ATTR_CIPHER_GROUP: Used with CONNECT and ASSOCIATE to indicate - * which group key cipher will be used with the connection (a u32). - * @NL80211_ATTR_WPA_VERSIONS: Used with CONNECT and ASSOCIATE to indicate - * which WPA version(s) the AP we want to associate with is using + * @NL80211_ATTR_CIPHER_GROUP: Used with CONNECT, ASSOCIATE, and NEW_BEACON to + * indicate which group key cipher will be used with the connection (a + * u32). + * @NL80211_ATTR_WPA_VERSIONS: Used with CONNECT, ASSOCIATE, and NEW_BEACON to + * indicate which WPA version(s) the AP we want to associate with is using * (a u32 with flags from &enum nl80211_wpa_versions). - * @NL80211_ATTR_AKM_SUITES: Used with CONNECT and ASSOCIATE to indicate - * which key management algorithm(s) to use (an array of u32). + * @NL80211_ATTR_AKM_SUITES: Used with CONNECT, ASSOCIATE, and NEW_BEACON to + * indicate which key management algorithm(s) to use (an array of u32). * * @NL80211_ATTR_REQ_IE: (Re)association request information elements as * sent out by the card, for ROAM and successful CONNECT events. @@ -684,6 +1027,9 @@ enum nl80211_commands { * cache, a wiphy attribute. * * @NL80211_ATTR_DURATION: Duration of an operation in milliseconds, u32. + * @NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION: Device attribute that + * specifies the maximum duration that can be requested with the + * remain-on-channel operation, in milliseconds, u32. * * @NL80211_ATTR_COOKIE: Generic 64-bit cookie to identify objects. * @@ -695,11 +1041,22 @@ enum nl80211_commands { * is used with %NL80211_CMD_SET_TX_BITRATE_MASK. * * @NL80211_ATTR_FRAME_MATCH: A binary attribute which typically must contain - * at least one byte, currently used with @NL80211_CMD_REGISTER_ACTION. + * at least one byte, currently used with @NL80211_CMD_REGISTER_FRAME. + * @NL80211_ATTR_FRAME_TYPE: A u16 indicating the frame type/subtype for the + * @NL80211_CMD_REGISTER_FRAME command. + * @NL80211_ATTR_TX_FRAME_TYPES: wiphy capability attribute, which is a + * nested attribute of %NL80211_ATTR_FRAME_TYPE attributes, containing + * information about which frame types can be transmitted with + * %NL80211_CMD_FRAME. + * @NL80211_ATTR_RX_FRAME_TYPES: wiphy capability attribute, which is a + * nested attribute of %NL80211_ATTR_FRAME_TYPE attributes, containing + * information about which frame types can be registered for RX. * * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was * acknowledged by the recipient. * + * @NL80211_ATTR_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. * @@ -709,11 +1066,237 @@ enum nl80211_commands { * NL80211_CMD_AUTHENTICATE, NL80211_CMD_DEAUTHENTICATE, * NL80211_CMD_DISASSOCIATE. * + * @NL80211_ATTR_AP_ISOLATE: (AP mode) Do not forward traffic between stations + * connected to this BSS. + * + * @NL80211_ATTR_WIPHY_TX_POWER_SETTING: Transmit power setting type. See + * &enum nl80211_tx_power_setting for possible values. + * @NL80211_ATTR_WIPHY_TX_POWER_LEVEL: Transmit power level in signed mBm units. + * This is used in association with @NL80211_ATTR_WIPHY_TX_POWER_SETTING + * for non-automatic settings. + * + * @NL80211_ATTR_SUPPORT_IBSS_RSN: The device supports IBSS RSN, which mostly + * means support for per-station GTKs. + * + * @NL80211_ATTR_WIPHY_ANTENNA_TX: Bitmap of allowed antennas for transmitting. + * This can be used to mask out antennas which are not attached or should + * not be used for transmitting. If an antenna is not selected in this + * bitmap the hardware is not allowed to transmit on this antenna. + * + * Each bit represents one antenna, starting with antenna 1 at the first + * bit. Depending on which antennas are selected in the bitmap, 802.11n + * drivers can derive which chainmasks to use (if all antennas belonging to + * a particular chain are disabled this chain should be disabled) and if + * a chain has diversity antennas wether diversity should be used or not. + * HT capabilities (STBC, TX Beamforming, Antenna selection) can be + * derived from the available chains after applying the antenna mask. + * Non-802.11n drivers can derive wether to use diversity or not. + * Drivers may reject configurations or RX/TX mask combinations they cannot + * support by returning -EINVAL. + * + * @NL80211_ATTR_WIPHY_ANTENNA_RX: Bitmap of allowed antennas for receiving. + * This can be used to mask out antennas which are not attached or should + * not be used for receiving. If an antenna is not selected in this bitmap + * the hardware should not be configured to receive on this antenna. + * For a more detailed description see @NL80211_ATTR_WIPHY_ANTENNA_TX. + * + * @NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX: Bitmap of antennas which are available + * for configuration as TX antennas via the above parameters. + * + * @NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX: Bitmap of antennas which are available + * for configuration as RX antennas via the above parameters. + * + * @NL80211_ATTR_MCAST_RATE: Multicast tx rate (in 100 kbps) for IBSS + * + * @NL80211_ATTR_OFFCHANNEL_TX_OK: For management frame TX, the frame may be + * transmitted on another channel when the channel given doesn't match + * the current channel. If the current channel doesn't match and this + * flag isn't set, the frame will be rejected. This is also used as an + * nl80211 capability flag. + * + * @NL80211_ATTR_BSS_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. + * See &enum nl80211_key_default_types. + * + * @NL80211_ATTR_MESH_SETUP: Optional mesh setup parameters. These cannot be + * changed once the mesh is active. + * @NL80211_ATTR_MESH_CONFIG: Mesh configuration parameters, a nested attribute + * containing attributes from &enum nl80211_meshconf_params. + * @NL80211_ATTR_SUPPORT_MESH_AUTH: Currently, this means the underlying driver + * allows auth frames in a mesh to be passed to userspace for processing via + * the @NL80211_MESH_SETUP_USERSPACE_AUTH flag. + * @NL80211_ATTR_STA_PLINK_STATE: The state of a mesh peer link as + * defined in &enum nl80211_plink_state. Used when userspace is + * driving the peer link management state machine. + * @NL80211_MESH_SETUP_USERSPACE_AMPE must be enabled. + * + * @NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED: indicates, as part of the wiphy + * capabilities, the supported WoWLAN triggers + * @NL80211_ATTR_WOWLAN_TRIGGERS: used by %NL80211_CMD_SET_WOWLAN to + * 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 + * pass-thru filter rules. + * For a match to succeed, the BSS must match all attributes of a + * set. Since not every hardware supports matching all types of + * attributes, there is no guarantee that the reported BSSs are + * fully complying with the match sets and userspace needs to be + * able to ignore them by itself. + * Thus, the implementation is somewhat hardware-dependent, but + * this is only an optimization and the userspace application + * needs to handle all the non-filtered results anyway. + * If the match attributes don't make sense when combined with + * the values passed in @NL80211_ATTR_SCAN_SSIDS (eg. if an SSID + * is included in the probe request, but the match attributes + * will never let it go through), -EINVAL may be returned. + * If ommited, no filtering is done. + * + * @NL80211_ATTR_INTERFACE_COMBINATIONS: Nested attribute listing the supported + * interface combinations. In each nested item, it contains attributes + * defined in &enum nl80211_if_combination_attrs. + * @NL80211_ATTR_SOFTWARE_IFTYPES: Nested attribute (just like + * %NL80211_ATTR_SUPPORTED_IFTYPES) containing the interface types that + * 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 + * 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, + * nested array attribute containing an entry for each band, with the entry + * being a list of supported rates as defined by IEEE 802.11 7.3.2.2 but + * without the length restriction (at most %NL80211_MAX_SUPP_RATES). + * + * @NL80211_ATTR_HIDDEN_SSID: indicates whether SSID is to be hidden from Beacon + * and Probe Response (when response to wildcard Probe Request); see + * &enum nl80211_hidden_ssid, represented as a u32 + * + * @NL80211_ATTR_IE_PROBE_RESP: Information element(s) for Probe Response frame. + * This is used with %NL80211_CMD_NEW_BEACON and %NL80211_CMD_SET_BEACON to + * provide extra IEs (e.g., WPS/P2P IE) into Probe Response frames when the + * driver (or firmware) replies to Probe Request frames. + * @NL80211_ATTR_IE_ASSOC_RESP: Information element(s) for (Re)Association + * Response frames. This is used with %NL80211_CMD_NEW_BEACON and + * %NL80211_CMD_SET_BEACON to provide extra IEs (e.g., WPS/P2P IE) into + * (Re)Association Response frames when the driver (or firmware) replies to + * (Re)Association Request frames. + * + * @NL80211_ATTR_STA_WME: Nested attribute containing the wme configuration + * of the station, see &enum nl80211_sta_wme_attr. + * @NL80211_ATTR_SUPPORT_AP_UAPSD: the device supports uapsd when working + * as AP. + * + * @NL80211_ATTR_ROAM_SUPPORT: Indicates whether the firmware is capable of + * roaming to another AP in the same ESS if the signal lever is low. + * + * @NL80211_ATTR_PMKSA_CANDIDATE: Nested attribute containing the PMKSA caching + * candidate information, see &enum nl80211_pmksa_candidate_attr. + * + * @NL80211_ATTR_TX_NO_CCK_RATE: Indicates whether to use CCK rate or not + * for management frames transmission. In order to avoid p2p probe/action + * frames are being transmitted at CCK rate in 2GHz band, the user space + * applications use this attribute. + * This attribute is used with %NL80211_CMD_TRIGGER_SCAN and + * %NL80211_CMD_FRAME commands. + * + * @NL80211_ATTR_TDLS_ACTION: Low level TDLS action code (e.g. link setup + * request, link setup confirm, link teardown, etc.). Values are + * described in the TDLS (802.11z) specification. + * @NL80211_ATTR_TDLS_DIALOG_TOKEN: Non-zero token for uniquely identifying a + * TDLS conversation between two devices. + * @NL80211_ATTR_TDLS_OPERATION: High level TDLS operation; see + * &enum nl80211_tdls_operation, represented as a u8. + * @NL80211_ATTR_TDLS_SUPPORT: A flag indicating the device can operate + * as a TDLS peer sta. + * @NL80211_ATTR_TDLS_EXTERNAL_SETUP: The TDLS discovery/setup and teardown + * procedures should be performed by sending TDLS packets via + * %NL80211_CMD_TDLS_MGMT. Otherwise %NL80211_CMD_TDLS_OPER should be + * used for asking the driver to perform a TDLS operation. + * + * @NL80211_ATTR_DEVICE_AP_SME: This u32 attribute may be listed for devices + * that have AP support to indicate that they have the AP SME integrated + * with support for the features listed in this attribute, see + * &enum nl80211_ap_sme_features. + * + * @NL80211_ATTR_DONT_WAIT_FOR_ACK: Used with %NL80211_CMD_FRAME, this tells + * the driver to not wait for an acknowledgement. Note that due to this, + * it will also not give a status callback nor return a cookie. This is + * mostly useful for probe responses to save airtime. + * + * @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). + * + * @NL80211_ATTR_PROBE_RESP: Probe Response template data. Contains the entire + * probe-response frame. The DA field in the 802.11 header is zero-ed out, + * to be filled by the FW. + * @NL80211_ATTR_DISABLE_HT: Force HT capable interfaces to disable + * this feature. Currently, only supported in mac80211 drivers. + * @NL80211_ATTR_HT_CAPABILITY_MASK: Specify which bits of the + * ATTR_HT_CAPABILITY to which attention should be paid. + * Currently, only mac80211 NICs support this feature. + * The values that may be configured are: + * MCS rates, MAX-AMSDU, HT-20-40 and HT_CAP_SGI_40 + * AMPDU density and AMPDU factor. + * All values are treated as suggestions and may be ignored + * by the driver as required. The actual values may be seen in + * the station debugfs ht_caps file. + * + * @NL80211_ATTR_DFS_REGION: region for regulatory rules which this country + * abides to when initiating radiation on DFS channels. A country maps + * to one DFS region. + * + * @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_USER_REG_HINT_TYPE: type of regulatory hint passed from + * userspace. If unset it is assumed the hint comes directly from + * a user. If set code could specify exactly what type of source + * was used to provide the hint. For the different types of + * allowed user regulatory hints see nl80211_user_reg_hint_type. + * + * @NL80211_ATTR_CONN_FAILED_REASON: The reason for which AP has rejected + * the connection request from a station. nl80211_connect_failed_reason + * enum has different reasons of connection failure. + * + * @NL80211_ATTR_SAE_DATA: SAE elements in Authentication frames. This starts + * with the Authentication transaction sequence number field. + * + * @NL80211_ATTR_VHT_CAPABILITY: VHT Capability information element (from + * association request when used with NL80211_CMD_NEW_STATION) + * + * @NL80211_ATTR_SCAN_FLAGS: scan request control flags (u32) + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ enum nl80211_attrs { -/* don't change the order or add anything inbetween, this is ABI! */ +/* don't change the order or add anything between, this is ABI! */ NL80211_ATTR_UNSPEC, NL80211_ATTR_WIPHY, @@ -763,7 +1346,7 @@ enum nl80211_attrs { NL80211_ATTR_REG_ALPHA2, NL80211_ATTR_REG_RULES, - NL80211_ATTR_MESH_PARAMS, + NL80211_ATTR_MESH_CONFIG, NL80211_ATTR_BSS_BASIC_RATES, @@ -864,6 +1447,114 @@ enum nl80211_attrs { NL80211_ATTR_LOCAL_STATE_CHANGE, + NL80211_ATTR_AP_ISOLATE, + + NL80211_ATTR_WIPHY_TX_POWER_SETTING, + NL80211_ATTR_WIPHY_TX_POWER_LEVEL, + + NL80211_ATTR_TX_FRAME_TYPES, + NL80211_ATTR_RX_FRAME_TYPES, + NL80211_ATTR_FRAME_TYPE, + + NL80211_ATTR_CONTROL_PORT_ETHERTYPE, + NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, + + NL80211_ATTR_SUPPORT_IBSS_RSN, + + NL80211_ATTR_WIPHY_ANTENNA_TX, + NL80211_ATTR_WIPHY_ANTENNA_RX, + + NL80211_ATTR_MCAST_RATE, + + NL80211_ATTR_OFFCHANNEL_TX_OK, + + NL80211_ATTR_BSS_HT_OPMODE, + + NL80211_ATTR_KEY_DEFAULT_TYPES, + + NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION, + + NL80211_ATTR_MESH_SETUP, + + NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX, + NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX, + + NL80211_ATTR_SUPPORT_MESH_AUTH, + NL80211_ATTR_STA_PLINK_STATE, + + NL80211_ATTR_WOWLAN_TRIGGERS, + NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, + + NL80211_ATTR_SCHED_SCAN_INTERVAL, + + NL80211_ATTR_INTERFACE_COMBINATIONS, + NL80211_ATTR_SOFTWARE_IFTYPES, + + NL80211_ATTR_REKEY_DATA, + + NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS, + NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN, + + NL80211_ATTR_SCAN_SUPP_RATES, + + NL80211_ATTR_HIDDEN_SSID, + + NL80211_ATTR_IE_PROBE_RESP, + NL80211_ATTR_IE_ASSOC_RESP, + + NL80211_ATTR_STA_WME, + NL80211_ATTR_SUPPORT_AP_UAPSD, + + NL80211_ATTR_ROAM_SUPPORT, + + NL80211_ATTR_SCHED_SCAN_MATCH, + NL80211_ATTR_MAX_MATCH_SETS, + + NL80211_ATTR_PMKSA_CANDIDATE, + + NL80211_ATTR_TX_NO_CCK_RATE, + + NL80211_ATTR_TDLS_ACTION, + NL80211_ATTR_TDLS_DIALOG_TOKEN, + NL80211_ATTR_TDLS_OPERATION, + NL80211_ATTR_TDLS_SUPPORT, + NL80211_ATTR_TDLS_EXTERNAL_SETUP, + + NL80211_ATTR_DEVICE_AP_SME, + + NL80211_ATTR_DONT_WAIT_FOR_ACK, + + NL80211_ATTR_FEATURE_FLAGS, + + NL80211_ATTR_PROBE_RESP_OFFLOAD, + + NL80211_ATTR_PROBE_RESP, + + NL80211_ATTR_DFS_REGION, + + NL80211_ATTR_DISABLE_HT, + NL80211_ATTR_HT_CAPABILITY_MASK, + + NL80211_ATTR_NOACK_MAP, + + NL80211_ATTR_INACTIVITY_TIMEOUT, + + NL80211_ATTR_RX_SIGNAL_DBM, + + NL80211_ATTR_BG_SCAN_PERIOD, + + NL80211_ATTR_WDEV, + + NL80211_ATTR_USER_REG_HINT_TYPE, + + NL80211_ATTR_CONN_FAILED_REASON, + + NL80211_ATTR_SAE_DATA, + + NL80211_ATTR_VHT_CAPABILITY, + + NL80211_ATTR_SCAN_FLAGS, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -872,6 +1563,7 @@ enum nl80211_attrs { /* source-level API compatibility */ #define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION +#define NL80211_ATTR_MESH_PARAMS NL80211_ATTR_MESH_CONFIG /* * Allow user space programs to use #ifdef on new attributes by defining them @@ -897,17 +1589,27 @@ enum nl80211_attrs { #define NL80211_ATTR_AKM_SUITES NL80211_ATTR_AKM_SUITES #define NL80211_ATTR_KEY NL80211_ATTR_KEY #define NL80211_ATTR_KEYS NL80211_ATTR_KEYS +#define NL80211_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 #define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY 24 #define NL80211_HT_CAPABILITY_LEN 26 +#define NL80211_VHT_CAPABILITY_LEN 12 #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 + +#define NL80211_CQM_TXE_MAX_INTVL 1800 + /** * enum nl80211_iftype - (virtual) interface types * @@ -915,12 +1617,20 @@ enum nl80211_attrs { * @NL80211_IFTYPE_ADHOC: independent BSS member * @NL80211_IFTYPE_STATION: managed BSS member * @NL80211_IFTYPE_AP: access point - * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points + * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points; VLAN interfaces + * are a bit special in that they must always be tied to a pre-existing + * AP type interface. * @NL80211_IFTYPE_WDS: wireless distribution interface * @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames * @NL80211_IFTYPE_MESH_POINT: mesh point + * @NL80211_IFTYPE_P2P_CLIENT: P2P client + * @NL80211_IFTYPE_P2P_GO: P2P group owner + * @NL80211_IFTYPE_P2P_DEVICE: P2P device interface type, this is not a netdev + * and therefore can't be created in the normal ways, use the + * %NL80211_CMD_START_P2P_DEVICE and %NL80211_CMD_STOP_P2P_DEVICE + * commands to create and destroy one * @NL80211_IFTYPE_MAX: highest interface type number currently defined - * @__NL80211_IFTYPE_AFTER_LAST: internal use + * @NUM_NL80211_IFTYPES: number of defined interface types * * These values are used with the %NL80211_ATTR_IFTYPE * to set the type of an interface. @@ -935,10 +1645,13 @@ enum nl80211_iftype { NL80211_IFTYPE_WDS, NL80211_IFTYPE_MONITOR, NL80211_IFTYPE_MESH_POINT, + NL80211_IFTYPE_P2P_CLIENT, + NL80211_IFTYPE_P2P_GO, + NL80211_IFTYPE_P2P_DEVICE, /* keep last */ - __NL80211_IFTYPE_AFTER_LAST, - NL80211_IFTYPE_MAX = __NL80211_IFTYPE_AFTER_LAST - 1 + NUM_NL80211_IFTYPES, + NL80211_IFTYPE_MAX = NUM_NL80211_IFTYPES - 1 }; /** @@ -947,11 +1660,20 @@ enum nl80211_iftype { * Station flags. When a station is added to an AP interface, it is * assumed to be already associated (and hence authenticated.) * + * @__NL80211_STA_FLAG_INVALID: attribute number 0 is reserved * @NL80211_STA_FLAG_AUTHORIZED: station is authorized (802.1X) * @NL80211_STA_FLAG_SHORT_PREAMBLE: station is capable of receiving frames * with short barker preamble * @NL80211_STA_FLAG_WME: station is WME/QoS capable * @NL80211_STA_FLAG_MFP: station uses management frame protection + * @NL80211_STA_FLAG_AUTHENTICATED: station is authenticated + * @NL80211_STA_FLAG_TDLS_PEER: station is a TDLS peer -- this flag should + * only be used in managed mode (even in the flags mask). Note that the + * flag can't be changed, it is only valid while adding a station, and + * attempts to change it will silently be ignored (rather than rejected + * as errors.) + * @NL80211_STA_FLAG_MAX: highest station flag number currently defined + * @__NL80211_STA_FLAG_AFTER_LAST: internal use */ enum nl80211_sta_flags { __NL80211_STA_FLAG_INVALID, @@ -959,12 +1681,16 @@ enum nl80211_sta_flags { NL80211_STA_FLAG_SHORT_PREAMBLE, NL80211_STA_FLAG_WME, NL80211_STA_FLAG_MFP, + NL80211_STA_FLAG_AUTHENTICATED, + NL80211_STA_FLAG_TDLS_PEER, /* keep last */ __NL80211_STA_FLAG_AFTER_LAST, 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 @@ -982,12 +1708,20 @@ struct nl80211_sta_flag_update { * * These attribute types are used with %NL80211_STA_INFO_TXRATE * when getting information about the bitrate of a station. + * There are 2 attributes for bitrate, a legacy one that represents + * a 16-bit value, and new one that represents a 32-bit value. + * If the rate value fits into 16 bit, both attributes are reported + * with the same value. If the rate is too high to fit into 16 bits + * (>6.5535Gbps) only 32-bit attribute is included. + * User space tools encouraged to use the 32-bit attribute and fall + * back to the 16-bit one for compatibility with older kernels. * * @__NL80211_RATE_INFO_INVALID: attribute number 0 is reserved * @NL80211_RATE_INFO_BITRATE: total bitrate (u16, 100kbit/s) * @NL80211_RATE_INFO_MCS: mcs index for 802.11n (u8) * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 Mhz dualchannel bitrate * @NL80211_RATE_INFO_SHORT_GI: 400ns guard interval + * @NL80211_RATE_INFO_BITRATE32: total bitrate (u32, 100kbit/s) * @NL80211_RATE_INFO_MAX: highest rate_info number currently defined * @__NL80211_RATE_INFO_AFTER_LAST: internal use */ @@ -997,6 +1731,7 @@ enum nl80211_rate_info { NL80211_RATE_INFO_MCS, NL80211_RATE_INFO_40_MHZ_WIDTH, NL80211_RATE_INFO_SHORT_GI, + NL80211_RATE_INFO_BITRATE32, /* keep last */ __NL80211_RATE_INFO_AFTER_LAST, @@ -1004,6 +1739,36 @@ enum nl80211_rate_info { }; /** + * enum nl80211_sta_bss_param - BSS information collected by STA + * + * These attribute types are used with %NL80211_STA_INFO_BSS_PARAM + * when getting information about the bitrate of a station. + * + * @__NL80211_STA_BSS_PARAM_INVALID: attribute number 0 is reserved + * @NL80211_STA_BSS_PARAM_CTS_PROT: whether CTS protection is enabled (flag) + * @NL80211_STA_BSS_PARAM_SHORT_PREAMBLE: whether short preamble is enabled + * (flag) + * @NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME: whether short slot time is enabled + * (flag) + * @NL80211_STA_BSS_PARAM_DTIM_PERIOD: DTIM period for beaconing (u8) + * @NL80211_STA_BSS_PARAM_BEACON_INTERVAL: Beacon interval (u16) + * @NL80211_STA_BSS_PARAM_MAX: highest sta_bss_param number currently defined + * @__NL80211_STA_BSS_PARAM_AFTER_LAST: internal use + */ +enum nl80211_sta_bss_param { + __NL80211_STA_BSS_PARAM_INVALID, + NL80211_STA_BSS_PARAM_CTS_PROT, + NL80211_STA_BSS_PARAM_SHORT_PREAMBLE, + NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME, + NL80211_STA_BSS_PARAM_DTIM_PERIOD, + NL80211_STA_BSS_PARAM_BEACON_INTERVAL, + + /* keep last */ + __NL80211_STA_BSS_PARAM_AFTER_LAST, + NL80211_STA_BSS_PARAM_MAX = __NL80211_STA_BSS_PARAM_AFTER_LAST - 1 +}; + +/** * enum nl80211_sta_info - station information * * These attribute types are used with %NL80211_ATTR_STA_INFO @@ -1013,14 +1778,29 @@ enum nl80211_rate_info { * @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs) * @NL80211_STA_INFO_RX_BYTES: total received bytes (u32, from this station) * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station) - * @__NL80211_STA_INFO_AFTER_LAST: internal - * @NL80211_STA_INFO_MAX: highest possible station info attribute * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm) * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute - * containing info as possible, see &enum nl80211_sta_info_txrate. + * containing info as possible, see &enum nl80211_rate_info * @NL80211_STA_INFO_RX_PACKETS: total received packet (u32, from this station) * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (u32, to this * station) + * @NL80211_STA_INFO_TX_RETRIES: total retries (u32, to this station) + * @NL80211_STA_INFO_TX_FAILED: total failed packets (u32, to this station) + * @NL80211_STA_INFO_SIGNAL_AVG: signal strength average (u8, dBm) + * @NL80211_STA_INFO_LLID: the station's mesh LLID + * @NL80211_STA_INFO_PLID: the station's mesh PLID + * @NL80211_STA_INFO_PLINK_STATE: peer link state for the station + * (see %enum nl80211_plink_state) + * @NL80211_STA_INFO_RX_BITRATE: last unicast data frame rx rate, nested + * attribute, like NL80211_STA_INFO_TX_BITRATE. + * @NL80211_STA_INFO_BSS_PARAM: current station's view of BSS, nested attribute + * containing info as possible, see &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 */ enum nl80211_sta_info { __NL80211_STA_INFO_INVALID, @@ -1034,6 +1814,15 @@ enum nl80211_sta_info { NL80211_STA_INFO_TX_BITRATE, NL80211_STA_INFO_RX_PACKETS, NL80211_STA_INFO_TX_PACKETS, + NL80211_STA_INFO_TX_RETRIES, + NL80211_STA_INFO_TX_FAILED, + NL80211_STA_INFO_SIGNAL_AVG, + NL80211_STA_INFO_RX_BITRATE, + NL80211_STA_INFO_BSS_PARAM, + 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, @@ -1064,14 +1853,17 @@ enum nl80211_mpath_flags { * information about a mesh path. * * @__NL80211_MPATH_INFO_INVALID: attribute number 0 is reserved - * @NL80211_ATTR_MPATH_FRAME_QLEN: number of queued frames for this destination - * @NL80211_ATTR_MPATH_SN: destination sequence number - * @NL80211_ATTR_MPATH_METRIC: metric (cost) of this mesh path - * @NL80211_ATTR_MPATH_EXPTIME: expiration time for the path, in msec from now - * @NL80211_ATTR_MPATH_FLAGS: mesh path flags, enumerated in + * @NL80211_MPATH_INFO_FRAME_QLEN: number of queued frames for this destination + * @NL80211_MPATH_INFO_SN: destination sequence number + * @NL80211_MPATH_INFO_METRIC: metric (cost) of this mesh path + * @NL80211_MPATH_INFO_EXPTIME: expiration time for the path, in msec from now + * @NL80211_MPATH_INFO_FLAGS: mesh path flags, enumerated in * &enum nl80211_mpath_flags; - * @NL80211_ATTR_MPATH_DISCOVERY_TIMEOUT: total path discovery timeout, in msec - * @NL80211_ATTR_MPATH_DISCOVERY_RETRIES: mesh path discovery retries + * @NL80211_MPATH_INFO_DISCOVERY_TIMEOUT: total path discovery timeout, in msec + * @NL80211_MPATH_INFO_DISCOVERY_RETRIES: mesh path discovery retries + * @NL80211_MPATH_INFO_MAX: highest mesh path information attribute number + * currently defind + * @__NL80211_MPATH_INFO_AFTER_LAST: internal use */ enum nl80211_mpath_info { __NL80211_MPATH_INFO_INVALID, @@ -1100,6 +1892,11 @@ 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 */ enum nl80211_band_attr { __NL80211_BAND_ATTR_INVALID, @@ -1111,6 +1908,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 @@ -1120,6 +1920,7 @@ enum nl80211_band_attr { /** * enum nl80211_frequency_attr - frequency attributes + * @__NL80211_FREQUENCY_ATTR_INVALID: attribute number 0 is reserved * @NL80211_FREQUENCY_ATTR_FREQ: Frequency in MHz * @NL80211_FREQUENCY_ATTR_DISABLED: Channel is disabled in current * regulatory domain. @@ -1131,6 +1932,9 @@ enum nl80211_band_attr { * on this channel in current regulatory domain. * @NL80211_FREQUENCY_ATTR_MAX_TX_POWER: Maximum transmission power in mBm * (100 * dBm). + * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number + * currently defined + * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use */ enum nl80211_frequency_attr { __NL80211_FREQUENCY_ATTR_INVALID, @@ -1150,9 +1954,13 @@ enum nl80211_frequency_attr { /** * enum nl80211_bitrate_attr - bitrate attributes + * @__NL80211_BITRATE_ATTR_INVALID: attribute number 0 is reserved * @NL80211_BITRATE_ATTR_RATE: Bitrate in units of 100 kbps * @NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE: Short preamble supported * in 2.4 GHz band. + * @NL80211_BITRATE_ATTR_MAX: highest bitrate attribute number + * currently defined + * @__NL80211_BITRATE_ATTR_AFTER_LAST: internal use */ enum nl80211_bitrate_attr { __NL80211_BITRATE_ATTR_INVALID, @@ -1174,7 +1982,11 @@ enum nl80211_bitrate_attr { * wireless core it thinks its knows the regulatory domain we should be in. * @NL80211_REGDOM_SET_BY_COUNTRY_IE: the wireless core has received an * 802.11 country information element with regulatory information it - * thinks we should consider. + * thinks we should consider. cfg80211 only processes the country + * code from the IE, and relies on the regulatory domain information + * structure passed by userspace (CRDA) from our wireless-regdb. + * If a channel is enabled but the country code indicates it should + * be disabled we disable the channel and re-enable it upon disassociation. */ enum nl80211_reg_initiator { NL80211_REGDOM_SET_BY_CORE, @@ -1208,6 +2020,7 @@ enum nl80211_reg_type { /** * enum nl80211_reg_rule_attr - regulatory rule attributes + * @__NL80211_REG_RULE_ATTR_INVALID: attribute number 0 is reserved * @NL80211_ATTR_REG_RULE_FLAGS: a set of flags which specify additional * considerations for a given frequency range. These are the * &enum nl80211_reg_rule_flags. @@ -1224,6 +2037,9 @@ enum nl80211_reg_type { * If you don't have one then don't send this. * @NL80211_ATTR_POWER_RULE_MAX_EIRP: the maximum allowed EIRP for * a given frequency range. The value is in mBm (100 * dBm). + * @NL80211_REG_RULE_ATTR_MAX: highest regulatory rule attribute number + * currently defined + * @__NL80211_REG_RULE_ATTR_AFTER_LAST: internal use */ enum nl80211_reg_rule_attr { __NL80211_REG_RULE_ATTR_INVALID, @@ -1242,6 +2058,32 @@ enum nl80211_reg_rule_attr { }; /** + * enum nl80211_sched_scan_match_attr - scheduled scan match attributes + * @__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 + */ +enum nl80211_sched_scan_match_attr { + __NL80211_SCHED_SCAN_MATCH_ATTR_INVALID, + + NL80211_SCHED_SCAN_MATCH_ATTR_SSID, + NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, + + /* keep last */ + __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST, + NL80211_SCHED_SCAN_MATCH_ATTR_MAX = + __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 * * @NL80211_RRF_NO_OFDM: OFDM modulation not allowed @@ -1267,6 +2109,41 @@ 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_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, + NL80211_DFS_FCC = 1, + NL80211_DFS_ETSI = 2, + NL80211_DFS_JP = 3, +}; + +/** + * enum nl80211_user_reg_hint_type - type of user regulatory hint + * + * @NL80211_USER_REG_HINT_USER: a user sent the hint. This is always + * assumed if the attribute is not set. + * @NL80211_USER_REG_HINT_CELL_BASE: the hint comes from a cellular + * base station. Device drivers that have been tested to work + * properly to support this type of hint can enable these hints + * by setting the NL80211_FEATURE_CELL_BASE_REG_HINTS feature + * capability on the struct wiphy. The wireless core will + * ignore all cell base station hints until at least one device + * present has been registered with the wireless core that + * has listed NL80211_FEATURE_CELL_BASE_REG_HINTS as a + * supported feature. + */ +enum nl80211_user_reg_hint_type { + NL80211_USER_REG_HINT_USER = 0, + NL80211_USER_REG_HINT_CELL_BASE = 1, +}; + +/** * enum nl80211_survey_info - survey information * * These attribute types are used with %NL80211_ATTR_SURVEY_INFO @@ -1275,11 +2152,31 @@ enum nl80211_reg_rule_flags { * @__NL80211_SURVEY_INFO_INVALID: attribute number 0 is reserved * @NL80211_SURVEY_INFO_FREQUENCY: center frequency of channel * @NL80211_SURVEY_INFO_NOISE: noise level of channel (u8, dBm) + * @NL80211_SURVEY_INFO_IN_USE: channel is currently being used + * @NL80211_SURVEY_INFO_CHANNEL_TIME: amount of time (in ms) that the radio + * spent on this channel + * @NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY: amount of the time the primary + * channel was sensed busy (either due to activity or energy detect) + * @NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY: amount of time the extension + * channel was sensed busy + * @NL80211_SURVEY_INFO_CHANNEL_TIME_RX: amount of time the radio spent + * receiving data + * @NL80211_SURVEY_INFO_CHANNEL_TIME_TX: amount of time the radio spent + * transmitting data + * @NL80211_SURVEY_INFO_MAX: highest survey info attribute number + * currently defined + * @__NL80211_SURVEY_INFO_AFTER_LAST: internal use */ enum nl80211_survey_info { __NL80211_SURVEY_INFO_INVALID, NL80211_SURVEY_INFO_FREQUENCY, NL80211_SURVEY_INFO_NOISE, + NL80211_SURVEY_INFO_IN_USE, + NL80211_SURVEY_INFO_CHANNEL_TIME, + NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY, + NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY, + NL80211_SURVEY_INFO_CHANNEL_TIME_RX, + NL80211_SURVEY_INFO_CHANNEL_TIME_TX, /* keep last */ __NL80211_SURVEY_INFO_AFTER_LAST, @@ -1319,57 +2216,97 @@ enum nl80211_mntr_flags { /** * enum nl80211_meshconf_params - mesh configuration parameters * - * Mesh configuration parameters + * Mesh configuration parameters. These can be changed while the mesh is + * active. * * @__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 inital confirm timeout, in - * millisecond units, used by the peer link management to close a peer link + * @NL80211_MESHCONF_CONFIRM_TIMEOUT: specifies the initial confirm timeout, in + * millisecond units, used by the peer link management to close a peer link * * @NL80211_MESHCONF_HOLDING_TIMEOUT: specifies the holding timeout, in - * millisecond units + * 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. + * + * @NL80211_MESHCONF_HWMP_RANN_INTERVAL: The interval of time (in TUs) between + * root announcements are transmitted. * - * @NL80211_MESHCONF_ROOTMODE: whether root mode is enabled or not + * @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. + * + * @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. + * + * @NL80211_MESHCONF_FORWARDING: set Mesh STA as forwarding or non-forwarding + * 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 { @@ -1388,6 +2325,17 @@ enum nl80211_meshconf_params { NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, NL80211_MESHCONF_HWMP_ROOTMODE, + NL80211_MESHCONF_ELEMENT_TTL, + NL80211_MESHCONF_HWMP_RANN_INTERVAL, + 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, @@ -1395,9 +2343,65 @@ enum nl80211_meshconf_params { }; /** + * enum nl80211_mesh_setup_params - mesh setup parameters + * + * Mesh setup parameters. These are used to start/join a mesh and cannot be + * changed while the mesh is active. + * + * @__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. + * + * @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. + * + * @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. + * + * @NL80211_MESH_SETUP_USERSPACE_AUTH: Enable this option if an authentication + * 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. + * + * @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 { + __NL80211_MESH_SETUP_INVALID, + NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL, + NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC, + 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, + NL80211_MESH_SETUP_ATTR_MAX = __NL80211_MESH_SETUP_ATTR_AFTER_LAST - 1 +}; + +/** * enum nl80211_txq_attr - TX queue parameter attributes * @__NL80211_TXQ_ATTR_INVALID: Attribute number 0 is reserved - * @NL80211_TXQ_ATTR_QUEUE: TX queue identifier (NL80211_TXQ_Q_*) + * @NL80211_TXQ_ATTR_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 @@ -1410,7 +2414,7 @@ enum nl80211_meshconf_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, @@ -1421,13 +2425,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, @@ -1439,6 +2451,7 @@ enum nl80211_channel_type { * enum nl80211_bss - netlink attributes for a BSS * * @__NL80211_BSS_INVALID: invalid + * @NL80211_BSS_BSSID: BSSID of the BSS (6 octets) * @NL80211_BSS_FREQUENCY: frequency in MHz (u32) * @NL80211_BSS_TSF: TSF of the received probe response/beacon (u64) * @NL80211_BSS_BEACON_INTERVAL: beacon interval of the (I)BSS (u16) @@ -1482,6 +2495,12 @@ enum nl80211_bss { /** * enum nl80211_bss_status - BSS "status" + * @NL80211_BSS_STATUS_AUTHENTICATED: Authenticated with this BSS. + * @NL80211_BSS_STATUS_ASSOCIATED: Associated with this BSS. + * @NL80211_BSS_STATUS_IBSS_JOINED: Joined to this IBSS. + * + * The BSS status is a BSS attribute in scan dumps, which + * indicates the status the interface has wrt. this BSS. */ enum nl80211_bss_status { NL80211_BSS_STATUS_AUTHENTICATED, @@ -1496,6 +2515,7 @@ enum nl80211_bss_status { * @NL80211_AUTHTYPE_SHARED_KEY: Shared Key authentication (WEP only) * @NL80211_AUTHTYPE_FT: Fast BSS Transition (IEEE 802.11r) * @NL80211_AUTHTYPE_NETWORK_EAP: Network EAP (some Cisco APs and mainly LEAP) + * @NL80211_AUTHTYPE_SAE: Simultaneous authentication of equals * @__NL80211_AUTHTYPE_NUM: internal * @NL80211_AUTHTYPE_MAX: maximum valid auth algorithm * @NL80211_AUTHTYPE_AUTOMATIC: determine automatically (if necessary by @@ -1507,6 +2527,7 @@ enum nl80211_auth_type { NL80211_AUTHTYPE_SHARED_KEY, NL80211_AUTHTYPE_FT, NL80211_AUTHTYPE_NETWORK_EAP, + NL80211_AUTHTYPE_SAE, /* keep last */ __NL80211_AUTHTYPE_NUM, @@ -1519,11 +2540,14 @@ enum nl80211_auth_type { * @NL80211_KEYTYPE_GROUP: Group (broadcast/multicast) key * @NL80211_KEYTYPE_PAIRWISE: Pairwise (unicast/individual) key * @NL80211_KEYTYPE_PEERKEY: PeerKey (DLS) + * @NUM_NL80211_KEYTYPES: number of defined key types */ enum nl80211_key_type { NL80211_KEYTYPE_GROUP, NL80211_KEYTYPE_PAIRWISE, NL80211_KEYTYPE_PEERKEY, + + NUM_NL80211_KEYTYPES }; /** @@ -1542,6 +2566,23 @@ enum nl80211_wpa_versions { }; /** + * enum nl80211_key_default_types - key default types + * @__NL80211_KEY_DEFAULT_TYPE_INVALID: invalid + * @NL80211_KEY_DEFAULT_TYPE_UNICAST: key should be used as default + * unicast key + * @NL80211_KEY_DEFAULT_TYPE_MULTICAST: key should be used as default + * multicast key + * @NUM_NL80211_KEY_DEFAULT_TYPES: number of default types + */ +enum nl80211_key_default_types { + __NL80211_KEY_DEFAULT_TYPE_INVALID, + NL80211_KEY_DEFAULT_TYPE_UNICAST, + NL80211_KEY_DEFAULT_TYPE_MULTICAST, + + NUM_NL80211_KEY_DEFAULT_TYPES +}; + +/** * enum nl80211_key_attributes - key attributes * @__NL80211_KEY_INVALID: invalid * @NL80211_KEY_DATA: (temporal) key data; for TKIP this consists of @@ -1554,6 +2595,12 @@ enum nl80211_wpa_versions { * CCMP keys, each six bytes in little endian * @NL80211_KEY_DEFAULT: flag indicating default key * @NL80211_KEY_DEFAULT_MGMT: flag indicating default management key + * @NL80211_KEY_TYPE: the key type from enum nl80211_key_type, if not + * specified the default depends on whether a MAC address was + * given with the command using the key or not (u32) + * @NL80211_KEY_DEFAULT_TYPES: A nested attribute containing flags + * attributes, specifying what a key should be set as default as. + * See &enum nl80211_key_default_types. * @__NL80211_KEY_AFTER_LAST: internal * @NL80211_KEY_MAX: highest key attribute */ @@ -1565,6 +2612,8 @@ enum nl80211_key_attributes { NL80211_KEY_SEQ, NL80211_KEY_DEFAULT, NL80211_KEY_DEFAULT_MGMT, + NL80211_KEY_TYPE, + NL80211_KEY_DEFAULT_TYPES, /* keep last */ __NL80211_KEY_AFTER_LAST, @@ -1578,12 +2627,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, @@ -1592,14 +2644,21 @@ 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_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, @@ -1615,6 +2674,19 @@ enum nl80211_ps_state { * the minimum amount the RSSI level must change after an event before a * new event may be issued (to reduce effects of RSSI oscillation). * @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event + * @NL80211_ATTR_CQM_PKT_LOSS_EVENT: a u32 value indicating that this many + * consecutive packets were not acknowledged by the peer + * @NL80211_ATTR_CQM_TXE_RATE: TX error rate in %. Minimum % of TX failures + * during the given %NL80211_ATTR_CQM_TXE_INTVL before an + * %NL80211_CMD_NOTIFY_CQM with reported %NL80211_ATTR_CQM_TXE_RATE and + * %NL80211_ATTR_CQM_TXE_PKTS is generated. + * @NL80211_ATTR_CQM_TXE_PKTS: number of attempted packets in a given + * %NL80211_ATTR_CQM_TXE_INTVL before %NL80211_ATTR_CQM_TXE_RATE is + * checked. + * @NL80211_ATTR_CQM_TXE_INTVL: interval in seconds. Specifies the periodic + * interval in which %NL80211_ATTR_CQM_TXE_PKTS and + * %NL80211_ATTR_CQM_TXE_RATE must be satisfied before generating an + * %NL80211_CMD_NOTIFY_CQM. Set to 0 to turn off TX error reporting. * @__NL80211_ATTR_CQM_AFTER_LAST: internal * @NL80211_ATTR_CQM_MAX: highest key attribute */ @@ -1623,6 +2695,10 @@ enum nl80211_attr_cqm { NL80211_ATTR_CQM_RSSI_THOLD, NL80211_ATTR_CQM_RSSI_HYST, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, + NL80211_ATTR_CQM_PKT_LOSS_EVENT, + NL80211_ATTR_CQM_TXE_RATE, + NL80211_ATTR_CQM_TXE_PKTS, + NL80211_ATTR_CQM_TXE_INTVL, /* keep last */ __NL80211_ATTR_CQM_AFTER_LAST, @@ -1631,14 +2707,431 @@ enum nl80211_attr_cqm { /** * enum nl80211_cqm_rssi_threshold_event - RSSI threshold event - * @NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW - The RSSI level is lower than the + * @NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW: The RSSI level is lower than the * configured threshold - * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH - The RSSI is higher than the + * @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, +}; + + +/** + * enum nl80211_tx_power_setting - TX power adjustment + * @NL80211_TX_POWER_AUTOMATIC: automatically determine transmit power + * @NL80211_TX_POWER_LIMITED: limit TX power by the mBm parameter + * @NL80211_TX_POWER_FIXED: fix TX power to the mBm parameter + */ +enum nl80211_tx_power_setting { + NL80211_TX_POWER_AUTOMATIC, + NL80211_TX_POWER_LIMITED, + NL80211_TX_POWER_FIXED, +}; + +/** + * enum nl80211_wowlan_packet_pattern_attr - WoWLAN packet pattern attribute + * @__NL80211_WOWLAN_PKTPAT_INVALID: invalid number for nested attribute + * @NL80211_WOWLAN_PKTPAT_PATTERN: the pattern, values where the mask has + * a zero bit are ignored + * @NL80211_WOWLAN_PKTPAT_MASK: pattern mask, must be long enough to have + * a bit for each byte in the pattern. The lowest-order bit corresponds + * to the first byte of the pattern, but the bytes of the pattern are + * in a little-endian-like format, i.e. the 9th byte of the pattern + * corresponds to the lowest-order bit in the second byte of the mask. + * For example: The match 00:xx:00:00:xx:00:00:00:00:xx:xx:xx (where + * xx indicates "don't care") would be represented by a pattern of + * twelve zero bytes, and a mask of "0xed,0x07". + * Note that the pattern matching is done as though frames were not + * 802.11 frames but 802.3 frames, i.e. the frame is fully unpacked + * first (including SNAP header unpacking) and then matched. + * @NUM_NL80211_WOWLAN_PKTPAT: number of attributes + * @MAX_NL80211_WOWLAN_PKTPAT: max attribute number + */ +enum nl80211_wowlan_packet_pattern_attr { + __NL80211_WOWLAN_PKTPAT_INVALID, + NL80211_WOWLAN_PKTPAT_MASK, + NL80211_WOWLAN_PKTPAT_PATTERN, + + NUM_NL80211_WOWLAN_PKTPAT, + MAX_NL80211_WOWLAN_PKTPAT = NUM_NL80211_WOWLAN_PKTPAT - 1, +}; + +/** + * struct nl80211_wowlan_pattern_support - pattern support information + * @max_patterns: maximum number of patterns supported + * @min_pattern_len: minimum length of each pattern + * @max_pattern_len: maximum length of each pattern + * + * This struct is carried in %NL80211_WOWLAN_TRIG_PKT_PATTERN when + * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED in the + * capability information given by the kernel to userspace. + */ +struct nl80211_wowlan_pattern_support { + __u32 max_patterns; + __u32 min_pattern_len; + __u32 max_pattern_len; +} __attribute__((packed)); + +/** + * enum nl80211_wowlan_triggers - WoWLAN trigger definitions + * @__NL80211_WOWLAN_TRIG_INVALID: invalid number for nested attributes + * @NL80211_WOWLAN_TRIG_ANY: wake up on any activity, do not really put + * the chip into a special state -- works best with chips that have + * support for low-power operation already (flag) + * @NL80211_WOWLAN_TRIG_DISCONNECT: wake up on disconnect, the way disconnect + * is detected is implementation-specific (flag) + * @NL80211_WOWLAN_TRIG_MAGIC_PKT: wake up on magic packet (6x 0xff, followed + * by 16 repetitions of MAC addr, anywhere in payload) (flag) + * @NL80211_WOWLAN_TRIG_PKT_PATTERN: wake up on the specified packet patterns + * which are passed in an array of nested attributes, each nested attribute + * defining a with attributes from &struct nl80211_wowlan_trig_pkt_pattern. + * Each pattern defines a wakeup packet. The matching is done on the MSDU, + * i.e. as though the packet was an 802.3 packet, so the pattern matching + * is done after the packet is converted to the MSDU. + * + * In %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, it is a binary attribute + * carrying a &struct nl80211_wowlan_pattern_support. + * @NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED: Not a real trigger, and cannot be + * used when setting, used only to indicate that GTK rekeying is supported + * by the device (flag) + * @NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE: wake up on GTK rekey failure (if + * done by the device) (flag) + * @NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST: wake up on EAP Identity Request + * packet (flag) + * @NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE: wake up on 4-way handshake (flag) + * @NL80211_WOWLAN_TRIG_RFKILL_RELEASE: wake up when rfkill is released + * (on devices that have rfkill in the device) (flag) + * @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers + * @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number + */ +enum nl80211_wowlan_triggers { + __NL80211_WOWLAN_TRIG_INVALID, + NL80211_WOWLAN_TRIG_ANY, + NL80211_WOWLAN_TRIG_DISCONNECT, + NL80211_WOWLAN_TRIG_MAGIC_PKT, + NL80211_WOWLAN_TRIG_PKT_PATTERN, + NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED, + NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE, + NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST, + NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE, + NL80211_WOWLAN_TRIG_RFKILL_RELEASE, + + /* keep last */ + NUM_NL80211_WOWLAN_TRIG, + MAX_NL80211_WOWLAN_TRIG = NUM_NL80211_WOWLAN_TRIG - 1 +}; + +/** + * enum nl80211_iface_limit_attrs - limit attributes + * @NL80211_IFACE_LIMIT_UNSPEC: (reserved) + * @NL80211_IFACE_LIMIT_MAX: maximum number of interfaces that + * can be chosen from this set of interface types (u32) + * @NL80211_IFACE_LIMIT_TYPES: nested attribute containing a + * flag attribute for each interface type in this set + * @NUM_NL80211_IFACE_LIMIT: number of attributes + * @MAX_NL80211_IFACE_LIMIT: highest attribute number + */ +enum nl80211_iface_limit_attrs { + NL80211_IFACE_LIMIT_UNSPEC, + NL80211_IFACE_LIMIT_MAX, + NL80211_IFACE_LIMIT_TYPES, + + /* keep last */ + NUM_NL80211_IFACE_LIMIT, + MAX_NL80211_IFACE_LIMIT = NUM_NL80211_IFACE_LIMIT - 1 +}; + +/** + * enum nl80211_if_combination_attrs -- interface combination attributes + * + * @NL80211_IFACE_COMB_UNSPEC: (reserved) + * @NL80211_IFACE_COMB_LIMITS: Nested attributes containing the limits + * for given interface types, see &enum nl80211_iface_limit_attrs. + * @NL80211_IFACE_COMB_MAXNUM: u32 attribute giving the total number of + * interfaces that can be created in this group. This number doesn't + * apply to interfaces purely managed in software, which are listed + * in a separate attribute %NL80211_ATTR_INTERFACES_SOFTWARE. + * @NL80211_IFACE_COMB_STA_AP_BI_MATCH: flag attribute specifying that + * beacon intervals within this group must be all the same even for + * infrastructure and AP/GO combinations, i.e. the GO(s) must adopt + * the infrastructure network's beacon interval. + * @NL80211_IFACE_COMB_NUM_CHANNELS: u32 attribute specifying how many + * different channels may be used within this group. + * @NUM_NL80211_IFACE_COMB: number of attributes + * @MAX_NL80211_IFACE_COMB: highest attribute number + * + * Examples: + * limits = [ #{STA} <= 1, #{AP} <= 1 ], matching BI, channels = 1, max = 2 + * => allows an AP and a STA that must match BIs + * + * numbers = [ #{AP, P2P-GO} <= 8 ], channels = 1, max = 8 + * => allows 8 of AP/GO + * + * numbers = [ #{STA} <= 2 ], channels = 2, max = 2 + * => allows two STAs on different channels + * + * numbers = [ #{STA} <= 1, #{P2P-client,P2P-GO} <= 3 ], max = 4 + * => allows a STA plus three P2P interfaces + * + * The list of these four possiblities could completely be contained + * within the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute to indicate + * that any of these groups must match. + * + * "Combinations" of just a single interface will not be listed here, + * a single interface of any valid interface type is assumed to always + * be possible by itself. This means that implicitly, for each valid + * interface type, the following group always exists: + * numbers = [ #{<type>} <= 1 ], channels = 1, max = 1 + */ +enum nl80211_if_combination_attrs { + NL80211_IFACE_COMB_UNSPEC, + NL80211_IFACE_COMB_LIMITS, + NL80211_IFACE_COMB_MAXNUM, + NL80211_IFACE_COMB_STA_AP_BI_MATCH, + NL80211_IFACE_COMB_NUM_CHANNELS, + + /* keep last */ + NUM_NL80211_IFACE_COMB, + MAX_NL80211_IFACE_COMB = NUM_NL80211_IFACE_COMB - 1 +}; + + +/** + * enum nl80211_plink_state - state of a mesh peer link finite state machine + * + * @NL80211_PLINK_LISTEN: initial state, considered the implicit + * state of non existant mesh peer links + * @NL80211_PLINK_OPN_SNT: mesh plink open frame has been sent to + * this mesh peer + * @NL80211_PLINK_OPN_RCVD: mesh plink open frame has been received + * from this mesh peer + * @NL80211_PLINK_CNF_RCVD: mesh plink confirm frame has been + * received from this mesh peer + * @NL80211_PLINK_ESTAB: mesh peer link is established + * @NL80211_PLINK_HOLDING: mesh peer link is being closed or cancelled + * @NL80211_PLINK_BLOCKED: all frames transmitted from this mesh + * plink are discarded + * @NUM_NL80211_PLINK_STATES: number of peer link states + * @MAX_NL80211_PLINK_STATES: highest numerical value of plink states + */ +enum nl80211_plink_state { + NL80211_PLINK_LISTEN, + NL80211_PLINK_OPN_SNT, + NL80211_PLINK_OPN_RCVD, + NL80211_PLINK_CNF_RCVD, + NL80211_PLINK_ESTAB, + NL80211_PLINK_HOLDING, + NL80211_PLINK_BLOCKED, + + /* keep last */ + NUM_NL80211_PLINK_STATES, + MAX_NL80211_PLINK_STATES = NUM_NL80211_PLINK_STATES - 1 +}; + +#define NL80211_KCK_LEN 16 +#define NL80211_KEK_LEN 16 +#define NL80211_REPLAY_CTR_LEN 8 + +/** + * enum nl80211_rekey_data - attributes for GTK rekey offload + * @__NL80211_REKEY_DATA_INVALID: invalid number for nested attributes + * @NL80211_REKEY_DATA_KEK: key encryption key (binary) + * @NL80211_REKEY_DATA_KCK: key confirmation key (binary) + * @NL80211_REKEY_DATA_REPLAY_CTR: replay counter (binary) + * @NUM_NL80211_REKEY_DATA: number of rekey attributes (internal) + * @MAX_NL80211_REKEY_DATA: highest rekey attribute (internal) + */ +enum nl80211_rekey_data { + __NL80211_REKEY_DATA_INVALID, + NL80211_REKEY_DATA_KEK, + NL80211_REKEY_DATA_KCK, + NL80211_REKEY_DATA_REPLAY_CTR, + + /* keep last */ + NUM_NL80211_REKEY_DATA, + MAX_NL80211_REKEY_DATA = NUM_NL80211_REKEY_DATA - 1 +}; + +/** + * enum nl80211_hidden_ssid - values for %NL80211_ATTR_HIDDEN_SSID + * @NL80211_HIDDEN_SSID_NOT_IN_USE: do not hide SSID (i.e., broadcast it in + * Beacon frames) + * @NL80211_HIDDEN_SSID_ZERO_LEN: hide SSID by using zero-length SSID element + * in Beacon frames + * @NL80211_HIDDEN_SSID_ZERO_CONTENTS: hide SSID by using correct length of SSID + * element in Beacon frames but zero out each byte in the SSID + */ +enum nl80211_hidden_ssid { + NL80211_HIDDEN_SSID_NOT_IN_USE, + NL80211_HIDDEN_SSID_ZERO_LEN, + NL80211_HIDDEN_SSID_ZERO_CONTENTS +}; + +/** + * enum nl80211_sta_wme_attr - station WME attributes + * @__NL80211_STA_WME_INVALID: invalid number for nested attribute + * @NL80211_STA_WME_UAPSD_QUEUES: bitmap of uapsd queues. the format + * is the same as the AC bitmap in the QoS info field. + * @NL80211_STA_WME_MAX_SP: max service period. the format is the same + * as the MAX_SP field in the QoS info field (but already shifted down). + * @__NL80211_STA_WME_AFTER_LAST: internal + * @NL80211_STA_WME_MAX: highest station WME attribute + */ +enum nl80211_sta_wme_attr { + __NL80211_STA_WME_INVALID, + NL80211_STA_WME_UAPSD_QUEUES, + NL80211_STA_WME_MAX_SP, + + /* keep last */ + __NL80211_STA_WME_AFTER_LAST, + NL80211_STA_WME_MAX = __NL80211_STA_WME_AFTER_LAST - 1 +}; + +/** + * enum nl80211_pmksa_candidate_attr - attributes for PMKSA caching candidates + * @__NL80211_PMKSA_CANDIDATE_INVALID: invalid number for nested attributes + * @NL80211_PMKSA_CANDIDATE_INDEX: candidate index (u32; the smaller, the higher + * priority) + * @NL80211_PMKSA_CANDIDATE_BSSID: candidate BSSID (6 octets) + * @NL80211_PMKSA_CANDIDATE_PREAUTH: RSN pre-authentication supported (flag) + * @NUM_NL80211_PMKSA_CANDIDATE: number of PMKSA caching candidate attributes + * (internal) + * @MAX_NL80211_PMKSA_CANDIDATE: highest PMKSA caching candidate attribute + * (internal) + */ +enum nl80211_pmksa_candidate_attr { + __NL80211_PMKSA_CANDIDATE_INVALID, + NL80211_PMKSA_CANDIDATE_INDEX, + NL80211_PMKSA_CANDIDATE_BSSID, + NL80211_PMKSA_CANDIDATE_PREAUTH, + + /* keep last */ + NUM_NL80211_PMKSA_CANDIDATE, + MAX_NL80211_PMKSA_CANDIDATE = NUM_NL80211_PMKSA_CANDIDATE - 1 +}; + +/** + * enum nl80211_tdls_operation - values for %NL80211_ATTR_TDLS_OPERATION + * @NL80211_TDLS_DISCOVERY_REQ: Send a TDLS discovery request + * @NL80211_TDLS_SETUP: Setup TDLS link + * @NL80211_TDLS_TEARDOWN: Teardown a TDLS link which is already established + * @NL80211_TDLS_ENABLE_LINK: Enable TDLS link + * @NL80211_TDLS_DISABLE_LINK: Disable TDLS link + */ +enum nl80211_tdls_operation { + NL80211_TDLS_DISCOVERY_REQ, + NL80211_TDLS_SETUP, + NL80211_TDLS_TEARDOWN, + NL80211_TDLS_ENABLE_LINK, + NL80211_TDLS_DISABLE_LINK, +}; + +/* + * enum nl80211_ap_sme_features - device-integrated AP features + * Reserved for future use, no bits are defined in + * NL80211_ATTR_DEVICE_AP_SME yet. +enum nl80211_ap_sme_features { +}; + */ + +/** + * enum nl80211_feature_flags - device/driver features + * @NL80211_FEATURE_SK_TX_STATUS: This driver supports reflecting back + * 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. + * @NL80211_FEATURE_CELL_BASE_REG_HINTS: This driver has been tested + * to work properly to suppport receiving regulatory hints from + * cellular base stations. + * @NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL: If this is set, an active + * P2P Device (%NL80211_IFTYPE_P2P_DEVICE) requires its own channel + * in the interface combinations, even when it's only used for scan + * and remain-on-channel. This could be due to, for example, the + * remain-on-channel implementation requiring a channel context. + * @NL80211_FEATURE_SAE: This driver supports simultaneous authentication of + * equals (SAE) with user space SME (NL80211_CMD_AUTHENTICATE) in station + * mode + * @NL80211_FEATURE_LOW_PRIORITY_SCAN: This driver supports low priority scan + * @NL80211_FEATURE_SCAN_FLUSH: Scan flush is supported + * @NL80211_FEATURE_AP_SCAN: Support scanning using an AP vif + * @NL80211_FEATURE_VIF_TXPOWER: The driver supports per-vif TX power setting + * @NL80211_FEATURE_NEED_OBSS_SCAN: The driver expects userspace to perform + * OBSS scans and generate 20/40 BSS coex reports. This flag is used only + * for drivers implementing the CONNECT API, for AUTH/ASSOC it is implied. + */ +enum nl80211_feature_flags { + NL80211_FEATURE_SK_TX_STATUS = 1 << 0, + NL80211_FEATURE_HT_IBSS = 1 << 1, + NL80211_FEATURE_INACTIVITY_TIMER = 1 << 2, + NL80211_FEATURE_CELL_BASE_REG_HINTS = 1 << 3, + NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL = 1 << 4, + NL80211_FEATURE_SAE = 1 << 5, + NL80211_FEATURE_LOW_PRIORITY_SCAN = 1 << 6, + NL80211_FEATURE_SCAN_FLUSH = 1 << 7, + NL80211_FEATURE_AP_SCAN = 1 << 8, + NL80211_FEATURE_VIF_TXPOWER = 1 << 9, + NL80211_FEATURE_NEED_OBSS_SCAN = 1 << 10, +}; + +/** + * enum nl80211_probe_resp_offload_support_attr - optional supported + * protocols for probe-response offloading by the driver/FW. + * To be used with the %NL80211_ATTR_PROBE_RESP_OFFLOAD attribute. + * Each enum value represents a bit in the bitmap of supported + * protocols. Typically a subset of probe-requests belonging to a + * supported protocol will be excluded from offload and uploaded + * to the host. + * + * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS: Support for WPS ver. 1 + * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2: Support for WPS ver. 2 + * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P: Support for P2P + * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U: Support for 802.11u + */ +enum nl80211_probe_resp_offload_support_attr { + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS = 1<<0, + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 = 1<<1, + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P = 1<<2, + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U = 1<<3, +}; + +/** + * enum nl80211_connect_failed_reason - connection request failed reasons + * @NL80211_CONN_FAIL_MAX_CLIENTS: Maximum number of clients that can be + * handled by the AP is reached. + * @NL80211_CONN_FAIL_BLOCKED_CLIENT: Client's MAC is in the AP's blocklist. + */ +enum nl80211_connect_failed_reason { + NL80211_CONN_FAIL_MAX_CLIENTS, + NL80211_CONN_FAIL_BLOCKED_CLIENT, +}; + +/** + * enum nl80211_scan_flags - scan request control flags + * + * Scan request control flags are used to control the handling + * of NL80211_CMD_TRIGGER_SCAN and NL80211_CMD_START_SCHED_SCAN + * requests. + * + * @NL80211_SCAN_FLAG_LOW_PRIORITY: scan request has low priority + * @NL80211_SCAN_FLAG_FLUSH: flush cache before scanning + * @NL80211_SCAN_FLAG_AP: force a scan even if the interface is configured + * as AP and the beaconing has already been configured. This attribute is + * dangerous because will destroy stations performance as a lot of frames + * will be lost while scanning off-channel, therefore it must be used only + * when really needed + */ +enum nl80211_scan_flags { + NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0, + NL80211_SCAN_FLAG_FLUSH = 1<<1, + NL80211_SCAN_FLAG_AP = 1<<2, }; #endif /* __LINUX_NL80211_H */ diff --git a/src/drivers/priv_netlink.h b/src/drivers/priv_netlink.h index 23eff83fadd43..74d6ce58e4b1f 100644 --- a/src/drivers/priv_netlink.h +++ b/src/drivers/priv_netlink.h @@ -2,14 +2,8 @@ * wpa_supplicant - Private copy of Linux netlink/rtnetlink definitions. * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef PRIV_NETLINK_H diff --git a/src/drivers/rfkill.c b/src/drivers/rfkill.c new file mode 100644 index 0000000000000..45b26c46b69c7 --- /dev/null +++ b/src/drivers/rfkill.c @@ -0,0 +1,188 @@ +/* + * Linux rfkill helper functions for driver wrappers + * Copyright (c) 2010, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include <fcntl.h> + +#include "utils/common.h" +#include "utils/eloop.h" +#include "rfkill.h" + +#define RFKILL_EVENT_SIZE_V1 8 + +struct rfkill_event { + u32 idx; + u8 type; + u8 op; + u8 soft; + u8 hard; +} STRUCT_PACKED; + +enum rfkill_operation { + RFKILL_OP_ADD = 0, + RFKILL_OP_DEL, + RFKILL_OP_CHANGE, + RFKILL_OP_CHANGE_ALL, +}; + +enum rfkill_type { + RFKILL_TYPE_ALL = 0, + RFKILL_TYPE_WLAN, + RFKILL_TYPE_BLUETOOTH, + RFKILL_TYPE_UWB, + RFKILL_TYPE_WIMAX, + RFKILL_TYPE_WWAN, + RFKILL_TYPE_GPS, + RFKILL_TYPE_FM, + NUM_RFKILL_TYPES, +}; + + +struct rfkill_data { + struct rfkill_config *cfg; + int fd; + int blocked; +}; + + +static void rfkill_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct rfkill_data *rfkill = eloop_ctx; + struct rfkill_event event; + ssize_t len; + int new_blocked; + + len = read(rfkill->fd, &event, sizeof(event)); + if (len < 0) { + wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s", + strerror(errno)); + return; + } + if (len != RFKILL_EVENT_SIZE_V1) { + wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size " + "%d (expected %d)", + (int) len, RFKILL_EVENT_SIZE_V1); + return; + } + wpa_printf(MSG_DEBUG, "rfkill: event: idx=%u type=%d " + "op=%u soft=%u hard=%u", + event.idx, event.type, event.op, event.soft, + event.hard); + if (event.op != RFKILL_OP_CHANGE || event.type != RFKILL_TYPE_WLAN) + return; + + if (event.hard) { + wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked"); + new_blocked = 1; + } else if (event.soft) { + wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked"); + new_blocked = 1; + } else { + wpa_printf(MSG_INFO, "rfkill: WLAN unblocked"); + new_blocked = 0; + } + + if (new_blocked != rfkill->blocked) { + rfkill->blocked = new_blocked; + if (new_blocked) + rfkill->cfg->blocked_cb(rfkill->cfg->ctx); + else + rfkill->cfg->unblocked_cb(rfkill->cfg->ctx); + } +} + + +struct rfkill_data * rfkill_init(struct rfkill_config *cfg) +{ + struct rfkill_data *rfkill; + struct rfkill_event event; + ssize_t len; + + rfkill = os_zalloc(sizeof(*rfkill)); + if (rfkill == NULL) + return NULL; + + rfkill->cfg = cfg; + rfkill->fd = open("/dev/rfkill", O_RDONLY); + if (rfkill->fd < 0) { + wpa_printf(MSG_INFO, "rfkill: Cannot open RFKILL control " + "device"); + goto fail; + } + + if (fcntl(rfkill->fd, F_SETFL, O_NONBLOCK) < 0) { + wpa_printf(MSG_ERROR, "rfkill: Cannot set non-blocking mode: " + "%s", strerror(errno)); + goto fail2; + } + + for (;;) { + len = read(rfkill->fd, &event, sizeof(event)); + if (len < 0) { + if (errno == EAGAIN) + break; /* No more entries */ + wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s", + strerror(errno)); + break; + } + if (len != RFKILL_EVENT_SIZE_V1) { + wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size " + "%d (expected %d)", + (int) len, RFKILL_EVENT_SIZE_V1); + continue; + } + wpa_printf(MSG_DEBUG, "rfkill: initial event: idx=%u type=%d " + "op=%u soft=%u hard=%u", + event.idx, event.type, event.op, event.soft, + event.hard); + if (event.op != RFKILL_OP_ADD || + event.type != RFKILL_TYPE_WLAN) + continue; + if (event.hard) { + wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked"); + rfkill->blocked = 1; + } else if (event.soft) { + wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked"); + rfkill->blocked = 1; + } + } + + eloop_register_read_sock(rfkill->fd, rfkill_receive, rfkill, NULL); + + return rfkill; + +fail2: + close(rfkill->fd); +fail: + os_free(rfkill); + return NULL; +} + + +void rfkill_deinit(struct rfkill_data *rfkill) +{ + if (rfkill == NULL) + return; + + if (rfkill->fd >= 0) { + eloop_unregister_read_sock(rfkill->fd); + close(rfkill->fd); + } + + os_free(rfkill->cfg); + os_free(rfkill); +} + + +int rfkill_is_blocked(struct rfkill_data *rfkill) +{ + if (rfkill == NULL) + return 0; + + return rfkill->blocked; +} diff --git a/src/drivers/rfkill.h b/src/drivers/rfkill.h new file mode 100644 index 0000000000000..0412ac33054c9 --- /dev/null +++ b/src/drivers/rfkill.h @@ -0,0 +1,25 @@ +/* + * Linux rfkill helper functions for driver wrappers + * Copyright (c) 2010, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef RFKILL_H +#define RFKILL_H + +struct rfkill_data; + +struct rfkill_config { + void *ctx; + char ifname[IFNAMSIZ]; + void (*blocked_cb)(void *ctx); + void (*unblocked_cb)(void *ctx); +}; + +struct rfkill_data * rfkill_init(struct rfkill_config *cfg); +void rfkill_deinit(struct rfkill_data *rfkill); +int rfkill_is_blocked(struct rfkill_data *rfkill); + +#endif /* RFKILL_H */ diff --git a/src/drivers/wireless_copy.h b/src/drivers/wireless_copy.h deleted file mode 100644 index ad764663766f8..0000000000000 --- a/src/drivers/wireless_copy.h +++ /dev/null @@ -1,1099 +0,0 @@ -/* This is based on Linux Wireless Extensions header file from WIRELESS_EXT 18. - * I have just removed kernel related headers and added some typedefs etc. to - * make this easier to include into user space programs. - * Jouni Malinen, 2005-03-12. - */ - - -/* - * This file define a set of standard wireless extensions - * - * Version : 19 18.3.05 - * - * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> - * Copyright (c) 1997-2005 Jean Tourrilhes, All Rights Reserved. - */ - -#ifndef _LINUX_WIRELESS_H -#define _LINUX_WIRELESS_H - -/************************** DOCUMENTATION **************************/ -/* - * Initial APIs (1996 -> onward) : - * ----------------------------- - * Basically, the wireless extensions are for now a set of standard ioctl - * call + /proc/net/wireless - * - * The entry /proc/net/wireless give statistics and information on the - * driver. - * This is better than having each driver having its entry because - * its centralised and we may remove the driver module safely. - * - * Ioctl are used to configure the driver and issue commands. This is - * better than command line options of insmod because we may want to - * change dynamically (while the driver is running) some parameters. - * - * The ioctl mechanimsm are copied from standard devices ioctl. - * We have the list of command plus a structure descibing the - * data exchanged... - * Note that to add these ioctl, I was obliged to modify : - * # net/core/dev.c (two place + add include) - * # net/ipv4/af_inet.c (one place + add include) - * - * /proc/net/wireless is a copy of /proc/net/dev. - * We have a structure for data passed from the driver to /proc/net/wireless - * Too add this, I've modified : - * # net/core/dev.c (two other places) - * # include/linux/netdevice.h (one place) - * # include/linux/proc_fs.h (one place) - * - * New driver API (2002 -> onward) : - * ------------------------------- - * This file is only concerned with the user space API and common definitions. - * The new driver API is defined and documented in : - * # include/net/iw_handler.h - * - * Note as well that /proc/net/wireless implementation has now moved in : - * # net/core/wireless.c - * - * Wireless Events (2002 -> onward) : - * -------------------------------- - * Events are defined at the end of this file, and implemented in : - * # net/core/wireless.c - * - * Other comments : - * -------------- - * Do not add here things that are redundant with other mechanisms - * (drivers init, ifconfig, /proc/net/dev, ...) and with are not - * wireless specific. - * - * These wireless extensions are not magic : each driver has to provide - * support for them... - * - * IMPORTANT NOTE : As everything in the kernel, this is very much a - * work in progress. Contact me if you have ideas of improvements... - */ - -/***************************** INCLUDES *****************************/ - - /* jkm - replaced linux headers with C library headers, added typedefs */ -#if 0 -/* To minimise problems in user space, I might remove those headers - * at some point. Jean II */ -#include <linux/types.h> /* for "caddr_t" et al */ -#include <linux/socket.h> /* for "struct sockaddr" et al */ -#include <linux/if.h> /* for IFNAMSIZ and co... */ -#else -#include <sys/types.h> -#include <net/if.h> -typedef __uint32_t __u32; -typedef __int32_t __s32; -typedef __uint16_t __u16; -typedef __int16_t __s16; -typedef __uint8_t __u8; -#ifndef __user -#define __user -#endif /* __user */ -#endif - -/***************************** VERSION *****************************/ -/* - * This constant is used to know the availability of the wireless - * extensions and to know which version of wireless extensions it is - * (there is some stuff that will be added in the future...) - * I just plan to increment with each new version. - */ -#define WIRELESS_EXT 19 - -/* - * Changes : - * - * V2 to V3 - * -------- - * Alan Cox start some incompatibles changes. I've integrated a bit more. - * - Encryption renamed to Encode to avoid US regulation problems - * - Frequency changed from float to struct to avoid problems on old 386 - * - * V3 to V4 - * -------- - * - Add sensitivity - * - * V4 to V5 - * -------- - * - Missing encoding definitions in range - * - Access points stuff - * - * V5 to V6 - * -------- - * - 802.11 support (ESSID ioctls) - * - * V6 to V7 - * -------- - * - define IW_ESSID_MAX_SIZE and IW_MAX_AP - * - * V7 to V8 - * -------- - * - Changed my e-mail address - * - More 802.11 support (nickname, rate, rts, frag) - * - List index in frequencies - * - * V8 to V9 - * -------- - * - Support for 'mode of operation' (ad-hoc, managed...) - * - Support for unicast and multicast power saving - * - Change encoding to support larger tokens (>64 bits) - * - Updated iw_params (disable, flags) and use it for NWID - * - Extracted iw_point from iwreq for clarity - * - * V9 to V10 - * --------- - * - Add PM capability to range structure - * - Add PM modifier : MAX/MIN/RELATIVE - * - Add encoding option : IW_ENCODE_NOKEY - * - Add TxPower ioctls (work like TxRate) - * - * V10 to V11 - * ---------- - * - Add WE version in range (help backward/forward compatibility) - * - Add retry ioctls (work like PM) - * - * V11 to V12 - * ---------- - * - Add SIOCSIWSTATS to get /proc/net/wireless programatically - * - Add DEV PRIVATE IOCTL to avoid collisions in SIOCDEVPRIVATE space - * - Add new statistics (frag, retry, beacon) - * - Add average quality (for user space calibration) - * - * V12 to V13 - * ---------- - * - Document creation of new driver API. - * - Extract union iwreq_data from struct iwreq (for new driver API). - * - Rename SIOCSIWNAME as SIOCSIWCOMMIT - * - * V13 to V14 - * ---------- - * - Wireless Events support : define struct iw_event - * - Define additional specific event numbers - * - Add "addr" and "param" fields in union iwreq_data - * - AP scanning stuff (SIOCSIWSCAN and friends) - * - * V14 to V15 - * ---------- - * - Add IW_PRIV_TYPE_ADDR for struct sockaddr private arg - * - Make struct iw_freq signed (both m & e), add explicit padding - * - Add IWEVCUSTOM for driver specific event/scanning token - * - Add IW_MAX_GET_SPY for driver returning a lot of addresses - * - Add IW_TXPOW_RANGE for range of Tx Powers - * - Add IWEVREGISTERED & IWEVEXPIRED events for Access Points - * - Add IW_MODE_MONITOR for passive monitor - * - * V15 to V16 - * ---------- - * - Increase the number of bitrates in iw_range to 32 (for 802.11g) - * - Increase the number of frequencies in iw_range to 32 (for 802.11b+a) - * - Reshuffle struct iw_range for increases, add filler - * - Increase IW_MAX_AP to 64 for driver returning a lot of addresses - * - Remove IW_MAX_GET_SPY because conflict with enhanced spy support - * - Add SIOCSIWTHRSPY/SIOCGIWTHRSPY and "struct iw_thrspy" - * - Add IW_ENCODE_TEMP and iw_range->encoding_login_index - * - * V16 to V17 - * ---------- - * - Add flags to frequency -> auto/fixed - * - Document (struct iw_quality *)->updated, add new flags (INVALID) - * - Wireless Event capability in struct iw_range - * - Add support for relative TxPower (yick !) - * - * V17 to V18 (From Jouni Malinen <j@w1.fi>) - * ---------- - * - Add support for WPA/WPA2 - * - Add extended encoding configuration (SIOCSIWENCODEEXT and - * SIOCGIWENCODEEXT) - * - Add SIOCSIWGENIE/SIOCGIWGENIE - * - Add SIOCSIWMLME - * - Add SIOCSIWPMKSA - * - Add struct iw_range bit field for supported encoding capabilities - * - Add optional scan request parameters for SIOCSIWSCAN - * - Add SIOCSIWAUTH/SIOCGIWAUTH for setting authentication and WPA - * related parameters (extensible up to 4096 parameter values) - * - Add wireless events: IWEVGENIE, IWEVMICHAELMICFAILURE, - * IWEVASSOCREQIE, IWEVASSOCRESPIE, IWEVPMKIDCAND - * - * V18 to V19 - * ---------- - * - Remove (struct iw_point *)->pointer from events and streams - * - Remove header includes to help user space - * - Increase IW_ENCODING_TOKEN_MAX from 32 to 64 - * - Add IW_QUAL_ALL_UPDATED and IW_QUAL_ALL_INVALID macros - * - Add explicit flag to tell stats are in dBm : IW_QUAL_DBM - * - Add IW_IOCTL_IDX() and IW_EVENT_IDX() macros - */ - -/**************************** CONSTANTS ****************************/ - -/* -------------------------- IOCTL LIST -------------------------- */ - -/* Wireless Identification */ -#define SIOCSIWCOMMIT 0x8B00 /* Commit pending changes to driver */ -#define SIOCGIWNAME 0x8B01 /* get name == wireless protocol */ -/* SIOCGIWNAME is used to verify the presence of Wireless Extensions. - * Common values : "IEEE 802.11-DS", "IEEE 802.11-FH", "IEEE 802.11b"... - * Don't put the name of your driver there, it's useless. */ - -/* Basic operations */ -#define SIOCSIWNWID 0x8B02 /* set network id (pre-802.11) */ -#define SIOCGIWNWID 0x8B03 /* get network id (the cell) */ -#define SIOCSIWFREQ 0x8B04 /* set channel/frequency (Hz) */ -#define SIOCGIWFREQ 0x8B05 /* get channel/frequency (Hz) */ -#define SIOCSIWMODE 0x8B06 /* set operation mode */ -#define SIOCGIWMODE 0x8B07 /* get operation mode */ -#define SIOCSIWSENS 0x8B08 /* set sensitivity (dBm) */ -#define SIOCGIWSENS 0x8B09 /* get sensitivity (dBm) */ - -/* Informative stuff */ -#define SIOCSIWRANGE 0x8B0A /* Unused */ -#define SIOCGIWRANGE 0x8B0B /* Get range of parameters */ -#define SIOCSIWPRIV 0x8B0C /* Unused */ -#define SIOCGIWPRIV 0x8B0D /* get private ioctl interface info */ -#define SIOCSIWSTATS 0x8B0E /* Unused */ -#define SIOCGIWSTATS 0x8B0F /* Get /proc/net/wireless stats */ -/* SIOCGIWSTATS is strictly used between user space and the kernel, and - * is never passed to the driver (i.e. the driver will never see it). */ - -/* Spy support (statistics per MAC address - used for Mobile IP support) */ -#define SIOCSIWSPY 0x8B10 /* set spy addresses */ -#define SIOCGIWSPY 0x8B11 /* get spy info (quality of link) */ -#define SIOCSIWTHRSPY 0x8B12 /* set spy threshold (spy event) */ -#define SIOCGIWTHRSPY 0x8B13 /* get spy threshold */ - -/* Access Point manipulation */ -#define SIOCSIWAP 0x8B14 /* set access point MAC addresses */ -#define SIOCGIWAP 0x8B15 /* get access point MAC addresses */ -#define SIOCGIWAPLIST 0x8B17 /* Deprecated in favor of scanning */ -#define SIOCSIWSCAN 0x8B18 /* trigger scanning (list cells) */ -#define SIOCGIWSCAN 0x8B19 /* get scanning results */ - -/* 802.11 specific support */ -#define SIOCSIWESSID 0x8B1A /* set ESSID (network name) */ -#define SIOCGIWESSID 0x8B1B /* get ESSID */ -#define SIOCSIWNICKN 0x8B1C /* set node name/nickname */ -#define SIOCGIWNICKN 0x8B1D /* get node name/nickname */ -/* As the ESSID and NICKN are strings up to 32 bytes long, it doesn't fit - * within the 'iwreq' structure, so we need to use the 'data' member to - * point to a string in user space, like it is done for RANGE... */ - -/* Other parameters useful in 802.11 and some other devices */ -#define SIOCSIWRATE 0x8B20 /* set default bit rate (bps) */ -#define SIOCGIWRATE 0x8B21 /* get default bit rate (bps) */ -#define SIOCSIWRTS 0x8B22 /* set RTS/CTS threshold (bytes) */ -#define SIOCGIWRTS 0x8B23 /* get RTS/CTS threshold (bytes) */ -#define SIOCSIWFRAG 0x8B24 /* set fragmentation thr (bytes) */ -#define SIOCGIWFRAG 0x8B25 /* get fragmentation thr (bytes) */ -#define SIOCSIWTXPOW 0x8B26 /* set transmit power (dBm) */ -#define SIOCGIWTXPOW 0x8B27 /* get transmit power (dBm) */ -#define SIOCSIWRETRY 0x8B28 /* set retry limits and lifetime */ -#define SIOCGIWRETRY 0x8B29 /* get retry limits and lifetime */ - -/* Encoding stuff (scrambling, hardware security, WEP...) */ -#define SIOCSIWENCODE 0x8B2A /* set encoding token & mode */ -#define SIOCGIWENCODE 0x8B2B /* get encoding token & mode */ -/* Power saving stuff (power management, unicast and multicast) */ -#define SIOCSIWPOWER 0x8B2C /* set Power Management settings */ -#define SIOCGIWPOWER 0x8B2D /* get Power Management settings */ - -/* WPA : Generic IEEE 802.11 informatiom element (e.g., for WPA/RSN/WMM). - * This ioctl uses struct iw_point and data buffer that includes IE id and len - * fields. More than one IE may be included in the request. Setting the generic - * IE to empty buffer (len=0) removes the generic IE from the driver. Drivers - * are allowed to generate their own WPA/RSN IEs, but in these cases, drivers - * are required to report the used IE as a wireless event, e.g., when - * associating with an AP. */ -#define SIOCSIWGENIE 0x8B30 /* set generic IE */ -#define SIOCGIWGENIE 0x8B31 /* get generic IE */ - -/* WPA : IEEE 802.11 MLME requests */ -#define SIOCSIWMLME 0x8B16 /* request MLME operation; uses - * struct iw_mlme */ -/* WPA : Authentication mode parameters */ -#define SIOCSIWAUTH 0x8B32 /* set authentication mode params */ -#define SIOCGIWAUTH 0x8B33 /* get authentication mode params */ - -/* WPA : Extended version of encoding configuration */ -#define SIOCSIWENCODEEXT 0x8B34 /* set encoding token & mode */ -#define SIOCGIWENCODEEXT 0x8B35 /* get encoding token & mode */ - -/* WPA2 : PMKSA cache management */ -#define SIOCSIWPMKSA 0x8B36 /* PMKSA cache operation */ - -/* -------------------- DEV PRIVATE IOCTL LIST -------------------- */ - -/* These 32 ioctl are wireless device private, for 16 commands. - * Each driver is free to use them for whatever purpose it chooses, - * however the driver *must* export the description of those ioctls - * with SIOCGIWPRIV and *must* use arguments as defined below. - * If you don't follow those rules, DaveM is going to hate you (reason : - * it make mixed 32/64bit operation impossible). - */ -#define SIOCIWFIRSTPRIV 0x8BE0 -#define SIOCIWLASTPRIV 0x8BFF -/* Previously, we were using SIOCDEVPRIVATE, but we now have our - * separate range because of collisions with other tools such as - * 'mii-tool'. - * We now have 32 commands, so a bit more space ;-). - * Also, all 'odd' commands are only usable by root and don't return the - * content of ifr/iwr to user (but you are not obliged to use the set/get - * convention, just use every other two command). More details in iwpriv.c. - * And I repeat : you are not forced to use them with iwpriv, but you - * must be compliant with it. - */ - -/* ------------------------- IOCTL STUFF ------------------------- */ - -/* The first and the last (range) */ -#define SIOCIWFIRST 0x8B00 -#define SIOCIWLAST SIOCIWLASTPRIV /* 0x8BFF */ -#define IW_IOCTL_IDX(cmd) ((cmd) - SIOCIWFIRST) - -/* Even : get (world access), odd : set (root access) */ -#define IW_IS_SET(cmd) (!((cmd) & 0x1)) -#define IW_IS_GET(cmd) ((cmd) & 0x1) - -/* ----------------------- WIRELESS EVENTS ----------------------- */ -/* Those are *NOT* ioctls, do not issue request on them !!! */ -/* Most events use the same identifier as ioctl requests */ - -#define IWEVTXDROP 0x8C00 /* Packet dropped to excessive retry */ -#define IWEVQUAL 0x8C01 /* Quality part of statistics (scan) */ -#define IWEVCUSTOM 0x8C02 /* Driver specific ascii string */ -#define IWEVREGISTERED 0x8C03 /* Discovered a new node (AP mode) */ -#define IWEVEXPIRED 0x8C04 /* Expired a node (AP mode) */ -#define IWEVGENIE 0x8C05 /* Generic IE (WPA, RSN, WMM, ..) - * (scan results); This includes id and - * length fields. One IWEVGENIE may - * contain more than one IE. Scan - * results may contain one or more - * IWEVGENIE events. */ -#define IWEVMICHAELMICFAILURE 0x8C06 /* Michael MIC failure - * (struct iw_michaelmicfailure) - */ -#define IWEVASSOCREQIE 0x8C07 /* IEs used in (Re)Association Request. - * The data includes id and length - * fields and may contain more than one - * IE. This event is required in - * Managed mode if the driver - * generates its own WPA/RSN IE. This - * should be sent just before - * IWEVREGISTERED event for the - * association. */ -#define IWEVASSOCRESPIE 0x8C08 /* IEs used in (Re)Association - * Response. The data includes id and - * length fields and may contain more - * than one IE. This may be sent - * between IWEVASSOCREQIE and - * IWEVREGISTERED events for the - * association. */ -#define IWEVPMKIDCAND 0x8C09 /* PMKID candidate for RSN - * pre-authentication - * (struct iw_pmkid_cand) */ - -#define IWEVFIRST 0x8C00 -#define IW_EVENT_IDX(cmd) ((cmd) - IWEVFIRST) - -/* ------------------------- PRIVATE INFO ------------------------- */ -/* - * The following is used with SIOCGIWPRIV. It allow a driver to define - * the interface (name, type of data) for its private ioctl. - * Privates ioctl are SIOCIWFIRSTPRIV -> SIOCIWLASTPRIV - */ - -#define IW_PRIV_TYPE_MASK 0x7000 /* Type of arguments */ -#define IW_PRIV_TYPE_NONE 0x0000 -#define IW_PRIV_TYPE_BYTE 0x1000 /* Char as number */ -#define IW_PRIV_TYPE_CHAR 0x2000 /* Char as character */ -#define IW_PRIV_TYPE_INT 0x4000 /* 32 bits int */ -#define IW_PRIV_TYPE_FLOAT 0x5000 /* struct iw_freq */ -#define IW_PRIV_TYPE_ADDR 0x6000 /* struct sockaddr */ - -#define IW_PRIV_SIZE_FIXED 0x0800 /* Variable or fixed number of args */ - -#define IW_PRIV_SIZE_MASK 0x07FF /* Max number of those args */ - -/* - * Note : if the number of args is fixed and the size < 16 octets, - * instead of passing a pointer we will put args in the iwreq struct... - */ - -/* ----------------------- OTHER CONSTANTS ----------------------- */ - -/* Maximum frequencies in the range struct */ -#define IW_MAX_FREQUENCIES 32 -/* Note : if you have something like 80 frequencies, - * don't increase this constant and don't fill the frequency list. - * The user will be able to set by channel anyway... */ - -/* Maximum bit rates in the range struct */ -#define IW_MAX_BITRATES 32 - -/* Maximum tx powers in the range struct */ -#define IW_MAX_TXPOWER 8 -/* Note : if you more than 8 TXPowers, just set the max and min or - * a few of them in the struct iw_range. */ - -/* Maximum of address that you may set with SPY */ -#define IW_MAX_SPY 8 - -/* Maximum of address that you may get in the - list of access points in range */ -#define IW_MAX_AP 64 - -/* Maximum size of the ESSID and NICKN strings */ -#define IW_ESSID_MAX_SIZE 32 - -/* Modes of operation */ -#define IW_MODE_AUTO 0 /* Let the driver decides */ -#define IW_MODE_ADHOC 1 /* Single cell network */ -#define IW_MODE_INFRA 2 /* Multi cell network, roaming, ... */ -#define IW_MODE_MASTER 3 /* Synchronisation master or Access Point */ -#define IW_MODE_REPEAT 4 /* Wireless Repeater (forwarder) */ -#define IW_MODE_SECOND 5 /* Secondary master/repeater (backup) */ -#define IW_MODE_MONITOR 6 /* Passive monitor (listen only) */ - -/* Statistics flags (bitmask in updated) */ -#define IW_QUAL_QUAL_UPDATED 0x01 /* Value was updated since last read */ -#define IW_QUAL_LEVEL_UPDATED 0x02 -#define IW_QUAL_NOISE_UPDATED 0x04 -#define IW_QUAL_ALL_UPDATED 0x07 -#define IW_QUAL_DBM 0x08 /* Level + Noise are dBm */ -#define IW_QUAL_QUAL_INVALID 0x10 /* Driver doesn't provide value */ -#define IW_QUAL_LEVEL_INVALID 0x20 -#define IW_QUAL_NOISE_INVALID 0x40 -#define IW_QUAL_ALL_INVALID 0x70 - -/* Frequency flags */ -#define IW_FREQ_AUTO 0x00 /* Let the driver decides */ -#define IW_FREQ_FIXED 0x01 /* Force a specific value */ - -/* Maximum number of size of encoding token available - * they are listed in the range structure */ -#define IW_MAX_ENCODING_SIZES 8 - -/* Maximum size of the encoding token in bytes */ -#define IW_ENCODING_TOKEN_MAX 64 /* 512 bits (for now) */ - -/* Flags for encoding (along with the token) */ -#define IW_ENCODE_INDEX 0x00FF /* Token index (if needed) */ -#define IW_ENCODE_FLAGS 0xFF00 /* Flags defined below */ -#define IW_ENCODE_MODE 0xF000 /* Modes defined below */ -#define IW_ENCODE_DISABLED 0x8000 /* Encoding disabled */ -#define IW_ENCODE_ENABLED 0x0000 /* Encoding enabled */ -#define IW_ENCODE_RESTRICTED 0x4000 /* Refuse non-encoded packets */ -#define IW_ENCODE_OPEN 0x2000 /* Accept non-encoded packets */ -#define IW_ENCODE_NOKEY 0x0800 /* Key is write only, so not present */ -#define IW_ENCODE_TEMP 0x0400 /* Temporary key */ - -/* Power management flags available (along with the value, if any) */ -#define IW_POWER_ON 0x0000 /* No details... */ -#define IW_POWER_TYPE 0xF000 /* Type of parameter */ -#define IW_POWER_PERIOD 0x1000 /* Value is a period/duration of */ -#define IW_POWER_TIMEOUT 0x2000 /* Value is a timeout (to go asleep) */ -#define IW_POWER_MODE 0x0F00 /* Power Management mode */ -#define IW_POWER_UNICAST_R 0x0100 /* Receive only unicast messages */ -#define IW_POWER_MULTICAST_R 0x0200 /* Receive only multicast messages */ -#define IW_POWER_ALL_R 0x0300 /* Receive all messages though PM */ -#define IW_POWER_FORCE_S 0x0400 /* Force PM procedure for sending unicast */ -#define IW_POWER_REPEATER 0x0800 /* Repeat broadcast messages in PM period */ -#define IW_POWER_MODIFIER 0x000F /* Modify a parameter */ -#define IW_POWER_MIN 0x0001 /* Value is a minimum */ -#define IW_POWER_MAX 0x0002 /* Value is a maximum */ -#define IW_POWER_RELATIVE 0x0004 /* Value is not in seconds/ms/us */ - -/* Transmit Power flags available */ -#define IW_TXPOW_TYPE 0x00FF /* Type of value */ -#define IW_TXPOW_DBM 0x0000 /* Value is in dBm */ -#define IW_TXPOW_MWATT 0x0001 /* Value is in mW */ -#define IW_TXPOW_RELATIVE 0x0002 /* Value is in arbitrary units */ -#define IW_TXPOW_RANGE 0x1000 /* Range of value between min/max */ - -/* Retry limits and lifetime flags available */ -#define IW_RETRY_ON 0x0000 /* No details... */ -#define IW_RETRY_TYPE 0xF000 /* Type of parameter */ -#define IW_RETRY_LIMIT 0x1000 /* Maximum number of retries*/ -#define IW_RETRY_LIFETIME 0x2000 /* Maximum duration of retries in us */ -#define IW_RETRY_MODIFIER 0x000F /* Modify a parameter */ -#define IW_RETRY_MIN 0x0001 /* Value is a minimum */ -#define IW_RETRY_MAX 0x0002 /* Value is a maximum */ -#define IW_RETRY_RELATIVE 0x0004 /* Value is not in seconds/ms/us */ - -/* Scanning request flags */ -#define IW_SCAN_DEFAULT 0x0000 /* Default scan of the driver */ -#define IW_SCAN_ALL_ESSID 0x0001 /* Scan all ESSIDs */ -#define IW_SCAN_THIS_ESSID 0x0002 /* Scan only this ESSID */ -#define IW_SCAN_ALL_FREQ 0x0004 /* Scan all Frequencies */ -#define IW_SCAN_THIS_FREQ 0x0008 /* Scan only this Frequency */ -#define IW_SCAN_ALL_MODE 0x0010 /* Scan all Modes */ -#define IW_SCAN_THIS_MODE 0x0020 /* Scan only this Mode */ -#define IW_SCAN_ALL_RATE 0x0040 /* Scan all Bit-Rates */ -#define IW_SCAN_THIS_RATE 0x0080 /* Scan only this Bit-Rate */ -/* struct iw_scan_req scan_type */ -#define IW_SCAN_TYPE_ACTIVE 0 -#define IW_SCAN_TYPE_PASSIVE 1 -/* Maximum size of returned data */ -#define IW_SCAN_MAX_DATA 4096 /* In bytes */ - -/* Max number of char in custom event - use multiple of them if needed */ -#define IW_CUSTOM_MAX 256 /* In bytes */ - -/* Generic information element */ -#define IW_GENERIC_IE_MAX 1024 - -/* MLME requests (SIOCSIWMLME / struct iw_mlme) */ -#define IW_MLME_DEAUTH 0 -#define IW_MLME_DISASSOC 1 - -/* SIOCSIWAUTH/SIOCGIWAUTH struct iw_param flags */ -#define IW_AUTH_INDEX 0x0FFF -#define IW_AUTH_FLAGS 0xF000 -/* SIOCSIWAUTH/SIOCGIWAUTH parameters (0 .. 4095) - * (IW_AUTH_INDEX mask in struct iw_param flags; this is the index of the - * parameter that is being set/get to; value will be read/written to - * struct iw_param value field) */ -#define IW_AUTH_WPA_VERSION 0 -#define IW_AUTH_CIPHER_PAIRWISE 1 -#define IW_AUTH_CIPHER_GROUP 2 -#define IW_AUTH_KEY_MGMT 3 -#define IW_AUTH_TKIP_COUNTERMEASURES 4 -#define IW_AUTH_DROP_UNENCRYPTED 5 -#define IW_AUTH_80211_AUTH_ALG 6 -#define IW_AUTH_WPA_ENABLED 7 -#define IW_AUTH_RX_UNENCRYPTED_EAPOL 8 -#define IW_AUTH_ROAMING_CONTROL 9 -#define IW_AUTH_PRIVACY_INVOKED 10 -#define IW_AUTH_CIPHER_GROUP_MGMT 11 -#define IW_AUTH_MFP 12 - -/* IW_AUTH_WPA_VERSION values (bit field) */ -#define IW_AUTH_WPA_VERSION_DISABLED 0x00000001 -#define IW_AUTH_WPA_VERSION_WPA 0x00000002 -#define IW_AUTH_WPA_VERSION_WPA2 0x00000004 - -/* IW_AUTH_PAIRWISE_CIPHER and IW_AUTH_GROUP_CIPHER values (bit field) */ -#define IW_AUTH_CIPHER_NONE 0x00000001 -#define IW_AUTH_CIPHER_WEP40 0x00000002 -#define IW_AUTH_CIPHER_TKIP 0x00000004 -#define IW_AUTH_CIPHER_CCMP 0x00000008 -#define IW_AUTH_CIPHER_WEP104 0x00000010 - -/* IW_AUTH_KEY_MGMT values (bit field) */ -#define IW_AUTH_KEY_MGMT_802_1X 1 -#define IW_AUTH_KEY_MGMT_PSK 2 - -/* IW_AUTH_80211_AUTH_ALG values (bit field) */ -#define IW_AUTH_ALG_OPEN_SYSTEM 0x00000001 -#define IW_AUTH_ALG_SHARED_KEY 0x00000002 -#define IW_AUTH_ALG_LEAP 0x00000004 - -/* IW_AUTH_ROAMING_CONTROL values */ -#define IW_AUTH_ROAMING_ENABLE 0 /* driver/firmware based roaming */ -#define IW_AUTH_ROAMING_DISABLE 1 /* user space program used for roaming - * control */ - -/* IW_AUTH_MFP (management frame protection) values */ -#define IW_AUTH_MFP_DISABLED 0 /* MFP disabled */ -#define IW_AUTH_MFP_OPTIONAL 1 /* MFP optional */ -#define IW_AUTH_MFP_REQUIRED 2 /* MFP required */ - -/* SIOCSIWENCODEEXT definitions */ -#define IW_ENCODE_SEQ_MAX_SIZE 8 -/* struct iw_encode_ext ->alg */ -#define IW_ENCODE_ALG_NONE 0 -#define IW_ENCODE_ALG_WEP 1 -#define IW_ENCODE_ALG_TKIP 2 -#define IW_ENCODE_ALG_CCMP 3 -#define IW_ENCODE_ALG_PMK 4 -#define IW_ENCODE_ALG_AES_CMAC 5 -/* struct iw_encode_ext ->ext_flags */ -#define IW_ENCODE_EXT_TX_SEQ_VALID 0x00000001 -#define IW_ENCODE_EXT_RX_SEQ_VALID 0x00000002 -#define IW_ENCODE_EXT_GROUP_KEY 0x00000004 -#define IW_ENCODE_EXT_SET_TX_KEY 0x00000008 - -/* IWEVMICHAELMICFAILURE : struct iw_michaelmicfailure ->flags */ -#define IW_MICFAILURE_KEY_ID 0x00000003 /* Key ID 0..3 */ -#define IW_MICFAILURE_GROUP 0x00000004 -#define IW_MICFAILURE_PAIRWISE 0x00000008 -#define IW_MICFAILURE_STAKEY 0x00000010 -#define IW_MICFAILURE_COUNT 0x00000060 /* 1 or 2 (0 = count not supported) - */ - -/* Bit field values for enc_capa in struct iw_range */ -#define IW_ENC_CAPA_WPA 0x00000001 -#define IW_ENC_CAPA_WPA2 0x00000002 -#define IW_ENC_CAPA_CIPHER_TKIP 0x00000004 -#define IW_ENC_CAPA_CIPHER_CCMP 0x00000008 -#define IW_ENC_CAPA_4WAY_HANDSHAKE 0x00000010 - -/* Event capability macros - in (struct iw_range *)->event_capa - * Because we have more than 32 possible events, we use an array of - * 32 bit bitmasks. Note : 32 bits = 0x20 = 2^5. */ -#define IW_EVENT_CAPA_BASE(cmd) ((cmd >= SIOCIWFIRSTPRIV) ? \ - (cmd - SIOCIWFIRSTPRIV + 0x60) : \ - (cmd - SIOCSIWCOMMIT)) -#define IW_EVENT_CAPA_INDEX(cmd) (IW_EVENT_CAPA_BASE(cmd) >> 5) -#define IW_EVENT_CAPA_MASK(cmd) (1 << (IW_EVENT_CAPA_BASE(cmd) & 0x1F)) -/* Event capability constants - event autogenerated by the kernel - * This list is valid for most 802.11 devices, customise as needed... */ -#define IW_EVENT_CAPA_K_0 (IW_EVENT_CAPA_MASK(0x8B04) | \ - IW_EVENT_CAPA_MASK(0x8B06) | \ - IW_EVENT_CAPA_MASK(0x8B1A)) -#define IW_EVENT_CAPA_K_1 (IW_EVENT_CAPA_MASK(0x8B2A)) -/* "Easy" macro to set events in iw_range (less efficient) */ -#define IW_EVENT_CAPA_SET(event_capa, cmd) (event_capa[IW_EVENT_CAPA_INDEX(cmd)] |= IW_EVENT_CAPA_MASK(cmd)) -#define IW_EVENT_CAPA_SET_KERNEL(event_capa) {event_capa[0] |= IW_EVENT_CAPA_K_0; event_capa[1] |= IW_EVENT_CAPA_K_1; } - - -/****************************** TYPES ******************************/ - -/* --------------------------- SUBTYPES --------------------------- */ -/* - * Generic format for most parameters that fit in an int - */ -struct iw_param -{ - __s32 value; /* The value of the parameter itself */ - __u8 fixed; /* Hardware should not use auto select */ - __u8 disabled; /* Disable the feature */ - __u16 flags; /* Various specifc flags (if any) */ -}; - -/* - * For all data larger than 16 octets, we need to use a - * pointer to memory allocated in user space. - */ -struct iw_point -{ - void __user *pointer; /* Pointer to the data (in user space) */ - __u16 length; /* number of fields or size in bytes */ - __u16 flags; /* Optional params */ -}; - -/* - * A frequency - * For numbers lower than 10^9, we encode the number in 'm' and - * set 'e' to 0 - * For number greater than 10^9, we divide it by the lowest power - * of 10 to get 'm' lower than 10^9, with 'm'= f / (10^'e')... - * The power of 10 is in 'e', the result of the division is in 'm'. - */ -struct iw_freq -{ - __s32 m; /* Mantissa */ - __s16 e; /* Exponent */ - __u8 i; /* List index (when in range struct) */ - __u8 flags; /* Flags (fixed/auto) */ -}; - -/* - * Quality of the link - */ -struct iw_quality -{ - __u8 qual; /* link quality (%retries, SNR, - %missed beacons or better...) */ - __u8 level; /* signal level (dBm) */ - __u8 noise; /* noise level (dBm) */ - __u8 updated; /* Flags to know if updated */ -}; - -/* - * Packet discarded in the wireless adapter due to - * "wireless" specific problems... - * Note : the list of counter and statistics in net_device_stats - * is already pretty exhaustive, and you should use that first. - * This is only additional stats... - */ -struct iw_discarded -{ - __u32 nwid; /* Rx : Wrong nwid/essid */ - __u32 code; /* Rx : Unable to code/decode (WEP) */ - __u32 fragment; /* Rx : Can't perform MAC reassembly */ - __u32 retries; /* Tx : Max MAC retries num reached */ - __u32 misc; /* Others cases */ -}; - -/* - * Packet/Time period missed in the wireless adapter due to - * "wireless" specific problems... - */ -struct iw_missed -{ - __u32 beacon; /* Missed beacons/superframe */ -}; - -/* - * Quality range (for spy threshold) - */ -struct iw_thrspy -{ - struct sockaddr addr; /* Source address (hw/mac) */ - struct iw_quality qual; /* Quality of the link */ - struct iw_quality low; /* Low threshold */ - struct iw_quality high; /* High threshold */ -}; - -/* - * Optional data for scan request - * - * Note: these optional parameters are controlling parameters for the - * scanning behavior, these do not apply to getting scan results - * (SIOCGIWSCAN). Drivers are expected to keep a local BSS table and - * provide a merged results with all BSSes even if the previous scan - * request limited scanning to a subset, e.g., by specifying an SSID. - * Especially, scan results are required to include an entry for the - * current BSS if the driver is in Managed mode and associated with an AP. - */ -struct iw_scan_req -{ - __u8 scan_type; /* IW_SCAN_TYPE_{ACTIVE,PASSIVE} */ - __u8 essid_len; - __u8 num_channels; /* num entries in channel_list; - * 0 = scan all allowed channels */ - __u8 flags; /* reserved as padding; use zero, this may - * be used in the future for adding flags - * to request different scan behavior */ - struct sockaddr bssid; /* ff:ff:ff:ff:ff:ff for broadcast BSSID or - * individual address of a specific BSS */ - - /* - * Use this ESSID if IW_SCAN_THIS_ESSID flag is used instead of using - * the current ESSID. This allows scan requests for specific ESSID - * without having to change the current ESSID and potentially breaking - * the current association. - */ - __u8 essid[IW_ESSID_MAX_SIZE]; - - /* - * Optional parameters for changing the default scanning behavior. - * These are based on the MLME-SCAN.request from IEEE Std 802.11. - * TU is 1.024 ms. If these are set to 0, driver is expected to use - * reasonable default values. min_channel_time defines the time that - * will be used to wait for the first reply on each channel. If no - * replies are received, next channel will be scanned after this. If - * replies are received, total time waited on the channel is defined by - * max_channel_time. - */ - __u32 min_channel_time; /* in TU */ - __u32 max_channel_time; /* in TU */ - - struct iw_freq channel_list[IW_MAX_FREQUENCIES]; -}; - -/* ------------------------- WPA SUPPORT ------------------------- */ - -/* - * Extended data structure for get/set encoding (this is used with - * SIOCSIWENCODEEXT/SIOCGIWENCODEEXT. struct iw_point and IW_ENCODE_* - * flags are used in the same way as with SIOCSIWENCODE/SIOCGIWENCODE and - * only the data contents changes (key data -> this structure, including - * key data). - * - * If the new key is the first group key, it will be set as the default - * TX key. Otherwise, default TX key index is only changed if - * IW_ENCODE_EXT_SET_TX_KEY flag is set. - * - * Key will be changed with SIOCSIWENCODEEXT in all cases except for - * special "change TX key index" operation which is indicated by setting - * key_len = 0 and ext_flags |= IW_ENCODE_EXT_SET_TX_KEY. - * - * tx_seq/rx_seq are only used when respective - * IW_ENCODE_EXT_{TX,RX}_SEQ_VALID flag is set in ext_flags. Normal - * TKIP/CCMP operation is to set RX seq with SIOCSIWENCODEEXT and start - * TX seq from zero whenever key is changed. SIOCGIWENCODEEXT is normally - * used only by an Authenticator (AP or an IBSS station) to get the - * current TX sequence number. Using TX_SEQ_VALID for SIOCSIWENCODEEXT and - * RX_SEQ_VALID for SIOCGIWENCODEEXT are optional, but can be useful for - * debugging/testing. - */ -struct iw_encode_ext -{ - __u32 ext_flags; /* IW_ENCODE_EXT_* */ - __u8 tx_seq[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */ - __u8 rx_seq[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */ - struct sockaddr addr; /* ff:ff:ff:ff:ff:ff for broadcast/multicast - * (group) keys or unicast address for - * individual keys */ - __u16 alg; /* IW_ENCODE_ALG_* */ - __u16 key_len; - __u8 key[0]; -}; - -/* SIOCSIWMLME data */ -struct iw_mlme -{ - __u16 cmd; /* IW_MLME_* */ - __u16 reason_code; - struct sockaddr addr; -}; - -/* SIOCSIWPMKSA data */ -#define IW_PMKSA_ADD 1 -#define IW_PMKSA_REMOVE 2 -#define IW_PMKSA_FLUSH 3 - -#define IW_PMKID_LEN 16 - -struct iw_pmksa -{ - __u32 cmd; /* IW_PMKSA_* */ - struct sockaddr bssid; - __u8 pmkid[IW_PMKID_LEN]; -}; - -/* IWEVMICHAELMICFAILURE data */ -struct iw_michaelmicfailure -{ - __u32 flags; - struct sockaddr src_addr; - __u8 tsc[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */ -}; - -/* IWEVPMKIDCAND data */ -#define IW_PMKID_CAND_PREAUTH 0x00000001 /* RNS pre-authentication enabled */ -struct iw_pmkid_cand -{ - __u32 flags; /* IW_PMKID_CAND_* */ - __u32 index; /* the smaller the index, the higher the - * priority */ - struct sockaddr bssid; -}; - -/* ------------------------ WIRELESS STATS ------------------------ */ -/* - * Wireless statistics (used for /proc/net/wireless) - */ -struct iw_statistics -{ - __u16 status; /* Status - * - device dependent for now */ - - struct iw_quality qual; /* Quality of the link - * (instant/mean/max) */ - struct iw_discarded discard; /* Packet discarded counts */ - struct iw_missed miss; /* Packet missed counts */ -}; - -/* ------------------------ IOCTL REQUEST ------------------------ */ -/* - * This structure defines the payload of an ioctl, and is used - * below. - * - * Note that this structure should fit on the memory footprint - * of iwreq (which is the same as ifreq), which mean a max size of - * 16 octets = 128 bits. Warning, pointers might be 64 bits wide... - * You should check this when increasing the structures defined - * above in this file... - */ -union iwreq_data -{ - /* Config - generic */ - char name[IFNAMSIZ]; - /* Name : used to verify the presence of wireless extensions. - * Name of the protocol/provider... */ - - struct iw_point essid; /* Extended network name */ - struct iw_param nwid; /* network id (or domain - the cell) */ - struct iw_freq freq; /* frequency or channel : - * 0-1000 = channel - * > 1000 = frequency in Hz */ - - struct iw_param sens; /* signal level threshold */ - struct iw_param bitrate; /* default bit rate */ - struct iw_param txpower; /* default transmit power */ - struct iw_param rts; /* RTS threshold threshold */ - struct iw_param frag; /* Fragmentation threshold */ - __u32 mode; /* Operation mode */ - struct iw_param retry; /* Retry limits & lifetime */ - - struct iw_point encoding; /* Encoding stuff : tokens */ - struct iw_param power; /* PM duration/timeout */ - struct iw_quality qual; /* Quality part of statistics */ - - struct sockaddr ap_addr; /* Access point address */ - struct sockaddr addr; /* Destination address (hw/mac) */ - - struct iw_param param; /* Other small parameters */ - struct iw_point data; /* Other large parameters */ -}; - -/* - * The structure to exchange data for ioctl. - * This structure is the same as 'struct ifreq', but (re)defined for - * convenience... - * Do I need to remind you about structure size (32 octets) ? - */ -struct iwreq -{ - union - { - char ifrn_name[IFNAMSIZ]; /* if name, e.g. "eth0" */ - } ifr_ifrn; - - /* Data part (defined just above) */ - union iwreq_data u; -}; - -/* -------------------------- IOCTL DATA -------------------------- */ -/* - * For those ioctl which want to exchange mode data that what could - * fit in the above structure... - */ - -/* - * Range of parameters - */ - -struct iw_range -{ - /* Informative stuff (to choose between different interface) */ - __u32 throughput; /* To give an idea... */ - /* In theory this value should be the maximum benchmarked - * TCP/IP throughput, because with most of these devices the - * bit rate is meaningless (overhead an co) to estimate how - * fast the connection will go and pick the fastest one. - * I suggest people to play with Netperf or any benchmark... - */ - - /* NWID (or domain id) */ - __u32 min_nwid; /* Minimal NWID we are able to set */ - __u32 max_nwid; /* Maximal NWID we are able to set */ - - /* Old Frequency (backward compat - moved lower ) */ - __u16 old_num_channels; - __u8 old_num_frequency; - - /* Wireless event capability bitmasks */ - __u32 event_capa[6]; - - /* signal level threshold range */ - __s32 sensitivity; - - /* Quality of link & SNR stuff */ - /* Quality range (link, level, noise) - * If the quality is absolute, it will be in the range [0 ; max_qual], - * if the quality is dBm, it will be in the range [max_qual ; 0]. - * Don't forget that we use 8 bit arithmetics... */ - struct iw_quality max_qual; /* Quality of the link */ - /* This should contain the average/typical values of the quality - * indicator. This should be the threshold between a "good" and - * a "bad" link (example : monitor going from green to orange). - * Currently, user space apps like quality monitors don't have any - * way to calibrate the measurement. With this, they can split - * the range between 0 and max_qual in different quality level - * (using a geometric subdivision centered on the average). - * I expect that people doing the user space apps will feedback - * us on which value we need to put in each driver... */ - struct iw_quality avg_qual; /* Quality of the link */ - - /* Rates */ - __u8 num_bitrates; /* Number of entries in the list */ - __s32 bitrate[IW_MAX_BITRATES]; /* list, in bps */ - - /* RTS threshold */ - __s32 min_rts; /* Minimal RTS threshold */ - __s32 max_rts; /* Maximal RTS threshold */ - - /* Frag threshold */ - __s32 min_frag; /* Minimal frag threshold */ - __s32 max_frag; /* Maximal frag threshold */ - - /* Power Management duration & timeout */ - __s32 min_pmp; /* Minimal PM period */ - __s32 max_pmp; /* Maximal PM period */ - __s32 min_pmt; /* Minimal PM timeout */ - __s32 max_pmt; /* Maximal PM timeout */ - __u16 pmp_flags; /* How to decode max/min PM period */ - __u16 pmt_flags; /* How to decode max/min PM timeout */ - __u16 pm_capa; /* What PM options are supported */ - - /* Encoder stuff */ - __u16 encoding_size[IW_MAX_ENCODING_SIZES]; /* Different token sizes */ - __u8 num_encoding_sizes; /* Number of entry in the list */ - __u8 max_encoding_tokens; /* Max number of tokens */ - /* For drivers that need a "login/passwd" form */ - __u8 encoding_login_index; /* token index for login token */ - - /* Transmit power */ - __u16 txpower_capa; /* What options are supported */ - __u8 num_txpower; /* Number of entries in the list */ - __s32 txpower[IW_MAX_TXPOWER]; /* list, in bps */ - - /* Wireless Extension version info */ - __u8 we_version_compiled; /* Must be WIRELESS_EXT */ - __u8 we_version_source; /* Last update of source */ - - /* Retry limits and lifetime */ - __u16 retry_capa; /* What retry options are supported */ - __u16 retry_flags; /* How to decode max/min retry limit */ - __u16 r_time_flags; /* How to decode max/min retry life */ - __s32 min_retry; /* Minimal number of retries */ - __s32 max_retry; /* Maximal number of retries */ - __s32 min_r_time; /* Minimal retry lifetime */ - __s32 max_r_time; /* Maximal retry lifetime */ - - /* Frequency */ - __u16 num_channels; /* Number of channels [0; num - 1] */ - __u8 num_frequency; /* Number of entry in the list */ - struct iw_freq freq[IW_MAX_FREQUENCIES]; /* list */ - /* Note : this frequency list doesn't need to fit channel numbers, - * because each entry contain its channel index */ - - __u32 enc_capa; /* IW_ENC_CAPA_* bit field */ -}; - -/* - * Private ioctl interface information - */ - -struct iw_priv_args -{ - __u32 cmd; /* Number of the ioctl to issue */ - __u16 set_args; /* Type and number of args */ - __u16 get_args; /* Type and number of args */ - char name[IFNAMSIZ]; /* Name of the extension */ -}; - -/* ----------------------- WIRELESS EVENTS ----------------------- */ -/* - * Wireless events are carried through the rtnetlink socket to user - * space. They are encapsulated in the IFLA_WIRELESS field of - * a RTM_NEWLINK message. - */ - -/* - * A Wireless Event. Contains basically the same data as the ioctl... - */ -struct iw_event -{ - __u16 len; /* Real lenght of this stuff */ - __u16 cmd; /* Wireless IOCTL */ - union iwreq_data u; /* IOCTL fixed payload */ -}; - -/* Size of the Event prefix (including padding and alignement junk) */ -#define IW_EV_LCP_LEN (sizeof(struct iw_event) - sizeof(union iwreq_data)) -/* Size of the various events */ -#define IW_EV_CHAR_LEN (IW_EV_LCP_LEN + IFNAMSIZ) -#define IW_EV_UINT_LEN (IW_EV_LCP_LEN + sizeof(__u32)) -#define IW_EV_FREQ_LEN (IW_EV_LCP_LEN + sizeof(struct iw_freq)) -#define IW_EV_PARAM_LEN (IW_EV_LCP_LEN + sizeof(struct iw_param)) -#define IW_EV_ADDR_LEN (IW_EV_LCP_LEN + sizeof(struct sockaddr)) -#define IW_EV_QUAL_LEN (IW_EV_LCP_LEN + sizeof(struct iw_quality)) - -/* iw_point events are special. First, the payload (extra data) come at - * the end of the event, so they are bigger than IW_EV_POINT_LEN. Second, - * we omit the pointer, so start at an offset. */ -#define IW_EV_POINT_OFF (((char *) &(((struct iw_point *) NULL)->length)) - \ - (char *) NULL) -#define IW_EV_POINT_LEN (IW_EV_LCP_LEN + sizeof(struct iw_point) - \ - IW_EV_POINT_OFF) - -#endif /* _LINUX_WIRELESS_H */ diff --git a/src/eap_common/chap.c b/src/eap_common/chap.c index 60bfc1c8168fb..820d18a416987 100644 --- a/src/eap_common/chap.c +++ b/src/eap_common/chap.c @@ -2,14 +2,8 @@ * CHAP-MD5 (RFC 1994) * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/eap_common/chap.h b/src/eap_common/chap.h index b9c400c7c7f41..a791505f99d1a 100644 --- a/src/eap_common/chap.h +++ b/src/eap_common/chap.h @@ -2,14 +2,8 @@ * CHAP-MD5 (RFC 1994) * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef CHAP_H diff --git a/src/eap_common/eap_common.c b/src/eap_common/eap_common.c index 4afa1ddb2acb3..7b077cb9f5904 100644 --- a/src/eap_common/eap_common.c +++ b/src/eap_common/eap_common.c @@ -1,15 +1,9 @@ /* * EAP common peer/server definitions - * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-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 - * published 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" @@ -19,6 +13,41 @@ #include "eap_common.h" /** + * eap_hdr_len_valid - Validate EAP header length field + * @msg: EAP frame (starting with EAP header) + * @min_payload: Minimum payload length needed + * Returns: 1 for valid header, 0 for invalid + * + * This is a helper function that does minimal validation of EAP messages. The + * length field is verified to be large enough to include the header and not + * too large to go beyond the end of the buffer. + */ +int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload) +{ + const struct eap_hdr *hdr; + size_t len; + + if (msg == NULL) + return 0; + + hdr = wpabuf_head(msg); + + if (wpabuf_len(msg) < sizeof(*hdr)) { + wpa_printf(MSG_INFO, "EAP: Too short EAP frame"); + return 0; + } + + len = be_to_host16(hdr->length); + if (len < sizeof(*hdr) + min_payload || len > wpabuf_len(msg)) { + wpa_printf(MSG_INFO, "EAP: Invalid EAP length"); + return 0; + } + + return 1; +} + + +/** * eap_hdr_validate - Validate EAP header * @vendor: Expected EAP Vendor-Id (0 = IETF) * @eap_type: Expected EAP type number @@ -41,19 +70,11 @@ const u8 * eap_hdr_validate(int vendor, EapType eap_type, const u8 *pos; size_t len; - hdr = wpabuf_head(msg); - - if (wpabuf_len(msg) < sizeof(*hdr)) { - wpa_printf(MSG_INFO, "EAP: Too short EAP frame"); + if (!eap_hdr_len_valid(msg, 1)) return NULL; - } + hdr = wpabuf_head(msg); len = be_to_host16(hdr->length); - if (len < sizeof(*hdr) + 1 || len > wpabuf_len(msg)) { - wpa_printf(MSG_INFO, "EAP: Invalid EAP length"); - return NULL; - } - pos = (const u8 *) (hdr + 1); if (*pos == EAP_TYPE_EXPANDED) { diff --git a/src/eap_common/eap_common.h b/src/eap_common/eap_common.h index b95e76b94f5c3..8850c1fe55dc6 100644 --- a/src/eap_common/eap_common.h +++ b/src/eap_common/eap_common.h @@ -1,15 +1,9 @@ /* * EAP common peer/server definitions - * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-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 - * published 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. */ #ifndef EAP_COMMON_H @@ -17,6 +11,7 @@ #include "wpabuf.h" +int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload); const u8 * eap_hdr_validate(int vendor, EapType eap_type, const struct wpabuf *msg, size_t *plen); struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len, diff --git a/src/eap_common/eap_defs.h b/src/eap_common/eap_defs.h index 0efe7ab77ea31..0d247c4d13a72 100644 --- a/src/eap_common/eap_defs.h +++ b/src/eap_common/eap_defs.h @@ -2,14 +2,8 @@ * EAP server/peer: Shared EAP definitions * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_DEFS_H @@ -66,8 +60,9 @@ 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 */ } EapType; @@ -76,9 +71,13 @@ typedef enum { enum { EAP_VENDOR_IETF = 0, EAP_VENDOR_MICROSOFT = 0x000137 /* Microsoft */, - EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance */ + EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance */, + EAP_VENDOR_HOSTAP = 39068 /* hostapd/wpa_supplicant project */ }; +#define EAP_VENDOR_UNAUTH_TLS EAP_VENDOR_HOSTAP +#define EAP_VENDOR_TYPE_UNAUTH_TLS 1 + #define EAP_MSK_LEN 64 #define EAP_EMSK_LEN 64 diff --git a/src/eap_common/eap_fast_common.c b/src/eap_common/eap_fast_common.c index 4de34a87b611d..04b987d237256 100644 --- a/src/eap_common/eap_fast_common.c +++ b/src/eap_common/eap_fast_common.c @@ -2,14 +2,8 @@ * EAP-FAST common helper functions (RFC 4851) * Copyright (c) 2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -133,9 +127,9 @@ u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn, wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: master_secret for key " "expansion", keys.master_key, keys.master_key_len); - if (tls_prf(keys.master_key, keys.master_key_len, - label, rnd, keys.client_random_len + - keys.server_random_len, out, block_size + len)) + if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len, + label, rnd, keys.client_random_len + + keys.server_random_len, out, block_size + len)) goto fail; os_free(rnd); os_memmove(out, out + block_size, len); diff --git a/src/eap_common/eap_fast_common.h b/src/eap_common/eap_fast_common.h index c85fd37fd469c..895561747b6bd 100644 --- a/src/eap_common/eap_fast_common.h +++ b/src/eap_common/eap_fast_common.h @@ -2,14 +2,8 @@ * EAP-FAST definitions (RFC 4851) * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_FAST_H diff --git a/src/eap_common/eap_gpsk_common.c b/src/eap_common/eap_gpsk_common.c index 4076262880613..7d106dd062321 100644 --- a/src/eap_common/eap_gpsk_common.c +++ b/src/eap_common/eap_gpsk_common.c @@ -2,14 +2,8 @@ * EAP server/peer: EAP-GPSK shared routines * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/eap_common/eap_gpsk_common.h b/src/eap_common/eap_gpsk_common.h index a30ab97ffa07f..e3d2b6b4aa63e 100644 --- a/src/eap_common/eap_gpsk_common.h +++ b/src/eap_common/eap_gpsk_common.h @@ -2,14 +2,8 @@ * EAP server/peer: EAP-GPSK shared routines * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_GPSK_COMMON_H diff --git a/src/eap_common/eap_ikev2_common.c b/src/eap_common/eap_ikev2_common.c index e9a9c55eb3018..6095fd8ad7399 100644 --- a/src/eap_common/eap_ikev2_common.c +++ b/src/eap_common/eap_ikev2_common.c @@ -2,14 +2,8 @@ * EAP-IKEv2 common routines * Copyright (c) 2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/eap_common/eap_ikev2_common.h b/src/eap_common/eap_ikev2_common.h index a9fc2caae7269..329ccc4d7a1be 100644 --- a/src/eap_common/eap_ikev2_common.h +++ b/src/eap_common/eap_ikev2_common.h @@ -2,14 +2,8 @@ * EAP-IKEv2 definitions * Copyright (c) 2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_IKEV2_COMMON_H diff --git a/src/eap_common/eap_pax_common.c b/src/eap_common/eap_pax_common.c index 32dc80c74dc5b..b3bbacc63ea4f 100644 --- a/src/eap_common/eap_pax_common.c +++ b/src/eap_common/eap_pax_common.c @@ -2,14 +2,8 @@ * EAP server/peer: EAP-PAX shared routines * Copyright (c) 2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/eap_common/eap_pax_common.h b/src/eap_common/eap_pax_common.h index dcc171ec2c0c4..fb03df253fee6 100644 --- a/src/eap_common/eap_pax_common.h +++ b/src/eap_common/eap_pax_common.h @@ -2,14 +2,8 @@ * EAP server/peer: EAP-PAX shared routines * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_PAX_COMMON_H diff --git a/src/eap_common/eap_peap_common.c b/src/eap_common/eap_peap_common.c index 3a64b8ecc44c3..68b8878c93321 100644 --- a/src/eap_common/eap_peap_common.c +++ b/src/eap_common/eap_peap_common.c @@ -1,15 +1,9 @@ /* * EAP-PEAP common routines - * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -18,9 +12,9 @@ #include "crypto/sha1.h" #include "eap_peap_common.h" -void peap_prfplus(int version, const u8 *key, size_t key_len, - const char *label, const u8 *seed, size_t seed_len, - u8 *buf, size_t buf_len) +int peap_prfplus(int version, const u8 *key, size_t key_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *buf, size_t buf_len) { unsigned char counter = 0; size_t pos, plen; @@ -75,7 +69,8 @@ void peap_prfplus(int version, const u8 *key, size_t key_len, while (pos < buf_len) { counter++; plen = buf_len - pos; - hmac_sha1_vector(key, key_len, 5, addr, len, hash); + if (hmac_sha1_vector(key, key_len, 5, addr, len, hash) < 0) + return -1; if (plen >= SHA1_MAC_LEN) { os_memcpy(&buf[pos], hash, SHA1_MAC_LEN); pos += SHA1_MAC_LEN; @@ -85,4 +80,6 @@ void peap_prfplus(int version, const u8 *key, size_t key_len, } len[0] = SHA1_MAC_LEN; } + + return 0; } diff --git a/src/eap_common/eap_peap_common.h b/src/eap_common/eap_peap_common.h index f59afb07d098d..7aad0dff781d9 100644 --- a/src/eap_common/eap_peap_common.h +++ b/src/eap_common/eap_peap_common.h @@ -1,22 +1,16 @@ /* * EAP-PEAP common routines - * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_PEAP_COMMON_H #define EAP_PEAP_COMMON_H -void peap_prfplus(int version, const u8 *key, size_t key_len, - const char *label, const u8 *seed, size_t seed_len, - u8 *buf, size_t buf_len); +int peap_prfplus(int version, const u8 *key, size_t key_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *buf, size_t buf_len); #endif /* EAP_PEAP_COMMON_H */ diff --git a/src/eap_common/eap_psk_common.c b/src/eap_common/eap_psk_common.c index 7417d5c73df5e..638102ffeeae5 100644 --- a/src/eap_common/eap_psk_common.c +++ b/src/eap_common/eap_psk_common.c @@ -2,14 +2,8 @@ * EAP server/peer: EAP-PSK shared routines * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/eap_common/eap_psk_common.h b/src/eap_common/eap_psk_common.h index 8adc0541ee03a..8bc2c3c4cf161 100644 --- a/src/eap_common/eap_psk_common.h +++ b/src/eap_common/eap_psk_common.h @@ -2,14 +2,8 @@ * EAP server/peer: EAP-PSK shared routines * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_PSK_COMMON_H diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c new file mode 100644 index 0000000000000..7d6e6b8898a16 --- /dev/null +++ b/src/eap_common/eap_pwd_common.c @@ -0,0 +1,345 @@ +/* + * EAP server/peer: EAP-pwd shared routines + * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include "common.h" +#include "crypto/sha256.h" +#include "crypto/crypto.h" +#include "eap_defs.h" +#include "eap_pwd_common.h" + +/* The random function H(x) = HMAC-SHA256(0^32, x) */ +struct crypto_hash * eap_pwd_h_init(void) +{ + 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 eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len) +{ + crypto_hash_update(hash, data, len); +} + + +void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest) +{ + size_t len = SHA256_MAC_LEN; + crypto_hash_finish(hash, digest, &len); +} + + +/* a counter-based KDF based on NIST SP800-108 */ +static int eap_pwd_kdf(const u8 *key, size_t keylen, const u8 *label, + size_t labellen, u8 *result, size_t resultbitlen) +{ + struct crypto_hash *hash; + u8 digest[SHA256_MAC_LEN]; + u16 i, ctr, L; + size_t resultbytelen, len = 0, mdlen; + + resultbytelen = (resultbitlen + 7) / 8; + ctr = 0; + L = htons(resultbitlen); + while (len < resultbytelen) { + ctr++; + i = htons(ctr); + hash = crypto_hash_init(CRYPTO_HASH_ALG_HMAC_SHA256, + key, keylen); + if (hash == NULL) + return -1; + if (ctr > 1) + 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; + } + + /* 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; +} + + +/* + * compute a "random" secret point on an elliptic curve based + * on the password and identities. + */ +int compute_password_element(EAP_PWD_group *grp, u16 num, + u8 *password, int password_len, + u8 *id_server, int id_server_len, + u8 *id_peer, int id_peer_len, u8 *token) +{ + BIGNUM *x_candidate = NULL, *rnd = NULL, *cofactor = NULL; + 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: + nid = NID_X9_62_prime256v1; + break; + case 20: + nid = NID_secp384r1; + break; + case 21: + nid = NID_secp521r1; + break; + case 25: + nid = NID_X9_62_prime192v1; + break; + case 26: + nid = NID_secp224r1; + break; + default: + wpa_printf(MSG_INFO, "EAP-pwd: unsupported group %d", num); + return -1; + } + + grp->pwe = NULL; + grp->order = NULL; + grp->prime = NULL; + + if ((grp->group = EC_GROUP_new_by_curve_name(nid)) == NULL) { + wpa_printf(MSG_INFO, "EAP-pwd: unable to create EC_GROUP"); + goto fail; + } + + if (((rnd = BN_new()) == NULL) || + ((cofactor = BN_new()) == NULL) || + ((grp->pwe = EC_POINT_new(grp->group)) == NULL) || + ((grp->order = BN_new()) == NULL) || + ((grp->prime = BN_new()) == NULL) || + ((x_candidate = BN_new()) == NULL)) { + wpa_printf(MSG_INFO, "EAP-pwd: unable to create bignums"); + goto fail; + } + + if (!EC_GROUP_get_curve_GFp(grp->group, grp->prime, NULL, NULL, NULL)) + { + wpa_printf(MSG_INFO, "EAP-pwd: unable to get prime for GFp " + "curve"); + goto fail; + } + if (!EC_GROUP_get_order(grp->group, grp->order, NULL)) { + wpa_printf(MSG_INFO, "EAP-pwd: unable to get order for curve"); + goto fail; + } + if (!EC_GROUP_get_cofactor(grp->group, cofactor, NULL)) { + wpa_printf(MSG_INFO, "EAP-pwd: unable to get cofactor for " + "curve"); + goto fail; + } + primebitlen = BN_num_bits(grp->prime); + primebytelen = BN_num_bytes(grp->prime); + if ((prfbuf = os_malloc(primebytelen)) == NULL) { + wpa_printf(MSG_INFO, "EAP-pwd: unable to malloc space for prf " + "buffer"); + goto fail; + } + os_memset(prfbuf, 0, primebytelen); + ctr = 0; + while (1) { + if (ctr > 30) { + wpa_printf(MSG_INFO, "EAP-pwd: unable to find random " + "point on curve for group %d, something's " + "fishy", num); + goto fail; + } + ctr++; + + /* + * compute counter-mode password value and stretch to prime + * pwd-seed = H(token | peer-id | server-id | password | + * counter) + */ + 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); + + /* + * eap_pwd_kdf() returns a string of bits 0..primebitlen but + * BN_bin2bn will treat that string of bits as a big endian + * number. If the primebitlen is not an even multiple of 8 + * then excessive bits-- those _after_ primebitlen-- so now + * we have to shift right the amount we masked off. + */ + if (primebitlen % 8) + BN_rshift(x_candidate, x_candidate, + (8 - (primebitlen % 8))); + + if (BN_ucmp(x_candidate, grp->prime) >= 0) + continue; + + wpa_hexdump(MSG_DEBUG, "EAP-pwd: x_candidate", + prfbuf, primebytelen); + + /* + * need to unambiguously identify the solution, if there is + * one... + */ + if (BN_is_odd(rnd)) + is_odd = 1; + else + is_odd = 0; + + /* + * solve the quadratic equation, if it's not solvable then we + * don't have a point + */ + if (!EC_POINT_set_compressed_coordinates_GFp(grp->group, + grp->pwe, + x_candidate, + is_odd, NULL)) + continue; + /* + * If there's a solution to the equation then the point must be + * on the curve so why check again explicitly? OpenSSL code + * says this is required by X9.62. We're not X9.62 but it can't + * hurt just to be sure. + */ + if (!EC_POINT_is_on_curve(grp->group, grp->pwe, NULL)) { + wpa_printf(MSG_INFO, "EAP-pwd: point is not on curve"); + continue; + } + + if (BN_cmp(cofactor, BN_value_one())) { + /* make sure the point is not in a small sub-group */ + if (!EC_POINT_mul(grp->group, grp->pwe, NULL, grp->pwe, + cofactor, NULL)) { + wpa_printf(MSG_INFO, "EAP-pwd: cannot " + "multiply generator by order"); + continue; + } + if (EC_POINT_is_at_infinity(grp->group, grp->pwe)) { + wpa_printf(MSG_INFO, "EAP-pwd: point is at " + "infinity"); + continue; + } + } + /* if we got here then we have a new generator. */ + break; + } + wpa_printf(MSG_DEBUG, "EAP-pwd: found a PWE in %d tries", ctr); + grp->group_num = num; + if (0) { + fail: + EC_GROUP_free(grp->group); + grp->group = NULL; + EC_POINT_free(grp->pwe); + grp->pwe = NULL; + BN_free(grp->order); + grp->order = NULL; + BN_free(grp->prime); + grp->prime = NULL; + ret = 1; + } + /* cleanliness and order.... */ + BN_free(cofactor); + BN_free(x_candidate); + BN_free(rnd); + os_free(prfbuf); + + return ret; +} + + +int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, BIGNUM *k, + BIGNUM *peer_scalar, BIGNUM *server_scalar, + u8 *confirm_peer, u8 *confirm_server, + u32 *ciphersuite, u8 *msk, u8 *emsk) +{ + 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; + + if ((cruft = os_malloc(BN_num_bytes(grp->prime))) == NULL) + return -1; + + /* + * first compute the session-id = TypeCode | H(ciphersuite | scal_p | + * scal_s) + */ + session_id[0] = EAP_TYPE_PWD; + 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); + 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); + 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) */ + 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); + 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 */ + 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); + + return 1; +} diff --git a/src/eap_common/eap_pwd_common.h b/src/eap_common/eap_pwd_common.h new file mode 100644 index 0000000000000..816e58ccb3d01 --- /dev/null +++ b/src/eap_common/eap_pwd_common.h @@ -0,0 +1,67 @@ +/* + * EAP server/peer: EAP-pwd shared definitions + * Copyright (c) 2009, Dan Harkins <dharkins@lounge.org> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_PWD_COMMON_H +#define EAP_PWD_COMMON_H + +#include <openssl/bn.h> +#include <openssl/ec.h> +#include <openssl/evp.h> + +/* + * definition of a finite cyclic group + * TODO: support one based on a prime field + */ +typedef struct group_definition_ { + u16 group_num; + EC_GROUP *group; + EC_POINT *pwe; + BIGNUM *order; + BIGNUM *prime; +} EAP_PWD_group; + +/* + * EAP-pwd header, included on all payloads + * L(1 bit) | M(1 bit) | exch(6 bits) | total_length(if L is set) + */ +#define EAP_PWD_HDR_SIZE 1 + +#define EAP_PWD_OPCODE_ID_EXCH 1 +#define EAP_PWD_OPCODE_COMMIT_EXCH 2 +#define EAP_PWD_OPCODE_CONFIRM_EXCH 3 +#define EAP_PWD_GET_LENGTH_BIT(x) ((x) & 0x80) +#define EAP_PWD_SET_LENGTH_BIT(x) ((x) |= 0x80) +#define EAP_PWD_GET_MORE_BIT(x) ((x) & 0x40) +#define EAP_PWD_SET_MORE_BIT(x) ((x) |= 0x40) +#define EAP_PWD_GET_EXCHANGE(x) ((x) & 0x3f) +#define EAP_PWD_SET_EXCHANGE(x,y) ((x) |= (y)) + +/* EAP-pwd-ID payload */ +struct eap_pwd_id { + be16 group_num; + u8 random_function; +#define EAP_PWD_DEFAULT_RAND_FUNC 1 + u8 prf; +#define EAP_PWD_DEFAULT_PRF 1 + u8 token[4]; + u8 prep; +#define EAP_PWD_PREP_NONE 0 +#define EAP_PWD_PREP_MS 1 + u8 identity[0]; /* length inferred from payload */ +} STRUCT_PACKED; + +/* common routines */ +int compute_password_element(EAP_PWD_group *, u16, u8 *, int, u8 *, int, u8 *, + int, u8 *); +int compute_keys(EAP_PWD_group *, BN_CTX *, BIGNUM *, BIGNUM *, BIGNUM *, + u8 *, u8 *, u32 *, u8 *, u8 *); +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_common/eap_sake_common.c b/src/eap_common/eap_sake_common.c index 9002b0ca328a4..a76253d00f606 100644 --- a/src/eap_common/eap_sake_common.c +++ b/src/eap_common/eap_sake_common.c @@ -2,14 +2,8 @@ * EAP server/peer: EAP-SAKE shared routines * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/eap_common/eap_sake_common.h b/src/eap_common/eap_sake_common.h index 201e20729cb0e..9e1e75745a115 100644 --- a/src/eap_common/eap_sake_common.h +++ b/src/eap_common/eap_sake_common.h @@ -2,14 +2,8 @@ * EAP server/peer: EAP-SAKE shared routines * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_SAKE_COMMON_H diff --git a/src/eap_common/eap_sim_common.c b/src/eap_common/eap_sim_common.c index 56b4ded45963e..e1773bf1ace9f 100644 --- a/src/eap_common/eap_sim_common.c +++ b/src/eap_common/eap_sim_common.c @@ -2,14 +2,8 @@ * EAP peer/server: EAP-SIM/AKA/AKA' shared routines * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -20,6 +14,7 @@ #include "crypto/crypto.h" #include "crypto/sha1.h" #include "crypto/sha256.h" +#include "crypto/random.h" #include "eap_common/eap_defs.h" #include "eap_common/eap_sim_common.h" @@ -1121,8 +1116,8 @@ int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv, if (pos == NULL) return -1; msg->iv = (pos - wpabuf_head_u8(msg->buf)) + 4; - if (os_get_random(wpabuf_mhead_u8(msg->buf) + msg->iv, - EAP_SIM_IV_LEN)) { + if (random_get_bytes(wpabuf_mhead_u8(msg->buf) + msg->iv, + EAP_SIM_IV_LEN)) { msg->iv = 0; return -1; } diff --git a/src/eap_common/eap_sim_common.h b/src/eap_common/eap_sim_common.h index 48c8eaac0a097..6021bd2e2baec 100644 --- a/src/eap_common/eap_sim_common.h +++ b/src/eap_common/eap_sim_common.h @@ -2,14 +2,8 @@ * EAP peer/server: EAP-SIM/AKA/AKA' shared routines * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_SIM_COMMON_H diff --git a/src/eap_common/eap_tlv_common.h b/src/eap_common/eap_tlv_common.h index f86015d1795a7..3286055ab7bca 100644 --- a/src/eap_common/eap_tlv_common.h +++ b/src/eap_common/eap_tlv_common.h @@ -2,14 +2,8 @@ * EAP-TLV definitions (draft-josefsson-pppext-eap-tls-eap-10.txt) * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_TLV_COMMON_H diff --git a/src/eap_common/eap_ttls.h b/src/eap_common/eap_ttls.h index 797d084786f3d..17901d42db1e4 100644 --- a/src/eap_common/eap_ttls.h +++ b/src/eap_common/eap_ttls.h @@ -2,14 +2,8 @@ * EAP server/peer: EAP-TTLS (RFC 5281) * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_TTLS_H diff --git a/src/eap_common/eap_wsc_common.c b/src/eap_common/eap_wsc_common.c index 5d4e8cc1140cf..7c1496ec336e3 100644 --- a/src/eap_common/eap_wsc_common.c +++ b/src/eap_common/eap_wsc_common.c @@ -2,14 +2,8 @@ * EAP-WSC common routines for Wi-Fi Protected Setup * Copyright (c) 2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/eap_common/eap_wsc_common.h b/src/eap_common/eap_wsc_common.h index fdf61d317de60..0e7b6530818fe 100644 --- a/src/eap_common/eap_wsc_common.h +++ b/src/eap_common/eap_wsc_common.h @@ -2,14 +2,8 @@ * EAP-WSC definitions for Wi-Fi Protected Setup * Copyright (c) 2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_WSC_COMMON_H diff --git a/src/eap_common/ikev2_common.c b/src/eap_common/ikev2_common.c index 67754d8159d0e..376fcadbcec28 100644 --- a/src/eap_common/ikev2_common.c +++ b/src/eap_common/ikev2_common.c @@ -2,14 +2,8 @@ * IKEv2 common routines for initiator and responder * Copyright (c) 2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -18,6 +12,7 @@ #include "crypto/crypto.h" #include "crypto/md5.h" #include "crypto/sha1.h" +#include "crypto/random.h" #include "ikev2_common.h" @@ -639,7 +634,7 @@ int ikev2_build_encrypted(int encr_id, int integ_id, struct ikev2_keys *keys, phdr->flags = 0; iv = wpabuf_put(msg, iv_len); - if (os_get_random(iv, iv_len)) { + if (random_get_bytes(iv, iv_len)) { wpa_printf(MSG_INFO, "IKEV2: Could not generate IV"); return -1; } diff --git a/src/eap_common/ikev2_common.h b/src/eap_common/ikev2_common.h index c96a070d5bcb8..45c970b6083ab 100644 --- a/src/eap_common/ikev2_common.h +++ b/src/eap_common/ikev2_common.h @@ -2,14 +2,8 @@ * IKEv2 definitions * Copyright (c) 2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef IKEV2_COMMON_H @@ -139,7 +133,7 @@ enum { IKEV2_TRANSFORM_ESN = 5 }; -/* IKEv2 Tranform Type 1 (Encryption Algorithm) */ +/* IKEv2 Transform Type 1 (Encryption Algorithm) */ enum { ENCR_DES_IV64 = 1, ENCR_DES = 2, diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c index b9f186bf17d04..a4c9b250696ba 100644 --- a/src/eap_peer/eap.c +++ b/src/eap_peer/eap.c @@ -1,15 +1,9 @@ /* * 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 program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published 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. * * This file implements the Peer State Machine as defined in RFC 4137. The used * states and state transitions match mostly with the RFC. However, there are @@ -26,6 +20,7 @@ #include "common.h" #include "pcsc_funcs.h" #include "state_machine.h" +#include "ext_password.h" #include "crypto/crypto.h" #include "crypto/tls.h" #include "common/wpa_ctrl.h" @@ -37,6 +32,7 @@ #define STATE_MACHINE_DEBUG_PREFIX "EAP" #define EAP_MAX_AUTH_ROUNDS 50 +#define EAP_CLIENT_TIMEOUT_DEFAULT 60 static Boolean eap_sm_allowMethod(struct eap_sm *sm, int vendor, @@ -86,8 +82,21 @@ 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) { + ext_password_free(sm->ext_pw_buf); + sm->ext_pw_buf = NULL; + if (sm->m == NULL || sm->eap_method_priv == NULL) return; @@ -146,6 +155,7 @@ SM_STATE(EAP, INITIALIZE) sm->methodState = METHOD_NONE; sm->allowNotifications = TRUE; sm->decision = DECISION_FAIL; + sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT; eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout); eapol_set_bool(sm, EAPOL_eapSuccess, FALSE); eapol_set_bool(sm, EAPOL_eapFail, FALSE); @@ -179,6 +189,12 @@ SM_STATE(EAP, DISABLED) { SM_ENTRY(EAP, DISABLED); sm->num_rounds = 0; + /* + * RFC 4137 does not describe clearing of idleWhile here, but doing so + * allows the timer tick to be stopped more quickly when EAP is not in + * use. + */ + eapol_set_int(sm, EAPOL_idleWhile, 0); } @@ -217,6 +233,7 @@ SM_STATE(EAP, GET_METHOD) { int reinit; EapType method; + const struct eap_method *eap_method; SM_ENTRY(EAP, GET_METHOD); @@ -225,18 +242,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 @@ -260,7 +283,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", @@ -268,6 +291,8 @@ SM_STATE(EAP, GET_METHOD) goto nak; } + sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT; + wpa_printf(MSG_DEBUG, "EAP: Initialize selected EAP method: " "vendor %u method %u (%s)", sm->reqVendor, method, sm->m->name); @@ -323,6 +348,7 @@ SM_STATE(EAP, METHOD) { struct wpabuf *eapReqData; struct eap_method_ret ret; + int min_len = 1; SM_ENTRY(EAP, METHOD); if (sm->m == NULL) { @@ -331,6 +357,10 @@ SM_STATE(EAP, METHOD) } eapReqData = eapol_get_eapReqData(sm); + if (sm->m->vendor == EAP_VENDOR_IETF && sm->m->method == EAP_TYPE_LEAP) + min_len = 0; /* LEAP uses EAP-Success without payload */ + if (!eap_hdr_len_valid(eapReqData, min_len)) + return; /* * Get ignore, methodState, decision, allowNotifications, and @@ -419,6 +449,8 @@ SM_STATE(EAP, IDENTITY) SM_ENTRY(EAP, IDENTITY); eapReqData = eapol_get_eapReqData(sm); + if (!eap_hdr_len_valid(eapReqData, 1)) + return; eap_sm_processIdentity(sm, eapReqData); wpabuf_free(sm->eapRespData); sm->eapRespData = NULL; @@ -435,6 +467,8 @@ SM_STATE(EAP, NOTIFICATION) SM_ENTRY(EAP, NOTIFICATION); eapReqData = eapol_get_eapReqData(sm); + if (!eap_hdr_len_valid(eapReqData, 1)) + return; eap_sm_processNotify(sm, eapReqData); wpabuf_free(sm->eapRespData); sm->eapRespData = NULL; @@ -852,13 +886,17 @@ static struct wpabuf * eap_sm_buildNak(struct eap_sm *sm, int id) static void eap_sm_processIdentity(struct eap_sm *sm, const struct wpabuf *req) { - const struct eap_hdr *hdr = wpabuf_head(req); - const u8 *pos = (const u8 *) (hdr + 1); - pos++; + const u8 *pos; + size_t msg_len; wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED "EAP authentication started"); + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, req, + &msg_len); + if (pos == NULL) + return; + /* * RFC 3748 - 5.1: Identity * Data field may contain a displayable message in UTF-8. If this @@ -869,15 +907,78 @@ static void eap_sm_processIdentity(struct eap_sm *sm, const struct wpabuf *req) /* TODO: could save displayable message so that it can be shown to the * user in case of interaction is required */ wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Identity data", - pos, be_to_host16(hdr->length) - 5); + pos, msg_len); } #ifdef PCSC_FUNCS + +/* + * Rules for figuring out MNC length based on IMSI for SIM cards that do not + * include MNC length field. + */ +static int mnc_len_from_imsi(const char *imsi) +{ + char mcc_str[4]; + unsigned int mcc; + + os_memcpy(mcc_str, imsi, 3); + mcc_str[3] = '\0'; + mcc = atoi(mcc_str); + + if (mcc == 244) + return 2; /* Networks in Finland use 2-digit MNC */ + + return -1; +} + + +static int eap_sm_append_3gpp_realm(struct eap_sm *sm, char *imsi, + size_t max_len, size_t *imsi_len) +{ + int mnc_len; + char *pos, mnc[4]; + + if (*imsi_len + 36 > max_len) { + wpa_printf(MSG_WARNING, "No room for realm in IMSI buffer"); + return -1; + } + + /* MNC (2 or 3 digits) */ + mnc_len = scard_get_mnc_len(sm->scard_ctx); + if (mnc_len < 0) + mnc_len = mnc_len_from_imsi(imsi); + if (mnc_len < 0) { + wpa_printf(MSG_INFO, "Failed to get MNC length from (U)SIM " + "assuming 3"); + mnc_len = 3; + } + + if (mnc_len == 2) { + mnc[0] = '0'; + mnc[1] = imsi[3]; + mnc[2] = imsi[4]; + } else if (mnc_len == 3) { + mnc[0] = imsi[3]; + mnc[1] = imsi[4]; + mnc[2] = imsi[5]; + } + mnc[3] = '\0'; + + pos = imsi + *imsi_len; + pos += os_snprintf(pos, imsi + max_len - pos, + "@wlan.mnc%s.mcc%c%c%c.3gppnetwork.org", + mnc, imsi[0], imsi[1], imsi[2]); + *imsi_len = pos - imsi; + + return 0; +} + + static int eap_sm_imsi_identity(struct eap_sm *sm, struct eap_peer_config *conf) { - 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; @@ -891,11 +992,28 @@ static int eap_sm_imsi_identity(struct eap_sm *sm, wpa_hexdump_ascii(MSG_DEBUG, "IMSI", (u8 *) imsi, imsi_len); + if (imsi_len < 7) { + wpa_printf(MSG_WARNING, "Too short IMSI for SIM identity"); + return -1; + } + + if (eap_sm_append_3gpp_realm(sm, imsi, sizeof(imsi), &imsi_len) < 0) { + wpa_printf(MSG_WARNING, "Could not add realm to SIM identity"); + return -1; + } + wpa_hexdump_ascii(MSG_DEBUG, "IMSI + realm", (u8 *) imsi, imsi_len); + for (i = 0; m && (m[i].vendor != EAP_VENDOR_IETF || m[i].method != EAP_TYPE_NONE); i++) { if (m[i].vendor == EAP_VENDOR_IETF && + m[i].method == EAP_TYPE_AKA_PRIME) { + method = EAP_SM_AKA_PRIME; + break; + } + + if (m[i].vendor == EAP_VENDOR_IETF && m[i].method == EAP_TYPE_AKA) { - aka = 1; + method = EAP_SM_AKA; break; } } @@ -908,12 +1026,23 @@ 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; return 0; } + #endif /* PCSC_FUNCS */ @@ -1146,10 +1275,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: @@ -1165,9 +1296,12 @@ static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev, { struct eap_sm *sm = ctx; char *hash_hex = NULL; - char *cert_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'", @@ -1175,8 +1309,13 @@ 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) + break; + if (data->peer_cert.hash) { size_t len = data->peer_cert.hash_len * 2 + 1; hash_hex = os_malloc(len); @@ -1186,31 +1325,23 @@ static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev, data->peer_cert.hash_len); } } - wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PEER_CERT - "depth=%d subject='%s'%s%s", - data->peer_cert.depth, data->peer_cert.subject, - hash_hex ? " hash=" : "", hash_hex ? hash_hex : ""); - - if (data->peer_cert.cert) { - size_t len = wpabuf_len(data->peer_cert.cert) * 2 + 1; - cert_hex = os_malloc(len); - if (cert_hex == NULL) - break; - wpa_snprintf_hex(cert_hex, len, - wpabuf_head(data->peer_cert.cert), - wpabuf_len(data->peer_cert.cert)); - wpa_msg_ctrl(sm->msg_ctx, MSG_INFO, - WPA_EVENT_EAP_PEER_CERT - "depth=%d subject='%s' cert=%s", - data->peer_cert.depth, - data->peer_cert.subject, - cert_hex); - } + + sm->eapol_cb->notify_cert(sm->eapol_ctx, + data->peer_cert.depth, + 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); - os_free(cert_hex); } @@ -1241,7 +1372,7 @@ struct eap_sm * eap_peer_sm_init(void *eapol_ctx, sm->eapol_ctx = eapol_ctx; sm->eapol_cb = eapol_cb; sm->msg_ctx = msg_ctx; - sm->ClientTimeout = 60; + sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT; sm->wps = conf->wps; os_memset(&tlsconf, 0, sizeof(tlsconf)); @@ -1253,6 +1384,7 @@ struct eap_sm * eap_peer_sm_init(void *eapol_ctx, #endif /* CONFIG_FIPS */ tlsconf.event_cb = eap_peer_sm_tls_event; tlsconf.cb_ctx = sm; + tlsconf.cert_in_cb = conf->cert_in_cb; sm->ssl_ctx = tls_init(&tlsconf); if (sm->ssl_ctx == NULL) { wpa_printf(MSG_WARNING, "SSL: Failed to initialize TLS " @@ -1261,6 +1393,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; } @@ -1278,6 +1417,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); } @@ -1477,16 +1618,11 @@ int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose) #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) -typedef enum { - TYPE_IDENTITY, TYPE_PASSWORD, TYPE_OTP, TYPE_PIN, TYPE_NEW_PASSWORD, - TYPE_PASSPHRASE -} eap_ctrl_req_type; - -static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type, +static void eap_sm_request(struct eap_sm *sm, enum wpa_ctrl_req_type field, const char *msg, size_t msglen) { struct eap_peer_config *config; - char *field, *txt, *tmp; + char *txt = NULL, *tmp; if (sm == NULL) return; @@ -1494,29 +1630,20 @@ static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type, if (config == NULL) return; - switch (type) { - case TYPE_IDENTITY: - field = "IDENTITY"; - txt = "Identity"; + switch (field) { + case WPA_CTRL_REQ_EAP_IDENTITY: config->pending_req_identity++; break; - case TYPE_PASSWORD: - field = "PASSWORD"; - txt = "Password"; + case WPA_CTRL_REQ_EAP_PASSWORD: config->pending_req_password++; break; - case TYPE_NEW_PASSWORD: - field = "NEW_PASSWORD"; - txt = "New Password"; + case WPA_CTRL_REQ_EAP_NEW_PASSWORD: config->pending_req_new_password++; break; - case TYPE_PIN: - field = "PIN"; - txt = "PIN"; + case WPA_CTRL_REQ_EAP_PIN: config->pending_req_pin++; break; - case TYPE_OTP: - field = "OTP"; + case WPA_CTRL_REQ_EAP_OTP: if (msg) { tmp = os_malloc(msglen + 3); if (tmp == NULL) @@ -1535,9 +1662,7 @@ static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type, txt = config->pending_req_otp; } break; - case TYPE_PASSPHRASE: - field = "PASSPHRASE"; - txt = "Private key passphrase"; + case WPA_CTRL_REQ_EAP_PASSPHRASE: config->pending_req_passphrase++; break; default: @@ -1551,6 +1676,13 @@ static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type, #define eap_sm_request(sm, type, msg, msglen) do { } while (0) #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ +const char * eap_sm_get_method_name(struct eap_sm *sm) +{ + if (sm->m == NULL) + return "UNKNOWN"; + return sm->m->name; +} + /** * eap_sm_request_identity - Request identity from user (ctrl_iface) @@ -1563,7 +1695,7 @@ static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type, */ void eap_sm_request_identity(struct eap_sm *sm) { - eap_sm_request(sm, TYPE_IDENTITY, NULL, 0); + eap_sm_request(sm, WPA_CTRL_REQ_EAP_IDENTITY, NULL, 0); } @@ -1578,7 +1710,7 @@ void eap_sm_request_identity(struct eap_sm *sm) */ void eap_sm_request_password(struct eap_sm *sm) { - eap_sm_request(sm, TYPE_PASSWORD, NULL, 0); + eap_sm_request(sm, WPA_CTRL_REQ_EAP_PASSWORD, NULL, 0); } @@ -1593,7 +1725,7 @@ void eap_sm_request_password(struct eap_sm *sm) */ void eap_sm_request_new_password(struct eap_sm *sm) { - eap_sm_request(sm, TYPE_NEW_PASSWORD, NULL, 0); + eap_sm_request(sm, WPA_CTRL_REQ_EAP_NEW_PASSWORD, NULL, 0); } @@ -1608,7 +1740,7 @@ void eap_sm_request_new_password(struct eap_sm *sm) */ void eap_sm_request_pin(struct eap_sm *sm) { - eap_sm_request(sm, TYPE_PIN, NULL, 0); + eap_sm_request(sm, WPA_CTRL_REQ_EAP_PIN, NULL, 0); } @@ -1624,7 +1756,7 @@ void eap_sm_request_pin(struct eap_sm *sm) */ void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len) { - eap_sm_request(sm, TYPE_OTP, msg, msg_len); + eap_sm_request(sm, WPA_CTRL_REQ_EAP_OTP, msg, msg_len); } @@ -1639,7 +1771,7 @@ void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len) */ void eap_sm_request_passphrase(struct eap_sm *sm) { - eap_sm_request(sm, TYPE_PASSPHRASE, NULL, 0); + eap_sm_request(sm, WPA_CTRL_REQ_EAP_PASSPHRASE, NULL, 0); } @@ -1806,6 +1938,27 @@ const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len) } +static int eap_get_ext_password(struct eap_sm *sm, + struct eap_peer_config *config) +{ + char *name; + + if (config->password == NULL) + return -1; + + name = os_zalloc(config->password_len + 1); + if (name == NULL) + return -1; + os_memcpy(name, config->password, config->password_len); + + ext_password_free(sm->ext_pw_buf); + sm->ext_pw_buf = ext_password_get(sm->ext_pw, name); + os_free(name); + + return sm->ext_pw_buf == NULL ? -1 : 0; +} + + /** * eap_get_config_password - Get password from the network configuration * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() @@ -1817,6 +1970,14 @@ const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len) struct eap_peer_config *config = eap_get_config(sm); if (config == NULL) return NULL; + + if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { + if (eap_get_ext_password(sm, config) < 0) + return NULL; + *len = wpabuf_len(sm->ext_pw_buf); + return wpabuf_head(sm->ext_pw_buf); + } + *len = config->password_len; return config->password; } @@ -1836,6 +1997,14 @@ const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash) struct eap_peer_config *config = eap_get_config(sm); if (config == NULL) return NULL; + + if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { + if (eap_get_ext_password(sm, config) < 0) + return NULL; + *len = wpabuf_len(sm->ext_pw_buf); + return wpabuf_head(sm->ext_pw_buf); + } + *len = config->password_len; if (hash) *hash = !!(config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH); @@ -1923,6 +2092,15 @@ const char * eap_get_config_phase2(struct eap_sm *sm) } +int eap_get_config_fragment_size(struct eap_sm *sm) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return -1; + return config->fragment_size; +} + + /** * eap_key_available - Get key availability (eapKeyAvailable variable) * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() @@ -2138,3 +2316,24 @@ int eap_is_wps_pin_enrollee(struct eap_peer_config *conf) return 1; } + + +void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext) +{ + ext_password_free(sm->ext_pw_buf); + sm->ext_pw_buf = NULL; + sm->ext_pw = ext; +} + + +/** + * eap_set_anon_id - Set or add anonymous identity + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @id: Anonymous identity (e.g., EAP-SIM pseudonym) or %NULL to clear + * @len: Length of anonymous identity in octets + */ +void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len) +{ + if (sm->eapol_cb->set_anon_id) + sm->eapol_cb->set_anon_id(sm->eapol_ctx, id, len); +} diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h index 40d0b6929ce95..8bccef1b17d42 100644 --- a/src/eap_peer/eap.h +++ b/src/eap_peer/eap.h @@ -1,15 +1,9 @@ /* * EAP peer state machine functions (RFC 4137) - * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-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 - * published 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. */ #ifndef EAP_H @@ -216,11 +210,39 @@ struct eapol_callbacks { /** * eap_param_needed - Notify that EAP parameter is needed * @ctx: eapol_ctx from eap_peer_sm_init() call - * @field: Field name (e.g., "IDENTITY") + * @field: Field indicator (e.g., WPA_CTRL_REQ_EAP_IDENTITY) * @txt: User readable text describing the required parameter */ - void (*eap_param_needed)(void *ctx, const char *field, + void (*eap_param_needed)(void *ctx, enum wpa_ctrl_req_type field, const char *txt); + + /** + * notify_cert - Notification of a peer certificate + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @depth: Depth in certificate chain (0 = server) + * @subject: Subject of the peer certificate + * @cert_hash: SHA-256 hash of the certificate + * @cert: Peer certificate + */ + 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); + + /** + * set_anon_id - Set or add anonymous identity + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @id: Anonymous identity (e.g., EAP-SIM pseudonym) or %NULL to clear + * @len: Length of anonymous identity in octets + */ + void (*set_anon_id)(void *ctx, const u8 *id, size_t len); }; /** @@ -251,6 +273,11 @@ struct eap_config { * This is only used by EAP-WSC and can be left %NULL if not available. */ struct wps_context *wps; + + /** + * cert_in_cb - Include server certificates in callback + */ + int cert_in_cb; }; struct eap_sm * eap_peer_sm_init(void *eapol_ctx, @@ -261,6 +288,7 @@ int eap_peer_sm_step(struct eap_sm *sm); void eap_sm_abort(struct eap_sm *sm); int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose); +const char * eap_sm_get_method_name(struct eap_sm *sm); struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted); void eap_sm_request_identity(struct eap_sm *sm); void eap_sm_request_password(struct eap_sm *sm); @@ -286,6 +314,10 @@ void eap_invalidate_cached_session(struct eap_sm *sm); int eap_is_wps_pbc_enrollee(struct eap_peer_config *conf); int eap_is_wps_pin_enrollee(struct eap_peer_config *conf); +struct ext_password_data; +void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext); +void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len); + #endif /* IEEE8021X_EAPOL */ #endif /* EAP_H */ diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c index 182f01a5e60ad..59861cba1dc01 100644 --- a/src/eap_peer/eap_aka.c +++ b/src/eap_peer/eap_aka.c @@ -1,15 +1,9 @@ /* - * 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 program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published 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" @@ -96,6 +90,7 @@ static void * eap_aka_init(struct eap_sm *sm) { struct eap_aka_data *data; const char *phase1 = eap_get_config_phase1(sm); + struct eap_peer_config *config = eap_get_config(sm); data = os_zalloc(sizeof(*data)); if (data == NULL) @@ -108,6 +103,15 @@ static void * eap_aka_init(struct eap_sm *sm) data->result_ind = phase1 && os_strstr(phase1, "result_ind=1") != NULL; + if (config && config->anonymous_identity) { + data->pseudonym = os_malloc(config->anonymous_identity_len); + if (data->pseudonym) { + os_memcpy(data->pseudonym, config->anonymous_identity, + config->anonymous_identity_len); + data->pseudonym_len = config->anonymous_identity_len; + } + } + return data; } @@ -233,23 +237,24 @@ static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data) #define CLEAR_REAUTH_ID 0x02 #define CLEAR_EAP_ID 0x04 -static void eap_aka_clear_identities(struct eap_aka_data *data, int id) +static void eap_aka_clear_identities(struct eap_sm *sm, + struct eap_aka_data *data, int id) { - wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old%s%s%s", - id & CLEAR_PSEUDONYM ? " pseudonym" : "", - id & CLEAR_REAUTH_ID ? " reauth_id" : "", - id & CLEAR_EAP_ID ? " eap_id" : ""); - if (id & CLEAR_PSEUDONYM) { + 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; + eap_set_anon_id(sm, NULL, 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; data->last_eap_identity_len = 0; @@ -257,24 +262,45 @@ static void eap_aka_clear_identities(struct eap_aka_data *data, int id) } -static int eap_aka_learn_ids(struct eap_aka_data *data, +static int eap_aka_learn_ids(struct eap_sm *sm, struct eap_aka_data *data, struct eap_sim_attrs *attr) { if (attr->next_pseudonym) { + const u8 *identity = NULL; + size_t identity_len = 0; + const u8 *realm = NULL; + size_t realm_len = 0; + + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-AKA: (encr) AT_NEXT_PSEUDONYM", + attr->next_pseudonym, + attr->next_pseudonym_len); os_free(data->pseudonym); - data->pseudonym = os_malloc(attr->next_pseudonym_len); + /* Look for the realm of the permanent identity */ + identity = eap_get_config_identity(sm, &identity_len); + if (identity) { + for (realm = identity, realm_len = identity_len; + realm_len > 0; realm_len--, realm++) { + if (*realm == '@') + break; + } + } + data->pseudonym = os_malloc(attr->next_pseudonym_len + + realm_len); if (data->pseudonym == NULL) { wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for " "next pseudonym"); + data->pseudonym_len = 0; return -1; } os_memcpy(data->pseudonym, attr->next_pseudonym, attr->next_pseudonym_len); - data->pseudonym_len = attr->next_pseudonym_len; - wpa_hexdump_ascii(MSG_DEBUG, - "EAP-AKA: (encr) AT_NEXT_PSEUDONYM", - data->pseudonym, - data->pseudonym_len); + if (realm_len) { + os_memcpy(data->pseudonym + attr->next_pseudonym_len, + realm, realm_len); + } + data->pseudonym_len = attr->next_pseudonym_len + realm_len; + eap_set_anon_id(sm, data->pseudonym, data->pseudonym_len); } if (attr->next_reauth_id) { @@ -283,6 +309,7 @@ static int eap_aka_learn_ids(struct eap_aka_data *data, if (data->reauth_id == NULL) { wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for " "next reauth_id"); + data->reauth_id_len = 0; return -1; } os_memcpy(data->reauth_id, attr->next_reauth_id, @@ -411,6 +438,8 @@ static struct wpabuf * eap_aka_client_error(struct eap_aka_data *data, u8 id, data->num_id_req = 0; data->num_notification = 0; + wpa_printf(MSG_DEBUG, "EAP-AKA: Send Client-Error (error code %d)", + err); msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, EAP_AKA_SUBTYPE_CLIENT_ERROR); eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0); @@ -472,16 +501,16 @@ static struct wpabuf * eap_aka_response_identity(struct eap_sm *sm, data->pseudonym) { identity = data->pseudonym; identity_len = data->pseudonym_len; - eap_aka_clear_identities(data, CLEAR_REAUTH_ID); + eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID); } else if (id_req != NO_ID_REQ) { identity = eap_get_config_identity(sm, &identity_len); if (identity) { - eap_aka_clear_identities(data, CLEAR_PSEUDONYM | + eap_aka_clear_identities(sm, data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID); } } if (id_req != NO_ID_REQ) - eap_aka_clear_identities(data, CLEAR_EAP_ID); + eap_aka_clear_identities(sm, data, CLEAR_EAP_ID); wpa_printf(MSG_DEBUG, "Generating EAP-AKA Identity (id=%d)", id); msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, @@ -880,11 +909,11 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, EAP_AKA_UNABLE_TO_PROCESS_PACKET); } - /* Old reauthentication and pseudonym identities must not be used - * anymore. In other words, if no new identities are received, full - * authentication will be used on next reauthentication. */ - eap_aka_clear_identities(data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID | - CLEAR_EAP_ID); + /* Old reauthentication identity must not be used anymore. In + * other words, if no new identities are received, full + * authentication will be used on next reauthentication (using + * pseudonym identity or permanent identity). */ + eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); if (attr->encr_data) { u8 *decrypted; @@ -895,7 +924,7 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, return eap_aka_client_error( data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); } - eap_aka_learn_ids(data, &eattr); + eap_aka_learn_ids(sm, data, &eattr); os_free(decrypted); } @@ -1112,8 +1141,8 @@ static struct wpabuf * eap_aka_process_reauthentication( data->nonce_s, data->mk, data->msk, data->emsk); } - eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); - eap_aka_learn_ids(data, &eattr); + eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + eap_aka_learn_ids(sm, data, &eattr); if (data->result_ind && attr->result_ind) data->use_result_ind = 1; @@ -1128,7 +1157,8 @@ static struct wpabuf * eap_aka_process_reauthentication( if (data->counter > EAP_AKA_MAX_FAST_REAUTHS) { wpa_printf(MSG_DEBUG, "EAP-AKA: Maximum number of " "fast reauths performed - force fullauth"); - eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + eap_aka_clear_identities(sm, data, + CLEAR_REAUTH_ID | CLEAR_EAP_ID); } os_free(decrypted); return eap_aka_response_reauth(data, id, 0, data->nonce_s); @@ -1246,7 +1276,7 @@ static Boolean eap_aka_has_reauth_data(struct eap_sm *sm, void *priv) static void eap_aka_deinit_for_reauth(struct eap_sm *sm, void *priv) { struct eap_aka_data *data = priv; - eap_aka_clear_identities(data, CLEAR_EAP_ID); + eap_aka_clear_identities(sm, data, CLEAR_EAP_ID); data->prev_id = -1; wpabuf_free(data->id_msgs); data->id_msgs = NULL; diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h index b64b68f4b76c2..ed909198b1158 100644 --- a/src/eap_peer/eap_config.h +++ b/src/eap_peer/eap_config.h @@ -2,14 +2,8 @@ * EAP peer configuration data * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_CONFIG_H @@ -41,6 +35,9 @@ struct eap_peer_config { * * If not set, the identity field will be used for both unencrypted and * protected fields. + * + * This field can also be used with EAP-SIM/AKA/AKA' to store the + * pseudonym identity. */ u8 *anonymous_identity; @@ -625,6 +622,7 @@ struct eap_peer_config { int fragment_size; #define EAP_CONFIG_FLAGS_PASSWORD_NTHASH BIT(0) +#define EAP_CONFIG_FLAGS_EXT_PASSWORD BIT(1) /** * flags - Network configuration flags (bitfield) * @@ -632,6 +630,8 @@ struct eap_peer_config { * for the network parameters. * bit 0 = password is represented as a 16-byte NtPasswordHash value * instead of plaintext password + * bit 1 = password is stored in external storage; the value in the + * password field is the name of that external entry */ u32 flags; }; diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c index 5d3e69d3cdfe6..7ca5288ca26e3 100644 --- a/src/eap_peer/eap_fast.c +++ b/src/eap_peer/eap_fast.c @@ -2,14 +2,8 @@ * EAP peer method: EAP-FAST (RFC 4851) * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -175,7 +169,7 @@ static void * eap_fast_init(struct eap_sm *sm) data->phase2_type.vendor = EAP_VENDOR_IETF; data->phase2_type.method = EAP_TYPE_NONE; - if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) { + if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_FAST)) { wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize SSL."); eap_fast_deinit(sm, data); return NULL; @@ -444,8 +438,9 @@ static int eap_fast_phase2_request(struct eap_sm *sm, return 0; } - if (data->phase2_priv == NULL && - eap_fast_init_phase2_method(sm, data) < 0) { + if ((data->phase2_priv == NULL && + eap_fast_init_phase2_method(sm, data) < 0) || + data->phase2_method == NULL) { wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize " "Phase 2 EAP method %d", *pos); ret->methodState = METHOD_DONE; @@ -542,7 +537,7 @@ static struct wpabuf * eap_fast_tlv_pac_ack(void) static struct wpabuf * eap_fast_process_eap_payload_tlv( struct eap_sm *sm, struct eap_fast_data *data, - struct eap_method_ret *ret, const struct eap_hdr *req, + struct eap_method_ret *ret, u8 *eap_payload_tlv, size_t eap_payload_tlv_len) { struct eap_hdr *hdr; @@ -1037,11 +1032,15 @@ static struct wpabuf * eap_fast_process_pac(struct eap_sm *sm, } else { /* * This is PAC refreshing, i.e., normal authentication that is - * expected to be completed with an EAP-Success. + * expected to be completed with an EAP-Success. However, + * RFC 5422, Section 3.5 allows EAP-Failure to be sent even + * after protected success exchange in case of EAP-Fast + * provisioning, so we better use DECISION_COND_SUCC here + * instead of DECISION_UNCOND_SUCC. */ wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV " "- PAC refreshing completed successfully"); - ret->decision = DECISION_UNCOND_SUCC; + ret->decision = DECISION_COND_SUCC; } ret->methodState = METHOD_DONE; return eap_fast_tlv_pac_ack(); @@ -1184,7 +1183,7 @@ static int eap_fast_process_decrypted(struct eap_sm *sm, if (tlv.eap_payload_tlv) { tmp = eap_fast_process_eap_payload_tlv( - sm, data, ret, req, tlv.eap_payload_tlv, + sm, data, ret, tlv.eap_payload_tlv, tlv.eap_payload_tlv_len); resp = wpabuf_concat(resp, tmp); } diff --git a/src/eap_peer/eap_fast_pac.c b/src/eap_peer/eap_fast_pac.c index 541cce50dc019..8c480b967974d 100644 --- a/src/eap_peer/eap_fast_pac.c +++ b/src/eap_peer/eap_fast_pac.c @@ -2,14 +2,8 @@ * EAP peer method: EAP-FAST PAC file processing * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -428,8 +422,12 @@ int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_pac **pac_root, if (eap_fast_init_pac_data(sm, pac_file, &rc) < 0) return 0; - if (eap_fast_read_line(&rc, &pos) < 0 || - os_strcmp(pac_file_hdr, rc.buf) != 0) + if (eap_fast_read_line(&rc, &pos) < 0) { + /* empty file - assume it is fine to overwrite */ + eap_fast_deinit_pac_data(&rc); + return 0; + } + if (os_strcmp(pac_file_hdr, rc.buf) != 0) err = "Unrecognized header line"; while (!err && eap_fast_read_line(&rc, &pos) == 0) { @@ -497,6 +495,7 @@ static void eap_fast_write(char **buf, char **pos, size_t *buf_len, *buf = NULL; return; } + *pos = nbuf + (*pos - *buf); *buf = nbuf; *buf_len += need; } diff --git a/src/eap_peer/eap_fast_pac.h b/src/eap_peer/eap_fast_pac.h index 9483f96852c65..8815d9168d008 100644 --- a/src/eap_peer/eap_fast_pac.h +++ b/src/eap_peer/eap_fast_pac.h @@ -2,14 +2,8 @@ * EAP peer method: EAP-FAST PAC file processing * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_FAST_PAC_H diff --git a/src/eap_peer/eap_gpsk.c b/src/eap_peer/eap_gpsk.c index f6a1955baff1a..2bd0d480f8e45 100644 --- a/src/eap_peer/eap_gpsk.c +++ b/src/eap_peer/eap_gpsk.c @@ -2,19 +2,14 @@ * EAP peer method: EAP-GPSK (RFC 5433) * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#include "crypto/random.h" #include "eap_peer/eap_i.h" #include "eap_common/eap_gpsk_common.h" @@ -326,7 +321,7 @@ static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data, wpabuf_put_be16(resp, data->id_server_len); wpabuf_put_data(resp, data->id_server, data->id_server_len); - if (os_get_random(data->rand_peer, EAP_GPSK_RAND_LEN)) { + if (random_get_bytes(data->rand_peer, EAP_GPSK_RAND_LEN)) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to get random data " "for RAND_Peer"); eap_gpsk_state(data, FAILURE); diff --git a/src/eap_peer/eap_gtc.c b/src/eap_peer/eap_gtc.c index b2b554b4478b6..9f3cfbdacadc8 100644 --- a/src/eap_peer/eap_gtc.c +++ b/src/eap_peer/eap_gtc.c @@ -2,14 +2,8 @@ * EAP peer method: EAP-GTC (RFC 3748) * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h index e7c826ee8feac..dd943174e444b 100644 --- a/src/eap_peer/eap_i.h +++ b/src/eap_peer/eap_i.h @@ -2,14 +2,8 @@ * EAP peer state machines internal structures (RFC 4137) * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_I_H @@ -323,6 +317,7 @@ struct eap_sm { void *msg_ctx; void *scard_ctx; void *ssl_ctx; + void *ssl_ctx2; unsigned int workaround; @@ -335,6 +330,9 @@ struct eap_sm { struct wps_context *wps; int prev_failure; + + struct ext_password_data *ext_pw; + struct wpabuf *ext_pw_buf; }; const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len); @@ -345,6 +343,7 @@ const u8 * eap_get_config_otp(struct eap_sm *sm, size_t *len); void eap_clear_config_otp(struct eap_sm *sm); const char * eap_get_config_phase1(struct eap_sm *sm); const char * eap_get_config_phase2(struct eap_sm *sm); +int eap_get_config_fragment_size(struct eap_sm *sm); struct eap_peer_config * eap_get_config(struct eap_sm *sm); void eap_set_config_blob(struct eap_sm *sm, struct wpa_config_blob *blob); const struct wpa_config_blob * diff --git a/src/eap_peer/eap_ikev2.c b/src/eap_peer/eap_ikev2.c index bb49a662d72d0..a227f8b14ed04 100644 --- a/src/eap_peer/eap_ikev2.c +++ b/src/eap_peer/eap_ikev2.c @@ -2,14 +2,8 @@ * EAP-IKEv2 peer (RFC 5106) * Copyright (c) 2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/eap_peer/eap_leap.c b/src/eap_peer/eap_leap.c index a7c94a4d4480d..df3401384cf97 100644 --- a/src/eap_peer/eap_leap.c +++ b/src/eap_peer/eap_leap.c @@ -2,14 +2,8 @@ * EAP peer method: LEAP * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -17,6 +11,7 @@ #include "common.h" #include "crypto/ms_funcs.h" #include "crypto/crypto.h" +#include "crypto/random.h" #include "eap_i.h" #define LEAP_VERSION 1 @@ -167,7 +162,7 @@ static struct wpabuf * eap_leap_process_success(struct eap_sm *sm, void *priv, wpabuf_put_u8(resp, 0); /* unused */ wpabuf_put_u8(resp, LEAP_CHALLENGE_LEN); pos = wpabuf_put(resp, LEAP_CHALLENGE_LEN); - if (os_get_random(pos, LEAP_CHALLENGE_LEN)) { + if (random_get_bytes(pos, LEAP_CHALLENGE_LEN)) { wpa_printf(MSG_WARNING, "EAP-LEAP: Failed to read random data " "for challenge"); wpabuf_free(resp); diff --git a/src/eap_peer/eap_md5.c b/src/eap_peer/eap_md5.c index 0edbae8f74d37..d06befaeb1df5 100644 --- a/src/eap_peer/eap_md5.c +++ b/src/eap_peer/eap_md5.c @@ -1,15 +1,9 @@ /* * EAP peer method: EAP-MD5 (RFC 3748 and RFC 1994) - * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-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 - * published 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" @@ -92,7 +86,13 @@ static struct wpabuf * eap_md5_process(struct eap_sm *sm, void *priv, id = eap_get_id(resp); rpos = wpabuf_put(resp, CHAP_MD5_LEN); - chap_md5(id, password, password_len, challenge, challenge_len, rpos); + if (chap_md5(id, password, password_len, challenge, challenge_len, + rpos)) { + wpa_printf(MSG_INFO, "EAP-MD5: CHAP MD5 operation failed"); + ret->ignore = TRUE; + wpabuf_free(resp); + return NULL; + } wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", rpos, CHAP_MD5_LEN); return resp; diff --git a/src/eap_peer/eap_methods.c b/src/eap_peer/eap_methods.c index 3b0af055b39c7..83a1457964a24 100644 --- a/src/eap_peer/eap_methods.c +++ b/src/eap_peer/eap_methods.c @@ -2,14 +2,8 @@ * EAP peer: Method registration * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -77,6 +71,8 @@ EapType eap_peer_get_type(const char *name, int *vendor) const char * eap_get_name(int vendor, EapType type) { struct eap_method *m; + if (vendor == EAP_VENDOR_IETF && type == EAP_TYPE_EXPANDED) + return "expanded"; for (m = eap_methods; m; m = m->next) { if (m->vendor == vendor && m->method == type) return m->name; diff --git a/src/eap_peer/eap_methods.h b/src/eap_peer/eap_methods.h index 384c61bb70ae1..4994ff1cd9a0c 100644 --- a/src/eap_peer/eap_methods.h +++ b/src/eap_peer/eap_methods.h @@ -2,14 +2,8 @@ * EAP peer: Method registration * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_METHODS_H @@ -91,6 +85,7 @@ static inline int eap_peer_method_unload(struct eap_method *method) /* EAP peer method registration calls for statically linked in methods */ int eap_peer_md5_register(void); int eap_peer_tls_register(void); +int eap_peer_unauth_tls_register(void); int eap_peer_mschapv2_register(void); int eap_peer_peap_register(void); int eap_peer_ttls_register(void); @@ -109,5 +104,6 @@ int eap_peer_wsc_register(void); int eap_peer_ikev2_register(void); int eap_peer_vendor_test_register(void); int eap_peer_tnc_register(void); +int eap_peer_pwd_register(void); #endif /* EAP_METHODS_H */ diff --git a/src/eap_peer/eap_mschapv2.c b/src/eap_peer/eap_mschapv2.c index cd410d91b5df8..fb6c282a16250 100644 --- a/src/eap_peer/eap_mschapv2.c +++ b/src/eap_peer/eap_mschapv2.c @@ -2,14 +2,8 @@ * EAP peer method: EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt) * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file implements EAP peer part of EAP-MSCHAPV2 method (EAP type 26). * draft-kamath-pppext-eap-mschapv2-00.txt defines the Microsoft EAP CHAP @@ -23,6 +17,7 @@ #include "common.h" #include "crypto/ms_funcs.h" +#include "crypto/random.h" #include "common/wpa_ctrl.h" #include "mschapv2.h" #include "eap_i.h" @@ -199,7 +194,7 @@ static struct wpabuf * eap_mschapv2_challenge_reply( "in Phase 1"); peer_challenge = data->peer_challenge; os_memset(r->peer_challenge, 0, MSCHAPV2_CHAL_LEN); - } else if (os_get_random(peer_challenge, MSCHAPV2_CHAL_LEN)) { + } else if (random_get_bytes(peer_challenge, MSCHAPV2_CHAL_LEN)) { wpabuf_free(resp); return NULL; } @@ -309,7 +304,9 @@ static void eap_mschapv2_password_changed(struct eap_sm *sm, "EAP-MSCHAPV2: Password changed successfully"); data->prev_error = 0; os_free(config->password); - if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) { + if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { + /* TODO: update external storage */ + } else if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) { config->password = os_malloc(16); config->password_len = 16; if (config->password) { @@ -564,7 +561,7 @@ static struct wpabuf * eap_mschapv2_change_password( } /* Peer-Challenge */ - if (os_get_random(cp->peer_challenge, MSCHAPV2_CHAL_LEN)) + if (random_get_bytes(cp->peer_challenge, MSCHAPV2_CHAL_LEN)) goto fail; /* Reserved, must be zero */ diff --git a/src/eap_peer/eap_otp.c b/src/eap_peer/eap_otp.c index 556c22f9e2d59..9ac744a7ddade 100644 --- a/src/eap_peer/eap_otp.c +++ b/src/eap_peer/eap_otp.c @@ -2,14 +2,8 @@ * EAP peer method: EAP-OTP (RFC 3748) * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/eap_peer/eap_pax.c b/src/eap_peer/eap_pax.c index 2e04831ae53dc..7f870520712b2 100644 --- a/src/eap_peer/eap_pax.c +++ b/src/eap_peer/eap_pax.c @@ -2,19 +2,14 @@ * EAP peer method: EAP-PAX (RFC 4746) * Copyright (c) 2005-2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#include "crypto/random.h" #include "eap_common/eap_pax_common.h" #include "eap_i.h" @@ -174,7 +169,7 @@ static struct wpabuf * eap_pax_process_std_1(struct eap_pax_data *data, pos, left); } - if (os_get_random(data->rand.r.y, EAP_PAX_RAND_LEN)) { + if (random_get_bytes(data->rand.r.y, EAP_PAX_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data"); ret->ignore = TRUE; return NULL; diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c index 2b72084e54337..7fff1458a8af8 100644 --- a/src/eap_peer/eap_peap.c +++ b/src/eap_peer/eap_peap.c @@ -2,14 +2,8 @@ * EAP peer method: EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt) * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -165,7 +159,7 @@ static void * eap_peap_init(struct eap_sm *sm) data->phase2_type.vendor = EAP_VENDOR_IETF; data->phase2_type.method = EAP_TYPE_NONE; - if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) { + if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_PEAP)) { wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL."); eap_peap_deinit(sm, data); return NULL; @@ -196,7 +190,7 @@ static void eap_peap_deinit(struct eap_sm *sm, void *priv) * @nak_type: TLV type (EAP_TLV_*) * Returns: Buffer to the allocated EAP-TLV NAK message or %NULL on failure * - * This funtion builds an EAP-TLV NAK message. The caller is responsible for + * This function builds an EAP-TLV NAK message. The caller is responsible for * freeing the returned buffer. */ static struct wpabuf * eap_tlv_build_nak(int id, u16 nak_type) @@ -285,8 +279,10 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) * in the end of the label just before ISK; is that just a typo?) */ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40); - peap_prfplus(data->peap_version, tk, 40, "Inner Methods Compound Keys", - isk, sizeof(isk), imck, sizeof(imck)); + if (peap_prfplus(data->peap_version, tk, 40, + "Inner Methods Compound Keys", + isk, sizeof(isk), imck, sizeof(imck)) < 0) + return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)", imck, sizeof(imck)); @@ -346,8 +342,8 @@ static int eap_tlv_add_cryptobinding(struct eap_sm *sm, * @status: Status (EAP_TLV_RESULT_SUCCESS or EAP_TLV_RESULT_FAILURE) * Returns: Buffer to the allocated EAP-TLV Result message or %NULL on failure * - * This funtion builds an EAP-TLV Result message. The caller is responsible for - * freeing the returned buffer. + * This function builds an EAP-TLV Result message. The caller is responsible + * for freeing the returned buffer. */ static struct wpabuf * eap_tlv_build_result(struct eap_sm *sm, struct eap_peap_data *data, @@ -1247,9 +1243,12 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) * termination for this label while the one used for deriving * IPMK|CMK did not use null termination. */ - peap_prfplus(data->peap_version, data->ipmk, 40, - "Session Key Generating Function", - (u8 *) "\00", 1, csk, sizeof(csk)); + if (peap_prfplus(data->peap_version, data->ipmk, 40, + "Session Key Generating Function", + (u8 *) "\00", 1, csk, sizeof(csk)) < 0) { + os_free(key); + return NULL; + } wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk)); os_memcpy(key, csk, EAP_TLS_KEY_LEN); wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key", diff --git a/src/eap_peer/eap_psk.c b/src/eap_peer/eap_psk.c index ccf871e13ba0b..d618fcfd68e95 100644 --- a/src/eap_peer/eap_psk.c +++ b/src/eap_peer/eap_psk.c @@ -2,14 +2,8 @@ * EAP peer method: EAP-PSK (RFC 4764) * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * Note: EAP-PSK is an EAP authentication method and as such, completely * different from WPA-PSK. This file is not needed for WPA-PSK functionality. @@ -19,6 +13,7 @@ #include "common.h" #include "crypto/aes_wrap.h" +#include "crypto/random.h" #include "eap_common/eap_psk_common.h" #include "eap_i.h" @@ -130,7 +125,7 @@ static struct wpabuf * eap_psk_process_1(struct eap_psk_data *data, wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_S", data->id_s, data->id_s_len); - if (os_get_random(data->rand_p, EAP_PSK_RAND_LEN)) { + if (random_get_bytes(data->rand_p, EAP_PSK_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data"); ret->ignore = TRUE; return NULL; diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c new file mode 100644 index 0000000000000..267d0a5c69768 --- /dev/null +++ b/src/eap_peer/eap_pwd.c @@ -0,0 +1,922 @@ +/* + * EAP peer method: EAP-pwd (RFC 5931) + * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/sha256.h" +#include "eap_peer/eap_i.h" +#include "eap_common/eap_pwd_common.h" + + +struct eap_pwd_data { + enum { + PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req, SUCCESS, FAILURE + } state; + u8 *id_peer; + size_t id_peer_len; + u8 *id_server; + size_t id_server_len; + u8 *password; + size_t password_len; + u16 group_num; + EAP_PWD_group *grp; + + struct wpabuf *inbuf; + size_t in_frag_pos; + struct wpabuf *outbuf; + size_t out_frag_pos; + size_t mtu; + + BIGNUM *k; + BIGNUM *private_value; + BIGNUM *server_scalar; + BIGNUM *my_scalar; + EC_POINT *my_element; + EC_POINT *server_element; + + u8 msk[EAP_MSK_LEN]; + u8 emsk[EAP_EMSK_LEN]; + + BN_CTX *bnctx; +}; + + +#ifndef CONFIG_NO_STDOUT_DEBUG +static const char * eap_pwd_state_txt(int state) +{ + switch (state) { + case PWD_ID_Req: + return "PWD-ID-Req"; + case PWD_Commit_Req: + return "PWD-Commit-Req"; + case PWD_Confirm_Req: + return "PWD-Confirm-Req"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "PWD-UNK"; + } +} +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + +static void eap_pwd_state(struct eap_pwd_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-PWD: %s -> %s", + eap_pwd_state_txt(data->state), eap_pwd_state_txt(state)); + data->state = state; +} + + +static void * eap_pwd_init(struct eap_sm *sm) +{ + struct eap_pwd_data *data; + const u8 *identity, *password; + size_t identity_len, password_len; + + password = eap_get_config_password(sm, &password_len); + if (password == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: No password configured!"); + return NULL; + } + + identity = eap_get_config_identity(sm, &identity_len); + if (identity == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: No identity configured!"); + return NULL; + } + + if ((data = os_zalloc(sizeof(*data))) == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: memory allocation data fail"); + return NULL; + } + + if ((data->bnctx = BN_CTX_new()) == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail"); + os_free(data); + return NULL; + } + + if ((data->id_peer = os_malloc(identity_len)) == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail"); + BN_CTX_free(data->bnctx); + os_free(data); + return NULL; + } + + os_memcpy(data->id_peer, identity, identity_len); + data->id_peer_len = identity_len; + + if ((data->password = os_malloc(password_len)) == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: memory allocation psk fail"); + BN_CTX_free(data->bnctx); + os_free(data->id_peer); + os_free(data); + return NULL; + } + os_memcpy(data->password, password, password_len); + data->password_len = password_len; + + data->out_frag_pos = data->in_frag_pos = 0; + data->inbuf = data->outbuf = NULL; + data->mtu = 1020; /* default from RFC 5931, make it configurable! */ + + data->state = PWD_ID_Req; + + return data; +} + + +static void eap_pwd_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_pwd_data *data = priv; + + BN_free(data->private_value); + BN_free(data->server_scalar); + BN_free(data->my_scalar); + BN_free(data->k); + BN_CTX_free(data->bnctx); + EC_POINT_free(data->my_element); + EC_POINT_free(data->server_element); + os_free(data->id_peer); + os_free(data->id_server); + os_free(data->password); + if (data->grp) { + EC_GROUP_free(data->grp->group); + EC_POINT_free(data->grp->pwe); + BN_free(data->grp->order); + BN_free(data->grp->prime); + os_free(data->grp); + } + os_free(data); +} + + +static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_pwd_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_MSK_LEN); + if (key == NULL) + return NULL; + + os_memcpy(key, data->msk, EAP_MSK_LEN); + *len = EAP_MSK_LEN; + + return key; +} + + +static void +eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, size_t payload_len) +{ + struct eap_pwd_id *id; + + if (data->state != PWD_ID_Req) { + ret->ignore = TRUE; + eap_pwd_state(data, FAILURE); + return; + } + + if (payload_len < sizeof(struct eap_pwd_id)) { + ret->ignore = TRUE; + eap_pwd_state(data, FAILURE); + return; + } + + id = (struct eap_pwd_id *) payload; + data->group_num = be_to_host16(id->group_num); + if ((id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) || + (id->prf != EAP_PWD_DEFAULT_PRF)) { + ret->ignore = TRUE; + eap_pwd_state(data, FAILURE); + return; + } + + wpa_printf(MSG_DEBUG, "EAP-PWD (peer): using group %d", + data->group_num); + + data->id_server = os_malloc(payload_len - sizeof(struct eap_pwd_id)); + if (data->id_server == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail"); + eap_pwd_state(data, FAILURE); + return; + } + data->id_server_len = payload_len - sizeof(struct eap_pwd_id); + os_memcpy(data->id_server, id->identity, data->id_server_len); + wpa_hexdump_ascii(MSG_INFO, "EAP-PWD (peer): server sent id of", + data->id_server, data->id_server_len); + + if ((data->grp = (EAP_PWD_group *) os_malloc(sizeof(EAP_PWD_group))) == + NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for " + "group"); + eap_pwd_state(data, FAILURE); + return; + } + + /* compute PWE */ + if (compute_password_element(data->grp, data->group_num, + data->password, data->password_len, + data->id_server, data->id_server_len, + data->id_peer, data->id_peer_len, + id->token)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute PWE"); + eap_pwd_state(data, FAILURE); + return; + } + + wpa_printf(MSG_DEBUG, "EAP-PWD (peer): computed %d bit PWE...", + BN_num_bits(data->grp->prime)); + + data->outbuf = wpabuf_alloc(sizeof(struct eap_pwd_id) + + data->id_peer_len); + if (data->outbuf == NULL) { + eap_pwd_state(data, FAILURE); + return; + } + wpabuf_put_be16(data->outbuf, data->group_num); + wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC); + wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF); + wpabuf_put_data(data->outbuf, id->token, sizeof(id->token)); + wpabuf_put_u8(data->outbuf, EAP_PWD_PREP_NONE); + wpabuf_put_data(data->outbuf, data->id_peer, data->id_peer_len); + + eap_pwd_state(data, PWD_Commit_Req); +} + + +static void +eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, size_t payload_len) +{ + EC_POINT *K = NULL, *point = NULL; + BIGNUM *mask = NULL, *x = NULL, *y = NULL, *cofactor = NULL; + u16 offset; + u8 *ptr, *scalar = NULL, *element = NULL; + + if (((data->private_value = BN_new()) == NULL) || + ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) || + ((cofactor = BN_new()) == NULL) || + ((data->my_scalar = BN_new()) == NULL) || + ((mask = BN_new()) == NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): scalar allocation fail"); + goto fin; + } + + if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) { + wpa_printf(MSG_INFO, "EAP-pwd (peer): unable to get cofactor " + "for curve"); + goto fin; + } + + BN_rand_range(data->private_value, data->grp->order); + BN_rand_range(mask, data->grp->order); + BN_add(data->my_scalar, data->private_value, mask); + BN_mod(data->my_scalar, data->my_scalar, data->grp->order, + data->bnctx); + + if (!EC_POINT_mul(data->grp->group, data->my_element, NULL, + data->grp->pwe, mask, data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): element allocation " + "fail"); + eap_pwd_state(data, FAILURE); + goto fin; + } + + if (!EC_POINT_invert(data->grp->group, data->my_element, data->bnctx)) + { + wpa_printf(MSG_INFO, "EAP-PWD (peer): element inversion fail"); + goto fin; + } + BN_free(mask); + + if (((x = BN_new()) == NULL) || + ((y = BN_new()) == NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): point allocation fail"); + goto fin; + } + + /* process the request */ + if (((data->server_scalar = BN_new()) == NULL) || + ((data->k = BN_new()) == NULL) || + ((K = EC_POINT_new(data->grp->group)) == NULL) || + ((point = EC_POINT_new(data->grp->group)) == NULL) || + ((data->server_element = EC_POINT_new(data->grp->group)) == NULL)) + { + wpa_printf(MSG_INFO, "EAP-PWD (peer): peer data allocation " + "fail"); + goto fin; + } + + /* element, x then y, followed by scalar */ + ptr = (u8 *) payload; + BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x); + ptr += BN_num_bytes(data->grp->prime); + BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y); + ptr += BN_num_bytes(data->grp->prime); + BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->server_scalar); + if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group, + data->server_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): setting peer element " + "fail"); + goto fin; + } + + /* check to ensure server's element is not in a small sub-group */ + if (BN_cmp(cofactor, BN_value_one())) { + if (!EC_POINT_mul(data->grp->group, point, NULL, + data->server_element, cofactor, NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply " + "server element by order!\n"); + goto fin; + } + if (EC_POINT_is_at_infinity(data->grp->group, point)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): server element " + "is at infinity!\n"); + goto fin; + } + } + + /* compute the shared key, k */ + if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe, + data->server_scalar, data->bnctx)) || + (!EC_POINT_add(data->grp->group, K, K, data->server_element, + data->bnctx)) || + (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value, + data->bnctx))) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): computing shared key " + "fail"); + goto fin; + } + + /* ensure that the shared key isn't in a small sub-group */ + if (BN_cmp(cofactor, BN_value_one())) { + if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor, + NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply " + "shared key point by order"); + goto fin; + } + } + + /* + * This check is strictly speaking just for the case above where + * co-factor > 1 but it was suggested that even though this is probably + * never going to happen it is a simple and safe check "just to be + * sure" so let's be safe. + */ + if (EC_POINT_is_at_infinity(data->grp->group, K)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): shared key point is at " + "infinity!\n"); + goto fin; + } + + if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k, + NULL, data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to extract " + "shared secret from point"); + goto fin; + } + + /* now do the response */ + if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, + data->my_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): point assignment fail"); + goto fin; + } + + if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) || + ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) == + NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): data allocation fail"); + goto fin; + } + + /* + * bignums occupy as little memory as possible so one that is + * sufficiently smaller than the prime or order might need pre-pending + * with zeros. + */ + os_memset(scalar, 0, BN_num_bytes(data->grp->order)); + os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2); + offset = BN_num_bytes(data->grp->order) - + BN_num_bytes(data->my_scalar); + BN_bn2bin(data->my_scalar, scalar + offset); + + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); + BN_bn2bin(x, element + offset); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); + BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset); + + data->outbuf = wpabuf_alloc(BN_num_bytes(data->grp->order) + + 2 * BN_num_bytes(data->grp->prime)); + if (data->outbuf == NULL) + goto fin; + + /* we send the element as (x,y) follwed by the scalar */ + wpabuf_put_data(data->outbuf, element, + 2 * BN_num_bytes(data->grp->prime)); + wpabuf_put_data(data->outbuf, scalar, BN_num_bytes(data->grp->order)); + +fin: + os_free(scalar); + os_free(element); + BN_free(x); + BN_free(y); + BN_free(cofactor); + EC_POINT_free(K); + EC_POINT_free(point); + if (data->outbuf == NULL) + eap_pwd_state(data, FAILURE); + else + eap_pwd_state(data, PWD_Confirm_Req); +} + + +static void +eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, size_t payload_len) +{ + BIGNUM *x = NULL, *y = NULL; + struct crypto_hash *hash; + u32 cs; + u16 grp; + u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; + int offset; + + /* + * first build up the ciphersuite which is group | random_function | + * prf + */ + grp = htons(data->group_num); + ptr = (u8 *) &cs; + os_memcpy(ptr, &grp, sizeof(u16)); + ptr += sizeof(u16); + *ptr = EAP_PWD_DEFAULT_RAND_FUNC; + ptr += sizeof(u8); + *ptr = EAP_PWD_DEFAULT_PRF; + + /* each component of the cruft will be at most as big as the prime */ + if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) || + ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): confirm allocation " + "fail"); + goto fin; + } + + /* + * server's commit is H(k | server_element | server_scalar | + * peer_element | peer_scalar | ciphersuite) + */ + hash = eap_pwd_h_init(); + if (hash == NULL) + goto fin; + + /* + * zero the memory each time because this is mod prime math and some + * value may start with a few zeros and the previous one did not. + */ + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); + BN_bn2bin(data->k, cruft + offset); + 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, + data->server_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " + "assignment fail"); + goto fin; + } + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); + BN_bn2bin(x, cruft + offset); + 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); + 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); + 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, + data->my_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " + "assignment fail"); + goto fin; + } + + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); + BN_bn2bin(x, cruft + offset); + 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); + 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); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); + + /* the ciphersuite */ + eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32)); + + /* random function fin */ + eap_pwd_h_final(hash, conf); + + ptr = (u8 *) payload; + if (os_memcmp(conf, ptr, SHA256_MAC_LEN)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm did not verify"); + goto fin; + } + + wpa_printf(MSG_DEBUG, "EAP-pwd (peer): confirm verified"); + + /* + * compute confirm: + * H(k | peer_element | peer_scalar | server_element | server_scalar | + * ciphersuite) + */ + 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); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + + /* my element */ + if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, + data->my_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point " + "assignment fail"); + goto fin; + } + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); + BN_bn2bin(x, cruft + offset); + 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); + 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); + 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, + data->server_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point " + "assignment fail"); + goto fin; + } + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); + BN_bn2bin(x, cruft + offset); + 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); + 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); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); + + /* the ciphersuite */ + eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32)); + + /* all done */ + eap_pwd_h_final(hash, conf); + + if (compute_keys(data->grp, data->bnctx, data->k, + data->my_scalar, data->server_scalar, conf, ptr, + &cs, data->msk, data->emsk) < 0) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute MSK | " + "EMSK"); + goto fin; + } + + data->outbuf = wpabuf_alloc(SHA256_MAC_LEN); + if (data->outbuf == NULL) + goto fin; + + wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN); + +fin: + os_free(cruft); + BN_free(x); + BN_free(y); + ret->methodState = METHOD_DONE; + if (data->outbuf == NULL) { + ret->decision = DECISION_FAIL; + eap_pwd_state(data, FAILURE); + } else { + ret->decision = DECISION_UNCOND_SUCC; + eap_pwd_state(data, SUCCESS); + } +} + + +static struct wpabuf * +eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_pwd_data *data = priv; + struct wpabuf *resp = NULL; + const u8 *pos, *buf; + size_t len; + u16 tot_len = 0; + u8 lm_exch; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, reqData, &len); + if ((pos == NULL) || (len < 1)) { + wpa_printf(MSG_DEBUG, "EAP-pwd: Got a frame but pos is %s and " + "len is %d", + pos == NULL ? "NULL" : "not NULL", (int) len); + ret->ignore = TRUE; + return NULL; + } + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = FALSE; + + lm_exch = *pos; + pos++; /* skip over the bits and the exch */ + len--; + + /* + * we're fragmenting so send out the next fragment + */ + if (data->out_frag_pos) { + /* + * this should be an ACK + */ + if (len) + wpa_printf(MSG_INFO, "Bad Response! Fragmenting but " + "not an ACK"); + + wpa_printf(MSG_DEBUG, "EAP-pwd: Got an ACK for a fragment"); + /* + * check if there are going to be more fragments + */ + len = wpabuf_len(data->outbuf) - data->out_frag_pos; + if ((len + EAP_PWD_HDR_SIZE) > data->mtu) { + len = data->mtu - EAP_PWD_HDR_SIZE; + EAP_PWD_SET_MORE_BIT(lm_exch); + } + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, + EAP_PWD_HDR_SIZE + len, + EAP_CODE_RESPONSE, eap_get_id(reqData)); + if (resp == NULL) { + wpa_printf(MSG_INFO, "Unable to allocate memory for " + "next fragment!"); + return NULL; + } + wpabuf_put_u8(resp, lm_exch); + buf = wpabuf_head_u8(data->outbuf); + wpabuf_put_data(resp, buf + data->out_frag_pos, len); + data->out_frag_pos += len; + /* + * this is the last fragment so get rid of the out buffer + */ + 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", + data->out_frag_pos == 0 ? "last" : "next", + (int) len); + return resp; + } + + /* + * see if this is a fragment that needs buffering + * + * if it's the first fragment there'll be a length field + */ + if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) { + tot_len = WPA_GET_BE16(pos); + wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments whose " + "total length = %d", tot_len); + data->inbuf = wpabuf_alloc(tot_len); + if (data->inbuf == NULL) { + wpa_printf(MSG_INFO, "Out of memory to buffer " + "fragments!"); + return NULL; + } + pos += sizeof(u16); + len -= sizeof(u16); + } + /* + * buffer and ACK the fragment + */ + if (EAP_PWD_GET_MORE_BIT(lm_exch)) { + data->in_frag_pos += len; + if (data->in_frag_pos > wpabuf_size(data->inbuf)) { + wpa_printf(MSG_INFO, "EAP-pwd: Buffer overflow attack " + "detected (%d vs. %d)!", + (int) data->in_frag_pos, + (int) wpabuf_len(data->inbuf)); + wpabuf_free(data->inbuf); + data->in_frag_pos = 0; + return NULL; + } + wpabuf_put_data(data->inbuf, pos, len); + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, + EAP_PWD_HDR_SIZE, + EAP_CODE_RESPONSE, eap_get_id(reqData)); + if (resp != NULL) + wpabuf_put_u8(resp, (EAP_PWD_GET_EXCHANGE(lm_exch))); + wpa_printf(MSG_DEBUG, "EAP-pwd: ACKing a %d byte fragment", + (int) len); + return resp; + } + /* + * we're buffering and this is the last fragment + */ + if (data->in_frag_pos) { + wpabuf_put_data(data->inbuf, pos, len); + wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes", + (int) len); + data->in_frag_pos += len; + pos = wpabuf_head_u8(data->inbuf); + len = data->in_frag_pos; + } + wpa_printf(MSG_DEBUG, "EAP-pwd: processing frame: exch %d, len %d", + EAP_PWD_GET_EXCHANGE(lm_exch), (int) len); + + switch (EAP_PWD_GET_EXCHANGE(lm_exch)) { + case EAP_PWD_OPCODE_ID_EXCH: + eap_pwd_perform_id_exchange(sm, data, ret, reqData, + pos, len); + break; + case EAP_PWD_OPCODE_COMMIT_EXCH: + eap_pwd_perform_commit_exchange(sm, data, ret, reqData, + pos, len); + break; + case EAP_PWD_OPCODE_CONFIRM_EXCH: + eap_pwd_perform_confirm_exchange(sm, data, ret, reqData, + pos, len); + break; + default: + wpa_printf(MSG_INFO, "EAP-pwd: Ignoring message with unknown " + "opcode %d", lm_exch); + break; + } + /* + * if we buffered the just processed input now's the time to free it + */ + if (data->in_frag_pos) { + wpabuf_free(data->inbuf); + data->in_frag_pos = 0; + } + + if (data->outbuf == NULL) + return NULL; /* generic failure */ + + /* + * we have output! Do we need to fragment it? + */ + len = wpabuf_len(data->outbuf); + if ((len + EAP_PWD_HDR_SIZE) > data->mtu) { + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, data->mtu, + EAP_CODE_RESPONSE, eap_get_id(reqData)); + /* + * if so it's the first so include a length field + */ + EAP_PWD_SET_LENGTH_BIT(lm_exch); + EAP_PWD_SET_MORE_BIT(lm_exch); + tot_len = len; + /* + * keep the packet at the MTU + */ + len = data->mtu - EAP_PWD_HDR_SIZE - sizeof(u16); + wpa_printf(MSG_DEBUG, "EAP-pwd: Fragmenting output, total " + "length = %d", tot_len); + } else { + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, + EAP_PWD_HDR_SIZE + len, + EAP_CODE_RESPONSE, eap_get_id(reqData)); + } + if (resp == NULL) + return NULL; + + wpabuf_put_u8(resp, lm_exch); + if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) { + wpabuf_put_be16(resp, tot_len); + data->out_frag_pos += len; + } + buf = wpabuf_head_u8(data->outbuf); + wpabuf_put_data(resp, buf, len); + /* + * if we're not fragmenting then there's no need to carry this around + */ + if (data->out_frag_pos == 0) { + wpabuf_free(data->outbuf); + data->outbuf = NULL; + data->out_frag_pos = 0; + } + + return resp; +} + + +static Boolean eap_pwd_key_available(struct eap_sm *sm, void *priv) +{ + struct eap_pwd_data *data = priv; + return data->state == SUCCESS; +} + + +static u8 * eap_pwd_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_pwd_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + if ((key = os_malloc(EAP_EMSK_LEN)) == NULL) + return NULL; + + os_memcpy(key, data->emsk, EAP_EMSK_LEN); + *len = EAP_EMSK_LEN; + + return key; +} + + +int eap_peer_pwd_register(void) +{ + struct eap_method *eap; + int ret; + + EVP_add_digest(EVP_sha256()); + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_PWD, "PWD"); + if (eap == NULL) + return -1; + + eap->init = eap_pwd_init; + eap->deinit = eap_pwd_deinit; + eap->process = eap_pwd_process; + eap->isKeyAvailable = eap_pwd_key_available; + eap->getKey = eap_pwd_getkey; + eap->get_emsk = eap_pwd_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/src/eap_peer/eap_sake.c b/src/eap_peer/eap_sake.c index bb06bb2f4256e..e072f46375786 100644 --- a/src/eap_peer/eap_sake.c +++ b/src/eap_peer/eap_sake.c @@ -2,19 +2,14 @@ * EAP peer method: EAP-SAKE (RFC 4763) * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#include "crypto/random.h" #include "eap_peer/eap_i.h" #include "eap_common/eap_sake_common.h" @@ -223,7 +218,7 @@ static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm, wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)", data->rand_s, EAP_SAKE_RAND_LEN); - if (os_get_random(data->rand_p, EAP_SAKE_RAND_LEN)) { + if (random_get_bytes(data->rand_p, EAP_SAKE_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data"); return NULL; } diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c index 3d8afb22fb00a..c936a44753034 100644 --- a/src/eap_peer/eap_sim.c +++ b/src/eap_peer/eap_sim.c @@ -1,15 +1,9 @@ /* * EAP peer method: EAP-SIM (RFC 4186) - * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-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 - * published 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" @@ -17,6 +11,7 @@ #include "common.h" #include "pcsc_funcs.h" #include "crypto/milenage.h" +#include "crypto/random.h" #include "eap_peer/eap_i.h" #include "eap_config.h" #include "eap_common/eap_sim_common.h" @@ -93,7 +88,7 @@ static void * eap_sim_init(struct eap_sm *sm) if (data == NULL) return NULL; - if (os_get_random(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) { + if (random_get_bytes(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) { wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data " "for NONCE_MT"); os_free(data); @@ -122,6 +117,15 @@ static void * eap_sim_init(struct eap_sm *sm) NULL; } + if (config && config->anonymous_identity) { + data->pseudonym = os_malloc(config->anonymous_identity_len); + if (data->pseudonym) { + os_memcpy(data->pseudonym, config->anonymous_identity, + config->anonymous_identity_len); + data->pseudonym_len = config->anonymous_identity_len; + } + } + eap_sim_state(data, CONTINUE); return data; @@ -263,23 +267,24 @@ static int eap_sim_supported_ver(int version) #define CLEAR_REAUTH_ID 0x02 #define CLEAR_EAP_ID 0x04 -static void eap_sim_clear_identities(struct eap_sim_data *data, int id) +static void eap_sim_clear_identities(struct eap_sm *sm, + struct eap_sim_data *data, int id) { - wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old%s%s%s", - id & CLEAR_PSEUDONYM ? " pseudonym" : "", - id & CLEAR_REAUTH_ID ? " reauth_id" : "", - id & CLEAR_EAP_ID ? " eap_id" : ""); - if (id & CLEAR_PSEUDONYM) { + if ((id & CLEAR_PSEUDONYM) && data->pseudonym) { + wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old pseudonym"); os_free(data->pseudonym); data->pseudonym = NULL; data->pseudonym_len = 0; + eap_set_anon_id(sm, NULL, 0); } - if (id & CLEAR_REAUTH_ID) { + if ((id & CLEAR_REAUTH_ID) && data->reauth_id) { + wpa_printf(MSG_DEBUG, "EAP-SIM: 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-SIM: forgetting old eap_id"); os_free(data->last_eap_identity); data->last_eap_identity = NULL; data->last_eap_identity_len = 0; @@ -287,24 +292,45 @@ static void eap_sim_clear_identities(struct eap_sim_data *data, int id) } -static int eap_sim_learn_ids(struct eap_sim_data *data, +static int eap_sim_learn_ids(struct eap_sm *sm, struct eap_sim_data *data, struct eap_sim_attrs *attr) { if (attr->next_pseudonym) { + const u8 *identity = NULL; + size_t identity_len = 0; + const u8 *realm = NULL; + size_t realm_len = 0; + + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-SIM: (encr) AT_NEXT_PSEUDONYM", + attr->next_pseudonym, + attr->next_pseudonym_len); os_free(data->pseudonym); - data->pseudonym = os_malloc(attr->next_pseudonym_len); + /* Look for the realm of the permanent identity */ + identity = eap_get_config_identity(sm, &identity_len); + if (identity) { + for (realm = identity, realm_len = identity_len; + realm_len > 0; realm_len--, realm++) { + if (*realm == '@') + break; + } + } + data->pseudonym = os_malloc(attr->next_pseudonym_len + + realm_len); if (data->pseudonym == NULL) { wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for " "next pseudonym"); + data->pseudonym_len = 0; return -1; } os_memcpy(data->pseudonym, attr->next_pseudonym, attr->next_pseudonym_len); - data->pseudonym_len = attr->next_pseudonym_len; - wpa_hexdump_ascii(MSG_DEBUG, - "EAP-SIM: (encr) AT_NEXT_PSEUDONYM", - data->pseudonym, - data->pseudonym_len); + if (realm_len) { + os_memcpy(data->pseudonym + attr->next_pseudonym_len, + realm, realm_len); + } + data->pseudonym_len = attr->next_pseudonym_len + realm_len; + eap_set_anon_id(sm, data->pseudonym, data->pseudonym_len); } if (attr->next_reauth_id) { @@ -313,6 +339,7 @@ static int eap_sim_learn_ids(struct eap_sim_data *data, if (data->reauth_id == NULL) { wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for " "next reauth_id"); + data->reauth_id_len = 0; return -1; } os_memcpy(data->reauth_id, attr->next_reauth_id, @@ -337,6 +364,8 @@ static struct wpabuf * eap_sim_client_error(struct eap_sim_data *data, u8 id, data->num_id_req = 0; data->num_notification = 0; + wpa_printf(MSG_DEBUG, "EAP-SIM: Send Client-Error (error code %d)", + err); msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CLIENT_ERROR); eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0); @@ -361,16 +390,16 @@ static struct wpabuf * eap_sim_response_start(struct eap_sm *sm, data->pseudonym) { identity = data->pseudonym; identity_len = data->pseudonym_len; - eap_sim_clear_identities(data, CLEAR_REAUTH_ID); + eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID); } else if (id_req != NO_ID_REQ) { identity = eap_get_config_identity(sm, &identity_len); if (identity) { - eap_sim_clear_identities(data, CLEAR_PSEUDONYM | + eap_sim_clear_identities(sm, data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID); } } if (id_req != NO_ID_REQ) - eap_sim_clear_identities(data, CLEAR_EAP_ID); + eap_sim_clear_identities(sm, data, CLEAR_EAP_ID); wpa_printf(MSG_DEBUG, "Generating EAP-SIM Start (id=%d)", id); msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, @@ -417,7 +446,8 @@ static struct wpabuf * eap_sim_response_challenge(struct eap_sim_data *data, static struct wpabuf * eap_sim_response_reauth(struct eap_sim_data *data, - u8 id, int counter_too_small) + u8 id, int counter_too_small, + const u8 *nonce_s) { struct eap_sim_msg *msg; unsigned int counter; @@ -452,7 +482,7 @@ static struct wpabuf * eap_sim_response_reauth(struct eap_sim_data *data, } wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); - return eap_sim_msg_finish(msg, data->k_aut, data->nonce_s, + return eap_sim_msg_finish(msg, data->k_aut, nonce_s, EAP_SIM_NONCE_S_LEN); } @@ -648,11 +678,11 @@ static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm, EAP_SIM_UNABLE_TO_PROCESS_PACKET); } - /* Old reauthentication and pseudonym identities must not be used - * anymore. In other words, if no new identities are received, full - * authentication will be used on next reauthentication. */ - eap_sim_clear_identities(data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID | - CLEAR_EAP_ID); + /* Old reauthentication identity must not be used anymore. In + * other words, if no new reauth identity is received, full + * authentication will be used on next reauthentication (using + * pseudonym identity or permanent identity). */ + eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); if (attr->encr_data) { u8 *decrypted; @@ -663,7 +693,7 @@ static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm, return eap_sim_client_error( data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); } - eap_sim_learn_ids(data, &eattr); + eap_sim_learn_ids(sm, data, &eattr); os_free(decrypted); } @@ -848,7 +878,7 @@ static struct wpabuf * eap_sim_process_reauthentication( data->reauth_id = NULL; data->reauth_id_len = 0; os_free(decrypted); - return eap_sim_response_reauth(data, id, 1); + return eap_sim_response_reauth(data, id, 1, eattr.nonce_s); } data->counter = eattr.counter; @@ -860,8 +890,8 @@ static struct wpabuf * eap_sim_process_reauthentication( data->reauth_id, data->reauth_id_len, data->nonce_s, data->mk, data->msk, data->emsk); - eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); - eap_sim_learn_ids(data, &eattr); + eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + eap_sim_learn_ids(sm, data, &eattr); if (data->result_ind && attr->result_ind) data->use_result_ind = 1; @@ -876,10 +906,11 @@ static struct wpabuf * eap_sim_process_reauthentication( if (data->counter > EAP_SIM_MAX_FAST_REAUTHS) { wpa_printf(MSG_DEBUG, "EAP-SIM: Maximum number of " "fast reauths performed - force fullauth"); - eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + eap_sim_clear_identities(sm, data, + CLEAR_REAUTH_ID | CLEAR_EAP_ID); } os_free(decrypted); - return eap_sim_response_reauth(data, id, 0); + return eap_sim_response_reauth(data, id, 0, data->nonce_s); } @@ -987,7 +1018,7 @@ static Boolean eap_sim_has_reauth_data(struct eap_sm *sm, void *priv) static void eap_sim_deinit_for_reauth(struct eap_sm *sm, void *priv) { struct eap_sim_data *data = priv; - eap_sim_clear_identities(data, CLEAR_EAP_ID); + eap_sim_clear_identities(sm, data, CLEAR_EAP_ID); data->use_result_ind = 0; } @@ -995,7 +1026,7 @@ static void eap_sim_deinit_for_reauth(struct eap_sm *sm, void *priv) static void * eap_sim_init_for_reauth(struct eap_sm *sm, void *priv) { struct eap_sim_data *data = priv; - if (os_get_random(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) { + if (random_get_bytes(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) { wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data " "for NONCE_MT"); os_free(data); diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c index 20b2212e1cdc4..061a72b10a542 100644 --- a/src/eap_peer/eap_tls.c +++ b/src/eap_peer/eap_tls.c @@ -1,15 +1,9 @@ /* * 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 program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published 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" @@ -27,6 +21,8 @@ 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; + u8 eap_type; }; @@ -46,7 +42,10 @@ static void * eap_tls_init(struct eap_sm *sm) if (data == NULL) return NULL; - if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) { + data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 : + sm->ssl_ctx; + + if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TLS)) { wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); eap_tls_deinit(sm, data); if (config->engine) { @@ -64,8 +63,37 @@ static void * eap_tls_init(struct eap_sm *sm) return NULL; } + data->eap_type = EAP_TYPE_TLS; + + return data; +} + + +#ifdef EAP_UNAUTH_TLS +static void * eap_unauth_tls_init(struct eap_sm *sm) +{ + struct eap_tls_data *data; + struct eap_peer_config *config = eap_get_config(sm); + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 : + sm->ssl_ctx; + + if (eap_peer_tls_ssl_init(sm, &data->ssl, config, + EAP_UNAUTH_TLS_TYPE)) { + wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); + eap_tls_deinit(sm, data); + return NULL; + } + + data->eap_type = EAP_UNAUTH_TLS_TYPE; + return data; } +#endif /* EAP_UNAUTH_TLS */ static void eap_tls_deinit(struct eap_sm *sm, void *priv) @@ -111,7 +139,7 @@ static struct wpabuf * eap_tls_failure(struct eap_sm *sm, return resp; } - return eap_peer_tls_build_ack(id, EAP_TYPE_TLS, 0); + return eap_peer_tls_build_ack(id, data->eap_type, 0); } @@ -151,7 +179,7 @@ static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv, const u8 *pos; struct eap_tls_data *data = priv; - pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TLS, ret, + pos = eap_peer_tls_process_init(sm, &data->ssl, data->eap_type, ret, reqData, &left, &flags); if (pos == NULL) return NULL; @@ -164,19 +192,19 @@ static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv, } resp = NULL; - res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TLS, 0, id, - pos, left, &resp); + res = eap_peer_tls_process_helper(sm, &data->ssl, data->eap_type, 0, + id, pos, left, &resp); if (res < 0) { return eap_tls_failure(sm, data, ret, res, resp, id); } - if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) + if (tls_connection_established(data->ssl_ctx, data->ssl.conn)) eap_tls_success(sm, data, ret); if (res == 1) { wpabuf_free(resp); - return eap_peer_tls_build_ack(id, EAP_TYPE_TLS, 0); + return eap_peer_tls_build_ack(id, data->eap_type, 0); } return resp; @@ -186,7 +214,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); } @@ -287,3 +315,34 @@ int eap_peer_tls_register(void) eap_peer_method_free(eap); return ret; } + + +#ifdef EAP_UNAUTH_TLS +int eap_peer_unauth_tls_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, "UNAUTH-TLS"); + if (eap == NULL) + return -1; + + eap->init = eap_unauth_tls_init; + eap->deinit = eap_tls_deinit; + eap->process = eap_tls_process; + eap->isKeyAvailable = eap_tls_isKeyAvailable; + eap->getKey = eap_tls_getKey; + eap->get_status = eap_tls_get_status; + eap->has_reauth_data = eap_tls_has_reauth_data; + eap->deinit_for_reauth = eap_tls_deinit_for_reauth; + eap->init_for_reauth = eap_tls_init_for_reauth; + eap->get_emsk = eap_tls_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} +#endif /* EAP_UNAUTH_TLS */ diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c index 7bd50f677bc8f..aedd85a79e853 100644 --- a/src/eap_peer/eap_tls_common.c +++ b/src/eap_peer/eap_tls_common.c @@ -1,15 +1,9 @@ /* * 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 program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published 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" @@ -22,6 +16,18 @@ #include "eap_config.h" +static struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len, + u8 code, u8 identifier) +{ + if (type == EAP_UNAUTH_TLS_TYPE) + return eap_msg_alloc(EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, payload_len, + code, identifier); + return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code, + identifier); +} + + static int eap_tls_check_blob(struct eap_sm *sm, const char **name, const u8 **data, size_t *data_len) { @@ -54,6 +60,10 @@ static void eap_tls_params_flags(struct tls_connection_params *params, params->flags |= TLS_CONN_ALLOW_SIGN_RSA_MD5; if (os_strstr(txt, "tls_disable_time_checks=1")) params->flags |= TLS_CONN_DISABLE_TIME_CHECKS; + if (os_strstr(txt, "tls_disable_session_ticket=1")) + params->flags |= TLS_CONN_DISABLE_SESSION_TICKET; + if (os_strstr(txt, "tls_disable_session_ticket=0")) + params->flags &= ~TLS_CONN_DISABLE_SESSION_TICKET; } @@ -105,6 +115,18 @@ static int eap_tls_params_from_conf(struct eap_sm *sm, struct eap_peer_config *config, int phase2) { os_memset(params, 0, sizeof(*params)); + if (sm->workaround && data->eap_type != EAP_TYPE_FAST) { + /* + * Some deployed authentication servers seem to be unable to + * handle the TLS Session Ticket extension (they are supposed + * to ignore unrecognized TLS extensions, but end up rejecting + * the ClientHello instead). As a workaround, disable use of + * TLS Sesson Ticket extension for EAP-TLS, EAP-PEAP, and + * EAP-TTLS (EAP-FAST uses session ticket, so any server that + * supports EAP-FAST does not need this workaround). + */ + params->flags |= TLS_CONN_DISABLE_SESSION_TICKET; + } if (phase2) { wpa_printf(MSG_DEBUG, "TLS: using phase2 config options"); eap_tls_params_from_conf2(params, config); @@ -112,7 +134,6 @@ static int eap_tls_params_from_conf(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "TLS: using phase1 config options"); eap_tls_params_from_conf1(params, config); } - params->tls_ia = data->tls_ia; /* * Use blob data, if available. Otherwise, leave reference to external @@ -143,14 +164,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. @@ -169,13 +190,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; } @@ -189,13 +210,14 @@ static int eap_tls_init_connection(struct eap_sm *sm, * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @data: Data for TLS processing * @config: Pointer to the network configuration + * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST) * Returns: 0 on success, -1 on failure * * This function is used to initialize shared TLS functionality for EAP-TLS, * EAP-PEAP, EAP-TTLS, and EAP-FAST. */ int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, - struct eap_peer_config *config) + struct eap_peer_config *config, u8 eap_type) { struct tls_connection_params params; @@ -203,7 +225,10 @@ int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, return -1; data->eap = sm; + data->eap_type = eap_type; data->phase2 = sm->init_phase2; + data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 : + sm->ssl_ctx; if (eap_tls_params_from_conf(sm, data, ¶ms, config, data->phase2) < 0) return -1; @@ -241,7 +266,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); } @@ -264,7 +289,9 @@ void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, const char *label, size_t len) { +#ifndef CONFIG_FIPS struct tls_keys keys; +#endif /* CONFIG_FIPS */ u8 *rnd = NULL, *out; out = os_malloc(len); @@ -272,16 +299,17 @@ 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; +#ifndef CONFIG_FIPS /* * TLS library did not support key generation, so get the needed TLS * session parameters and use an internal implementation of TLS PRF to * derive the key. */ - if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) + if (tls_connection_get_keys(data->ssl_ctx, data->conn, &keys)) goto fail; if (keys.client_random == NULL || keys.server_random == NULL || @@ -295,15 +323,16 @@ u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, os_memcpy(rnd + keys.client_random_len, keys.server_random, keys.server_random_len); - if (tls_prf(keys.master_key, keys.master_key_len, - label, rnd, keys.client_random_len + - keys.server_random_len, out, len)) + if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len, + label, rnd, keys.client_random_len + + keys.server_random_len, out, len)) goto fail; os_free(rnd); return out; fail: +#endif /* CONFIG_FIPS */ os_free(out); os_free(rnd); return NULL; @@ -361,7 +390,8 @@ static int eap_peer_tls_reassemble_fragment(struct eap_ssl_data *data, eap_peer_tls_reset_input(data); return -1; } - wpabuf_put_buf(data->tls_in, in_data); + if (in_data) + wpabuf_put_buf(data->tls_in, in_data); data->tls_in_left -= in_len; if (data->tls_in_left > 0) { @@ -447,14 +477,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; @@ -520,9 +550,8 @@ static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type, length_included = 1; } - *out_data = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, - 1 + length_included * 4 + len, - EAP_CODE_RESPONSE, id); + *out_data = eap_tls_msg_alloc(eap_type, 1 + length_included * 4 + len, + EAP_CODE_RESPONSE, id); if (*out_data == NULL) return -1; @@ -622,7 +651,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"); @@ -660,8 +689,7 @@ struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type, { struct wpabuf *resp; - resp = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, 1, EAP_CODE_RESPONSE, - id); + resp = eap_tls_msg_alloc(eap_type, 1, EAP_CODE_RESPONSE, id); if (resp == NULL) return NULL; wpa_printf(MSG_DEBUG, "SSL: Building ACK (type=%d id=%d ver=%d)", @@ -681,7 +709,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); } @@ -700,7 +728,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) @@ -747,13 +776,19 @@ 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; } - pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, reqData, &left); + if (eap_type == EAP_UNAUTH_TLS_TYPE) + pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, reqData, + &left); + else + pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, reqData, + &left); if (pos == NULL) { ret->ignore = TRUE; return NULL; @@ -794,6 +829,14 @@ const u8 * eap_peer_tls_process_init(struct eap_sm *sm, } pos += 4; left -= 4; + + if (left > tls_msg_len) { + wpa_printf(MSG_INFO, "SSL: TLS Message Length (%d " + "bytes) smaller than this fragment (%d " + "bytes)", (int) tls_msg_len, (int) left); + ret->ignore = TRUE; + return NULL; + } } ret->ignore = FALSE; @@ -855,7 +898,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"); @@ -883,8 +926,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)", @@ -949,8 +992,8 @@ int eap_peer_select_phase2_methods(struct eap_peer_config *config, "method '%s'", start); } else { num_methods++; - _methods = os_realloc(methods, - num_methods * sizeof(*methods)); + _methods = os_realloc_array(methods, num_methods, + sizeof(*methods)); if (_methods == NULL) { os_free(methods); os_free(buf); diff --git a/src/eap_peer/eap_tls_common.h b/src/eap_peer/eap_tls_common.h index e9e0998098ccb..91d3a25a0ec97 100644 --- a/src/eap_peer/eap_tls_common.h +++ b/src/eap_peer/eap_tls_common.h @@ -1,15 +1,9 @@ /* * 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 program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published 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. */ #ifndef EAP_TLS_COMMON_H @@ -66,14 +60,19 @@ struct eap_ssl_data { int include_tls_length; /** - * tls_ia - Whether TLS/IA is enabled for this TLS connection + * eap - EAP state machine allocated with eap_peer_sm_init() */ - int tls_ia; + struct eap_sm *eap; /** - * eap - EAP state machine allocated with eap_peer_sm_init() + * ssl_ctx - TLS library context to use for the connection */ - struct eap_sm *eap; + void *ssl_ctx; + + /** + * eap_type - EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST) + */ + u8 eap_type; }; @@ -86,9 +85,12 @@ struct eap_ssl_data { /* could be up to 128 bytes, but only the first 64 bytes are used */ #define EAP_TLS_KEY_LEN 64 +/* dummy type used as a flag for UNAUTH-TLS */ +#define EAP_UNAUTH_TLS_TYPE 255 + int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, - struct eap_peer_config *config); + struct eap_peer_config *config, u8 eap_type); void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, const char *label, size_t len); diff --git a/src/eap_peer/eap_tnc.c b/src/eap_peer/eap_tnc.c index 6c95f72c15071..bc136470b334c 100644 --- a/src/eap_peer/eap_tnc.c +++ b/src/eap_peer/eap_tnc.c @@ -2,20 +2,13 @@ * EAP peer method: EAP-TNC (Trusted Network Connect) * Copyright (c) 2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "base64.h" #include "eap_i.h" #include "tncc.h" diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c index 25737803beeda..9360a424c799a 100644 --- a/src/eap_peer/eap_ttls.c +++ b/src/eap_peer/eap_ttls.c @@ -1,15 +1,9 @@ /* * EAP peer method: EAP-TTLS (RFC 5281) - * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -26,17 +20,7 @@ #include "eap_config.h" -/* Maximum supported TTLS version - * 0 = RFC 5281 - * 1 = draft-funk-eap-ttls-v1-00.txt - */ -#ifndef EAP_TTLS_VERSION -#define EAP_TTLS_VERSION 0 /* TTLSv1 implementation is not yet complete */ -#endif /* EAP_TTLS_VERSION */ - - -#define MSCHAPV2_KEY_LEN 16 -#define MSCHAPV2_NT_RESPONSE_LEN 24 +#define EAP_TTLS_VERSION 0 static void eap_ttls_deinit(struct eap_sm *sm, void *priv); @@ -44,9 +28,8 @@ static void eap_ttls_deinit(struct eap_sm *sm, void *priv); struct eap_ttls_data { struct eap_ssl_data ssl; - int ssl_initialized; - int ttls_version, force_ttls_version; + int ttls_version; const struct eap_method *phase2_method; void *phase2_priv; @@ -91,22 +74,9 @@ static void * eap_ttls_init(struct eap_sm *sm) if (data == NULL) return NULL; data->ttls_version = EAP_TTLS_VERSION; - data->force_ttls_version = -1; selected = "EAP"; data->phase2_type = EAP_TTLS_PHASE2_EAP; -#if EAP_TTLS_VERSION > 0 - if (config && config->phase1) { - const char *pos = os_strstr(config->phase1, "ttlsver="); - if (pos) { - data->force_ttls_version = atoi(pos + 8); - data->ttls_version = data->force_ttls_version; - wpa_printf(MSG_DEBUG, "EAP-TTLS: Forced TTLS version " - "%d", data->force_ttls_version); - } - } -#endif /* EAP_TTLS_VERSION */ - if (config && config->phase2) { if (os_strstr(config->phase2, "autheap=")) { selected = "EAP"; @@ -140,19 +110,11 @@ static void * eap_ttls_init(struct eap_sm *sm) data->phase2_eap_type.method = EAP_TYPE_NONE; } -#if EAP_TTLS_VERSION > 0 - if (!(tls_capabilities(sm->ssl_ctx) & TLS_CAPABILITY_IA) && - data->ttls_version > 0) { - if (data->force_ttls_version > 0) { - wpa_printf(MSG_INFO, "EAP-TTLS: Forced TTLSv%d and " - "TLS library does not support TLS/IA.", - data->force_ttls_version); - eap_ttls_deinit(sm, data); - return NULL; - } - data->ttls_version = 0; + if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TTLS)) { + wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL."); + eap_ttls_deinit(sm, data); + return NULL; } -#endif /* EAP_TTLS_VERSION */ return data; } @@ -176,8 +138,7 @@ static void eap_ttls_deinit(struct eap_sm *sm, void *priv) return; eap_ttls_phase2_eap_deinit(sm, data); os_free(data->phase2_eap_types); - if (data->ssl_initialized) - eap_peer_tls_ssl_deinit(sm, &data->ssl); + eap_peer_tls_ssl_deinit(sm, &data->ssl); os_free(data->key_data); wpabuf_free(data->pending_phase2_req); os_free(data); @@ -202,7 +163,7 @@ static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id, } avp->avp_code = host_to_be32(avp_code); - avp->avp_length = host_to_be32((flags << 24) | (hdrlen + len)); + avp->avp_length = host_to_be32((flags << 24) | (u32) (hdrlen + len)); return avphdr + hdrlen; } @@ -246,39 +207,6 @@ static int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code, } -#if EAP_TTLS_VERSION > 0 -static int eap_ttls_ia_permute_inner_secret(struct eap_sm *sm, - struct eap_ttls_data *data, - const u8 *key, size_t key_len) -{ - u8 *buf; - size_t buf_len; - int ret; - - if (key) { - buf_len = 2 + key_len; - buf = os_malloc(buf_len); - if (buf == NULL) - return -1; - WPA_PUT_BE16(buf, key_len); - os_memcpy(buf + 2, key, key_len); - } else { - buf = NULL; - buf_len = 0; - } - - wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Session keys for TLS/IA inner " - "secret permutation", buf, buf_len); - ret = tls_connection_ia_permute_inner_secret(sm->ssl_ctx, - data->ssl.conn, - buf, buf_len); - os_free(buf); - - return ret; -} -#endif /* EAP_TTLS_VERSION */ - - static int eap_ttls_v0_derive_key(struct eap_sm *sm, struct eap_ttls_data *data) { @@ -298,156 +226,10 @@ static int eap_ttls_v0_derive_key(struct eap_sm *sm, } -#if EAP_TTLS_VERSION > 0 -static int eap_ttls_v1_derive_key(struct eap_sm *sm, - struct eap_ttls_data *data) -{ - struct tls_keys keys; - u8 *rnd; - - os_free(data->key_data); - data->key_data = NULL; - - os_memset(&keys, 0, sizeof(keys)); - if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) || - keys.client_random == NULL || keys.server_random == NULL || - keys.inner_secret == NULL) { - wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, " - "client random, or server random to derive keying " - "material"); - return -1; - } - - rnd = os_malloc(keys.client_random_len + keys.server_random_len); - data->key_data = os_malloc(EAP_TLS_KEY_LEN); - if (rnd == NULL || data->key_data == NULL) { - wpa_printf(MSG_INFO, "EAP-TTLS: No memory for key derivation"); - os_free(rnd); - os_free(data->key_data); - data->key_data = NULL; - return -1; - } - os_memcpy(rnd, keys.client_random, keys.client_random_len); - os_memcpy(rnd + keys.client_random_len, keys.server_random, - keys.server_random_len); - - if (tls_prf(keys.inner_secret, keys.inner_secret_len, - "ttls v1 keying material", rnd, keys.client_random_len + - keys.server_random_len, data->key_data, EAP_TLS_KEY_LEN)) { - wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key"); - os_free(rnd); - os_free(data->key_data); - data->key_data = NULL; - return -1; - } - - wpa_hexdump(MSG_DEBUG, "EAP-TTLS: client/server random", - rnd, keys.client_random_len + keys.server_random_len); - wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: TLS/IA inner secret", - keys.inner_secret, keys.inner_secret_len); - - os_free(rnd); - - wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key", - data->key_data, EAP_TLS_KEY_LEN); - - return 0; -} -#endif /* EAP_TTLS_VERSION */ - - static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm, struct eap_ttls_data *data, size_t len) { -#if EAP_TTLS_VERSION > 0 - struct tls_keys keys; - u8 *challenge, *rnd; -#endif /* EAP_TTLS_VERSION */ - - if (data->ttls_version == 0) { - return eap_peer_tls_derive_key(sm, &data->ssl, - "ttls challenge", len); - } - -#if EAP_TTLS_VERSION > 0 - - os_memset(&keys, 0, sizeof(keys)); - if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) || - keys.client_random == NULL || keys.server_random == NULL || - keys.inner_secret == NULL) { - wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, " - "client random, or server random to derive " - "implicit challenge"); - return NULL; - } - - rnd = os_malloc(keys.client_random_len + keys.server_random_len); - challenge = os_malloc(len); - if (rnd == NULL || challenge == NULL) { - wpa_printf(MSG_INFO, "EAP-TTLS: No memory for implicit " - "challenge derivation"); - os_free(rnd); - os_free(challenge); - return NULL; - } - os_memcpy(rnd, keys.server_random, keys.server_random_len); - os_memcpy(rnd + keys.server_random_len, keys.client_random, - keys.client_random_len); - - if (tls_prf(keys.inner_secret, keys.inner_secret_len, - "inner application challenge", rnd, - keys.client_random_len + keys.server_random_len, - challenge, len)) { - wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive implicit " - "challenge"); - os_free(rnd); - os_free(challenge); - return NULL; - } - - os_free(rnd); - - wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived implicit challenge", - challenge, len); - - return challenge; - -#else /* EAP_TTLS_VERSION */ - - return NULL; - -#endif /* EAP_TTLS_VERSION */ -} - - -static void eap_ttlsv1_phase2_eap_finish(struct eap_sm *sm, - struct eap_ttls_data *data, - struct eap_method_ret *ret) -{ -#if EAP_TTLS_VERSION > 0 - if (data->ttls_version > 0) { - const struct eap_method *m = data->phase2_method; - void *priv = data->phase2_priv; - - /* TTLSv1 requires TLS/IA FinalPhaseFinished */ - if (ret->decision == DECISION_UNCOND_SUCC) - ret->decision = DECISION_COND_SUCC; - ret->methodState = METHOD_CONT; - - if (ret->decision == DECISION_COND_SUCC && - m->isKeyAvailable && m->getKey && - m->isKeyAvailable(sm, priv)) { - u8 *key; - size_t key_len; - key = m->getKey(sm, priv, &key_len); - if (key) { - eap_ttls_ia_permute_inner_secret( - sm, data, key, key_len); - os_free(key); - } - } - } -#endif /* EAP_TTLS_VERSION */ + return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge", len); } @@ -494,7 +276,6 @@ static int eap_ttls_phase2_eap_process(struct eap_sm *sm, ret->methodState = iret.methodState; ret->decision = iret.decision; } - eap_ttlsv1_phase2_eap_finish(sm, data, ret); return 0; } @@ -615,31 +396,12 @@ static int eap_ttls_phase2_request_eap(struct eap_sm *sm, } -static void eap_ttlsv1_permute_inner(struct eap_sm *sm, - struct eap_ttls_data *data) -{ -#if EAP_TTLS_VERSION > 0 - u8 session_key[2 * MSCHAPV2_KEY_LEN]; - - if (data->ttls_version == 0) - return; - - get_asymetric_start_key(data->master_key, session_key, - MSCHAPV2_KEY_LEN, 0, 0); - get_asymetric_start_key(data->master_key, - session_key + MSCHAPV2_KEY_LEN, - MSCHAPV2_KEY_LEN, 1, 0); - eap_ttls_ia_permute_inner_secret(sm, data, session_key, - sizeof(session_key)); -#endif /* EAP_TTLS_VERSION */ -} - - static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, struct wpabuf **resp) { +#ifdef EAP_MSCHAPv2 struct wpabuf *msg; u8 *buf, *pos, *challenge, *peer_challenge; const u8 *identity, *password; @@ -674,7 +436,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, @@ -687,7 +448,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; @@ -695,6 +463,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"); @@ -702,8 +471,6 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, } data->auth_response_valid = 1; - eap_ttlsv1_permute_inner(sm, data); - pos += 24; os_free(challenge); AVP_PAD(buf, pos); @@ -711,7 +478,7 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, wpabuf_put(msg, pos - buf); *resp = msg; - if (sm->workaround && data->ttls_version == 0) { + if (sm->workaround) { /* At least FreeRADIUS seems to be terminating * EAP-TTLS/MSHCAPV2 without the expected MS-CHAP-v2 Success * packet. */ @@ -722,6 +489,10 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, } return 0; +#else /* EAP_MSCHAPv2 */ + wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build"); + return -1; +#endif /* EAP_MSCHAPv2 */ } @@ -798,17 +569,10 @@ static int eap_ttls_phase2_request_mschap(struct eap_sm *sm, wpabuf_put(msg, pos - buf); *resp = msg; - if (data->ttls_version > 0) { - /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success, - * so do not allow connection to be terminated yet. */ - ret->methodState = METHOD_CONT; - ret->decision = DECISION_COND_SUCC; - } else { - /* EAP-TTLS/MSCHAP does not provide tunneled success - * notification, so assume that Phase2 succeeds. */ - ret->methodState = METHOD_DONE; - ret->decision = DECISION_COND_SUCC; - } + /* EAP-TTLS/MSCHAP does not provide tunneled success + * notification, so assume that Phase2 succeeds. */ + ret->methodState = METHOD_DONE; + ret->decision = DECISION_COND_SUCC; return 0; } @@ -859,17 +623,10 @@ static int eap_ttls_phase2_request_pap(struct eap_sm *sm, wpabuf_put(msg, pos - buf); *resp = msg; - if (data->ttls_version > 0) { - /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success, - * so do not allow connection to be terminated yet. */ - ret->methodState = METHOD_CONT; - ret->decision = DECISION_COND_SUCC; - } else { - /* EAP-TTLS/PAP does not provide tunneled success notification, - * so assume that Phase2 succeeds. */ - ret->methodState = METHOD_DONE; - ret->decision = DECISION_COND_SUCC; - } + /* EAP-TTLS/PAP does not provide tunneled success notification, + * so assume that Phase2 succeeds. */ + ret->methodState = METHOD_DONE; + ret->decision = DECISION_COND_SUCC; return 0; } @@ -942,17 +699,10 @@ static int eap_ttls_phase2_request_chap(struct eap_sm *sm, wpabuf_put(msg, pos - buf); *resp = msg; - if (data->ttls_version > 0) { - /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success, - * so do not allow connection to be terminated yet. */ - ret->methodState = METHOD_CONT; - ret->decision = DECISION_COND_SUCC; - } else { - /* EAP-TTLS/CHAP does not provide tunneled success - * notification, so assume that Phase2 succeeds. */ - ret->methodState = METHOD_DONE; - ret->decision = DECISION_COND_SUCC; - } + /* EAP-TTLS/CHAP does not provide tunneled success + * notification, so assume that Phase2 succeeds. */ + ret->methodState = METHOD_DONE; + ret->decision = DECISION_COND_SUCC; return 0; } @@ -1027,36 +777,6 @@ static int eap_ttls_phase2_request(struct eap_sm *sm, } -#if EAP_TTLS_VERSION > 0 -static struct wpabuf * eap_ttls_build_phase_finished( - struct eap_sm *sm, struct eap_ttls_data *data, int id, int final) -{ - struct wpabuf *req, *buf; - - buf = tls_connection_ia_send_phase_finished(sm->ssl_ctx, - data->ssl.conn, - final); - if (buf == NULL) - return NULL; - - req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS, - 1 + wpabuf_len(buf), - EAP_CODE_RESPONSE, id); - if (req == NULL) { - wpabuf_free(buf); - return NULL; - } - - wpabuf_put_u8(req, data->ttls_version); - wpabuf_put_buf(req, buf); - wpabuf_free(buf); - eap_update_len(req); - - return req; -} -#endif /* EAP_TTLS_VERSION */ - - struct ttls_parse_avp { u8 *mschapv2; u8 *eapdata; @@ -1327,6 +1047,7 @@ static int eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, struct eap_method_ret *ret, struct ttls_parse_avp *parse) { +#ifdef EAP_MSCHAPv2 if (parse->mschapv2_error) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Received " "MS-CHAP-Error - failed"); @@ -1366,25 +1087,19 @@ static int eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 MSCHAPV2 " "authentication succeeded"); - if (data->ttls_version > 0) { - /* - * EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report - * success, so do not allow connection to be terminated - * yet. - */ - ret->methodState = METHOD_CONT; - ret->decision = DECISION_COND_SUCC; - } else { - ret->methodState = METHOD_DONE; - ret->decision = DECISION_UNCOND_SUCC; - data->phase2_success = 1; - } + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + data->phase2_success = 1; /* * Reply with empty data; authentication server will reply * with EAP-Success after this. */ return 1; +#else /* EAP_MSCHAPv2 */ + wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build"); + return -1; +#endif /* EAP_MSCHAPv2 */ } @@ -1493,24 +1208,6 @@ static int eap_ttls_process_decrypted(struct eap_sm *sm, } -#if EAP_TTLS_VERSION > 0 -static void eap_ttls_final_phase_finished(struct eap_sm *sm, - struct eap_ttls_data *data, - struct eap_method_ret *ret, - u8 identifier, - struct wpabuf **out_data) -{ - wpa_printf(MSG_DEBUG, "EAP-TTLS: FinalPhaseFinished received"); - wpa_printf(MSG_INFO, "EAP-TTLS: TLS/IA authentication succeeded"); - ret->methodState = METHOD_DONE; - ret->decision = DECISION_UNCOND_SUCC; - data->phase2_success = 1; - *out_data = eap_ttls_build_phase_finished(sm, data, identifier, 1); - eap_ttls_v1_derive_key(sm, data); -} -#endif /* EAP_TTLS_VERSION */ - - static int eap_ttls_implicit_identity_request(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, @@ -1534,6 +1231,21 @@ static int eap_ttls_implicit_identity_request(struct eap_sm *sm, "processing failed"); retval = -1; } else { + struct eap_peer_config *config = eap_get_config(sm); + if (resp == NULL && + (config->pending_req_identity || + config->pending_req_password || + config->pending_req_otp || + config->pending_req_new_password)) { + /* + * Use empty buffer to force implicit request + * processing when EAP request is re-processed after + * user input. + */ + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = wpabuf_alloc(0); + } + retval = eap_ttls_encrypt_response(sm, data, resp, identifier, out_data); } @@ -1627,17 +1339,6 @@ static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data, if (retval) goto done; -#if EAP_TTLS_VERSION > 0 - if (data->ttls_version > 0 && - (in_decrypted == NULL || wpabuf_len(in_decrypted) == 0) && - tls_connection_ia_final_phase_finished(sm->ssl_ctx, - data->ssl.conn)) { - eap_ttls_final_phase_finished(sm, data, ret, identifier, - out_data); - goto done; - } -#endif /* EAP_TTLS_VERSION */ - continue_req: data->phase2_start = 0; @@ -1662,46 +1363,6 @@ done: } -static int eap_ttls_process_start(struct eap_sm *sm, - struct eap_ttls_data *data, u8 flags, - struct eap_method_ret *ret) -{ - struct eap_peer_config *config = eap_get_config(sm); - - wpa_printf(MSG_DEBUG, "EAP-TTLS: Start (server ver=%d, own ver=%d)", - flags & EAP_TLS_VERSION_MASK, data->ttls_version); -#if EAP_TTLS_VERSION > 0 - if ((flags & EAP_TLS_VERSION_MASK) < data->ttls_version) - data->ttls_version = flags & EAP_TLS_VERSION_MASK; - if (data->force_ttls_version >= 0 && - data->force_ttls_version != data->ttls_version) { - wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to select " - "forced TTLS version %d", - data->force_ttls_version); - ret->methodState = METHOD_DONE; - ret->decision = DECISION_FAIL; - ret->allowNotifications = FALSE; - return -1; - } - wpa_printf(MSG_DEBUG, "EAP-TTLS: Using TTLS version %d", - data->ttls_version); - - if (data->ttls_version > 0) - data->ssl.tls_ia = 1; -#endif /* EAP_TTLS_VERSION */ - if (!data->ssl_initialized && - eap_peer_tls_ssl_init(sm, &data->ssl, config)) { - wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL."); - return -1; - } - data->ssl_initialized = 1; - - wpa_printf(MSG_DEBUG, "EAP-TTLS: Start"); - - return 0; -} - - static int eap_ttls_process_handshake(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, @@ -1725,8 +1386,7 @@ static int eap_ttls_process_handshake(struct eap_sm *sm, ret->methodState = METHOD_MAY_CONT; } data->phase2_start = 1; - if (data->ttls_version == 0) - eap_ttls_v0_derive_key(sm, data); + eap_ttls_v0_derive_key(sm, data); if (*out_data == NULL || wpabuf_len(*out_data) == 0) { if (eap_ttls_decrypt(sm, data, ret, identifier, @@ -1761,7 +1421,7 @@ static void eap_ttls_check_auth_status(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret) { - if (data->ttls_version == 0 && ret->methodState == METHOD_DONE) { + if (ret->methodState == METHOD_DONE) { ret->allowNotifications = FALSE; if (ret->decision == DECISION_UNCOND_SUCC || ret->decision == DECISION_COND_SUCC) { @@ -1779,8 +1439,7 @@ static void eap_ttls_check_auth_status(struct eap_sm *sm, } #endif /* EAP_TNC */ } - } else if (data->ttls_version == 0 && - ret->methodState == METHOD_MAY_CONT && + } else if (ret->methodState == METHOD_MAY_CONT && (ret->decision == DECISION_UNCOND_SUCC || ret->decision == DECISION_COND_SUCC)) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication " @@ -1808,8 +1467,9 @@ static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv, id = eap_get_id(reqData); if (flags & EAP_TLS_FLAGS_START) { - if (eap_ttls_process_start(sm, data, flags, ret) < 0) - return NULL; + wpa_printf(MSG_DEBUG, "EAP-TTLS: Start (server ver=%d, own " + "ver=%d)", flags & EAP_TLS_VERSION_MASK, + data->ttls_version); /* RFC 5281, Ch. 9.2: * "This packet MAY contain additional information in the form @@ -1817,13 +1477,6 @@ static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv, * For now, ignore any potential extra data. */ left = 0; - } else if (!data->ssl_initialized) { - wpa_printf(MSG_DEBUG, "EAP-TTLS: First message did not " - "include Start flag"); - ret->methodState = METHOD_DONE; - ret->decision = DECISION_FAIL; - ret->allowNotifications = FALSE; - return NULL; } resp = NULL; diff --git a/src/eap_peer/eap_vendor_test.c b/src/eap_peer/eap_vendor_test.c index 3e114c142a428..040d1e7f9a5a5 100644 --- a/src/eap_peer/eap_vendor_test.c +++ b/src/eap_peer/eap_vendor_test.c @@ -2,14 +2,8 @@ * EAP peer method: Test method for vendor specific (expanded) EAP type * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file implements a vendor specific test method using EAP expanded types. * This is only for test use and must not be used for authentication since no @@ -25,7 +19,7 @@ #endif /* TEST_PENDING_REQUEST */ -#define EAP_VENDOR_ID 0xfffefd +#define EAP_VENDOR_ID EAP_VENDOR_HOSTAP #define EAP_VENDOR_TYPE 0xfcfbfaf9 diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c index 8317f728f8eae..d007a57082b53 100644 --- a/src/eap_peer/eap_wsc.c +++ b/src/eap_peer/eap_wsc.c @@ -1,15 +1,9 @@ /* * 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 program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published 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" @@ -143,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) { @@ -190,19 +186,36 @@ 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); return NULL; } + pos = os_strstr(phase1, "dev_pw_id="); + if (pos && cfg.pin) + cfg.dev_pw_id = atoi(pos + 10); + res = eap_wsc_new_ap_settings(&new_ap_settings, phase1); if (res < 0) { os_free(data); @@ -219,10 +232,16 @@ static void * eap_wsc_init(struct eap_sm *sm) os_free(data); return NULL; } - data->fragment_size = WSC_FRAGMENT_SIZE; + res = eap_get_config_fragment_size(sm); + if (res > 0) + data->fragment_size = res; + else + data->fragment_size = WSC_FRAGMENT_SIZE; + wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment size limit %u", + (unsigned int) data->fragment_size); if (registrar && cfg.pin) { - wps_registrar_add_pin(data->wps_ctx->registrar, NULL, + wps_registrar_add_pin(data->wps_ctx->registrar, NULL, NULL, cfg.pin, cfg.pin_len, 0); } diff --git a/src/eap_peer/ikev2.c b/src/eap_peer/ikev2.c index 309a331124eb8..fcf4712aced62 100644 --- a/src/eap_peer/ikev2.c +++ b/src/eap_peer/ikev2.c @@ -2,20 +2,15 @@ * IKEv2 responder (RFC 4306) for EAP-IKEV2 * Copyright (c) 2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" #include "crypto/dh_groups.h" +#include "crypto/random.h" #include "ikev2.h" @@ -424,7 +419,7 @@ static int ikev2_process_kei(struct ikev2_responder_data *data, } /* RFC 4306, Section 3.4: - * The length of DH public value MUST be equal to the lenght of the + * The length of DH public value MUST be equal to the length of the * prime modulus. */ if (kei_len - 4 != data->dh->prime_len) { @@ -1133,7 +1128,7 @@ static struct wpabuf * ikev2_build_sa_init(struct ikev2_responder_data *data) data->r_spi, IKEV2_SPI_LEN); data->r_nonce_len = IKEV2_NONCE_MIN_LEN; - if (os_get_random(data->r_nonce, data->r_nonce_len)) + if (random_get_bytes(data->r_nonce, data->r_nonce_len)) return NULL; #ifdef CCNS_PL /* Zeros are removed incorrectly from the beginning of the nonces in diff --git a/src/eap_peer/ikev2.h b/src/eap_peer/ikev2.h index 9ca0ca56959d1..627a2cbbb0313 100644 --- a/src/eap_peer/ikev2.h +++ b/src/eap_peer/ikev2.h @@ -2,14 +2,8 @@ * IKEv2 responder (RFC 4306) for EAP-IKEV2 * Copyright (c) 2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef IKEV2_H diff --git a/src/eap_peer/mschapv2.c b/src/eap_peer/mschapv2.c index b8fb07502fd73..37e6735efb34d 100644 --- a/src/eap_peer/mschapv2.c +++ b/src/eap_peer/mschapv2.c @@ -2,14 +2,8 @@ * MSCHAPV2 (RFC 2759) * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -69,22 +63,28 @@ int mschapv2_derive_response(const u8 *identity, size_t identity_len, if (pwhash) { wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: password hash", password, password_len); - generate_nt_response_pwhash(auth_challenge, peer_challenge, - username, username_len, - password, nt_response); - generate_authenticator_response_pwhash( - password, peer_challenge, auth_challenge, - username, username_len, nt_response, auth_response); + if (generate_nt_response_pwhash(auth_challenge, peer_challenge, + username, username_len, + password, nt_response) || + generate_authenticator_response_pwhash( + password, peer_challenge, auth_challenge, + username, username_len, nt_response, + auth_response)) + return -1; } else { wpa_hexdump_ascii_key(MSG_DEBUG, "MSCHAPV2: password", password, password_len); - generate_nt_response(auth_challenge, peer_challenge, - username, username_len, - password, password_len, nt_response); - generate_authenticator_response(password, password_len, - peer_challenge, auth_challenge, - username, username_len, - nt_response, auth_response); + if (generate_nt_response(auth_challenge, peer_challenge, + username, username_len, + password, password_len, + nt_response) || + generate_authenticator_response(password, password_len, + peer_challenge, + auth_challenge, + username, username_len, + nt_response, + auth_response)) + return -1; } wpa_hexdump(MSG_DEBUG, "MSCHAPV2: NT Response", nt_response, MSCHAPV2_NT_RESPONSE_LEN); @@ -100,7 +100,8 @@ int mschapv2_derive_response(const u8 *identity, size_t identity_len, hash_nt_password_hash(password_hash, password_hash_hash)) return -1; } - get_master_key(password_hash_hash, nt_response, master_key); + if (get_master_key(password_hash_hash, nt_response, master_key)) + return -1; wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: Master Key", master_key, MSCHAPV2_MASTER_KEY_LEN); diff --git a/src/eap_peer/mschapv2.h b/src/eap_peer/mschapv2.h index 90dad31ef72a4..edd458b402bef 100644 --- a/src/eap_peer/mschapv2.h +++ b/src/eap_peer/mschapv2.h @@ -2,14 +2,8 @@ * MSCHAPV2 (RFC 2759) * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef MSCHAPV2_H diff --git a/src/eap_peer/tncc.c b/src/eap_peer/tncc.c index eaaa1689b58ff..f5edfd52c2502 100644 --- a/src/eap_peer/tncc.c +++ b/src/eap_peer/tncc.c @@ -2,14 +2,8 @@ * EAP-TNC - TNCC (IF-IMC and IF-TNCCS) * Copyright (c) 2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -180,11 +174,11 @@ TNC_Result TNC_TNCC_ReportMessageTypes( imc = tnc_imc[imcID]; os_free(imc->supported_types); imc->supported_types = - os_malloc(typeCount * sizeof(TNC_MessageTypeList)); + os_malloc(typeCount * sizeof(TNC_MessageType)); if (imc->supported_types == NULL) return TNC_RESULT_FATAL; os_memcpy(imc->supported_types, supportedTypes, - typeCount * sizeof(TNC_MessageTypeList)); + typeCount * sizeof(TNC_MessageType)); imc->num_supported_types = typeCount; return TNC_RESULT_SUCCESS; diff --git a/src/eap_peer/tncc.h b/src/eap_peer/tncc.h index 4d42a05b9a0e2..df2a2870f9a6d 100644 --- a/src/eap_peer/tncc.h +++ b/src/eap_peer/tncc.h @@ -2,14 +2,8 @@ * EAP-TNC - TNCC (IF-IMC and IF-TNCCS) * Copyright (c) 2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TNCC_H diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h index 92400a5680c4f..f2a7cd752f750 100644 --- a/src/eap_server/eap.h +++ b/src/eap_server/eap.h @@ -2,14 +2,8 @@ * hostapd / EAP Full Authenticator state machine (RFC 4137) * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_H @@ -22,8 +16,6 @@ struct eap_sm; -#define EAP_MAX_METHODS 8 - #define EAP_TTLS_AUTH_PAP 1 #define EAP_TTLS_AUTH_CHAP 2 #define EAP_TTLS_AUTH_MSCHAP 4 @@ -95,6 +87,7 @@ struct eap_config { void *eap_sim_db_priv; Boolean backend_auth; int eap_server; + u16 pwd_group; u8 *pac_opaque_encr_key; u8 *eap_fast_a_id; size_t eap_fast_a_id_len; @@ -106,7 +99,11 @@ struct eap_config { int tnc; struct wps_context *wps; const struct wpabuf *assoc_wps_ie; + const struct wpabuf *assoc_p2p_ie; const u8 *peer_addr; + int fragment_size; + + int pbc_in_m1; }; @@ -120,5 +117,6 @@ void eap_sm_pending_cb(struct eap_sm *sm); int eap_sm_method_pending(struct eap_sm *sm); const u8 * eap_get_identity(struct eap_sm *sm, size_t *len); struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm); +void eap_server_clear_identity(struct eap_sm *sm); #endif /* EAP_H */ diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h index 4269a8cfd6a01..f92704a11e0f6 100644 --- a/src/eap_server/eap_i.h +++ b/src/eap_server/eap_i.h @@ -2,14 +2,8 @@ * hostapd / EAP Authenticator state machine internal structures (RFC 4137) * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_I_H @@ -119,7 +113,7 @@ struct eap_sm { /* Full authenticator state machine local variables */ - /* Long-term (maintained betwen packets) */ + /* Long-term (maintained between packets) */ EapType currentMethod; int currentId; enum { @@ -157,7 +151,7 @@ struct eap_sm { int user_eap_method_index; int init_phase2; void *ssl_ctx; - void *eap_sim_db_priv; + struct eap_sim_db_data *eap_sim_db_priv; Boolean backend_auth; Boolean update_user; int eap_server; @@ -181,12 +175,19 @@ struct eap_sm { int pac_key_refresh_time; int eap_sim_aka_result_ind; int tnc; + u16 pwd_group; struct wps_context *wps; struct wpabuf *assoc_wps_ie; + struct wpabuf *assoc_p2p_ie; Boolean start_reauth; u8 peer_addr[ETH_ALEN]; + + /* Fragmentation size for EAP method init() handler */ + int fragment_size; + + int pbc_in_m1; }; int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len, diff --git a/src/eap_server/eap_methods.h b/src/eap_server/eap_methods.h index 5d4d92cbf2156..bc810a9b815c8 100644 --- a/src/eap_server/eap_methods.h +++ b/src/eap_server/eap_methods.h @@ -2,14 +2,8 @@ * EAP server method registration * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_SERVER_METHODS_H @@ -32,6 +26,7 @@ const char * eap_server_get_name(int vendor, EapType type); int eap_server_identity_register(void); int eap_server_md5_register(void); int eap_server_tls_register(void); +int eap_server_unauth_tls_register(void); int eap_server_mschapv2_register(void); int eap_server_peap_register(void); int eap_server_tlv_register(void); @@ -49,5 +44,6 @@ int eap_server_fast_register(void); int eap_server_wsc_register(void); int eap_server_ikev2_register(void); int eap_server_tnc_register(void); +int eap_server_pwd_register(void); #endif /* EAP_SERVER_METHODS_H */ diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c index fdc26f9343fb0..15f7e22846eca 100644 --- a/src/eap_server/eap_server.c +++ b/src/eap_server/eap_server.c @@ -2,14 +2,8 @@ * hostapd / EAP Full Authenticator state machine (RFC 4137) * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This state machine is based on the full authenticator state machine defined * in RFC 4137. However, to support backend authentication in RADIUS @@ -136,6 +130,14 @@ SM_STATE(EAP, INITIALIZE) { SM_ENTRY(EAP, INITIALIZE); + if (sm->eap_if.eapRestart && !sm->eap_server && sm->identity) { + /* + * Need to allow internal Identity method to be used instead + * of passthrough at the beginning of reauthentication. + */ + eap_server_clear_identity(sm); + } + sm->currentId = -1; sm->eap_if.eapSuccess = FALSE; sm->eap_if.eapFail = FALSE; @@ -273,6 +275,11 @@ SM_STATE(EAP, INTEGRITY_CHECK) { SM_ENTRY(EAP, INTEGRITY_CHECK); + if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) { + sm->ignore = TRUE; + return; + } + if (sm->m->check) { sm->ignore = sm->m->check(sm, sm->eap_method_priv, sm->eap_if.eapRespData); @@ -307,6 +314,9 @@ SM_STATE(EAP, METHOD_RESPONSE) { SM_ENTRY(EAP, METHOD_RESPONSE); + if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) + return; + sm->m->process(sm, sm->eap_method_priv, sm->eap_if.eapRespData); if (sm->m->isDone(sm, sm->eap_method_priv)) { eap_sm_Policy_update(sm, NULL, 0); @@ -378,6 +388,9 @@ SM_STATE(EAP, NAK) } sm->m = NULL; + if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) + return; + nak = wpabuf_head(sm->eap_if.eapRespData); if (nak && wpabuf_len(sm->eap_if.eapRespData) > sizeof(*nak)) { len = be_to_host16(nak->length); @@ -1028,9 +1041,12 @@ void eap_sm_process_nak(struct eap_sm *sm, const u8 *nak_list, size_t len) not_found: /* not found - remove from the list */ - os_memmove(&sm->user->methods[i], &sm->user->methods[i + 1], - (EAP_MAX_METHODS - i - 1) * - sizeof(sm->user->methods[0])); + if (i + 1 < EAP_MAX_METHODS) { + os_memmove(&sm->user->methods[i], + &sm->user->methods[i + 1], + (EAP_MAX_METHODS - i - 1) * + sizeof(sm->user->methods[0])); + } sm->user->methods[EAP_MAX_METHODS - 1].vendor = EAP_VENDOR_IETF; sm->user->methods[EAP_MAX_METHODS - 1].method = EAP_TYPE_NONE; @@ -1255,8 +1271,13 @@ struct eap_sm * eap_server_sm_init(void *eapol_ctx, sm->wps = conf->wps; if (conf->assoc_wps_ie) sm->assoc_wps_ie = wpabuf_dup(conf->assoc_wps_ie); + if (conf->assoc_p2p_ie) + sm->assoc_p2p_ie = wpabuf_dup(conf->assoc_p2p_ie); if (conf->peer_addr) os_memcpy(sm->peer_addr, conf->peer_addr, ETH_ALEN); + sm->fragment_size = conf->fragment_size; + sm->pwd_group = conf->pwd_group; + sm->pbc_in_m1 = conf->pbc_in_m1; wpa_printf(MSG_DEBUG, "EAP: Server state machine created"); @@ -1291,6 +1312,7 @@ void eap_server_sm_deinit(struct eap_sm *sm) os_free(sm->eap_if.aaaEapKeyData); eap_user_free(sm->user); wpabuf_free(sm->assoc_wps_ie); + wpabuf_free(sm->assoc_p2p_ie); os_free(sm); } @@ -1362,3 +1384,18 @@ struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm) { return &sm->eap_if; } + + +/** + * eap_server_clear_identity - Clear EAP identity information + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * + * This function can be used to clear the EAP identity information in the EAP + * server context. This allows the EAP/Identity method to be used again after + * EAPOL-Start or EAPOL-Logoff. + */ +void eap_server_clear_identity(struct eap_sm *sm) +{ + os_free(sm->identity); + sm->identity = NULL; +} diff --git a/src/eap_server/eap_server_aka.c b/src/eap_server/eap_server_aka.c index 4e7db48d0793d..469b9a0fa6538 100644 --- a/src/eap_server/eap_server_aka.c +++ b/src/eap_server/eap_server_aka.c @@ -1,15 +1,9 @@ /* - * 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 program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published 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" @@ -17,6 +11,7 @@ #include "common.h" #include "crypto/sha256.h" #include "crypto/crypto.h" +#include "crypto/random.h" #include "eap_common/eap_sim_common.h" #include "eap_server/eap_i.h" #include "eap_server/eap_sim_db.h" @@ -54,12 +49,12 @@ struct eap_aka_data { u8 *network_name; size_t network_name_len; u16 kdf; + int identity_round; + char permanent[20]; /* Permanent username */ }; -static void eap_aka_determine_identity(struct eap_sm *sm, - struct eap_aka_data *data, - int before_identity, int after_reauth); +static void eap_aka_fullauth(struct eap_sm *sm, struct eap_aka_data *data); static const char * eap_aka_state_txt(int state) @@ -92,6 +87,96 @@ static void eap_aka_state(struct eap_aka_data *data, int state) } +static int eap_aka_check_identity_reauth(struct eap_sm *sm, + struct eap_aka_data *data, + const char *username) +{ + if (data->eap_method == EAP_TYPE_AKA_PRIME && + username[0] != EAP_AKA_PRIME_REAUTH_ID_PREFIX) + return 0; + if (data->eap_method == EAP_TYPE_AKA && + username[0] != EAP_AKA_REAUTH_ID_PREFIX) + return 0; + + wpa_printf(MSG_DEBUG, "EAP-AKA: Reauth username '%s'", username); + data->reauth = eap_sim_db_get_reauth_entry(sm->eap_sim_db_priv, + username); + if (data->reauth == NULL) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown reauth identity - " + "request full auth identity"); + /* Remain in IDENTITY state for another round */ + return 0; + } + + wpa_printf(MSG_DEBUG, "EAP-AKA: Using fast re-authentication"); + os_strlcpy(data->permanent, data->reauth->permanent, + sizeof(data->permanent)); + data->counter = data->reauth->counter; + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + os_memcpy(data->k_encr, data->reauth->k_encr, + EAP_SIM_K_ENCR_LEN); + os_memcpy(data->k_aut, data->reauth->k_aut, + EAP_AKA_PRIME_K_AUT_LEN); + os_memcpy(data->k_re, data->reauth->k_re, + EAP_AKA_PRIME_K_RE_LEN); + } else { + os_memcpy(data->mk, data->reauth->mk, EAP_SIM_MK_LEN); + } + + eap_aka_state(data, REAUTH); + return 1; +} + + +static void eap_aka_check_identity(struct eap_sm *sm, + struct eap_aka_data *data) +{ + char *username; + + /* Check if we already know the identity from EAP-Response/Identity */ + + username = sim_get_username(sm->identity, sm->identity_len); + if (username == NULL) + return; + + if (eap_aka_check_identity_reauth(sm, data, username) > 0) { + os_free(username); + /* + * Since re-auth username was recognized, skip AKA/Identity + * exchange. + */ + return; + } + + if ((data->eap_method == EAP_TYPE_AKA_PRIME && + username[0] == EAP_AKA_PRIME_PSEUDONYM_PREFIX) || + (data->eap_method == EAP_TYPE_AKA && + username[0] == EAP_AKA_PSEUDONYM_PREFIX)) { + const char *permanent; + wpa_printf(MSG_DEBUG, "EAP-AKA: Pseudonym username '%s'", + username); + permanent = eap_sim_db_get_permanent( + sm->eap_sim_db_priv, username); + if (permanent == NULL) { + os_free(username); + wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown pseudonym " + "identity - request permanent identity"); + /* Remain in IDENTITY state for another round */ + return; + } + os_strlcpy(data->permanent, permanent, + sizeof(data->permanent)); + /* + * Since pseudonym username was recognized, skip AKA/Identity + * exchange. + */ + eap_aka_fullauth(sm, data); + } + + os_free(username); +} + + static void * eap_aka_init(struct eap_sm *sm) { struct eap_aka_data *data; @@ -108,8 +193,8 @@ static void * eap_aka_init(struct eap_sm *sm) data->eap_method = EAP_TYPE_AKA; data->state = IDENTITY; - eap_aka_determine_identity(sm, data, 1, 0); data->pending_id = -1; + eap_aka_check_identity(sm, data); return data; } @@ -132,18 +217,17 @@ static void * eap_aka_prime_init(struct eap_sm *sm) return NULL; data->eap_method = EAP_TYPE_AKA_PRIME; - data->network_name = os_malloc(os_strlen(network_name)); + data->network_name = (u8 *) os_strdup(network_name); if (data->network_name == NULL) { os_free(data); return NULL; } data->network_name_len = os_strlen(network_name); - os_memcpy(data->network_name, network_name, data->network_name_len); data->state = IDENTITY; - eap_aka_determine_identity(sm, data, 1, 0); data->pending_id = -1; + eap_aka_check_identity(sm, data); return data; } @@ -270,11 +354,8 @@ static struct wpabuf * eap_aka_build_identity(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Identity"); msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, EAP_AKA_SUBTYPE_IDENTITY); - if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity, - sm->identity_len)) { - wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ"); - eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0); - } else { + data->identity_round++; + if (data->identity_round == 1) { /* * RFC 4187, Chap. 4.1.4 recommends that identity from EAP is * ignored and the AKA/Identity is used to request the @@ -282,6 +363,19 @@ static struct wpabuf * eap_aka_build_identity(struct eap_sm *sm, */ wpa_printf(MSG_DEBUG, " AT_ANY_ID_REQ"); eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0); + } else if (data->identity_round > 3) { + /* Cannot use more than three rounds of Identity messages */ + eap_sim_msg_free(msg); + return NULL; + } else if (sm->identity && 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); } buf = eap_sim_msg_finish(msg, NULL, NULL, 0); if (eap_aka_add_id_msg(data, buf) < 0) { @@ -298,12 +392,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"); @@ -440,7 +545,7 @@ static struct wpabuf * eap_aka_build_reauth(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Re-authentication"); - if (os_get_random(data->nonce_s, EAP_SIM_NONCE_S_LEN)) + if (random_get_bytes(data->nonce_s, EAP_SIM_NONCE_S_LEN)) return NULL; wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA: NONCE_S", data->nonce_s, EAP_SIM_NONCE_S_LEN); @@ -607,92 +712,83 @@ static Boolean eap_aka_subtype_ok(struct eap_aka_data *data, u8 subtype) static void eap_aka_determine_identity(struct eap_sm *sm, - struct eap_aka_data *data, - int before_identity, int after_reauth) + struct eap_aka_data *data) { - const u8 *identity; - size_t identity_len; - int res; + char *username; - identity = NULL; - identity_len = 0; + wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity", + sm->identity, sm->identity_len); - if (after_reauth && data->reauth) { - identity = data->reauth->identity; - identity_len = data->reauth->identity_len; - } else if (sm->identity && sm->identity_len > 0 && - sm->identity[0] == EAP_AKA_PERMANENT_PREFIX) { - identity = sm->identity; - identity_len = sm->identity_len; - } else { - identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, - sm->identity, - sm->identity_len, - &identity_len); - if (identity == NULL) { - data->reauth = eap_sim_db_get_reauth_entry( - sm->eap_sim_db_priv, sm->identity, - sm->identity_len); - if (data->reauth && - data->reauth->aka_prime != - (data->eap_method == EAP_TYPE_AKA_PRIME)) { - wpa_printf(MSG_DEBUG, "EAP-AKA: Reauth data " - "was for different AKA version"); - data->reauth = NULL; - } - if (data->reauth) { - wpa_printf(MSG_DEBUG, "EAP-AKA: Using fast " - "re-authentication"); - identity = data->reauth->identity; - identity_len = data->reauth->identity_len; - data->counter = data->reauth->counter; - if (data->eap_method == EAP_TYPE_AKA_PRIME) { - os_memcpy(data->k_encr, - data->reauth->k_encr, - EAP_SIM_K_ENCR_LEN); - os_memcpy(data->k_aut, - data->reauth->k_aut, - EAP_AKA_PRIME_K_AUT_LEN); - os_memcpy(data->k_re, - data->reauth->k_re, - EAP_AKA_PRIME_K_RE_LEN); - } else { - os_memcpy(data->mk, data->reauth->mk, - EAP_SIM_MK_LEN); - } - } - } + username = sim_get_username(sm->identity, sm->identity_len); + if (username == NULL) { + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); + return; } - if (identity == NULL || - eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity, - sm->identity_len) < 0) { - if (before_identity) { - wpa_printf(MSG_DEBUG, "EAP-AKA: Permanent user name " - "not known - send AKA-Identity request"); - eap_aka_state(data, IDENTITY); - return; - } else { - wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown whether the " - "permanent user name is known; try to use " - "it"); - /* eap_sim_db_get_aka_auth() will report failure, if - * this identity is not known. */ - } + if (eap_aka_check_identity_reauth(sm, data, username) > 0) { + os_free(username); + return; } - wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity", - identity, identity_len); + if (((data->eap_method == EAP_TYPE_AKA_PRIME && + username[0] == EAP_AKA_PRIME_REAUTH_ID_PREFIX) || + (data->eap_method == EAP_TYPE_AKA && + username[0] == EAP_AKA_REAUTH_ID_PREFIX)) && + data->identity_round == 1) { + /* Remain in IDENTITY state for another round to request full + * auth identity since we did not recognize reauth id */ + os_free(username); + return; + } - if (!after_reauth && data->reauth) { - eap_aka_state(data, REAUTH); + if ((data->eap_method == EAP_TYPE_AKA_PRIME && + username[0] == EAP_AKA_PRIME_PSEUDONYM_PREFIX) || + (data->eap_method == EAP_TYPE_AKA && + username[0] == EAP_AKA_PSEUDONYM_PREFIX)) { + const char *permanent; + wpa_printf(MSG_DEBUG, "EAP-AKA: Pseudonym username '%s'", + username); + permanent = eap_sim_db_get_permanent( + sm->eap_sim_db_priv, username); + os_free(username); + if (permanent == NULL) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown pseudonym " + "identity - request permanent identity"); + /* Remain in IDENTITY state for another round */ + return; + } + os_strlcpy(data->permanent, permanent, + sizeof(data->permanent)); + } else if ((data->eap_method == EAP_TYPE_AKA_PRIME && + username[0] == EAP_AKA_PRIME_PERMANENT_PREFIX) || + (data->eap_method == EAP_TYPE_AKA && + username[0] == EAP_AKA_PERMANENT_PREFIX)) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Permanent username '%s'", + username); + os_strlcpy(data->permanent, username, sizeof(data->permanent)); + os_free(username); + } else { + wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized username '%s'", + username); + os_free(username); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); return; } - res = eap_sim_db_get_aka_auth(sm->eap_sim_db_priv, identity, - identity_len, data->rand, data->autn, - data->ik, data->ck, data->res, - &data->res_len, sm); + eap_aka_fullauth(sm, data); +} + + +static void eap_aka_fullauth(struct eap_sm *sm, struct eap_aka_data *data) +{ + size_t identity_len; + int res; + + res = eap_sim_db_get_aka_auth(sm->eap_sim_db_priv, data->permanent, + data->rand, data->autn, data->ik, + data->ck, data->res, &data->res_len, sm); if (res == EAP_SIM_DB_PENDING) { wpa_printf(MSG_DEBUG, "EAP-AKA: AKA authentication data " "not yet available - pending request"); @@ -737,7 +833,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 { @@ -756,6 +852,8 @@ static void eap_aka_process_identity(struct eap_sm *sm, struct wpabuf *respData, struct eap_sim_attrs *attr) { + u8 *new_identity; + wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Identity"); if (attr->mac || attr->iv || attr->encr_data) { @@ -766,17 +864,30 @@ static void eap_aka_process_identity(struct eap_sm *sm, return; } - if (attr->identity) { - os_free(sm->identity); - sm->identity = os_malloc(attr->identity_len); - if (sm->identity) { - os_memcpy(sm->identity, attr->identity, - attr->identity_len); - sm->identity_len = attr->identity_len; - } + /* + * We always request identity with AKA/Identity, so the peer is + * required to have replied with one. + */ + if (!attr->identity || attr->identity_len == 0) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Peer did not provide any " + "identity"); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); + return; + } + + new_identity = os_malloc(attr->identity_len); + if (new_identity == NULL) { + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); + return; } + os_free(sm->identity); + sm->identity = new_identity; + os_memcpy(sm->identity, attr->identity, attr->identity_len); + sm->identity_len = attr->identity_len; - eap_aka_determine_identity(sm, data, 0, 0); + eap_aka_determine_identity(sm, data); if (eap_get_id(respData) == data->pending_id) { data->pending_id = -1; eap_aka_add_id_msg(data, respData); @@ -801,9 +912,6 @@ static void eap_aka_process_challenge(struct eap_sm *sm, struct wpabuf *respData, struct eap_sim_attrs *attr) { - const u8 *identity; - size_t identity_len; - wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Challenge"); #ifdef EAP_SERVER_AKA_PRIME @@ -876,16 +984,8 @@ static void eap_aka_process_challenge(struct eap_sm *sm, } else eap_aka_state(data, SUCCESS); - identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, sm->identity, - sm->identity_len, &identity_len); - if (identity == NULL) { - identity = sm->identity; - identity_len = sm->identity_len; - } - if (data->next_pseudonym) { - eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity, - identity_len, + eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, data->permanent, data->next_pseudonym); data->next_pseudonym = NULL; } @@ -893,16 +993,15 @@ static void eap_aka_process_challenge(struct eap_sm *sm, if (data->eap_method == EAP_TYPE_AKA_PRIME) { #ifdef EAP_SERVER_AKA_PRIME eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv, - identity, - identity_len, + data->permanent, data->next_reauth_id, data->counter + 1, data->k_encr, data->k_aut, data->k_re); #endif /* EAP_SERVER_AKA_PRIME */ } else { - eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity, - identity_len, + eap_sim_db_add_reauth(sm->eap_sim_db_priv, + data->permanent, data->next_reauth_id, data->counter + 1, data->mk); @@ -931,9 +1030,8 @@ static void eap_aka_process_sync_failure(struct eap_sm *sm, * maintaining a local flag stating whether this AUTS has already been * reported. */ if (!data->auts_reported && - eap_sim_db_resynchronize(sm->eap_sim_db_priv, sm->identity, - sm->identity_len, attr->auts, - data->rand)) { + eap_sim_db_resynchronize(sm->eap_sim_db_priv, data->permanent, + attr->auts, data->rand)) { wpa_printf(MSG_WARNING, "EAP-AKA: Resynchronization failed"); data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; eap_aka_state(data, NOTIFICATION); @@ -941,8 +1039,7 @@ static void eap_aka_process_sync_failure(struct eap_sm *sm, } data->auts_reported = 1; - /* Try again after resynchronization */ - eap_aka_determine_identity(sm, data, 0, 0); + /* Remain in CHALLENGE state to re-try after resynchronization */ } @@ -953,8 +1050,6 @@ static void eap_aka_process_reauth(struct eap_sm *sm, { struct eap_sim_attrs eattr; u8 *decrypted = NULL; - const u8 *identity, *id2; - size_t identity_len, id2_len; wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Reauthentication"); @@ -997,7 +1092,7 @@ static void eap_aka_process_reauth(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response " "included AT_COUNTER_TOO_SMALL - starting full " "authentication"); - eap_aka_determine_identity(sm, data, 0, 1); + eap_aka_fullauth(sm, data); return; } @@ -1008,40 +1103,19 @@ static void eap_aka_process_reauth(struct eap_sm *sm, } else eap_aka_state(data, SUCCESS); - if (data->reauth) { - identity = data->reauth->identity; - identity_len = data->reauth->identity_len; - } else { - identity = sm->identity; - identity_len = sm->identity_len; - } - - id2 = eap_sim_db_get_permanent(sm->eap_sim_db_priv, identity, - identity_len, &id2_len); - if (id2) { - identity = id2; - identity_len = id2_len; - } - - if (data->next_pseudonym) { - eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity, - identity_len, data->next_pseudonym); - data->next_pseudonym = NULL; - } if (data->next_reauth_id) { if (data->eap_method == EAP_TYPE_AKA_PRIME) { #ifdef EAP_SERVER_AKA_PRIME eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv, - identity, - identity_len, + data->permanent, data->next_reauth_id, data->counter + 1, data->k_encr, data->k_aut, data->k_re); #endif /* EAP_SERVER_AKA_PRIME */ } else { - eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity, - identity_len, + eap_sim_db_add_reauth(sm->eap_sim_db_priv, + data->permanent, data->next_reauth_id, data->counter + 1, data->mk); diff --git a/src/eap_server/eap_server_fast.c b/src/eap_server/eap_server_fast.c index 39beb33056fb7..fcb80dc756de1 100644 --- a/src/eap_server/eap_server_fast.c +++ b/src/eap_server/eap_server_fast.c @@ -2,14 +2,8 @@ * EAP-FAST server (RFC 4851) * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -18,6 +12,7 @@ #include "crypto/aes_wrap.h" #include "crypto/sha1.h" #include "crypto/tls.h" +#include "crypto/random.h" #include "eap_common/eap_tlv_common.h" #include "eap_common/eap_fast_common.h" #include "eap_i.h" @@ -642,7 +637,7 @@ static struct wpabuf * eap_fast_build_crypto_binding( binding->version = EAP_FAST_VERSION; binding->received_version = data->peer_version; binding->subtype = EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST; - if (os_get_random(binding->nonce, sizeof(binding->nonce)) < 0) { + if (random_get_bytes(binding->nonce, sizeof(binding->nonce)) < 0) { wpabuf_free(buf); return NULL; } @@ -692,7 +687,7 @@ static struct wpabuf * eap_fast_build_pac(struct eap_sm *sm, struct eap_tlv_result_tlv *result; struct os_time now; - if (os_get_random(pac_key, EAP_FAST_PAC_KEY_LEN) < 0 || + if (random_get_bytes(pac_key, EAP_FAST_PAC_KEY_LEN) < 0 || os_get_time(&now) < 0) return NULL; wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Generated PAC-Key", diff --git a/src/eap_server/eap_server_gpsk.c b/src/eap_server/eap_server_gpsk.c index d0c7559d758b4..2853c486ab4c4 100644 --- a/src/eap_server/eap_server_gpsk.c +++ b/src/eap_server/eap_server_gpsk.c @@ -2,19 +2,14 @@ * hostapd / EAP-GPSK (RFC 5433) server * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#include "crypto/random.h" #include "eap_server/eap_i.h" #include "eap_common/eap_gpsk_common.h" @@ -120,7 +115,7 @@ static struct wpabuf * eap_gpsk_build_gpsk_1(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-GPSK: Request/GPSK-1"); - if (os_get_random(data->rand_server, EAP_GPSK_RAND_LEN)) { + if (random_get_bytes(data->rand_server, EAP_GPSK_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to get random data"); eap_gpsk_state(data, FAILURE); return NULL; diff --git a/src/eap_server/eap_server_gtc.c b/src/eap_server/eap_server_gtc.c index 79b9696b2c953..f423106bfc9c8 100644 --- a/src/eap_server/eap_server_gtc.c +++ b/src/eap_server/eap_server_gtc.c @@ -2,14 +2,8 @@ * hostapd / EAP-GTC (RFC 3748) * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/eap_server/eap_server_identity.c b/src/eap_server/eap_server_identity.c index cd8da2a632b9b..51dc4e8b4f57f 100644 --- a/src/eap_server/eap_server_identity.c +++ b/src/eap_server/eap_server_identity.c @@ -2,14 +2,8 @@ * hostapd / EAP-Identity * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/eap_server/eap_server_ikev2.c b/src/eap_server/eap_server_ikev2.c index 06074ee28fb05..42aaca2b48129 100644 --- a/src/eap_server/eap_server_ikev2.c +++ b/src/eap_server/eap_server_ikev2.c @@ -2,14 +2,8 @@ * EAP-IKEv2 server (RFC 5106) * Copyright (c) 2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -93,7 +87,8 @@ static void * eap_ikev2_init(struct eap_sm *sm) if (data == NULL) return NULL; data->state = MSG; - data->fragment_size = IKEV2_FRAGMENT_SIZE; + data->fragment_size = sm->fragment_size > 0 ? sm->fragment_size : + IKEV2_FRAGMENT_SIZE; data->ikev2.state = SA_INIT; data->ikev2.peer_auth = PEER_AUTH_SECRET; data->ikev2.key_pad = (u8 *) os_strdup("Key Pad for EAP-IKEv2"); diff --git a/src/eap_server/eap_server_md5.c b/src/eap_server/eap_server_md5.c index dee2dc5a013e5..5a5e2907efd62 100644 --- a/src/eap_server/eap_server_md5.c +++ b/src/eap_server/eap_server_md5.c @@ -1,20 +1,15 @@ /* * hostapd / EAP-MD5 server - * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-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 - * published 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" #include "common.h" +#include "crypto/random.h" #include "eap_i.h" #include "eap_common/chap.h" @@ -52,7 +47,7 @@ static struct wpabuf * eap_md5_buildReq(struct eap_sm *sm, void *priv, u8 id) struct eap_md5_data *data = priv; struct wpabuf *req; - if (os_get_random(data->challenge, CHALLENGE_LEN)) { + if (random_get_bytes(data->challenge, CHALLENGE_LEN)) { wpa_printf(MSG_ERROR, "EAP-MD5: Failed to get random data"); data->state = FAILURE; return NULL; @@ -124,8 +119,12 @@ static void eap_md5_process(struct eap_sm *sm, void *priv, wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", pos, CHAP_MD5_LEN); id = eap_get_id(respData); - chap_md5(id, sm->user->password, sm->user->password_len, - data->challenge, CHALLENGE_LEN, hash); + if (chap_md5(id, sm->user->password, sm->user->password_len, + data->challenge, CHALLENGE_LEN, hash)) { + wpa_printf(MSG_INFO, "EAP-MD5: CHAP MD5 operation failed"); + data->state = FAILURE; + return; + } if (os_memcmp(hash, pos, CHAP_MD5_LEN) == 0) { wpa_printf(MSG_DEBUG, "EAP-MD5: Done - Success"); diff --git a/src/eap_server/eap_server_methods.c b/src/eap_server/eap_server_methods.c index 900a5dd318105..0209fad639479 100644 --- a/src/eap_server/eap_server_methods.c +++ b/src/eap_server/eap_server_methods.c @@ -2,14 +2,8 @@ * EAP server method registration * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -167,6 +161,8 @@ void eap_server_unregister_methods(void) const char * eap_server_get_name(int vendor, EapType type) { struct eap_method *m; + if (vendor == EAP_VENDOR_IETF && type == EAP_TYPE_EXPANDED) + return "expanded"; for (m = eap_methods; m; m = m->next) { if (m->vendor == vendor && m->method == type) return m->name; diff --git a/src/eap_server/eap_server_mschapv2.c b/src/eap_server/eap_server_mschapv2.c index 39d1c6ef2b368..8d3dd5233beb5 100644 --- a/src/eap_server/eap_server_mschapv2.c +++ b/src/eap_server/eap_server_mschapv2.c @@ -2,20 +2,15 @@ * hostapd / EAP-MSCHAPv2 (draft-kamath-pppext-eap-mschapv2-00.txt) server * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" #include "crypto/ms_funcs.h" +#include "crypto/random.h" #include "eap_i.h" @@ -109,7 +104,7 @@ static struct wpabuf * eap_mschapv2_build_challenge( size_t ms_len; if (!data->auth_challenge_from_tls && - os_get_random(data->auth_challenge, CHALLENGE_LEN)) { + random_get_bytes(data->auth_challenge, CHALLENGE_LEN)) { wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to get random " "data"); data->state = FAILURE; @@ -404,9 +399,12 @@ static void eap_mschapv2_process_response(struct eap_sm *sm, if (sm->user->password_hash) { pw_hash = sm->user->password; } else { - nt_password_hash(sm->user->password, - sm->user->password_len, - pw_hash_buf); + if (nt_password_hash(sm->user->password, + sm->user->password_len, + pw_hash_buf) < 0) { + data->state = FAILURE; + return; + } pw_hash = pw_hash_buf; } generate_authenticator_response_pwhash( diff --git a/src/eap_server/eap_server_pax.c b/src/eap_server/eap_server_pax.c index 1dc023b699551..35a42ad107487 100644 --- a/src/eap_server/eap_server_pax.c +++ b/src/eap_server/eap_server_pax.c @@ -2,19 +2,14 @@ * hostapd / EAP-PAX (RFC 4746) server * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#include "crypto/random.h" #include "eap_server/eap_i.h" #include "eap_common/eap_pax_common.h" @@ -82,7 +77,7 @@ static struct wpabuf * eap_pax_build_std_1(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (sending)"); - if (os_get_random(data->rand.r.x, EAP_PAX_RAND_LEN)) { + if (random_get_bytes(data->rand.r.x, EAP_PAX_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data"); data->state = FAILURE; return NULL; diff --git a/src/eap_server/eap_server_peap.c b/src/eap_server/eap_server_peap.c index 674ecd2231e2b..68253c4383555 100644 --- a/src/eap_server/eap_server_peap.c +++ b/src/eap_server/eap_server_peap.c @@ -2,14 +2,8 @@ * hostapd / EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt) * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -17,6 +11,7 @@ #include "common.h" #include "crypto/sha1.h" #include "crypto/tls.h" +#include "crypto/random.h" #include "eap_i.h" #include "eap_tls_common.h" #include "eap_common/eap_tlv_common.h" @@ -350,8 +345,12 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) * in the end of the label just before ISK; is that just a typo?) */ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40); - peap_prfplus(data->peap_version, tk, 40, "Inner Methods Compound Keys", - isk, sizeof(isk), imck, sizeof(imck)); + if (peap_prfplus(data->peap_version, tk, 40, + "Inner Methods Compound Keys", + isk, sizeof(isk), imck, sizeof(imck)) < 0) { + os_free(tk); + return -1; + } wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)", imck, sizeof(imck)); @@ -414,7 +413,7 @@ static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm, #endif /* EAP_SERVER_TNC */ if (eap_peap_derive_cmk(sm, data) < 0 || - os_get_random(data->binding_nonce, 32)) { + random_get_bytes(data->binding_nonce, 32)) { wpabuf_free(buf); return NULL; } @@ -1059,8 +1058,6 @@ static void eap_peap_process_phase2(struct eap_sm *sm, wpa_hexdump_buf_key(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP", in_decrypted); - hdr = wpabuf_head(in_decrypted); - if (data->peap_version == 0 && data->state != PHASE2_TLV) { const struct eap_hdr *resp; struct eap_hdr *nhdr; @@ -1319,9 +1316,10 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) * termination for this label while the one used for deriving * IPMK|CMK did not use null termination. */ - peap_prfplus(data->peap_version, data->ipmk, 40, - "Session Key Generating Function", - (u8 *) "\00", 1, csk, sizeof(csk)); + if (peap_prfplus(data->peap_version, data->ipmk, 40, + "Session Key Generating Function", + (u8 *) "\00", 1, csk, sizeof(csk)) < 0) + return NULL; wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk)); eapKeyData = os_malloc(EAP_TLS_KEY_LEN); if (eapKeyData) { diff --git a/src/eap_server/eap_server_psk.c b/src/eap_server/eap_server_psk.c index 4c30346e59ced..0cd979920f4ad 100644 --- a/src/eap_server/eap_server_psk.c +++ b/src/eap_server/eap_server_psk.c @@ -2,14 +2,8 @@ * hostapd / EAP-PSK (RFC 4764) server * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * Note: EAP-PSK is an EAP authentication method and as such, completely * different from WPA-PSK. This file is not needed for WPA-PSK functionality. @@ -19,6 +13,7 @@ #include "common.h" #include "crypto/aes_wrap.h" +#include "crypto/random.h" #include "eap_common/eap_psk_common.h" #include "eap_server/eap_i.h" @@ -66,7 +61,7 @@ static struct wpabuf * eap_psk_build_1(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-PSK: PSK-1 (sending)"); - if (os_get_random(data->rand_s, EAP_PSK_RAND_LEN)) { + if (random_get_bytes(data->rand_s, EAP_PSK_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data"); data->state = FAILURE; return NULL; @@ -124,8 +119,10 @@ static struct wpabuf * eap_psk_build_3(struct eap_sm *sm, os_memcpy(buf, data->id_s, data->id_s_len); os_memcpy(buf + data->id_s_len, data->rand_p, EAP_PSK_RAND_LEN); - if (omac1_aes_128(data->ak, buf, buflen, psk->mac_s)) + if (omac1_aes_128(data->ak, buf, buflen, psk->mac_s)) { + os_free(buf); goto fail; + } os_free(buf); if (eap_psk_derive_keys(data->kdk, data->rand_p, data->tek, data->msk, diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c new file mode 100644 index 0000000000000..b61061bce702d --- /dev/null +++ b/src/eap_server/eap_server_pwd.c @@ -0,0 +1,1045 @@ +/* + * hostapd / EAP-pwd (RFC 5931) server + * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/sha256.h" +#include "eap_server/eap_i.h" +#include "eap_common/eap_pwd_common.h" + + +struct eap_pwd_data { + enum { + PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req, SUCCESS, FAILURE + } state; + u8 *id_peer; + size_t id_peer_len; + u8 *id_server; + size_t id_server_len; + u8 *password; + size_t password_len; + u32 token; + u16 group_num; + EAP_PWD_group *grp; + + struct wpabuf *inbuf; + size_t in_frag_pos; + struct wpabuf *outbuf; + size_t out_frag_pos; + size_t mtu; + + BIGNUM *k; + BIGNUM *private_value; + BIGNUM *peer_scalar; + BIGNUM *my_scalar; + EC_POINT *my_element; + EC_POINT *peer_element; + + u8 my_confirm[SHA256_MAC_LEN]; + + u8 msk[EAP_MSK_LEN]; + u8 emsk[EAP_EMSK_LEN]; + + BN_CTX *bnctx; +}; + + +static const char * eap_pwd_state_txt(int state) +{ + switch (state) { + case PWD_ID_Req: + return "PWD-ID-Req"; + case PWD_Commit_Req: + return "PWD-Commit-Req"; + case PWD_Confirm_Req: + return "PWD-Confirm-Req"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "PWD-Unk"; + } +} + + +static void eap_pwd_state(struct eap_pwd_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-pwd: %s -> %s", + eap_pwd_state_txt(data->state), eap_pwd_state_txt(state)); + data->state = state; +} + + +static void * eap_pwd_init(struct eap_sm *sm) +{ + struct eap_pwd_data *data; + + if (sm->user == NULL || sm->user->password == NULL || + sm->user->password_len == 0) { + wpa_printf(MSG_INFO, "EAP-PWD (server): Password is not " + "configured"); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + data->group_num = sm->pwd_group; + wpa_printf(MSG_DEBUG, "EAP-pwd: Selected group number %d", + data->group_num); + data->state = PWD_ID_Req; + + data->id_server = (u8 *) os_strdup("server"); + if (data->id_server) + data->id_server_len = os_strlen((char *) data->id_server); + + data->password = os_malloc(sm->user->password_len); + if (data->password == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: Memory allocation password " + "fail"); + os_free(data->id_server); + os_free(data); + return NULL; + } + data->password_len = sm->user->password_len; + os_memcpy(data->password, sm->user->password, data->password_len); + + data->bnctx = BN_CTX_new(); + if (data->bnctx == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail"); + os_free(data->password); + os_free(data->id_server); + os_free(data); + return NULL; + } + + data->in_frag_pos = data->out_frag_pos = 0; + data->inbuf = data->outbuf = NULL; + data->mtu = 1020; /* default from RFC 5931, make it configurable! */ + + return data; +} + + +static void eap_pwd_reset(struct eap_sm *sm, void *priv) +{ + struct eap_pwd_data *data = priv; + + BN_free(data->private_value); + BN_free(data->peer_scalar); + BN_free(data->my_scalar); + BN_free(data->k); + BN_CTX_free(data->bnctx); + EC_POINT_free(data->my_element); + EC_POINT_free(data->peer_element); + os_free(data->id_peer); + os_free(data->id_server); + os_free(data->password); + if (data->grp) { + EC_GROUP_free(data->grp->group); + EC_POINT_free(data->grp->pwe); + BN_free(data->grp->order); + BN_free(data->grp->prime); + os_free(data->grp); + } + os_free(data); +} + + +static void eap_pwd_build_id_req(struct eap_sm *sm, struct eap_pwd_data *data, + u8 id) +{ + wpa_printf(MSG_DEBUG, "EAP-pwd: ID/Request"); + /* + * if we're fragmenting then we already have an id request, just return + */ + if (data->out_frag_pos) + return; + + data->outbuf = wpabuf_alloc(sizeof(struct eap_pwd_id) + + data->id_server_len); + if (data->outbuf == NULL) { + eap_pwd_state(data, FAILURE); + return; + } + + /* an lfsr is good enough to generate unpredictable tokens */ + data->token = os_random(); + wpabuf_put_be16(data->outbuf, data->group_num); + wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC); + wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF); + wpabuf_put_data(data->outbuf, &data->token, sizeof(data->token)); + wpabuf_put_u8(data->outbuf, EAP_PWD_PREP_NONE); + wpabuf_put_data(data->outbuf, data->id_server, data->id_server_len); +} + + +static void eap_pwd_build_commit_req(struct eap_sm *sm, + struct eap_pwd_data *data, u8 id) +{ + BIGNUM *mask = NULL, *x = NULL, *y = NULL; + u8 *scalar = NULL, *element = NULL; + u16 offset; + + wpa_printf(MSG_DEBUG, "EAP-pwd: Commit/Request"); + /* + * if we're fragmenting then we already have an commit request, just + * return + */ + if (data->out_frag_pos) + return; + + if (((data->private_value = BN_new()) == NULL) || + ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) || + ((data->my_scalar = BN_new()) == NULL) || + ((mask = BN_new()) == NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): scalar allocation " + "fail"); + goto fin; + } + + BN_rand_range(data->private_value, data->grp->order); + BN_rand_range(mask, data->grp->order); + BN_add(data->my_scalar, data->private_value, mask); + BN_mod(data->my_scalar, data->my_scalar, data->grp->order, + data->bnctx); + + if (!EC_POINT_mul(data->grp->group, data->my_element, NULL, + data->grp->pwe, mask, data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): element allocation " + "fail"); + eap_pwd_state(data, FAILURE); + goto fin; + } + + if (!EC_POINT_invert(data->grp->group, data->my_element, data->bnctx)) + { + wpa_printf(MSG_INFO, "EAP-PWD (server): element inversion " + "fail"); + goto fin; + } + BN_free(mask); + + if (((x = BN_new()) == NULL) || + ((y = BN_new()) == NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): point allocation " + "fail"); + goto fin; + } + if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, + data->my_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): point assignment " + "fail"); + goto fin; + } + + if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) || + ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) == + NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): data allocation fail"); + goto fin; + } + + /* + * bignums occupy as little memory as possible so one that is + * sufficiently smaller than the prime or order might need pre-pending + * with zeros. + */ + os_memset(scalar, 0, BN_num_bytes(data->grp->order)); + os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2); + offset = BN_num_bytes(data->grp->order) - + BN_num_bytes(data->my_scalar); + BN_bn2bin(data->my_scalar, scalar + offset); + + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); + BN_bn2bin(x, element + offset); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); + BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset); + + data->outbuf = wpabuf_alloc(2 * BN_num_bytes(data->grp->prime) + + BN_num_bytes(data->grp->order)); + if (data->outbuf == NULL) + goto fin; + + /* We send the element as (x,y) followed by the scalar */ + wpabuf_put_data(data->outbuf, element, + 2 * BN_num_bytes(data->grp->prime)); + wpabuf_put_data(data->outbuf, scalar, BN_num_bytes(data->grp->order)); + +fin: + os_free(scalar); + os_free(element); + BN_free(x); + BN_free(y); + if (data->outbuf == NULL) + eap_pwd_state(data, FAILURE); +} + + +static void eap_pwd_build_confirm_req(struct eap_sm *sm, + struct eap_pwd_data *data, u8 id) +{ + BIGNUM *x = NULL, *y = NULL; + struct crypto_hash *hash; + u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; + u16 grp; + int offset; + + wpa_printf(MSG_DEBUG, "EAP-pwd: Confirm/Request"); + /* + * if we're fragmenting then we already have an confirm request, just + * return + */ + if (data->out_frag_pos) + return; + + /* Each component of the cruft will be at most as big as the prime */ + if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) || + ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): debug allocation " + "fail"); + goto fin; + } + + /* + * commit is H(k | server_element | server_scalar | peer_element | + * peer_scalar | ciphersuite) + */ + hash = eap_pwd_h_init(); + if (hash == NULL) + goto fin; + + /* + * Zero the memory each time because this is mod prime math and some + * value may start with a few zeros and the previous one did not. + * + * First is k + */ + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); + BN_bn2bin(data->k, cruft + offset); + 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, + data->my_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " + "assignment fail"); + goto fin; + } + + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); + BN_bn2bin(x, cruft + offset); + 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); + 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); + 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, + data->peer_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " + "assignment fail"); + goto fin; + } + + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); + BN_bn2bin(x, cruft + offset); + 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); + 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); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); + + /* ciphersuite */ + grp = htons(data->group_num); + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + ptr = cruft; + os_memcpy(ptr, &grp, sizeof(u16)); + ptr += sizeof(u16); + *ptr = EAP_PWD_DEFAULT_RAND_FUNC; + ptr += sizeof(u8); + *ptr = EAP_PWD_DEFAULT_PRF; + ptr += sizeof(u8); + eap_pwd_h_update(hash, cruft, ptr - cruft); + + /* all done with the random function */ + eap_pwd_h_final(hash, conf); + os_memcpy(data->my_confirm, conf, SHA256_MAC_LEN); + + data->outbuf = wpabuf_alloc(SHA256_MAC_LEN); + if (data->outbuf == NULL) + goto fin; + + wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN); + +fin: + os_free(cruft); + BN_free(x); + BN_free(y); + if (data->outbuf == NULL) + eap_pwd_state(data, FAILURE); +} + + +static struct wpabuf * +eap_pwd_build_req(struct eap_sm *sm, void *priv, u8 id) +{ + struct eap_pwd_data *data = priv; + struct wpabuf *req; + u8 lm_exch; + const u8 *buf; + u16 totlen = 0; + size_t len; + + /* + * if we're buffering response fragments then just ACK + */ + if (data->in_frag_pos) { + wpa_printf(MSG_DEBUG, "EAP-pwd: ACKing a fragment!!"); + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, + EAP_PWD_HDR_SIZE, EAP_CODE_REQUEST, id); + if (req == NULL) { + eap_pwd_state(data, FAILURE); + return NULL; + } + switch (data->state) { + case PWD_ID_Req: + wpabuf_put_u8(req, EAP_PWD_OPCODE_ID_EXCH); + break; + case PWD_Commit_Req: + wpabuf_put_u8(req, EAP_PWD_OPCODE_COMMIT_EXCH); + break; + case PWD_Confirm_Req: + wpabuf_put_u8(req, EAP_PWD_OPCODE_CONFIRM_EXCH); + break; + default: + eap_pwd_state(data, FAILURE); /* just to be sure */ + wpabuf_free(req); + return NULL; + } + return req; + } + + /* + * build the data portion of a request + */ + switch (data->state) { + case PWD_ID_Req: + eap_pwd_build_id_req(sm, data, id); + lm_exch = EAP_PWD_OPCODE_ID_EXCH; + break; + case PWD_Commit_Req: + eap_pwd_build_commit_req(sm, data, id); + lm_exch = EAP_PWD_OPCODE_COMMIT_EXCH; + break; + case PWD_Confirm_Req: + eap_pwd_build_confirm_req(sm, data, id); + lm_exch = EAP_PWD_OPCODE_CONFIRM_EXCH; + break; + default: + wpa_printf(MSG_INFO, "EAP-pwd: Unknown state %d in build_req", + data->state); + eap_pwd_state(data, FAILURE); + lm_exch = 0; /* hush now, sweet compiler */ + break; + } + + if (data->state == FAILURE) + return NULL; + + /* + * determine whether that data needs to be fragmented + */ + len = wpabuf_len(data->outbuf) - data->out_frag_pos; + if ((len + EAP_PWD_HDR_SIZE) > data->mtu) { + len = data->mtu - EAP_PWD_HDR_SIZE; + EAP_PWD_SET_MORE_BIT(lm_exch); + /* + * if this is the first fragment, need to set the M bit + * and add the total length to the eap_pwd_hdr + */ + if (data->out_frag_pos == 0) { + EAP_PWD_SET_LENGTH_BIT(lm_exch); + totlen = wpabuf_len(data->outbuf) + + EAP_PWD_HDR_SIZE + sizeof(u16); + len -= sizeof(u16); + wpa_printf(MSG_DEBUG, "EAP-pwd: Fragmenting output, " + "total length = %d", totlen); + } + wpa_printf(MSG_DEBUG, "EAP-pwd: Send a %d byte fragment", + (int) len); + } + + /* + * alloc an eap request and populate it with the data + */ + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, + EAP_PWD_HDR_SIZE + len + + (totlen ? sizeof(u16) : 0), + EAP_CODE_REQUEST, id); + if (req == NULL) { + eap_pwd_state(data, FAILURE); + return NULL; + } + + wpabuf_put_u8(req, lm_exch); + if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) + wpabuf_put_be16(req, totlen); + + buf = wpabuf_head_u8(data->outbuf); + wpabuf_put_data(req, buf + data->out_frag_pos, len); + data->out_frag_pos += len; + /* + * either not fragged or last fragment, either way free up the data + */ + if (data->out_frag_pos >= wpabuf_len(data->outbuf)) { + wpabuf_free(data->outbuf); + data->out_frag_pos = 0; + } + + return req; +} + + +static Boolean eap_pwd_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_pwd_data *data = priv; + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, respData, &len); + if (pos == NULL || len < 1) { + wpa_printf(MSG_INFO, "EAP-pwd: Invalid frame"); + return TRUE; + } + + wpa_printf(MSG_DEBUG, "EAP-pwd: Received frame: exch = %d, len = %d", + EAP_PWD_GET_EXCHANGE(*pos), (int) len); + + if (data->state == PWD_ID_Req && + ((EAP_PWD_GET_EXCHANGE(*pos)) == EAP_PWD_OPCODE_ID_EXCH)) + return FALSE; + + if (data->state == PWD_Commit_Req && + ((EAP_PWD_GET_EXCHANGE(*pos)) == EAP_PWD_OPCODE_COMMIT_EXCH)) + return FALSE; + + if (data->state == PWD_Confirm_Req && + ((EAP_PWD_GET_EXCHANGE(*pos)) == EAP_PWD_OPCODE_CONFIRM_EXCH)) + return FALSE; + + wpa_printf(MSG_INFO, "EAP-pwd: Unexpected opcode=%d in state=%d", + *pos, data->state); + + return TRUE; +} + + +static void eap_pwd_process_id_resp(struct eap_sm *sm, + struct eap_pwd_data *data, + const u8 *payload, size_t payload_len) +{ + struct eap_pwd_id *id; + + if (payload_len < sizeof(struct eap_pwd_id)) { + wpa_printf(MSG_INFO, "EAP-pwd: Invalid ID response"); + return; + } + + id = (struct eap_pwd_id *) payload; + if ((data->group_num != be_to_host16(id->group_num)) || + (id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) || + (os_memcmp(id->token, (u8 *)&data->token, sizeof(data->token))) || + (id->prf != EAP_PWD_DEFAULT_PRF)) { + wpa_printf(MSG_INFO, "EAP-pwd: peer changed parameters"); + eap_pwd_state(data, FAILURE); + return; + } + data->id_peer = os_malloc(payload_len - sizeof(struct eap_pwd_id)); + if (data->id_peer == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail"); + return; + } + data->id_peer_len = payload_len - sizeof(struct eap_pwd_id); + os_memcpy(data->id_peer, id->identity, data->id_peer_len); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-PWD (server): peer sent id of", + data->id_peer, data->id_peer_len); + + if ((data->grp = os_malloc(sizeof(EAP_PWD_group))) == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for " + "group"); + return; + } + if (compute_password_element(data->grp, data->group_num, + data->password, data->password_len, + data->id_server, data->id_server_len, + data->id_peer, data->id_peer_len, + (u8 *) &data->token)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): unable to compute " + "PWE"); + return; + } + wpa_printf(MSG_DEBUG, "EAP-PWD (server): computed %d bit PWE...", + BN_num_bits(data->grp->prime)); + + eap_pwd_state(data, PWD_Commit_Req); +} + + +static void +eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data, + const u8 *payload, size_t payload_len) +{ + u8 *ptr; + BIGNUM *x = NULL, *y = NULL, *cofactor = NULL; + EC_POINT *K = NULL, *point = NULL; + int res = 0; + + wpa_printf(MSG_DEBUG, "EAP-pwd: Received commit response"); + + if (((data->peer_scalar = BN_new()) == NULL) || + ((data->k = BN_new()) == NULL) || + ((cofactor = BN_new()) == NULL) || + ((x = BN_new()) == NULL) || + ((y = BN_new()) == NULL) || + ((point = EC_POINT_new(data->grp->group)) == NULL) || + ((K = EC_POINT_new(data->grp->group)) == NULL) || + ((data->peer_element = EC_POINT_new(data->grp->group)) == NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation " + "fail"); + goto fin; + } + + if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): unable to get " + "cofactor for curve"); + goto fin; + } + + /* element, x then y, followed by scalar */ + ptr = (u8 *) payload; + BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x); + ptr += BN_num_bytes(data->grp->prime); + BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y); + ptr += BN_num_bytes(data->grp->prime); + BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->peer_scalar); + if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group, + data->peer_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): setting peer element " + "fail"); + goto fin; + } + + /* check to ensure peer's element is not in a small sub-group */ + if (BN_cmp(cofactor, BN_value_one())) { + if (!EC_POINT_mul(data->grp->group, point, NULL, + data->peer_element, cofactor, NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): cannot " + "multiply peer element by order"); + goto fin; + } + if (EC_POINT_is_at_infinity(data->grp->group, point)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): peer element " + "is at infinity!\n"); + goto fin; + } + } + + /* compute the shared key, k */ + if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe, + data->peer_scalar, data->bnctx)) || + (!EC_POINT_add(data->grp->group, K, K, data->peer_element, + data->bnctx)) || + (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value, + data->bnctx))) { + wpa_printf(MSG_INFO, "EAP-PWD (server): computing shared key " + "fail"); + goto fin; + } + + /* ensure that the shared key isn't in a small sub-group */ + if (BN_cmp(cofactor, BN_value_one())) { + if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor, + NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): cannot " + "multiply shared key point by order!\n"); + goto fin; + } + } + + /* + * This check is strictly speaking just for the case above where + * co-factor > 1 but it was suggested that even though this is probably + * never going to happen it is a simple and safe check "just to be + * sure" so let's be safe. + */ + if (EC_POINT_is_at_infinity(data->grp->group, K)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): shared key point is " + "at infinity"); + goto fin; + } + if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k, + NULL, data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): unable to extract " + "shared secret from secret point"); + goto fin; + } + res = 1; + +fin: + EC_POINT_free(K); + EC_POINT_free(point); + BN_free(cofactor); + BN_free(x); + BN_free(y); + + if (res) + eap_pwd_state(data, PWD_Confirm_Req); + else + eap_pwd_state(data, FAILURE); +} + + +static void +eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, + const u8 *payload, size_t payload_len) +{ + BIGNUM *x = NULL, *y = NULL; + struct crypto_hash *hash; + u32 cs; + u16 grp; + u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; + int offset; + + /* build up the ciphersuite: group | random_function | prf */ + grp = htons(data->group_num); + ptr = (u8 *) &cs; + os_memcpy(ptr, &grp, sizeof(u16)); + ptr += sizeof(u16); + *ptr = EAP_PWD_DEFAULT_RAND_FUNC; + ptr += sizeof(u8); + *ptr = EAP_PWD_DEFAULT_PRF; + + /* each component of the cruft will be at most as big as the prime */ + if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) || + ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): allocation fail"); + goto fin; + } + + /* + * commit is H(k | peer_element | peer_scalar | server_element | + * server_scalar | ciphersuite) + */ + 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); + 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, + data->peer_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " + "assignment fail"); + goto fin; + } + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); + BN_bn2bin(x, cruft + offset); + 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); + 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); + 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, + data->my_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " + "assignment fail"); + goto fin; + } + + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); + BN_bn2bin(x, cruft + offset); + 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); + 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); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); + + /* ciphersuite */ + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32)); + + /* all done */ + eap_pwd_h_final(hash, conf); + + ptr = (u8 *) payload; + if (os_memcmp(conf, ptr, SHA256_MAC_LEN)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): confirm did not " + "verify"); + goto fin; + } + + wpa_printf(MSG_DEBUG, "EAP-pwd (server): confirm verified"); + if (compute_keys(data->grp, data->bnctx, data->k, + data->peer_scalar, data->my_scalar, conf, + data->my_confirm, &cs, data->msk, data->emsk) < 0) + eap_pwd_state(data, FAILURE); + else + eap_pwd_state(data, SUCCESS); + +fin: + os_free(cruft); + BN_free(x); + BN_free(y); +} + + +static void eap_pwd_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_pwd_data *data = priv; + const u8 *pos; + size_t len; + u8 lm_exch; + u16 tot_len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, respData, &len); + if ((pos == NULL) || (len < 1)) { + wpa_printf(MSG_INFO, "Bad EAP header! pos %s and len = %d", + (pos == NULL) ? "is NULL" : "is not NULL", + (int) len); + return; + } + + lm_exch = *pos; + pos++; /* skip over the bits and the exch */ + len--; + + /* + * if we're fragmenting then this should be an ACK with no data, + * just return and continue fragmenting in the "build" section above + */ + if (data->out_frag_pos) { + if (len > 1) + wpa_printf(MSG_INFO, "EAP-pwd: Bad response! " + "Fragmenting but not an ACK"); + else + wpa_printf(MSG_DEBUG, "EAP-pwd: received ACK from " + "peer"); + return; + } + /* + * if we're receiving fragmented packets then we need to buffer... + * + * the first fragment has a total length + */ + if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) { + tot_len = WPA_GET_BE16(pos); + wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments, total " + "length = %d", tot_len); + data->inbuf = wpabuf_alloc(tot_len); + if (data->inbuf == NULL) { + wpa_printf(MSG_INFO, "EAP-pwd: Out of memory to " + "buffer fragments!"); + return; + } + pos += sizeof(u16); + len -= sizeof(u16); + } + /* + * the first and all intermediate fragments have the M bit set + */ + if (EAP_PWD_GET_MORE_BIT(lm_exch)) { + if ((data->in_frag_pos + len) > wpabuf_size(data->inbuf)) { + wpa_printf(MSG_DEBUG, "EAP-pwd: Buffer overflow " + "attack detected! (%d+%d > %d)", + (int) data->in_frag_pos, (int) len, + (int) wpabuf_size(data->inbuf)); + eap_pwd_state(data, FAILURE); + return; + } + wpabuf_put_data(data->inbuf, pos, len); + data->in_frag_pos += len; + wpa_printf(MSG_DEBUG, "EAP-pwd: Got a %d byte fragment", + (int) len); + return; + } + /* + * last fragment won't have the M bit set (but we're obviously + * buffering fragments so that's how we know it's the last) + */ + if (data->in_frag_pos) { + wpabuf_put_data(data->inbuf, pos, len); + data->in_frag_pos += len; + pos = wpabuf_head_u8(data->inbuf); + len = data->in_frag_pos; + wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes", + (int) len); + } + switch (EAP_PWD_GET_EXCHANGE(lm_exch)) { + case EAP_PWD_OPCODE_ID_EXCH: + eap_pwd_process_id_resp(sm, data, pos, len); + break; + case EAP_PWD_OPCODE_COMMIT_EXCH: + eap_pwd_process_commit_resp(sm, data, pos, len); + break; + case EAP_PWD_OPCODE_CONFIRM_EXCH: + eap_pwd_process_confirm_resp(sm, data, pos, len); + break; + } + /* + * if we had been buffering fragments, here's a great place + * to clean up + */ + if (data->in_frag_pos) { + wpabuf_free(data->inbuf); + data->in_frag_pos = 0; + } +} + + +static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_pwd_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_MSK_LEN); + if (key == NULL) + return NULL; + + os_memcpy(key, data->msk, EAP_MSK_LEN); + *len = EAP_MSK_LEN; + + return key; +} + + +static u8 * eap_pwd_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_pwd_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + + os_memcpy(key, data->emsk, EAP_EMSK_LEN); + *len = EAP_EMSK_LEN; + + return key; +} + + +static Boolean eap_pwd_is_success(struct eap_sm *sm, void *priv) +{ + struct eap_pwd_data *data = priv; + return data->state == SUCCESS; +} + + +static Boolean eap_pwd_is_done(struct eap_sm *sm, void *priv) +{ + struct eap_pwd_data *data = priv; + return (data->state == SUCCESS) || (data->state == FAILURE); +} + + +int eap_server_pwd_register(void) +{ + struct eap_method *eap; + int ret; + struct timeval tp; + struct timezone tz; + u32 sr; + + EVP_add_digest(EVP_sha256()); + + sr = 0xdeaddada; + (void) gettimeofday(&tp, &tz); + sr ^= (tp.tv_sec ^ tp.tv_usec); + srandom(sr); + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_PWD, + "PWD"); + if (eap == NULL) + return -1; + + eap->init = eap_pwd_init; + eap->reset = eap_pwd_reset; + eap->buildReq = eap_pwd_build_req; + eap->check = eap_pwd_check; + eap->process = eap_pwd_process; + eap->isDone = eap_pwd_is_done; + eap->getKey = eap_pwd_getkey; + eap->get_emsk = eap_pwd_get_emsk; + eap->isSuccess = eap_pwd_is_success; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} + diff --git a/src/eap_server/eap_server_sake.c b/src/eap_server/eap_server_sake.c index ce4848f85e44c..f72e1bf510c64 100644 --- a/src/eap_server/eap_server_sake.c +++ b/src/eap_server/eap_server_sake.c @@ -2,19 +2,14 @@ * hostapd / EAP-SAKE (RFC 4763) server * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#include "crypto/random.h" #include "eap_server/eap_i.h" #include "eap_common/eap_sake_common.h" @@ -166,7 +161,7 @@ static struct wpabuf * eap_sake_build_challenge(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge"); - if (os_get_random(data->rand_s, EAP_SAKE_RAND_LEN)) { + if (random_get_bytes(data->rand_s, EAP_SAKE_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data"); data->state = FAILURE; return NULL; diff --git a/src/eap_server/eap_server_sim.c b/src/eap_server/eap_server_sim.c index 436c65591f5d1..b531241e84e1f 100644 --- a/src/eap_server/eap_server_sim.c +++ b/src/eap_server/eap_server_sim.c @@ -1,20 +1,15 @@ /* * hostapd / EAP-SIM (RFC 4186) - * Copyright (c) 2005-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2005-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 - * published 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" #include "common.h" +#include "crypto/random.h" #include "eap_server/eap_i.h" #include "eap_common/eap_sim_common.h" #include "eap_server/eap_sim_db.h" @@ -41,6 +36,8 @@ struct eap_sim_data { struct eap_sim_reauth *reauth; u16 notification; int use_result_ind; + int start_round; + char permanent[20]; /* Permanent username */ }; @@ -110,17 +107,33 @@ static struct wpabuf * eap_sim_build_start(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Start"); msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START); - if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity, - sm->identity_len)) { - wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ"); - eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0); - } else { + data->start_round++; + if (data->start_round == 1) { /* * RFC 4186, Chap. 4.2.4 recommends that identity from EAP is * ignored and the SIM/Start is used to request the identity. */ wpa_printf(MSG_DEBUG, " AT_ANY_ID_REQ"); eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0); + } else if (data->start_round > 3) { + /* Cannot use more than three rounds of Start messages */ + eap_sim_msg_free(msg); + return NULL; + } else if (data->start_round == 0) { + /* + * This is a special case that is used to recover from + * AT_COUNTER_TOO_SMALL during re-authentication. Since we + * already know the identity of the peer, there is no need to + * request any identity in this case. + */ + } else if (sm->identity && 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); } wpa_printf(MSG_DEBUG, " AT_VERSION_LIST"); ver[0] = 0; @@ -136,12 +149,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"); @@ -232,7 +252,7 @@ static struct wpabuf * eap_sim_build_reauth(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Re-authentication"); - if (os_get_random(data->nonce_s, EAP_SIM_NONCE_S_LEN)) + if (random_get_bytes(data->nonce_s, EAP_SIM_NONCE_S_LEN)) return NULL; wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: NONCE_S", data->nonce_s, EAP_SIM_NONCE_S_LEN); @@ -326,18 +346,22 @@ static struct wpabuf * eap_sim_buildReq(struct eap_sm *sm, void *priv, u8 id) static Boolean eap_sim_check(struct eap_sm *sm, void *priv, struct wpabuf *respData) { - struct eap_sim_data *data = priv; const u8 *pos; size_t len; - u8 subtype; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, respData, &len); if (pos == NULL || len < 3) { wpa_printf(MSG_INFO, "EAP-SIM: Invalid frame"); return TRUE; } - subtype = *pos; + return FALSE; +} + + +static Boolean eap_sim_unexpected_subtype(struct eap_sim_data *data, + u8 subtype) +{ if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR) return FALSE; @@ -391,85 +415,113 @@ static void eap_sim_process_start(struct eap_sm *sm, struct wpabuf *respData, struct eap_sim_attrs *attr) { - const u8 *identity; size_t identity_len; u8 ver_list[2]; + u8 *new_identity; + char *username; wpa_printf(MSG_DEBUG, "EAP-SIM: Receive start response"); - if (attr->identity) { - os_free(sm->identity); - sm->identity = os_malloc(attr->identity_len); - if (sm->identity) { - os_memcpy(sm->identity, attr->identity, - attr->identity_len); - sm->identity_len = attr->identity_len; - } + if (data->start_round == 0) { + /* + * Special case for AT_COUNTER_TOO_SMALL recovery - no identity + * was requested since we already know it. + */ + goto skip_id_update; } - identity = NULL; - identity_len = 0; - - if (sm->identity && sm->identity_len > 0 && - sm->identity[0] == EAP_SIM_PERMANENT_PREFIX) { - identity = sm->identity; - identity_len = sm->identity_len; - } else { - identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, - sm->identity, - sm->identity_len, - &identity_len); - if (identity == NULL) { - data->reauth = eap_sim_db_get_reauth_entry( - sm->eap_sim_db_priv, sm->identity, - sm->identity_len); - if (data->reauth) { - wpa_printf(MSG_DEBUG, "EAP-SIM: Using fast " - "re-authentication"); - identity = data->reauth->identity; - identity_len = data->reauth->identity_len; - data->counter = data->reauth->counter; - os_memcpy(data->mk, data->reauth->mk, - EAP_SIM_MK_LEN); - } - } + /* + * We always request identity in SIM/Start, so the peer is required to + * have replied with one. + */ + if (!attr->identity || attr->identity_len == 0) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Peer did not provide any " + "identity"); + goto failed; } - if (identity == NULL) { - wpa_printf(MSG_DEBUG, "EAP-SIM: Could not get proper permanent" - " user name"); - eap_sim_state(data, FAILURE); - return; - } + new_identity = os_malloc(attr->identity_len); + if (new_identity == NULL) + goto failed; + os_free(sm->identity); + sm->identity = new_identity; + os_memcpy(sm->identity, attr->identity, attr->identity_len); + sm->identity_len = attr->identity_len; wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity", - identity, identity_len); - - if (data->reauth) { + sm->identity, sm->identity_len); + username = sim_get_username(sm->identity, sm->identity_len); + if (username == NULL) + goto failed; + + if (username[0] == EAP_SIM_REAUTH_ID_PREFIX) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Reauth username '%s'", + username); + data->reauth = eap_sim_db_get_reauth_entry( + sm->eap_sim_db_priv, username); + os_free(username); + if (data->reauth == NULL) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown reauth " + "identity - request full auth identity"); + /* Remain in START state for another round */ + return; + } + wpa_printf(MSG_DEBUG, "EAP-SIM: Using fast re-authentication"); + os_strlcpy(data->permanent, data->reauth->permanent, + sizeof(data->permanent)); + data->counter = data->reauth->counter; + os_memcpy(data->mk, data->reauth->mk, EAP_SIM_MK_LEN); eap_sim_state(data, REAUTH); return; } + if (username[0] == EAP_SIM_PSEUDONYM_PREFIX) { + const char *permanent; + wpa_printf(MSG_DEBUG, "EAP-SIM: Pseudonym username '%s'", + username); + permanent = eap_sim_db_get_permanent( + sm->eap_sim_db_priv, username); + os_free(username); + if (permanent == NULL) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown pseudonym " + "identity - request permanent identity"); + /* Remain in START state for another round */ + return; + } + os_strlcpy(data->permanent, permanent, + sizeof(data->permanent)); + } else if (username[0] == EAP_SIM_PERMANENT_PREFIX) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Permanent username '%s'", + username); + os_strlcpy(data->permanent, username, sizeof(data->permanent)); + os_free(username); + } else { + wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized username '%s'", + username); + os_free(username); + goto failed; + } + +skip_id_update: + /* Full authentication */ + if (attr->nonce_mt == NULL || attr->selected_version < 0) { wpa_printf(MSG_DEBUG, "EAP-SIM: Start/Response missing " "required attributes"); - eap_sim_state(data, FAILURE); - return; + goto failed; } if (!eap_sim_supported_ver(data, attr->selected_version)) { wpa_printf(MSG_DEBUG, "EAP-SIM: Peer selected unsupported " "version %d", attr->selected_version); - eap_sim_state(data, FAILURE); - return; + goto failed; } data->counter = 0; /* reset re-auth counter since this is full auth */ data->reauth = NULL; data->num_chal = eap_sim_db_get_gsm_triplets( - sm->eap_sim_db_priv, identity, identity_len, - EAP_SIM_MAX_CHAL, + sm->eap_sim_db_priv, data->permanent, EAP_SIM_MAX_CHAL, (u8 *) data->rand, (u8 *) data->kc, (u8 *) data->sres, sm); if (data->num_chal == EAP_SIM_DB_PENDING) { wpa_printf(MSG_DEBUG, "EAP-SIM: GSM authentication triplets " @@ -480,8 +532,7 @@ static void eap_sim_process_start(struct eap_sm *sm, if (data->num_chal < 2) { wpa_printf(MSG_INFO, "EAP-SIM: Failed to get GSM " "authentication triplets for the peer"); - eap_sim_state(data, FAILURE); - return; + goto failed; } identity_len = sm->identity_len; @@ -502,6 +553,11 @@ static void eap_sim_process_start(struct eap_sm *sm, data->emsk); eap_sim_state(data, CHALLENGE); + return; + +failed: + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_sim_state(data, NOTIFICATION); } @@ -510,16 +566,14 @@ static void eap_sim_process_challenge(struct eap_sm *sm, struct wpabuf *respData, struct eap_sim_attrs *attr) { - const u8 *identity; - size_t identity_len; - if (attr->mac == NULL || eap_sim_verify_mac(data->k_aut, respData, attr->mac, (u8 *) data->sres, data->num_chal * EAP_SIM_SRES_LEN)) { wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message " "did not include valid AT_MAC"); - eap_sim_state(data, FAILURE); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_sim_state(data, NOTIFICATION); return; } @@ -532,22 +586,13 @@ static void eap_sim_process_challenge(struct eap_sm *sm, } else eap_sim_state(data, SUCCESS); - identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, sm->identity, - sm->identity_len, &identity_len); - if (identity == NULL) { - identity = sm->identity; - identity_len = sm->identity_len; - } - if (data->next_pseudonym) { - eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity, - identity_len, + eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, data->permanent, 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, + eap_sim_db_add_reauth(sm->eap_sim_db_priv, data->permanent, data->next_reauth_id, data->counter + 1, data->mk); data->next_reauth_id = NULL; @@ -562,8 +607,6 @@ static void eap_sim_process_reauth(struct eap_sm *sm, { struct eap_sim_attrs eattr; u8 *decrypted = NULL; - const u8 *identity, *id2; - size_t identity_len, id2_len; if (attr->mac == NULL || eap_sim_verify_mac(data->k_aut, respData, attr->mac, data->nonce_s, @@ -599,6 +642,16 @@ static void eap_sim_process_reauth(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-SIM: Re-authentication response includes " "the correct AT_MAC"); + + if (eattr.counter_too_small) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response " + "included AT_COUNTER_TOO_SMALL - starting full " + "authentication"); + data->start_round = -1; + eap_sim_state(data, START); + return; + } + if (sm->eap_sim_aka_result_ind && attr->result_ind) { data->use_result_ind = 1; data->notification = EAP_SIM_SUCCESS; @@ -606,29 +659,9 @@ static void eap_sim_process_reauth(struct eap_sm *sm, } else eap_sim_state(data, SUCCESS); - if (data->reauth) { - identity = data->reauth->identity; - identity_len = data->reauth->identity_len; - } else { - identity = sm->identity; - identity_len = sm->identity_len; - } - - id2 = eap_sim_db_get_permanent(sm->eap_sim_db_priv, identity, - identity_len, &id2_len); - if (id2) { - identity = id2; - identity_len = id2_len; - } - - if (data->next_pseudonym) { - eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity, - identity_len, data->next_pseudonym); - data->next_pseudonym = NULL; - } if (data->next_reauth_id) { - eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity, - identity_len, data->next_reauth_id, + eap_sim_db_add_reauth(sm->eap_sim_db_priv, data->permanent, + data->next_reauth_id, data->counter + 1, data->mk); data->next_reauth_id = NULL; } else { @@ -639,7 +672,8 @@ static void eap_sim_process_reauth(struct eap_sm *sm, return; fail: - eap_sim_state(data, FAILURE); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_sim_state(data, NOTIFICATION); eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth); data->reauth = NULL; os_free(decrypted); @@ -690,8 +724,24 @@ static void eap_sim_process(struct eap_sm *sm, void *priv, subtype = *pos; pos += 3; + if (eap_sim_unexpected_subtype(data, subtype)) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized or unexpected " + "EAP-SIM Subtype in EAP Response"); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_sim_state(data, NOTIFICATION); + return; + } + if (eap_sim_parse_attr(pos, end, &attr, 0, 0)) { wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to parse attributes"); + if (subtype != EAP_SIM_SUBTYPE_CLIENT_ERROR && + (data->state == START || data->state == CHALLENGE || + data->state == REAUTH)) { + data->notification = + EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_sim_state(data, NOTIFICATION); + return; + } eap_sim_state(data, FAILURE); return; } diff --git a/src/eap_server/eap_server_tls.c b/src/eap_server/eap_server_tls.c index c98fa185bb5df..447f47cfa00a8 100644 --- a/src/eap_server/eap_server_tls.c +++ b/src/eap_server/eap_server_tls.c @@ -2,14 +2,8 @@ * hostapd / EAP-TLS (RFC 2716) * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -27,6 +21,7 @@ struct eap_tls_data { struct eap_ssl_data ssl; enum { START, CONTINUE, SUCCESS, FAILURE } state; int established; + u8 eap_type; }; @@ -71,10 +66,34 @@ static void * eap_tls_init(struct eap_sm *sm) return NULL; } + data->eap_type = EAP_TYPE_TLS; + return data; } +#ifdef EAP_SERVER_UNAUTH_TLS +static void * eap_unauth_tls_init(struct eap_sm *sm) +{ + struct eap_tls_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = START; + + if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { + wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); + eap_tls_reset(sm, data); + return NULL; + } + + data->eap_type = EAP_UNAUTH_TLS_TYPE; + return data; +} +#endif /* EAP_SERVER_UNAUTH_TLS */ + + static void eap_tls_reset(struct eap_sm *sm, void *priv) { struct eap_tls_data *data = priv; @@ -90,8 +109,7 @@ static struct wpabuf * eap_tls_build_start(struct eap_sm *sm, { struct wpabuf *req; - req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLS, 1, EAP_CODE_REQUEST, - id); + req = eap_tls_msg_alloc(data->eap_type, 1, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-TLS: Failed to allocate memory for " "request"); @@ -113,11 +131,11 @@ static struct wpabuf * eap_tls_buildReq(struct eap_sm *sm, void *priv, u8 id) struct wpabuf *res; if (data->ssl.state == FRAG_ACK) { - return eap_server_tls_build_ack(id, EAP_TYPE_TLS, 0); + return eap_server_tls_build_ack(id, data->eap_type, 0); } if (data->ssl.state == WAIT_FRAG_ACK) { - res = eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0, + res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0, id); goto check_established; } @@ -135,7 +153,7 @@ static struct wpabuf * eap_tls_buildReq(struct eap_sm *sm, void *priv, u8 id) return NULL; } - res = eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0, id); + res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0, id); check_established: if (data->established && data->ssl.state != WAIT_FRAG_ACK) { @@ -152,10 +170,17 @@ check_established: static Boolean eap_tls_check(struct eap_sm *sm, void *priv, struct wpabuf *respData) { + struct eap_tls_data *data = priv; const u8 *pos; size_t len; - pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLS, respData, &len); + if (data->eap_type == EAP_UNAUTH_TLS_TYPE) + pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, respData, + &len); + else + pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_type, + respData, &len); if (pos == NULL || len < 1) { wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame"); return TRUE; @@ -184,7 +209,7 @@ static void eap_tls_process(struct eap_sm *sm, void *priv, { struct eap_tls_data *data = priv; if (eap_server_tls_process(sm, &data->ssl, respData, data, - EAP_TYPE_TLS, NULL, eap_tls_process_msg) < + data->eap_type, NULL, eap_tls_process_msg) < 0) eap_tls_state(data, FAILURE); } @@ -284,3 +309,34 @@ int eap_server_tls_register(void) eap_server_method_free(eap); return ret; } + + +#ifdef EAP_SERVER_UNAUTH_TLS +int eap_server_unauth_tls_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, + "UNAUTH-TLS"); + if (eap == NULL) + return -1; + + eap->init = eap_unauth_tls_init; + eap->reset = eap_tls_reset; + eap->buildReq = eap_tls_buildReq; + eap->check = eap_tls_check; + eap->process = eap_tls_process; + eap->isDone = eap_tls_isDone; + eap->getKey = eap_tls_getKey; + eap->isSuccess = eap_tls_isSuccess; + eap->get_emsk = eap_tls_get_emsk; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} +#endif /* EAP_SERVER_UNAUTH_TLS */ diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c index 25ae683f06688..9efb5b2938538 100644 --- a/src/eap_server/eap_server_tls_common.c +++ b/src/eap_server/eap_server_tls_common.c @@ -2,14 +2,8 @@ * EAP-TLS/PEAP/TTLS/FAST server common functions * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -24,6 +18,18 @@ static void eap_server_tls_free_in_buf(struct eap_ssl_data *data); +struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len, + u8 code, u8 identifier) +{ + if (type == EAP_UNAUTH_TLS_TYPE) + return eap_msg_alloc(EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, payload_len, + code, identifier); + return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code, + identifier); +} + + int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, int verify_peer) { @@ -45,8 +51,7 @@ int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, return -1; } - /* TODO: make this configurable */ - data->tls_out_limit = 1398; + data->tls_out_limit = sm->fragment_size > 0 ? sm->fragment_size : 1398; if (data->phase2) { /* Limit the fragment size in the inner TLS authentication * since the outer authentication with EAP-PEAP does not yet @@ -95,9 +100,9 @@ u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, os_memcpy(rnd + keys.client_random_len, keys.server_random, keys.server_random_len); - if (tls_prf(keys.master_key, keys.master_key_len, - label, rnd, keys.client_random_len + - keys.server_random_len, out, len)) + if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len, + label, rnd, keys.client_random_len + + keys.server_random_len, out, len)) goto fail; os_free(rnd); @@ -138,8 +143,7 @@ struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data, if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) plen += 4; - req = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, plen, - EAP_CODE_REQUEST, id); + req = eap_tls_msg_alloc(eap_type, plen, EAP_CODE_REQUEST, id); if (req == NULL) return NULL; @@ -175,8 +179,7 @@ struct wpabuf * eap_server_tls_build_ack(u8 id, int eap_type, int version) { struct wpabuf *req; - req = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, 1, EAP_CODE_REQUEST, - id); + req = eap_tls_msg_alloc(eap_type, 1, EAP_CODE_REQUEST, id); if (req == NULL) return NULL; wpa_printf(MSG_DEBUG, "SSL: Building ACK"); @@ -225,6 +228,14 @@ static int eap_server_tls_process_fragment(struct eap_ssl_data *data, return -1; } + if (len > message_length) { + wpa_printf(MSG_INFO, "SSL: Too much data (%d bytes) in " + "first fragment of frame (TLS Message " + "Length %d bytes)", + (int) len, (int) message_length); + return -1; + } + data->tls_in = wpabuf_alloc(message_length); if (data->tls_in == NULL) { wpa_printf(MSG_DEBUG, "SSL: No memory for message"); @@ -286,6 +297,13 @@ static int eap_server_tls_reassemble(struct eap_ssl_data *data, u8 flags, tls_msg_len); *pos += 4; *left -= 4; + + if (*left > tls_msg_len) { + wpa_printf(MSG_INFO, "SSL: TLS Message Length (%d " + "bytes) smaller than this fragment (%d " + "bytes)", (int) tls_msg_len, (int) *left); + return -1; + } } wpa_printf(MSG_DEBUG, "SSL: Received packet: Flags 0x%x " @@ -366,7 +384,13 @@ int eap_server_tls_process(struct eap_sm *sm, struct eap_ssl_data *data, size_t left; int ret, res = 0; - pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, respData, &left); + if (eap_type == EAP_UNAUTH_TLS_TYPE) + pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, respData, + &left); + else + pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, respData, + &left); if (pos == NULL || left < 1) return 0; /* Should not happen - frame already validated */ flags = *pos++; diff --git a/src/eap_server/eap_server_tnc.c b/src/eap_server/eap_server_tnc.c index f3b70edabfcd6..67a3dfa306119 100644 --- a/src/eap_server/eap_server_tnc.c +++ b/src/eap_server/eap_server_tnc.c @@ -2,20 +2,13 @@ * EAP server method: EAP-TNC (Trusted Network Connect) * Copyright (c) 2007-2010, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "base64.h" #include "eap_i.h" #include "tncs.h" @@ -91,7 +84,8 @@ static void * eap_tnc_init(struct eap_sm *sm) return NULL; } - data->fragment_size = 1300; + data->fragment_size = sm->fragment_size > 100 ? + sm->fragment_size - 98 : 1300; return data; } diff --git a/src/eap_server/eap_server_ttls.c b/src/eap_server/eap_server_ttls.c index 702c50c3566e4..647bd2fad938f 100644 --- a/src/eap_server/eap_server_ttls.c +++ b/src/eap_server/eap_server_ttls.c @@ -1,15 +1,9 @@ /* * hostapd / EAP-TTLS (RFC 5281) - * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -24,16 +18,7 @@ #include "eap_common/eap_ttls.h" -/* Maximum supported TTLS version - * 0 = RFC 5281 - * 1 = draft-funk-eap-ttls-v1-00.txt - */ -#ifndef EAP_TTLS_VERSION -#define EAP_TTLS_VERSION 0 /* TTLSv1 implementation is not yet complete */ -#endif /* EAP_TTLS_VERSION */ - - -#define MSCHAPV2_KEY_LEN 16 +#define EAP_TTLS_VERSION 0 static void eap_ttls_reset(struct eap_sm *sm, void *priv); @@ -43,17 +28,15 @@ struct eap_ttls_data { struct eap_ssl_data ssl; enum { START, PHASE1, PHASE2_START, PHASE2_METHOD, - PHASE2_MSCHAPV2_RESP, PHASE_FINISHED, SUCCESS, FAILURE + PHASE2_MSCHAPV2_RESP, SUCCESS, FAILURE } state; int ttls_version; - int force_version; const struct eap_method *phase2_method; void *phase2_priv; int mschapv2_resp_ok; u8 mschapv2_auth_response[20]; u8 mschapv2_ident; - int tls_ia_configured; struct wpabuf *pending_phase2_eap_resp; int tnc_started; }; @@ -72,8 +55,6 @@ static const char * eap_ttls_state_txt(int state) return "PHASE2_METHOD"; case PHASE2_MSCHAPV2_RESP: return "PHASE2_MSCHAPV2_RESP"; - case PHASE_FINISHED: - return "PHASE_FINISHED"; case SUCCESS: return "SUCCESS"; case FAILURE: @@ -111,7 +92,8 @@ static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id, } avp->avp_code = host_to_be32(avp_code); - avp->avp_length = host_to_be32((flags << 24) | (hdrlen + len)); + avp->avp_length = host_to_be32(((u32) flags << 24) | + ((u32) (hdrlen + len))); return avphdr + hdrlen; } @@ -320,54 +302,8 @@ fail: static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm, struct eap_ttls_data *data, size_t len) { - struct tls_keys keys; - u8 *challenge, *rnd; - - if (data->ttls_version == 0) { - return eap_server_tls_derive_key(sm, &data->ssl, - "ttls challenge", len); - } - - os_memset(&keys, 0, sizeof(keys)); - if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) || - keys.client_random == NULL || keys.server_random == NULL || - keys.inner_secret == NULL) { - wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, " - "client random, or server random to derive " - "implicit challenge"); - return NULL; - } - - rnd = os_malloc(keys.client_random_len + keys.server_random_len); - challenge = os_malloc(len); - if (rnd == NULL || challenge == NULL) { - wpa_printf(MSG_INFO, "EAP-TTLS: No memory for implicit " - "challenge derivation"); - os_free(rnd); - os_free(challenge); - return NULL; - } - os_memcpy(rnd, keys.server_random, keys.server_random_len); - os_memcpy(rnd + keys.server_random_len, keys.client_random, - keys.client_random_len); - - if (tls_prf(keys.inner_secret, keys.inner_secret_len, - "inner application challenge", rnd, - keys.client_random_len + keys.server_random_len, - challenge, len)) { - wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive implicit " - "challenge"); - os_free(rnd); - os_free(challenge); - return NULL; - } - - os_free(rnd); - - wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived implicit challenge", - challenge, len); - - return challenge; + return eap_server_tls_derive_key(sm, &data->ssl, "ttls challenge", + len); } @@ -379,27 +315,8 @@ static void * eap_ttls_init(struct eap_sm *sm) if (data == NULL) return NULL; data->ttls_version = EAP_TTLS_VERSION; - data->force_version = -1; - if (sm->user && sm->user->force_version >= 0) { - data->force_version = sm->user->force_version; - wpa_printf(MSG_DEBUG, "EAP-TTLS: forcing version %d", - data->force_version); - data->ttls_version = data->force_version; - } data->state = START; - if (!(tls_capabilities(sm->ssl_ctx) & TLS_CAPABILITY_IA) && - data->ttls_version > 0) { - if (data->force_version > 0) { - wpa_printf(MSG_INFO, "EAP-TTLS: Forced TTLSv%d and " - "TLS library does not support TLS/IA.", - data->force_version); - eap_ttls_reset(sm, data); - return NULL; - } - data->ttls_version = 0; - } - if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL."); eap_ttls_reset(sm, data); @@ -516,14 +433,6 @@ static struct wpabuf * eap_ttls_build_phase2_mschapv2( } -static struct wpabuf * eap_ttls_build_phase_finished( - struct eap_sm *sm, struct eap_ttls_data *data, int final) -{ - return tls_connection_ia_send_phase_finished(sm->ssl_ctx, - data->ssl.conn, final); -} - - static struct wpabuf * eap_ttls_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_ttls_data *data = priv; @@ -559,11 +468,6 @@ static struct wpabuf * eap_ttls_buildReq(struct eap_sm *sm, void *priv, u8 id) data->ssl.tls_out_pos = 0; data->ssl.tls_out = eap_ttls_build_phase2_mschapv2(sm, data); break; - case PHASE_FINISHED: - wpabuf_free(data->ssl.tls_out); - data->ssl.tls_out_pos = 0; - data->ssl.tls_out = eap_ttls_build_phase_finished(sm, data, 1); - break; default: wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d", __func__, data->state); @@ -591,37 +495,6 @@ static Boolean eap_ttls_check(struct eap_sm *sm, void *priv, } -static int eap_ttls_ia_permute_inner_secret(struct eap_sm *sm, - struct eap_ttls_data *data, - const u8 *key, size_t key_len) -{ - u8 *buf; - size_t buf_len; - int ret; - - if (key) { - buf_len = 2 + key_len; - buf = os_malloc(buf_len); - if (buf == NULL) - return -1; - WPA_PUT_BE16(buf, key_len); - os_memcpy(buf + 2, key, key_len); - } else { - buf = NULL; - buf_len = 0; - } - - wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Session keys for TLS/IA inner " - "secret permutation", buf, buf_len); - ret = tls_connection_ia_permute_inner_secret(sm->ssl_ctx, - data->ssl.conn, - buf, buf_len); - os_free(buf); - - return ret; -} - - static void eap_ttls_process_phase2_pap(struct eap_sm *sm, struct eap_ttls_data *data, const u8 *user_password, @@ -644,8 +517,7 @@ static void eap_ttls_process_phase2_pap(struct eap_sm *sm, } wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Correct user password"); - eap_ttls_state(data, data->ttls_version > 0 ? PHASE_FINISHED : - SUCCESS); + eap_ttls_state(data, SUCCESS); } @@ -701,8 +573,7 @@ static void eap_ttls_process_phase2_chap(struct eap_sm *sm, if (os_memcmp(hash, password + 1, EAP_TTLS_CHAP_PASSWORD_LEN) == 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Correct user password"); - eap_ttls_state(data, data->ttls_version > 0 ? PHASE_FINISHED : - SUCCESS); + eap_ttls_state(data, SUCCESS); } else { wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid user password"); eap_ttls_state(data, FAILURE); @@ -762,8 +633,7 @@ static void eap_ttls_process_phase2_mschap(struct eap_sm *sm, if (os_memcmp(nt_response, response + 2 + 24, 24) == 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Correct response"); - eap_ttls_state(data, data->ttls_version > 0 ? PHASE_FINISHED : - SUCCESS); + eap_ttls_state(data, SUCCESS); } else { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid NT-Response"); wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Received", @@ -804,6 +674,13 @@ static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, return; } + if (sm->identity == NULL) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: No user identity " + "known"); + eap_ttls_state(data, FAILURE); + return; + } + /* MSCHAPv2 does not include optional domain name in the * challenge-response calculation, so remove domain prefix * (if present). */ @@ -863,30 +740,6 @@ static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Correct " "NT-Response"); data->mschapv2_resp_ok = 1; - if (data->ttls_version > 0) { - const u8 *pw_hash; - u8 pw_hash_buf[16], pw_hash_hash[16], master_key[16]; - u8 session_key[2 * MSCHAPV2_KEY_LEN]; - - if (sm->user->password_hash) - pw_hash = sm->user->password; - else { - nt_password_hash(sm->user->password, - sm->user->password_len, - pw_hash_buf); - pw_hash = pw_hash_buf; - } - hash_nt_password_hash(pw_hash, pw_hash_hash); - get_master_key(pw_hash_hash, nt_response, master_key); - get_asymetric_start_key(master_key, session_key, - MSCHAPV2_KEY_LEN, 0, 0); - get_asymetric_start_key(master_key, - session_key + MSCHAPV2_KEY_LEN, - MSCHAPV2_KEY_LEN, 1, 0); - eap_ttls_ia_permute_inner_secret(sm, data, - session_key, - sizeof(session_key)); - } if (sm->user->password_hash) { generate_authenticator_response_pwhash( @@ -1030,17 +883,7 @@ static void eap_ttls_process_phase2_eap_response(struct eap_sm *sm, } break; case PHASE2_METHOD: - if (data->ttls_version > 0) { - if (m->getKey) { - u8 *key; - size_t key_len; - key = m->getKey(sm, priv, &key_len); - eap_ttls_ia_permute_inner_secret(sm, data, - key, key_len); - } - eap_ttls_state(data, PHASE_FINISHED); - } else - eap_ttls_state(data, SUCCESS); + eap_ttls_state(data, SUCCESS); break; case FAILURE: break; @@ -1130,23 +973,6 @@ static void eap_ttls_process_phase2(struct eap_sm *sm, return; } - if (data->state == PHASE_FINISHED) { - if (wpabuf_len(in_decrypted) == 0 && - tls_connection_ia_final_phase_finished(sm->ssl_ctx, - data->ssl.conn)) { - wpa_printf(MSG_DEBUG, "EAP-TTLS: FinalPhaseFinished " - "received"); - eap_ttls_state(data, SUCCESS); - } else { - wpa_printf(MSG_INFO, "EAP-TTLS: Did not receive valid " - "FinalPhaseFinished"); - eap_ttls_state(data, FAILURE); - } - - wpabuf_free(in_decrypted); - return; - } - wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 EAP", in_decrypted); @@ -1160,11 +986,12 @@ static void eap_ttls_process_phase2(struct eap_sm *sm, if (parse.user_name) { os_free(sm->identity); sm->identity = os_malloc(parse.user_name_len); - if (sm->identity) { - os_memcpy(sm->identity, parse.user_name, - parse.user_name_len); - sm->identity_len = parse.user_name_len; + if (sm->identity == NULL) { + eap_ttls_state(data, FAILURE); + goto done; } + os_memcpy(sm->identity, parse.user_name, parse.user_name_len); + sm->identity_len = parse.user_name_len; if (eap_user_get(sm, parse.user_name, parse.user_name_len, 1) != 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 Identity not " @@ -1245,15 +1072,6 @@ static int eap_ttls_process_version(struct eap_sm *sm, void *priv, data->ttls_version = peer_version; } - if (data->ttls_version > 0 && !data->tls_ia_configured) { - if (tls_connection_set_ia(sm->ssl_ctx, data->ssl.conn, 1)) { - wpa_printf(MSG_INFO, "EAP-TTLS: Failed to enable " - "TLS/IA"); - return -1; - } - data->tls_ia_configured = 1; - } - return 0; } @@ -1270,7 +1088,6 @@ static void eap_ttls_process_msg(struct eap_sm *sm, void *priv, break; case PHASE2_START: case PHASE2_METHOD: - case PHASE_FINISHED: eap_ttls_process_phase2(sm, data, data->ssl.tls_in); eap_ttls_start_tnc(sm, data); break; @@ -1279,8 +1096,7 @@ static void eap_ttls_process_msg(struct eap_sm *sm, void *priv, 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer " "acknowledged response"); - eap_ttls_state(data, data->ttls_version > 0 ? - PHASE_FINISHED : SUCCESS); + eap_ttls_state(data, SUCCESS); } else if (!data->mschapv2_resp_ok) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer " "acknowledged error"); @@ -1321,54 +1137,6 @@ static Boolean eap_ttls_isDone(struct eap_sm *sm, void *priv) } -static u8 * eap_ttls_v1_derive_key(struct eap_sm *sm, - struct eap_ttls_data *data) -{ - struct tls_keys keys; - u8 *rnd, *key; - - os_memset(&keys, 0, sizeof(keys)); - if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) || - keys.client_random == NULL || keys.server_random == NULL || - keys.inner_secret == NULL) { - wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, " - "client random, or server random to derive keying " - "material"); - return NULL; - } - - rnd = os_malloc(keys.client_random_len + keys.server_random_len); - key = os_malloc(EAP_TLS_KEY_LEN); - if (rnd == NULL || key == NULL) { - wpa_printf(MSG_INFO, "EAP-TTLS: No memory for key derivation"); - os_free(rnd); - os_free(key); - return NULL; - } - os_memcpy(rnd, keys.client_random, keys.client_random_len); - os_memcpy(rnd + keys.client_random_len, keys.server_random, - keys.server_random_len); - - if (tls_prf(keys.inner_secret, keys.inner_secret_len, - "ttls v1 keying material", rnd, keys.client_random_len + - keys.server_random_len, key, EAP_TLS_KEY_LEN)) { - wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key"); - os_free(rnd); - os_free(key); - return NULL; - } - - wpa_hexdump(MSG_DEBUG, "EAP-TTLS: client/server random", - rnd, keys.client_random_len + keys.server_random_len); - wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: TLS/IA inner secret", - keys.inner_secret, keys.inner_secret_len); - - os_free(rnd); - - return key; -} - - static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_ttls_data *data = priv; @@ -1377,14 +1145,9 @@ static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - if (data->ttls_version == 0) { - eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, - "ttls keying material", - EAP_TLS_KEY_LEN); - } else { - eapKeyData = eap_ttls_v1_derive_key(sm, data); - } - + eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, + "ttls keying material", + EAP_TLS_KEY_LEN); if (eapKeyData) { *len = EAP_TLS_KEY_LEN; wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key", diff --git a/src/eap_server/eap_server_vendor_test.c b/src/eap_server/eap_server_vendor_test.c index 0dd0aca911b4d..30f600d3baf63 100644 --- a/src/eap_server/eap_server_vendor_test.c +++ b/src/eap_server/eap_server_vendor_test.c @@ -2,14 +2,8 @@ * hostapd / Test method for vendor specific (expanded) EAP type * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -18,7 +12,7 @@ #include "eap_i.h" -#define EAP_VENDOR_ID 0xfffefd +#define EAP_VENDOR_ID EAP_VENDOR_HOSTAP #define EAP_VENDOR_TYPE 0xfcfbfaf9 diff --git a/src/eap_server/eap_server_wsc.c b/src/eap_server/eap_server_wsc.c index 77cf9e2af2b12..97ec0c0eaa99b 100644 --- a/src/eap_server/eap_server_wsc.c +++ b/src/eap_server/eap_server_wsc.c @@ -2,14 +2,8 @@ * EAP-WSC server for Wi-Fi Protected Setup * Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -18,6 +12,7 @@ #include "eloop.h" #include "eap_i.h" #include "eap_common/eap_wsc_common.h" +#include "p2p/p2p.h" #include "wps/wps.h" @@ -135,14 +130,22 @@ static void * eap_wsc_init(struct eap_sm *sm) } cfg.assoc_wps_ie = sm->assoc_wps_ie; cfg.peer_addr = sm->peer_addr; - if (0 /* TODO: could provide option for forcing PSK format */) - cfg.use_psk_key = 1; +#ifdef CONFIG_P2P + if (sm->assoc_p2p_ie) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Prefer PSK format for P2P " + "client"); + cfg.use_psk_key = 1; + cfg.p2p_dev_addr = p2p_get_go_dev_addr(sm->assoc_p2p_ie); + } +#endif /* CONFIG_P2P */ + cfg.pbc_in_m1 = sm->pbc_in_m1; data->wps = wps_init(&cfg); if (data->wps == NULL) { os_free(data); return NULL; } - data->fragment_size = WSC_FRAGMENT_SIZE; + data->fragment_size = sm->fragment_size > 0 ? sm->fragment_size : + WSC_FRAGMENT_SIZE; return data; } diff --git a/src/eap_server/eap_sim_db.c b/src/eap_server/eap_sim_db.c index aba919aa55126..257013e3e6612 100644 --- a/src/eap_server/eap_sim_db.c +++ b/src/eap_server/eap_sim_db.c @@ -1,15 +1,9 @@ /* * 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 program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published 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. * * This is an example implementation of the EAP-SIM/AKA database/authentication * gateway interface that is using an external program as an SS7 gateway to @@ -23,23 +17,25 @@ #include "includes.h" #include <sys/un.h> +#ifdef CONFIG_SQLITE +#include <sqlite3.h> +#endif /* CONFIG_SQLITE */ #include "common.h" +#include "crypto/random.h" #include "eap_common/eap_sim_common.h" #include "eap_server/eap_sim_db.h" #include "eloop.h" struct eap_sim_pseudonym { struct eap_sim_pseudonym *next; - u8 *identity; - size_t identity_len; - char *pseudonym; + char *permanent; /* permanent username */ + char *pseudonym; /* pseudonym username */ }; struct eap_sim_db_pending { struct eap_sim_db_pending *next; - u8 imsi[20]; - size_t imsi_len; + char imsi[20]; enum { PENDING, SUCCESS, FAILURE } state; void *cb_session_ctx; struct os_time timestamp; @@ -71,19 +67,316 @@ struct eap_sim_db_data { struct eap_sim_pseudonym *pseudonyms; struct eap_sim_reauth *reauths; struct eap_sim_db_pending *pending; +#ifdef CONFIG_SQLITE + sqlite3 *sqlite_db; + char db_tmp_identity[100]; + char db_tmp_pseudonym_str[100]; + struct eap_sim_pseudonym db_tmp_pseudonym; + struct eap_sim_reauth db_tmp_reauth; +#endif /* CONFIG_SQLITE */ }; +#ifdef CONFIG_SQLITE + +static int db_table_exists(sqlite3 *db, const char *name) +{ + char cmd[128]; + os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name); + return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK; +} + + +static int db_table_create_pseudonym(sqlite3 *db) +{ + char *err = NULL; + const char *sql = + "CREATE TABLE pseudonyms(" + " permanent CHAR(21) PRIMARY KEY," + " pseudonym CHAR(21) NOT NULL" + ");"; + + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Adding database table for " + "pseudonym information"); + if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) { + wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err); + sqlite3_free(err); + return -1; + } + + return 0; +} + + +static int db_table_create_reauth(sqlite3 *db) +{ + char *err = NULL; + const char *sql = + "CREATE TABLE reauth(" + " permanent CHAR(21) PRIMARY KEY," + " reauth_id CHAR(21) NOT NULL," + " counter INTEGER," + " mk CHAR(40)," + " k_encr CHAR(32)," + " k_aut CHAR(64)," + " k_re CHAR(64)" + ");"; + + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Adding database table for " + "reauth information"); + if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) { + wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err); + sqlite3_free(err); + return -1; + } + + return 0; +} + + +static sqlite3 * db_open(const char *db_file) +{ + sqlite3 *db; + + if (sqlite3_open(db_file, &db)) { + wpa_printf(MSG_ERROR, "EAP-SIM DB: Failed to open database " + "%s: %s", db_file, sqlite3_errmsg(db)); + sqlite3_close(db); + return NULL; + } + + if (!db_table_exists(db, "pseudonyms") && + db_table_create_pseudonym(db) < 0) { + sqlite3_close(db); + return NULL; + } + + if (!db_table_exists(db, "reauth") && + db_table_create_reauth(db) < 0) { + sqlite3_close(db); + return NULL; + } + + return db; +} + + +static int valid_db_string(const char *str) +{ + const char *pos = str; + while (*pos) { + if ((*pos < '0' || *pos > '9') && + (*pos < 'a' || *pos > 'f')) + return 0; + pos++; + } + return 1; +} + + +static int db_add_pseudonym(struct eap_sim_db_data *data, + const char *permanent, char *pseudonym) +{ + char cmd[128]; + char *err = NULL; + + if (!valid_db_string(permanent) || !valid_db_string(pseudonym)) { + os_free(pseudonym); + return -1; + } + + os_snprintf(cmd, sizeof(cmd), "INSERT OR REPLACE INTO pseudonyms " + "(permanent, pseudonym) VALUES ('%s', '%s');", + permanent, pseudonym); + os_free(pseudonym); + if (sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, &err) != SQLITE_OK) + { + wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err); + sqlite3_free(err); + return -1; + } + + return 0; +} + + +static int get_pseudonym_cb(void *ctx, int argc, char *argv[], char *col[]) +{ + struct eap_sim_db_data *data = ctx; + int i; + + for (i = 0; i < argc; i++) { + if (os_strcmp(col[i], "permanent") == 0 && argv[i]) { + os_strlcpy(data->db_tmp_identity, argv[i], + sizeof(data->db_tmp_identity)); + } + } + + return 0; +} + + +static char * +db_get_pseudonym(struct eap_sim_db_data *data, const char *pseudonym) +{ + char cmd[128]; + + if (!valid_db_string(pseudonym)) + return NULL; + os_memset(&data->db_tmp_identity, 0, sizeof(data->db_tmp_identity)); + os_snprintf(cmd, sizeof(cmd), + "SELECT permanent FROM pseudonyms WHERE pseudonym='%s';", + pseudonym); + if (sqlite3_exec(data->sqlite_db, cmd, get_pseudonym_cb, data, NULL) != + SQLITE_OK) + return NULL; + if (data->db_tmp_identity[0] == '\0') + return NULL; + return data->db_tmp_identity; +} + + +static int db_add_reauth(struct eap_sim_db_data *data, const char *permanent, + char *reauth_id, u16 counter, const u8 *mk, + const u8 *k_encr, const u8 *k_aut, const u8 *k_re) +{ + char cmd[2000], *pos, *end; + char *err = NULL; + + if (!valid_db_string(permanent) || !valid_db_string(reauth_id)) { + os_free(reauth_id); + return -1; + } + + pos = cmd; + end = pos + sizeof(cmd); + pos += os_snprintf(pos, end - pos, "INSERT OR REPLACE INTO reauth " + "(permanent, reauth_id, counter%s%s%s%s) " + "VALUES ('%s', '%s', %u", + mk ? ", mk" : "", + k_encr ? ", k_encr" : "", + k_aut ? ", k_aut" : "", + k_re ? ", k_re" : "", + permanent, reauth_id, counter); + os_free(reauth_id); + + if (mk) { + pos += os_snprintf(pos, end - pos, ", '"); + pos += wpa_snprintf_hex(pos, end - pos, mk, EAP_SIM_MK_LEN); + pos += os_snprintf(pos, end - pos, "'"); + } + + if (k_encr) { + pos += os_snprintf(pos, end - pos, ", '"); + pos += wpa_snprintf_hex(pos, end - pos, k_encr, + EAP_SIM_K_ENCR_LEN); + pos += os_snprintf(pos, end - pos, "'"); + } + + if (k_aut) { + pos += os_snprintf(pos, end - pos, ", '"); + pos += wpa_snprintf_hex(pos, end - pos, k_aut, + EAP_AKA_PRIME_K_AUT_LEN); + pos += os_snprintf(pos, end - pos, "'"); + } + + if (k_re) { + pos += os_snprintf(pos, end - pos, ", '"); + pos += wpa_snprintf_hex(pos, end - pos, k_re, + EAP_AKA_PRIME_K_RE_LEN); + pos += os_snprintf(pos, end - pos, "'"); + } + + os_snprintf(pos, end - pos, ");"); + + if (sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, &err) != SQLITE_OK) + { + wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err); + sqlite3_free(err); + return -1; + } + + return 0; +} + + +static int get_reauth_cb(void *ctx, int argc, char *argv[], char *col[]) +{ + struct eap_sim_db_data *data = ctx; + int i; + struct eap_sim_reauth *reauth = &data->db_tmp_reauth; + + for (i = 0; i < argc; i++) { + if (os_strcmp(col[i], "permanent") == 0 && argv[i]) { + os_strlcpy(data->db_tmp_identity, argv[i], + sizeof(data->db_tmp_identity)); + reauth->permanent = data->db_tmp_identity; + } else if (os_strcmp(col[i], "counter") == 0 && argv[i]) { + reauth->counter = atoi(argv[i]); + } else if (os_strcmp(col[i], "mk") == 0 && argv[i]) { + hexstr2bin(argv[i], reauth->mk, sizeof(reauth->mk)); + } else if (os_strcmp(col[i], "k_encr") == 0 && argv[i]) { + hexstr2bin(argv[i], reauth->k_encr, + sizeof(reauth->k_encr)); + } else if (os_strcmp(col[i], "k_aut") == 0 && argv[i]) { + hexstr2bin(argv[i], reauth->k_aut, + sizeof(reauth->k_aut)); + } else if (os_strcmp(col[i], "k_re") == 0 && argv[i]) { + hexstr2bin(argv[i], reauth->k_re, + sizeof(reauth->k_re)); + } + } + + return 0; +} + + +static struct eap_sim_reauth * +db_get_reauth(struct eap_sim_db_data *data, const char *reauth_id) +{ + char cmd[256]; + + if (!valid_db_string(reauth_id)) + return NULL; + os_memset(&data->db_tmp_reauth, 0, sizeof(data->db_tmp_reauth)); + os_strlcpy(data->db_tmp_pseudonym_str, reauth_id, + sizeof(data->db_tmp_pseudonym_str)); + data->db_tmp_reauth.reauth_id = data->db_tmp_pseudonym_str; + os_snprintf(cmd, sizeof(cmd), + "SELECT * FROM reauth WHERE reauth_id='%s';", reauth_id); + if (sqlite3_exec(data->sqlite_db, cmd, get_reauth_cb, data, NULL) != + SQLITE_OK) + return NULL; + if (data->db_tmp_reauth.permanent == NULL) + return NULL; + return &data->db_tmp_reauth; +} + + +static void db_remove_reauth(struct eap_sim_db_data *data, + struct eap_sim_reauth *reauth) +{ + char cmd[256]; + + if (!valid_db_string(reauth->permanent)) + return; + os_snprintf(cmd, sizeof(cmd), + "DELETE FROM reauth WHERE permanent='%s';", + reauth->permanent); + sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, NULL); +} + +#endif /* CONFIG_SQLITE */ + + static struct eap_sim_db_pending * -eap_sim_db_get_pending(struct eap_sim_db_data *data, const u8 *imsi, - size_t imsi_len, int aka) +eap_sim_db_get_pending(struct eap_sim_db_data *data, const char *imsi, int aka) { struct eap_sim_db_pending *entry, *prev = NULL; entry = data->pending; while (entry) { - if (entry->aka == aka && entry->imsi_len == imsi_len && - os_memcmp(entry->imsi, imsi, imsi_len) == 0) { + if (entry->aka == aka && os_strcmp(entry->imsi, imsi) == 0) { if (prev) prev->next = entry->next; else @@ -118,7 +411,7 @@ static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data, * (IMSI = ASCII string, Kc/SRES/RAND = hex string) */ - entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 0); + entry = eap_sim_db_get_pending(data, imsi, 0); if (entry == NULL) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the " "received message found"); @@ -196,7 +489,7 @@ static void eap_sim_db_aka_resp_auth(struct eap_sim_db_data *data, * (IMSI = ASCII string, RAND/AUTN/IK/CK/RES = hex string) */ - entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 1); + entry = eap_sim_db_get_pending(data, imsi, 1); if (entry == NULL) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the " "received message found"); @@ -345,6 +638,7 @@ static int eap_sim_db_open_socket(struct eap_sim_db_data *data) addr.sun_family = AF_UNIX; os_snprintf(addr.sun_path, sizeof(addr.sun_path), "/tmp/eap_sim_db_%d-%d", getpid(), counter++); + os_free(data->local_sock); data->local_sock = os_strdup(addr.sun_path); if (bind(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("bind(eap_sim_db)"); @@ -394,11 +688,13 @@ static void eap_sim_db_close_socket(struct eap_sim_db_data *data) * @ctx: Context pointer for get_complete_cb * Returns: Pointer to a private data structure or %NULL on failure */ -void * eap_sim_db_init(const char *config, - void (*get_complete_cb)(void *ctx, void *session_ctx), - void *ctx) +struct eap_sim_db_data * +eap_sim_db_init(const char *config, + void (*get_complete_cb)(void *ctx, void *session_ctx), + void *ctx) { struct eap_sim_db_data *data; + char *pos; data = os_zalloc(sizeof(*data)); if (data == NULL) @@ -410,10 +706,23 @@ void * eap_sim_db_init(const char *config, data->fname = os_strdup(config); if (data->fname == NULL) goto fail; + pos = os_strstr(data->fname, " db="); + if (pos) { + *pos = '\0'; +#ifdef CONFIG_SQLITE + pos += 4; + data->sqlite_db = db_open(pos); + if (data->sqlite_db == NULL) + goto fail; +#endif /* CONFIG_SQLITE */ + } if (os_strncmp(data->fname, "unix:", 5) == 0) { - if (eap_sim_db_open_socket(data)) - goto fail; + if (eap_sim_db_open_socket(data)) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: External database " + "connection not available - will retry " + "later"); + } } return data; @@ -428,7 +737,7 @@ fail: static void eap_sim_db_free_pseudonym(struct eap_sim_pseudonym *p) { - os_free(p->identity); + os_free(p->permanent); os_free(p->pseudonym); os_free(p); } @@ -436,7 +745,7 @@ static void eap_sim_db_free_pseudonym(struct eap_sim_pseudonym *p) static void eap_sim_db_free_reauth(struct eap_sim_reauth *r) { - os_free(r->identity); + os_free(r->permanent); os_free(r->reauth_id); os_free(r); } @@ -453,6 +762,13 @@ void eap_sim_db_deinit(void *priv) struct eap_sim_reauth *r, *prevr; struct eap_sim_db_pending *pending, *prev_pending; +#ifdef CONFIG_SQLITE + if (data->sqlite_db) { + sqlite3_close(data->sqlite_db); + data->sqlite_db = NULL; + } +#endif /* CONFIG_SQLITE */ + eap_sim_db_close_socket(data); os_free(data->fname); @@ -519,9 +835,8 @@ static void eap_sim_db_expire_pending(struct eap_sim_db_data *data) /** * eap_sim_db_get_gsm_triplets - Get GSM triplets - * @priv: Private data pointer from eap_sim_db_init() - * @identity: User name identity - * @identity_len: Length of identity in bytes + * @data: Private data pointer from eap_sim_db_init() + * @username: Permanent username (prefix | IMSI) * @max_chal: Maximum number of triplets * @_rand: Buffer for RAND values * @kc: Buffer for Kc values @@ -533,9 +848,6 @@ static void eap_sim_db_expire_pending(struct eap_sim_db_data *data) * callback function registered with eap_sim_db_init() will be called once the * results become available. * - * In most cases, the user name is '1' | IMSI, i.e., 1 followed by the IMSI in - * ASCII format. - * * When using an external server for GSM triplets, this function can always * start a request and return EAP_SIM_DB_PENDING immediately if authentication * triplets are not available. Once the triplets are received, callback @@ -544,39 +856,28 @@ static void eap_sim_db_expire_pending(struct eap_sim_db_data *data) * function will then be called again and the newly received triplets will then * be given to the caller. */ -int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity, - size_t identity_len, int max_chal, +int eap_sim_db_get_gsm_triplets(struct eap_sim_db_data *data, + const char *username, int max_chal, u8 *_rand, u8 *kc, u8 *sres, void *cb_session_ctx) { - struct eap_sim_db_data *data = priv; struct eap_sim_db_pending *entry; int len, ret; - size_t i; char msg[40]; + const char *imsi; + size_t imsi_len; - if (identity_len < 2 || identity[0] != EAP_SIM_PERMANENT_PREFIX) { - wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", - identity, identity_len); + if (username == NULL || username[0] != EAP_SIM_PERMANENT_PREFIX || + username[1] == '\0' || os_strlen(username) > sizeof(entry->imsi)) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'", + username); return EAP_SIM_DB_FAILURE; } - identity++; - identity_len--; - for (i = 0; i < identity_len; i++) { - if (identity[i] == '@') { - identity_len = i; - break; - } - } - if (identity_len + 1 > sizeof(entry->imsi)) { - wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", - identity, identity_len); - return EAP_SIM_DB_FAILURE; - } - wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI", - identity, identity_len); + imsi = username + 1; + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI '%s'", + imsi); - entry = eap_sim_db_get_pending(data, identity, identity_len, 0); + entry = eap_sim_db_get_pending(data, imsi, 0); if (entry) { int num_chal; if (entry->state == FAILURE) { @@ -611,18 +912,19 @@ int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity, return EAP_SIM_DB_FAILURE; } + imsi_len = os_strlen(imsi); len = os_snprintf(msg, sizeof(msg), "SIM-REQ-AUTH "); - if (len < 0 || len + identity_len >= sizeof(msg)) + if (len < 0 || len + imsi_len >= sizeof(msg)) return EAP_SIM_DB_FAILURE; - os_memcpy(msg + len, identity, identity_len); - len += identity_len; + os_memcpy(msg + len, imsi, imsi_len); + len += imsi_len; ret = os_snprintf(msg + len, sizeof(msg) - len, " %d", max_chal); if (ret < 0 || (size_t) ret >= sizeof(msg) - len) return EAP_SIM_DB_FAILURE; len += ret; - wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting SIM authentication " - "data for IMSI", identity, identity_len); + wpa_printf(MSG_DEBUG, "EAP-SIM DB: requesting SIM authentication " + "data for IMSI '%s'", imsi); if (eap_sim_db_send(data, msg, len) < 0) return EAP_SIM_DB_FAILURE; @@ -631,8 +933,7 @@ int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity, return EAP_SIM_DB_FAILURE; os_get_time(&entry->timestamp); - os_memcpy(entry->imsi, identity, identity_len); - entry->imsi_len = identity_len; + os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi)); entry->cb_session_ctx = cb_session_ctx; entry->state = PENDING; eap_sim_db_add_pending(data, entry); @@ -642,195 +943,12 @@ int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity, } -static struct eap_sim_pseudonym * -eap_sim_db_get_pseudonym(struct eap_sim_db_data *data, const u8 *identity, - size_t identity_len) -{ - char *pseudonym; - size_t len; - struct eap_sim_pseudonym *p; - - if (identity_len == 0 || - (identity[0] != EAP_SIM_PSEUDONYM_PREFIX && - identity[0] != EAP_AKA_PSEUDONYM_PREFIX)) - return NULL; - - /* Remove possible realm from identity */ - len = 0; - while (len < identity_len) { - if (identity[len] == '@') - break; - len++; - } - - pseudonym = os_malloc(len + 1); - if (pseudonym == NULL) - return NULL; - os_memcpy(pseudonym, identity, len); - pseudonym[len] = '\0'; - - p = data->pseudonyms; - while (p) { - if (os_strcmp(p->pseudonym, pseudonym) == 0) - break; - p = p->next; - } - - os_free(pseudonym); - - return p; -} - - -static struct eap_sim_pseudonym * -eap_sim_db_get_pseudonym_id(struct eap_sim_db_data *data, const u8 *identity, - size_t identity_len) -{ - struct eap_sim_pseudonym *p; - - if (identity_len == 0 || - (identity[0] != EAP_SIM_PERMANENT_PREFIX && - identity[0] != EAP_AKA_PERMANENT_PREFIX)) - return NULL; - - p = data->pseudonyms; - while (p) { - if (identity_len == p->identity_len && - os_memcmp(p->identity, identity, identity_len) == 0) - break; - p = p->next; - } - - return p; -} - - -static struct eap_sim_reauth * -eap_sim_db_get_reauth(struct eap_sim_db_data *data, const u8 *identity, - size_t identity_len) -{ - char *reauth_id; - size_t len; - struct eap_sim_reauth *r; - - if (identity_len == 0 || - (identity[0] != EAP_SIM_REAUTH_ID_PREFIX && - identity[0] != EAP_AKA_REAUTH_ID_PREFIX)) - return NULL; - - /* Remove possible realm from identity */ - len = 0; - while (len < identity_len) { - if (identity[len] == '@') - break; - len++; - } - - reauth_id = os_malloc(len + 1); - if (reauth_id == NULL) - return NULL; - os_memcpy(reauth_id, identity, len); - reauth_id[len] = '\0'; - - r = data->reauths; - while (r) { - if (os_strcmp(r->reauth_id, reauth_id) == 0) - break; - r = r->next; - } - - os_free(reauth_id); - - return r; -} - - -static struct eap_sim_reauth * -eap_sim_db_get_reauth_id(struct eap_sim_db_data *data, const u8 *identity, - size_t identity_len) -{ - struct eap_sim_pseudonym *p; - struct eap_sim_reauth *r; - - if (identity_len == 0) - return NULL; - - p = eap_sim_db_get_pseudonym(data, identity, identity_len); - if (p == NULL) - p = eap_sim_db_get_pseudonym_id(data, identity, identity_len); - if (p) { - identity = p->identity; - identity_len = p->identity_len; - } - - r = data->reauths; - while (r) { - if (identity_len == r->identity_len && - os_memcmp(r->identity, identity, identity_len) == 0) - break; - r = r->next; - } - - return r; -} - - -/** - * eap_sim_db_identity_known - Verify whether the given identity is known - * @priv: Private data pointer from eap_sim_db_init() - * @identity: User name identity - * @identity_len: Length of identity in bytes - * Returns: 0 if the user is found or -1 on failure - * - * In most cases, the user name is ['0','1'] | IMSI, i.e., 1 followed by the - * IMSI in ASCII format, ['2','3'] | pseudonym, or ['4','5'] | reauth_id. - */ -int eap_sim_db_identity_known(void *priv, const u8 *identity, - size_t identity_len) -{ - struct eap_sim_db_data *data = priv; - - if (identity == NULL || identity_len < 2) - return -1; - - if (identity[0] == EAP_SIM_PSEUDONYM_PREFIX || - identity[0] == EAP_AKA_PSEUDONYM_PREFIX) { - struct eap_sim_pseudonym *p = - eap_sim_db_get_pseudonym(data, identity, identity_len); - return p ? 0 : -1; - } - - if (identity[0] == EAP_SIM_REAUTH_ID_PREFIX || - identity[0] == EAP_AKA_REAUTH_ID_PREFIX) { - struct eap_sim_reauth *r = - eap_sim_db_get_reauth(data, identity, identity_len); - return r ? 0 : -1; - } - - if (identity[0] != EAP_SIM_PERMANENT_PREFIX && - identity[0] != EAP_AKA_PERMANENT_PREFIX) { - /* Unknown identity prefix */ - return -1; - } - - /* TODO: Should consider asking HLR/AuC gateway whether this permanent - * identity is known. If it is, EAP-SIM/AKA can skip identity request. - * In case of EAP-AKA, this would reduce number of needed round-trips. - * Ideally, this would be done with one wait, i.e., just request - * authentication data and store it for the next use. This would then - * need to use similar pending-request functionality as the normal - * request for authentication data at later phase. - */ - return -1; -} - - static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix) { char *id, *pos, *end; u8 buf[10]; - if (os_get_random(buf, sizeof(buf))) + if (random_get_bytes(buf, sizeof(buf))) return NULL; id = os_malloc(sizeof(buf) * 2 + 2); if (id == NULL) @@ -847,8 +965,8 @@ 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 + * @data: Private data pointer from eap_sim_db_init() + * @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 @@ -856,18 +974,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(struct eap_sim_db_data *data, + 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 + * @data: Private data pointer from eap_sim_db_init() + * @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 @@ -876,19 +1007,31 @@ 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(struct eap_sim_db_data *data, + 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); } /** * eap_sim_db_add_pseudonym - EAP-SIM DB: Add new pseudonym - * @priv: Private data pointer from eap_sim_db_init() - * @identity: Identity of the user (may be permanent identity or pseudonym) - * @identity_len: Length of identity + * @data: Private data pointer from eap_sim_db_init() + * @permanent: Permanent username * @pseudonym: Pseudonym for this user. This needs to be an allocated buffer, * e.g., return value from eap_sim_db_get_next_pseudonym(). Caller must not * free it. @@ -897,20 +1040,22 @@ char * eap_sim_db_get_next_reauth_id(void *priv, int aka) * This function adds a new pseudonym for EAP-SIM user. EAP-SIM DB is * responsible of freeing pseudonym buffer once it is not needed anymore. */ -int eap_sim_db_add_pseudonym(void *priv, const u8 *identity, - size_t identity_len, char *pseudonym) +int eap_sim_db_add_pseudonym(struct eap_sim_db_data *data, + const char *permanent, char *pseudonym) { - struct eap_sim_db_data *data = priv; struct eap_sim_pseudonym *p; - wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add pseudonym for identity", - identity, identity_len); - wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pseudonym: %s", pseudonym); + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add pseudonym '%s' for permanent " + "username '%s'", pseudonym, permanent); /* TODO: could store last two pseudonyms */ - p = eap_sim_db_get_pseudonym(data, identity, identity_len); - if (p == NULL) - p = eap_sim_db_get_pseudonym_id(data, identity, identity_len); - +#ifdef CONFIG_SQLITE + if (data->sqlite_db) + return db_add_pseudonym(data, permanent, pseudonym); +#endif /* CONFIG_SQLITE */ + for (p = data->pseudonyms; p; p = p->next) { + if (os_strcmp(permanent, p->permanent) == 0) + break; + } if (p) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous " "pseudonym: %s", p->pseudonym); @@ -926,14 +1071,12 @@ int eap_sim_db_add_pseudonym(void *priv, const u8 *identity, } p->next = data->pseudonyms; - p->identity = os_malloc(identity_len); - if (p->identity == NULL) { + p->permanent = os_strdup(permanent); + if (p->permanent == NULL) { os_free(p); os_free(pseudonym); return -1; } - os_memcpy(p->identity, identity, identity_len); - p->identity_len = identity_len; p->pseudonym = pseudonym; data->pseudonyms = p; @@ -943,18 +1086,16 @@ int eap_sim_db_add_pseudonym(void *priv, const u8 *identity, static struct eap_sim_reauth * -eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, const u8 *identity, - size_t identity_len, char *reauth_id, u16 counter) +eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, + const char *permanent, + char *reauth_id, u16 counter) { struct eap_sim_reauth *r; - wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add reauth_id for identity", - identity, identity_len); - wpa_printf(MSG_DEBUG, "EAP-SIM DB: reauth_id: %s", reauth_id); - - r = eap_sim_db_get_reauth(data, identity, identity_len); - if (r == NULL) - r = eap_sim_db_get_reauth_id(data, identity, identity_len); + for (r = data->reauths; r; r = r->next) { + if (os_strcmp(r->permanent, permanent) == 0) + break; + } if (r) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous " @@ -969,14 +1110,12 @@ eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, const u8 *identity, } r->next = data->reauths; - r->identity = os_malloc(identity_len); - if (r->identity == NULL) { + r->permanent = os_strdup(permanent); + if (r->permanent == NULL) { os_free(r); os_free(reauth_id); return NULL; } - os_memcpy(r->identity, identity, identity_len); - r->identity_len = identity_len; r->reauth_id = reauth_id; data->reauths = r; wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new reauth entry"); @@ -991,7 +1130,7 @@ eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, const u8 *identity, /** * eap_sim_db_add_reauth - EAP-SIM DB: Add new re-authentication entry * @priv: Private data pointer from eap_sim_db_init() - * @identity: Identity of the user (may be permanent identity or pseudonym) + * @permanent: Permanent username * @identity_len: Length of identity * @reauth_id: reauth_id for this user. This needs to be an allocated buffer, * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not @@ -1004,20 +1143,24 @@ eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, const u8 *identity, * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed * anymore. */ -int eap_sim_db_add_reauth(void *priv, const u8 *identity, - size_t identity_len, char *reauth_id, u16 counter, - const u8 *mk) +int eap_sim_db_add_reauth(struct eap_sim_db_data *data, const char *permanent, + char *reauth_id, u16 counter, const u8 *mk) { - struct eap_sim_db_data *data = priv; struct eap_sim_reauth *r; - r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id, - counter); + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add reauth_id '%s' for permanent " + "identity '%s'", reauth_id, permanent); + +#ifdef CONFIG_SQLITE + if (data->sqlite_db) + return db_add_reauth(data, permanent, reauth_id, counter, mk, + NULL, NULL, NULL); +#endif /* CONFIG_SQLITE */ + r = eap_sim_db_add_reauth_data(data, permanent, reauth_id, counter); if (r == NULL) return -1; os_memcpy(r->mk, mk, EAP_SIM_MK_LEN); - r->aka_prime = 0; return 0; } @@ -1026,9 +1169,8 @@ int eap_sim_db_add_reauth(void *priv, const u8 *identity, #ifdef EAP_SERVER_AKA_PRIME /** * eap_sim_db_add_reauth_prime - EAP-AKA' DB: Add new re-authentication entry - * @priv: Private data pointer from eap_sim_db_init() - * @identity: Identity of the user (may be permanent identity or pseudonym) - * @identity_len: Length of identity + * @data: Private data pointer from eap_sim_db_init() + * @permanent: Permanent username * @reauth_id: reauth_id for this user. This needs to be an allocated buffer, * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not * free it. @@ -1042,20 +1184,25 @@ int eap_sim_db_add_reauth(void *priv, const u8 *identity, * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed * anymore. */ -int eap_sim_db_add_reauth_prime(void *priv, const u8 *identity, - size_t identity_len, char *reauth_id, - u16 counter, const u8 *k_encr, const u8 *k_aut, - const u8 *k_re) +int eap_sim_db_add_reauth_prime(struct eap_sim_db_data *data, + const char *permanent, char *reauth_id, + u16 counter, const u8 *k_encr, + const u8 *k_aut, const u8 *k_re) { - struct eap_sim_db_data *data = priv; struct eap_sim_reauth *r; - r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id, - counter); + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add reauth_id '%s' for permanent " + "identity '%s'", reauth_id, permanent); + +#ifdef CONFIG_SQLITE + if (data->sqlite_db) + return db_add_reauth(data, permanent, reauth_id, counter, NULL, + k_encr, k_aut, k_re); +#endif /* CONFIG_SQLITE */ + r = eap_sim_db_add_reauth_data(data, permanent, reauth_id, counter); if (r == NULL) return -1; - r->aka_prime = 1; os_memcpy(r->k_encr, k_encr, EAP_SIM_K_ENCR_LEN); os_memcpy(r->k_aut, k_aut, EAP_AKA_PRIME_K_AUT_LEN); os_memcpy(r->k_re, k_re, EAP_AKA_PRIME_K_RE_LEN); @@ -1067,66 +1214,75 @@ int eap_sim_db_add_reauth_prime(void *priv, const u8 *identity, /** * eap_sim_db_get_permanent - EAP-SIM DB: Get permanent identity - * @priv: Private data pointer from eap_sim_db_init() - * @identity: Identity of the user (may be permanent identity or pseudonym) - * @identity_len: Length of identity - * @len: Buffer for length of the returned permanent identity - * Returns: Pointer to the permanent identity, or %NULL if not found + * @data: Private data pointer from eap_sim_db_init() + * @pseudonym: Pseudonym username + * Returns: Pointer to permanent username or %NULL if not found */ -const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity, - size_t identity_len, size_t *len) +const char * +eap_sim_db_get_permanent(struct eap_sim_db_data *data, const char *pseudonym) { - struct eap_sim_db_data *data = priv; struct eap_sim_pseudonym *p; - if (identity == NULL) - return NULL; +#ifdef CONFIG_SQLITE + if (data->sqlite_db) + return db_get_pseudonym(data, pseudonym); +#endif /* CONFIG_SQLITE */ - p = eap_sim_db_get_pseudonym(data, identity, identity_len); - if (p == NULL) - p = eap_sim_db_get_pseudonym_id(data, identity, identity_len); - if (p == NULL) - return NULL; + p = data->pseudonyms; + while (p) { + if (os_strcmp(p->pseudonym, pseudonym) == 0) + return p->permanent; + p = p->next; + } - *len = p->identity_len; - return p->identity; + return NULL; } /** * eap_sim_db_get_reauth_entry - EAP-SIM DB: Get re-authentication entry - * @priv: Private data pointer from eap_sim_db_init() - * @identity: Identity of the user (may be permanent identity, pseudonym, or - * reauth_id) - * @identity_len: Length of identity + * @data: Private data pointer from eap_sim_db_init() + * @reauth_id: Fast re-authentication username * Returns: Pointer to the re-auth entry, or %NULL if not found */ struct eap_sim_reauth * -eap_sim_db_get_reauth_entry(void *priv, const u8 *identity, - size_t identity_len) +eap_sim_db_get_reauth_entry(struct eap_sim_db_data *data, + const char *reauth_id) { - struct eap_sim_db_data *data = priv; struct eap_sim_reauth *r; - if (identity == NULL) - return NULL; - r = eap_sim_db_get_reauth(data, identity, identity_len); - if (r == NULL) - r = eap_sim_db_get_reauth_id(data, identity, identity_len); +#ifdef CONFIG_SQLITE + if (data->sqlite_db) + return db_get_reauth(data, reauth_id); +#endif /* CONFIG_SQLITE */ + + r = data->reauths; + while (r) { + if (os_strcmp(r->reauth_id, reauth_id) == 0) + break; + r = r->next; + } + return r; } /** * eap_sim_db_remove_reauth - EAP-SIM DB: Remove re-authentication entry - * @priv: Private data pointer from eap_sim_db_init() + * @data: Private data pointer from eap_sim_db_init() * @reauth: Pointer to re-authentication entry from * eap_sim_db_get_reauth_entry() */ -void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth) +void eap_sim_db_remove_reauth(struct eap_sim_db_data *data, + struct eap_sim_reauth *reauth) { - struct eap_sim_db_data *data = priv; struct eap_sim_reauth *r, *prev = NULL; +#ifdef CONFIG_SQLITE + if (data->sqlite_db) { + db_remove_reauth(data, reauth); + return; + } +#endif /* CONFIG_SQLITE */ r = data->reauths; while (r) { if (r == reauth) { @@ -1145,9 +1301,8 @@ void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth) /** * eap_sim_db_get_aka_auth - Get AKA authentication values - * @priv: Private data pointer from eap_sim_db_init() - * @identity: User name identity - * @identity_len: Length of identity in bytes + * @data: Private data pointer from eap_sim_db_init() + * @username: Permanent username (prefix | IMSI) * @_rand: Buffer for RAND value * @autn: Buffer for AUTN value * @ik: Buffer for IK value @@ -1160,9 +1315,6 @@ void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth) * case, the callback function registered with eap_sim_db_init() will be * called once the results become available. * - * In most cases, the user name is '0' | IMSI, i.e., 0 followed by the IMSI in - * ASCII format. - * * When using an external server for AKA authentication, this function can * always start a request and return EAP_SIM_DB_PENDING immediately if * authentication triplets are not available. Once the authentication data are @@ -1171,40 +1323,29 @@ void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth) * eap_sim_db_get_aka_auth() function will then be called again and the newly * received triplets will then be given to the caller. */ -int eap_sim_db_get_aka_auth(void *priv, const u8 *identity, - size_t identity_len, u8 *_rand, u8 *autn, u8 *ik, - u8 *ck, u8 *res, size_t *res_len, - void *cb_session_ctx) +int eap_sim_db_get_aka_auth(struct eap_sim_db_data *data, const char *username, + u8 *_rand, u8 *autn, u8 *ik, u8 *ck, + u8 *res, size_t *res_len, void *cb_session_ctx) { - struct eap_sim_db_data *data = priv; struct eap_sim_db_pending *entry; int len; - size_t i; char msg[40]; + const char *imsi; + size_t imsi_len; - if (identity_len < 2 || identity == NULL || - identity[0] != EAP_AKA_PERMANENT_PREFIX) { - wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", - identity, identity_len); - return EAP_SIM_DB_FAILURE; - } - identity++; - identity_len--; - for (i = 0; i < identity_len; i++) { - if (identity[i] == '@') { - identity_len = i; - break; - } - } - if (identity_len + 1 > sizeof(entry->imsi)) { - wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", - identity, identity_len); + if (username == NULL || + (username[0] != EAP_AKA_PERMANENT_PREFIX && + username[0] != EAP_AKA_PRIME_PERMANENT_PREFIX) || + username[1] == '\0' || os_strlen(username) > sizeof(entry->imsi)) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'", + username); return EAP_SIM_DB_FAILURE; } - wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI", - identity, identity_len); + imsi = username + 1; + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI '%s'", + imsi); - entry = eap_sim_db_get_pending(data, identity, identity_len, 1); + entry = eap_sim_db_get_pending(data, imsi, 1); if (entry) { if (entry->state == FAILURE) { os_free(entry); @@ -1235,14 +1376,15 @@ int eap_sim_db_get_aka_auth(void *priv, const u8 *identity, return EAP_SIM_DB_FAILURE; } + imsi_len = os_strlen(imsi); len = os_snprintf(msg, sizeof(msg), "AKA-REQ-AUTH "); - if (len < 0 || len + identity_len >= sizeof(msg)) + if (len < 0 || len + imsi_len >= sizeof(msg)) return EAP_SIM_DB_FAILURE; - os_memcpy(msg + len, identity, identity_len); - len += identity_len; + os_memcpy(msg + len, imsi, imsi_len); + len += imsi_len; - wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication " - "data for IMSI", identity, identity_len); + wpa_printf(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication " + "data for IMSI '%s'", imsi); if (eap_sim_db_send(data, msg, len) < 0) return EAP_SIM_DB_FAILURE; @@ -1252,8 +1394,7 @@ int eap_sim_db_get_aka_auth(void *priv, const u8 *identity, os_get_time(&entry->timestamp); entry->aka = 1; - os_memcpy(entry->imsi, identity, identity_len); - entry->imsi_len = identity_len; + os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi)); entry->cb_session_ctx = cb_session_ctx; entry->state = PENDING; eap_sim_db_add_pending(data, entry); @@ -1265,9 +1406,8 @@ int eap_sim_db_get_aka_auth(void *priv, const u8 *identity, /** * eap_sim_db_resynchronize - Resynchronize AKA AUTN - * @priv: Private data pointer from eap_sim_db_init() - * @identity: User name identity - * @identity_len: Length of identity in bytes + * @data: Private data pointer from eap_sim_db_init() + * @username: Permanent username * @auts: AUTS value from the peer * @_rand: RAND value used in the rejected message * Returns: 0 on success, -1 on failure @@ -1278,42 +1418,35 @@ int eap_sim_db_get_aka_auth(void *priv, const u8 *identity, * eap_sim_db_get_aka_auth() will be called again to to fetch updated * RAND/AUTN values for the next challenge. */ -int eap_sim_db_resynchronize(void *priv, const u8 *identity, - size_t identity_len, const u8 *auts, - const u8 *_rand) +int eap_sim_db_resynchronize(struct eap_sim_db_data *data, + const char *username, + const u8 *auts, const u8 *_rand) { - struct eap_sim_db_data *data = priv; - size_t i; + const char *imsi; + size_t imsi_len; - if (identity_len < 2 || identity == NULL || - identity[0] != EAP_AKA_PERMANENT_PREFIX) { - wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", - identity, identity_len); - return -1; - } - identity++; - identity_len--; - for (i = 0; i < identity_len; i++) { - if (identity[i] == '@') { - identity_len = i; - break; - } - } - if (identity_len > 20) { - wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", - identity, identity_len); + if (username == NULL || + (username[0] != EAP_AKA_PERMANENT_PREFIX && + username[0] != EAP_AKA_PRIME_PERMANENT_PREFIX) || + username[1] == '\0' || os_strlen(username) > 20) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'", + username); return -1; } + imsi = username + 1; + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI '%s'", + imsi); if (data->sock >= 0) { char msg[100]; int len, ret; + imsi_len = os_strlen(imsi); len = os_snprintf(msg, sizeof(msg), "AKA-AUTS "); - if (len < 0 || len + identity_len >= sizeof(msg)) + if (len < 0 || len + imsi_len >= sizeof(msg)) return -1; - os_memcpy(msg + len, identity, identity_len); - len += identity_len; + os_memcpy(msg + len, imsi, imsi_len); + len += imsi_len; ret = os_snprintf(msg + len, sizeof(msg) - len, " "); if (ret < 0 || (size_t) ret >= sizeof(msg) - len) @@ -1327,11 +1460,42 @@ int eap_sim_db_resynchronize(void *priv, const u8 *identity, len += ret; len += wpa_snprintf_hex(msg + len, sizeof(msg) - len, _rand, EAP_AKA_RAND_LEN); - wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: reporting AKA AUTS for " - "IMSI", identity, identity_len); + wpa_printf(MSG_DEBUG, "EAP-SIM DB: reporting AKA AUTS for " + "IMSI '%s'", imsi); if (eap_sim_db_send(data, msg, len) < 0) return -1; } return 0; } + + +/** + * sim_get_username - Extract username from SIM identity + * @identity: Identity + * @identity_len: Identity length + * Returns: Allocated buffer with the username part of the identity + * + * Caller is responsible for freeing the returned buffer with os_free(). + */ +char * sim_get_username(const u8 *identity, size_t identity_len) +{ + char *username; + size_t pos; + + if (identity == NULL) + return NULL; + + for (pos = 0; pos < identity_len; pos++) { + if (identity[pos] == '@' || identity[pos] == '\0') + break; + } + + username = os_malloc(pos + 1); + if (username == NULL) + return NULL; + os_memcpy(username, identity, pos); + username[pos] = '\0'; + + return username; +} diff --git a/src/eap_server/eap_sim_db.h b/src/eap_server/eap_sim_db.h index ab89ae97d5a0f..53a1a7c3b4e30 100644 --- a/src/eap_server/eap_sim_db.h +++ b/src/eap_server/eap_sim_db.h @@ -1,15 +1,9 @@ /* * 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 program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published 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. */ #ifndef EAP_SIM_DB_H @@ -24,49 +18,57 @@ #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), - void *ctx); +struct eap_sim_db_data; + +struct eap_sim_db_data * +eap_sim_db_init(const char *config, + void (*get_complete_cb)(void *ctx, void *session_ctx), + void *ctx); void eap_sim_db_deinit(void *priv); -int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity, - size_t identity_len, int max_chal, +int eap_sim_db_get_gsm_triplets(struct eap_sim_db_data *data, + const char *username, int max_chal, u8 *_rand, u8 *kc, u8 *sres, void *cb_session_ctx); #define EAP_SIM_DB_FAILURE -1 #define EAP_SIM_DB_PENDING -2 -int eap_sim_db_identity_known(void *priv, const u8 *identity, - size_t identity_len); - -char * eap_sim_db_get_next_pseudonym(void *priv, int aka); +char * eap_sim_db_get_next_pseudonym(struct eap_sim_db_data *data, + 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(struct eap_sim_db_data *data, + enum eap_sim_db_method method); -int eap_sim_db_add_pseudonym(void *priv, const u8 *identity, - size_t identity_len, char *pseudonym); +int eap_sim_db_add_pseudonym(struct eap_sim_db_data *data, + const char *permanent, char *pseudonym); -int eap_sim_db_add_reauth(void *priv, const u8 *identity, - size_t identity_len, char *reauth_id, u16 counter, - const u8 *mk); -int eap_sim_db_add_reauth_prime(void *priv, const u8 *identity, - size_t identity_len, char *reauth_id, - u16 counter, const u8 *k_encr, const u8 *k_aut, - const u8 *k_re); +int eap_sim_db_add_reauth(struct eap_sim_db_data *data, const char *permanent, + char *reauth_id, u16 counter, const u8 *mk); +int eap_sim_db_add_reauth_prime(struct eap_sim_db_data *data, + const char *permanent, + char *reauth_id, u16 counter, const u8 *k_encr, + const u8 *k_aut, const u8 *k_re); -const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity, - size_t identity_len, size_t *len); +const char * eap_sim_db_get_permanent(struct eap_sim_db_data *data, + const char *pseudonym); struct eap_sim_reauth { struct eap_sim_reauth *next; - u8 *identity; - size_t identity_len; - char *reauth_id; + char *permanent; /* Permanent username */ + char *reauth_id; /* Fast re-authentication username */ u16 counter; - int aka_prime; u8 mk[EAP_SIM_MK_LEN]; u8 k_encr[EAP_SIM_K_ENCR_LEN]; u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN]; @@ -74,18 +76,20 @@ struct eap_sim_reauth { }; struct eap_sim_reauth * -eap_sim_db_get_reauth_entry(void *priv, const u8 *identity, - size_t identity_len); +eap_sim_db_get_reauth_entry(struct eap_sim_db_data *data, + const char *reauth_id); -void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth); +void eap_sim_db_remove_reauth(struct eap_sim_db_data *data, + struct eap_sim_reauth *reauth); -int eap_sim_db_get_aka_auth(void *priv, const u8 *identity, - size_t identity_len, u8 *_rand, u8 *autn, u8 *ik, - u8 *ck, u8 *res, size_t *res_len, - void *cb_session_ctx); +int eap_sim_db_get_aka_auth(struct eap_sim_db_data *data, const char *username, + u8 *_rand, u8 *autn, u8 *ik, u8 *ck, + u8 *res, size_t *res_len, void *cb_session_ctx); -int eap_sim_db_resynchronize(void *priv, const u8 *identity, - size_t identity_len, const u8 *auts, +int eap_sim_db_resynchronize(struct eap_sim_db_data *data, + const char *username, const u8 *auts, const u8 *_rand); +char * sim_get_username(const u8 *identity, size_t identity_len); + #endif /* EAP_SIM_DB_H */ diff --git a/src/eap_server/eap_tls_common.h b/src/eap_server/eap_tls_common.h index c34c40108b24e..11f5827513ab2 100644 --- a/src/eap_server/eap_tls_common.h +++ b/src/eap_server/eap_tls_common.h @@ -2,14 +2,8 @@ * EAP-TLS/PEAP/TTLS/FAST server common functions * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_TLS_COMMON_H @@ -68,7 +62,12 @@ struct eap_ssl_data { /* could be up to 128 bytes, but only the first 64 bytes are used */ #define EAP_TLS_KEY_LEN 64 +/* dummy type used as a flag for UNAUTH-TLS */ +#define EAP_UNAUTH_TLS_TYPE 255 + +struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len, + u8 code, u8 identifier); int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, int verify_peer); void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); diff --git a/src/eap_server/ikev2.c b/src/eap_server/ikev2.c index 435ba26249332..0e77efb7a2ef7 100644 --- a/src/eap_server/ikev2.c +++ b/src/eap_server/ikev2.c @@ -2,20 +2,15 @@ * IKEv2 initiator (RFC 4306) for EAP-IKEV2 * Copyright (c) 2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" #include "crypto/dh_groups.h" +#include "crypto/random.h" #include "ikev2.h" @@ -403,7 +398,7 @@ static int ikev2_process_ker(struct ikev2_initiator_data *data, } /* RFC 4306, Section 3.4: - * The length of DH public value MUST be equal to the lenght of the + * The length of DH public value MUST be equal to the length of the * prime modulus. */ if (ker_len - 4 != data->dh->prime_len) { @@ -1100,7 +1095,7 @@ static struct wpabuf * ikev2_build_sa_init(struct ikev2_initiator_data *data) data->i_spi, IKEV2_SPI_LEN); data->i_nonce_len = IKEV2_NONCE_MIN_LEN; - if (os_get_random(data->i_nonce, data->i_nonce_len)) + if (random_get_bytes(data->i_nonce, data->i_nonce_len)) return NULL; wpa_hexdump(MSG_DEBUG, "IKEV2: Ni", data->i_nonce, data->i_nonce_len); @@ -1148,7 +1143,7 @@ static struct wpabuf * ikev2_build_sa_auth(struct ikev2_initiator_data *data) if (data->shared_secret == NULL) return NULL; data->shared_secret_len = 16; - if (os_get_random(data->shared_secret, 16)) + if (random_get_bytes(data->shared_secret, 16)) return NULL; } else { os_free(data->shared_secret); diff --git a/src/eap_server/ikev2.h b/src/eap_server/ikev2.h index 8349fbe62de60..051a93869c1e8 100644 --- a/src/eap_server/ikev2.h +++ b/src/eap_server/ikev2.h @@ -2,14 +2,8 @@ * IKEv2 initiator (RFC 4306) for EAP-IKEV2 * Copyright (c) 2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef IKEV2_H diff --git a/src/eap_server/tncs.c b/src/eap_server/tncs.c index 497b51a0f3e5e..5e332ae02088f 100644 --- a/src/eap_server/tncs.c +++ b/src/eap_server/tncs.c @@ -2,14 +2,8 @@ * EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH) * Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -234,11 +228,11 @@ TNC_Result TNC_TNCS_ReportMessageTypes( return TNC_RESULT_INVALID_PARAMETER; os_free(imv->supported_types); imv->supported_types = - os_malloc(typeCount * sizeof(TNC_MessageTypeList)); + os_malloc(typeCount * sizeof(TNC_MessageType)); if (imv->supported_types == NULL) return TNC_RESULT_FATAL; os_memcpy(imv->supported_types, supportedTypes, - typeCount * sizeof(TNC_MessageTypeList)); + typeCount * sizeof(TNC_MessageType)); imv->num_supported_types = typeCount; return TNC_RESULT_SUCCESS; diff --git a/src/eap_server/tncs.h b/src/eap_server/tncs.h index 18a3a1fa3c474..ac7251bf8c3ae 100644 --- a/src/eap_server/tncs.h +++ b/src/eap_server/tncs.h @@ -2,14 +2,8 @@ * EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH) * Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TNCS_H diff --git a/src/eapol_auth/eapol_auth_dump.c b/src/eapol_auth/eapol_auth_dump.c index a0f0e8d616691..b6e0b137a52f2 100644 --- a/src/eapol_auth/eapol_auth_dump.c +++ b/src/eapol_auth/eapol_auth_dump.c @@ -2,14 +2,8 @@ * IEEE 802.1X-2004 Authenticator - State dump * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c index a1976e8f0a298..c3ccb46bf3832 100644 --- a/src/eapol_auth/eapol_auth_sm.c +++ b/src/eapol_auth/eapol_auth_sm.c @@ -2,14 +2,8 @@ * IEEE 802.1X-2004 Authenticator - EAPOL state machine * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -762,7 +756,9 @@ SM_STEP(CTRL_DIR) struct eapol_state_machine * eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, - int flags, const struct wpabuf *assoc_wps_ie, void *sta_ctx) + int flags, const struct wpabuf *assoc_wps_ie, + const struct wpabuf *assoc_p2p_ie, void *sta_ctx, + const char *identity, const char *radius_cui) { struct eapol_state_machine *sm; struct eap_config eap_conf; @@ -829,7 +825,11 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, eap_conf.tnc = eapol->conf.tnc; eap_conf.wps = eapol->conf.wps; eap_conf.assoc_wps_ie = assoc_wps_ie; + eap_conf.assoc_p2p_ie = assoc_p2p_ie; eap_conf.peer_addr = addr; + eap_conf.fragment_size = eapol->conf.fragment_size; + eap_conf.pwd_group = eapol->conf.pwd_group; + eap_conf.pbc_in_m1 = eapol->conf.pbc_in_m1; sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf); if (sm->eap == NULL) { eapol_auth_free(sm); @@ -839,6 +839,15 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, eapol_auth_initialize(sm); + if (identity) { + sm->identity = (u8 *) os_strdup(identity); + if (sm->identity) + sm->identity_len = os_strlen(identity); + } + if (radius_cui) + sm->radius_cui = wpabuf_alloc_copy(radius_cui, + os_strlen(radius_cui)); + return sm; } @@ -1012,7 +1021,7 @@ static struct eapol_callbacks eapol_cb = int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx) { - if (sm == NULL || ctx != sm->eap) + if (sm == NULL || ctx == NULL || ctx != sm->eap) return -1; eap_sm_pending_cb(sm->eap); @@ -1034,6 +1043,8 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst, dst->msg_ctx = src->msg_ctx; dst->eap_sim_db_priv = src->eap_sim_db_priv; os_free(dst->eap_req_id_text); + dst->pwd_group = src->pwd_group; + dst->pbc_in_m1 = src->pbc_in_m1; if (src->eap_req_id_text) { dst->eap_req_id_text = os_malloc(src->eap_req_id_text_len); if (dst->eap_req_id_text == NULL) @@ -1077,6 +1088,7 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst, dst->eap_sim_aka_result_ind = src->eap_sim_aka_result_ind; dst->tnc = src->tnc; dst->wps = src->wps; + dst->fragment_size = src->fragment_size; return 0; } diff --git a/src/eapol_auth/eapol_auth_sm.h b/src/eapol_auth/eapol_auth_sm.h index ef943ad478137..b50bbdd0f1ce6 100644 --- a/src/eapol_auth/eapol_auth_sm.h +++ b/src/eapol_auth/eapol_auth_sm.h @@ -2,14 +2,8 @@ * IEEE 802.1X-2004 Authenticator - EAPOL state machine * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAPOL_AUTH_SM_H @@ -40,6 +34,9 @@ struct eapol_auth_config { int eap_sim_aka_result_ind; int tnc; struct wps_context *wps; + int fragment_size; + u16 pwd_group; + int pbc_in_m1; /* Opaque context pointer to owner data for callback functions */ void *ctx; @@ -79,7 +76,9 @@ struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf, void eapol_auth_deinit(struct eapol_authenticator *eapol); struct eapol_state_machine * eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, - int flags, const struct wpabuf *assoc_wps_ie, void *sta_ctx); + int flags, const struct wpabuf *assoc_wps_ie, + const struct wpabuf *assoc_p2p_ie, void *sta_ctx, + const char *identity, const char *radius_cui); void eapol_auth_free(struct eapol_state_machine *sm); void eapol_auth_step(struct eapol_state_machine *sm); void eapol_auth_dump_state(FILE *f, const char *prefix, diff --git a/src/eapol_auth/eapol_auth_sm_i.h b/src/eapol_auth/eapol_auth_sm_i.h index 1000da4df148c..d7f893a1d666e 100644 --- a/src/eapol_auth/eapol_auth_sm_i.h +++ b/src/eapol_auth/eapol_auth_sm_i.h @@ -2,14 +2,8 @@ * IEEE 802.1X-2004 Authenticator - EAPOL state machine (internal definitions) * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAPOL_AUTH_SM_I_H @@ -163,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 77cd564b0d43b..f90fb6257880f 100644 --- a/src/eapol_supp/eapol_supp_sm.c +++ b/src/eapol_supp/eapol_supp_sm.c @@ -1,15 +1,9 @@ /* * EAPOL supplicant state machines - * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-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 - * published 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" @@ -145,46 +139,6 @@ struct eapol_sm { }; -#define IEEE8021X_REPLAY_COUNTER_LEN 8 -#define IEEE8021X_KEY_SIGN_LEN 16 -#define IEEE8021X_KEY_IV_LEN 16 - -#define IEEE8021X_KEY_INDEX_FLAG 0x80 -#define IEEE8021X_KEY_INDEX_MASK 0x03 - -#ifdef _MSC_VER -#pragma pack(push, 1) -#endif /* _MSC_VER */ - -struct ieee802_1x_eapol_key { - u8 type; - /* Note: key_length is unaligned */ - u8 key_length[2]; - /* does not repeat within the life of the keying material used to - * encrypt the Key field; 64-bit NTP timestamp MAY be used here */ - u8 replay_counter[IEEE8021X_REPLAY_COUNTER_LEN]; - u8 key_iv[IEEE8021X_KEY_IV_LEN]; /* cryptographically random number */ - u8 key_index; /* key flag in the most significant bit: - * 0 = broadcast (default key), - * 1 = unicast (key mapping key); key index is in the - * 7 least significant bits */ - /* HMAC-MD5 message integrity check computed with MS-MPPE-Send-Key as - * the key */ - u8 key_signature[IEEE8021X_KEY_SIGN_LEN]; - - /* followed by key: if packet body length = 44 + key length, then the - * key field (of key_length bytes) contains the key in encrypted form; - * if packet body length = 44, key field is absent and key_length - * represents the number of least significant octets from - * MS-MPPE-Send-Key attribute to be used as the keying material; - * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */ -} STRUCT_PACKED; - -#ifdef _MSC_VER -#pragma pack(pop) -#endif /* _MSC_VER */ - - static void eapol_sm_txLogoff(struct eapol_sm *sm); static void eapol_sm_txStart(struct eapol_sm *sm); static void eapol_sm_processKey(struct eapol_sm *sm); @@ -268,6 +222,15 @@ SM_STATE(SUPP_PAE, DISCONNECTED) sm->unicast_key_received = FALSE; sm->broadcast_key_received = FALSE; + + /* + * IEEE Std 802.1X-2004 does not clear heldWhile here, but doing so + * allows the timer tick to be stopped more quickly when the port is + * not enabled. Since this variable is used only within HELD state, + * clearing it on initialization does not change actual state machine + * behavior. + */ + sm->heldWhile = 0; } @@ -535,6 +498,15 @@ SM_STATE(SUPP_BE, INITIALIZE) SM_ENTRY(SUPP_BE, INITIALIZE); eapol_sm_abortSupp(sm); sm->suppAbort = FALSE; + + /* + * IEEE Std 802.1X-2004 does not clear authWhile here, but doing so + * allows the timer tick to be stopped more quickly when the port is + * not enabled. Since this variable is used only within RECEIVE state, + * clearing it on initialization does not change actual state machine + * behavior. + */ + sm->authWhile = 0; } @@ -561,7 +533,7 @@ SM_STEP(SUPP_BE) * IEEE Std 802.1X-2004 has transitions from REQUEST to FAIL * and SUCCESS based on eapFail and eapSuccess, respectively. * However, IEEE Std 802.1X-2004 is also specifying that - * eapNoResp should be set in conjuction with eapSuccess and + * eapNoResp should be set in conjunction with eapSuccess and * eapFail which would mean that more than one of the * transitions here would be activated at the same time. * Skipping RESPONSE and/or RECEIVE states in these cases can @@ -652,6 +624,7 @@ struct eap_key_data { static void eapol_sm_processKey(struct eapol_sm *sm) { +#ifndef CONFIG_FIPS struct ieee802_1x_hdr *hdr; struct ieee802_1x_eapol_key *key; struct eap_key_data keydata; @@ -659,6 +632,7 @@ static void eapol_sm_processKey(struct eapol_sm *sm) u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN]; int key_len, res, sign_key_len, encr_key_len; u16 rx_key_length; + size_t plen; wpa_printf(MSG_DEBUG, "EAPOL: processKey"); if (sm->last_rx_key == NULL) @@ -671,9 +645,12 @@ static void eapol_sm_processKey(struct eapol_sm *sm) return; } + if (sm->last_rx_key_len < sizeof(*hdr) + sizeof(*key)) + return; hdr = (struct ieee802_1x_hdr *) sm->last_rx_key; key = (struct ieee802_1x_eapol_key *) (hdr + 1); - if (sizeof(*hdr) + be_to_host16(hdr->length) > sm->last_rx_key_len) { + plen = be_to_host16(hdr->length); + if (sizeof(*hdr) + plen > sm->last_rx_key_len || plen < sizeof(*key)) { wpa_printf(MSG_WARNING, "EAPOL: Too short EAPOL-Key frame"); return; } @@ -739,7 +716,7 @@ static void eapol_sm_processKey(struct eapol_sm *sm) } wpa_printf(MSG_DEBUG, "EAPOL: EAPOL-Key key signature verified"); - key_len = be_to_host16(hdr->length) - sizeof(*key); + key_len = plen - sizeof(*key); if (key_len > 32 || rx_key_length > 32) { wpa_printf(MSG_WARNING, "EAPOL: Too long key data length %d", key_len ? key_len : rx_key_length); @@ -810,6 +787,7 @@ static void eapol_sm_processKey(struct eapol_sm *sm) sm->ctx->eapol_done_cb(sm->ctx->ctx); } } +#endif /* CONFIG_FIPS */ } @@ -1029,6 +1007,21 @@ void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod, } +/** + * eapol_sm_get_method_name - Get EAPOL method name + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * Returns: Static string containing name of current eap method or NULL + */ +const char * eapol_sm_get_method_name(struct eapol_sm *sm) +{ + if (sm->SUPP_PAE_state != SUPP_PAE_AUTHENTICATED || + sm->suppPortStatus != Authorized) + return NULL; + + return eap_sm_get_method_name(sm->eap); +} + + #ifdef CONFIG_CTRL_IFACE /** * eapol_sm_get_status - Get EAPOL state machine status @@ -1476,10 +1469,7 @@ void eapol_sm_notify_cached(struct eapol_sm *sm) if (sm == NULL) return; wpa_printf(MSG_DEBUG, "EAPOL: PMKSA caching was used - skip EAPOL"); - sm->SUPP_PAE_state = SUPP_PAE_AUTHENTICATED; - sm->suppPortStatus = Authorized; - eapol_sm_set_port_authorized(sm); - sm->portValid = TRUE; + sm->eapSuccess = TRUE; eap_notify_success(sm->eap); eapol_sm_step(sm); } @@ -1751,7 +1741,8 @@ static void eapol_sm_set_int(void *ctx, enum eapol_int_var variable, switch (variable) { case EAPOL_idleWhile: sm->idleWhile = value; - eapol_enable_timer_tick(sm); + if (sm->idleWhile > 0) + eapol_enable_timer_tick(sm); break; } } @@ -1798,7 +1789,7 @@ static void eapol_sm_notify_pending(void *ctx) #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) -static void eapol_sm_eap_param_needed(void *ctx, const char *field, +static void eapol_sm_eap_param_needed(void *ctx, enum wpa_ctrl_req_type field, const char *txt) { struct eapol_sm *sm = ctx; @@ -1810,6 +1801,35 @@ static void eapol_sm_eap_param_needed(void *ctx, const char *field, #define eapol_sm_eap_param_needed NULL #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ +static void eapol_sm_notify_cert(void *ctx, int depth, const char *subject, + const char *cert_hash, + const struct wpabuf *cert) +{ + struct eapol_sm *sm = ctx; + if (sm->ctx->cert_cb) + sm->ctx->cert_cb(sm->ctx->ctx, depth, subject, + 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 void eapol_sm_set_anon_id(void *ctx, const u8 *id, size_t len) +{ + struct eapol_sm *sm = ctx; + + if (sm->ctx->set_anon_id) + sm->ctx->set_anon_id(sm->ctx->ctx, id, len); +} + static struct eapol_callbacks eapol_cb = { @@ -1822,7 +1842,10 @@ static struct eapol_callbacks eapol_cb = eapol_sm_set_config_blob, eapol_sm_get_config_blob, eapol_sm_notify_pending, - eapol_sm_eap_param_needed + eapol_sm_eap_param_needed, + eapol_sm_notify_cert, + eapol_sm_notify_status, + eapol_sm_set_anon_id }; @@ -1858,6 +1881,7 @@ struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) conf.pkcs11_engine_path = ctx->pkcs11_engine_path; conf.pkcs11_module_path = ctx->pkcs11_module_path; conf.wps = ctx->wps; + conf.cert_in_cb = ctx->cert_in_cb; sm->eap = eap_peer_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx, &conf); if (sm->eap == NULL) { @@ -1896,3 +1920,19 @@ void eapol_sm_deinit(struct eapol_sm *sm) os_free(sm->ctx); os_free(sm); } + + +void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm, + struct ext_password_data *ext) +{ + if (sm && sm->eap) + eap_sm_set_ext_pw_ctx(sm->eap, ext); +} + + +int eapol_sm_failed(struct eapol_sm *sm) +{ + if (sm == NULL) + return 0; + return !sm->eapSuccess && sm->eapFail; +} diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h index 1d2a32bb7cbf8..c4b87da1f6bf1 100644 --- a/src/eapol_supp/eapol_supp_sm.h +++ b/src/eapol_supp/eapol_supp_sm.h @@ -1,15 +1,9 @@ /* * EAPOL supplicant state machines - * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-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 - * published 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. */ #ifndef EAPOL_SUPP_SM_H @@ -208,10 +202,10 @@ struct eapol_ctx { /** * eap_param_needed - Notify that EAP parameter is needed * @ctx: Callback context (ctx) - * @field: Field name (e.g., "IDENTITY") + * @field: Field indicator (e.g., WPA_CTRL_REQ_EAP_IDENTITY) * @txt: User readable text describing the required parameter */ - void (*eap_param_needed)(void *ctx, const char *field, + void (*eap_param_needed)(void *ctx, enum wpa_ctrl_req_type field, const char *txt); /** @@ -220,10 +214,44 @@ struct eapol_ctx { * @authorized: Whether the supplicant port is now in authorized state */ void (*port_cb)(void *ctx, int authorized); + + /** + * cert_cb - Notification of a peer certificate + * @ctx: Callback context (ctx) + * @depth: Depth in certificate chain (0 = server) + * @subject: Subject of the peer certificate + * @cert_hash: SHA-256 hash of the certificate + * @cert: Peer certificate + */ + void (*cert_cb)(void *ctx, int depth, const char *subject, + const char *cert_hash, const struct wpabuf *cert); + + /** + * cert_in_cb - Include server certificates in callback + */ + int cert_in_cb; + + /** + * status_cb - Notification of a change in EAP status + * @ctx: Callback context (ctx) + * @status: Step in the process of EAP authentication + * @parameter: Step-specific parameter, e.g., EAP method name + */ + void (*status_cb)(void *ctx, const char *status, + const char *parameter); + + /** + * set_anon_id - Set or add anonymous identity + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @id: Anonymous identity (e.g., EAP-SIM pseudonym) + * @len: Length of anonymous identity in octets + */ + void (*set_anon_id)(void *ctx, const u8 *id, size_t len); }; struct eap_peer_config; +struct ext_password_data; #ifdef IEEE8021X_EAPOL struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx); @@ -255,6 +283,10 @@ void eapol_sm_notify_ctrl_response(struct eapol_sm *sm); void eapol_sm_request_reauth(struct eapol_sm *sm); void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, int in_eapol_sm); void eapol_sm_invalidate_cached_session(struct eapol_sm *sm); +const char * eapol_sm_get_method_name(struct eapol_sm *sm); +void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm, + struct ext_password_data *ext); +int eapol_sm_failed(struct eapol_sm *sm); #else /* IEEE8021X_EAPOL */ static inline struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) { @@ -342,6 +374,18 @@ static inline void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, static inline void eapol_sm_invalidate_cached_session(struct eapol_sm *sm) { } +static inline const char * eapol_sm_get_method_name(struct eapol_sm *sm) +{ + return NULL; +} +static inline void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm, + struct ext_password_data *ext) +{ +} +static inline int eapol_sm_failed(struct eapol_sm *sm) +{ + return 0; +} #endif /* IEEE8021X_EAPOL */ #endif /* EAPOL_SUPP_SM_H */ diff --git a/src/l2_packet/l2_packet.h b/src/l2_packet/l2_packet.h index c7b50141e9c37..dd825b56894df 100644 --- a/src/l2_packet/l2_packet.h +++ b/src/l2_packet/l2_packet.h @@ -2,14 +2,8 @@ * WPA Supplicant - Layer2 packet interface definition * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file defines an interface for layer 2 (link layer) packet sending and * receiving. l2_packet_linux.c is one implementation for such a layer 2 diff --git a/src/l2_packet/l2_packet_freebsd.c b/src/l2_packet/l2_packet_freebsd.c index 009e02c79c669..2e9a04c8943c4 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" @@ -20,7 +14,11 @@ #include <pcap.h> #include <sys/ioctl.h> +#ifdef __sun__ +#include <libdlpi.h> +#else /* __sun__ */ #include <sys/sysctl.h> +#endif /* __sun__ */ #include <net/if.h> #include <net/if_dl.h> @@ -139,6 +137,7 @@ static int l2_packet_init_libpcap(struct l2_packet_data *l2, } pcap_freecode(&pcap_fp); +#ifndef __sun__ /* * When libpcap uses BPF we must enable "immediate mode" to * receive frames right away; otherwise the system may @@ -153,6 +152,7 @@ static int l2_packet_init_libpcap(struct l2_packet_data *l2, /* XXX should we fail? */ } } +#endif /* __sun__ */ eloop_register_read_sock(pcap_get_selectable_fd(l2->pcap), l2_packet_receive, l2, l2->pcap); @@ -163,6 +163,30 @@ static int l2_packet_init_libpcap(struct l2_packet_data *l2, static int eth_get(const char *device, u8 ea[ETH_ALEN]) { +#ifdef __sun__ + dlpi_handle_t dh; + u32 physaddrlen = DLPI_PHYSADDR_MAX; + u8 physaddr[DLPI_PHYSADDR_MAX]; + int retval; + + retval = dlpi_open(device, &dh, 0); + if (retval != DLPI_SUCCESS) { + wpa_printf(MSG_ERROR, "dlpi_open error: %s", + dlpi_strerror(retval)); + return -1; + } + + retval = dlpi_get_physaddr(dh, DL_CURR_PHYS_ADDR, physaddr, + &physaddrlen); + if (retval != DLPI_SUCCESS) { + wpa_printf(MSG_ERROR, "dlpi_get_physaddr error: %s", + dlpi_strerror(retval)); + dlpi_close(dh); + return -1; + } + os_memcpy(ea, physaddr, ETH_ALEN); + dlpi_close(dh); +#else /* __sun__ */ struct if_msghdr *ifm; struct sockaddr_dl *sdl; u_char *p, *buf; @@ -195,6 +219,7 @@ static int eth_get(const char *device, u8 ea[ETH_ALEN]) errno = ESRCH; return -1; } +#endif /* __sun__ */ return 0; } diff --git a/src/l2_packet/l2_packet_linux.c b/src/l2_packet/l2_packet_linux.c index 48d1bde024e24..1419830db7ac5 100644 --- a/src/l2_packet/l2_packet_linux.c +++ b/src/l2_packet/l2_packet_linux.c @@ -2,14 +2,8 @@ * WPA Supplicant - Layer2 packet handling with Linux packet sockets * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -51,7 +45,8 @@ int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, if (l2->l2_hdr) { ret = send(l2->fd, buf, len, 0); if (ret < 0) - perror("l2_packet_send - send"); + wpa_printf(MSG_ERROR, "l2_packet_send - send: %s", + strerror(errno)); } else { struct sockaddr_ll ll; os_memset(&ll, 0, sizeof(ll)); @@ -62,8 +57,10 @@ int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, os_memcpy(ll.sll_addr, dst_addr, ETH_ALEN); ret = sendto(l2->fd, buf, len, 0, (struct sockaddr *) &ll, sizeof(ll)); - if (ret < 0) - perror("l2_packet_send - sendto"); + if (ret < 0) { + wpa_printf(MSG_ERROR, "l2_packet_send - sendto: %s", + strerror(errno)); + } } return ret; } @@ -82,7 +79,8 @@ static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll, &fromlen); if (res < 0) { - perror("l2_packet_receive - recvfrom"); + wpa_printf(MSG_DEBUG, "l2_packet_receive - recvfrom: %s", + strerror(errno)); return; } @@ -111,14 +109,16 @@ struct l2_packet_data * l2_packet_init( l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM, htons(protocol)); if (l2->fd < 0) { - perror("socket(PF_PACKET)"); + wpa_printf(MSG_ERROR, "%s: socket(PF_PACKET): %s", + __func__, strerror(errno)); os_free(l2); return NULL; } os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, l2->ifname, sizeof(ifr.ifr_name)); if (ioctl(l2->fd, SIOCGIFINDEX, &ifr) < 0) { - perror("ioctl[SIOCGIFINDEX]"); + wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFINDEX]: %s", + __func__, strerror(errno)); close(l2->fd); os_free(l2); return NULL; @@ -130,14 +130,16 @@ struct l2_packet_data * l2_packet_init( ll.sll_ifindex = ifr.ifr_ifindex; ll.sll_protocol = htons(protocol); if (bind(l2->fd, (struct sockaddr *) &ll, sizeof(ll)) < 0) { - perror("bind[PF_PACKET]"); + wpa_printf(MSG_ERROR, "%s: bind[PF_PACKET]: %s", + __func__, strerror(errno)); close(l2->fd); os_free(l2); return NULL; } if (ioctl(l2->fd, SIOCGIFHWADDR, &ifr) < 0) { - perror("ioctl[SIOCGIFHWADDR]"); + wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFHWADDR]: %s", + __func__, strerror(errno)); close(l2->fd); os_free(l2); return NULL; @@ -173,14 +175,16 @@ int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { - perror("socket"); + wpa_printf(MSG_ERROR, "%s: socket: %s", + __func__, strerror(errno)); return -1; } os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, l2->ifname, sizeof(ifr.ifr_name)); if (ioctl(s, SIOCGIFADDR, &ifr) < 0) { if (errno != EADDRNOTAVAIL) - perror("ioctl[SIOCGIFADDR]"); + wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFADDR]: %s", + __func__, strerror(errno)); close(s); return -1; } diff --git a/src/l2_packet/l2_packet_ndis.c b/src/l2_packet/l2_packet_ndis.c index 6ce29aa20ec93..23b8ddcc9e361 100644 --- a/src/l2_packet/l2_packet_ndis.c +++ b/src/l2_packet/l2_packet_ndis.c @@ -2,14 +2,8 @@ * WPA Supplicant - Layer2 packet handling with Microsoft NDISUIO * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This implementation requires Windows specific event loop implementation, * i.e., eloop_win.c. In addition, the NDISUIO connection is shared with diff --git a/src/l2_packet/l2_packet_none.c b/src/l2_packet/l2_packet_none.c index 5e3f6e972384f..b01e830220f88 100644 --- a/src/l2_packet/l2_packet_none.c +++ b/src/l2_packet/l2_packet_none.c @@ -2,14 +2,8 @@ * WPA Supplicant - Layer2 packet handling example with dummy functions * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file can be used as a starting point for layer2 packet implementation. */ diff --git a/src/l2_packet/l2_packet_pcap.c b/src/l2_packet/l2_packet_pcap.c index 8156e294b1bb2..45aef56bcdbd8 100644 --- a/src/l2_packet/l2_packet_pcap.c +++ b/src/l2_packet/l2_packet_pcap.c @@ -2,14 +2,8 @@ * WPA Supplicant - Layer2 packet handling with libpcap/libdnet and WinPcap * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * 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 79d29681565a1..6b117ca2b9076 100644 --- a/src/l2_packet/l2_packet_privsep.c +++ b/src/l2_packet/l2_packet_privsep.c @@ -2,14 +2,8 @@ * WPA Supplicant - Layer2 packet handling with privilege separation * Copyright (c) 2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -179,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/l2_packet/l2_packet_winpcap.c b/src/l2_packet/l2_packet_winpcap.c index f76b386fc033a..b6e5088938ec0 100644 --- a/src/l2_packet/l2_packet_winpcap.c +++ b/src/l2_packet/l2_packet_winpcap.c @@ -2,14 +2,8 @@ * WPA Supplicant - Layer2 packet handling with WinPcap RX thread * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This l2_packet implementation is explicitly for WinPcap and Windows events. * l2_packet_pcap.c has support for WinPcap, but it requires polling to receive diff --git a/src/p2p/Makefile b/src/p2p/Makefile new file mode 100644 index 0000000000000..cffba620da04f --- /dev/null +++ b/src/p2p/Makefile @@ -0,0 +1,9 @@ +all: + @echo Nothing to be made. + +clean: + for d in $(SUBDIRS); do make -C $$d clean; done + rm -f *~ *.o *.d + +install: + @echo Nothing to be made. diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c new file mode 100644 index 0000000000000..b994a44abc1cd --- /dev/null +++ b/src/p2p/p2p.c @@ -0,0 +1,4320 @@ +/* + * Wi-Fi Direct - P2P module + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "common/wpa_ctrl.h" +#include "wps/wps_i.h" +#include "p2p_i.h" +#include "p2p.h" + + +static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx); +static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev); +static void p2p_process_presence_req(struct p2p_data *p2p, const u8 *da, + const u8 *sa, const u8 *data, size_t len, + int rx_freq); +static void p2p_process_presence_resp(struct p2p_data *p2p, const u8 *da, + const u8 *sa, const u8 *data, + size_t len); +static void p2p_ext_listen_timeout(void *eloop_ctx, void *timeout_ctx); +static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx); + + +/* + * p2p_scan recovery timeout + * + * Many drivers are using 30 second timeout on scan results. Allow a bit larger + * timeout for this to avoid hitting P2P timeout unnecessarily. + */ +#define P2P_SCAN_TIMEOUT 35 + +/** + * P2P_PEER_EXPIRATION_AGE - Number of seconds after which inactive peer + * entries will be removed + */ +#define P2P_PEER_EXPIRATION_AGE 300 + +#define P2P_PEER_EXPIRATION_INTERVAL (P2P_PEER_EXPIRATION_AGE / 2) + +static void p2p_expire_peers(struct p2p_data *p2p) +{ + struct p2p_device *dev, *n; + struct os_time now; + size_t i; + + os_get_time(&now); + dl_list_for_each_safe(dev, n, &p2p->devices, struct p2p_device, list) { + if (dev->last_seen.sec + P2P_PEER_EXPIRATION_AGE >= now.sec) + continue; + + if (p2p->cfg->go_connected && + p2p->cfg->go_connected(p2p->cfg->cb_ctx, + dev->info.p2p_device_addr)) { + /* + * We are connected as a client to a group in which the + * peer is the GO, so do not expire the peer entry. + */ + os_get_time(&dev->last_seen); + continue; + } + + for (i = 0; i < p2p->num_groups; i++) { + if (p2p_group_is_client_connected( + p2p->groups[i], dev->info.p2p_device_addr)) + break; + } + if (i < p2p->num_groups) { + /* + * The peer is connected as a client in a group where + * we are the GO, so do not expire the peer entry. + */ + os_get_time(&dev->last_seen); + continue; + } + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Expiring old peer " + "entry " MACSTR, MAC2STR(dev->info.p2p_device_addr)); + dl_list_del(&dev->list); + p2p_device_free(p2p, dev); + } +} + + +static void p2p_expiration_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct p2p_data *p2p = eloop_ctx; + p2p_expire_peers(p2p); + eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0, + p2p_expiration_timeout, p2p, NULL); +} + + +static const char * p2p_state_txt(int state) +{ + switch (state) { + case P2P_IDLE: + return "IDLE"; + case P2P_SEARCH: + return "SEARCH"; + case P2P_CONNECT: + return "CONNECT"; + case P2P_CONNECT_LISTEN: + return "CONNECT_LISTEN"; + case P2P_GO_NEG: + return "GO_NEG"; + case P2P_LISTEN_ONLY: + return "LISTEN_ONLY"; + case P2P_WAIT_PEER_CONNECT: + return "WAIT_PEER_CONNECT"; + case P2P_WAIT_PEER_IDLE: + return "WAIT_PEER_IDLE"; + case P2P_SD_DURING_FIND: + return "SD_DURING_FIND"; + case P2P_PROVISIONING: + return "PROVISIONING"; + case P2P_PD_DURING_FIND: + return "PD_DURING_FIND"; + case P2P_INVITE: + return "INVITE"; + case P2P_INVITE_LISTEN: + return "INVITE_LISTEN"; + case P2P_SEARCH_WHEN_READY: + return "SEARCH_WHEN_READY"; + case P2P_CONTINUE_SEARCH_WHEN_READY: + return "CONTINUE_SEARCH_WHEN_READY"; + default: + return "?"; + } +} + + +u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr) +{ + struct p2p_device *dev = NULL; + + if (!addr || !p2p) + return 0; + + dev = p2p_get_device(p2p, addr); + if (dev) + return dev->wps_prov_info; + else + return 0; +} + + +void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *addr) +{ + struct p2p_device *dev = NULL; + + if (!addr || !p2p) + return; + + dev = p2p_get_device(p2p, addr); + if (dev) + dev->wps_prov_info = 0; +} + + +void p2p_set_state(struct p2p_data *p2p, int new_state) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: State %s -> %s", + p2p_state_txt(p2p->state), p2p_state_txt(new_state)); + p2p->state = new_state; +} + + +void p2p_set_timeout(struct p2p_data *p2p, unsigned int sec, unsigned int usec) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Set timeout (state=%s): %u.%06u sec", + p2p_state_txt(p2p->state), sec, usec); + eloop_cancel_timeout(p2p_state_timeout, p2p, NULL); + eloop_register_timeout(sec, usec, p2p_state_timeout, p2p, NULL); +} + + +void p2p_clear_timeout(struct p2p_data *p2p) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Clear timeout (state=%s)", + p2p_state_txt(p2p->state)); + eloop_cancel_timeout(p2p_state_timeout, p2p, NULL); +} + + +void p2p_go_neg_failed(struct p2p_data *p2p, struct p2p_device *peer, + int status) +{ + struct p2p_go_neg_results res; + p2p_clear_timeout(p2p); + p2p_set_state(p2p, P2P_IDLE); + if (p2p->go_neg_peer) + p2p->go_neg_peer->wps_method = WPS_NOT_READY; + p2p->go_neg_peer = NULL; + + os_memset(&res, 0, sizeof(res)); + res.status = status; + if (peer) { + os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, + ETH_ALEN); + os_memcpy(res.peer_interface_addr, peer->intended_addr, + ETH_ALEN); + } + p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res); +} + + +static void p2p_listen_in_find(struct p2p_data *p2p, int dev_disc) +{ + unsigned int r, tu; + int freq; + struct wpabuf *ies; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Starting short listen state (state=%s)", + p2p_state_txt(p2p->state)); + + freq = p2p_channel_to_freq(p2p->cfg->country, p2p->cfg->reg_class, + p2p->cfg->channel); + if (freq < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unknown regulatory class/channel"); + return; + } + + os_get_random((u8 *) &r, sizeof(r)); + tu = (r % ((p2p->max_disc_int - p2p->min_disc_int) + 1) + + p2p->min_disc_int) * 100; + if (p2p->max_disc_tu >= 0 && tu > (unsigned int) p2p->max_disc_tu) + tu = p2p->max_disc_tu; + if (!dev_disc && tu < 100) + tu = 100; /* Need to wait in non-device discovery use cases */ + if (p2p->cfg->max_listen && 1024 * tu / 1000 > p2p->cfg->max_listen) + tu = p2p->cfg->max_listen * 1000 / 1024; + + if (tu == 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Skip listen state " + "since duration was 0 TU"); + p2p_set_timeout(p2p, 0, 0); + return; + } + + p2p->pending_listen_freq = freq; + p2p->pending_listen_sec = 0; + p2p->pending_listen_usec = 1024 * tu; + + ies = p2p_build_probe_resp_ies(p2p); + if (ies == NULL) + return; + + if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, 1024 * tu / 1000, + ies) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to start listen mode"); + p2p->pending_listen_freq = 0; + } + wpabuf_free(ies); +} + + +int p2p_listen(struct p2p_data *p2p, unsigned int timeout) +{ + int freq; + struct wpabuf *ies; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Going to listen(only) state"); + + freq = p2p_channel_to_freq(p2p->cfg->country, p2p->cfg->reg_class, + p2p->cfg->channel); + if (freq < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unknown regulatory class/channel"); + return -1; + } + + p2p->pending_listen_freq = freq; + p2p->pending_listen_sec = timeout / 1000; + p2p->pending_listen_usec = (timeout % 1000) * 1000; + + if (p2p->p2p_scan_running) { + 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"); + return 0; + } + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: p2p_scan running - delay start of listen state"); + p2p->start_after_scan = P2P_AFTER_SCAN_LISTEN; + return 0; + } + + ies = p2p_build_probe_resp_ies(p2p); + if (ies == NULL) + return -1; + + if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, timeout, ies) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to start listen mode"); + p2p->pending_listen_freq = 0; + wpabuf_free(ies); + return -1; + } + wpabuf_free(ies); + + p2p_set_state(p2p, P2P_LISTEN_ONLY); + + return 0; +} + + +static void p2p_device_clear_reported(struct p2p_data *p2p) +{ + struct p2p_device *dev; + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) + dev->flags &= ~P2P_DEV_REPORTED; +} + + +/** + * p2p_get_device - Fetch a peer entry + * @p2p: P2P module context from p2p_init() + * @addr: P2P Device Address of the peer + * Returns: Pointer to the device entry or %NULL if not found + */ +struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr) +{ + struct p2p_device *dev; + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { + if (os_memcmp(dev->info.p2p_device_addr, addr, ETH_ALEN) == 0) + return dev; + } + return NULL; +} + + +/** + * p2p_get_device_interface - Fetch a peer entry based on P2P Interface Address + * @p2p: P2P module context from p2p_init() + * @addr: P2P Interface Address of the peer + * Returns: Pointer to the device entry or %NULL if not found + */ +struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p, + const u8 *addr) +{ + struct p2p_device *dev; + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { + if (os_memcmp(dev->interface_addr, addr, ETH_ALEN) == 0) + return dev; + } + return NULL; +} + + +/** + * p2p_create_device - Create a peer entry + * @p2p: P2P module context from p2p_init() + * @addr: P2P Device Address of the peer + * Returns: Pointer to the device entry or %NULL on failure + * + * If there is already an entry for the peer, it will be returned instead of + * creating a new one. + */ +static struct p2p_device * p2p_create_device(struct p2p_data *p2p, + const u8 *addr) +{ + struct p2p_device *dev, *oldest = NULL; + size_t count = 0; + + dev = p2p_get_device(p2p, addr); + if (dev) + return dev; + + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { + count++; + if (oldest == NULL || + os_time_before(&dev->last_seen, &oldest->last_seen)) + oldest = dev; + } + if (count + 1 > p2p->cfg->max_peers && oldest) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Remove oldest peer entry to make room for a new " + "peer"); + dl_list_del(&oldest->list); + p2p_device_free(p2p, oldest); + } + + dev = os_zalloc(sizeof(*dev)); + if (dev == NULL) + return NULL; + dl_list_add(&p2p->devices, &dev->list); + os_memcpy(dev->info.p2p_device_addr, addr, ETH_ALEN); + + return dev; +} + + +static void p2p_copy_client_info(struct p2p_device *dev, + struct p2p_client_info *cli) +{ + os_memcpy(dev->info.device_name, cli->dev_name, cli->dev_name_len); + dev->info.device_name[cli->dev_name_len] = '\0'; + dev->info.dev_capab = cli->dev_capab; + dev->info.config_methods = cli->config_methods; + os_memcpy(dev->info.pri_dev_type, cli->pri_dev_type, 8); + dev->info.wps_sec_dev_type_list_len = 8 * cli->num_sec_dev_types; + os_memcpy(dev->info.wps_sec_dev_type_list, cli->sec_dev_types, + dev->info.wps_sec_dev_type_list_len); +} + + +static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr, + const u8 *go_interface_addr, int freq, + const u8 *gi, size_t gi_len) +{ + struct p2p_group_info info; + size_t c; + struct p2p_device *dev; + + if (gi == NULL) + return 0; + + if (p2p_group_info_parse(gi, gi_len, &info) < 0) + return -1; + + /* + * Clear old data for this group; if the devices are still in the + * group, the information will be restored in the loop following this. + */ + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { + if (os_memcmp(dev->member_in_go_iface, go_interface_addr, + ETH_ALEN) == 0) { + os_memset(dev->member_in_go_iface, 0, ETH_ALEN); + os_memset(dev->member_in_go_dev, 0, ETH_ALEN); + } + } + + for (c = 0; c < info.num_clients; c++) { + struct p2p_client_info *cli = &info.client[c]; + if (os_memcmp(cli->p2p_device_addr, p2p->cfg->dev_addr, + ETH_ALEN) == 0) + continue; /* ignore our own entry */ + dev = p2p_get_device(p2p, cli->p2p_device_addr); + if (dev) { + if (dev->flags & (P2P_DEV_GROUP_CLIENT_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; + } + } else { + dev = p2p_create_device(p2p, cli->p2p_device_addr); + if (dev == NULL) + continue; + dev->flags |= P2P_DEV_GROUP_CLIENT_ONLY; + p2p_copy_client_info(dev, cli); + dev->oper_freq = freq; + p2p->cfg->dev_found(p2p->cfg->cb_ctx, + dev->info.p2p_device_addr, + &dev->info, 1); + dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE; + } + + os_memcpy(dev->interface_addr, cli->p2p_interface_addr, + ETH_ALEN); + os_get_time(&dev->last_seen); + os_memcpy(dev->member_in_go_dev, go_dev_addr, ETH_ALEN); + os_memcpy(dev->member_in_go_iface, go_interface_addr, + ETH_ALEN); + } + + return 0; +} + + +static void p2p_copy_wps_info(struct p2p_device *dev, int probe_req, + const struct p2p_message *msg) +{ + os_memcpy(dev->info.device_name, msg->device_name, + sizeof(dev->info.device_name)); + + if (msg->manufacturer && + msg->manufacturer_len < sizeof(dev->info.manufacturer)) { + os_memset(dev->info.manufacturer, 0, + sizeof(dev->info.manufacturer)); + os_memcpy(dev->info.manufacturer, msg->manufacturer, + msg->manufacturer_len); + } + + if (msg->model_name && + msg->model_name_len < sizeof(dev->info.model_name)) { + os_memset(dev->info.model_name, 0, + sizeof(dev->info.model_name)); + os_memcpy(dev->info.model_name, msg->model_name, + msg->model_name_len); + } + + if (msg->model_number && + msg->model_number_len < sizeof(dev->info.model_number)) { + os_memset(dev->info.model_number, 0, + sizeof(dev->info.model_number)); + os_memcpy(dev->info.model_number, msg->model_number, + msg->model_number_len); + } + + if (msg->serial_number && + msg->serial_number_len < sizeof(dev->info.serial_number)) { + os_memset(dev->info.serial_number, 0, + sizeof(dev->info.serial_number)); + os_memcpy(dev->info.serial_number, msg->serial_number, + msg->serial_number_len); + } + + if (msg->pri_dev_type) + os_memcpy(dev->info.pri_dev_type, msg->pri_dev_type, + sizeof(dev->info.pri_dev_type)); + else if (msg->wps_pri_dev_type) + os_memcpy(dev->info.pri_dev_type, msg->wps_pri_dev_type, + sizeof(dev->info.pri_dev_type)); + + if (msg->wps_sec_dev_type_list) { + os_memcpy(dev->info.wps_sec_dev_type_list, + msg->wps_sec_dev_type_list, + msg->wps_sec_dev_type_list_len); + dev->info.wps_sec_dev_type_list_len = + msg->wps_sec_dev_type_list_len; + } + + if (msg->capability) { + /* + * 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]; + } + + if (msg->ext_listen_timing) { + dev->ext_listen_period = WPA_GET_LE16(msg->ext_listen_timing); + dev->ext_listen_interval = + WPA_GET_LE16(msg->ext_listen_timing + 2); + } + + if (!probe_req) { + dev->info.config_methods = msg->config_methods ? + msg->config_methods : msg->wps_config_methods; + } +} + + +/** + * p2p_add_device - Add peer entries based on scan results 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) + * @level: Signal level (signal strength of the received frame from the peer) + * @freq: Frequency on which the Beacon or Probe Response frame was received + * @age_ms: Age of the information in milliseconds + * @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 + * to the peer table. This function can also be used with some other frames + * like Provision Discovery Request that contains P2P Capability and P2P Device + * Info attributes. + */ +int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, + unsigned int age_ms, int level, const u8 *ies, + size_t ies_len, int scan_res) +{ + struct p2p_device *dev; + struct p2p_message msg; + const u8 *p2p_dev_addr; + int i; + struct os_time time_now, time_tmp_age, entry_ts; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_ies(ies, ies_len, &msg)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to parse P2P IE for a device entry"); + p2p_parse_free(&msg); + return -1; + } + + if (msg.p2p_device_addr) + p2p_dev_addr = msg.p2p_device_addr; + else if (msg.device_id) + p2p_dev_addr = msg.device_id; + else { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Ignore scan data without P2P Device Info or " + "P2P Device Id"); + p2p_parse_free(&msg); + return -1; + } + + if (!is_zero_ether_addr(p2p->peer_filter) && + os_memcmp(p2p_dev_addr, p2p->peer_filter, ETH_ALEN) != 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Do not add peer " + "filter for " MACSTR " due to peer filter", + MAC2STR(p2p_dev_addr)); + p2p_parse_free(&msg); + return 0; + } + + dev = p2p_create_device(p2p, p2p_dev_addr); + if (dev == NULL) { + p2p_parse_free(&msg); + return -1; + } + + os_get_time(&time_now); + time_tmp_age.sec = age_ms / 1000; + time_tmp_age.usec = (age_ms % 1000) * 1000; + os_time_sub(&time_now, &time_tmp_age, &entry_ts); + + /* + * Update the device entry only if the new peer + * entry is newer than the one previously stored. + */ + if (dev->last_seen.usec > 0 && + os_time_before(&entry_ts, &dev->last_seen)) { + p2p_parse_free(&msg); + return -1; + } + + os_memcpy(&dev->last_seen, &entry_ts, sizeof(struct os_time)); + + dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY); + + if (os_memcmp(addr, p2p_dev_addr, ETH_ALEN) != 0) + os_memcpy(dev->interface_addr, addr, ETH_ALEN); + if (msg.ssid && + (msg.ssid[1] != P2P_WILDCARD_SSID_LEN || + os_memcmp(msg.ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) + != 0)) { + os_memcpy(dev->oper_ssid, msg.ssid + 2, msg.ssid[1]); + dev->oper_ssid_len = msg.ssid[1]; + } + + if (freq >= 2412 && freq <= 2484 && msg.ds_params && + *msg.ds_params >= 1 && *msg.ds_params <= 14) { + int ds_freq; + if (*msg.ds_params == 14) + ds_freq = 2484; + else + ds_freq = 2407 + *msg.ds_params * 5; + if (freq != ds_freq) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Update Listen frequency based on DS " + "Parameter Set IE: %d -> %d MHz", + freq, ds_freq); + freq = ds_freq; + } + } + + if (dev->listen_freq && dev->listen_freq != freq && 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); + } + 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); + + for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { + wpabuf_free(dev->info.wps_vendor_ext[i]); + dev->info.wps_vendor_ext[i] = NULL; + } + + for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { + if (msg.wps_vendor_ext[i] == NULL) + break; + dev->info.wps_vendor_ext[i] = wpabuf_alloc_copy( + msg.wps_vendor_ext[i], msg.wps_vendor_ext_len[i]); + if (dev->info.wps_vendor_ext[i] == NULL) + break; + } + + if (msg.wfd_subelems) { + wpabuf_free(dev->info.wfd_subelems); + dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); + } + + if (scan_res) { + p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq, + msg.group_info, msg.group_info_len); + } + + p2p_parse_free(&msg); + + if (p2p_pending_sd_req(p2p, dev)) + dev->flags |= P2P_DEV_SD_SCHEDULE; + + if (dev->flags & P2P_DEV_REPORTED) + return 0; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Peer found with Listen frequency %d MHz", freq); + if (dev->flags & P2P_DEV_USER_REJECTED) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Do not report rejected device"); + return 0; + } + + p2p->cfg->dev_found(p2p->cfg->cb_ctx, addr, &dev->info, + !(dev->flags & P2P_DEV_REPORTED_ONCE)); + dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE; + + return 0; +} + + +static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev) +{ + int i; + + if (p2p->go_neg_peer == dev) { + /* + * If GO Negotiation is in progress, report that it has failed. + */ + p2p_go_neg_failed(p2p, dev, -1); + p2p->go_neg_peer = NULL; + } + if (p2p->invite_peer == dev) + p2p->invite_peer = NULL; + if (p2p->sd_peer == dev) + p2p->sd_peer = NULL; + if (p2p->pending_client_disc_go == dev) + p2p->pending_client_disc_go = NULL; + + /* dev_lost() device, but only if it was previously dev_found() */ + if (dev->flags & P2P_DEV_REPORTED_ONCE) + p2p->cfg->dev_lost(p2p->cfg->cb_ctx, + dev->info.p2p_device_addr); + + for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { + wpabuf_free(dev->info.wps_vendor_ext[i]); + dev->info.wps_vendor_ext[i] = NULL; + } + + wpabuf_free(dev->info.wfd_subelems); + + os_free(dev); +} + + +static int p2p_get_next_prog_freq(struct p2p_data *p2p) +{ + struct p2p_channels *c; + struct p2p_reg_class *cla; + size_t cl, ch; + int found = 0; + u8 reg_class; + u8 channel; + int freq; + + c = &p2p->cfg->channels; + for (cl = 0; cl < c->reg_classes; cl++) { + cla = &c->reg_class[cl]; + if (cla->reg_class != p2p->last_prog_scan_class) + continue; + for (ch = 0; ch < cla->channels; ch++) { + if (cla->channel[ch] == p2p->last_prog_scan_chan) { + found = 1; + break; + } + } + if (found) + break; + } + + if (!found) { + /* Start from beginning */ + reg_class = c->reg_class[0].reg_class; + channel = c->reg_class[0].channel[0]; + } else { + /* Pick the next channel */ + ch++; + if (ch == cla->channels) { + cl++; + if (cl == c->reg_classes) + cl = 0; + ch = 0; + } + reg_class = c->reg_class[cl].reg_class; + channel = c->reg_class[cl].channel[ch]; + } + + freq = p2p_channel_to_freq(p2p->cfg->country, reg_class, channel); + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Next progressive search " + "channel: reg_class %u channel %u -> %d MHz", + reg_class, channel, freq); + p2p->last_prog_scan_class = reg_class; + p2p->last_prog_scan_chan = channel; + + if (freq == 2412 || freq == 2437 || freq == 2462) + return 0; /* No need to add social channels */ + return freq; +} + + +static void p2p_search(struct p2p_data *p2p) +{ + int freq = 0; + enum p2p_scan_type type; + u16 pw_id = DEV_PW_DEFAULT; + int res; + + if (p2p->drv_in_listen) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver is still " + "in Listen state - wait for it to end before " + "continuing"); + return; + } + p2p->cfg->stop_listen(p2p->cfg->cb_ctx); + + if (p2p->find_type == P2P_FIND_PROGRESSIVE && + (freq = p2p_get_next_prog_freq(p2p)) > 0) { + type = P2P_SCAN_SOCIAL_PLUS_ONE; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search " + "(+ freq %u)", freq); + } else { + type = P2P_SCAN_SOCIAL; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search"); + } + + res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, type, freq, + p2p->num_req_dev_types, p2p->req_dev_types, + p2p->find_dev_id, pw_id); + if (res < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Scan request failed"); + p2p_continue_find(p2p); + } else if (res == 1) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Could not start " + "p2p_scan at this point - will try again after " + "previous scan completes"); + p2p_set_state(p2p, P2P_CONTINUE_SEARCH_WHEN_READY); + } else { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Running p2p_scan"); + p2p->p2p_scan_running = 1; + eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); + eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout, + p2p, NULL); + } +} + + +static void p2p_find_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct p2p_data *p2p = eloop_ctx; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Find timeout -> stop"); + p2p_stop_find(p2p); +} + + +static int p2p_run_after_scan(struct p2p_data *p2p) +{ + struct p2p_device *dev; + enum p2p_after_scan op; + + if (p2p->after_scan_tx) { + /* TODO: schedule p2p_run_after_scan to be called from TX + * status callback(?) */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send pending " + "Action frame at p2p_scan completion"); + p2p->cfg->send_action(p2p->cfg->cb_ctx, + p2p->after_scan_tx->freq, + p2p->after_scan_tx->dst, + p2p->after_scan_tx->src, + p2p->after_scan_tx->bssid, + (u8 *) (p2p->after_scan_tx + 1), + p2p->after_scan_tx->len, + p2p->after_scan_tx->wait_time); + os_free(p2p->after_scan_tx); + p2p->after_scan_tx = NULL; + return 1; + } + + op = p2p->start_after_scan; + p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; + switch (op) { + case P2P_AFTER_SCAN_NOTHING: + break; + case P2P_AFTER_SCAN_LISTEN: + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Start previously " + "requested Listen state"); + p2p_listen(p2p, p2p->pending_listen_sec * 1000 + + p2p->pending_listen_usec / 1000); + return 1; + case P2P_AFTER_SCAN_CONNECT: + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Start previously " + "requested connect with " MACSTR, + MAC2STR(p2p->after_scan_peer)); + dev = p2p_get_device(p2p, p2p->after_scan_peer); + if (dev == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer not " + "known anymore"); + break; + } + p2p_connect_send(p2p, dev); + return 1; + } + + return 0; +} + + +static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct p2p_data *p2p = eloop_ctx; + int running; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan timeout " + "(running=%d)", p2p->p2p_scan_running); + running = p2p->p2p_scan_running; + /* Make sure we recover from missed scan results callback */ + p2p->p2p_scan_running = 0; + + if (running) + p2p_run_after_scan(p2p); +} + + +static void p2p_free_req_dev_types(struct p2p_data *p2p) +{ + p2p->num_req_dev_types = 0; + os_free(p2p->req_dev_types); + p2p->req_dev_types = NULL; +} + + +int p2p_find(struct p2p_data *p2p, unsigned int timeout, + enum p2p_discovery_type type, + unsigned int num_req_dev_types, const u8 *req_dev_types, + const u8 *dev_id, unsigned int search_delay) +{ + int res; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting find (type=%d)", + type); + if (p2p->p2p_scan_running) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan is " + "already running"); + } + + p2p_free_req_dev_types(p2p); + if (req_dev_types && num_req_dev_types) { + p2p->req_dev_types = os_malloc(num_req_dev_types * + WPS_DEV_TYPE_LEN); + if (p2p->req_dev_types == NULL) + return -1; + os_memcpy(p2p->req_dev_types, req_dev_types, + num_req_dev_types * WPS_DEV_TYPE_LEN); + p2p->num_req_dev_types = num_req_dev_types; + } + + if (dev_id) { + os_memcpy(p2p->find_dev_id_buf, dev_id, ETH_ALEN); + p2p->find_dev_id = p2p->find_dev_id_buf; + } else + p2p->find_dev_id = NULL; + + p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; + p2p_clear_timeout(p2p); + p2p->cfg->stop_listen(p2p->cfg->cb_ctx); + p2p->find_type = type; + p2p_device_clear_reported(p2p); + p2p_set_state(p2p, P2P_SEARCH); + p2p->search_delay = search_delay; + p2p->in_search_delay = 0; + eloop_cancel_timeout(p2p_find_timeout, p2p, NULL); + p2p->last_p2p_find_timeout = timeout; + if (timeout) + eloop_register_timeout(timeout, 0, p2p_find_timeout, + p2p, NULL); + switch (type) { + case P2P_FIND_START_WITH_FULL: + case P2P_FIND_PROGRESSIVE: + res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_FULL, 0, + p2p->num_req_dev_types, + p2p->req_dev_types, 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, + DEV_PW_DEFAULT); + break; + default: + return -1; + } + + if (res == 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Running p2p_scan"); + p2p->p2p_scan_running = 1; + eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); + eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout, + p2p, NULL); + } else if (res == 1) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Could not start " + "p2p_scan at this point - will try again after " + "previous scan completes"); + res = 0; + p2p_set_state(p2p, P2P_SEARCH_WHEN_READY); + eloop_cancel_timeout(p2p_find_timeout, p2p, NULL); + } else { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Failed to start " + "p2p_scan"); + p2p_set_state(p2p, P2P_IDLE); + eloop_cancel_timeout(p2p_find_timeout, p2p, NULL); + } + + return res; +} + + +int p2p_other_scan_completed(struct p2p_data *p2p) +{ + if (p2p->state == P2P_CONTINUE_SEARCH_WHEN_READY) { + p2p_set_state(p2p, P2P_SEARCH); + p2p_search(p2p); + return 1; + } + if (p2p->state != P2P_SEARCH_WHEN_READY) + return 0; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting pending P2P find " + "now that previous scan was completed"); + if (p2p_find(p2p, p2p->last_p2p_find_timeout, p2p->find_type, + p2p->num_req_dev_types, p2p->req_dev_types, + p2p->find_dev_id, p2p->search_delay) < 0) + return 0; + return 1; +} + + +void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Stopping find"); + eloop_cancel_timeout(p2p_find_timeout, p2p, NULL); + p2p_clear_timeout(p2p); + if (p2p->state == P2P_SEARCH) + wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, P2P_EVENT_FIND_STOPPED); + p2p_set_state(p2p, P2P_IDLE); + p2p_free_req_dev_types(p2p); + p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; + p2p->go_neg_peer = NULL; + p2p->sd_peer = NULL; + p2p->invite_peer = NULL; + 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() + * when the operation gets canceled, so clear the internal + * variable that is tracking driver state. + */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Clear " + "drv_in_listen (%d)", p2p->drv_in_listen); + p2p->drv_in_listen = 0; + } + p2p->cfg->stop_listen(p2p->cfg->cb_ctx); +} + + +void p2p_stop_find(struct p2p_data *p2p) +{ + p2p_stop_find_for_freq(p2p, 0); +} + + +static int p2p_prepare_channel_pref(struct p2p_data *p2p, + unsigned int force_freq, + unsigned int pref_freq) +{ + u8 op_class, op_channel; + unsigned int freq = force_freq ? force_freq : pref_freq; + + if (p2p_freq_to_channel(p2p->cfg->country, freq, + &op_class, &op_channel) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported frequency %u MHz", freq); + return -1; + } + + if (!p2p_channels_includes(&p2p->cfg->channels, op_class, op_channel)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Frequency %u MHz (oper_class %u channel %u) not " + "allowed for P2P", freq, op_class, op_channel); + return -1; + } + + p2p->op_reg_class = op_class; + p2p->op_channel = op_channel; + + if (force_freq) { + p2p->channels.reg_classes = 1; + p2p->channels.reg_class[0].channels = 1; + p2p->channels.reg_class[0].reg_class = p2p->op_reg_class; + p2p->channels.reg_class[0].channel[0] = p2p->op_channel; + } else { + os_memcpy(&p2p->channels, &p2p->cfg->channels, + sizeof(struct p2p_channels)); + } + + return 0; +} + + +static void p2p_prepare_channel_best(struct p2p_data *p2p) +{ + u8 op_class, op_channel; + + if (!p2p->cfg->cfg_op_channel && p2p->best_freq_overall > 0 && + p2p_supported_freq(p2p, p2p->best_freq_overall) && + p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_overall, + &op_class, &op_channel) == 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Select best " + "overall channel as operating channel preference"); + p2p->op_reg_class = op_class; + p2p->op_channel = op_channel; + } else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_5 > 0 && + p2p_supported_freq(p2p, p2p->best_freq_5) && + p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_5, + &op_class, &op_channel) == 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Select best 5 GHz " + "channel as operating channel preference"); + p2p->op_reg_class = op_class; + p2p->op_channel = op_channel; + } else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_24 > 0 && + p2p_supported_freq(p2p, p2p->best_freq_24) && + p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_24, + &op_class, &op_channel) == 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Select best 2.4 " + "GHz channel as operating channel preference"); + p2p->op_reg_class = op_class; + p2p->op_channel = op_channel; + } else { + p2p->op_reg_class = p2p->cfg->op_reg_class; + p2p->op_channel = p2p->cfg->op_channel; + } + + os_memcpy(&p2p->channels, &p2p->cfg->channels, + sizeof(struct p2p_channels)); +} + + +/** + * p2p_prepare_channel - Select operating channel for GO Negotiation + * @p2p: P2P module context from p2p_init() + * @dev: Selected peer device + * @force_freq: Forced frequency in MHz or 0 if not forced + * @pref_freq: Preferred frequency in MHz or 0 if no preference + * Returns: 0 on success, -1 on failure (channel not supported for P2P) + * + * This function is used to do initial operating channel selection for GO + * Negotiation prior to having received peer information. The selected channel + * may be further optimized in p2p_reselect_channel() once the peer information + * is available. + */ +static int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev, + unsigned int force_freq, unsigned int pref_freq) +{ + if (force_freq || pref_freq) { + if (p2p_prepare_channel_pref(p2p, force_freq, pref_freq) < 0) + return -1; + } else { + p2p_prepare_channel_best(p2p); + } + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Own preference for operation channel: " + "Operating Class %u Channel %u%s", + p2p->op_reg_class, p2p->op_channel, + force_freq ? " (forced)" : ""); + + if (force_freq) + dev->flags |= P2P_DEV_FORCE_FREQ; + else + dev->flags &= ~P2P_DEV_FORCE_FREQ; + + return 0; +} + + +static void p2p_set_dev_persistent(struct p2p_device *dev, + int persistent_group) +{ + switch (persistent_group) { + case 0: + dev->flags &= ~(P2P_DEV_PREFER_PERSISTENT_GROUP | + P2P_DEV_PREFER_PERSISTENT_RECONN); + break; + case 1: + dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP; + dev->flags &= ~P2P_DEV_PREFER_PERSISTENT_RECONN; + break; + case 2: + dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP | + P2P_DEV_PREFER_PERSISTENT_RECONN; + break; + } +} + + +int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, + enum p2p_wps_method wps_method, + int go_intent, const u8 *own_interface_addr, + unsigned int force_freq, int persistent_group, + const u8 *force_ssid, size_t force_ssid_len, + int pd_before_go_neg, unsigned int pref_freq) +{ + 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 pd_before_go_neg=%d", + MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr), + wps_method, persistent_group, pd_before_go_neg); + + dev = p2p_get_device(p2p, peer_addr); + if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Cannot connect to unknown P2P Device " MACSTR, + MAC2STR(peer_addr)); + return -1; + } + + if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq) < 0) + return -1; + + if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) { + if (!(dev->info.dev_capab & + P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Cannot connect to P2P Device " MACSTR + " that is in a group and is not discoverable", + MAC2STR(peer_addr)); + return -1; + } + if (dev->oper_freq <= 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Cannot connect to P2P Device " MACSTR + " with incomplete information", + MAC2STR(peer_addr)); + return -1; + } + + /* + * First, try to connect directly. If the peer does not + * acknowledge frames, assume it is sleeping and use device + * discoverability via the GO at that point. + */ + } + + 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; + p2p_set_dev_persistent(dev, persistent_group); + p2p->go_intent = go_intent; + os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN); + + if (p2p->state != P2P_IDLE) + p2p_stop_find(p2p); + + if (p2p->after_scan_tx) { + /* + * We need to drop the pending frame to avoid issues with the + * new GO Negotiation, e.g., when the pending frame was from a + * previous attempt at starting a GO Negotiation. + */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Dropped " + "previous pending Action frame TX that was waiting " + "for p2p_scan completion"); + os_free(p2p->after_scan_tx); + p2p->after_scan_tx = NULL; + } + + dev->wps_method = wps_method; + dev->status = P2P_SC_SUCCESS; + + if (p2p->p2p_scan_running) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: p2p_scan running - delay connect send"); + p2p->start_after_scan = P2P_AFTER_SCAN_CONNECT; + os_memcpy(p2p->after_scan_peer, peer_addr, ETH_ALEN); + return 0; + } + p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; + + return p2p_connect_send(p2p, dev); +} + + +int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr, + enum p2p_wps_method wps_method, + int go_intent, const u8 *own_interface_addr, + unsigned int force_freq, int persistent_group, + const u8 *force_ssid, size_t force_ssid_len, + unsigned int pref_freq) +{ + struct p2p_device *dev; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Request to authorize group negotiation - peer=" MACSTR + " GO Intent=%d Intended Interface Address=" MACSTR + " wps_method=%d persistent_group=%d", + MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr), + wps_method, persistent_group); + + dev = p2p_get_device(p2p, peer_addr); + if (dev == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Cannot authorize unknown P2P Device " MACSTR, + MAC2STR(peer_addr)); + return -1; + } + + if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq) < 0) + 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; + dev->go_state = UNKNOWN_GO; + p2p_set_dev_persistent(dev, persistent_group); + p2p->go_intent = go_intent; + os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN); + + dev->wps_method = wps_method; + dev->status = P2P_SC_SUCCESS; + + return 0; +} + + +void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr, + struct p2p_device *dev, struct p2p_message *msg) +{ + os_get_time(&dev->last_seen); + + p2p_copy_wps_info(dev, 0, msg); + + if (msg->listen_channel) { + int freq; + freq = p2p_channel_to_freq((char *) msg->listen_channel, + msg->listen_channel[3], + msg->listen_channel[4]); + if (freq < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unknown peer Listen channel: " + "country=%c%c(0x%02x) reg_class=%u channel=%u", + msg->listen_channel[0], + msg->listen_channel[1], + msg->listen_channel[2], + msg->listen_channel[3], + msg->listen_channel[4]); + } else { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Update " + "peer " MACSTR " Listen channel: %u -> %u MHz", + MAC2STR(dev->info.p2p_device_addr), + dev->listen_freq, freq); + dev->listen_freq = freq; + } + } + + if (msg->wfd_subelems) { + wpabuf_free(dev->info.wfd_subelems); + dev->info.wfd_subelems = wpabuf_dup(msg->wfd_subelems); + } + + if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) { + dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Completed device entry based on data from " + "GO Negotiation Request"); + } else { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Created device entry based on GO Neg Req: " + MACSTR " dev_capab=0x%x group_capab=0x%x name='%s' " + "listen_freq=%d", + MAC2STR(dev->info.p2p_device_addr), + dev->info.dev_capab, dev->info.group_capab, + dev->info.device_name, dev->listen_freq); + } + + dev->flags &= ~P2P_DEV_GROUP_CLIENT_ONLY; + + if (dev->flags & P2P_DEV_USER_REJECTED) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Do not report rejected device"); + return; + } + + p2p->cfg->dev_found(p2p->cfg->cb_ctx, addr, &dev->info, + !(dev->flags & P2P_DEV_REPORTED_ONCE)); + dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE; +} + + +void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len) +{ + os_memcpy(ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN); + p2p_random((char *) &ssid[P2P_WILDCARD_SSID_LEN], 2); + os_memcpy(&ssid[P2P_WILDCARD_SSID_LEN + 2], + p2p->cfg->ssid_postfix, p2p->cfg->ssid_postfix_len); + *ssid_len = P2P_WILDCARD_SSID_LEN + 2 + p2p->cfg->ssid_postfix_len; +} + + +int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params) +{ + p2p_build_ssid(p2p, params->ssid, ¶ms->ssid_len); + p2p_random(params->passphrase, 8); + return 0; +} + + +void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer) +{ + struct p2p_go_neg_results res; + int go = peer->go_state == LOCAL_GO; + struct p2p_channels intersection; + int freqs; + size_t i, j; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: GO Negotiation with " MACSTR " completed (%s will be " + "GO)", MAC2STR(peer->info.p2p_device_addr), + go ? "local end" : "peer"); + + os_memset(&res, 0, sizeof(res)); + res.role_go = go; + os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, ETH_ALEN); + os_memcpy(res.peer_interface_addr, peer->intended_addr, ETH_ALEN); + res.wps_method = peer->wps_method; + if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) { + if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN) + res.persistent_group = 2; + else + res.persistent_group = 1; + } + + if (go) { + /* Setup AP mode for WPS provisioning */ + res.freq = p2p_channel_to_freq(p2p->cfg->country, + p2p->op_reg_class, + p2p->op_channel); + os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len); + res.ssid_len = p2p->ssid_len; + p2p_random(res.passphrase, 8); + } else { + res.freq = peer->oper_freq; + if (p2p->ssid_len) { + os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len); + res.ssid_len = p2p->ssid_len; + } + } + + p2p_channels_intersect(&p2p->channels, &peer->channels, + &intersection); + freqs = 0; + for (i = 0; i < intersection.reg_classes; i++) { + struct p2p_reg_class *c = &intersection.reg_class[i]; + if (freqs + 1 == P2P_MAX_CHANNELS) + break; + for (j = 0; j < c->channels; j++) { + int freq; + if (freqs + 1 == P2P_MAX_CHANNELS) + break; + freq = p2p_channel_to_freq(peer->country, c->reg_class, + c->channel[j]); + if (freq < 0) + continue; + res.freq_list[freqs++] = freq; + } + } + + res.peer_config_timeout = go ? peer->client_timeout : peer->go_timeout; + + p2p_clear_timeout(p2p); + p2p->ssid_set = 0; + peer->go_neg_req_sent = 0; + peer->wps_method = WPS_NOT_READY; + + p2p_set_state(p2p, P2P_PROVISIONING); + p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res); +} + + +static void p2p_rx_p2p_action(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: RX P2P Public Action from " MACSTR, MAC2STR(sa)); + wpa_hexdump(MSG_MSGDUMP, "P2P: P2P Public Action contents", data, len); + + if (len < 1) + return; + + switch (data[0]) { + case P2P_GO_NEG_REQ: + p2p_process_go_neg_req(p2p, sa, data + 1, len - 1, rx_freq); + break; + case P2P_GO_NEG_RESP: + p2p_process_go_neg_resp(p2p, sa, data + 1, len - 1, rx_freq); + break; + case P2P_GO_NEG_CONF: + p2p_process_go_neg_conf(p2p, sa, data + 1, len - 1); + break; + case P2P_INVITATION_REQ: + p2p_process_invitation_req(p2p, sa, data + 1, len - 1, + rx_freq); + break; + case P2P_INVITATION_RESP: + p2p_process_invitation_resp(p2p, sa, data + 1, len - 1); + break; + case P2P_PROV_DISC_REQ: + p2p_process_prov_disc_req(p2p, sa, data + 1, len - 1, rx_freq); + break; + case P2P_PROV_DISC_RESP: + p2p_process_prov_disc_resp(p2p, sa, data + 1, len - 1); + break; + case P2P_DEV_DISC_REQ: + p2p_process_dev_disc_req(p2p, sa, data + 1, len - 1, rx_freq); + break; + case P2P_DEV_DISC_RESP: + p2p_process_dev_disc_resp(p2p, sa, data + 1, len - 1); + break; + default: + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported P2P Public Action frame type %d", + data[0]); + break; + } +} + + +static void p2p_rx_action_public(struct p2p_data *p2p, const u8 *da, + const u8 *sa, const u8 *bssid, const u8 *data, + size_t len, int freq) +{ + if (len < 1) + return; + + switch (data[0]) { + case WLAN_PA_VENDOR_SPECIFIC: + data++; + len--; + if (len < 3) + return; + if (WPA_GET_BE24(data) != OUI_WFA) + return; + + data += 3; + len -= 3; + if (len < 1) + return; + + if (*data != P2P_OUI_TYPE) + return; + + p2p_rx_p2p_action(p2p, sa, data + 1, len - 1, freq); + break; + case WLAN_PA_GAS_INITIAL_REQ: + p2p_rx_gas_initial_req(p2p, sa, data + 1, len - 1, freq); + break; + case WLAN_PA_GAS_INITIAL_RESP: + p2p_rx_gas_initial_resp(p2p, sa, data + 1, len - 1, freq); + break; + case WLAN_PA_GAS_COMEBACK_REQ: + p2p_rx_gas_comeback_req(p2p, sa, data + 1, len - 1, freq); + break; + case WLAN_PA_GAS_COMEBACK_RESP: + p2p_rx_gas_comeback_resp(p2p, sa, data + 1, len - 1, freq); + break; + } +} + + +void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa, + const u8 *bssid, u8 category, + const u8 *data, size_t len, int freq) +{ + if (category == WLAN_ACTION_PUBLIC) { + p2p_rx_action_public(p2p, da, sa, bssid, data, len, freq); + return; + } + + if (category != WLAN_ACTION_VENDOR_SPECIFIC) + return; + + if (len < 4) + return; + + if (WPA_GET_BE24(data) != OUI_WFA) + return; + data += 3; + len -= 3; + + if (*data != P2P_OUI_TYPE) + return; + data++; + len--; + + /* P2P action frame */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: RX P2P Action from " MACSTR, MAC2STR(sa)); + wpa_hexdump(MSG_MSGDUMP, "P2P: P2P Action contents", data, len); + + if (len < 1) + return; + switch (data[0]) { + case P2P_NOA: + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received P2P Action - Notice of Absence"); + /* TODO */ + break; + case P2P_PRESENCE_REQ: + p2p_process_presence_req(p2p, da, sa, data + 1, len - 1, freq); + break; + case P2P_PRESENCE_RESP: + p2p_process_presence_resp(p2p, da, sa, data + 1, len - 1); + break; + case P2P_GO_DISC_REQ: + p2p_process_go_disc_req(p2p, da, sa, data + 1, len - 1, freq); + break; + default: + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received P2P Action - unknown type %u", data[0]); + break; + } +} + + +static void p2p_go_neg_start(void *eloop_ctx, void *timeout_ctx) +{ + struct p2p_data *p2p = eloop_ctx; + if (p2p->go_neg_peer == NULL) + return; + p2p->cfg->stop_listen(p2p->cfg->cb_ctx); + p2p->go_neg_peer->status = P2P_SC_SUCCESS; + p2p_connect_send(p2p, p2p->go_neg_peer); +} + + +static void p2p_invite_start(void *eloop_ctx, void *timeout_ctx) +{ + struct p2p_data *p2p = eloop_ctx; + if (p2p->invite_peer == NULL) + return; + p2p->cfg->stop_listen(p2p->cfg->cb_ctx); + p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr); +} + + +static void p2p_add_dev_from_probe_req(struct p2p_data *p2p, const u8 *addr, + const u8 *ie, size_t ie_len) +{ + struct p2p_message msg; + struct p2p_device *dev; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_ies(ie, ie_len, &msg) < 0 || msg.p2p_attributes == NULL) + { + p2p_parse_free(&msg); + return; /* not a P2P probe */ + } + + if (msg.ssid == NULL || msg.ssid[1] != P2P_WILDCARD_SSID_LEN || + os_memcmp(msg.ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) + != 0) { + /* The Probe Request is not part of P2P Device Discovery. It is + * not known whether the source address of the frame is the P2P + * Device Address or P2P Interface Address. Do not add a new + * peer entry based on this frames. + */ + p2p_parse_free(&msg); + return; + } + + dev = p2p_get_device(p2p, addr); + if (dev) { + if (dev->country[0] == 0 && msg.listen_channel) + os_memcpy(dev->country, msg.listen_channel, 3); + os_get_time(&dev->last_seen); + p2p_parse_free(&msg); + return; /* already known */ + } + + dev = p2p_create_device(p2p, addr); + if (dev == NULL) { + p2p_parse_free(&msg); + return; + } + + os_get_time(&dev->last_seen); + dev->flags |= P2P_DEV_PROBE_REQ_ONLY; + + if (msg.listen_channel) { + os_memcpy(dev->country, msg.listen_channel, 3); + dev->listen_freq = p2p_channel_to_freq(dev->country, + msg.listen_channel[3], + msg.listen_channel[4]); + } + + p2p_copy_wps_info(dev, 1, &msg); + + if (msg.wfd_subelems) { + wpabuf_free(dev->info.wfd_subelems); + dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); + } + + p2p_parse_free(&msg); + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Created device entry based on Probe Req: " MACSTR + " dev_capab=0x%x group_capab=0x%x name='%s' listen_freq=%d", + MAC2STR(dev->info.p2p_device_addr), dev->info.dev_capab, + dev->info.group_capab, dev->info.device_name, + dev->listen_freq); +} + + +struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p, + const u8 *addr, + struct p2p_message *msg) +{ + struct p2p_device *dev; + + dev = p2p_get_device(p2p, addr); + if (dev) { + os_get_time(&dev->last_seen); + return dev; /* already known */ + } + + dev = p2p_create_device(p2p, addr); + if (dev == NULL) + return NULL; + + p2p_add_dev_info(p2p, addr, dev, msg); + + return dev; +} + + +static int dev_type_match(const u8 *dev_type, const u8 *req_dev_type) +{ + if (os_memcmp(dev_type, req_dev_type, WPS_DEV_TYPE_LEN) == 0) + return 1; + if (os_memcmp(dev_type, req_dev_type, 2) == 0 && + WPA_GET_BE32(&req_dev_type[2]) == 0 && + WPA_GET_BE16(&req_dev_type[6]) == 0) + return 1; /* Category match with wildcard OUI/sub-category */ + return 0; +} + + +int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[], + size_t num_req_dev_type) +{ + size_t i; + for (i = 0; i < num_req_dev_type; i++) { + if (dev_type_match(dev_type, req_dev_type[i])) + return 1; + } + return 0; +} + + +/** + * p2p_match_dev_type - Match local device type with requested type + * @p2p: P2P module context from p2p_init() + * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs) + * Returns: 1 on match, 0 on mismatch + * + * This function can be used to match the Requested Device Type attribute in + * WPS IE with the local device types for deciding whether to reply to a Probe + * Request frame. + */ +int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps) +{ + struct wps_parse_attr attr; + size_t i; + + if (wps_parse_msg(wps, &attr)) + return 1; /* assume no Requested Device Type attributes */ + + if (attr.num_req_dev_type == 0) + return 1; /* no Requested Device Type attributes -> match */ + + if (dev_type_list_match(p2p->cfg->pri_dev_type, attr.req_dev_type, + attr.num_req_dev_type)) + return 1; /* Own Primary Device Type matches */ + + for (i = 0; i < p2p->cfg->num_sec_dev_types; i++) + if (dev_type_list_match(p2p->cfg->sec_dev_type[i], + attr.req_dev_type, + attr.num_req_dev_type)) + return 1; /* Own Secondary Device Type matches */ + + /* No matching device type found */ + return 0; +} + + +struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p) +{ + struct wpabuf *buf; + u8 *len; + int pw_id = -1; + size_t extra = 0; + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_probe_resp) + extra = wpabuf_len(p2p->wfd_ie_probe_resp); +#endif /* CONFIG_WIFI_DISPLAY */ + + buf = wpabuf_alloc(1000 + extra); + if (buf == NULL) + return NULL; + + 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); + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_probe_resp) + wpabuf_put_buf(buf, p2p->wfd_ie_probe_resp); +#endif /* CONFIG_WIFI_DISPLAY */ + + /* P2P IE */ + len = p2p_buf_add_ie_hdr(buf); + 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); + p2p_buf_add_device_info(buf, p2p, NULL); + p2p_buf_update_ie_hdr(buf, len); + + return buf; +} + + +static int is_11b(u8 rate) +{ + return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16; +} + + +static int supp_rates_11b_only(struct ieee802_11_elems *elems) +{ + int num_11b = 0, num_others = 0; + int i; + + if (elems->supp_rates == NULL && elems->ext_supp_rates == NULL) + return 0; + + for (i = 0; elems->supp_rates && i < elems->supp_rates_len; i++) { + if (is_11b(elems->supp_rates[i])) + num_11b++; + else + num_others++; + } + + for (i = 0; elems->ext_supp_rates && i < elems->ext_supp_rates_len; + i++) { + if (is_11b(elems->ext_supp_rates[i])) + num_11b++; + else + num_others++; + } + + return num_11b > 0 && num_others == 0; +} + + +static enum p2p_probe_req_status +p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, + const u8 *bssid, const u8 *ie, size_t ie_len) +{ + struct ieee802_11_elems elems; + struct wpabuf *buf; + struct ieee80211_mgmt *resp; + struct p2p_message msg; + struct wpabuf *ies; + + if (!p2p->in_listen || !p2p->drv_in_listen) { + /* not in Listen state - ignore Probe Request */ + return P2P_PREQ_NOT_LISTEN; + } + + if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) == + ParseFailed) { + /* Ignore invalid Probe Request frames */ + return P2P_PREQ_MALFORMED; + } + + if (elems.p2p == NULL) { + /* not a P2P probe - ignore it */ + 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 P2P_PREQ_NOT_PROCESSED; + } + + if (bssid && !is_broadcast_ether_addr(bssid)) { + /* Not sent to the Wildcard BSSID */ + 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 P2P_PREQ_NOT_PROCESSED; + } + + if (supp_rates_11b_only(&elems)) { + /* Indicates support for 11b rates only */ + 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 P2P_PREQ_NOT_P2P; + } + + if (msg.device_id && + os_memcmp(msg.device_id, p2p->cfg->dev_addr, ETH_ALEN) != 0) { + /* Device ID did not match */ + p2p_parse_free(&msg); + return P2P_PREQ_NOT_PROCESSED; + } + + /* Check Requested Device Type match */ + if (msg.wps_attributes && + !p2p_match_dev_type(p2p, msg.wps_attributes)) { + /* No match with Requested Device Type */ + p2p_parse_free(&msg); + return P2P_PREQ_NOT_PROCESSED; + } + p2p_parse_free(&msg); + + 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"); + + /* + * We do not really have a specific BSS that this frame is advertising, + * so build a frame that has some information in valid format. This is + * really only used for discovery purposes, not to learn exact BSS + * parameters. + */ + ies = p2p_build_probe_resp_ies(p2p); + if (ies == NULL) + return P2P_PREQ_NOT_PROCESSED; + + buf = wpabuf_alloc(200 + wpabuf_len(ies)); + if (buf == NULL) { + wpabuf_free(ies); + return P2P_PREQ_NOT_PROCESSED; + } + + resp = NULL; + resp = wpabuf_put(buf, resp->u.probe_resp.variable - (u8 *) resp); + + resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) | + (WLAN_FC_STYPE_PROBE_RESP << 4)); + os_memcpy(resp->da, addr, ETH_ALEN); + os_memcpy(resp->sa, p2p->cfg->dev_addr, ETH_ALEN); + os_memcpy(resp->bssid, p2p->cfg->dev_addr, ETH_ALEN); + resp->u.probe_resp.beacon_int = host_to_le16(100); + /* hardware or low-level driver will setup seq_ctrl and timestamp */ + resp->u.probe_resp.capab_info = + host_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE | + WLAN_CAPABILITY_PRIVACY | + WLAN_CAPABILITY_SHORT_SLOT_TIME); + + wpabuf_put_u8(buf, WLAN_EID_SSID); + wpabuf_put_u8(buf, P2P_WILDCARD_SSID_LEN); + wpabuf_put_data(buf, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN); + + wpabuf_put_u8(buf, WLAN_EID_SUPP_RATES); + wpabuf_put_u8(buf, 8); + wpabuf_put_u8(buf, (60 / 5) | 0x80); + wpabuf_put_u8(buf, 90 / 5); + wpabuf_put_u8(buf, (120 / 5) | 0x80); + wpabuf_put_u8(buf, 180 / 5); + wpabuf_put_u8(buf, (240 / 5) | 0x80); + wpabuf_put_u8(buf, 360 / 5); + wpabuf_put_u8(buf, 480 / 5); + wpabuf_put_u8(buf, 540 / 5); + + wpabuf_put_u8(buf, WLAN_EID_DS_PARAMS); + wpabuf_put_u8(buf, 1); + wpabuf_put_u8(buf, p2p->cfg->channel); + + wpabuf_put_buf(buf, ies); + wpabuf_free(ies); + + p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf); + + wpabuf_free(buf); + + return P2P_PREQ_NOT_PROCESSED; +} + + +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); + + 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 && + os_memcmp(addr, p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN) + == 0) { + /* Received a Probe Request from GO Negotiation peer */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Found GO Negotiation peer - try to start GO " + "negotiation from timeout"); + eloop_register_timeout(0, 0, p2p_go_neg_start, p2p, NULL); + return P2P_PREQ_PROCESSED; + } + + if ((p2p->state == P2P_INVITE || p2p->state == P2P_INVITE_LISTEN) && + p2p->invite_peer && + os_memcmp(addr, p2p->invite_peer->info.p2p_device_addr, ETH_ALEN) + == 0) { + /* Received a Probe Request from Invite peer */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Found Invite peer - try to start Invite from " + "timeout"); + eloop_register_timeout(0, 0, p2p_invite_start, p2p, NULL); + return P2P_PREQ_PROCESSED; + } + + return res; +} + + +static int p2p_assoc_req_ie_wlan_ap(struct p2p_data *p2p, const u8 *bssid, + u8 *buf, size_t len, struct wpabuf *p2p_ie) +{ + struct wpabuf *tmp; + u8 *lpos; + size_t tmplen; + int res; + u8 group_capab; + + if (p2p_ie == NULL) + return 0; /* WLAN AP is not a P2P manager */ + + /* + * (Re)Association Request - P2P IE + * P2P Capability attribute (shall be present) + * P2P Interface attribute (present if concurrent device and + * P2P Management is enabled) + */ + tmp = wpabuf_alloc(200); + if (tmp == NULL) + return -1; + + lpos = p2p_buf_add_ie_hdr(tmp); + group_capab = 0; + if (p2p->num_groups > 0) { + group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER; + if ((p2p->dev_capab & P2P_DEV_CAPAB_CONCURRENT_OPER) && + (p2p->dev_capab & P2P_DEV_CAPAB_INFRA_MANAGED) && + p2p->cross_connect) + group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; + } + p2p_buf_add_capability(tmp, p2p->dev_capab, group_capab); + if ((p2p->dev_capab & P2P_DEV_CAPAB_CONCURRENT_OPER) && + (p2p->dev_capab & P2P_DEV_CAPAB_INFRA_MANAGED)) + p2p_buf_add_p2p_interface(tmp, p2p); + p2p_buf_update_ie_hdr(tmp, lpos); + + tmplen = wpabuf_len(tmp); + if (tmplen > len) + res = -1; + else { + os_memcpy(buf, wpabuf_head(tmp), tmplen); + res = tmplen; + } + wpabuf_free(tmp); + + return res; +} + + +int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf, + size_t len, int p2p_group, struct wpabuf *p2p_ie) +{ + struct wpabuf *tmp; + u8 *lpos; + struct p2p_device *peer; + size_t tmplen; + int res; + size_t extra = 0; + + if (!p2p_group) + return p2p_assoc_req_ie_wlan_ap(p2p, bssid, buf, len, p2p_ie); + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_assoc_req) + extra = wpabuf_len(p2p->wfd_ie_assoc_req); +#endif /* CONFIG_WIFI_DISPLAY */ + + /* + * (Re)Association Request - P2P IE + * P2P Capability attribute (shall be present) + * Extended Listen Timing (may be present) + * P2P Device Info attribute (shall be present) + */ + tmp = wpabuf_alloc(200 + extra); + if (tmp == NULL) + return -1; + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_assoc_req) + wpabuf_put_buf(tmp, p2p->wfd_ie_assoc_req); +#endif /* CONFIG_WIFI_DISPLAY */ + + peer = bssid ? p2p_get_device(p2p, bssid) : NULL; + + lpos = p2p_buf_add_ie_hdr(tmp); + p2p_buf_add_capability(tmp, p2p->dev_capab, 0); + if (p2p->ext_listen_interval) + p2p_buf_add_ext_listen_timing(tmp, p2p->ext_listen_period, + p2p->ext_listen_interval); + p2p_buf_add_device_info(tmp, p2p, peer); + p2p_buf_update_ie_hdr(tmp, lpos); + + tmplen = wpabuf_len(tmp); + if (tmplen > len) + res = -1; + else { + os_memcpy(buf, wpabuf_head(tmp), tmplen); + res = tmplen; + } + wpabuf_free(tmp); + + return res; +} + + +int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end) +{ + struct wpabuf *p2p_ie; + int ret; + + p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len, P2P_IE_VENDOR_TYPE); + if (p2p_ie == NULL) + return 0; + + ret = p2p_attr_text(p2p_ie, buf, end); + wpabuf_free(p2p_ie); + return ret; +} + + +int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr) +{ + struct p2p_message msg; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_p2p_ie(p2p_ie, &msg)) + 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; +} + + +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 ret; +} + + +static void p2p_clear_go_neg(struct p2p_data *p2p) +{ + p2p->go_neg_peer = NULL; + p2p_clear_timeout(p2p); + p2p_set_state(p2p, P2P_IDLE); +} + + +void p2p_wps_success_cb(struct p2p_data *p2p, const u8 *mac_addr) +{ + if (p2p->go_neg_peer == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No pending Group Formation - " + "ignore WPS registration success notification"); + return; /* No pending Group Formation */ + } + + if (os_memcmp(mac_addr, p2p->go_neg_peer->intended_addr, ETH_ALEN) != + 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Ignore WPS registration success notification " + "for " MACSTR " (GO Negotiation peer " MACSTR ")", + MAC2STR(mac_addr), + MAC2STR(p2p->go_neg_peer->intended_addr)); + return; /* Ignore unexpected peer address */ + } + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Group Formation completed successfully with " MACSTR, + MAC2STR(mac_addr)); + + p2p_clear_go_neg(p2p); +} + + +void p2p_group_formation_failed(struct p2p_data *p2p) +{ + if (p2p->go_neg_peer == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No pending Group Formation - " + "ignore group formation failure notification"); + return; /* No pending Group Formation */ + } + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Group Formation failed with " MACSTR, + MAC2STR(p2p->go_neg_peer->intended_addr)); + + p2p_clear_go_neg(p2p); +} + + +struct p2p_data * p2p_init(const struct p2p_config *cfg) +{ + struct p2p_data *p2p; + + if (cfg->max_peers < 1) + return NULL; + + p2p = os_zalloc(sizeof(*p2p) + sizeof(*cfg)); + if (p2p == NULL) + return NULL; + p2p->cfg = (struct p2p_config *) (p2p + 1); + os_memcpy(p2p->cfg, cfg, sizeof(*cfg)); + if (cfg->dev_name) + p2p->cfg->dev_name = os_strdup(cfg->dev_name); + if (cfg->manufacturer) + p2p->cfg->manufacturer = os_strdup(cfg->manufacturer); + if (cfg->model_name) + p2p->cfg->model_name = os_strdup(cfg->model_name); + if (cfg->model_number) + p2p->cfg->model_number = os_strdup(cfg->model_number); + if (cfg->serial_number) + p2p->cfg->serial_number = os_strdup(cfg->serial_number); + 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; + } + + p2p->min_disc_int = 1; + p2p->max_disc_int = 3; + p2p->max_disc_tu = -1; + + os_get_random(&p2p->next_tie_breaker, 1); + p2p->next_tie_breaker &= 0x01; + if (cfg->sd_request) + p2p->dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY; + p2p->dev_capab |= P2P_DEV_CAPAB_INVITATION_PROCEDURE; + if (cfg->concurrent_operations) + p2p->dev_capab |= P2P_DEV_CAPAB_CONCURRENT_OPER; + p2p->dev_capab |= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; + + dl_list_init(&p2p->devices); + + eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0, + p2p_expiration_timeout, p2p, NULL); + + p2p->go_timeout = 100; + p2p->client_timeout = 20; + + return p2p; +} + + +void p2p_deinit(struct p2p_data *p2p) +{ +#ifdef CONFIG_WIFI_DISPLAY + wpabuf_free(p2p->wfd_ie_beacon); + wpabuf_free(p2p->wfd_ie_probe_req); + wpabuf_free(p2p->wfd_ie_probe_resp); + wpabuf_free(p2p->wfd_ie_assoc_req); + wpabuf_free(p2p->wfd_ie_invitation); + wpabuf_free(p2p->wfd_ie_prov_disc_req); + wpabuf_free(p2p->wfd_ie_prov_disc_resp); + wpabuf_free(p2p->wfd_ie_go_neg); + wpabuf_free(p2p->wfd_dev_info); + wpabuf_free(p2p->wfd_assoc_bssid); + wpabuf_free(p2p->wfd_coupled_sink_info); +#endif /* CONFIG_WIFI_DISPLAY */ + + eloop_cancel_timeout(p2p_expiration_timeout, p2p, NULL); + eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL); + eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); + p2p_flush(p2p); + p2p_free_req_dev_types(p2p); + os_free(p2p->cfg->dev_name); + os_free(p2p->cfg->manufacturer); + os_free(p2p->cfg->model_name); + os_free(p2p->cfg->model_number); + os_free(p2p->cfg->serial_number); + os_free(p2p->cfg->pref_chan); + os_free(p2p->groups); + wpabuf_free(p2p->sd_resp); + os_free(p2p->after_scan_tx); + p2p_remove_wps_vendor_extensions(p2p); + os_free(p2p); +} + + +void p2p_flush(struct p2p_data *p2p) +{ + struct p2p_device *dev, *prev; + p2p_stop_find(p2p); + dl_list_for_each_safe(dev, prev, &p2p->devices, struct p2p_device, + list) { + dl_list_del(&dev->list); + p2p_device_free(p2p, dev); + } + p2p_free_sd_queries(p2p); + os_free(p2p->after_scan_tx); + p2p->after_scan_tx = NULL; +} + + +int p2p_unauthorize(struct p2p_data *p2p, const u8 *addr) +{ + struct p2p_device *dev; + + dev = p2p_get_device(p2p, addr); + if (dev == NULL) + return -1; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Unauthorizing " MACSTR, + MAC2STR(addr)); + + if (p2p->go_neg_peer == dev) + p2p->go_neg_peer = NULL; + + dev->wps_method = WPS_NOT_READY; + dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE; + dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM; + + /* Check if after_scan_tx is for this peer. If so free it */ + if (p2p->after_scan_tx && + os_memcmp(addr, p2p->after_scan_tx->dst, ETH_ALEN) == 0) { + os_free(p2p->after_scan_tx); + p2p->after_scan_tx = NULL; + } + + return 0; +} + + +int p2p_set_dev_name(struct p2p_data *p2p, const char *dev_name) +{ + os_free(p2p->cfg->dev_name); + if (dev_name) { + p2p->cfg->dev_name = os_strdup(dev_name); + if (p2p->cfg->dev_name == NULL) + return -1; + } else + p2p->cfg->dev_name = NULL; + return 0; +} + + +int p2p_set_manufacturer(struct p2p_data *p2p, const char *manufacturer) +{ + os_free(p2p->cfg->manufacturer); + p2p->cfg->manufacturer = NULL; + if (manufacturer) { + p2p->cfg->manufacturer = os_strdup(manufacturer); + if (p2p->cfg->manufacturer == NULL) + return -1; + } + + return 0; +} + + +int p2p_set_model_name(struct p2p_data *p2p, const char *model_name) +{ + os_free(p2p->cfg->model_name); + p2p->cfg->model_name = NULL; + if (model_name) { + p2p->cfg->model_name = os_strdup(model_name); + if (p2p->cfg->model_name == NULL) + return -1; + } + + return 0; +} + + +int p2p_set_model_number(struct p2p_data *p2p, const char *model_number) +{ + os_free(p2p->cfg->model_number); + p2p->cfg->model_number = NULL; + if (model_number) { + p2p->cfg->model_number = os_strdup(model_number); + if (p2p->cfg->model_number == NULL) + return -1; + } + + return 0; +} + + +int p2p_set_serial_number(struct p2p_data *p2p, const char *serial_number) +{ + os_free(p2p->cfg->serial_number); + p2p->cfg->serial_number = NULL; + if (serial_number) { + p2p->cfg->serial_number = os_strdup(serial_number); + if (p2p->cfg->serial_number == NULL) + return -1; + } + + return 0; +} + + +void p2p_set_config_methods(struct p2p_data *p2p, u16 config_methods) +{ + p2p->cfg->config_methods = config_methods; +} + + +void p2p_set_uuid(struct p2p_data *p2p, const u8 *uuid) +{ + os_memcpy(p2p->cfg->uuid, uuid, 16); +} + + +int p2p_set_pri_dev_type(struct p2p_data *p2p, const u8 *pri_dev_type) +{ + os_memcpy(p2p->cfg->pri_dev_type, pri_dev_type, 8); + return 0; +} + + +int p2p_set_sec_dev_types(struct p2p_data *p2p, const u8 dev_types[][8], + size_t num_dev_types) +{ + if (num_dev_types > P2P_SEC_DEVICE_TYPES) + num_dev_types = P2P_SEC_DEVICE_TYPES; + p2p->cfg->num_sec_dev_types = num_dev_types; + os_memcpy(p2p->cfg->sec_dev_type, dev_types, num_dev_types * 8); + return 0; +} + + +void p2p_remove_wps_vendor_extensions(struct p2p_data *p2p) +{ + int i; + + for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { + wpabuf_free(p2p->wps_vendor_ext[i]); + p2p->wps_vendor_ext[i] = NULL; + } +} + + +int p2p_add_wps_vendor_extension(struct p2p_data *p2p, + const struct wpabuf *vendor_ext) +{ + int i; + + if (vendor_ext == NULL) + return -1; + + for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { + if (p2p->wps_vendor_ext[i] == NULL) + break; + } + if (i >= P2P_MAX_WPS_VENDOR_EXT) + return -1; + + p2p->wps_vendor_ext[i] = wpabuf_dup(vendor_ext); + if (p2p->wps_vendor_ext[i] == NULL) + return -1; + + return 0; +} + + +int p2p_set_country(struct p2p_data *p2p, const char *country) +{ + os_memcpy(p2p->cfg->country, country, 3); + return 0; +} + + +void p2p_continue_find(struct p2p_data *p2p) +{ + struct p2p_device *dev; + p2p_set_state(p2p, P2P_SEARCH); + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { + if (dev->flags & P2P_DEV_SD_SCHEDULE) { + if (p2p_start_sd(p2p, dev) == 0) + return; + else + break; + } else if (dev->req_config_methods && + !(dev->flags & P2P_DEV_PD_FOR_JOIN)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send " + "pending Provision Discovery Request to " + MACSTR " (config methods 0x%x)", + MAC2STR(dev->info.p2p_device_addr), + dev->req_config_methods); + if (p2p_send_prov_disc_req(p2p, dev, 0, 0) == 0) + return; + } + } + + p2p_listen_in_find(p2p, 1); +} + + +static void p2p_sd_cb(struct p2p_data *p2p, int success) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Service Discovery Query TX callback: success=%d", + success); + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + + if (!success) { + if (p2p->sd_peer) { + p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE; + p2p->sd_peer = NULL; + } + p2p_continue_find(p2p); + return; + } + + if (p2p->sd_peer == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No SD peer entry known"); + p2p_continue_find(p2p); + return; + } + + /* Wait for response from the peer */ + p2p_set_state(p2p, P2P_SD_DURING_FIND); + p2p_set_timeout(p2p, 0, 200000); +} + + +/** + * p2p_retry_pd - Retry any pending provision disc requests in IDLE state + * @p2p: P2P module context from p2p_init() + */ +static void p2p_retry_pd(struct p2p_data *p2p) +{ + struct p2p_device *dev; + + if (p2p->state != P2P_IDLE) + return; + + /* + * Retry the prov disc req attempt only for the peer that the user had + * requested. + */ + + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { + if (os_memcmp(p2p->pending_pd_devaddr, + dev->info.p2p_device_addr, ETH_ALEN) != 0) + continue; + if (!dev->req_config_methods) + continue; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send " + "pending Provision Discovery Request to " + MACSTR " (config methods 0x%x)", + MAC2STR(dev->info.p2p_device_addr), + dev->req_config_methods); + p2p_send_prov_disc_req(p2p, dev, + dev->flags & P2P_DEV_PD_FOR_JOIN, 0); + return; + } +} + + +static void p2p_prov_disc_cb(struct p2p_data *p2p, int success) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Provision Discovery Request TX callback: success=%d", + success); + + /* + * Postpone resetting the pending action state till after we actually + * time out. This allows us to take some action like notifying any + * interested parties about no response to the request. + * + * When the timer (below) goes off we check in IDLE, SEARCH, or + * LISTEN_ONLY state, which are the only allowed states to issue a PD + * requests in, if this was still pending and then raise notification. + */ + + if (!success) { + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + + if (p2p->user_initiated_pd && + (p2p->state == P2P_SEARCH || p2p->state == P2P_LISTEN_ONLY)) + { + /* Retry request from timeout to avoid busy loops */ + p2p->pending_action_state = P2P_PENDING_PD; + p2p_set_timeout(p2p, 0, 50000); + } else if (p2p->state != P2P_IDLE) + p2p_continue_find(p2p); + else if (p2p->user_initiated_pd) { + p2p->pending_action_state = P2P_PENDING_PD; + p2p_set_timeout(p2p, 0, 300000); + } + return; + } + + /* + * This postponing, of resetting pending_action_state, needs to be + * done only for user initiated PD requests and not internal ones. + */ + if (p2p->user_initiated_pd) + p2p->pending_action_state = P2P_PENDING_PD; + else + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + + /* Wait for response from the peer */ + if (p2p->state == P2P_SEARCH) + p2p_set_state(p2p, P2P_PD_DURING_FIND); + p2p_set_timeout(p2p, 0, 200000); +} + + +int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq, + unsigned int age, int level, const u8 *ies, + size_t ies_len) +{ + p2p_add_device(p2p, bssid, freq, age, level, ies, ies_len, 1); + + return 0; +} + + +void p2p_scan_res_handled(struct p2p_data *p2p) +{ + if (!p2p->p2p_scan_running) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan was not " + "running, but scan results received"); + } + p2p->p2p_scan_running = 0; + eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); + + if (p2p_run_after_scan(p2p)) + return; + if (p2p->state == P2P_SEARCH) + p2p_continue_find(p2p); +} + + +void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id) +{ + u8 *len; + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_probe_req) + wpabuf_put_buf(ies, p2p->wfd_ie_probe_req); +#endif /* CONFIG_WIFI_DISPLAY */ + + len = p2p_buf_add_ie_hdr(ies); + 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) + p2p_buf_add_listen_channel(ies, p2p->cfg->country, + p2p->cfg->reg_class, + p2p->cfg->channel); + if (p2p->ext_listen_interval) + p2p_buf_add_ext_listen_timing(ies, p2p->ext_listen_period, + p2p->ext_listen_interval); + /* TODO: p2p_buf_add_operating_channel() if GO */ + p2p_buf_update_ie_hdr(ies, len); +} + + +size_t p2p_scan_ie_buf_len(struct p2p_data *p2p) +{ + size_t len = 100; + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p && p2p->wfd_ie_probe_req) + len += wpabuf_len(p2p->wfd_ie_probe_req); +#endif /* CONFIG_WIFI_DISPLAY */ + + return len; +} + + +int p2p_ie_text(struct wpabuf *p2p_ie, char *buf, char *end) +{ + return p2p_attr_text(p2p_ie, buf, end); +} + + +static void p2p_go_neg_req_cb(struct p2p_data *p2p, int success) +{ + struct p2p_device *dev = p2p->go_neg_peer; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: GO Negotiation Request TX callback: success=%d", + success); + + if (dev == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No pending GO Negotiation"); + return; + } + + if (success) { + if (dev->flags & P2P_DEV_USER_REJECTED) { + p2p_set_state(p2p, P2P_IDLE); + return; + } + } else if (dev->go_neg_req_sent) { + /* Cancel the increment from p2p_connect_send() on failure */ + dev->go_neg_req_sent--; + } + + if (!success && + (dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY) && + !is_zero_ether_addr(dev->member_in_go_dev)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Peer " MACSTR " did not acknowledge request - " + "try to use device discoverability through its GO", + MAC2STR(dev->info.p2p_device_addr)); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p_send_dev_disc_req(p2p, dev); + return; + } + + /* + * Use P2P find, if needed, to find the other device from its listen + * channel. + */ + p2p_set_state(p2p, P2P_CONNECT); + p2p_set_timeout(p2p, 0, success ? 200000 : 100000); +} + + +static void p2p_go_neg_resp_cb(struct p2p_data *p2p, int success) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: GO Negotiation Response TX callback: success=%d", + success); + if (!p2p->go_neg_peer && p2p->state == P2P_PROVISIONING) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Ignore TX callback event - GO Negotiation is " + "not running anymore"); + return; + } + p2p_set_state(p2p, P2P_CONNECT); + p2p_set_timeout(p2p, 0, 250000); +} + + +static void p2p_go_neg_resp_failure_cb(struct p2p_data *p2p, int success) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: GO Negotiation Response (failure) TX callback: " + "success=%d", success); + if (p2p->go_neg_peer && p2p->go_neg_peer->status != P2P_SC_SUCCESS) { + p2p_go_neg_failed(p2p, p2p->go_neg_peer, + p2p->go_neg_peer->status); + } +} + + +static void p2p_go_neg_conf_cb(struct p2p_data *p2p, + enum p2p_send_action_result result) +{ + struct p2p_device *dev; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: GO Negotiation Confirm TX callback: result=%d", + result); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + if (result == P2P_SEND_ACTION_FAILED) { + p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); + return; + } + if (result == P2P_SEND_ACTION_NO_ACK) { + /* + * It looks like the TX status for GO Negotiation Confirm is + * often showing failure even when the peer has actually + * received the frame. Since the peer may change channels + * immediately after having received the frame, we may not see + * an Ack for retries, so just dropping a single frame may + * trigger this. To allow the group formation to succeed if the + * peer did indeed receive the frame, continue regardless of + * the TX status. + */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Assume GO Negotiation Confirm TX was actually " + "received by the peer even though Ack was not " + "reported"); + } + + dev = p2p->go_neg_peer; + if (dev == NULL) + return; + + p2p_go_complete(p2p, dev); +} + + +void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, + enum p2p_send_action_result result) +{ + enum p2p_pending_action_state state; + int success; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Action frame TX callback (state=%d freq=%u dst=" MACSTR + " src=" MACSTR " bssid=" MACSTR " result=%d", + p2p->pending_action_state, freq, MAC2STR(dst), MAC2STR(src), + MAC2STR(bssid), result); + success = result == P2P_SEND_ACTION_SUCCESS; + state = p2p->pending_action_state; + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + switch (state) { + case P2P_NO_PENDING_ACTION: + break; + case P2P_PENDING_GO_NEG_REQUEST: + p2p_go_neg_req_cb(p2p, success); + break; + case P2P_PENDING_GO_NEG_RESPONSE: + p2p_go_neg_resp_cb(p2p, success); + break; + case P2P_PENDING_GO_NEG_RESPONSE_FAILURE: + p2p_go_neg_resp_failure_cb(p2p, success); + break; + case P2P_PENDING_GO_NEG_CONFIRM: + p2p_go_neg_conf_cb(p2p, result); + break; + case P2P_PENDING_SD: + p2p_sd_cb(p2p, success); + break; + case P2P_PENDING_PD: + p2p_prov_disc_cb(p2p, success); + break; + case P2P_PENDING_INVITATION_REQUEST: + p2p_invitation_req_cb(p2p, success); + break; + case P2P_PENDING_INVITATION_RESPONSE: + p2p_invitation_resp_cb(p2p, success); + break; + case P2P_PENDING_DEV_DISC_REQUEST: + p2p_dev_disc_req_cb(p2p, success); + break; + case P2P_PENDING_DEV_DISC_RESPONSE: + p2p_dev_disc_resp_cb(p2p, success); + break; + case P2P_PENDING_GO_DISC_REQ: + p2p_go_disc_req_cb(p2p, success); + break; + } +} + + +void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq, + unsigned int duration) +{ + if (freq == p2p->pending_client_disc_freq) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Client discoverability remain-awake completed"); + p2p->pending_client_disc_freq = 0; + return; + } + + if (freq != p2p->pending_listen_freq) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unexpected listen callback for freq=%u " + "duration=%u (pending_listen_freq=%u)", + freq, duration, p2p->pending_listen_freq); + return; + } + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Starting Listen timeout(%u,%u) on freq=%u based on " + "callback", + p2p->pending_listen_sec, p2p->pending_listen_usec, + p2p->pending_listen_freq); + p2p->in_listen = 1; + p2p->drv_in_listen = freq; + if (p2p->pending_listen_sec || p2p->pending_listen_usec) { + /* + * Add 20 msec extra wait to avoid race condition with driver + * remain-on-channel end event, i.e., give driver more time to + * complete the operation before our timeout expires. + */ + p2p_set_timeout(p2p, p2p->pending_listen_sec, + p2p->pending_listen_usec + 20000); + } + + p2p->pending_listen_freq = 0; +} + + +int p2p_listen_end(struct p2p_data *p2p, unsigned int freq) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver ended Listen " + "state (freq=%u)", freq); + p2p->drv_in_listen = 0; + if (p2p->in_listen) + return 0; /* Internal timeout will trigger the next step */ + + if (p2p->state == P2P_CONNECT_LISTEN && p2p->go_neg_peer) { + if (p2p->go_neg_peer->connect_reqs >= 120) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Timeout on sending GO Negotiation " + "Request without getting response"); + p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); + return 0; + } + + p2p_set_state(p2p, P2P_CONNECT); + p2p_connect_send(p2p, p2p->go_neg_peer); + return 1; + } else if (p2p->state == P2P_SEARCH) { + if (p2p->p2p_scan_running) { + /* + * Search is already in progress. This can happen if + * an Action frame RX is reported immediately after + * the end of a remain-on-channel operation and the + * response frame to that is sent using an offchannel + * operation while in p2p_find. Avoid an attempt to + * restart a scan here. + */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan " + "already in progress - do not try to start a " + "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; + } + if (p2p->search_delay) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Delay " + "search operation by %u ms", + p2p->search_delay); + p2p_set_timeout(p2p, p2p->search_delay / 1000, + (p2p->search_delay % 1000) * 1000); + return 1; + } + p2p_search(p2p); + return 1; + } + + return 0; +} + + +static void p2p_timeout_connect(struct p2p_data *p2p) +{ + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + 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, 0); +} + + +static void p2p_timeout_connect_listen(struct p2p_data *p2p) +{ + if (p2p->go_neg_peer) { + if (p2p->drv_in_listen) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver is " + "still in Listen state; wait for it to " + "complete"); + return; + } + + if (p2p->go_neg_peer->connect_reqs >= 120) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Timeout on sending GO Negotiation " + "Request without getting response"); + p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); + return; + } + + p2p_set_state(p2p, P2P_CONNECT); + p2p_connect_send(p2p, p2p->go_neg_peer); + } else + p2p_set_state(p2p, P2P_IDLE); +} + + +static void p2p_timeout_wait_peer_connect(struct p2p_data *p2p) +{ + /* + * TODO: could remain constantly in Listen state for some time if there + * are no other concurrent uses for the radio. For now, go to listen + * state once per second to give other uses a chance to use the radio. + */ + p2p_set_state(p2p, P2P_WAIT_PEER_IDLE); + p2p_set_timeout(p2p, 0, 500000); +} + + +static void p2p_timeout_wait_peer_idle(struct p2p_data *p2p) +{ + struct p2p_device *dev = p2p->go_neg_peer; + + if (dev == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unknown GO Neg peer - stop GO Neg wait"); + return; + } + + dev->wait_count++; + if (dev->wait_count >= 120) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Timeout on waiting peer to become ready for GO " + "Negotiation"); + p2p_go_neg_failed(p2p, dev, -1); + return; + } + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Go to Listen state while waiting for the peer to become " + "ready for GO Negotiation"); + p2p_set_state(p2p, P2P_WAIT_PEER_CONNECT); + p2p_listen_in_find(p2p, 0); +} + + +static void p2p_timeout_sd_during_find(struct p2p_data *p2p) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Service Discovery Query timeout"); + if (p2p->sd_peer) { + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE; + p2p->sd_peer = NULL; + } + p2p_continue_find(p2p); +} + + +static void p2p_timeout_prov_disc_during_find(struct p2p_data *p2p) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Provision Discovery Request timeout"); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p_continue_find(p2p); +} + + +static void p2p_timeout_prov_disc_req(struct p2p_data *p2p) +{ + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + + /* + * For user initiated PD requests that we have not gotten any responses + * for while in IDLE state, we retry them a couple of times before + * giving up. + */ + if (!p2p->user_initiated_pd) + return; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: User initiated Provision Discovery Request timeout"); + + if (p2p->pd_retries) { + p2p->pd_retries--; + p2p_retry_pd(p2p); + } else { + struct p2p_device *dev; + int for_join = 0; + + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { + if (os_memcmp(p2p->pending_pd_devaddr, + dev->info.p2p_device_addr, ETH_ALEN) != 0) + continue; + if (dev->req_config_methods && + (dev->flags & P2P_DEV_PD_FOR_JOIN)) + for_join = 1; + } + + if (p2p->cfg->prov_disc_fail) + p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, + p2p->pending_pd_devaddr, + for_join ? + P2P_PROV_DISC_TIMEOUT_JOIN : + P2P_PROV_DISC_TIMEOUT); + p2p_reset_pending_pd(p2p); + } +} + + +static void p2p_timeout_invite(struct p2p_data *p2p) +{ + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p_set_state(p2p, P2P_INVITE_LISTEN); + if (p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO) { + /* + * Better remain on operating channel instead of listen channel + * when running a group. + */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Inviting in " + "active GO role - wait on operating channel"); + p2p_set_timeout(p2p, 0, 100000); + return; + } + p2p_listen_in_find(p2p, 0); +} + + +static void p2p_timeout_invite_listen(struct p2p_data *p2p) +{ + if (p2p->invite_peer && p2p->invite_peer->invitation_reqs < 100) { + p2p_set_state(p2p, P2P_INVITE); + p2p_invite_send(p2p, p2p->invite_peer, + p2p->invite_go_dev_addr); + } else { + if (p2p->invite_peer) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invitation Request retry limit reached"); + if (p2p->cfg->invitation_result) + p2p->cfg->invitation_result( + p2p->cfg->cb_ctx, -1, NULL); + } + p2p_set_state(p2p, P2P_IDLE); + } +} + + +static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct p2p_data *p2p = eloop_ctx; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Timeout (state=%s)", + p2p_state_txt(p2p->state)); + + p2p->in_listen = 0; + + switch (p2p->state) { + case P2P_IDLE: + /* Check if we timed out waiting for PD req */ + if (p2p->pending_action_state == P2P_PENDING_PD) + p2p_timeout_prov_disc_req(p2p); + break; + case P2P_SEARCH: + /* Check if we timed out waiting for PD req */ + if (p2p->pending_action_state == P2P_PENDING_PD) + p2p_timeout_prov_disc_req(p2p); + if (p2p->search_delay && !p2p->in_search_delay) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Delay " + "search operation by %u ms", + p2p->search_delay); + p2p->in_search_delay = 1; + p2p_set_timeout(p2p, p2p->search_delay / 1000, + (p2p->search_delay % 1000) * 1000); + break; + } + p2p->in_search_delay = 0; + p2p_search(p2p); + break; + case P2P_CONNECT: + p2p_timeout_connect(p2p); + break; + case P2P_CONNECT_LISTEN: + p2p_timeout_connect_listen(p2p); + break; + case P2P_GO_NEG: + break; + case P2P_LISTEN_ONLY: + /* Check if we timed out waiting for PD req */ + if (p2p->pending_action_state == P2P_PENDING_PD) + p2p_timeout_prov_disc_req(p2p); + + if (p2p->ext_listen_only) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Extended Listen Timing - Listen State " + "completed"); + p2p->ext_listen_only = 0; + p2p_set_state(p2p, P2P_IDLE); + } + break; + case P2P_WAIT_PEER_CONNECT: + p2p_timeout_wait_peer_connect(p2p); + break; + case P2P_WAIT_PEER_IDLE: + p2p_timeout_wait_peer_idle(p2p); + break; + case P2P_SD_DURING_FIND: + p2p_timeout_sd_during_find(p2p); + break; + case P2P_PROVISIONING: + break; + case P2P_PD_DURING_FIND: + p2p_timeout_prov_disc_during_find(p2p); + break; + case P2P_INVITE: + p2p_timeout_invite(p2p); + break; + case P2P_INVITE_LISTEN: + p2p_timeout_invite_listen(p2p); + break; + case P2P_SEARCH_WHEN_READY: + break; + case P2P_CONTINUE_SEARCH_WHEN_READY: + break; + } +} + + +int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr) +{ + struct p2p_device *dev; + + dev = p2p_get_device(p2p, peer_addr); + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Local request to reject " + "connection attempts by peer " MACSTR, MAC2STR(peer_addr)); + if (dev == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR + " unknown", MAC2STR(peer_addr)); + return -1; + } + dev->status = P2P_SC_FAIL_REJECTED_BY_USER; + dev->flags |= P2P_DEV_USER_REJECTED; + return 0; +} + + +const char * p2p_wps_method_text(enum p2p_wps_method method) +{ + switch (method) { + case WPS_NOT_READY: + return "not-ready"; + case WPS_PIN_DISPLAY: + return "Display"; + case WPS_PIN_KEYPAD: + return "Keypad"; + case WPS_PBC: + return "PBC"; + } + + return "??"; +} + + +static const char * p2p_go_state_text(enum p2p_go_state go_state) +{ + switch (go_state) { + case UNKNOWN_GO: + return "unknown"; + case LOCAL_GO: + return "local"; + case REMOTE_GO: + return "remote"; + } + + return "??"; +} + + +const struct p2p_peer_info * p2p_get_peer_info(struct p2p_data *p2p, + const u8 *addr, int next) +{ + struct p2p_device *dev; + + if (addr) + dev = p2p_get_device(p2p, addr); + else + dev = dl_list_first(&p2p->devices, struct p2p_device, list); + + if (dev && next) { + dev = dl_list_first(&dev->list, struct p2p_device, list); + if (&dev->list == &p2p->devices) + dev = NULL; + } + + if (dev == NULL) + return NULL; + + return &dev->info; +} + + +int p2p_get_peer_info_txt(const struct p2p_peer_info *info, + char *buf, size_t buflen) +{ + struct p2p_device *dev; + int res; + char *pos, *end; + struct os_time now; + + if (info == NULL) + return -1; + + dev = (struct p2p_device *) (((u8 *) info) - + offsetof(struct p2p_device, info)); + + pos = buf; + end = buf + buflen; + + os_get_time(&now); + res = os_snprintf(pos, end - pos, + "age=%d\n" + "listen_freq=%d\n" + "wps_method=%s\n" + "interface_addr=" MACSTR "\n" + "member_in_go_dev=" MACSTR "\n" + "member_in_go_iface=" MACSTR "\n" + "go_neg_req_sent=%d\n" + "go_state=%s\n" + "dialog_token=%u\n" + "intended_addr=" MACSTR "\n" + "country=%c%c\n" + "oper_freq=%d\n" + "req_config_methods=0x%x\n" + "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n" + "status=%d\n" + "wait_count=%u\n" + "invitation_reqs=%u\n", + (int) (now.sec - dev->last_seen.sec), + dev->listen_freq, + p2p_wps_method_text(dev->wps_method), + MAC2STR(dev->interface_addr), + MAC2STR(dev->member_in_go_dev), + MAC2STR(dev->member_in_go_iface), + dev->go_neg_req_sent, + p2p_go_state_text(dev->go_state), + dev->dialog_token, + MAC2STR(dev->intended_addr), + dev->country[0] ? dev->country[0] : '_', + dev->country[1] ? dev->country[1] : '_', + dev->oper_freq, + dev->req_config_methods, + dev->flags & P2P_DEV_PROBE_REQ_ONLY ? + "[PROBE_REQ_ONLY]" : "", + dev->flags & P2P_DEV_REPORTED ? "[REPORTED]" : "", + dev->flags & P2P_DEV_NOT_YET_READY ? + "[NOT_YET_READY]" : "", + dev->flags & P2P_DEV_SD_INFO ? "[SD_INFO]" : "", + dev->flags & P2P_DEV_SD_SCHEDULE ? "[SD_SCHEDULE]" : + "", + dev->flags & P2P_DEV_PD_PEER_DISPLAY ? + "[PD_PEER_DISPLAY]" : "", + dev->flags & P2P_DEV_PD_PEER_KEYPAD ? + "[PD_PEER_KEYPAD]" : "", + dev->flags & P2P_DEV_USER_REJECTED ? + "[USER_REJECTED]" : "", + dev->flags & P2P_DEV_PEER_WAITING_RESPONSE ? + "[PEER_WAITING_RESPONSE]" : "", + dev->flags & P2P_DEV_PREFER_PERSISTENT_GROUP ? + "[PREFER_PERSISTENT_GROUP]" : "", + dev->flags & P2P_DEV_WAIT_GO_NEG_RESPONSE ? + "[WAIT_GO_NEG_RESPONSE]" : "", + dev->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM ? + "[WAIT_GO_NEG_CONFIRM]" : "", + dev->flags & P2P_DEV_GROUP_CLIENT_ONLY ? + "[GROUP_CLIENT_ONLY]" : "", + dev->flags & P2P_DEV_FORCE_FREQ ? + "[FORCE_FREQ]" : "", + dev->flags & P2P_DEV_PD_FOR_JOIN ? + "[PD_FOR_JOIN]" : "", + dev->status, + dev->wait_count, + dev->invitation_reqs); + if (res < 0 || res >= end - pos) + return pos - buf; + pos += res; + + if (dev->ext_listen_period) { + res = os_snprintf(pos, end - pos, + "ext_listen_period=%u\n" + "ext_listen_interval=%u\n", + dev->ext_listen_period, + dev->ext_listen_interval); + if (res < 0 || res >= end - pos) + return pos - buf; + pos += res; + } + + if (dev->oper_ssid_len) { + res = os_snprintf(pos, end - pos, + "oper_ssid=%s\n", + wpa_ssid_txt(dev->oper_ssid, + dev->oper_ssid_len)); + if (res < 0 || res >= end - pos) + return pos - buf; + pos += res; + } + +#ifdef CONFIG_WIFI_DISPLAY + if (dev->info.wfd_subelems) { + res = os_snprintf(pos, end - pos, "wfd_subelems="); + if (res < 0 || res >= end - pos) + return pos - buf; + pos += res; + + pos += wpa_snprintf_hex(pos, end - pos, + wpabuf_head(dev->info.wfd_subelems), + wpabuf_len(dev->info.wfd_subelems)); + + res = os_snprintf(pos, end - pos, "\n"); + if (res < 0 || res >= end - pos) + return pos - buf; + pos += res; + } +#endif /* CONFIG_WIFI_DISPLAY */ + + return pos - buf; +} + + +int p2p_peer_known(struct p2p_data *p2p, const u8 *addr) +{ + return p2p_get_device(p2p, addr) != NULL; +} + + +void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled) +{ + if (enabled) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Client " + "discoverability enabled"); + p2p->dev_capab |= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; + } else { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Client " + "discoverability disabled"); + p2p->dev_capab &= ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; + } +} + + +static struct wpabuf * p2p_build_presence_req(u32 duration1, u32 interval1, + u32 duration2, u32 interval2) +{ + struct wpabuf *req; + struct p2p_noa_desc desc1, desc2, *ptr1 = NULL, *ptr2 = NULL; + u8 *len; + + req = wpabuf_alloc(100); + if (req == NULL) + return NULL; + + if (duration1 || interval1) { + os_memset(&desc1, 0, sizeof(desc1)); + desc1.count_type = 1; + desc1.duration = duration1; + desc1.interval = interval1; + ptr1 = &desc1; + + if (duration2 || interval2) { + os_memset(&desc2, 0, sizeof(desc2)); + desc2.count_type = 2; + desc2.duration = duration2; + desc2.interval = interval2; + ptr2 = &desc2; + } + } + + p2p_buf_add_action_hdr(req, P2P_PRESENCE_REQ, 1); + len = p2p_buf_add_ie_hdr(req); + p2p_buf_add_noa(req, 0, 0, 0, ptr1, ptr2); + p2p_buf_update_ie_hdr(req, len); + + return req; +} + + +int p2p_presence_req(struct p2p_data *p2p, const u8 *go_interface_addr, + const u8 *own_interface_addr, unsigned int freq, + u32 duration1, u32 interval1, u32 duration2, + u32 interval2) +{ + struct wpabuf *req; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send Presence Request to " + "GO " MACSTR " (own interface " MACSTR ") freq=%u dur1=%u " + "int1=%u dur2=%u int2=%u", + MAC2STR(go_interface_addr), MAC2STR(own_interface_addr), + freq, duration1, interval1, duration2, interval2); + + req = p2p_build_presence_req(duration1, interval1, duration2, + interval2); + if (req == NULL) + return -1; + + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + if (p2p_send_action(p2p, freq, go_interface_addr, own_interface_addr, + go_interface_addr, + wpabuf_head(req), wpabuf_len(req), 200) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + } + wpabuf_free(req); + + return 0; +} + + +static struct wpabuf * p2p_build_presence_resp(u8 status, const u8 *noa, + size_t noa_len, u8 dialog_token) +{ + struct wpabuf *resp; + u8 *len; + + resp = wpabuf_alloc(100 + noa_len); + if (resp == NULL) + return NULL; + + p2p_buf_add_action_hdr(resp, P2P_PRESENCE_RESP, dialog_token); + len = p2p_buf_add_ie_hdr(resp); + p2p_buf_add_status(resp, status); + if (noa) { + wpabuf_put_u8(resp, P2P_ATTR_NOTICE_OF_ABSENCE); + wpabuf_put_le16(resp, noa_len); + wpabuf_put_data(resp, noa, noa_len); + } else + p2p_buf_add_noa(resp, 0, 0, 0, NULL, NULL); + p2p_buf_update_ie_hdr(resp, len); + + return resp; +} + + +static void p2p_process_presence_req(struct p2p_data *p2p, const u8 *da, + const u8 *sa, const u8 *data, size_t len, + int rx_freq) +{ + struct p2p_message msg; + u8 status; + struct wpabuf *resp; + size_t g; + struct p2p_group *group = NULL; + int parsed = 0; + u8 noa[50]; + int noa_len; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received P2P Action - P2P Presence Request"); + + for (g = 0; g < p2p->num_groups; g++) { + if (os_memcmp(da, p2p_group_get_interface_addr(p2p->groups[g]), + ETH_ALEN) == 0) { + group = p2p->groups[g]; + break; + } + } + if (group == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Ignore P2P Presence Request for unknown group " + MACSTR, MAC2STR(da)); + return; + } + + if (p2p_parse(data, len, &msg) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to parse P2P Presence Request"); + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; + } + parsed = 1; + + if (msg.noa == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No NoA attribute in P2P Presence Request"); + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; + } + + status = p2p_group_presence_req(group, sa, msg.noa, msg.noa_len); + +fail: + if (p2p->cfg->get_noa) + noa_len = p2p->cfg->get_noa(p2p->cfg->cb_ctx, da, noa, + sizeof(noa)); + else + noa_len = -1; + resp = p2p_build_presence_resp(status, noa_len > 0 ? noa : NULL, + noa_len > 0 ? noa_len : 0, + msg.dialog_token); + if (parsed) + p2p_parse_free(&msg); + if (resp == NULL) + return; + + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + if (p2p_send_action(p2p, rx_freq, sa, da, da, + wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + } + wpabuf_free(resp); +} + + +static void p2p_process_presence_resp(struct p2p_data *p2p, const u8 *da, + const u8 *sa, const u8 *data, size_t len) +{ + struct p2p_message msg; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received P2P Action - P2P Presence Response"); + + if (p2p_parse(data, len, &msg) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to parse P2P Presence Response"); + return; + } + + if (msg.status == NULL || msg.noa == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No Status or NoA attribute in P2P Presence " + "Response"); + p2p_parse_free(&msg); + return; + } + + if (*msg.status) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: P2P Presence Request was rejected: status %u", + *msg.status); + p2p_parse_free(&msg); + return; + } + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: P2P Presence Request was accepted"); + wpa_hexdump(MSG_DEBUG, "P2P: P2P Presence Response - NoA", + msg.noa, msg.noa_len); + /* TODO: process NoA */ + p2p_parse_free(&msg); +} + + +static void p2p_ext_listen_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct p2p_data *p2p = eloop_ctx; + + if (p2p->ext_listen_interval) { + /* Schedule next extended listen timeout */ + eloop_register_timeout(p2p->ext_listen_interval_sec, + p2p->ext_listen_interval_usec, + p2p_ext_listen_timeout, p2p, NULL); + } + + if (p2p->state == P2P_LISTEN_ONLY && p2p->ext_listen_only) { + /* + * This should not really happen, but it looks like the Listen + * command may fail is something else (e.g., a scan) was + * running at an inconvenient time. As a workaround, allow new + * Extended Listen operation to be started. + */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Previous " + "Extended Listen operation had not been completed - " + "try again"); + p2p->ext_listen_only = 0; + p2p_set_state(p2p, P2P_IDLE); + } + + if (p2p->state != P2P_IDLE) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Skip Extended " + "Listen timeout in active state (%s)", + p2p_state_txt(p2p->state)); + return; + } + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Extended Listen timeout"); + p2p->ext_listen_only = 1; + if (p2p_listen(p2p, p2p->ext_listen_period) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Failed to start " + "Listen state for Extended Listen Timing"); + p2p->ext_listen_only = 0; + } +} + + +int p2p_ext_listen(struct p2p_data *p2p, unsigned int period, + unsigned int interval) +{ + if (period > 65535 || interval > 65535 || period > interval || + (period == 0 && interval > 0) || (period > 0 && interval == 0)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invalid Extended Listen Timing request: " + "period=%u interval=%u", period, interval); + return -1; + } + + eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL); + + if (interval == 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Disabling Extended Listen Timing"); + p2p->ext_listen_period = 0; + p2p->ext_listen_interval = 0; + return 0; + } + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Enabling Extended Listen Timing: period %u msec, " + "interval %u msec", period, interval); + p2p->ext_listen_period = period; + p2p->ext_listen_interval = interval; + p2p->ext_listen_interval_sec = interval / 1000; + p2p->ext_listen_interval_usec = (interval % 1000) * 1000; + + eloop_register_timeout(p2p->ext_listen_interval_sec, + p2p->ext_listen_interval_usec, + p2p_ext_listen_timeout, p2p, NULL); + + return 0; +} + + +void p2p_deauth_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code, + const u8 *ie, size_t ie_len) +{ + struct p2p_message msg; + + if (bssid == NULL || ie == NULL) + return; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_ies(ie, ie_len, &msg)) + return; + if (msg.minor_reason_code == NULL) + return; + + wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, + "P2P: Deauthentication notification BSSID " MACSTR + " reason_code=%u minor_reason_code=%u", + MAC2STR(bssid), reason_code, *msg.minor_reason_code); + + p2p_parse_free(&msg); +} + + +void p2p_disassoc_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code, + const u8 *ie, size_t ie_len) +{ + struct p2p_message msg; + + if (bssid == NULL || ie == NULL) + return; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_ies(ie, ie_len, &msg)) + return; + if (msg.minor_reason_code == NULL) + return; + + wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, + "P2P: Disassociation notification BSSID " MACSTR + " reason_code=%u minor_reason_code=%u", + MAC2STR(bssid), reason_code, *msg.minor_reason_code); + + p2p_parse_free(&msg); +} + + +void p2p_set_managed_oper(struct p2p_data *p2p, int enabled) +{ + if (enabled) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Managed P2P " + "Device operations enabled"); + p2p->dev_capab |= P2P_DEV_CAPAB_INFRA_MANAGED; + } else { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Managed P2P " + "Device operations disabled"); + p2p->dev_capab &= ~P2P_DEV_CAPAB_INFRA_MANAGED; + } +} + + +int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel) +{ + if (p2p_channel_to_freq(p2p->cfg->country, reg_class, channel) < 0) + return -1; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Set Listen channel: " + "reg_class %u channel %u", reg_class, channel); + p2p->cfg->reg_class = reg_class; + p2p->cfg->channel = channel; + + return 0; +} + + +int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len) +{ + wpa_hexdump_ascii(MSG_DEBUG, "P2P: New SSID postfix", postfix, len); + if (postfix == NULL) { + p2p->cfg->ssid_postfix_len = 0; + return 0; + } + if (len > sizeof(p2p->cfg->ssid_postfix)) + return -1; + os_memcpy(p2p->cfg->ssid_postfix, postfix, len); + p2p->cfg->ssid_postfix_len = len; + return 0; +} + + +int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel, + int cfg_op_channel) +{ + if (p2p_channel_to_freq(p2p->cfg->country, op_reg_class, op_channel) + < 0) + return -1; + + wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, "P2P: Set Operating channel: " + "reg_class %u channel %u", op_reg_class, op_channel); + p2p->cfg->op_reg_class = op_reg_class; + p2p->cfg->op_channel = op_channel; + p2p->cfg->cfg_op_channel = cfg_op_channel; + return 0; +} + + +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) +{ + struct p2p_device *dev = p2p_get_device(p2p, dev_addr); + if (dev == NULL || is_zero_ether_addr(dev->interface_addr)) + return -1; + os_memcpy(iface_addr, dev->interface_addr, ETH_ALEN); + return 0; +} + + +int p2p_get_dev_addr(struct p2p_data *p2p, const u8 *iface_addr, + u8 *dev_addr) +{ + struct p2p_device *dev = p2p_get_device_interface(p2p, iface_addr); + if (dev == NULL) + return -1; + os_memcpy(dev_addr, dev->info.p2p_device_addr, ETH_ALEN); + return 0; +} + + +void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr) +{ + os_memcpy(p2p->peer_filter, addr, ETH_ALEN); + if (is_zero_ether_addr(p2p->peer_filter)) + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Disable peer " + "filter"); + else + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Enable peer " + "filter for " MACSTR, MAC2STR(p2p->peer_filter)); +} + + +void p2p_set_cross_connect(struct p2p_data *p2p, int enabled) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Cross connection %s", + enabled ? "enabled" : "disabled"); + if (p2p->cross_connect == enabled) + return; + p2p->cross_connect = enabled; + /* TODO: may need to tear down any action group where we are GO(?) */ +} + + +int p2p_get_oper_freq(struct p2p_data *p2p, const u8 *iface_addr) +{ + struct p2p_device *dev = p2p_get_device_interface(p2p, iface_addr); + if (dev == NULL) + return -1; + if (dev->oper_freq <= 0) + return -1; + return dev->oper_freq; +} + + +void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Intra BSS distribution %s", + enabled ? "enabled" : "disabled"); + p2p->cfg->p2p_intra_bss = enabled; +} + + +void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Update channel list"); + os_memcpy(&p2p->cfg->channels, chan, sizeof(struct p2p_channels)); +} + + +int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, const u8 *buf, + size_t len, unsigned int wait_time) +{ + if (p2p->p2p_scan_running) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Delay Action " + "frame TX until p2p_scan completes"); + if (p2p->after_scan_tx) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Dropped " + "previous pending Action frame TX"); + os_free(p2p->after_scan_tx); + } + p2p->after_scan_tx = os_malloc(sizeof(*p2p->after_scan_tx) + + len); + if (p2p->after_scan_tx == NULL) + return -1; + p2p->after_scan_tx->freq = freq; + os_memcpy(p2p->after_scan_tx->dst, dst, ETH_ALEN); + os_memcpy(p2p->after_scan_tx->src, src, ETH_ALEN); + os_memcpy(p2p->after_scan_tx->bssid, bssid, ETH_ALEN); + p2p->after_scan_tx->len = len; + p2p->after_scan_tx->wait_time = wait_time; + os_memcpy(p2p->after_scan_tx + 1, buf, len); + return 0; + } + + return p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, dst, src, bssid, + buf, len, wait_time); +} + + +void p2p_set_best_channels(struct p2p_data *p2p, int freq_24, int freq_5, + int freq_overall) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Best channel: 2.4 GHz: %d," + " 5 GHz: %d, overall: %d", freq_24, freq_5, freq_overall); + p2p->best_freq_24 = freq_24; + p2p->best_freq_5 = freq_5; + p2p->best_freq_overall = freq_overall; +} + + +const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p) +{ + if (p2p == NULL || p2p->go_neg_peer == NULL) + return NULL; + return p2p->go_neg_peer->info.p2p_device_addr; +} + + +const struct p2p_peer_info * +p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next) +{ + struct p2p_device *dev; + + if (addr) { + dev = p2p_get_device(p2p, addr); + if (!dev) + return NULL; + + if (!next) { + if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) + return NULL; + + return &dev->info; + } else { + do { + dev = dl_list_first(&dev->list, + struct p2p_device, + list); + if (&dev->list == &p2p->devices) + return NULL; + } while (dev->flags & P2P_DEV_PROBE_REQ_ONLY); + } + } else { + dev = dl_list_first(&p2p->devices, struct p2p_device, list); + if (!dev) + return NULL; + while (dev->flags & P2P_DEV_PROBE_REQ_ONLY) { + dev = dl_list_first(&dev->list, + struct p2p_device, + list); + if (&dev->list == &p2p->devices) + return NULL; + } + } + + return &dev->info; +} + + +int p2p_in_progress(struct p2p_data *p2p) +{ + if (p2p == NULL) + return 0; + if (p2p->state == P2P_SEARCH || p2p->state == P2P_SEARCH_WHEN_READY || + p2p->state == P2P_CONTINUE_SEARCH_WHEN_READY) + return 2; + return p2p->state != P2P_IDLE && p2p->state != P2P_PROVISIONING; +} + + +void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout, + u8 client_timeout) +{ + if (p2p) { + p2p->go_timeout = go_timeout; + p2p->client_timeout = client_timeout; + } +} + + +void p2p_increase_search_delay(struct p2p_data *p2p, unsigned int delay) +{ + if (p2p && p2p->search_delay < delay) + p2p->search_delay = delay; +} + + +#ifdef CONFIG_WIFI_DISPLAY + +static void p2p_update_wfd_ie_groups(struct p2p_data *p2p) +{ + size_t g; + struct p2p_group *group; + + for (g = 0; g < p2p->num_groups; g++) { + group = p2p->groups[g]; + p2p_group_update_ies(group); + } +} + + +int p2p_set_wfd_ie_beacon(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_beacon); + p2p->wfd_ie_beacon = ie; + p2p_update_wfd_ie_groups(p2p); + return 0; +} + + +int p2p_set_wfd_ie_probe_req(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_probe_req); + p2p->wfd_ie_probe_req = ie; + return 0; +} + + +int p2p_set_wfd_ie_probe_resp(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_probe_resp); + p2p->wfd_ie_probe_resp = ie; + p2p_update_wfd_ie_groups(p2p); + return 0; +} + + +int p2p_set_wfd_ie_assoc_req(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_assoc_req); + p2p->wfd_ie_assoc_req = ie; + return 0; +} + + +int p2p_set_wfd_ie_invitation(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_invitation); + p2p->wfd_ie_invitation = ie; + return 0; +} + + +int p2p_set_wfd_ie_prov_disc_req(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_prov_disc_req); + p2p->wfd_ie_prov_disc_req = ie; + return 0; +} + + +int p2p_set_wfd_ie_prov_disc_resp(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_prov_disc_resp); + p2p->wfd_ie_prov_disc_resp = ie; + return 0; +} + + +int p2p_set_wfd_ie_go_neg(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_go_neg); + p2p->wfd_ie_go_neg = ie; + return 0; +} + + +int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem) +{ + wpabuf_free(p2p->wfd_dev_info); + if (elem) { + p2p->wfd_dev_info = wpabuf_dup(elem); + if (p2p->wfd_dev_info == NULL) + return -1; + } else + p2p->wfd_dev_info = NULL; + + return 0; +} + + +int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem) +{ + wpabuf_free(p2p->wfd_assoc_bssid); + if (elem) { + p2p->wfd_assoc_bssid = wpabuf_dup(elem); + if (p2p->wfd_assoc_bssid == NULL) + return -1; + } else + p2p->wfd_assoc_bssid = NULL; + + return 0; +} + + +int p2p_set_wfd_coupled_sink_info(struct p2p_data *p2p, + const struct wpabuf *elem) +{ + wpabuf_free(p2p->wfd_coupled_sink_info); + if (elem) { + p2p->wfd_coupled_sink_info = wpabuf_dup(elem); + if (p2p->wfd_coupled_sink_info == NULL) + return -1; + } else + p2p->wfd_coupled_sink_info = NULL; + + return 0; +} + +#endif /* CONFIG_WIFI_DISPLAY */ + + +int p2p_set_disc_int(struct p2p_data *p2p, int min_disc_int, int max_disc_int, + int max_disc_tu) +{ + if (min_disc_int > max_disc_int || min_disc_int < 0 || max_disc_int < 0) + return -1; + + p2p->min_disc_int = min_disc_int; + p2p->max_disc_int = max_disc_int; + p2p->max_disc_tu = max_disc_tu; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Set discoverable interval: " + "min=%d max=%d max_tu=%d", min_disc_int, max_disc_int, + max_disc_tu); + + return 0; +} diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h new file mode 100644 index 0000000000000..2d8b2c25750b7 --- /dev/null +++ b/src/p2p/p2p.h @@ -0,0 +1,1783 @@ +/* + * Wi-Fi Direct - P2P module + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef P2P_H +#define P2P_H + +/** + * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes + */ +#define P2P_MAX_REG_CLASSES 10 + +/** + * P2P_MAX_REG_CLASS_CHANNELS - Maximum number of channels per regulatory class + */ +#define P2P_MAX_REG_CLASS_CHANNELS 20 + +/** + * struct p2p_channels - List of supported channels + */ +struct p2p_channels { + /** + * struct p2p_reg_class - Supported regulatory class + */ + struct p2p_reg_class { + /** + * reg_class - Regulatory class (IEEE 802.11-2007, Annex J) + */ + u8 reg_class; + + /** + * channel - Supported channels + */ + u8 channel[P2P_MAX_REG_CLASS_CHANNELS]; + + /** + * channels - Number of channel entries in use + */ + size_t channels; + } reg_class[P2P_MAX_REG_CLASSES]; + + /** + * reg_classes - Number of reg_class entries in use + */ + size_t reg_classes; +}; + +enum p2p_wps_method { + WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC +}; + +/** + * struct p2p_go_neg_results - P2P Group Owner Negotiation results + */ +struct p2p_go_neg_results { + /** + * status - Negotiation result (Status Code) + * + * 0 (P2P_SC_SUCCESS) indicates success. Non-zero values indicate + * failed negotiation. + */ + int status; + + /** + * role_go - Whether local end is Group Owner + */ + int role_go; + + /** + * freq - Frequency of the group operational channel in MHz + */ + int freq; + + int ht40; + + /** + * ssid - SSID of the group + */ + u8 ssid[32]; + + /** + * ssid_len - Length of SSID in octets + */ + size_t ssid_len; + + /** + * psk - WPA pre-shared key (256 bits) (GO only) + */ + u8 psk[32]; + + /** + * psk_set - Whether PSK field is configured (GO only) + */ + int psk_set; + + /** + * passphrase - WPA2-Personal passphrase for the group (GO only) + */ + char passphrase[64]; + + /** + * peer_device_addr - P2P Device Address of the peer + */ + u8 peer_device_addr[ETH_ALEN]; + + /** + * peer_interface_addr - P2P Interface Address of the peer + */ + u8 peer_interface_addr[ETH_ALEN]; + + /** + * wps_method - WPS method to be used during provisioning + */ + enum p2p_wps_method wps_method; + +#define P2P_MAX_CHANNELS 50 + + /** + * freq_list - Zero-terminated list of possible operational channels + */ + int freq_list[P2P_MAX_CHANNELS]; + + /** + * persistent_group - Whether the group should be made persistent + * 0 = not persistent + * 1 = persistent group without persistent reconnect + * 2 = persistent group with persistent reconnect + */ + int persistent_group; + + /** + * peer_config_timeout - Peer configuration timeout (in 10 msec units) + */ + unsigned int peer_config_timeout; +}; + +struct p2p_data; + +enum p2p_scan_type { + P2P_SCAN_SOCIAL, + P2P_SCAN_FULL, + P2P_SCAN_SOCIAL_PLUS_ONE +}; + +#define P2P_MAX_WPS_VENDOR_EXT 10 + +/** + * struct p2p_peer_info - P2P peer information + */ +struct p2p_peer_info { + /** + * p2p_device_addr - P2P Device Address of the peer + */ + u8 p2p_device_addr[ETH_ALEN]; + + /** + * pri_dev_type - Primary Device Type + */ + u8 pri_dev_type[8]; + + /** + * device_name - Device Name (0..32 octets encoded in UTF-8) + */ + char device_name[33]; + + /** + * manufacturer - Manufacturer (0..64 octets encoded in UTF-8) + */ + char manufacturer[65]; + + /** + * model_name - Model Name (0..32 octets encoded in UTF-8) + */ + char model_name[33]; + + /** + * model_number - Model Number (0..32 octets encoded in UTF-8) + */ + char model_number[33]; + + /** + * serial_number - Serial Number (0..32 octets encoded in UTF-8) + */ + char serial_number[33]; + + /** + * level - Signal level + */ + int level; + + /** + * config_methods - WPS Configuration Methods + */ + u16 config_methods; + + /** + * dev_capab - Device Capabilities + */ + u8 dev_capab; + + /** + * group_capab - Group Capabilities + */ + u8 group_capab; + + /** + * wps_sec_dev_type_list - WPS secondary device type list + * + * This list includes from 0 to 16 Secondary Device Types as indicated + * by wps_sec_dev_type_list_len (8 * number of types). + */ + u8 wps_sec_dev_type_list[128]; + + /** + * wps_sec_dev_type_list_len - Length of secondary device type list + */ + size_t wps_sec_dev_type_list_len; + + struct wpabuf *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT]; + + /** + * wfd_subelems - Wi-Fi Display subelements from WFD IE(s) + */ + struct wpabuf *wfd_subelems; +}; + +enum p2p_prov_disc_status { + P2P_PROV_DISC_SUCCESS, + P2P_PROV_DISC_TIMEOUT, + P2P_PROV_DISC_REJECTED, + P2P_PROV_DISC_TIMEOUT_JOIN, +}; + +struct p2p_channel { + u8 op_class; + u8 chan; +}; + +/** + * struct p2p_config - P2P configuration + * + * This configuration is provided to the P2P module during initialization with + * p2p_init(). + */ +struct p2p_config { + /** + * country - Country code to use in P2P operations + */ + char country[3]; + + /** + * reg_class - Regulatory class for own listen channel + */ + u8 reg_class; + + /** + * channel - Own listen channel + */ + u8 channel; + + /** + * Regulatory class for own operational channel + */ + u8 op_reg_class; + + /** + * op_channel - Own operational channel + */ + u8 op_channel; + + /** + * cfg_op_channel - Whether op_channel is hardcoded in configuration + */ + u8 cfg_op_channel; + + /** + * channels - Own supported regulatory classes and channels + * + * List of supposerted channels per regulatory class. The regulatory + * classes are defined in IEEE Std 802.11-2007 Annex J and the + * numbering of the clases depends on the configured country code. + */ + struct p2p_channels channels; + + /** + * num_pref_chan - Number of pref_chan entries + */ + unsigned int num_pref_chan; + + /** + * pref_chan - Preferred channels for GO Negotiation + */ + struct p2p_channel *pref_chan; + + /** + * pri_dev_type - Primary Device Type (see WPS) + */ + u8 pri_dev_type[8]; + + /** + * P2P_SEC_DEVICE_TYPES - Maximum number of secondary device types + */ +#define P2P_SEC_DEVICE_TYPES 5 + + /** + * sec_dev_type - Optional secondary device types + */ + u8 sec_dev_type[P2P_SEC_DEVICE_TYPES][8]; + + /** + * num_sec_dev_types - Number of sec_dev_type entries + */ + size_t num_sec_dev_types; + + /** + * dev_addr - P2P Device Address + */ + u8 dev_addr[ETH_ALEN]; + + /** + * dev_name - Device Name + */ + char *dev_name; + + char *manufacturer; + char *model_name; + char *model_number; + char *serial_number; + + u8 uuid[16]; + u16 config_methods; + + /** + * concurrent_operations - Whether concurrent operations are supported + */ + int concurrent_operations; + + /** + * max_peers - Maximum number of discovered peers to remember + * + * If more peers are discovered, older entries will be removed to make + * room for the new ones. + */ + size_t max_peers; + + /** + * p2p_intra_bss - Intra BSS communication is supported + */ + int p2p_intra_bss; + + /** + * ssid_postfix - Postfix data to add to the SSID + * + * This data will be added to the end of the SSID after the + * DIRECT-<random two octets> prefix. + */ + u8 ssid_postfix[32 - 9]; + + /** + * ssid_postfix_len - Length of the ssid_postfix data + */ + size_t ssid_postfix_len; + + /** + * max_listen - Maximum listen duration in ms + */ + unsigned int max_listen; + + /** + * msg_ctx - Context to use with wpa_msg() calls + */ + void *msg_ctx; + + /** + * cb_ctx - Context to use with callback functions + */ + void *cb_ctx; + + + /* Callbacks to request lower layer driver operations */ + + /** + * p2p_scan - Request a P2P scan/search + * @ctx: Callback context from cb_ctx + * @type: Scan type + * @freq: Specific frequency (MHz) to scan or 0 for no restriction + * @num_req_dev_types: Number of requested device types + * @req_dev_types: Array containing requested device types + * @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_SOCIAL_PLUS_ONE request scan of all the social channels + * plus one extra channel specified by freq. + * + * The full scan is used for the initial scan to find group owners from + * all. The other types are used during search phase scan of the social + * channels (with potential variation if the Listen channel of the + * target peer is known or if other channels are scanned in steps). + * + * The scan results are returned after this call by calling + * p2p_scan_res_handler() for each scan result that has a P2P IE and + * then calling p2p_scan_res_handled() to indicate that all scan + * results have been indicated. + */ + int (*p2p_scan)(void *ctx, enum p2p_scan_type type, int freq, + unsigned int num_req_dev_types, + const u8 *req_dev_types, const u8 *dev_id, u16 pw_id); + + /** + * send_probe_resp - Transmit a Probe Response frame + * @ctx: Callback context from cb_ctx + * @buf: Probe Response frame (including the header and body) + * Returns: 0 on success, -1 on failure + * + * This function is used to reply to Probe Request frames that were + * indicated with a call to p2p_probe_req_rx(). The response is to be + * sent on the same channel or to be dropped if the driver is not + * anymore listening to Probe Request frames. + * + * Alternatively, the responsibility for building the Probe Response + * frames in Listen state may be in another system component in which + * case this function need to be implemented (i.e., the function + * pointer can be %NULL). The WPS and P2P IEs to be added for Probe + * Response frames in such a case are available from the + * start_listen() callback. It should be noted that the received Probe + * Request frames must be indicated by calling p2p_probe_req_rx() even + * if this send_probe_resp() is not used. + */ + int (*send_probe_resp)(void *ctx, const struct wpabuf *buf); + + /** + * send_action - Transmit an Action frame + * @ctx: Callback context from cb_ctx + * @freq: Frequency in MHz for the channel on which to transmit + * @dst: Destination MAC address (Address 1) + * @src: Source MAC address (Address 2) + * @bssid: BSSID (Address 3) + * @buf: Frame body (starting from Category field) + * @len: Length of buf in octets + * @wait_time: How many msec to wait for a response frame + * Returns: 0 on success, -1 on failure + * + * The Action frame may not be transmitted immediately and the status + * of the transmission must be reported by calling + * p2p_send_action_cb() once the frame has either been transmitted or + * it has been dropped due to excessive retries or other failure to + * transmit. + */ + int (*send_action)(void *ctx, unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, const u8 *buf, + size_t len, unsigned int wait_time); + + /** + * send_action_done - Notify that Action frame sequence was completed + * @ctx: Callback context from cb_ctx + * + * This function is called when the Action frame sequence that was + * started with send_action() has been completed, i.e., when there is + * no need to wait for a response from the destination peer anymore. + */ + void (*send_action_done)(void *ctx); + + /** + * start_listen - Start Listen state + * @ctx: Callback context from cb_ctx + * @freq: Frequency of the listen channel in MHz + * @duration: Duration for the Listen state in milliseconds + * @probe_resp_ie: IE(s) to be added to Probe Response frames + * Returns: 0 on success, -1 on failure + * + * This Listen state may not start immediately since the driver may + * have other pending operations to complete first. Once the Listen + * state has started, p2p_listen_cb() must be called to notify the P2P + * module. Once the Listen state is stopped, p2p_listen_end() must be + * called to notify the P2P module that the driver is not in the Listen + * state anymore. + * + * If the send_probe_resp() is not used for generating the response, + * the IEs from probe_resp_ie need to be added to the end of the Probe + * Response frame body. If send_probe_resp() is used, the probe_resp_ie + * information can be ignored. + */ + int (*start_listen)(void *ctx, unsigned int freq, + unsigned int duration, + const struct wpabuf *probe_resp_ie); + /** + * stop_listen - Stop Listen state + * @ctx: Callback context from cb_ctx + * + * This callback can be used to stop a Listen state operation that was + * previously requested with start_listen(). + */ + void (*stop_listen)(void *ctx); + + /** + * get_noa - Get current Notice of Absence attribute payload + * @ctx: Callback context from cb_ctx + * @interface_addr: P2P Interface Address of the GO + * @buf: Buffer for returning NoA + * @buf_len: Buffer length in octets + * Returns: Number of octets used in buf, 0 to indicate no NoA is being + * advertized, or -1 on failure + * + * This function is used to fetch the current Notice of Absence + * attribute value from GO. + */ + int (*get_noa)(void *ctx, const u8 *interface_addr, u8 *buf, + size_t buf_len); + + /* Callbacks to notify events to upper layer management entity */ + + /** + * dev_found - Notification of a found P2P Device + * @ctx: Callback context from cb_ctx + * @addr: Source address of the message triggering this notification + * @info: P2P peer information + * @new_device: Inform if the peer is newly found + * + * This callback is used to notify that a new P2P Device has been + * found. This may happen, e.g., during Search state based on scan + * results or during Listen state based on receive Probe Request and + * Group Owner Negotiation Request. + */ + void (*dev_found)(void *ctx, const u8 *addr, + const struct p2p_peer_info *info, + int new_device); + + /** + * dev_lost - Notification of a lost P2P Device + * @ctx: Callback context from cb_ctx + * @dev_addr: P2P Device Address of the lost P2P Device + * + * This callback is used to notify that a P2P Device has been deleted. + */ + void (*dev_lost)(void *ctx, const u8 *dev_addr); + + /** + * go_neg_req_rx - Notification of a receive GO Negotiation Request + * @ctx: Callback context from cb_ctx + * @src: Source address of the message triggering this notification + * @dev_passwd_id: WPS Device Password ID + * + * This callback is used to notify that a P2P Device is requesting + * group owner negotiation with us, but we do not have all the + * necessary information to start GO Negotiation. This indicates that + * the local user has not authorized the connection yet by providing a + * PIN or PBC button press. This information can be provided with a + * call to p2p_connect(). + */ + void (*go_neg_req_rx)(void *ctx, const u8 *src, u16 dev_passwd_id); + + /** + * go_neg_completed - Notification of GO Negotiation results + * @ctx: Callback context from cb_ctx + * @res: GO Negotiation results + * + * This callback is used to notify that Group Owner Negotiation has + * been completed. Non-zero struct p2p_go_neg_results::status indicates + * failed negotiation. In case of success, this function is responsible + * for creating a new group interface (or using the existing interface + * depending on driver features), setting up the group interface in + * proper mode based on struct p2p_go_neg_results::role_go and + * initializing WPS provisioning either as a Registrar (if GO) or as an + * Enrollee. Successful WPS provisioning must be indicated by calling + * p2p_wps_success_cb(). The callee is responsible for timing out group + * formation if WPS provisioning cannot be completed successfully + * within 15 seconds. + */ + void (*go_neg_completed)(void *ctx, struct p2p_go_neg_results *res); + + /** + * sd_request - Callback on Service Discovery Request + * @ctx: Callback context from cb_ctx + * @freq: Frequency (in MHz) of the channel + * @sa: Source address of the request + * @dialog_token: Dialog token + * @update_indic: Service Update Indicator from the source of request + * @tlvs: P2P Service Request TLV(s) + * @tlvs_len: Length of tlvs buffer in octets + * + * This callback is used to indicate reception of a service discovery + * request. Response to the query must be indicated by calling + * p2p_sd_response() with the context information from the arguments to + * this callback function. + * + * This callback handler can be set to %NULL to indicate that service + * discovery is not supported. + */ + void (*sd_request)(void *ctx, int freq, const u8 *sa, u8 dialog_token, + u16 update_indic, const u8 *tlvs, size_t tlvs_len); + + /** + * sd_response - Callback on Service Discovery Response + * @ctx: Callback context from cb_ctx + * @sa: Source address of the request + * @update_indic: Service Update Indicator from the source of response + * @tlvs: P2P Service Response TLV(s) + * @tlvs_len: Length of tlvs buffer in octets + * + * This callback is used to indicate reception of a service discovery + * response. This callback handler can be set to %NULL if no service + * discovery requests are used. The information provided with this call + * is replies to the queries scheduled with p2p_sd_request(). + */ + void (*sd_response)(void *ctx, const u8 *sa, u16 update_indic, + const u8 *tlvs, size_t tlvs_len); + + /** + * prov_disc_req - Callback on Provisiong Discovery Request + * @ctx: Callback context from cb_ctx + * @peer: Source address of the request + * @config_methods: Requested WPS Config Method + * @dev_addr: P2P Device Address of the found P2P Device + * @pri_dev_type: Primary Device Type + * @dev_name: Device Name + * @supp_config_methods: Supported configuration Methods + * @dev_capab: Device Capabilities + * @group_capab: Group Capabilities + * @group_id: P2P Group ID (or %NULL if not included) + * @group_id_len: Length of P2P Group ID + * + * This callback is used to indicate reception of a Provision Discovery + * Request frame that the P2P module accepted. + */ + void (*prov_disc_req)(void *ctx, const u8 *peer, u16 config_methods, + const u8 *dev_addr, const u8 *pri_dev_type, + const char *dev_name, u16 supp_config_methods, + u8 dev_capab, u8 group_capab, + const u8 *group_id, size_t group_id_len); + + /** + * prov_disc_resp - Callback on Provisiong Discovery Response + * @ctx: Callback context from cb_ctx + * @peer: Source address of the response + * @config_methods: Value from p2p_prov_disc_req() or 0 on failure + * + * This callback is used to indicate reception of a Provision Discovery + * Response frame for a pending request scheduled with + * p2p_prov_disc_req(). This callback handler can be set to %NULL if + * provision discovery is not used. + */ + void (*prov_disc_resp)(void *ctx, const u8 *peer, u16 config_methods); + + /** + * prov_disc_fail - Callback on Provision Discovery failure + * @ctx: Callback context from cb_ctx + * @peer: Source address of the response + * @status: Cause of failure, will not be %P2P_PROV_DISC_SUCCESS + * + * This callback is used to indicate either a failure or no response + * to an earlier provision discovery request. + * + * This callback handler can be set to %NULL if provision discovery + * is not used or failures do not need to be indicated. + */ + void (*prov_disc_fail)(void *ctx, const u8 *peer, + enum p2p_prov_disc_status status); + + /** + * invitation_process - Optional callback for processing Invitations + * @ctx: Callback context from cb_ctx + * @sa: Source address of the Invitation Request + * @bssid: P2P Group BSSID from the request or %NULL if not included + * @go_dev_addr: GO Device Address from P2P Group ID + * @ssid: SSID from P2P Group ID + * @ssid_len: Length of ssid buffer in octets + * @go: Variable for returning whether the local end is GO in the group + * @group_bssid: Buffer for returning P2P Group BSSID (if local end GO) + * @force_freq: Variable for returning forced frequency for the group + * @persistent_group: Whether this is an invitation to reinvoke a + * persistent group (instead of invitation to join an active + * group) + * Returns: Status code (P2P_SC_*) + * + * This optional callback can be used to implement persistent reconnect + * by allowing automatic restarting of persistent groups without user + * interaction. If this callback is not implemented (i.e., is %NULL), + * the received Invitation Request frames are replied with + * %P2P_SC_REQ_RECEIVED status and indicated to upper layer with the + * invitation_result() callback. + * + * If the requested parameters are acceptable and the group is known, + * %P2P_SC_SUCCESS may be returned. If the requested group is unknown, + * %P2P_SC_FAIL_UNKNOWN_GROUP should be returned. %P2P_SC_REQ_RECEIVED + * can be returned if there is not enough data to provide immediate + * response, i.e., if some sort of user interaction is needed. The + * invitation_received() callback will be called in that case + * immediately after this call. + */ + u8 (*invitation_process)(void *ctx, const u8 *sa, const u8 *bssid, + const u8 *go_dev_addr, const u8 *ssid, + size_t ssid_len, int *go, u8 *group_bssid, + int *force_freq, int persistent_group); + + /** + * invitation_received - Callback on Invitation Request RX + * @ctx: Callback context from cb_ctx + * @sa: Source address of the Invitation Request + * @bssid: P2P Group BSSID or %NULL if not received + * @ssid: SSID of the group + * @ssid_len: Length of ssid in octets + * @go_dev_addr: GO Device Address + * @status: Response Status + * @op_freq: Operational frequency for the group + * + * This callback is used to indicate sending of an Invitation Response + * for a received Invitation Request. If status == 0 (success), the + * upper layer code is responsible for starting the group. status == 1 + * indicates need to get user authorization for the group. Other status + * values indicate that the invitation request was rejected. + */ + void (*invitation_received)(void *ctx, const u8 *sa, const u8 *bssid, + const u8 *ssid, size_t ssid_len, + const u8 *go_dev_addr, u8 status, + int op_freq); + + /** + * invitation_result - Callback on Invitation result + * @ctx: Callback context from cb_ctx + * @status: Negotiation result (Status Code) + * @bssid: P2P Group BSSID or %NULL if not received + * + * This callback is used to indicate result of an Invitation procedure + * started with a call to p2p_invite(). The indicated status code is + * the value received from the peer in Invitation Response with 0 + * (P2P_SC_SUCCESS) indicating success or -1 to indicate a timeout or a + * local failure in transmitting the Invitation Request. + */ + void (*invitation_result)(void *ctx, int status, const u8 *bssid); + + /** + * go_connected - Check whether we are connected to a GO + * @ctx: Callback context from cb_ctx + * @dev_addr: P2P Device Address of a GO + * Returns: 1 if we are connected as a P2P client to the specified GO + * or 0 if not. + */ + int (*go_connected)(void *ctx, const u8 *dev_addr); +}; + + +/* P2P module initialization/deinitialization */ + +/** + * p2p_init - Initialize P2P module + * @cfg: P2P module configuration + * Returns: Pointer to private data or %NULL on failure + * + * This function is used to initialize global P2P module context (one per + * device). The P2P module will keep a copy of the configuration data, so the + * caller does not need to maintain this structure. However, the callback + * functions and the context parameters to them must be kept available until + * the P2P module is deinitialized with p2p_deinit(). + */ +struct p2p_data * p2p_init(const struct p2p_config *cfg); + +/** + * p2p_deinit - Deinitialize P2P module + * @p2p: P2P module context from p2p_init() + */ +void p2p_deinit(struct p2p_data *p2p); + +/** + * p2p_flush - Flush P2P module state + * @p2p: P2P module context from p2p_init() + * + * This command removes the P2P module state like peer device entries. + */ +void p2p_flush(struct p2p_data *p2p); + +/** + * p2p_unauthorize - Unauthorize the specified peer device + * @p2p: P2P module context from p2p_init() + * @addr: P2P peer entry to be unauthorized + * Returns: 0 on success, -1 on failure + * + * This command removes any connection authorization from the specified P2P + * peer device address. This can be used, e.g., to cancel effect of a previous + * p2p_authorize() or p2p_connect() call that has not yet resulted in completed + * GO Negotiation. + */ +int p2p_unauthorize(struct p2p_data *p2p, const u8 *addr); + +/** + * p2p_set_dev_name - Set device name + * @p2p: P2P module context from p2p_init() + * Returns: 0 on success, -1 on failure + * + * This function can be used to update the P2P module configuration with + * information that was not available at the time of the p2p_init() call. + */ +int p2p_set_dev_name(struct p2p_data *p2p, const char *dev_name); + +int p2p_set_manufacturer(struct p2p_data *p2p, const char *manufacturer); +int p2p_set_model_name(struct p2p_data *p2p, const char *model_name); +int p2p_set_model_number(struct p2p_data *p2p, const char *model_number); +int p2p_set_serial_number(struct p2p_data *p2p, const char *serial_number); + +void p2p_set_config_methods(struct p2p_data *p2p, u16 config_methods); +void p2p_set_uuid(struct p2p_data *p2p, const u8 *uuid); + +/** + * p2p_set_pri_dev_type - Set primary device type + * @p2p: P2P module context from p2p_init() + * Returns: 0 on success, -1 on failure + * + * This function can be used to update the P2P module configuration with + * information that was not available at the time of the p2p_init() call. + */ +int p2p_set_pri_dev_type(struct p2p_data *p2p, const u8 *pri_dev_type); + +/** + * p2p_set_sec_dev_types - Set secondary device types + * @p2p: P2P module context from p2p_init() + * Returns: 0 on success, -1 on failure + * + * This function can be used to update the P2P module configuration with + * information that was not available at the time of the p2p_init() call. + */ +int p2p_set_sec_dev_types(struct p2p_data *p2p, const u8 dev_types[][8], + size_t num_dev_types); + +int p2p_set_country(struct p2p_data *p2p, const char *country); + + +/* Commands from upper layer management entity */ + +enum p2p_discovery_type { + P2P_FIND_START_WITH_FULL, + P2P_FIND_ONLY_SOCIAL, + P2P_FIND_PROGRESSIVE +}; + +/** + * p2p_find - Start P2P Find (Device Discovery) + * @p2p: P2P module context from p2p_init() + * @timeout: Timeout for find operation in seconds or 0 for no timeout + * @type: Device Discovery type + * @num_req_dev_types: Number of requested device types + * @req_dev_types: Requested device types array, must be an array + * containing num_req_dev_types * WPS_DEV_TYPE_LEN bytes; %NULL if no + * requested device types. + * @dev_id: Device ID to search for or %NULL to find all devices + * @search_delay: Extra delay in milliseconds between search iterations + * Returns: 0 on success, -1 on failure + */ +int p2p_find(struct p2p_data *p2p, unsigned int timeout, + enum p2p_discovery_type type, + unsigned int num_req_dev_types, const u8 *req_dev_types, + const u8 *dev_id, unsigned int search_delay); + +/** + * p2p_stop_find - Stop P2P Find (Device Discovery) + * @p2p: P2P module context from p2p_init() + */ +void p2p_stop_find(struct p2p_data *p2p); + +/** + * p2p_stop_find_for_freq - Stop P2P Find for next oper on specific freq + * @p2p: P2P module context from p2p_init() + * @freq: Frequency in MHz for next operation + * + * This is like p2p_stop_find(), but Listen state is not stopped if we are + * already on the same frequency. + */ +void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq); + +/** + * p2p_listen - Start P2P Listen state for specified duration + * @p2p: P2P module context from p2p_init() + * @timeout: Listen state duration in milliseconds + * Returns: 0 on success, -1 on failure + * + * This function can be used to request the P2P module to keep the device + * discoverable on the listen channel for an extended set of time. At least in + * its current form, this is mainly used for testing purposes and may not be of + * much use for normal P2P operations. + */ +int p2p_listen(struct p2p_data *p2p, unsigned int timeout); + +/** + * p2p_connect - Start P2P group formation (GO negotiation) + * @p2p: P2P module context from p2p_init() + * @peer_addr: MAC address of the peer P2P client + * @wps_method: WPS method to be used in provisioning + * @go_intent: Local GO intent value (1..15) + * @own_interface_addr: Intended interface address to use with the group + * @force_freq: The only allowed channel frequency in MHz or 0 + * @persistent_group: Whether to create a persistent group (0 = no, 1 = + * persistent group without persistent reconnect, 2 = persistent group with + * persistent reconnect) + * @force_ssid: Forced SSID for the group if we become GO or %NULL to generate + * a new SSID + * @force_ssid_len: Length of $force_ssid buffer + * @pd_before_go_neg: Whether to send Provision Discovery prior to GO + * Negotiation as an interoperability workaround when initiating group + * formation + * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if + * force_freq == 0) + * Returns: 0 on success, -1 on failure + */ +int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, + enum p2p_wps_method wps_method, + int go_intent, const u8 *own_interface_addr, + unsigned int force_freq, int persistent_group, + const u8 *force_ssid, size_t force_ssid_len, + int pd_before_go_neg, unsigned int pref_freq); + +/** + * p2p_authorize - Authorize P2P group formation (GO negotiation) + * @p2p: P2P module context from p2p_init() + * @peer_addr: MAC address of the peer P2P client + * @wps_method: WPS method to be used in provisioning + * @go_intent: Local GO intent value (1..15) + * @own_interface_addr: Intended interface address to use with the group + * @force_freq: The only allowed channel frequency in MHz or 0 + * @persistent_group: Whether to create a persistent group (0 = no, 1 = + * persistent group without persistent reconnect, 2 = persistent group with + * persistent reconnect) + * @force_ssid: Forced SSID for the group if we become GO or %NULL to generate + * a new SSID + * @force_ssid_len: Length of $force_ssid buffer + * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if + * force_freq == 0) + * Returns: 0 on success, -1 on failure + * + * This is like p2p_connect(), but the actual group negotiation is not + * initiated automatically, i.e., the other end is expected to do that. + */ +int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr, + enum p2p_wps_method wps_method, + int go_intent, const u8 *own_interface_addr, + unsigned int force_freq, int persistent_group, + const u8 *force_ssid, size_t force_ssid_len, + unsigned int pref_freq); + +/** + * p2p_reject - Reject peer device (explicitly block connection attempts) + * @p2p: P2P module context from p2p_init() + * @peer_addr: MAC address of the peer P2P client + * Returns: 0 on success, -1 on failure + */ +int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr); + +/** + * p2p_prov_disc_req - Send Provision Discovery Request + * @p2p: P2P module context from p2p_init() + * @peer_addr: MAC address of the peer P2P client + * @config_methods: WPS Config Methods value (only one bit set) + * @join: Whether this is used by a client joining an active group + * @force_freq: Forced TX frequency for the frame (mainly for the join case) + * @user_initiated_pd: Flag to indicate if initiated by user or not + * Returns: 0 on success, -1 on failure + * + * This function can be used to request a discovered P2P peer to display a PIN + * (config_methods = WPS_CONFIG_DISPLAY) or be prepared to enter a PIN from us + * (config_methods = WPS_CONFIG_KEYPAD). The Provision Discovery Request frame + * is transmitted once immediately and if no response is received, the frame + * will be sent again whenever the target device is discovered during device + * dsicovery (start with a p2p_find() call). Response from the peer is + * indicated with the p2p_config::prov_disc_resp() callback. + */ +int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, + u16 config_methods, int join, int force_freq, + int user_initiated_pd); + +/** + * p2p_sd_request - Schedule a service discovery query + * @p2p: P2P module context from p2p_init() + * @dst: Destination peer or %NULL to apply for all peers + * @tlvs: P2P Service Query TLV(s) + * Returns: Reference to the query or %NULL on failure + * + * Response to the query is indicated with the p2p_config::sd_response() + * callback. + */ +void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst, + const struct wpabuf *tlvs); + +#ifdef CONFIG_WIFI_DISPLAY +void * p2p_sd_request_wfd(struct p2p_data *p2p, const u8 *dst, + const struct wpabuf *tlvs); +#endif /* CONFIG_WIFI_DISPLAY */ + +/** + * p2p_sd_cancel_request - Cancel a pending service discovery query + * @p2p: P2P module context from p2p_init() + * @req: Query reference from p2p_sd_request() + * Returns: 0 if request for cancelled; -1 if not found + */ +int p2p_sd_cancel_request(struct p2p_data *p2p, void *req); + +/** + * p2p_sd_response - Send response to a service discovery query + * @p2p: P2P module context from p2p_init() + * @freq: Frequency from p2p_config::sd_request() callback + * @dst: Destination address from p2p_config::sd_request() callback + * @dialog_token: Dialog token from p2p_config::sd_request() callback + * @resp_tlvs: P2P Service Response TLV(s) + * + * This function is called as a response to the request indicated with + * p2p_config::sd_request() callback. + */ +void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst, + u8 dialog_token, const struct wpabuf *resp_tlvs); + +/** + * p2p_sd_service_update - Indicate a change in local services + * @p2p: P2P module context from p2p_init() + * + * This function needs to be called whenever there is a change in availability + * of the local services. This will increment the Service Update Indicator + * value which will be used in SD Request and Response frames. + */ +void p2p_sd_service_update(struct p2p_data *p2p); + + +enum p2p_invite_role { + P2P_INVITE_ROLE_GO, + P2P_INVITE_ROLE_ACTIVE_GO, + P2P_INVITE_ROLE_CLIENT +}; + +/** + * p2p_invite - Invite a P2P Device into a group + * @p2p: P2P module context from p2p_init() + * @peer: Device Address of the peer P2P Device + * @role: Local role in the group + * @bssid: Group BSSID or %NULL if not known + * @ssid: Group SSID + * @ssid_len: Length of ssid in octets + * @force_freq: The only allowed channel frequency in MHz or 0 + * @go_dev_addr: Forced GO Device Address or %NULL if none + * @persistent_group: Whether this is to reinvoke a persistent group + * Returns: 0 on success, -1 on failure + */ +int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role, + const u8 *bssid, const u8 *ssid, size_t ssid_len, + unsigned int force_freq, const u8 *go_dev_addr, + int persistent_group); + +/** + * p2p_presence_req - Request GO presence + * @p2p: P2P module context from p2p_init() + * @go_interface_addr: GO P2P Interface Address + * @own_interface_addr: Own P2P Interface Address for this group + * @freq: Group operating frequence (in MHz) + * @duration1: Preferred presence duration in microseconds + * @interval1: Preferred presence interval in microseconds + * @duration2: Acceptable presence duration in microseconds + * @interval2: Acceptable presence interval in microseconds + * Returns: 0 on success, -1 on failure + * + * If both duration and interval values are zero, the parameter pair is not + * specified (i.e., to remove Presence Request, use duration1 = interval1 = 0). + */ +int p2p_presence_req(struct p2p_data *p2p, const u8 *go_interface_addr, + const u8 *own_interface_addr, unsigned int freq, + u32 duration1, u32 interval1, u32 duration2, + u32 interval2); + +/** + * p2p_ext_listen - Set Extended Listen Timing + * @p2p: P2P module context from p2p_init() + * @freq: Group operating frequence (in MHz) + * @period: Availability period in milliseconds (1-65535; 0 to disable) + * @interval: Availability interval in milliseconds (1-65535; 0 to disable) + * Returns: 0 on success, -1 on failure + * + * This function can be used to enable or disable (period = interval = 0) + * Extended Listen Timing. When enabled, the P2P Device will become + * discoverable (go into Listen State) every @interval milliseconds for at + * least @period milliseconds. + */ +int p2p_ext_listen(struct p2p_data *p2p, unsigned int period, + unsigned int interval); + +/* Event notifications from upper layer management operations */ + +/** + * p2p_wps_success_cb - Report successfully completed WPS provisioning + * @p2p: P2P module context from p2p_init() + * @mac_addr: Peer address + * + * This function is used to report successfully completed WPS provisioning + * during group formation in both GO/Registrar and client/Enrollee roles. + */ +void p2p_wps_success_cb(struct p2p_data *p2p, const u8 *mac_addr); + +/** + * p2p_group_formation_failed - Report failed WPS provisioning + * @p2p: P2P module context from p2p_init() + * + * This function is used to report failed group formation. This can happen + * either due to failed WPS provisioning or due to 15 second timeout during + * the provisioning phase. + */ +void p2p_group_formation_failed(struct p2p_data *p2p); + +/** + * p2p_get_provisioning_info - Get any stored provisioning info + * @p2p: P2P module context from p2p_init() + * @addr: Peer P2P Device Address + * Returns: WPS provisioning information (WPS config method) or 0 if no + * information is available + * + * This function is used to retrieve stored WPS provisioning info for the given + * peer. + */ +u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr); + +/** + * p2p_clear_provisioning_info - Clear any stored provisioning info + * @p2p: P2P module context from p2p_init() + * @iface_addr: Peer P2P Device Address + * + * This function is used to clear stored WPS provisioning info for the given + * peer. + */ +void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *addr); + + +/* Event notifications from lower layer driver operations */ + +/** + * enum p2p_probe_req_status + * + * @P2P_PREQ_MALFORMED: frame was not well-formed + * @P2P_PREQ_NOT_LISTEN: device isn't in listen state, frame ignored + * @P2P_PREQ_NOT_P2P: frame was not a P2P probe request + * @P2P_PREQ_P2P_NOT_PROCESSED: frame was P2P but wasn't processed + * @P2P_PREQ_P2P_PROCESSED: frame has been processed by P2P + */ +enum p2p_probe_req_status { + P2P_PREQ_MALFORMED, + P2P_PREQ_NOT_LISTEN, + P2P_PREQ_NOT_P2P, + P2P_PREQ_NOT_PROCESSED, + P2P_PREQ_PROCESSED +}; + +/** + * p2p_probe_req_rx - Report reception of a Probe Request frame + * @p2p: P2P module context from p2p_init() + * @addr: Source MAC address + * @dst: Destination MAC address if available or %NULL + * @bssid: BSSID if available or %NULL + * @ie: Information elements from the Probe Request frame body + * @ie_len: Length of ie buffer in octets + * Returns: value indicating the type and status of the probe request + */ +enum p2p_probe_req_status +p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, + const u8 *bssid, const u8 *ie, size_t ie_len); + +/** + * p2p_rx_action - Report received Action frame + * @p2p: P2P module context from p2p_init() + * @da: Destination address of the received Action frame + * @sa: Source address of the received Action frame + * @bssid: Address 3 of the received Action frame + * @category: Category of the received Action frame + * @data: Action frame body after the Category field + * @len: Length of the data buffer in octets + * @freq: Frequency (in MHz) on which the frame was received + */ +void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa, + const u8 *bssid, u8 category, + const u8 *data, size_t len, int freq); + +/** + * p2p_scan_res_handler - Indicate a P2P scan results + * @p2p: P2P module context from p2p_init() + * @bssid: BSSID of the scan result + * @freq: Frequency of the channel on which the device was found in MHz + * @age: Age of the scan result in milliseconds + * @level: Signal level (signal strength of the received Beacon/Probe Response + * frame) + * @ies: Pointer to IEs from the scan result + * @ies_len: Length of the ies buffer + * Returns: 0 to continue or 1 to stop scan result indication + * + * This function is called to indicate a scan result entry with P2P IE from a + * scan requested with struct p2p_config::p2p_scan(). This can be called during + * the actual scan process (i.e., whenever a new device is found) or as a + * sequence of calls after the full scan has been completed. The former option + * can result in optimized operations, but may not be supported by all + * driver/firmware designs. The ies buffer need to include at least the P2P IE, + * but it is recommended to include all IEs received from the device. The + * caller does not need to check that the IEs contain a P2P IE before calling + * this function since frames will be filtered internally if needed. + * + * This function will return 1 if it wants to stop scan result iteration (and + * scan in general if it is still in progress). This is used to allow faster + * start of a pending operation, e.g., to start a pending GO negotiation. + */ +int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq, + unsigned int age, int level, const u8 *ies, + size_t ies_len); + +/** + * p2p_scan_res_handled - Indicate end of scan results + * @p2p: P2P module context from p2p_init() + * + * This function is called to indicate that all P2P scan results from a scan + * have been reported with zero or more calls to p2p_scan_res_handler(). This + * function must be called as a response to successful + * struct p2p_config::p2p_scan() call if none of the p2p_scan_res_handler() + * calls stopped iteration. + */ +void p2p_scan_res_handled(struct p2p_data *p2p); + +enum p2p_send_action_result { + P2P_SEND_ACTION_SUCCESS /* Frame was send and acknowledged */, + P2P_SEND_ACTION_NO_ACK /* Frame was sent, but not acknowledged */, + P2P_SEND_ACTION_FAILED /* Frame was not sent due to a failure */ +}; + +/** + * p2p_send_action_cb - Notify TX status of an Action frame + * @p2p: P2P module context from p2p_init() + * @freq: Channel frequency in MHz + * @dst: Destination MAC address (Address 1) + * @src: Source MAC address (Address 2) + * @bssid: BSSID (Address 3) + * @result: Result of the transmission attempt + * + * This function is used to indicate the result of an Action frame transmission + * that was requested with struct p2p_config::send_action() callback. + */ +void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, + enum p2p_send_action_result result); + +/** + * p2p_listen_cb - Indicate the start of a requested Listen state + * @p2p: P2P module context from p2p_init() + * @freq: Listen channel frequency in MHz + * @duration: Duration for the Listen state in milliseconds + * + * This function is used to indicate that a Listen state requested with + * struct p2p_config::start_listen() callback has started. + */ +void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq, + unsigned int duration); + +/** + * p2p_listen_end - Indicate the end of a requested Listen state + * @p2p: P2P module context from p2p_init() + * @freq: Listen channel frequency in MHz + * Returns: 0 if no operations were started, 1 if an operation was started + * + * This function is used to indicate that a Listen state requested with + * struct p2p_config::start_listen() callback has ended. + */ +int p2p_listen_end(struct p2p_data *p2p, unsigned int freq); + +void p2p_deauth_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code, + const u8 *ie, size_t ie_len); + +void p2p_disassoc_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code, + const u8 *ie, size_t ie_len); + + +/* Per-group P2P state for GO */ + +struct p2p_group; + +/** + * struct p2p_group_config - P2P group configuration + * + * This configuration is provided to the P2P module during initialization of + * the per-group information with p2p_group_init(). + */ +struct p2p_group_config { + /** + * persistent_group - Whether the group is persistent + * 0 = not a persistent group + * 1 = persistent group without persistent reconnect + * 2 = persistent group with persistent reconnect + */ + int persistent_group; + + /** + * interface_addr - P2P Interface Address of the group + */ + u8 interface_addr[ETH_ALEN]; + + /** + * max_clients - Maximum number of clients in the group + */ + unsigned int max_clients; + + /** + * ssid - Group SSID + */ + u8 ssid[32]; + + /** + * ssid_len - Length of SSID + */ + size_t ssid_len; + + /** + * cb_ctx - Context to use with callback functions + */ + void *cb_ctx; + + /** + * ie_update - Notification of IE update + * @ctx: Callback context from cb_ctx + * @beacon_ies: P2P IE for Beacon frames or %NULL if no change + * @proberesp_ies: P2P Ie for Probe Response frames + * + * P2P module uses this callback function to notify whenever the P2P IE + * in Beacon or Probe Response frames should be updated based on group + * events. + * + * The callee is responsible for freeing the returned buffer(s) with + * wpabuf_free(). + */ + void (*ie_update)(void *ctx, struct wpabuf *beacon_ies, + struct wpabuf *proberesp_ies); + + /** + * idle_update - Notification of changes in group idle state + * @ctx: Callback context from cb_ctx + * @idle: Whether the group is idle (no associated stations) + */ + void (*idle_update)(void *ctx, int idle); +}; + +/** + * p2p_group_init - Initialize P2P group + * @p2p: P2P module context from p2p_init() + * @config: P2P group configuration (will be freed by p2p_group_deinit()) + * Returns: Pointer to private data or %NULL on failure + * + * This function is used to initialize per-group P2P module context. Currently, + * this is only used to manage GO functionality and P2P clients do not need to + * create an instance of this per-group information. + */ +struct p2p_group * p2p_group_init(struct p2p_data *p2p, + struct p2p_group_config *config); + +/** + * p2p_group_deinit - Deinitialize P2P group + * @group: P2P group context from p2p_group_init() + */ +void p2p_group_deinit(struct p2p_group *group); + +/** + * p2p_group_notif_assoc - Notification of P2P client association with GO + * @group: P2P group context from p2p_group_init() + * @addr: Interface address of the P2P client + * @ie: IEs from the (Re)association Request frame + * @len: Length of the ie buffer in octets + * Returns: 0 on success, -1 on failure + */ +int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr, + const u8 *ie, size_t len); + +/** + * p2p_group_assoc_resp_ie - Build P2P IE for (re)association response + * @group: P2P group context from p2p_group_init() + * @status: Status value (P2P_SC_SUCCESS if association succeeded) + * Returns: P2P IE for (Re)association Response or %NULL on failure + * + * The caller is responsible for freeing the returned buffer with + * wpabuf_free(). + */ +struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status); + +/** + * p2p_group_notif_disassoc - Notification of P2P client disassociation from GO + * @group: P2P group context from p2p_group_init() + * @addr: Interface address of the P2P client + */ +void p2p_group_notif_disassoc(struct p2p_group *group, const u8 *addr); + +/** + * p2p_group_notif_formation_done - Notification of completed group formation + * @group: P2P group context from p2p_group_init() + */ +void p2p_group_notif_formation_done(struct p2p_group *group); + +/** + * p2p_group_notif_noa - Notification of NoA change + * @group: P2P group context from p2p_group_init() + * @noa: Notice of Absence attribute payload, %NULL if none + * @noa_len: Length of noa buffer in octets + * Returns: 0 on success, -1 on failure + * + * Notify the P2P group management about a new NoA contents. This will be + * inserted into the P2P IEs in Beacon and Probe Response frames with rest of + * the group information. + */ +int p2p_group_notif_noa(struct p2p_group *group, const u8 *noa, + size_t noa_len); + +/** + * p2p_group_match_dev_type - Match device types in group with requested type + * @group: P2P group context from p2p_group_init() + * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs) + * Returns: 1 on match, 0 on mismatch + * + * This function can be used to match the Requested Device Type attribute in + * WPS IE with the device types of a group member for deciding whether a GO + * should reply to a Probe Request frame. Match will be reported if the WPS IE + * is not requested any specific device type. + */ +int p2p_group_match_dev_type(struct p2p_group *group, struct wpabuf *wps); + +/** + * p2p_group_match_dev_id - Match P2P Device Address in group with requested device id + */ +int p2p_group_match_dev_id(struct p2p_group *group, struct wpabuf *p2p); + +/** + * p2p_group_go_discover - Send GO Discoverability Request to a group client + * @group: P2P group context from p2p_group_init() + * Returns: 0 on success (frame scheduled); -1 if client was not found + */ +int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id, + const u8 *searching_dev, int rx_freq); + + +/* Generic helper functions */ + +/** + * p2p_ie_text - Build text format description of P2P IE + * @p2p_ie: P2P IE + * @buf: Buffer for returning text + * @end: Pointer to the end of the buf area + * Returns: Number of octets written to the buffer or -1 on failure + * + * This function can be used to parse P2P IE contents into text format + * field=value lines. + */ +int p2p_ie_text(struct wpabuf *p2p_ie, char *buf, char *end); + +/** + * p2p_scan_result_text - Build text format description of P2P IE + * @ies: Information elements from scan results + * @ies_len: ies buffer length in octets + * @buf: Buffer for returning text + * @end: Pointer to the end of the buf area + * Returns: Number of octets written to the buffer or -1 on failure + * + * This function can be used to parse P2P IE contents into text format + * field=value lines. + */ +int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end); + +/** + * p2p_parse_dev_addr_in_p2p_ie - Parse P2P Device Address from a concatenated + * P2P IE + * @p2p_ie: P2P IE + * @dev_addr: Buffer for returning P2P Device Address + * Returns: 0 on success or -1 if P2P Device Address could not be parsed + */ +int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr); + +/** + * p2p_parse_dev_addr - Parse P2P Device Address from P2P IE(s) + * @ies: Information elements from scan results + * @ies_len: ies buffer length in octets + * @dev_addr: Buffer for returning P2P Device Address + * Returns: 0 on success or -1 if P2P Device Address could not be parsed + */ +int p2p_parse_dev_addr(const u8 *ies, size_t ies_len, u8 *dev_addr); + +/** + * p2p_assoc_req_ie - Build P2P IE for (Re)Association Request frame + * @p2p: P2P module context from p2p_init() + * @bssid: BSSID + * @buf: Buffer for writing the P2P IE + * @len: Maximum buf length in octets + * @p2p_group: Whether this is for association with a P2P GO + * @p2p_ie: Reassembled P2P IE data from scan results or %NULL if none + * Returns: Number of octets written into buf or -1 on failure + */ +int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf, + size_t len, int p2p_group, struct wpabuf *p2p_ie); + +/** + * p2p_scan_ie - Build P2P IE for Probe Request + * @p2p: P2P module context from p2p_init() + * @ies: Buffer for writing P2P IE + * @dev_id: Device ID to search for or %NULL for any + */ +void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id); + +/** + * p2p_scan_ie_buf_len - Get maximum buffer length needed for p2p_scan_ie + * @p2p: P2P module context from p2p_init() + * Returns: Number of octets that p2p_scan_ie() may add to the buffer + */ +size_t p2p_scan_ie_buf_len(struct p2p_data *p2p); + +/** + * p2p_go_params - Generate random P2P group parameters + * @p2p: P2P module context from p2p_init() + * @params: Buffer for parameters + * Returns: 0 on success, -1 on failure + */ +int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params); + +/** + * p2p_get_group_capab - Get Group Capability from P2P IE data + * @p2p_ie: P2P IE(s) contents + * Returns: Group Capability + */ +u8 p2p_get_group_capab(const struct wpabuf *p2p_ie); + +/** + * p2p_get_cross_connect_disallowed - Does WLAN AP disallows cross connection + * @p2p_ie: P2P IE(s) contents + * Returns: 0 if cross connection is allow, 1 if not + */ +int p2p_get_cross_connect_disallowed(const struct wpabuf *p2p_ie); + +/** + * p2p_get_go_dev_addr - Get P2P Device Address from P2P IE data + * @p2p_ie: P2P IE(s) contents + * Returns: Pointer to P2P Device Address or %NULL if not included + */ +const u8 * p2p_get_go_dev_addr(const struct wpabuf *p2p_ie); + +/** + * p2p_get_peer_info - Get P2P peer information + * @p2p: P2P module context from p2p_init() + * @addr: P2P Device Address of the peer or %NULL to indicate the first peer + * @next: Whether to select the peer entry following the one indicated by addr + * Returns: Pointer to peer info or %NULL if not found + */ +const struct p2p_peer_info * p2p_get_peer_info(struct p2p_data *p2p, + const u8 *addr, int next); + +/** + * p2p_get_peer_info_txt - Get internal P2P peer information in text format + * @info: Pointer to P2P peer info from p2p_get_peer_info() + * @buf: Buffer for returning text + * @buflen: Maximum buffer length + * Returns: Number of octets written to the buffer or -1 on failure + * + * Note: This information is internal to the P2P module and subject to change. + * As such, this should not really be used by external programs for purposes + * other than debugging. + */ +int p2p_get_peer_info_txt(const struct p2p_peer_info *info, + char *buf, size_t buflen); + +/** + * p2p_peer_known - Check whether P2P peer is known + * @p2p: P2P module context from p2p_init() + * @addr: P2P Device Address of the peer + * Returns: 1 if the specified device is in the P2P peer table or 0 if not + */ +int p2p_peer_known(struct p2p_data *p2p, const u8 *addr); + +/** + * p2p_set_client_discoverability - Set client discoverability capability + * @p2p: P2P module context from p2p_init() + * @enabled: Whether client discoverability will be enabled + * + * This function can be used to disable (and re-enable) client discoverability. + * This capability is enabled by default and should not be disabled in normal + * use cases, i.e., this is mainly for testing purposes. + */ +void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled); + +/** + * p2p_set_managed_oper - Set managed P2P Device operations capability + * @p2p: P2P module context from p2p_init() + * @enabled: Whether managed P2P Device operations will be enabled + */ +void p2p_set_managed_oper(struct p2p_data *p2p, int enabled); + +int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel); + +int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len); + +int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr, + u8 *iface_addr); +int p2p_get_dev_addr(struct p2p_data *p2p, const u8 *iface_addr, + u8 *dev_addr); + +void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr); + +/** + * p2p_set_cross_connect - Set cross connection capability + * @p2p: P2P module context from p2p_init() + * @enabled: Whether cross connection will be enabled + */ +void p2p_set_cross_connect(struct p2p_data *p2p, int enabled); + +int p2p_get_oper_freq(struct p2p_data *p2p, const u8 *iface_addr); + +/** + * p2p_set_intra_bss_dist - Set intra BSS distribution + * @p2p: P2P module context from p2p_init() + * @enabled: Whether intra BSS distribution will be enabled + */ +void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled); + +/** + * p2p_supported_freq - Check whether channel is supported for P2P + * @p2p: P2P module context from p2p_init() + * @freq: Channel frequency in MHz + * Returns: 0 if channel not usable for P2P, 1 if usable for P2P + */ +int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq); + +void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan); + +/** + * p2p_set_best_channels - Update best channel information + * @p2p: P2P module context from p2p_init() + * @freq_24: Frequency (MHz) of best channel in 2.4 GHz band + * @freq_5: Frequency (MHz) of best channel in 5 GHz band + * @freq_overall: Frequency (MHz) of best channel overall + */ +void p2p_set_best_channels(struct p2p_data *p2p, int freq_24, int freq_5, + int freq_overall); + +const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p); + +/** + * p2p_get_group_num_members - Get number of members in group + * @group: P2P group context from p2p_group_init() + * Returns: Number of members in the group + */ +unsigned int p2p_get_group_num_members(struct p2p_group *group); + +/** + * p2p_iterate_group_members - Iterate group members + * @group: P2P group context from p2p_group_init() + * @next: iteration pointer, must be a pointer to a void * that is set to %NULL + * on the first call and not modified later + * Returns: A P2P Interface Address for each call and %NULL for no more members + */ +const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next); + +/** + * p2p_group_get_dev_addr - Get a P2P Device Address of a client in a group + * @group: P2P group context from p2p_group_init() + * @addr: P2P Interface Address of the client + * Returns: P2P Device Address of the client if found or %NULL if no match + * found + */ +const u8 * p2p_group_get_dev_addr(struct p2p_group *group, const u8 *addr); + +/** + * p2p_group_is_client_connected - Check whether a specific client is connected + * @group: P2P group context from p2p_group_init() + * @addr: P2P Device Address of the client + * Returns: 1 if client is connected or 0 if not + */ +int p2p_group_is_client_connected(struct p2p_group *group, const u8 *dev_addr); + +/** + * p2p_get_peer_found - Get P2P peer info structure of a found peer + * @p2p: P2P module context from p2p_init() + * @addr: P2P Device Address of the peer or %NULL to indicate the first peer + * @next: Whether to select the peer entry following the one indicated by addr + * Returns: The first P2P peer info available or %NULL if no such peer exists + */ +const struct p2p_peer_info * +p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next); + +/** + * p2p_remove_wps_vendor_extensions - Remove WPS vendor extensions + * @p2p: P2P module context from p2p_init() + */ +void p2p_remove_wps_vendor_extensions(struct p2p_data *p2p); + +/** + * p2p_add_wps_vendor_extension - Add a WPS vendor extension + * @p2p: P2P module context from p2p_init() + * @vendor_ext: The vendor extensions to add + * Returns: 0 on success, -1 on failure + * + * The wpabuf structures in the array are owned by the P2P + * module after this call. + */ +int p2p_add_wps_vendor_extension(struct p2p_data *p2p, + const struct wpabuf *vendor_ext); + +/** + * p2p_set_oper_channel - Set the P2P operating channel + * @p2p: P2P module context from p2p_init() + * @op_reg_class: Operating regulatory class to set + * @op_channel: operating channel to set + * @cfg_op_channel : Whether op_channel is hardcoded in configuration + * Returns: 0 on success, -1 on failure + */ +int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel, + int cfg_op_channel); + +/** + * p2p_set_pref_chan - Set P2P preferred channel list + * @p2p: P2P module context from p2p_init() + * @num_pref_chan: Number of entries in pref_chan list + * @pref_chan: Preferred channels or %NULL to remove preferences + * Returns: 0 on success, -1 on failure + */ +int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan, + const struct p2p_channel *pref_chan); + +/** + * p2p_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 + */ +int p2p_in_progress(struct p2p_data *p2p); + +/** + * p2p_other_scan_completed - Notify completion of non-P2P scan + * @p2p: P2P module context from p2p_init() + * Returns: 0 if P2P module is idle or 1 if an operation was started + */ +int p2p_other_scan_completed(struct p2p_data *p2p); + +const char * p2p_wps_method_text(enum p2p_wps_method method); + +/** + * p2p_set_config_timeout - Set local config timeouts + * @p2p: P2P module context from p2p_init() + * @go_timeout: Time in 10 ms units it takes to start the GO mode + * @client_timeout: Time in 10 ms units it takes to start the client mode + */ +void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout, + u8 client_timeout); + +void p2p_increase_search_delay(struct p2p_data *p2p, unsigned int delay); + +int p2p_set_wfd_ie_beacon(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_probe_req(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_probe_resp(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_assoc_req(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_invitation(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_prov_disc_req(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_prov_disc_resp(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_go_neg(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem); +int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem); +int p2p_set_wfd_coupled_sink_info(struct p2p_data *p2p, + const struct wpabuf *elem); +struct wpabuf * wifi_display_encaps(struct wpabuf *subelems); + +/** + * p2p_set_disc_int - Set min/max discoverable interval for p2p_find + * @p2p: P2P module context from p2p_init() + * @min_disc_int: minDiscoverableInterval (in units of 100 TU); default 1 + * @max_disc_int: maxDiscoverableInterval (in units of 100 TU); default 3 + * @max_disc_tu: Maximum number of TUs (1.024 ms) for discoverable interval; or + * -1 not to limit + * Returns: 0 on success, or -1 on failure + * + * This function can be used to configure minDiscoverableInterval and + * maxDiscoverableInterval parameters for the Listen state during device + * discovery (p2p_find). A random number of 100 TU units is picked for each + * Listen state iteration from [min_disc_int,max_disc_int] range. + * + * max_disc_tu can be used to futher limit the discoverable duration. However, + * it should be noted that use of this parameter is not recommended since it + * would not be compliant with the P2P specification. + */ +int p2p_set_disc_int(struct p2p_data *p2p, int min_disc_int, int max_disc_int, + int max_disc_tu); + +#endif /* P2P_H */ diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c new file mode 100644 index 0000000000000..5838d35e977ee --- /dev/null +++ b/src/p2p/p2p_build.c @@ -0,0 +1,431 @@ +/* + * P2P - IE builder + * Copyright (c) 2009-2010, Atheros Communications + * + * 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 "wps/wps_i.h" +#include "p2p_i.h" + + +void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token) +{ + wpabuf_put_u8(buf, WLAN_ACTION_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, P2P_OUI_TYPE); + + wpabuf_put_u8(buf, subtype); /* OUI Subtype */ + wpabuf_put_u8(buf, dialog_token); + wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", dialog_token); +} + + +void p2p_buf_add_public_action_hdr(struct wpabuf *buf, u8 subtype, + u8 dialog_token) +{ + wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); + wpabuf_put_u8(buf, WLAN_PA_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, P2P_OUI_TYPE); + + wpabuf_put_u8(buf, subtype); /* OUI Subtype */ + wpabuf_put_u8(buf, dialog_token); + wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", dialog_token); +} + + +u8 * p2p_buf_add_ie_hdr(struct wpabuf *buf) +{ + u8 *len; + + /* P2P IE header */ + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + len = wpabuf_put(buf, 1); /* IE length to be filled */ + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, P2P_OUI_TYPE); + wpa_printf(MSG_DEBUG, "P2P: * P2P IE header"); + return len; +} + + +void p2p_buf_update_ie_hdr(struct wpabuf *buf, u8 *len) +{ + /* Update P2P IE Length */ + *len = (u8 *) wpabuf_put(buf, 0) - len - 1; +} + + +void p2p_buf_add_capability(struct wpabuf *buf, u8 dev_capab, u8 group_capab) +{ + /* P2P Capability */ + wpabuf_put_u8(buf, P2P_ATTR_CAPABILITY); + wpabuf_put_le16(buf, 2); + wpabuf_put_u8(buf, dev_capab); /* Device Capabilities */ + wpabuf_put_u8(buf, group_capab); /* Group Capabilities */ + wpa_printf(MSG_DEBUG, "P2P: * Capability dev=%02x group=%02x", + dev_capab, group_capab); +} + + +void p2p_buf_add_go_intent(struct wpabuf *buf, u8 go_intent) +{ + /* Group Owner Intent */ + wpabuf_put_u8(buf, P2P_ATTR_GROUP_OWNER_INTENT); + wpabuf_put_le16(buf, 1); + wpabuf_put_u8(buf, go_intent); + wpa_printf(MSG_DEBUG, "P2P: * GO Intent: Intent %u Tie breaker %u", + go_intent >> 1, go_intent & 0x01); +} + + +void p2p_buf_add_listen_channel(struct wpabuf *buf, const char *country, + u8 reg_class, u8 channel) +{ + /* Listen Channel */ + wpabuf_put_u8(buf, P2P_ATTR_LISTEN_CHANNEL); + wpabuf_put_le16(buf, 5); + wpabuf_put_data(buf, country, 3); + wpabuf_put_u8(buf, reg_class); /* Regulatory Class */ + wpabuf_put_u8(buf, channel); /* Channel Number */ + wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: Regulatory Class %u " + "Channel %u", reg_class, channel); +} + + +void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country, + u8 reg_class, u8 channel) +{ + /* Operating Channel */ + wpabuf_put_u8(buf, P2P_ATTR_OPERATING_CHANNEL); + wpabuf_put_le16(buf, 5); + wpabuf_put_data(buf, country, 3); + wpabuf_put_u8(buf, reg_class); /* Regulatory Class */ + wpabuf_put_u8(buf, channel); /* Channel Number */ + wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: Regulatory Class %u " + "Channel %u", reg_class, channel); +} + + +void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country, + struct p2p_channels *chan) +{ + u8 *len; + size_t i; + + /* Channel List */ + wpabuf_put_u8(buf, P2P_ATTR_CHANNEL_LIST); + len = wpabuf_put(buf, 2); /* IE length to be filled */ + wpabuf_put_data(buf, country, 3); /* Country String */ + + for (i = 0; i < chan->reg_classes; i++) { + struct p2p_reg_class *c = &chan->reg_class[i]; + wpabuf_put_u8(buf, c->reg_class); + wpabuf_put_u8(buf, c->channels); + wpabuf_put_data(buf, c->channel, c->channels); + } + + /* Update attribute length */ + WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2); + wpa_hexdump(MSG_DEBUG, "P2P: * Channel List", + len + 2, (u8 *) wpabuf_put(buf, 0) - len - 2); +} + + +void p2p_buf_add_status(struct wpabuf *buf, u8 status) +{ + /* Status */ + wpabuf_put_u8(buf, P2P_ATTR_STATUS); + wpabuf_put_le16(buf, 1); + wpabuf_put_u8(buf, status); + wpa_printf(MSG_DEBUG, "P2P: * Status: %d", status); +} + + +void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p, + struct p2p_device *peer) +{ + u8 *len; + u16 methods; + size_t nlen, i; + + /* P2P Device Info */ + wpabuf_put_u8(buf, P2P_ATTR_DEVICE_INFO); + len = wpabuf_put(buf, 2); /* IE length to be filled */ + + /* P2P Device address */ + wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN); + + /* Config Methods */ + methods = 0; + if (peer && peer->wps_method != WPS_NOT_READY) { + if (peer->wps_method == WPS_PBC) + methods |= WPS_CONFIG_PUSHBUTTON; + else if (peer->wps_method == WPS_PIN_DISPLAY || + peer->wps_method == WPS_PIN_KEYPAD) + methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; + } else if (p2p->cfg->config_methods) { + methods |= p2p->cfg->config_methods & + (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_DISPLAY | + WPS_CONFIG_KEYPAD); + } else { + methods |= WPS_CONFIG_PUSHBUTTON; + methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; + } + wpabuf_put_be16(buf, methods); + + /* Primary Device Type */ + wpabuf_put_data(buf, p2p->cfg->pri_dev_type, + sizeof(p2p->cfg->pri_dev_type)); + + /* Number of Secondary Device Types */ + wpabuf_put_u8(buf, p2p->cfg->num_sec_dev_types); + + /* Secondary Device Type List */ + for (i = 0; i < p2p->cfg->num_sec_dev_types; i++) + wpabuf_put_data(buf, p2p->cfg->sec_dev_type[i], + WPS_DEV_TYPE_LEN); + + /* Device Name */ + nlen = p2p->cfg->dev_name ? os_strlen(p2p->cfg->dev_name) : 0; + wpabuf_put_be16(buf, ATTR_DEV_NAME); + wpabuf_put_be16(buf, nlen); + wpabuf_put_data(buf, p2p->cfg->dev_name, nlen); + + /* Update attribute length */ + WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2); + wpa_printf(MSG_DEBUG, "P2P: * Device Info"); +} + + +void p2p_buf_add_device_id(struct wpabuf *buf, const u8 *dev_addr) +{ + /* P2P Device ID */ + wpabuf_put_u8(buf, P2P_ATTR_DEVICE_ID); + wpabuf_put_le16(buf, ETH_ALEN); + wpabuf_put_data(buf, dev_addr, ETH_ALEN); + wpa_printf(MSG_DEBUG, "P2P: * Device ID: " MACSTR, MAC2STR(dev_addr)); +} + + +void p2p_buf_add_config_timeout(struct wpabuf *buf, u8 go_timeout, + u8 client_timeout) +{ + /* Configuration Timeout */ + wpabuf_put_u8(buf, P2P_ATTR_CONFIGURATION_TIMEOUT); + wpabuf_put_le16(buf, 2); + wpabuf_put_u8(buf, go_timeout); + wpabuf_put_u8(buf, client_timeout); + wpa_printf(MSG_DEBUG, "P2P: * Configuration Timeout: GO %d (*10ms) " + "client %d (*10ms)", go_timeout, client_timeout); +} + + +void p2p_buf_add_intended_addr(struct wpabuf *buf, const u8 *interface_addr) +{ + /* Intended P2P Interface Address */ + wpabuf_put_u8(buf, P2P_ATTR_INTENDED_INTERFACE_ADDR); + wpabuf_put_le16(buf, ETH_ALEN); + wpabuf_put_data(buf, interface_addr, ETH_ALEN); + wpa_printf(MSG_DEBUG, "P2P: * Intended P2P Interface Address " MACSTR, + MAC2STR(interface_addr)); +} + + +void p2p_buf_add_group_bssid(struct wpabuf *buf, const u8 *bssid) +{ + /* P2P Group BSSID */ + wpabuf_put_u8(buf, P2P_ATTR_GROUP_BSSID); + wpabuf_put_le16(buf, ETH_ALEN); + wpabuf_put_data(buf, bssid, ETH_ALEN); + wpa_printf(MSG_DEBUG, "P2P: * P2P Group BSSID " MACSTR, + MAC2STR(bssid)); +} + + +void p2p_buf_add_group_id(struct wpabuf *buf, const u8 *dev_addr, + const u8 *ssid, size_t ssid_len) +{ + /* P2P Group ID */ + wpabuf_put_u8(buf, P2P_ATTR_GROUP_ID); + wpabuf_put_le16(buf, ETH_ALEN + ssid_len); + wpabuf_put_data(buf, dev_addr, ETH_ALEN); + wpabuf_put_data(buf, ssid, ssid_len); + wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID " MACSTR, + MAC2STR(dev_addr)); +} + + +void p2p_buf_add_invitation_flags(struct wpabuf *buf, u8 flags) +{ + /* Invitation Flags */ + wpabuf_put_u8(buf, P2P_ATTR_INVITATION_FLAGS); + wpabuf_put_le16(buf, 1); + wpabuf_put_u8(buf, flags); + wpa_printf(MSG_DEBUG, "P2P: * Invitation Flags: bitmap 0x%x", flags); +} + + +static void p2p_buf_add_noa_desc(struct wpabuf *buf, struct p2p_noa_desc *desc) +{ + if (desc == NULL) + return; + + wpabuf_put_u8(buf, desc->count_type); + wpabuf_put_le32(buf, desc->duration); + wpabuf_put_le32(buf, desc->interval); + wpabuf_put_le32(buf, desc->start_time); +} + + +void p2p_buf_add_noa(struct wpabuf *buf, u8 noa_index, u8 opp_ps, u8 ctwindow, + struct p2p_noa_desc *desc1, struct p2p_noa_desc *desc2) +{ + /* Notice of Absence */ + wpabuf_put_u8(buf, P2P_ATTR_NOTICE_OF_ABSENCE); + wpabuf_put_le16(buf, 2 + (desc1 ? 13 : 0) + (desc2 ? 13 : 0)); + wpabuf_put_u8(buf, noa_index); + wpabuf_put_u8(buf, (opp_ps ? 0x80 : 0) | (ctwindow & 0x7f)); + p2p_buf_add_noa_desc(buf, desc1); + p2p_buf_add_noa_desc(buf, desc2); + wpa_printf(MSG_DEBUG, "P2P: * Notice of Absence"); +} + + +void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period, + u16 interval) +{ + /* Extended Listen Timing */ + wpabuf_put_u8(buf, P2P_ATTR_EXT_LISTEN_TIMING); + wpabuf_put_le16(buf, 4); + wpabuf_put_le16(buf, period); + wpabuf_put_le16(buf, interval); + wpa_printf(MSG_DEBUG, "P2P: * Extended Listen Timing (period %u msec " + "interval %u msec)", period, interval); +} + + +void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p) +{ + /* P2P Interface */ + wpabuf_put_u8(buf, P2P_ATTR_INTERFACE); + wpabuf_put_le16(buf, ETH_ALEN + 1 + ETH_ALEN); + /* P2P Device address */ + wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN); + /* + * FIX: Fetch interface address list from driver. Do not include + * the P2P Device address if it is never used as interface address. + */ + /* P2P Interface Address Count */ + wpabuf_put_u8(buf, 1); + wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN); +} + + +static void p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr, + const char *val) +{ + size_t len; + + wpabuf_put_be16(buf, attr); + len = val ? os_strlen(val) : 0; +#ifndef CONFIG_WPS_STRICT + if (len == 0) { + /* + * Some deployed WPS implementations fail to parse zeor-length + * attributes. As a workaround, send a space character if the + * device attribute string is empty. + */ + wpabuf_put_be16(buf, 1); + wpabuf_put_u8(buf, ' '); + return; + } +#endif /* CONFIG_WPS_STRICT */ + wpabuf_put_be16(buf, len); + if (val) + wpabuf_put_data(buf, val, len); +} + + +void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, + int all_attr) +{ + u8 *len; + int i; + + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + len = wpabuf_put(buf, 1); + wpabuf_put_be32(buf, WPS_DEV_OUI_WFA); + + wps_build_version(buf); + + if (all_attr) { + wpabuf_put_be16(buf, ATTR_WPS_STATE); + wpabuf_put_be16(buf, 1); + wpabuf_put_u8(buf, WPS_STATE_NOT_CONFIGURED); + } + + 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); + wpabuf_put_be16(buf, 1); + wpabuf_put_u8(buf, WPS_RESP_ENROLLEE_INFO); + + wps_build_uuid_e(buf, p2p->cfg->uuid); + p2p_add_wps_string(buf, ATTR_MANUFACTURER, + p2p->cfg->manufacturer); + p2p_add_wps_string(buf, ATTR_MODEL_NAME, p2p->cfg->model_name); + p2p_add_wps_string(buf, ATTR_MODEL_NUMBER, + p2p->cfg->model_number); + p2p_add_wps_string(buf, ATTR_SERIAL_NUMBER, + p2p->cfg->serial_number); + + wpabuf_put_be16(buf, ATTR_PRIMARY_DEV_TYPE); + wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN); + wpabuf_put_data(buf, p2p->cfg->pri_dev_type, WPS_DEV_TYPE_LEN); + + p2p_add_wps_string(buf, ATTR_DEV_NAME, p2p->cfg->dev_name); + + wpabuf_put_be16(buf, ATTR_CONFIG_METHODS); + wpabuf_put_be16(buf, 2); + wpabuf_put_be16(buf, p2p->cfg->config_methods); + } + + wps_build_wfa_ext(buf, 0, NULL, 0); + + if (all_attr && p2p->cfg->num_sec_dev_types) { + wpabuf_put_be16(buf, ATTR_SECONDARY_DEV_TYPE_LIST); + wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN * + p2p->cfg->num_sec_dev_types); + wpabuf_put_data(buf, p2p->cfg->sec_dev_type, + WPS_DEV_TYPE_LEN * + p2p->cfg->num_sec_dev_types); + } + + /* Add the WPS vendor extensions */ + for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { + if (p2p->wps_vendor_ext[i] == NULL) + break; + if (wpabuf_tailroom(buf) < + 4 + wpabuf_len(p2p->wps_vendor_ext[i])) + continue; + wpabuf_put_be16(buf, ATTR_VENDOR_EXT); + wpabuf_put_be16(buf, wpabuf_len(p2p->wps_vendor_ext[i])); + wpabuf_put_buf(buf, p2p->wps_vendor_ext[i]); + } + + p2p_buf_update_ie_hdr(buf, len); +} diff --git a/src/p2p/p2p_dev_disc.c b/src/p2p/p2p_dev_disc.c new file mode 100644 index 0000000000000..c976b7c6d62b2 --- /dev/null +++ b/src/p2p/p2p_dev_disc.c @@ -0,0 +1,359 @@ +/* + * Wi-Fi Direct - P2P Device Discoverability procedure + * Copyright (c) 2010, Atheros Communications + * + * 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 "p2p_i.h" +#include "p2p.h" + + +static struct wpabuf * p2p_build_dev_disc_req(struct p2p_data *p2p, + struct p2p_device *go, + const u8 *dev_id) +{ + struct wpabuf *buf; + u8 *len; + + buf = wpabuf_alloc(100); + if (buf == NULL) + return NULL; + + go->dialog_token++; + if (go->dialog_token == 0) + go->dialog_token = 1; + p2p_buf_add_public_action_hdr(buf, P2P_DEV_DISC_REQ, go->dialog_token); + + len = p2p_buf_add_ie_hdr(buf); + p2p_buf_add_device_id(buf, dev_id); + p2p_buf_add_group_id(buf, go->info.p2p_device_addr, go->oper_ssid, + go->oper_ssid_len); + p2p_buf_update_ie_hdr(buf, len); + + return buf; +} + + +void p2p_dev_disc_req_cb(struct p2p_data *p2p, int success) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Device Discoverability Request TX callback: success=%d", + success); + + if (!success) { + /* + * Use P2P find, if needed, to find the other device or to + * retry device discoverability. + */ + p2p_set_state(p2p, P2P_CONNECT); + p2p_set_timeout(p2p, 0, 100000); + return; + } + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: GO acknowledged Device Discoverability Request - wait " + "for response"); + /* + * TODO: is the remain-on-channel from Action frame TX long enough for + * most cases or should we try to increase its duration and/or start + * another remain-on-channel if needed once the previous one expires? + */ +} + + +int p2p_send_dev_disc_req(struct p2p_data *p2p, struct p2p_device *dev) +{ + struct p2p_device *go; + struct wpabuf *req; + + go = p2p_get_device(p2p, dev->member_in_go_dev); + if (go == NULL || dev->oper_freq <= 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Could not find peer entry for GO and frequency " + "to send Device Discoverability Request"); + return -1; + } + + req = p2p_build_dev_disc_req(p2p, go, dev->info.p2p_device_addr); + if (req == NULL) + return -1; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Sending Device Discoverability Request to GO " MACSTR + " for client " MACSTR, + MAC2STR(go->info.p2p_device_addr), + MAC2STR(dev->info.p2p_device_addr)); + + p2p->pending_client_disc_go = go; + os_memcpy(p2p->pending_client_disc_addr, dev->info.p2p_device_addr, + ETH_ALEN); + p2p->pending_action_state = P2P_PENDING_DEV_DISC_REQUEST; + if (p2p_send_action(p2p, dev->oper_freq, go->info.p2p_device_addr, + p2p->cfg->dev_addr, go->info.p2p_device_addr, + wpabuf_head(req), wpabuf_len(req), 1000) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + wpabuf_free(req); + /* TODO: how to recover from failure? */ + return -1; + } + + wpabuf_free(req); + + return 0; +} + + +static struct wpabuf * p2p_build_dev_disc_resp(u8 dialog_token, u8 status) +{ + struct wpabuf *buf; + u8 *len; + + buf = wpabuf_alloc(100); + if (buf == NULL) + return NULL; + + p2p_buf_add_public_action_hdr(buf, P2P_DEV_DISC_RESP, dialog_token); + + len = p2p_buf_add_ie_hdr(buf); + p2p_buf_add_status(buf, status); + p2p_buf_update_ie_hdr(buf, len); + + return buf; +} + + +void p2p_dev_disc_resp_cb(struct p2p_data *p2p, int success) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Device Discoverability Response TX callback: success=%d", + success); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); +} + + +static void p2p_send_dev_disc_resp(struct p2p_data *p2p, u8 dialog_token, + const u8 *addr, int freq, u8 status) +{ + struct wpabuf *resp; + + resp = p2p_build_dev_disc_resp(dialog_token, status); + if (resp == NULL) + return; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Sending Device Discoverability Response to " MACSTR + " (status %u freq %d)", + MAC2STR(addr), status, freq); + + p2p->pending_action_state = P2P_PENDING_DEV_DISC_RESPONSE; + if (p2p_send_action(p2p, freq, addr, p2p->cfg->dev_addr, + p2p->cfg->dev_addr, + wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + } + + wpabuf_free(resp); +} + + +void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + struct p2p_message msg; + size_t g; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received Device Discoverability Request from " MACSTR + " (freq=%d)", MAC2STR(sa), rx_freq); + + if (p2p_parse(data, len, &msg)) + return; + + if (msg.dialog_token == 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invalid Dialog Token 0 (must be nonzero) in " + "Device Discoverability Request"); + p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq, + P2P_SC_FAIL_INVALID_PARAMS); + p2p_parse_free(&msg); + return; + } + + if (msg.device_id == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: P2P Device ID attribute missing from Device " + "Discoverability Request"); + p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq, + P2P_SC_FAIL_INVALID_PARAMS); + p2p_parse_free(&msg); + return; + } + + for (g = 0; g < p2p->num_groups; g++) { + if (p2p_group_go_discover(p2p->groups[g], msg.device_id, sa, + rx_freq) == 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Scheduled " + "GO Discoverability Request for the target " + "device"); + /* + * P2P group code will use a callback to indicate TX + * status, so that we can reply to the request once the + * target client has acknowledged the request or it has + * timed out. + */ + p2p->pending_dev_disc_dialog_token = msg.dialog_token; + os_memcpy(p2p->pending_dev_disc_addr, sa, ETH_ALEN); + p2p->pending_dev_disc_freq = rx_freq; + p2p_parse_free(&msg); + return; + } + } + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Requested client " + "was not found in any group or did not support client " + "discoverability"); + p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq, + P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE); + p2p_parse_free(&msg); +} + + +void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len) +{ + struct p2p_message msg; + struct p2p_device *go; + u8 status; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received Device Discoverability Response from " MACSTR, + MAC2STR(sa)); + + go = p2p->pending_client_disc_go; + if (go == NULL || + os_memcmp(sa, go->info.p2p_device_addr, ETH_ALEN) != 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Ignore unexpected " + "Device Discoverability Response"); + return; + } + + if (p2p_parse(data, len, &msg)) + return; + + if (msg.status == NULL) { + p2p_parse_free(&msg); + return; + } + + if (msg.dialog_token != go->dialog_token) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Ignore Device " + "Discoverability Response with unexpected dialog " + "token %u (expected %u)", + msg.dialog_token, go->dialog_token); + p2p_parse_free(&msg); + return; + } + + status = *msg.status; + p2p_parse_free(&msg); + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Device Discoverability Response status %u", status); + + if (p2p->go_neg_peer == NULL || + os_memcmp(p2p->pending_client_disc_addr, + p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN) != 0 || + os_memcmp(p2p->go_neg_peer->member_in_go_dev, + go->info.p2p_device_addr, ETH_ALEN) != 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending " + "operation with the client discoverability peer " + "anymore"); + return; + } + + if (status == 0) { + /* + * Peer is expected to be awake for at least 100 TU; try to + * connect immediately. + */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Client discoverability request succeeded"); + if (p2p->state == P2P_CONNECT) { + /* + * Change state to force the timeout to start in + * P2P_CONNECT again without going through the short + * Listen state. + */ + p2p_set_state(p2p, P2P_CONNECT_LISTEN); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + } + p2p_set_timeout(p2p, 0, 0); + } else { + /* + * Client discoverability request failed; try to connect from + * timeout. + */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Client discoverability request failed"); + p2p_set_timeout(p2p, 0, 500000); + } + +} + + +void p2p_go_disc_req_cb(struct p2p_data *p2p, int success) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: GO Discoverability Request TX callback: success=%d", + success); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + + if (p2p->pending_dev_disc_dialog_token == 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending Device " + "Discoverability Request"); + return; + } + + p2p_send_dev_disc_resp(p2p, p2p->pending_dev_disc_dialog_token, + p2p->pending_dev_disc_addr, + p2p->pending_dev_disc_freq, + success ? P2P_SC_SUCCESS : + P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE); + + p2p->pending_dev_disc_dialog_token = 0; +} + + +void p2p_process_go_disc_req(struct p2p_data *p2p, const u8 *da, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + unsigned int tu; + struct wpabuf *ies; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received GO Discoverability Request - remain awake for " + "100 TU"); + + ies = p2p_build_probe_resp_ies(p2p); + if (ies == NULL) + return; + + /* Remain awake 100 TU on operating channel */ + p2p->pending_client_disc_freq = rx_freq; + tu = 100; + if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, rx_freq, 1024 * tu / 1000, + ies) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to start listen mode for client " + "discoverability"); + } + wpabuf_free(ies); +} diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c new file mode 100644 index 0000000000000..2fdc47fc5d0f2 --- /dev/null +++ b/src/p2p/p2p_go_neg.c @@ -0,0 +1,1242 @@ +/* + * Wi-Fi Direct - P2P Group Owner Negotiation + * Copyright (c) 2009-2010, Atheros Communications + * + * 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 "wps/wps_defs.h" +#include "p2p_i.h" +#include "p2p.h" + + +static int p2p_go_det(u8 own_intent, u8 peer_value) +{ + u8 peer_intent = peer_value >> 1; + if (own_intent == peer_intent) { + if (own_intent == P2P_MAX_GO_INTENT) + return -1; /* both devices want to become GO */ + + /* Use tie breaker bit to determine GO */ + return (peer_value & 0x01) ? 0 : 1; + } + + return own_intent > peer_intent; +} + + +int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own, + struct p2p_device *dev, + const u8 *channel_list, size_t channel_list_len) +{ + const u8 *pos, *end; + struct p2p_channels *ch; + size_t channels; + struct p2p_channels intersection; + + ch = &dev->channels; + os_memset(ch, 0, sizeof(*ch)); + pos = channel_list; + end = channel_list + channel_list_len; + + if (end - pos < 3) + return -1; + os_memcpy(dev->country, pos, 3); + wpa_hexdump_ascii(MSG_DEBUG, "P2P: Peer country", pos, 3); + if (pos[2] != 0x04 && os_memcmp(pos, p2p->cfg->country, 2) != 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, + "P2P: Mismatching country (ours=%c%c peer's=%c%c)", + p2p->cfg->country[0], p2p->cfg->country[1], + pos[0], pos[1]); + return -1; + } + pos += 3; + + while (pos + 2 < end) { + struct p2p_reg_class *cl = &ch->reg_class[ch->reg_classes]; + cl->reg_class = *pos++; + if (pos + 1 + pos[0] > end) { + wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, + "P2P: Invalid peer Channel List"); + return -1; + } + channels = *pos++; + cl->channels = channels > P2P_MAX_REG_CLASS_CHANNELS ? + P2P_MAX_REG_CLASS_CHANNELS : channels; + os_memcpy(cl->channel, pos, cl->channels); + pos += channels; + ch->reg_classes++; + if (ch->reg_classes == P2P_MAX_REG_CLASSES) + break; + } + + p2p_channels_intersect(own, &dev->channels, &intersection); + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Own reg_classes %d " + "peer reg_classes %d intersection reg_classes %d", + (int) own->reg_classes, + (int) dev->channels.reg_classes, + (int) intersection.reg_classes); + if (intersection.reg_classes == 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, + "P2P: No common channels found"); + return -1; + } + return 0; +} + + +static int p2p_peer_channels(struct p2p_data *p2p, struct p2p_device *dev, + const u8 *channel_list, size_t channel_list_len) +{ + return p2p_peer_channels_check(p2p, &p2p->channels, dev, + channel_list, channel_list_len); +} + + +u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method) +{ + switch (wps_method) { + case WPS_PIN_DISPLAY: + return DEV_PW_REGISTRAR_SPECIFIED; + case WPS_PIN_KEYPAD: + return DEV_PW_USER_SPECIFIED; + case WPS_PBC: + return DEV_PW_PUSHBUTTON; + default: + return DEV_PW_DEFAULT; + } +} + + +static const char * p2p_wps_method_str(enum p2p_wps_method wps_method) +{ + switch (wps_method) { + case WPS_PIN_DISPLAY: + return "Display"; + case WPS_PIN_KEYPAD: + return "Keypad"; + case WPS_PBC: + return "PBC"; + default: + return "??"; + } +} + + +static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, + struct p2p_device *peer) +{ + struct wpabuf *buf; + u8 *len; + u8 group_capab; + size_t extra = 0; + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_go_neg) + extra = wpabuf_len(p2p->wfd_ie_go_neg); +#endif /* CONFIG_WIFI_DISPLAY */ + + buf = wpabuf_alloc(1000 + extra); + if (buf == NULL) + return NULL; + + peer->dialog_token++; + if (peer->dialog_token == 0) + peer->dialog_token = 1; + p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_REQ, peer->dialog_token); + + len = p2p_buf_add_ie_hdr(buf); + group_capab = 0; + if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) { + group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP; + if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN) + group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN; + } + if (p2p->cross_connect) + group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; + if (p2p->cfg->p2p_intra_bss) + group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; + p2p_buf_add_capability(buf, p2p->dev_capab & + ~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; + p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout); + p2p_buf_add_listen_channel(buf, p2p->cfg->country, p2p->cfg->reg_class, + p2p->cfg->channel); + if (p2p->ext_listen_interval) + p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period, + p2p->ext_listen_interval); + p2p_buf_add_intended_addr(buf, p2p->intended_addr); + p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels); + p2p_buf_add_device_info(buf, p2p, peer); + p2p_buf_add_operating_channel(buf, p2p->cfg->country, + p2p->op_reg_class, p2p->op_channel); + p2p_buf_update_ie_hdr(buf, len); + + /* WPS IE with Device Password ID attribute */ + p2p_build_wps_ie(p2p, buf, p2p_wps_method_pw_id(peer->wps_method), 0); + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_go_neg) + wpabuf_put_buf(buf, p2p->wfd_ie_go_neg); +#endif /* CONFIG_WIFI_DISPLAY */ + + return buf; +} + + +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, 1); + } + + freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; + if (freq <= 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No Listen/Operating frequency known for the " + "peer " MACSTR " to send GO Negotiation Request", + MAC2STR(dev->info.p2p_device_addr)); + return -1; + } + + req = p2p_build_go_neg_req(p2p, dev); + if (req == NULL) + return -1; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Sending GO Negotiation Request"); + p2p_set_state(p2p, P2P_CONNECT); + p2p->pending_action_state = P2P_PENDING_GO_NEG_REQUEST; + p2p->go_neg_peer = dev; + dev->flags |= P2P_DEV_WAIT_GO_NEG_RESPONSE; + dev->connect_reqs++; + if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, + p2p->cfg->dev_addr, dev->info.p2p_device_addr, + wpabuf_head(req), wpabuf_len(req), 200) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + /* Use P2P find to recover and retry */ + p2p_set_timeout(p2p, 0, 0); + } else + dev->go_neg_req_sent++; + + wpabuf_free(req); + + return 0; +} + + +static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, + struct p2p_device *peer, + u8 dialog_token, u8 status, + u8 tie_breaker) +{ + struct wpabuf *buf; + u8 *len; + u8 group_capab; + size_t extra = 0; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Building GO Negotiation Response"); + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_go_neg) + extra = wpabuf_len(p2p->wfd_ie_go_neg); +#endif /* CONFIG_WIFI_DISPLAY */ + + buf = wpabuf_alloc(1000 + extra); + if (buf == NULL) + return NULL; + + p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_RESP, dialog_token); + + len = p2p_buf_add_ie_hdr(buf); + p2p_buf_add_status(buf, status); + group_capab = 0; + if (peer && peer->go_state == LOCAL_GO) { + if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) { + group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP; + if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN) + group_capab |= + P2P_GROUP_CAPAB_PERSISTENT_RECONN; + } + if (p2p->cross_connect) + group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; + if (p2p->cfg->p2p_intra_bss) + group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; + } + p2p_buf_add_capability(buf, p2p->dev_capab & + ~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, p2p->go_timeout, p2p->client_timeout); + if (peer && peer->go_state == REMOTE_GO) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Omit Operating " + "Channel attribute"); + } else { + p2p_buf_add_operating_channel(buf, p2p->cfg->country, + p2p->op_reg_class, + p2p->op_channel); + } + p2p_buf_add_intended_addr(buf, p2p->intended_addr); + if (status || peer == NULL) { + p2p_buf_add_channel_list(buf, p2p->cfg->country, + &p2p->channels); + } else if (peer->go_state == REMOTE_GO) { + p2p_buf_add_channel_list(buf, p2p->cfg->country, + &p2p->channels); + } else { + struct p2p_channels res; + p2p_channels_intersect(&p2p->channels, &peer->channels, + &res); + p2p_buf_add_channel_list(buf, p2p->cfg->country, &res); + } + p2p_buf_add_device_info(buf, p2p, peer); + if (peer && peer->go_state == LOCAL_GO) { + p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, p2p->ssid, + p2p->ssid_len); + } + p2p_buf_update_ie_hdr(buf, len); + + /* WPS IE with Device Password ID attribute */ + p2p_build_wps_ie(p2p, buf, + p2p_wps_method_pw_id(peer ? peer->wps_method : + WPS_NOT_READY), 0); + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_go_neg) + wpabuf_put_buf(buf, p2p->wfd_ie_go_neg); +#endif /* CONFIG_WIFI_DISPLAY */ + + + return buf; +} + + +/** + * p2p_reselect_channel - Re-select operating channel based on peer information + * @p2p: P2P module context from p2p_init() + * @intersection: Support channel list intersection from local and peer + * + * This function is used to re-select the best channel after having received + * information from the peer to allow supported channel lists to be intersected. + * This can be used to improve initial channel selection done in + * p2p_prepare_channel() prior to the start of GO Negotiation. In addition, this + * can be used for Invitation case. + */ +void p2p_reselect_channel(struct p2p_data *p2p, + struct p2p_channels *intersection) +{ + struct p2p_reg_class *cl; + int freq; + u8 op_reg_class, op_channel; + unsigned int i; + + /* First, try to pick the best channel from another band */ + freq = p2p_channel_to_freq(p2p->cfg->country, p2p->op_reg_class, + p2p->op_channel); + if (freq >= 2400 && freq < 2500 && p2p->best_freq_5 > 0 && + p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_5, + &op_reg_class, &op_channel) == 0 && + p2p_channels_includes(intersection, op_reg_class, op_channel)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick best 5 GHz " + "channel (reg_class %u channel %u) from intersection", + op_reg_class, op_channel); + p2p->op_reg_class = op_reg_class; + p2p->op_channel = op_channel; + return; + } + + if (freq >= 4900 && freq < 6000 && p2p->best_freq_24 > 0 && + p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_24, + &op_reg_class, &op_channel) == 0 && + p2p_channels_includes(intersection, op_reg_class, op_channel)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick best 2.4 GHz " + "channel (reg_class %u channel %u) from intersection", + op_reg_class, op_channel); + p2p->op_reg_class = op_reg_class; + p2p->op_channel = op_channel; + return; + } + + /* 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; + } + } + + /* Try a channel where we might be able to use HT40 */ + for (i = 0; i < intersection->reg_classes; i++) { + struct p2p_reg_class *c = &intersection->reg_class[i]; + if (c->reg_class == 116 || c->reg_class == 117 || + c->reg_class == 126 || c->reg_class == 127) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Pick possible HT40 channel (reg_class " + "%u channel %u) from intersection", + c->reg_class, c->channel[0]); + p2p->op_reg_class = c->reg_class; + p2p->op_channel = c->channel[0]; + return; + } + } + + /* + * Try to see if the original channel is in the intersection. If + * so, no need to change anything, as it already contains some + * randomness. + */ + if (p2p_channels_includes(intersection, p2p->op_reg_class, + p2p->op_channel)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Using original operating class and channel " + "(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. + */ + cl = &intersection->reg_class[0]; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick another channel " + "(reg_class %u channel %u) from intersection", + cl->reg_class, cl->channel[0]); + p2p->op_reg_class = cl->reg_class; + p2p->op_channel = cl->channel[0]; +} + + +static int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev, + u8 *status) +{ + struct p2p_channels intersection; + size_t i; + + p2p_channels_intersect(&p2p->channels, &dev->channels, &intersection); + if (intersection.reg_classes == 0 || + intersection.reg_class[0].channels == 0) { + *status = P2P_SC_FAIL_NO_COMMON_CHANNELS; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No common channels found"); + return -1; + } + + for (i = 0; i < intersection.reg_classes; i++) { + struct p2p_reg_class *c; + c = &intersection.reg_class[i]; + wpa_printf(MSG_DEBUG, "P2P: reg_class %u", c->reg_class); + wpa_hexdump(MSG_DEBUG, "P2P: channels", + c->channel, c->channels); + } + + if (!p2p_channels_includes(&intersection, p2p->op_reg_class, + p2p->op_channel)) { + if (dev->flags & P2P_DEV_FORCE_FREQ) { + *status = P2P_SC_FAIL_NO_COMMON_CHANNELS; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer does " + "not support the forced channel"); + return -1; + } + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Selected operating " + "channel (op_class %u channel %u) not acceptable to " + "the peer", p2p->op_reg_class, p2p->op_channel); + p2p_reselect_channel(p2p, &intersection); + } else if (!(dev->flags & P2P_DEV_FORCE_FREQ) && + !p2p->cfg->cfg_op_channel) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Try to optimize " + "channel selection with peer information received; " + "previously selected op_class %u channel %u", + p2p->op_reg_class, p2p->op_channel); + p2p_reselect_channel(p2p, &intersection); + } + + if (!p2p->ssid_set) { + p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len); + p2p->ssid_set = 1; + } + + return 0; +} + + +void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + struct p2p_device *dev = NULL; + struct wpabuf *resp; + struct p2p_message msg; + u8 status = P2P_SC_FAIL_INVALID_PARAMS; + int tie_breaker = 0; + int freq; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received GO Negotiation Request from " MACSTR + "(freq=%d)", MAC2STR(sa), rx_freq); + + if (p2p_parse(data, len, &msg)) + return; + + if (!msg.capability) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Mandatory Capability attribute missing from GO " + "Negotiation Request"); +#ifdef CONFIG_P2P_STRICT + goto fail; +#endif /* CONFIG_P2P_STRICT */ + } + + if (msg.go_intent) + tie_breaker = *msg.go_intent & 0x01; + else { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Mandatory GO Intent attribute missing from GO " + "Negotiation Request"); +#ifdef CONFIG_P2P_STRICT + goto fail; +#endif /* CONFIG_P2P_STRICT */ + } + + if (!msg.config_timeout) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Mandatory Configuration Timeout attribute " + "missing from GO Negotiation Request"); +#ifdef CONFIG_P2P_STRICT + goto fail; +#endif /* CONFIG_P2P_STRICT */ + } + + if (!msg.listen_channel) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No Listen Channel attribute received"); + goto fail; + } + if (!msg.operating_channel) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No Operating Channel attribute received"); + goto fail; + } + if (!msg.channel_list) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No Channel List attribute received"); + goto fail; + } + if (!msg.intended_addr) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No Intended P2P Interface Address attribute " + "received"); + goto fail; + } + if (!msg.p2p_device_info) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No P2P Device Info attribute received"); + goto fail; + } + + if (os_memcmp(msg.p2p_device_addr, sa, ETH_ALEN) != 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unexpected GO Negotiation Request SA=" MACSTR + " != dev_addr=" MACSTR, + MAC2STR(sa), MAC2STR(msg.p2p_device_addr)); + goto fail; + } + + dev = p2p_get_device(p2p, sa); + + if (msg.status && *msg.status) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unexpected Status attribute (%d) in GO " + "Negotiation Request", *msg.status); + goto fail; + } + + if (dev == NULL) + dev = p2p_add_dev_from_go_neg_req(p2p, sa, &msg); + else if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) + p2p_add_dev_info(p2p, sa, dev, &msg); + if (dev && dev->flags & P2P_DEV_USER_REJECTED) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: User has rejected this peer"); + status = P2P_SC_FAIL_REJECTED_BY_USER; + } else if (dev == NULL || dev->wps_method == WPS_NOT_READY) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Not ready for GO negotiation with " MACSTR, + MAC2STR(sa)); + status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; + if (dev) + dev->flags |= P2P_DEV_PEER_WAITING_RESPONSE; + p2p->cfg->go_neg_req_rx(p2p->cfg->cb_ctx, sa, + msg.dev_password_id); + } else if (p2p->go_neg_peer && p2p->go_neg_peer != dev) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Already in Group Formation with another peer"); + status = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; + } else { + int go; + + if (!p2p->go_neg_peer) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting " + "GO Negotiation with previously authorized " + "peer"); + if (!(dev->flags & P2P_DEV_FORCE_FREQ)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Use default channel settings"); + p2p->op_reg_class = p2p->cfg->op_reg_class; + p2p->op_channel = p2p->cfg->op_channel; + os_memcpy(&p2p->channels, &p2p->cfg->channels, + sizeof(struct p2p_channels)); + } else { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Use previously configured " + "forced channel settings"); + } + } + + dev->flags &= ~P2P_DEV_NOT_YET_READY; + + if (!msg.go_intent) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No GO Intent attribute received"); + goto fail; + } + if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invalid GO Intent value (%u) received", + *msg.go_intent >> 1); + goto fail; + } + + if (dev->go_neg_req_sent && + os_memcmp(sa, p2p->cfg->dev_addr, ETH_ALEN) > 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Do not reply since peer has higher " + "address and GO Neg Request already sent"); + p2p_parse_free(&msg); + return; + } + + go = p2p_go_det(p2p->go_intent, *msg.go_intent); + if (go < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Incompatible GO Intent"); + status = P2P_SC_FAIL_BOTH_GO_INTENT_15; + goto fail; + } + + if (p2p_peer_channels(p2p, dev, msg.channel_list, + msg.channel_list_len) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No common channels found"); + status = P2P_SC_FAIL_NO_COMMON_CHANNELS; + goto fail; + } + + switch (msg.dev_password_id) { + case DEV_PW_REGISTRAR_SPECIFIED: + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: PIN from peer Display"); + if (dev->wps_method != WPS_PIN_KEYPAD) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: We have wps_method=%s -> " + "incompatible", + p2p_wps_method_str(dev->wps_method)); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + break; + case DEV_PW_USER_SPECIFIED: + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Peer entered PIN on Keypad"); + if (dev->wps_method != WPS_PIN_DISPLAY) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: We have wps_method=%s -> " + "incompatible", + p2p_wps_method_str(dev->wps_method)); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + break; + case DEV_PW_PUSHBUTTON: + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Peer using pushbutton"); + if (dev->wps_method != WPS_PBC) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: We have wps_method=%s -> " + "incompatible", + p2p_wps_method_str(dev->wps_method)); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + break; + default: + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported Device Password ID %d", + msg.dev_password_id); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + + if (go && p2p_go_select_channel(p2p, dev, &status) < 0) + goto fail; + + dev->go_state = go ? LOCAL_GO : REMOTE_GO; + dev->oper_freq = p2p_channel_to_freq((const char *) + msg.operating_channel, + msg.operating_channel[3], + msg.operating_channel[4]); + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer operating " + "channel preference: %d MHz", dev->oper_freq); + + if (msg.config_timeout) { + dev->go_timeout = msg.config_timeout[0]; + dev->client_timeout = msg.config_timeout[1]; + } + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: GO Negotiation with " MACSTR, MAC2STR(sa)); + if (p2p->state != P2P_IDLE) + p2p_stop_find_for_freq(p2p, rx_freq); + p2p_set_state(p2p, P2P_GO_NEG); + p2p_clear_timeout(p2p); + dev->dialog_token = msg.dialog_token; + os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN); + p2p->go_neg_peer = dev; + status = P2P_SC_SUCCESS; + } + +fail: + if (dev) + dev->status = status; + resp = p2p_build_go_neg_resp(p2p, dev, msg.dialog_token, status, + !tie_breaker); + p2p_parse_free(&msg); + if (resp == NULL) + return; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Sending GO Negotiation Response"); + if (rx_freq > 0) + freq = rx_freq; + else + freq = p2p_channel_to_freq(p2p->cfg->country, + p2p->cfg->reg_class, + p2p->cfg->channel); + if (freq < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unknown regulatory class/channel"); + wpabuf_free(resp); + return; + } + if (status == P2P_SC_SUCCESS) { + p2p->pending_action_state = P2P_PENDING_GO_NEG_RESPONSE; + dev->flags |= P2P_DEV_WAIT_GO_NEG_CONFIRM; + 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; + if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, + p2p->cfg->dev_addr, + wpabuf_head(resp), wpabuf_len(resp), 250) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + } + + wpabuf_free(resp); +} + + +static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p, + struct p2p_device *peer, + u8 dialog_token, u8 status, + const u8 *resp_chan, int go) +{ + struct wpabuf *buf; + u8 *len; + struct p2p_channels res; + u8 group_capab; + size_t extra = 0; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Building GO Negotiation Confirm"); + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_go_neg) + extra = wpabuf_len(p2p->wfd_ie_go_neg); +#endif /* CONFIG_WIFI_DISPLAY */ + + buf = wpabuf_alloc(1000 + extra); + if (buf == NULL) + return NULL; + + p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_CONF, dialog_token); + + len = p2p_buf_add_ie_hdr(buf); + p2p_buf_add_status(buf, status); + group_capab = 0; + if (peer->go_state == LOCAL_GO) { + if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) { + group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP; + if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN) + group_capab |= + P2P_GROUP_CAPAB_PERSISTENT_RECONN; + } + if (p2p->cross_connect) + group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; + if (p2p->cfg->p2p_intra_bss) + group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; + } + p2p_buf_add_capability(buf, p2p->dev_capab & + ~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, + p2p->op_channel); + else + p2p_buf_add_operating_channel(buf, (const char *) resp_chan, + resp_chan[3], resp_chan[4]); + p2p_channels_intersect(&p2p->channels, &peer->channels, &res); + p2p_buf_add_channel_list(buf, p2p->cfg->country, &res); + if (go) { + p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, p2p->ssid, + p2p->ssid_len); + } + p2p_buf_update_ie_hdr(buf, len); + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_go_neg) + wpabuf_put_buf(buf, p2p->wfd_ie_go_neg); +#endif /* CONFIG_WIFI_DISPLAY */ + + return buf; +} + + +void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + struct p2p_device *dev; + struct wpabuf *conf; + int go = -1; + struct p2p_message msg; + u8 status = P2P_SC_SUCCESS; + int freq; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received GO Negotiation Response from " MACSTR + " (freq=%d)", MAC2STR(sa), rx_freq); + dev = p2p_get_device(p2p, sa); + if (dev == NULL || dev->wps_method == WPS_NOT_READY || + dev != p2p->go_neg_peer) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Not ready for GO negotiation with " MACSTR, + MAC2STR(sa)); + return; + } + + if (p2p_parse(data, len, &msg)) + return; + + if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_RESPONSE)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Was not expecting GO Negotiation Response - " + "ignore"); + p2p_parse_free(&msg); + return; + } + dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE; + + if (msg.dialog_token != dev->dialog_token) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unexpected Dialog Token %u (expected %u)", + msg.dialog_token, dev->dialog_token); + p2p_parse_free(&msg); + return; + } + + if (!msg.status) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No Status attribute received"); + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; + } + if (*msg.status) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: GO Negotiation rejected: status %d", + *msg.status); + dev->go_neg_req_sent = 0; + if (*msg.status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Wait for the peer to become ready for " + "GO Negotiation"); + dev->flags |= P2P_DEV_NOT_YET_READY; + dev->wait_count = 0; + p2p_set_state(p2p, P2P_WAIT_PEER_IDLE); + p2p_set_timeout(p2p, 0, 0); + } else { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Stop GO Negotiation attempt"); + p2p_go_neg_failed(p2p, dev, *msg.status); + } + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p_parse_free(&msg); + return; + } + + if (!msg.capability) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Mandatory Capability attribute missing from GO " + "Negotiation Response"); +#ifdef CONFIG_P2P_STRICT + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; +#endif /* CONFIG_P2P_STRICT */ + } + + if (!msg.p2p_device_info) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Mandatory P2P Device Info attribute missing " + "from GO Negotiation Response"); +#ifdef CONFIG_P2P_STRICT + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; +#endif /* CONFIG_P2P_STRICT */ + } + + if (!msg.intended_addr) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No Intended P2P Interface Address attribute " + "received"); + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; + } + + if (!msg.go_intent) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No GO Intent attribute received"); + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; + } + if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invalid GO Intent value (%u) received", + *msg.go_intent >> 1); + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; + } + + go = p2p_go_det(p2p->go_intent, *msg.go_intent); + if (go < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Incompatible GO Intent"); + status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + goto fail; + } + + if (!go && msg.group_id) { + /* Store SSID for Provisioning step */ + p2p->ssid_len = msg.group_id_len - ETH_ALEN; + os_memcpy(p2p->ssid, msg.group_id + ETH_ALEN, p2p->ssid_len); + } else if (!go) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Mandatory P2P Group ID attribute missing from " + "GO Negotiation Response"); + p2p->ssid_len = 0; +#ifdef CONFIG_P2P_STRICT + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; +#endif /* CONFIG_P2P_STRICT */ + } + + if (!msg.config_timeout) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Mandatory Configuration Timeout attribute " + "missing from GO Negotiation Response"); +#ifdef CONFIG_P2P_STRICT + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; +#endif /* CONFIG_P2P_STRICT */ + } else { + dev->go_timeout = msg.config_timeout[0]; + dev->client_timeout = msg.config_timeout[1]; + } + + if (!msg.operating_channel && !go) { + /* + * Note: P2P Client may omit Operating Channel attribute to + * indicate it does not have a preference. + */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No Operating Channel attribute received"); + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; + } + if (!msg.channel_list) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No Channel List attribute received"); + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; + } + + if (p2p_peer_channels(p2p, dev, msg.channel_list, + msg.channel_list_len) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No common channels found"); + status = P2P_SC_FAIL_NO_COMMON_CHANNELS; + goto fail; + } + + if (msg.operating_channel) { + dev->oper_freq = p2p_channel_to_freq((const char *) + msg.operating_channel, + msg.operating_channel[3], + msg.operating_channel[4]); + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer operating " + "channel preference: %d MHz", dev->oper_freq); + } else + dev->oper_freq = 0; + + switch (msg.dev_password_id) { + case DEV_PW_REGISTRAR_SPECIFIED: + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: PIN from peer Display"); + if (dev->wps_method != WPS_PIN_KEYPAD) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: We have wps_method=%s -> " + "incompatible", + p2p_wps_method_str(dev->wps_method)); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + break; + case DEV_PW_USER_SPECIFIED: + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Peer entered PIN on Keypad"); + if (dev->wps_method != WPS_PIN_DISPLAY) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: We have wps_method=%s -> " + "incompatible", + p2p_wps_method_str(dev->wps_method)); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + break; + case DEV_PW_PUSHBUTTON: + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Peer using pushbutton"); + if (dev->wps_method != WPS_PBC) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: We have wps_method=%s -> " + "incompatible", + p2p_wps_method_str(dev->wps_method)); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + break; + default: + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported Device Password ID %d", + msg.dev_password_id); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + + if (go && p2p_go_select_channel(p2p, dev, &status) < 0) + goto fail; + + p2p_set_state(p2p, P2P_GO_NEG); + p2p_clear_timeout(p2p); + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: GO Negotiation with " MACSTR, MAC2STR(sa)); + os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN); + +fail: + conf = p2p_build_go_neg_conf(p2p, dev, msg.dialog_token, status, + msg.operating_channel, go); + p2p_parse_free(&msg); + if (conf == NULL) + return; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Sending GO Negotiation Confirm"); + if (status == P2P_SC_SUCCESS) { + p2p->pending_action_state = P2P_PENDING_GO_NEG_CONFIRM; + dev->go_state = go ? LOCAL_GO : REMOTE_GO; + } else + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + if (rx_freq > 0) + freq = rx_freq; + else + freq = dev->listen_freq; + if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, sa, + wpabuf_head(conf), wpabuf_len(conf), 0) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + p2p_go_neg_failed(p2p, dev, -1); + } + wpabuf_free(conf); +} + + +void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len) +{ + struct p2p_device *dev; + struct p2p_message msg; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received GO Negotiation Confirm from " MACSTR, + MAC2STR(sa)); + dev = p2p_get_device(p2p, sa); + if (dev == NULL || dev->wps_method == WPS_NOT_READY || + dev != p2p->go_neg_peer) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Not ready for GO negotiation with " MACSTR, + MAC2STR(sa)); + return; + } + + if (p2p->pending_action_state == P2P_PENDING_GO_NEG_RESPONSE) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Stopped waiting " + "for TX status on GO Negotiation Response since we " + "already received Confirmation"); + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + } + + if (p2p_parse(data, len, &msg)) + return; + + if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Was not expecting GO Negotiation Confirm - " + "ignore"); + return; + } + dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM; + + if (msg.dialog_token != dev->dialog_token) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unexpected Dialog Token %u (expected %u)", + msg.dialog_token, dev->dialog_token); + p2p_parse_free(&msg); + return; + } + + if (!msg.status) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No Status attribute received"); + p2p_parse_free(&msg); + return; + } + if (*msg.status) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: GO Negotiation rejected: status %d", + *msg.status); + p2p_parse_free(&msg); + return; + } + + if (dev->go_state == REMOTE_GO && msg.group_id) { + /* Store SSID for Provisioning step */ + p2p->ssid_len = msg.group_id_len - ETH_ALEN; + os_memcpy(p2p->ssid, msg.group_id + ETH_ALEN, p2p->ssid_len); + } else if (dev->go_state == REMOTE_GO) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Mandatory P2P Group ID attribute missing from " + "GO Negotiation Confirmation"); + p2p->ssid_len = 0; +#ifdef CONFIG_P2P_STRICT + p2p_parse_free(&msg); + return; +#endif /* CONFIG_P2P_STRICT */ + } + + if (!msg.operating_channel) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Mandatory Operating Channel attribute missing " + "from GO Negotiation Confirmation"); +#ifdef CONFIG_P2P_STRICT + p2p_parse_free(&msg); + return; +#endif /* CONFIG_P2P_STRICT */ + } + + if (!msg.channel_list) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Mandatory Operating Channel attribute missing " + "from GO Negotiation Confirmation"); +#ifdef CONFIG_P2P_STRICT + p2p_parse_free(&msg); + return; +#endif /* CONFIG_P2P_STRICT */ + } + + p2p_parse_free(&msg); + + if (dev->go_state == UNKNOWN_GO) { + /* + * This should not happen since GO negotiation has already + * been completed. + */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unexpected GO Neg state - do not know which end " + "becomes GO"); + return; + } + + /* + * The peer could have missed our ctrl::ack frame for GO Negotiation + * Confirm and continue retransmitting the frame. To reduce the + * likelihood of the peer not getting successful TX status for the + * GO Negotiation Confirm frame, wait a short time here before starting + * the group so that we will remain on the current channel to + * acknowledge any possible retransmission from the peer. + */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: 20 ms wait on current " + "channel before starting group"); + os_sleep(0, 20000); + + p2p_go_complete(p2p, dev); +} diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c new file mode 100644 index 0000000000000..86873205b8301 --- /dev/null +++ b/src/p2p/p2p_group.c @@ -0,0 +1,953 @@ +/* + * Wi-Fi Direct - P2P group operations + * Copyright (c) 2009-2010, Atheros Communications + * + * 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/ieee802_11_common.h" +#include "wps/wps_defs.h" +#include "wps/wps_i.h" +#include "p2p_i.h" +#include "p2p.h" + + +struct p2p_group_member { + struct p2p_group_member *next; + u8 addr[ETH_ALEN]; /* P2P Interface Address */ + u8 dev_addr[ETH_ALEN]; /* P2P Device Address */ + struct wpabuf *p2p_ie; + struct wpabuf *wfd_ie; + struct wpabuf *client_info; + u8 dev_capab; +}; + +/** + * struct p2p_group - Internal P2P module per-group data + */ +struct p2p_group { + struct p2p_data *p2p; + struct p2p_group_config *cfg; + struct p2p_group_member *members; + unsigned int num_members; + int group_formation; + int beacon_update; + struct wpabuf *noa; + struct wpabuf *wfd_ie; +}; + + +struct p2p_group * p2p_group_init(struct p2p_data *p2p, + struct p2p_group_config *config) +{ + struct p2p_group *group, **groups; + + group = os_zalloc(sizeof(*group)); + if (group == NULL) + return NULL; + + groups = os_realloc_array(p2p->groups, p2p->num_groups + 1, + sizeof(struct p2p_group *)); + if (groups == NULL) { + os_free(group); + return NULL; + } + groups[p2p->num_groups++] = group; + p2p->groups = groups; + + group->p2p = p2p; + group->cfg = config; + group->group_formation = 1; + group->beacon_update = 1; + p2p_group_update_ies(group); + group->cfg->idle_update(group->cfg->cb_ctx, 1); + + return group; +} + + +static void p2p_group_free_member(struct p2p_group_member *m) +{ + wpabuf_free(m->wfd_ie); + wpabuf_free(m->p2p_ie); + wpabuf_free(m->client_info); + os_free(m); +} + + +static void p2p_group_free_members(struct p2p_group *group) +{ + struct p2p_group_member *m, *prev; + m = group->members; + group->members = NULL; + group->num_members = 0; + while (m) { + prev = m; + m = m->next; + p2p_group_free_member(prev); + } +} + + +void p2p_group_deinit(struct p2p_group *group) +{ + size_t g; + struct p2p_data *p2p; + + if (group == NULL) + return; + + p2p = group->p2p; + + for (g = 0; g < p2p->num_groups; g++) { + if (p2p->groups[g] == group) { + while (g + 1 < p2p->num_groups) { + p2p->groups[g] = p2p->groups[g + 1]; + g++; + } + p2p->num_groups--; + break; + } + } + + p2p_group_free_members(group); + os_free(group->cfg); + wpabuf_free(group->noa); + wpabuf_free(group->wfd_ie); + os_free(group); +} + + +static void p2p_client_info(struct wpabuf *ie, struct p2p_group_member *m) +{ + if (m->client_info == NULL) + return; + if (wpabuf_tailroom(ie) < wpabuf_len(m->client_info) + 1) + return; + wpabuf_put_buf(ie, m->client_info); +} + + +static void p2p_group_add_common_ies(struct p2p_group *group, + struct wpabuf *ie) +{ + u8 dev_capab = group->p2p->dev_capab, group_capab = 0; + + /* P2P Capability */ + 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; + if (group->cfg->persistent_group == 2) + group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN; + } + if (group->p2p->cfg->p2p_intra_bss) + group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; + if (group->group_formation) + group_capab |= P2P_GROUP_CAPAB_GROUP_FORMATION; + if (group->p2p->cross_connect) + group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; + if (group->num_members >= group->cfg->max_clients) + group_capab |= P2P_GROUP_CAPAB_GROUP_LIMIT; + p2p_buf_add_capability(ie, dev_capab, group_capab); +} + + +static void p2p_group_add_noa(struct wpabuf *ie, struct wpabuf *noa) +{ + if (noa == NULL) + return; + /* Notice of Absence */ + wpabuf_put_u8(ie, P2P_ATTR_NOTICE_OF_ABSENCE); + wpabuf_put_le16(ie, wpabuf_len(noa)); + wpabuf_put_buf(ie, noa); +} + + +static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group) +{ + struct wpabuf *ie; + u8 *len; + size_t extra = 0; + +#ifdef CONFIG_WIFI_DISPLAY + if (group->p2p->wfd_ie_beacon) + extra = wpabuf_len(group->p2p->wfd_ie_beacon); +#endif /* CONFIG_WIFI_DISPLAY */ + + ie = wpabuf_alloc(257 + extra); + if (ie == NULL) + return NULL; + +#ifdef CONFIG_WIFI_DISPLAY + if (group->p2p->wfd_ie_beacon) + wpabuf_put_buf(ie, group->p2p->wfd_ie_beacon); +#endif /* CONFIG_WIFI_DISPLAY */ + + len = p2p_buf_add_ie_hdr(ie); + p2p_group_add_common_ies(group, ie); + p2p_buf_add_device_id(ie, group->p2p->cfg->dev_addr); + p2p_group_add_noa(ie, group->noa); + p2p_buf_update_ie_hdr(ie, len); + + return ie; +} + + +#ifdef CONFIG_WIFI_DISPLAY + +struct wpabuf * p2p_group_get_wfd_ie(struct p2p_group *g) +{ + return g->wfd_ie; +} + + +struct wpabuf * wifi_display_encaps(struct wpabuf *subelems) +{ + struct wpabuf *ie; + const u8 *pos, *end; + + if (subelems == NULL) + return NULL; + + ie = wpabuf_alloc(wpabuf_len(subelems) + 100); + if (ie == NULL) + return NULL; + + pos = wpabuf_head(subelems); + end = pos + wpabuf_len(subelems); + + while (end > pos) { + size_t frag_len = end - pos; + if (frag_len > 251) + frag_len = 251; + wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(ie, 4 + frag_len); + wpabuf_put_be32(ie, WFD_IE_VENDOR_TYPE); + wpabuf_put_data(ie, pos, frag_len); + pos += frag_len; + } + + return ie; +} + + +static int wifi_display_add_dev_info_descr(struct wpabuf *buf, + struct p2p_group_member *m) +{ + const u8 *pos, *end; + const u8 *dev_info = NULL; + const u8 *assoc_bssid = NULL; + const u8 *coupled_sink = NULL; + u8 zero_addr[ETH_ALEN]; + + if (m->wfd_ie == NULL) + return 0; + + os_memset(zero_addr, 0, ETH_ALEN); + pos = wpabuf_head_u8(m->wfd_ie); + end = pos + wpabuf_len(m->wfd_ie); + while (pos + 1 < end) { + u8 id; + u16 len; + + id = *pos++; + len = WPA_GET_BE16(pos); + pos += 2; + if (pos + len > end) + break; + + switch (id) { + case WFD_SUBELEM_DEVICE_INFO: + if (len < 6) + break; + dev_info = pos; + break; + case WFD_SUBELEM_ASSOCIATED_BSSID: + if (len < ETH_ALEN) + break; + assoc_bssid = pos; + break; + case WFD_SUBELEM_COUPLED_SINK: + if (len < 1 + ETH_ALEN) + break; + coupled_sink = pos; + break; + } + + pos += len; + } + + if (dev_info == NULL) + return 0; + + wpabuf_put_u8(buf, 23); + wpabuf_put_data(buf, m->dev_addr, ETH_ALEN); + if (assoc_bssid) + wpabuf_put_data(buf, assoc_bssid, ETH_ALEN); + else + wpabuf_put_data(buf, zero_addr, ETH_ALEN); + wpabuf_put_data(buf, dev_info, 2); /* WFD Device Info */ + wpabuf_put_data(buf, dev_info + 4, 2); /* WFD Device Max Throughput */ + if (coupled_sink) { + wpabuf_put_data(buf, coupled_sink, 1 + ETH_ALEN); + } else { + wpabuf_put_u8(buf, 0); + wpabuf_put_data(buf, zero_addr, ETH_ALEN); + } + + return 1; +} + + +static struct wpabuf * +wifi_display_build_go_ie(struct p2p_group *group) +{ + struct wpabuf *wfd_subelems, *wfd_ie; + struct p2p_group_member *m; + u8 *len; + unsigned int count = 0; + + if (!group->p2p->wfd_ie_probe_resp) + return NULL; + + wfd_subelems = wpabuf_alloc(wpabuf_len(group->p2p->wfd_ie_probe_resp) + + group->num_members * 24 + 100); + if (wfd_subelems == NULL) + return NULL; + if (group->p2p->wfd_dev_info) + wpabuf_put_buf(wfd_subelems, group->p2p->wfd_dev_info); + if (group->p2p->wfd_assoc_bssid) + wpabuf_put_buf(wfd_subelems, + group->p2p->wfd_assoc_bssid); + if (group->p2p->wfd_coupled_sink_info) + wpabuf_put_buf(wfd_subelems, + group->p2p->wfd_coupled_sink_info); + + /* Build WFD Session Info */ + wpabuf_put_u8(wfd_subelems, WFD_SUBELEM_SESSION_INFO); + len = wpabuf_put(wfd_subelems, 2); + m = group->members; + while (m) { + if (wifi_display_add_dev_info_descr(wfd_subelems, m)) + count++; + m = m->next; + } + + if (count == 0) { + /* No Wi-Fi Display clients - do not include subelement */ + wfd_subelems->used -= 3; + } else { + WPA_PUT_BE16(len, (u8 *) wpabuf_put(wfd_subelems, 0) - len - + 2); + wpa_printf(MSG_DEBUG, "WFD: WFD Session Info: %u descriptors", + count); + } + + wfd_ie = wifi_display_encaps(wfd_subelems); + wpabuf_free(wfd_subelems); + + return wfd_ie; +} + +static void wifi_display_group_update(struct p2p_group *group) +{ + wpabuf_free(group->wfd_ie); + group->wfd_ie = wifi_display_build_go_ie(group); +} + +#endif /* CONFIG_WIFI_DISPLAY */ + + +static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group) +{ + u8 *group_info; + struct wpabuf *ie; + struct p2p_group_member *m; + u8 *len; + size_t extra = 0; + +#ifdef CONFIG_WIFI_DISPLAY + if (group->wfd_ie) + extra += wpabuf_len(group->wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ + + ie = wpabuf_alloc(257 + extra); + if (ie == NULL) + return NULL; + +#ifdef CONFIG_WIFI_DISPLAY + if (group->wfd_ie) + wpabuf_put_buf(ie, group->wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ + + len = p2p_buf_add_ie_hdr(ie); + + p2p_group_add_common_ies(group, ie); + p2p_group_add_noa(ie, group->noa); + + /* P2P Device Info */ + p2p_buf_add_device_info(ie, group->p2p, NULL); + + /* P2P Group Info */ + group_info = wpabuf_put(ie, 0); + wpabuf_put_u8(ie, P2P_ATTR_GROUP_INFO); + wpabuf_put_le16(ie, 0); /* Length to be filled */ + for (m = group->members; m; m = m->next) + p2p_client_info(ie, m); + WPA_PUT_LE16(group_info + 1, + (u8 *) wpabuf_put(ie, 0) - group_info - 3); + + p2p_buf_update_ie_hdr(ie, len); + + return ie; +} + + +void p2p_group_update_ies(struct p2p_group *group) +{ + struct wpabuf *beacon_ie; + struct wpabuf *probe_resp_ie; + +#ifdef CONFIG_WIFI_DISPLAY + wifi_display_group_update(group); +#endif /* CONFIG_WIFI_DISPLAY */ + + probe_resp_ie = p2p_group_build_probe_resp_ie(group); + if (probe_resp_ie == NULL) + return; + wpa_hexdump_buf(MSG_MSGDUMP, "P2P: Update GO Probe Response P2P IE", + probe_resp_ie); + + if (group->beacon_update) { + beacon_ie = p2p_group_build_beacon_ie(group); + if (beacon_ie) + group->beacon_update = 0; + wpa_hexdump_buf(MSG_MSGDUMP, "P2P: Update GO Beacon P2P IE", + beacon_ie); + } else + beacon_ie = NULL; + + group->cfg->ie_update(group->cfg->cb_ctx, beacon_ie, probe_resp_ie); +} + + +/** + * p2p_build_client_info - Build P2P Client Info Descriptor + * @addr: MAC address of the peer device + * @p2p_ie: P2P IE from (Re)Association Request + * @dev_capab: Buffer for returning Device Capability + * @dev_addr: Buffer for returning P2P Device Address + * Returns: P2P Client Info Descriptor or %NULL on failure + * + * This function builds P2P Client Info Descriptor based on the information + * available from (Re)Association Request frame. Group owner can use this to + * build the P2P Group Info attribute for Probe Response frames. + */ +static struct wpabuf * p2p_build_client_info(const u8 *addr, + struct wpabuf *p2p_ie, + u8 *dev_capab, u8 *dev_addr) +{ + const u8 *spos; + struct p2p_message msg; + u8 *len_pos; + struct wpabuf *buf; + + if (p2p_ie == NULL) + return NULL; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_p2p_ie(p2p_ie, &msg) || + msg.capability == NULL || msg.p2p_device_info == NULL) + return NULL; + + buf = wpabuf_alloc(ETH_ALEN + 1 + 1 + msg.p2p_device_info_len); + if (buf == NULL) + return NULL; + + *dev_capab = msg.capability[0]; + os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN); + + spos = msg.p2p_device_info; /* P2P Device address */ + + /* P2P Client Info Descriptor */ + /* Length to be set */ + len_pos = wpabuf_put(buf, 1); + /* P2P Device address */ + wpabuf_put_data(buf, spos, ETH_ALEN); + /* P2P Interface address */ + wpabuf_put_data(buf, addr, ETH_ALEN); + /* Device Capability Bitmap */ + wpabuf_put_u8(buf, msg.capability[0]); + /* + * Config Methods, Primary Device Type, Number of Secondary Device + * Types, Secondary Device Type List, Device Name copied from + * Device Info + */ + wpabuf_put_data(buf, spos + ETH_ALEN, + msg.p2p_device_info_len - ETH_ALEN); + + *len_pos = wpabuf_len(buf) - 1; + + + return buf; +} + + +static int p2p_group_remove_member(struct p2p_group *group, const u8 *addr) +{ + struct p2p_group_member *m, *prev; + + if (group == NULL) + return 0; + + m = group->members; + prev = NULL; + while (m) { + if (os_memcmp(m->addr, addr, ETH_ALEN) == 0) + break; + prev = m; + m = m->next; + } + + if (m == NULL) + return 0; + + if (prev) + prev->next = m->next; + else + group->members = m->next; + p2p_group_free_member(m); + group->num_members--; + + return 1; +} + + +int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr, + const u8 *ie, size_t len) +{ + struct p2p_group_member *m; + + if (group == NULL) + return -1; + + m = os_zalloc(sizeof(*m)); + if (m == NULL) + return -1; + os_memcpy(m->addr, addr, ETH_ALEN); + m->p2p_ie = ieee802_11_vendor_ie_concat(ie, len, P2P_IE_VENDOR_TYPE); + if (m->p2p_ie) { + m->client_info = p2p_build_client_info(addr, m->p2p_ie, + &m->dev_capab, + m->dev_addr); + } +#ifdef CONFIG_WIFI_DISPLAY + m->wfd_ie = ieee802_11_vendor_ie_concat(ie, len, WFD_IE_VENDOR_TYPE); +#endif /* CONFIG_WIFI_DISPLAY */ + + p2p_group_remove_member(group, addr); + + m->next = group->members; + group->members = m; + group->num_members++; + wpa_msg(group->p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Add client " MACSTR + " to group (p2p=%d wfd=%d client_info=%d); num_members=%u/%u", + MAC2STR(addr), m->p2p_ie ? 1 : 0, m->wfd_ie ? 1 : 0, + m->client_info ? 1 : 0, + group->num_members, group->cfg->max_clients); + if (group->num_members == group->cfg->max_clients) + group->beacon_update = 1; + p2p_group_update_ies(group); + if (group->num_members == 1) + group->cfg->idle_update(group->cfg->cb_ctx, 0); + + return 0; +} + + +struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status) +{ + struct wpabuf *resp; + u8 *rlen; + size_t extra = 0; + +#ifdef CONFIG_WIFI_DISPLAY + if (group->wfd_ie) + extra = wpabuf_len(group->wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ + + /* + * (Re)Association Response - P2P IE + * Status attribute (shall be present when association request is + * denied) + * Extended Listen Timing (may be present) + */ + resp = wpabuf_alloc(20 + extra); + if (resp == NULL) + return NULL; + +#ifdef CONFIG_WIFI_DISPLAY + if (group->wfd_ie) + wpabuf_put_buf(resp, group->wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ + + rlen = p2p_buf_add_ie_hdr(resp); + if (status != P2P_SC_SUCCESS) + p2p_buf_add_status(resp, status); + p2p_buf_update_ie_hdr(resp, rlen); + + return resp; +} + + +void p2p_group_notif_disassoc(struct p2p_group *group, const u8 *addr) +{ + if (p2p_group_remove_member(group, addr)) { + wpa_msg(group->p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Remove " + "client " MACSTR " from group; num_members=%u/%u", + MAC2STR(addr), group->num_members, + group->cfg->max_clients); + if (group->num_members == group->cfg->max_clients - 1) + group->beacon_update = 1; + p2p_group_update_ies(group); + if (group->num_members == 0) + group->cfg->idle_update(group->cfg->cb_ctx, 1); + } +} + + +/** + * p2p_match_dev_type_member - Match client device type with requested type + * @m: Group member + * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs) + * Returns: 1 on match, 0 on mismatch + * + * This function can be used to match the Requested Device Type attribute in + * WPS IE with the device types of a group member for deciding whether a GO + * should reply to a Probe Request frame. + */ +static int p2p_match_dev_type_member(struct p2p_group_member *m, + struct wpabuf *wps) +{ + const u8 *pos, *end; + struct wps_parse_attr attr; + u8 num_sec; + + if (m->client_info == NULL || wps == NULL) + return 0; + + pos = wpabuf_head(m->client_info); + end = pos + wpabuf_len(m->client_info); + + pos += 1 + 2 * ETH_ALEN + 1 + 2; + if (end - pos < WPS_DEV_TYPE_LEN + 1) + return 0; + + if (wps_parse_msg(wps, &attr)) + return 1; /* assume no Requested Device Type attributes */ + + if (attr.num_req_dev_type == 0) + return 1; /* no Requested Device Type attributes -> match */ + + if (dev_type_list_match(pos, attr.req_dev_type, attr.num_req_dev_type)) + return 1; /* Match with client Primary Device Type */ + + pos += WPS_DEV_TYPE_LEN; + num_sec = *pos++; + if (end - pos < num_sec * WPS_DEV_TYPE_LEN) + return 0; + while (num_sec > 0) { + num_sec--; + if (dev_type_list_match(pos, attr.req_dev_type, + attr.num_req_dev_type)) + return 1; /* Match with client Secondary Device Type */ + pos += WPS_DEV_TYPE_LEN; + } + + /* No matching device type found */ + return 0; +} + + +int p2p_group_match_dev_type(struct p2p_group *group, struct wpabuf *wps) +{ + struct p2p_group_member *m; + + if (p2p_match_dev_type(group->p2p, wps)) + return 1; /* Match with own device type */ + + for (m = group->members; m; m = m->next) { + if (p2p_match_dev_type_member(m, wps)) + return 1; /* Match with group client device type */ + } + + /* No match with Requested Device Type */ + return 0; +} + + +int p2p_group_match_dev_id(struct p2p_group *group, struct wpabuf *p2p) +{ + struct p2p_group_member *m; + struct p2p_message msg; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_p2p_ie(p2p, &msg)) + return 1; /* Failed to parse - assume no filter on Device ID */ + + if (!msg.device_id) + return 1; /* No filter on Device ID */ + + if (os_memcmp(msg.device_id, group->p2p->cfg->dev_addr, ETH_ALEN) == 0) + return 1; /* Match with our P2P Device Address */ + + for (m = group->members; m; m = m->next) { + if (os_memcmp(msg.device_id, m->dev_addr, ETH_ALEN) == 0) + return 1; /* Match with group client P2P Device Address */ + } + + /* No match with Device ID */ + return 0; +} + + +void p2p_group_notif_formation_done(struct p2p_group *group) +{ + if (group == NULL) + return; + group->group_formation = 0; + group->beacon_update = 1; + p2p_group_update_ies(group); +} + + +int p2p_group_notif_noa(struct p2p_group *group, const u8 *noa, + size_t noa_len) +{ + if (noa == NULL) { + wpabuf_free(group->noa); + group->noa = NULL; + } else { + if (group->noa) { + if (wpabuf_size(group->noa) >= noa_len) { + group->noa->used = 0; + wpabuf_put_data(group->noa, noa, noa_len); + } else { + wpabuf_free(group->noa); + group->noa = NULL; + } + } + + if (!group->noa) { + group->noa = wpabuf_alloc_copy(noa, noa_len); + if (group->noa == NULL) + return -1; + } + } + + group->beacon_update = 1; + p2p_group_update_ies(group); + return 0; +} + + +static struct p2p_group_member * p2p_group_get_client(struct p2p_group *group, + const u8 *dev_id) +{ + struct p2p_group_member *m; + + for (m = group->members; m; m = m->next) { + if (os_memcmp(dev_id, m->dev_addr, ETH_ALEN) == 0) + return m; + } + + return NULL; +} + + +static struct p2p_group_member * p2p_group_get_client_iface( + struct p2p_group *group, const u8 *interface_addr) +{ + struct p2p_group_member *m; + + for (m = group->members; m; m = m->next) { + if (os_memcmp(interface_addr, m->addr, ETH_ALEN) == 0) + return m; + } + + return NULL; +} + + +const u8 * p2p_group_get_dev_addr(struct p2p_group *group, const u8 *addr) +{ + struct p2p_group_member *m; + + if (group == NULL) + return NULL; + m = p2p_group_get_client_iface(group, addr); + if (m && !is_zero_ether_addr(m->dev_addr)) + return m->dev_addr; + return NULL; +} + + +static struct wpabuf * p2p_build_go_disc_req(void) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(100); + if (buf == NULL) + return NULL; + + p2p_buf_add_action_hdr(buf, P2P_GO_DISC_REQ, 0); + + return buf; +} + + +int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id, + const u8 *searching_dev, int rx_freq) +{ + struct p2p_group_member *m; + struct wpabuf *req; + struct p2p_data *p2p = group->p2p; + int freq; + + m = p2p_group_get_client(group, dev_id); + if (m == NULL || m->client_info == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Requested client was not in this " + "group " MACSTR, + MAC2STR(group->cfg->interface_addr)); + return -1; + } + + if (!(m->dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { + wpa_printf(MSG_DEBUG, "P2P: Requested client does not support " + "client discoverability"); + return -1; + } + + wpa_printf(MSG_DEBUG, "P2P: Schedule GO Discoverability Request to be " + "sent to " MACSTR, MAC2STR(dev_id)); + + req = p2p_build_go_disc_req(); + if (req == NULL) + return -1; + + /* TODO: Should really use group operating frequency here */ + freq = rx_freq; + + p2p->pending_action_state = P2P_PENDING_GO_DISC_REQ; + if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, m->addr, + group->cfg->interface_addr, + group->cfg->interface_addr, + wpabuf_head(req), wpabuf_len(req), 200) < 0) + { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + } + + wpabuf_free(req); + + return 0; +} + + +const u8 * p2p_group_get_interface_addr(struct p2p_group *group) +{ + return group->cfg->interface_addr; +} + + +u8 p2p_group_presence_req(struct p2p_group *group, + const u8 *client_interface_addr, + const u8 *noa, size_t noa_len) +{ + struct p2p_group_member *m; + u8 curr_noa[50]; + int curr_noa_len; + + m = p2p_group_get_client_iface(group, client_interface_addr); + if (m == NULL || m->client_info == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Client was not in this group"); + return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; + } + + wpa_hexdump(MSG_DEBUG, "P2P: Presence Request NoA", noa, noa_len); + + if (group->p2p->cfg->get_noa) + curr_noa_len = group->p2p->cfg->get_noa( + group->p2p->cfg->cb_ctx, group->cfg->interface_addr, + curr_noa, sizeof(curr_noa)); + else + curr_noa_len = -1; + if (curr_noa_len < 0) + wpa_printf(MSG_DEBUG, "P2P: Failed to fetch current NoA"); + else if (curr_noa_len == 0) + wpa_printf(MSG_DEBUG, "P2P: No NoA being advertized"); + else + wpa_hexdump(MSG_DEBUG, "P2P: Current NoA", curr_noa, + curr_noa_len); + + /* TODO: properly process request and store copy */ + if (curr_noa_len > 0 || curr_noa_len == -1) + return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; + + return P2P_SC_SUCCESS; +} + + +unsigned int p2p_get_group_num_members(struct p2p_group *group) +{ + return group->num_members; +} + + +const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next) +{ + struct p2p_group_member *iter = *next; + + if (!iter) + iter = group->members; + else + iter = iter->next; + + *next = iter; + + if (!iter) + return NULL; + + return iter->addr; +} + + +int p2p_group_is_client_connected(struct p2p_group *group, const u8 *dev_addr) +{ + struct p2p_group_member *m; + + for (m = group->members; m; m = m->next) { + if (os_memcmp(m->dev_addr, dev_addr, ETH_ALEN) == 0) + return 1; + } + + 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 new file mode 100644 index 0000000000000..27fef0182941a --- /dev/null +++ b/src/p2p/p2p_i.h @@ -0,0 +1,714 @@ +/* + * P2P - Internal definitions for P2P module + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef P2P_I_H +#define P2P_I_H + +#include "utils/list.h" +#include "p2p.h" + +enum p2p_go_state { + UNKNOWN_GO, + LOCAL_GO, + REMOTE_GO +}; + +/** + * struct p2p_device - P2P Device data (internal to P2P module) + */ +struct p2p_device { + struct dl_list list; + struct os_time last_seen; + int listen_freq; + enum p2p_wps_method wps_method; + + struct p2p_peer_info info; + + /* + * If the peer was discovered based on an interface address (e.g., GO + * from Beacon/Probe Response), the interface address is stored here. + * p2p_device_addr must still be set in such a case to the unique + * identifier for the P2P Device. + */ + u8 interface_addr[ETH_ALEN]; + + /* + * P2P Device Address of the GO in whose group this P2P Device is a + * client. + */ + u8 member_in_go_dev[ETH_ALEN]; + + /* + * P2P Interface Address of the GO in whose group this P2P Device is a + * client. + */ + u8 member_in_go_iface[ETH_ALEN]; + + int go_neg_req_sent; + enum p2p_go_state go_state; + u8 dialog_token; + u8 intended_addr[ETH_ALEN]; + + char country[3]; + struct p2p_channels channels; + int oper_freq; + u8 oper_ssid[32]; + size_t oper_ssid_len; + + /** + * req_config_methods - Pending provision discovery methods + */ + u16 req_config_methods; + + /** + * wps_prov_info - Stored provisioning WPS config method + * + * This is used to store pending WPS config method between Provisioning + * Discovery and connection to a running group. + */ + u16 wps_prov_info; + +#define P2P_DEV_PROBE_REQ_ONLY BIT(0) +#define P2P_DEV_REPORTED BIT(1) +#define P2P_DEV_NOT_YET_READY BIT(2) +#define P2P_DEV_SD_INFO BIT(3) +#define P2P_DEV_SD_SCHEDULE BIT(4) +#define P2P_DEV_PD_PEER_DISPLAY BIT(5) +#define P2P_DEV_PD_PEER_KEYPAD BIT(6) +#define P2P_DEV_USER_REJECTED BIT(7) +#define P2P_DEV_PEER_WAITING_RESPONSE BIT(8) +#define P2P_DEV_PREFER_PERSISTENT_GROUP BIT(9) +#define P2P_DEV_WAIT_GO_NEG_RESPONSE BIT(10) +#define P2P_DEV_WAIT_GO_NEG_CONFIRM BIT(11) +#define P2P_DEV_GROUP_CLIENT_ONLY BIT(12) +#define P2P_DEV_FORCE_FREQ BIT(13) +#define P2P_DEV_PD_FOR_JOIN BIT(14) +#define P2P_DEV_REPORTED_ONCE BIT(15) +#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 */ + unsigned int wait_count; + unsigned int connect_reqs; + unsigned int invitation_reqs; + + u16 ext_listen_period; + u16 ext_listen_interval; + + u8 go_timeout; + u8 client_timeout; +}; + +struct p2p_sd_query { + struct p2p_sd_query *next; + u8 peer[ETH_ALEN]; + int for_all_peers; + int wsd; /* Wi-Fi Display Service Discovery Request */ + struct wpabuf *tlvs; +}; + +struct p2p_pending_action_tx { + unsigned int freq; + u8 dst[ETH_ALEN]; + u8 src[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + size_t len; + unsigned int wait_time; + /* Followed by len octets of the frame */ +}; + +/** + * struct p2p_data - P2P module data (internal to P2P module) + */ +struct p2p_data { + /** + * cfg - P2P module configuration + * + * This is included in the same memory allocation with the + * struct p2p_data and as such, must not be freed separately. + */ + struct p2p_config *cfg; + + /** + * state - The current P2P state + */ + enum p2p_state { + /** + * P2P_IDLE - Idle + */ + P2P_IDLE, + + /** + * P2P_SEARCH - Search (Device Discovery) + */ + P2P_SEARCH, + + /** + * P2P_CONNECT - Trying to start GO Negotiation + */ + P2P_CONNECT, + + /** + * P2P_CONNECT_LISTEN - Listen during GO Negotiation start + */ + P2P_CONNECT_LISTEN, + + /** + * P2P_GO_NEG - In GO Negotiation + */ + P2P_GO_NEG, + + /** + * P2P_LISTEN_ONLY - Listen only + */ + P2P_LISTEN_ONLY, + + /** + * P2P_WAIT_PEER_CONNECT - Waiting peer in List for GO Neg + */ + P2P_WAIT_PEER_CONNECT, + + /** + * P2P_WAIT_PEER_IDLE - Waiting peer idle for GO Neg + */ + P2P_WAIT_PEER_IDLE, + + /** + * P2P_SD_DURING_FIND - Service Discovery during find + */ + P2P_SD_DURING_FIND, + + /** + * P2P_PROVISIONING - Provisioning (during group formation) + */ + P2P_PROVISIONING, + + /** + * P2P_PD_DURING_FIND - Provision Discovery during find + */ + P2P_PD_DURING_FIND, + + /** + * P2P_INVITE - Trying to start Invite + */ + P2P_INVITE, + + /** + * P2P_INVITE_LISTEN - Listen during Invite + */ + P2P_INVITE_LISTEN, + + /** + * P2P_SEARCH_WHEN_READY - Waiting to start Search + */ + P2P_SEARCH_WHEN_READY, + + /** + * P2P_CONTINUE_SEARCH_WHEN_READY - Waiting to continue Search + */ + P2P_CONTINUE_SEARCH_WHEN_READY, + } state; + + /** + * min_disc_int - minDiscoverableInterval + */ + int min_disc_int; + + /** + * max_disc_int - maxDiscoverableInterval + */ + int max_disc_int; + + /** + * max_disc_tu - Maximum number of TUs for discoverable interval + */ + int max_disc_tu; + + /** + * devices - List of known P2P Device peers + */ + struct dl_list devices; + + /** + * go_neg_peer - Pointer to GO Negotiation peer + */ + struct p2p_device *go_neg_peer; + + /** + * invite_peer - Pointer to Invite peer + */ + struct p2p_device *invite_peer; + + const u8 *invite_go_dev_addr; + u8 invite_go_dev_addr_buf[ETH_ALEN]; + + /** + * sd_peer - Pointer to Service Discovery peer + */ + struct p2p_device *sd_peer; + + /** + * sd_query - Pointer to Service Discovery query + */ + struct p2p_sd_query *sd_query; + + /* GO Negotiation data */ + + /** + * intended_addr - Local Intended P2P Interface Address + * + * This address is used during group owner negotiation as the Intended + * P2P Interface Address and the group interface will be created with + * address as the local address in case of successfully completed + * negotiation. + */ + u8 intended_addr[ETH_ALEN]; + + /** + * go_intent - Local GO Intent to be used during GO Negotiation + */ + u8 go_intent; + + /** + * next_tie_breaker - Next tie-breaker value to use in GO Negotiation + */ + u8 next_tie_breaker; + + /** + * ssid - Selected SSID for GO Negotiation (if local end will be GO) + */ + u8 ssid[32]; + + /** + * ssid_len - ssid length in octets + */ + size_t ssid_len; + + /** + * ssid_set - Whether SSID is already set for GO Negotiation + */ + int ssid_set; + + /** + * Regulatory class for own operational channel + */ + u8 op_reg_class; + + /** + * op_channel - Own operational channel + */ + u8 op_channel; + + /** + * channels - Own supported regulatory classes and channels + * + * List of supposerted channels per regulatory class. The regulatory + * classes are defined in IEEE Std 802.11-2007 Annex J and the + * numbering of the clases depends on the configured country code. + */ + struct p2p_channels channels; + + enum p2p_pending_action_state { + P2P_NO_PENDING_ACTION, + P2P_PENDING_GO_NEG_REQUEST, + P2P_PENDING_GO_NEG_RESPONSE, + P2P_PENDING_GO_NEG_RESPONSE_FAILURE, + P2P_PENDING_GO_NEG_CONFIRM, + P2P_PENDING_SD, + P2P_PENDING_PD, + P2P_PENDING_INVITATION_REQUEST, + P2P_PENDING_INVITATION_RESPONSE, + P2P_PENDING_DEV_DISC_REQUEST, + P2P_PENDING_DEV_DISC_RESPONSE, + P2P_PENDING_GO_DISC_REQ + } pending_action_state; + + unsigned int pending_listen_freq; + unsigned int pending_listen_sec; + unsigned int pending_listen_usec; + + u8 dev_capab; + + int in_listen; + int drv_in_listen; + + /** + * sd_queries - Pending service discovery queries + */ + struct p2p_sd_query *sd_queries; + + /** + * srv_update_indic - Service Update Indicator for local services + */ + u16 srv_update_indic; + + struct wpabuf *sd_resp; /* Fragmented SD response */ + u8 sd_resp_addr[ETH_ALEN]; + u8 sd_resp_dialog_token; + size_t sd_resp_pos; /* Offset in sd_resp */ + u8 sd_frag_id; + + struct wpabuf *sd_rx_resp; /* Reassembled SD response */ + u16 sd_rx_update_indic; + + /* P2P Invitation data */ + enum p2p_invite_role inv_role; + u8 inv_bssid[ETH_ALEN]; + int inv_bssid_set; + u8 inv_ssid[32]; + size_t inv_ssid_len; + u8 inv_sa[ETH_ALEN]; + u8 inv_group_bssid[ETH_ALEN]; + u8 *inv_group_bssid_ptr; + u8 inv_go_dev_addr[ETH_ALEN]; + u8 inv_status; + int inv_op_freq; + int inv_persistent; + + enum p2p_discovery_type find_type; + unsigned int last_p2p_find_timeout; + u8 last_prog_scan_class; + u8 last_prog_scan_chan; + int p2p_scan_running; + enum p2p_after_scan { + P2P_AFTER_SCAN_NOTHING, + P2P_AFTER_SCAN_LISTEN, + P2P_AFTER_SCAN_CONNECT + } start_after_scan; + u8 after_scan_peer[ETH_ALEN]; + struct p2p_pending_action_tx *after_scan_tx; + + /* Requested device types for find/search */ + unsigned int num_req_dev_types; + u8 *req_dev_types; + u8 *find_dev_id; + u8 find_dev_id_buf[ETH_ALEN]; + + struct p2p_group **groups; + size_t num_groups; + + struct p2p_device *pending_client_disc_go; + u8 pending_client_disc_addr[ETH_ALEN]; + u8 pending_dev_disc_dialog_token; + u8 pending_dev_disc_addr[ETH_ALEN]; + int pending_dev_disc_freq; + unsigned int pending_client_disc_freq; + + int ext_listen_only; + unsigned int ext_listen_period; + unsigned int ext_listen_interval; + unsigned int ext_listen_interval_sec; + unsigned int ext_listen_interval_usec; + + u8 peer_filter[ETH_ALEN]; + + int cross_connect; + + int best_freq_24; + int best_freq_5; + int best_freq_overall; + + /** + * wps_vendor_ext - WPS Vendor Extensions to add + */ + struct wpabuf *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT]; + + /* + * user_initiated_pd - Whether a PD request is user initiated or not. + */ + u8 user_initiated_pd; + + /* + * Keep track of which peer a given PD request was sent to. + * Used to raise a timeout alert in case there is no response. + */ + u8 pending_pd_devaddr[ETH_ALEN]; + + /* + * Retry counter for provision discovery requests when issued + * in IDLE state. + */ + int pd_retries; + + u8 go_timeout; + u8 client_timeout; + + /* Extra delay in milliseconds between search iterations */ + unsigned int search_delay; + int in_search_delay; + +#ifdef CONFIG_WIFI_DISPLAY + struct wpabuf *wfd_ie_beacon; + struct wpabuf *wfd_ie_probe_req; + struct wpabuf *wfd_ie_probe_resp; + struct wpabuf *wfd_ie_assoc_req; + struct wpabuf *wfd_ie_invitation; + struct wpabuf *wfd_ie_prov_disc_req; + struct wpabuf *wfd_ie_prov_disc_resp; + struct wpabuf *wfd_ie_go_neg; + struct wpabuf *wfd_dev_info; + struct wpabuf *wfd_assoc_bssid; + struct wpabuf *wfd_coupled_sink_info; +#endif /* CONFIG_WIFI_DISPLAY */ +}; + +/** + * struct p2p_message - Parsed P2P message (or P2P IE) + */ +struct p2p_message { + struct wpabuf *p2p_attributes; + struct wpabuf *wps_attributes; + struct wpabuf *wfd_subelems; + + u8 dialog_token; + + const u8 *capability; + const u8 *go_intent; + const u8 *status; + const u8 *listen_channel; + const u8 *operating_channel; + const u8 *channel_list; + u8 channel_list_len; + const u8 *config_timeout; + const u8 *intended_addr; + const u8 *group_bssid; + const u8 *invitation_flags; + + const u8 *group_info; + size_t group_info_len; + + const u8 *group_id; + size_t group_id_len; + + const u8 *device_id; + + const u8 *manageability; + + const u8 *noa; + size_t noa_len; + + const u8 *ext_listen_timing; + + const u8 *minor_reason_code; + + /* P2P Device Info */ + const u8 *p2p_device_info; + size_t p2p_device_info_len; + const u8 *p2p_device_addr; + const u8 *pri_dev_type; + u8 num_sec_dev_types; + char device_name[33]; + u16 config_methods; + + /* WPS IE */ + u16 dev_password_id; + u16 wps_config_methods; + const u8 *wps_pri_dev_type; + const u8 *wps_sec_dev_type_list; + size_t wps_sec_dev_type_list_len; + const u8 *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT]; + size_t wps_vendor_ext_len[P2P_MAX_WPS_VENDOR_EXT]; + const u8 *manufacturer; + size_t manufacturer_len; + const u8 *model_name; + size_t model_name_len; + const u8 *model_number; + size_t model_number_len; + const u8 *serial_number; + size_t serial_number_len; + + /* DS Parameter Set IE */ + const u8 *ds_params; + + /* SSID IE */ + const u8 *ssid; +}; + + +#define P2P_MAX_GROUP_ENTRIES 50 + +struct p2p_group_info { + unsigned int num_clients; + struct p2p_client_info { + const u8 *p2p_device_addr; + const u8 *p2p_interface_addr; + u8 dev_capab; + u16 config_methods; + const u8 *pri_dev_type; + u8 num_sec_dev_types; + const u8 *sec_dev_types; + const char *dev_name; + size_t dev_name_len; + } client[P2P_MAX_GROUP_ENTRIES]; +}; + + +/* p2p_utils.c */ +int p2p_random(char *buf, size_t len); +int p2p_channel_to_freq(const char *country, int reg_class, int channel); +int p2p_freq_to_channel(const char *country, unsigned int freq, u8 *reg_class, + u8 *channel); +void p2p_channels_intersect(const struct p2p_channels *a, + const struct p2p_channels *b, + struct p2p_channels *res); +int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class, + u8 channel); + +/* p2p_parse.c */ +int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg); +int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg); +int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg); +void p2p_parse_free(struct p2p_message *msg); +int p2p_attr_text(struct wpabuf *data, char *buf, char *end); +int p2p_group_info_parse(const u8 *gi, size_t gi_len, + struct p2p_group_info *info); + +/* p2p_build.c */ + +struct p2p_noa_desc { + u8 count_type; + u32 duration; + u32 interval; + u32 start_time; +}; + +/* p2p_group.c */ +const u8 * p2p_group_get_interface_addr(struct p2p_group *group); +u8 p2p_group_presence_req(struct p2p_group *group, + const u8 *client_interface_addr, + const u8 *noa, size_t noa_len); +int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id, + size_t group_id_len); +void p2p_group_update_ies(struct p2p_group *group); +struct wpabuf * p2p_group_get_wfd_ie(struct p2p_group *g); + + +void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token); +void p2p_buf_add_public_action_hdr(struct wpabuf *buf, u8 subtype, + u8 dialog_token); +u8 * p2p_buf_add_ie_hdr(struct wpabuf *buf); +void p2p_buf_add_status(struct wpabuf *buf, u8 status); +void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p, + struct p2p_device *peer); +void p2p_buf_add_device_id(struct wpabuf *buf, const u8 *dev_addr); +void p2p_buf_update_ie_hdr(struct wpabuf *buf, u8 *len); +void p2p_buf_add_capability(struct wpabuf *buf, u8 dev_capab, u8 group_capab); +void p2p_buf_add_go_intent(struct wpabuf *buf, u8 go_intent); +void p2p_buf_add_listen_channel(struct wpabuf *buf, const char *country, + u8 reg_class, u8 channel); +void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country, + u8 reg_class, u8 channel); +void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country, + struct p2p_channels *chan); +void p2p_buf_add_config_timeout(struct wpabuf *buf, u8 go_timeout, + u8 client_timeout); +void p2p_buf_add_intended_addr(struct wpabuf *buf, const u8 *interface_addr); +void p2p_buf_add_group_bssid(struct wpabuf *buf, const u8 *bssid); +void p2p_buf_add_group_id(struct wpabuf *buf, const u8 *dev_addr, + const u8 *ssid, size_t ssid_len); +void p2p_buf_add_invitation_flags(struct wpabuf *buf, u8 flags); +void p2p_buf_add_noa(struct wpabuf *buf, u8 noa_index, u8 opp_ps, u8 ctwindow, + struct p2p_noa_desc *desc1, struct p2p_noa_desc *desc2); +void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period, + u16 interval); +void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p); +void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, + int all_attr); + +/* p2p_sd.c */ +struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p, + struct p2p_device *dev); +void p2p_free_sd_queries(struct p2p_data *p2p); +void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq); +void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq); +void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq); +void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq); +int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev); + +/* p2p_go_neg.c */ +int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own, + struct p2p_device *dev, + const u8 *channel_list, size_t channel_list_len); +void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq); +void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq); +void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len); +int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev); +u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method); +void p2p_reselect_channel(struct p2p_data *p2p, + struct p2p_channels *intersection); + +/* p2p_pd.c */ +void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq); +void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len); +int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, + int join, int force_freq); +void p2p_reset_pending_pd(struct p2p_data *p2p); + +/* p2p_invitation.c */ +void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq); +void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len); +int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev, + const u8 *go_dev_addr); +void p2p_invitation_req_cb(struct p2p_data *p2p, int success); +void p2p_invitation_resp_cb(struct p2p_data *p2p, int success); + +/* p2p_dev_disc.c */ +void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq); +void p2p_dev_disc_req_cb(struct p2p_data *p2p, int success); +int p2p_send_dev_disc_req(struct p2p_data *p2p, struct p2p_device *dev); +void p2p_dev_disc_resp_cb(struct p2p_data *p2p, int success); +void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len); +void p2p_go_disc_req_cb(struct p2p_data *p2p, int success); +void p2p_process_go_disc_req(struct p2p_data *p2p, const u8 *da, const u8 *sa, + const u8 *data, size_t len, int rx_freq); + +/* p2p.c */ +void p2p_set_state(struct p2p_data *p2p, int new_state); +void p2p_set_timeout(struct p2p_data *p2p, unsigned int sec, + unsigned int usec); +void p2p_clear_timeout(struct p2p_data *p2p); +void p2p_continue_find(struct p2p_data *p2p); +struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p, + const u8 *addr, + struct p2p_message *msg); +void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr, + struct p2p_device *dev, struct p2p_message *msg); +int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, + unsigned int age_ms, int level, 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); +void p2p_go_neg_failed(struct p2p_data *p2p, struct p2p_device *peer, + int status); +void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer); +int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps); +int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[], + size_t num_req_dev_type); +struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p); +void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len); +int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, const u8 *buf, + size_t len, unsigned int wait_time); +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 new file mode 100644 index 0000000000000..983dd6b36e405 --- /dev/null +++ b/src/p2p/p2p_invitation.c @@ -0,0 +1,608 @@ +/* + * Wi-Fi Direct - P2P Invitation procedure + * Copyright (c) 2010, Atheros Communications + * + * 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 "p2p_i.h" +#include "p2p.h" + + +static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p, + struct p2p_device *peer, + const u8 *go_dev_addr) +{ + struct wpabuf *buf; + u8 *len; + const u8 *dev_addr; + size_t extra = 0; + +#ifdef CONFIG_WIFI_DISPLAY + struct wpabuf *wfd_ie = p2p->wfd_ie_invitation; + if (wfd_ie && p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO) { + size_t i; + for (i = 0; i < p2p->num_groups; i++) { + struct p2p_group *g = p2p->groups[i]; + struct wpabuf *ie; + if (os_memcmp(p2p_group_get_interface_addr(g), + p2p->inv_bssid, ETH_ALEN) != 0) + continue; + ie = p2p_group_get_wfd_ie(g); + if (ie) { + wfd_ie = ie; + break; + } + } + } + if (wfd_ie) + extra = wpabuf_len(wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ + + buf = wpabuf_alloc(1000 + extra); + if (buf == NULL) + return NULL; + + peer->dialog_token++; + if (peer->dialog_token == 0) + peer->dialog_token = 1; + p2p_buf_add_public_action_hdr(buf, P2P_INVITATION_REQ, + peer->dialog_token); + + len = p2p_buf_add_ie_hdr(buf); + if (p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO || !p2p->inv_persistent) + p2p_buf_add_config_timeout(buf, 0, 0); + else + p2p_buf_add_config_timeout(buf, p2p->go_timeout, + p2p->client_timeout); + p2p_buf_add_invitation_flags(buf, p2p->inv_persistent ? + P2P_INVITATION_FLAGS_TYPE : 0); + p2p_buf_add_operating_channel(buf, p2p->cfg->country, + p2p->op_reg_class, p2p->op_channel); + if (p2p->inv_bssid_set) + p2p_buf_add_group_bssid(buf, p2p->inv_bssid); + p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels); + if (go_dev_addr) + dev_addr = go_dev_addr; + else if (p2p->inv_role == P2P_INVITE_ROLE_CLIENT) + dev_addr = peer->info.p2p_device_addr; + else + dev_addr = p2p->cfg->dev_addr; + p2p_buf_add_group_id(buf, dev_addr, p2p->inv_ssid, p2p->inv_ssid_len); + p2p_buf_add_device_info(buf, p2p, peer); + p2p_buf_update_ie_hdr(buf, len); + +#ifdef CONFIG_WIFI_DISPLAY + if (wfd_ie) + wpabuf_put_buf(buf, wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ + + return buf; +} + + +static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p, + struct p2p_device *peer, + u8 dialog_token, u8 status, + const u8 *group_bssid, + u8 reg_class, u8 channel, + struct p2p_channels *channels) +{ + struct wpabuf *buf; + u8 *len; + size_t extra = 0; + +#ifdef CONFIG_WIFI_DISPLAY + struct wpabuf *wfd_ie = p2p->wfd_ie_invitation; + if (wfd_ie && group_bssid) { + size_t i; + for (i = 0; i < p2p->num_groups; i++) { + struct p2p_group *g = p2p->groups[i]; + struct wpabuf *ie; + if (os_memcmp(p2p_group_get_interface_addr(g), + group_bssid, ETH_ALEN) != 0) + continue; + ie = p2p_group_get_wfd_ie(g); + if (ie) { + wfd_ie = ie; + break; + } + } + } + if (wfd_ie) + extra = wpabuf_len(wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ + + buf = wpabuf_alloc(1000 + extra); + if (buf == NULL) + return NULL; + + p2p_buf_add_public_action_hdr(buf, P2P_INVITATION_RESP, + dialog_token); + + len = p2p_buf_add_ie_hdr(buf); + p2p_buf_add_status(buf, status); + p2p_buf_add_config_timeout(buf, 0, 0); /* FIX */ + if (reg_class && channel) + p2p_buf_add_operating_channel(buf, p2p->cfg->country, + reg_class, channel); + if (group_bssid) + p2p_buf_add_group_bssid(buf, group_bssid); + if (channels) + p2p_buf_add_channel_list(buf, p2p->cfg->country, channels); + p2p_buf_update_ie_hdr(buf, len); + +#ifdef CONFIG_WIFI_DISPLAY + if (wfd_ie) + wpabuf_put_buf(buf, wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ + + return buf; +} + + +void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + struct p2p_device *dev; + struct p2p_message msg; + struct wpabuf *resp = NULL; + u8 status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; + int freq; + int go = 0; + u8 group_bssid[ETH_ALEN], *bssid; + int op_freq = 0; + u8 reg_class = 0, channel = 0; + struct p2p_channels intersection, *channels = NULL; + int persistent; + + os_memset(group_bssid, 0, sizeof(group_bssid)); + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received Invitation Request from " MACSTR " (freq=%d)", + MAC2STR(sa), rx_freq); + + if (p2p_parse(data, len, &msg)) + return; + + dev = p2p_get_device(p2p, sa); + if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invitation Request from unknown peer " + MACSTR, MAC2STR(sa)); + + if (p2p_add_device(p2p, sa, rx_freq, 0, 0, data + 1, len - 1, + 0)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invitation Request add device failed " + MACSTR, MAC2STR(sa)); + status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; + goto fail; + } + + dev = p2p_get_device(p2p, sa); + if (dev == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Reject Invitation Request from unknown " + "peer " MACSTR, MAC2STR(sa)); + status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; + goto fail; + } + } + + if (!msg.group_id || !msg.channel_list) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Mandatory attribute missing in Invitation " + "Request from " MACSTR, MAC2STR(sa)); + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; + } + + if (msg.invitation_flags) + persistent = *msg.invitation_flags & P2P_INVITATION_FLAGS_TYPE; + else { + /* Invitation Flags is a mandatory attribute starting from P2P + * spec 1.06. As a backwards compatibility mechanism, assume + * the request was for a persistent group if the attribute is + * missing. + */ + wpa_printf(MSG_DEBUG, "P2P: Mandatory Invitation Flags " + "attribute missing from Invitation Request"); + persistent = 1; + } + + if (p2p_peer_channels_check(p2p, &p2p->cfg->channels, dev, + msg.channel_list, msg.channel_list_len) < + 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No common channels found"); + status = P2P_SC_FAIL_NO_COMMON_CHANNELS; + goto fail; + } + + if (p2p->cfg->invitation_process) { + status = p2p->cfg->invitation_process( + p2p->cfg->cb_ctx, sa, msg.group_bssid, msg.group_id, + msg.group_id + ETH_ALEN, msg.group_id_len - ETH_ALEN, + &go, group_bssid, &op_freq, persistent); + } + + if (op_freq) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Invitation " + "processing forced frequency %d MHz", op_freq); + if (p2p_freq_to_channel(p2p->cfg->country, op_freq, + ®_class, &channel) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unknown forced freq %d MHz from " + "invitation_process()", op_freq); + status = P2P_SC_FAIL_NO_COMMON_CHANNELS; + goto fail; + } + + p2p_channels_intersect(&p2p->cfg->channels, &dev->channels, + &intersection); + if (!p2p_channels_includes(&intersection, reg_class, channel)) + { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: forced freq %d MHz not in the supported " + "channels interaction", op_freq); + status = P2P_SC_FAIL_NO_COMMON_CHANNELS; + goto fail; + } + + if (status == P2P_SC_SUCCESS) + channels = &intersection; + } else { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No forced channel from invitation processing - " + "figure out best one to use"); + + p2p_channels_intersect(&p2p->cfg->channels, &dev->channels, + &intersection); + /* Default to own configuration as a starting point */ + p2p->op_reg_class = p2p->cfg->op_reg_class; + p2p->op_channel = p2p->cfg->op_channel; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Own default " + "op_class %d channel %d", + p2p->op_reg_class, p2p->op_channel); + + /* Use peer preference if specified and compatible */ + if (msg.operating_channel) { + int req_freq; + req_freq = p2p_channel_to_freq( + (const char *) msg.operating_channel, + msg.operating_channel[3], + msg.operating_channel[4]); + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " + "operating channel preference: %d MHz", + req_freq); + if (req_freq > 0 && + p2p_channels_includes(&intersection, + msg.operating_channel[3], + msg.operating_channel[4])) { + p2p->op_reg_class = msg.operating_channel[3]; + p2p->op_channel = msg.operating_channel[4]; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Use peer preference op_class %d " + "channel %d", + p2p->op_reg_class, p2p->op_channel); + } else { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Cannot use peer channel " + "preference"); + } + } + + if (!p2p_channels_includes(&intersection, p2p->op_reg_class, + p2p->op_channel)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Initially selected channel (op_class %d " + "channel %d) not in channel intersection - try " + "to reselect", + p2p->op_reg_class, p2p->op_channel); + p2p_reselect_channel(p2p, &intersection); + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Re-selection result: op_class %d " + "channel %d", + p2p->op_reg_class, p2p->op_channel); + if (!p2p_channels_includes(&intersection, + p2p->op_reg_class, + p2p->op_channel)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Peer does not support selected " + "operating channel (reg_class=%u " + "channel=%u)", + p2p->op_reg_class, p2p->op_channel); + status = P2P_SC_FAIL_NO_COMMON_CHANNELS; + goto fail; + } + } + + op_freq = p2p_channel_to_freq(p2p->cfg->country, + p2p->op_reg_class, + p2p->op_channel); + if (op_freq < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unknown operational channel " + "(country=%c%c reg_class=%u channel=%u)", + p2p->cfg->country[0], p2p->cfg->country[1], + p2p->op_reg_class, p2p->op_channel); + status = P2P_SC_FAIL_NO_COMMON_CHANNELS; + goto fail; + } + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Selected operating " + "channel - %d MHz", op_freq); + + if (status == P2P_SC_SUCCESS) { + reg_class = p2p->op_reg_class; + channel = p2p->op_channel; + channels = &intersection; + } + } + +fail: + if (go && status == P2P_SC_SUCCESS && !is_zero_ether_addr(group_bssid)) + bssid = group_bssid; + else + bssid = NULL; + resp = p2p_build_invitation_resp(p2p, dev, msg.dialog_token, status, + bssid, reg_class, channel, channels); + + if (resp == NULL) + goto out; + + if (rx_freq > 0) + freq = rx_freq; + else + freq = p2p_channel_to_freq(p2p->cfg->country, + p2p->cfg->reg_class, + p2p->cfg->channel); + if (freq < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unknown regulatory class/channel"); + goto out; + } + + /* + * Store copy of invitation data to be used when processing TX status + * callback for the Acton frame. + */ + os_memcpy(p2p->inv_sa, sa, ETH_ALEN); + if (msg.group_bssid) { + os_memcpy(p2p->inv_group_bssid, msg.group_bssid, ETH_ALEN); + p2p->inv_group_bssid_ptr = p2p->inv_group_bssid; + } else + p2p->inv_group_bssid_ptr = NULL; + if (msg.group_id_len - ETH_ALEN <= 32) { + os_memcpy(p2p->inv_ssid, msg.group_id + ETH_ALEN, + msg.group_id_len - ETH_ALEN); + p2p->inv_ssid_len = msg.group_id_len - ETH_ALEN; + } + os_memcpy(p2p->inv_go_dev_addr, msg.group_id, ETH_ALEN); + p2p->inv_status = status; + p2p->inv_op_freq = op_freq; + + p2p->pending_action_state = P2P_PENDING_INVITATION_RESPONSE; + if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, + p2p->cfg->dev_addr, + wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + } + +out: + wpabuf_free(resp); + p2p_parse_free(&msg); +} + + +void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len) +{ + struct p2p_device *dev; + struct p2p_message msg; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received Invitation Response from " MACSTR, + MAC2STR(sa)); + + dev = p2p_get_device(p2p, sa); + if (dev == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Ignore Invitation Response from unknown peer " + MACSTR, MAC2STR(sa)); + return; + } + + if (dev != p2p->invite_peer) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Ignore unexpected Invitation Response from peer " + MACSTR, MAC2STR(sa)); + return; + } + + if (p2p_parse(data, len, &msg)) + return; + + if (!msg.status) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Mandatory Status attribute missing in " + "Invitation Response from " MACSTR, MAC2STR(sa)); + p2p_parse_free(&msg); + return; + } + + if (p2p->cfg->invitation_result) + p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status, + msg.group_bssid); + + p2p_parse_free(&msg); + + p2p_clear_timeout(p2p); + p2p_set_state(p2p, P2P_IDLE); + p2p->invite_peer = NULL; +} + + +int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev, + const u8 *go_dev_addr) +{ + struct wpabuf *req; + int freq; + + freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; + if (freq <= 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No Listen/Operating frequency known for the " + "peer " MACSTR " to send Invitation Request", + MAC2STR(dev->info.p2p_device_addr)); + return -1; + } + + req = p2p_build_invitation_req(p2p, dev, go_dev_addr); + if (req == NULL) + return -1; + 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); + p2p->pending_action_state = P2P_PENDING_INVITATION_REQUEST; + p2p->invite_peer = dev; + dev->invitation_reqs++; + if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, + p2p->cfg->dev_addr, dev->info.p2p_device_addr, + wpabuf_head(req), wpabuf_len(req), 200) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + /* Use P2P find to recover and retry */ + p2p_set_timeout(p2p, 0, 0); + } + + wpabuf_free(req); + + return 0; +} + + +void p2p_invitation_req_cb(struct p2p_data *p2p, int success) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invitation Request TX callback: success=%d", success); + + if (p2p->invite_peer == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No pending Invite"); + return; + } + + /* + * Use P2P find, if needed, to find the other device from its listen + * channel. + */ + p2p_set_state(p2p, P2P_INVITE); + p2p_set_timeout(p2p, 0, 100000); +} + + +void p2p_invitation_resp_cb(struct p2p_data *p2p, int success) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invitation Response TX callback: success=%d", success); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + + if (success && p2p->cfg->invitation_received) { + p2p->cfg->invitation_received(p2p->cfg->cb_ctx, + p2p->inv_sa, + p2p->inv_group_bssid_ptr, + p2p->inv_ssid, p2p->inv_ssid_len, + p2p->inv_go_dev_addr, + p2p->inv_status, + p2p->inv_op_freq); + } +} + + +int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role, + const u8 *bssid, const u8 *ssid, size_t ssid_len, + unsigned int force_freq, const u8 *go_dev_addr, + int persistent_group) +{ + struct p2p_device *dev; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Request to invite peer " MACSTR " role=%d persistent=%d " + "force_freq=%u", + MAC2STR(peer), role, persistent_group, force_freq); + if (bssid) + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invitation for BSSID " MACSTR, MAC2STR(bssid)); + if (go_dev_addr) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invitation for GO Device Address " MACSTR, + MAC2STR(go_dev_addr)); + os_memcpy(p2p->invite_go_dev_addr_buf, go_dev_addr, ETH_ALEN); + p2p->invite_go_dev_addr = p2p->invite_go_dev_addr_buf; + } else + p2p->invite_go_dev_addr = NULL; + wpa_hexdump_ascii(MSG_DEBUG, "P2P: Invitation for SSID", + ssid, ssid_len); + + dev = p2p_get_device(p2p, peer); + if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Cannot invite unknown P2P Device " MACSTR, + MAC2STR(peer)); + return -1; + } + + if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) { + if (!(dev->info.dev_capab & + P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Cannot invite a P2P Device " MACSTR + " that is in a group and is not discoverable", + MAC2STR(peer)); + } + /* TODO: use device discoverability request through GO */ + } + + dev->invitation_reqs = 0; + + if (force_freq) { + if (p2p_freq_to_channel(p2p->cfg->country, force_freq, + &p2p->op_reg_class, &p2p->op_channel) < + 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported frequency %u MHz", + force_freq); + return -1; + } + p2p->channels.reg_classes = 1; + p2p->channels.reg_class[0].channels = 1; + p2p->channels.reg_class[0].reg_class = p2p->op_reg_class; + p2p->channels.reg_class[0].channel[0] = p2p->op_channel; + } else { + p2p->op_reg_class = p2p->cfg->op_reg_class; + p2p->op_channel = p2p->cfg->op_channel; + os_memcpy(&p2p->channels, &p2p->cfg->channels, + sizeof(struct p2p_channels)); + } + + if (p2p->state != P2P_IDLE) + p2p_stop_find(p2p); + + p2p->inv_role = role; + p2p->inv_bssid_set = bssid != NULL; + if (bssid) + os_memcpy(p2p->inv_bssid, bssid, ETH_ALEN); + os_memcpy(p2p->inv_ssid, ssid, ssid_len); + p2p->inv_ssid_len = ssid_len; + p2p->inv_persistent = persistent_group; + return p2p_invite_send(p2p, dev, go_dev_addr); +} diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c new file mode 100644 index 0000000000000..097a31de19d34 --- /dev/null +++ b/src/p2p/p2p_parse.c @@ -0,0 +1,723 @@ +/* + * P2P - IE parser + * Copyright (c) 2009-2010, Atheros Communications + * + * 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/ieee802_11_common.h" +#include "wps/wps_i.h" +#include "p2p_i.h" + + +static int p2p_parse_attribute(u8 id, const u8 *data, u16 len, + struct p2p_message *msg) +{ + const u8 *pos; + size_t i, nlen; + char devtype[WPS_DEV_TYPE_BUFSIZE]; + + switch (id) { + case P2P_ATTR_CAPABILITY: + if (len < 2) { + wpa_printf(MSG_DEBUG, "P2P: Too short Capability " + "attribute (length %d)", len); + return -1; + } + msg->capability = data; + wpa_printf(MSG_DEBUG, "P2P: * Device Capability %02x " + "Group Capability %02x", + data[0], data[1]); + break; + case P2P_ATTR_DEVICE_ID: + if (len < ETH_ALEN) { + wpa_printf(MSG_DEBUG, "P2P: Too short Device ID " + "attribute (length %d)", len); + return -1; + } + msg->device_id = data; + wpa_printf(MSG_DEBUG, "P2P: * Device ID " MACSTR, + MAC2STR(msg->device_id)); + break; + case P2P_ATTR_GROUP_OWNER_INTENT: + if (len < 1) { + wpa_printf(MSG_DEBUG, "P2P: Too short GO Intent " + "attribute (length %d)", len); + return -1; + } + msg->go_intent = data; + wpa_printf(MSG_DEBUG, "P2P: * GO Intent: Intent %u " + "Tie breaker %u", data[0] >> 1, data[0] & 0x01); + break; + case P2P_ATTR_STATUS: + if (len < 1) { + wpa_printf(MSG_DEBUG, "P2P: Too short Status " + "attribute (length %d)", len); + return -1; + } + msg->status = data; + wpa_printf(MSG_DEBUG, "P2P: * Status: %d", data[0]); + break; + case P2P_ATTR_LISTEN_CHANNEL: + if (len == 0) { + wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: Ignore " + "null channel"); + break; + } + if (len < 5) { + wpa_printf(MSG_DEBUG, "P2P: Too short Listen Channel " + "attribute (length %d)", len); + return -1; + } + msg->listen_channel = data; + wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: " + "Country %c%c(0x%02x) Regulatory " + "Class %d Channel Number %d", data[0], data[1], + data[2], data[3], data[4]); + break; + case P2P_ATTR_OPERATING_CHANNEL: + if (len == 0) { + wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: " + "Ignore null channel"); + break; + } + if (len < 5) { + wpa_printf(MSG_DEBUG, "P2P: Too short Operating " + "Channel attribute (length %d)", len); + return -1; + } + msg->operating_channel = data; + wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: " + "Country %c%c(0x%02x) Regulatory " + "Class %d Channel Number %d", data[0], data[1], + data[2], data[3], data[4]); + break; + case P2P_ATTR_CHANNEL_LIST: + if (len < 3) { + wpa_printf(MSG_DEBUG, "P2P: Too short Channel List " + "attribute (length %d)", len); + return -1; + } + msg->channel_list = data; + msg->channel_list_len = len; + wpa_printf(MSG_DEBUG, "P2P: * Channel List: Country String " + "'%c%c(0x%02x)'", data[0], data[1], data[2]); + wpa_hexdump(MSG_MSGDUMP, "P2P: Channel List", + msg->channel_list, msg->channel_list_len); + break; + case P2P_ATTR_GROUP_INFO: + msg->group_info = data; + msg->group_info_len = len; + wpa_printf(MSG_DEBUG, "P2P: * Group Info"); + break; + case P2P_ATTR_DEVICE_INFO: + if (len < ETH_ALEN + 2 + 8 + 1) { + wpa_printf(MSG_DEBUG, "P2P: Too short Device Info " + "attribute (length %d)", len); + return -1; + } + msg->p2p_device_info = data; + msg->p2p_device_info_len = len; + pos = data; + msg->p2p_device_addr = pos; + pos += ETH_ALEN; + msg->config_methods = WPA_GET_BE16(pos); + pos += 2; + msg->pri_dev_type = pos; + pos += 8; + msg->num_sec_dev_types = *pos++; + if (msg->num_sec_dev_types * 8 > data + len - pos) { + wpa_printf(MSG_DEBUG, "P2P: Device Info underflow"); + return -1; + } + pos += msg->num_sec_dev_types * 8; + if (data + len - pos < 4) { + wpa_printf(MSG_DEBUG, "P2P: Invalid Device Name " + "length %d", (int) (data + len - pos)); + return -1; + } + if (WPA_GET_BE16(pos) != ATTR_DEV_NAME) { + wpa_hexdump(MSG_DEBUG, "P2P: Unexpected Device Name " + "header", pos, 4); + return -1; + } + pos += 2; + nlen = WPA_GET_BE16(pos); + pos += 2; + if (data + len - pos < (int) nlen || nlen > 32) { + wpa_printf(MSG_DEBUG, "P2P: Invalid Device Name " + "length %d (buf len %d)", (int) nlen, + (int) (data + len - pos)); + return -1; + } + os_memcpy(msg->device_name, pos, nlen); + msg->device_name[nlen] = '\0'; + for (i = 0; i < nlen; i++) { + if (msg->device_name[i] == '\0') + break; + if (msg->device_name[i] > 0 && + msg->device_name[i] < 32) + msg->device_name[i] = '_'; + } + wpa_printf(MSG_DEBUG, "P2P: * Device Info: addr " MACSTR + " primary device type %s device name '%s' " + "config methods 0x%x", + MAC2STR(msg->p2p_device_addr), + wps_dev_type_bin2str(msg->pri_dev_type, devtype, + sizeof(devtype)), + msg->device_name, msg->config_methods); + break; + case P2P_ATTR_CONFIGURATION_TIMEOUT: + if (len < 2) { + wpa_printf(MSG_DEBUG, "P2P: Too short Configuration " + "Timeout attribute (length %d)", len); + return -1; + } + msg->config_timeout = data; + wpa_printf(MSG_DEBUG, "P2P: * Configuration Timeout"); + break; + case P2P_ATTR_INTENDED_INTERFACE_ADDR: + if (len < ETH_ALEN) { + wpa_printf(MSG_DEBUG, "P2P: Too short Intended P2P " + "Interface Address attribute (length %d)", + len); + return -1; + } + msg->intended_addr = data; + wpa_printf(MSG_DEBUG, "P2P: * Intended P2P Interface Address: " + MACSTR, MAC2STR(msg->intended_addr)); + break; + case P2P_ATTR_GROUP_BSSID: + if (len < ETH_ALEN) { + wpa_printf(MSG_DEBUG, "P2P: Too short P2P Group BSSID " + "attribute (length %d)", len); + return -1; + } + msg->group_bssid = data; + wpa_printf(MSG_DEBUG, "P2P: * P2P Group BSSID: " MACSTR, + MAC2STR(msg->group_bssid)); + break; + case P2P_ATTR_GROUP_ID: + if (len < ETH_ALEN || len > ETH_ALEN + 32) { + wpa_printf(MSG_DEBUG, "P2P: Invalid P2P Group ID " + "attribute length %d", len); + return -1; + } + msg->group_id = data; + msg->group_id_len = len; + wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID: Device Address " + MACSTR, MAC2STR(msg->group_id)); + wpa_hexdump_ascii(MSG_DEBUG, "P2P: * P2P Group ID: SSID", + msg->group_id + ETH_ALEN, + msg->group_id_len - ETH_ALEN); + break; + case P2P_ATTR_INVITATION_FLAGS: + if (len < 1) { + wpa_printf(MSG_DEBUG, "P2P: Too short Invitation " + "Flag attribute (length %d)", len); + return -1; + } + msg->invitation_flags = data; + wpa_printf(MSG_DEBUG, "P2P: * Invitation Flags: bitmap 0x%x", + data[0]); + break; + case P2P_ATTR_MANAGEABILITY: + if (len < 1) { + wpa_printf(MSG_DEBUG, "P2P: Too short Manageability " + "attribute (length %d)", len); + return -1; + } + msg->manageability = data; + wpa_printf(MSG_DEBUG, "P2P: * Manageability: bitmap 0x%x", + data[0]); + break; + case P2P_ATTR_NOTICE_OF_ABSENCE: + if (len < 2) { + wpa_printf(MSG_DEBUG, "P2P: Too short Notice of " + "Absence attribute (length %d)", len); + return -1; + } + msg->noa = data; + msg->noa_len = len; + wpa_printf(MSG_DEBUG, "P2P: * Notice of Absence"); + break; + case P2P_ATTR_EXT_LISTEN_TIMING: + if (len < 4) { + wpa_printf(MSG_DEBUG, "P2P: Too short Extended Listen " + "Timing attribute (length %d)", len); + return -1; + } + msg->ext_listen_timing = data; + wpa_printf(MSG_DEBUG, "P2P: * Extended Listen Timing " + "(period %u msec interval %u msec)", + WPA_GET_LE16(msg->ext_listen_timing), + WPA_GET_LE16(msg->ext_listen_timing + 2)); + break; + case P2P_ATTR_MINOR_REASON_CODE: + if (len < 1) { + wpa_printf(MSG_DEBUG, "P2P: Too short Minor Reason " + "Code attribute (length %d)", len); + return -1; + } + msg->minor_reason_code = data; + wpa_printf(MSG_DEBUG, "P2P: * Minor Reason Code: %u", + *msg->minor_reason_code); + break; + default: + wpa_printf(MSG_DEBUG, "P2P: Skipped unknown attribute %d " + "(length %d)", id, len); + break; + } + + return 0; +} + + +/** + * p2p_parse_p2p_ie - Parse P2P IE + * @buf: Concatenated P2P IE(s) payload + * @msg: Buffer for returning parsed attributes + * Returns: 0 on success, -1 on failure + * + * Note: Caller is responsible for clearing the msg data structure before + * calling this function. + */ +int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg) +{ + const u8 *pos = wpabuf_head_u8(buf); + const u8 *end = pos + wpabuf_len(buf); + + wpa_printf(MSG_DEBUG, "P2P: Parsing P2P IE"); + + while (pos < end) { + u16 attr_len; + if (pos + 2 >= end) { + wpa_printf(MSG_DEBUG, "P2P: Invalid P2P attribute"); + return -1; + } + attr_len = WPA_GET_LE16(pos + 1); + wpa_printf(MSG_DEBUG, "P2P: Attribute %d length %u", + pos[0], attr_len); + if (pos + 3 + attr_len > end) { + wpa_printf(MSG_DEBUG, "P2P: Attribute underflow " + "(len=%u left=%d)", + attr_len, (int) (end - pos - 3)); + wpa_hexdump(MSG_MSGDUMP, "P2P: Data", pos, end - pos); + return -1; + } + if (p2p_parse_attribute(pos[0], pos + 3, attr_len, msg)) + return -1; + pos += 3 + attr_len; + } + + return 0; +} + + +static int p2p_parse_wps_ie(const struct wpabuf *buf, struct p2p_message *msg) +{ + struct wps_parse_attr attr; + int i; + + wpa_printf(MSG_DEBUG, "P2P: Parsing WPS IE"); + if (wps_parse_msg(buf, &attr)) + return -1; + if (attr.dev_name && attr.dev_name_len < sizeof(msg->device_name) && + !msg->device_name[0]) + os_memcpy(msg->device_name, attr.dev_name, attr.dev_name_len); + if (attr.config_methods) { + msg->wps_config_methods = + WPA_GET_BE16(attr.config_methods); + wpa_printf(MSG_DEBUG, "P2P: Config Methods (WPS): 0x%x", + msg->wps_config_methods); + } + if (attr.dev_password_id) { + msg->dev_password_id = WPA_GET_BE16(attr.dev_password_id); + wpa_printf(MSG_DEBUG, "P2P: Device Password ID: %d", + msg->dev_password_id); + } + if (attr.primary_dev_type) { + char devtype[WPS_DEV_TYPE_BUFSIZE]; + msg->wps_pri_dev_type = attr.primary_dev_type; + wpa_printf(MSG_DEBUG, "P2P: Primary Device Type (WPS): %s", + wps_dev_type_bin2str(msg->wps_pri_dev_type, devtype, + sizeof(devtype))); + } + if (attr.sec_dev_type_list) { + msg->wps_sec_dev_type_list = attr.sec_dev_type_list; + msg->wps_sec_dev_type_list_len = attr.sec_dev_type_list_len; + } + + for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { + msg->wps_vendor_ext[i] = attr.vendor_ext[i]; + msg->wps_vendor_ext_len[i] = attr.vendor_ext_len[i]; + } + + msg->manufacturer = attr.manufacturer; + msg->manufacturer_len = attr.manufacturer_len; + msg->model_name = attr.model_name; + msg->model_name_len = attr.model_name_len; + msg->model_number = attr.model_number; + msg->model_number_len = attr.model_number_len; + msg->serial_number = attr.serial_number; + msg->serial_number_len = attr.serial_number_len; + + return 0; +} + + +/** + * p2p_parse_ies - Parse P2P message IEs (both WPS and P2P IE) + * @data: IEs from the message + * @len: Length of data buffer in octets + * @msg: Buffer for returning parsed attributes + * Returns: 0 on success, -1 on failure + * + * Note: Caller is responsible for clearing the msg data structure before + * calling this function. + * + * Note: Caller must free temporary memory allocations by calling + * p2p_parse_free() when the parsed data is not needed anymore. + */ +int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg) +{ + struct ieee802_11_elems elems; + + ieee802_11_parse_elems(data, len, &elems, 0); + if (elems.ds_params && elems.ds_params_len >= 1) + msg->ds_params = elems.ds_params; + if (elems.ssid) + msg->ssid = elems.ssid - 2; + + msg->wps_attributes = ieee802_11_vendor_ie_concat(data, len, + WPS_DEV_OUI_WFA); + if (msg->wps_attributes && + p2p_parse_wps_ie(msg->wps_attributes, msg)) { + p2p_parse_free(msg); + return -1; + } + + msg->p2p_attributes = ieee802_11_vendor_ie_concat(data, len, + P2P_IE_VENDOR_TYPE); + if (msg->p2p_attributes && + p2p_parse_p2p_ie(msg->p2p_attributes, msg)) { + wpa_printf(MSG_DEBUG, "P2P: Failed to parse P2P IE data"); + if (msg->p2p_attributes) + wpa_hexdump_buf(MSG_MSGDUMP, "P2P: P2P IE data", + msg->p2p_attributes); + p2p_parse_free(msg); + return -1; + } + +#ifdef CONFIG_WIFI_DISPLAY + if (elems.wfd) { + msg->wfd_subelems = ieee802_11_vendor_ie_concat( + data, len, WFD_IE_VENDOR_TYPE); + } +#endif /* CONFIG_WIFI_DISPLAY */ + + return 0; +} + + +/** + * p2p_parse - Parse a P2P Action frame contents + * @data: Action frame payload after Category and Code fields + * @len: Length of data buffer in octets + * @msg: Buffer for returning parsed attributes + * Returns: 0 on success, -1 on failure + * + * Note: Caller must free temporary memory allocations by calling + * p2p_parse_free() when the parsed data is not needed anymore. + */ +int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg) +{ + os_memset(msg, 0, sizeof(*msg)); + wpa_printf(MSG_DEBUG, "P2P: Parsing the received message"); + if (len < 1) { + wpa_printf(MSG_DEBUG, "P2P: No Dialog Token in the message"); + return -1; + } + msg->dialog_token = data[0]; + wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", msg->dialog_token); + + return p2p_parse_ies(data + 1, len - 1, msg); +} + + +/** + * p2p_parse_free - Free temporary data from P2P parsing + * @msg: Parsed attributes + */ +void p2p_parse_free(struct p2p_message *msg) +{ + wpabuf_free(msg->p2p_attributes); + msg->p2p_attributes = NULL; + wpabuf_free(msg->wps_attributes); + msg->wps_attributes = NULL; +#ifdef CONFIG_WIFI_DISPLAY + wpabuf_free(msg->wfd_subelems); + msg->wfd_subelems = NULL; +#endif /* CONFIG_WIFI_DISPLAY */ +} + + +int p2p_group_info_parse(const u8 *gi, size_t gi_len, + struct p2p_group_info *info) +{ + const u8 *g, *gend; + + os_memset(info, 0, sizeof(*info)); + if (gi == NULL) + return 0; + + g = gi; + gend = gi + gi_len; + while (g < gend) { + struct p2p_client_info *cli; + const u8 *t, *cend; + int count; + + cli = &info->client[info->num_clients]; + cend = g + 1 + g[0]; + if (cend > gend) + return -1; /* invalid data */ + /* g at start of P2P Client Info Descriptor */ + /* t at Device Capability Bitmap */ + t = g + 1 + 2 * ETH_ALEN; + if (t > cend) + return -1; /* invalid data */ + cli->p2p_device_addr = g + 1; + cli->p2p_interface_addr = g + 1 + ETH_ALEN; + cli->dev_capab = t[0]; + + if (t + 1 + 2 + 8 + 1 > cend) + return -1; /* invalid data */ + + cli->config_methods = WPA_GET_BE16(&t[1]); + cli->pri_dev_type = &t[3]; + + t += 1 + 2 + 8; + /* t at Number of Secondary Device Types */ + cli->num_sec_dev_types = *t++; + if (t + 8 * cli->num_sec_dev_types > cend) + return -1; /* invalid data */ + cli->sec_dev_types = t; + t += 8 * cli->num_sec_dev_types; + + /* t at Device Name in WPS TLV format */ + if (t + 2 + 2 > cend) + return -1; /* invalid data */ + if (WPA_GET_BE16(t) != ATTR_DEV_NAME) + return -1; /* invalid Device Name TLV */ + t += 2; + count = WPA_GET_BE16(t); + t += 2; + if (count > cend - t) + return -1; /* invalid Device Name TLV */ + if (count >= 32) + count = 32; + cli->dev_name = (const char *) t; + cli->dev_name_len = count; + + g = cend; + + info->num_clients++; + if (info->num_clients == P2P_MAX_GROUP_ENTRIES) + return -1; + } + + return 0; +} + + +static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf, + char *end) +{ + char *pos = buf; + int ret; + struct p2p_group_info info; + unsigned int i; + + if (p2p_group_info_parse(gi, gi_len, &info) < 0) + return 0; + + for (i = 0; i < info.num_clients; i++) { + struct p2p_client_info *cli; + char name[33]; + char devtype[WPS_DEV_TYPE_BUFSIZE]; + u8 s; + int count; + + cli = &info.client[i]; + ret = os_snprintf(pos, end - pos, "p2p_group_client: " + "dev=" MACSTR " iface=" MACSTR, + MAC2STR(cli->p2p_device_addr), + MAC2STR(cli->p2p_interface_addr)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, end - pos, + " dev_capab=0x%x config_methods=0x%x " + "dev_type=%s", + cli->dev_capab, cli->config_methods, + wps_dev_type_bin2str(cli->pri_dev_type, + devtype, + sizeof(devtype))); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + for (s = 0; s < cli->num_sec_dev_types; s++) { + ret = os_snprintf(pos, end - pos, " dev_type=%s", + wps_dev_type_bin2str( + &cli->sec_dev_types[s * 8], + devtype, sizeof(devtype))); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + os_memcpy(name, cli->dev_name, cli->dev_name_len); + name[cli->dev_name_len] = '\0'; + count = (int) cli->dev_name_len - 1; + while (count >= 0) { + if (name[count] > 0 && name[count] < 32) + name[count] = '_'; + count--; + } + + ret = os_snprintf(pos, end - pos, " dev_name='%s'\n", name); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + return pos - buf; +} + + +/** + * p2p_attr_text - Build text format description of P2P IE attributes + * @data: P2P IE contents + * @buf: Buffer for returning text + * @end: Pointer to the end of the buf area + * Returns: Number of octets written to the buffer or -1 on faikure + * + * This function can be used to parse P2P IE contents into text format + * field=value lines. + */ +int p2p_attr_text(struct wpabuf *data, char *buf, char *end) +{ + struct p2p_message msg; + char *pos = buf; + int ret; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_p2p_ie(data, &msg)) + return -1; + + if (msg.capability) { + ret = os_snprintf(pos, end - pos, + "p2p_dev_capab=0x%x\n" + "p2p_group_capab=0x%x\n", + msg.capability[0], msg.capability[1]); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + if (msg.pri_dev_type) { + char devtype[WPS_DEV_TYPE_BUFSIZE]; + ret = os_snprintf(pos, end - pos, + "p2p_primary_device_type=%s\n", + wps_dev_type_bin2str(msg.pri_dev_type, + devtype, + sizeof(devtype))); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + ret = os_snprintf(pos, end - pos, "p2p_device_name=%s\n", + msg.device_name); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + if (msg.p2p_device_addr) { + ret = os_snprintf(pos, end - pos, "p2p_device_addr=" MACSTR + "\n", + MAC2STR(msg.p2p_device_addr)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + ret = os_snprintf(pos, end - pos, "p2p_config_methods=0x%x\n", + msg.config_methods); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ret = p2p_group_info_text(msg.group_info, msg.group_info_len, + pos, end); + if (ret < 0) + return pos - buf; + pos += ret; + + return pos - buf; +} + + +int p2p_get_cross_connect_disallowed(const struct wpabuf *p2p_ie) +{ + struct p2p_message msg; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_p2p_ie(p2p_ie, &msg)) + return 0; + + if (!msg.manageability) + return 0; + + return !(msg.manageability[0] & P2P_MAN_CROSS_CONNECTION_PERMITTED); +} + + +u8 p2p_get_group_capab(const struct wpabuf *p2p_ie) +{ + struct p2p_message msg; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_p2p_ie(p2p_ie, &msg)) + return 0; + + if (!msg.capability) + return 0; + + return msg.capability[1]; +} + + +const u8 * p2p_get_go_dev_addr(const struct wpabuf *p2p_ie) +{ + struct p2p_message msg; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_p2p_ie(p2p_ie, &msg)) + return NULL; + + if (msg.p2p_device_addr) + return msg.p2p_device_addr; + if (msg.device_id) + return msg.device_id; + + return NULL; +} diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c new file mode 100644 index 0000000000000..ca33f17a17b8f --- /dev/null +++ b/src/p2p/p2p_pd.c @@ -0,0 +1,484 @@ +/* + * Wi-Fi Direct - P2P provision discovery + * Copyright (c) 2009-2010, Atheros Communications + * + * 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 "wps/wps_defs.h" +#include "p2p_i.h" +#include "p2p.h" + + +/* + * Number of retries to attempt for provision discovery requests + * in case the peer is not listening. + */ +#define MAX_PROV_DISC_REQ_RETRIES 120 + + +static void p2p_build_wps_ie_config_methods(struct wpabuf *buf, + u16 config_methods) +{ + u8 *len; + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + len = wpabuf_put(buf, 1); + wpabuf_put_be32(buf, WPS_DEV_OUI_WFA); + + /* Config Methods */ + wpabuf_put_be16(buf, ATTR_CONFIG_METHODS); + wpabuf_put_be16(buf, 2); + wpabuf_put_be16(buf, config_methods); + + p2p_buf_update_ie_hdr(buf, len); +} + + +static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p, + u8 dialog_token, + u16 config_methods, + struct p2p_device *go) +{ + struct wpabuf *buf; + u8 *len; + size_t extra = 0; + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_prov_disc_req) + extra = wpabuf_len(p2p->wfd_ie_prov_disc_req); +#endif /* CONFIG_WIFI_DISPLAY */ + + buf = wpabuf_alloc(1000 + extra); + if (buf == NULL) + return NULL; + + p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token); + + len = p2p_buf_add_ie_hdr(buf); + p2p_buf_add_capability(buf, p2p->dev_capab & + ~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, + go->oper_ssid, go->oper_ssid_len); + } + p2p_buf_update_ie_hdr(buf, len); + + /* WPS IE with Config Methods attribute */ + p2p_build_wps_ie_config_methods(buf, config_methods); + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_prov_disc_req) + wpabuf_put_buf(buf, p2p->wfd_ie_prov_disc_req); +#endif /* CONFIG_WIFI_DISPLAY */ + + return buf; +} + + +static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, + u8 dialog_token, + u16 config_methods, + const u8 *group_id, + size_t group_id_len) +{ + struct wpabuf *buf; + size_t extra = 0; + +#ifdef CONFIG_WIFI_DISPLAY + struct wpabuf *wfd_ie = p2p->wfd_ie_prov_disc_resp; + if (wfd_ie && group_id) { + size_t i; + for (i = 0; i < p2p->num_groups; i++) { + struct p2p_group *g = p2p->groups[i]; + struct wpabuf *ie; + if (!p2p_group_is_group_id_match(g, group_id, + group_id_len)) + continue; + ie = p2p_group_get_wfd_ie(g); + if (ie) { + wfd_ie = ie; + break; + } + } + } + if (wfd_ie) + extra = wpabuf_len(wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ + + buf = wpabuf_alloc(100 + extra); + if (buf == NULL) + return NULL; + + p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_RESP, dialog_token); + + /* WPS IE with Config Methods attribute */ + p2p_build_wps_ie_config_methods(buf, config_methods); + +#ifdef CONFIG_WIFI_DISPLAY + if (wfd_ie) + wpabuf_put_buf(buf, wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ + + return buf; +} + + +void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + struct p2p_message msg; + struct p2p_device *dev; + int freq; + int reject = 1; + struct wpabuf *resp; + + if (p2p_parse(data, len, &msg)) + return; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received Provision Discovery Request from " MACSTR + " with config methods 0x%x (freq=%d)", + MAC2STR(sa), msg.wps_config_methods, rx_freq); + + dev = p2p_get_device(p2p, sa); + if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Provision Discovery Request from " + "unknown peer " MACSTR, MAC2STR(sa)); + + if (p2p_add_device(p2p, sa, rx_freq, 0, 0, data + 1, len - 1, + 0)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Provision Discovery Request add device " + "failed " MACSTR, MAC2STR(sa)); + } + } else if (msg.wfd_subelems) { + wpabuf_free(dev->info.wfd_subelems); + dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); + } + + if (!(msg.wps_config_methods & + (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD | + WPS_CONFIG_PUSHBUTTON))) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Unsupported " + "Config Methods in Provision Discovery Request"); + goto out; + } + + if (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); + if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR + " requested us to show a PIN on display", MAC2STR(sa)); + if (dev) + dev->flags |= P2P_DEV_PD_PEER_KEYPAD; + } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR + " requested us to write its PIN using keypad", + MAC2STR(sa)); + if (dev) + dev->flags |= P2P_DEV_PD_PEER_DISPLAY; + } + + reject = 0; + +out: + resp = p2p_build_prov_disc_resp(p2p, msg.dialog_token, + reject ? 0 : msg.wps_config_methods, + msg.group_id, msg.group_id_len); + if (resp == NULL) { + p2p_parse_free(&msg); + return; + } + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Sending Provision Discovery Response"); + if (rx_freq > 0) + freq = rx_freq; + else + freq = p2p_channel_to_freq(p2p->cfg->country, + p2p->cfg->reg_class, + p2p->cfg->channel); + if (freq < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unknown regulatory class/channel"); + wpabuf_free(resp); + p2p_parse_free(&msg); + return; + } + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, + p2p->cfg->dev_addr, + wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + } + + wpabuf_free(resp); + + if (!reject && p2p->cfg->prov_disc_req) { + const u8 *dev_addr = sa; + if (msg.p2p_device_addr) + dev_addr = msg.p2p_device_addr; + p2p->cfg->prov_disc_req(p2p->cfg->cb_ctx, sa, + msg.wps_config_methods, + dev_addr, msg.pri_dev_type, + msg.device_name, msg.config_methods, + msg.capability ? msg.capability[0] : 0, + msg.capability ? msg.capability[1] : + 0, + msg.group_id, msg.group_id_len); + } + p2p_parse_free(&msg); +} + + +void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len) +{ + struct p2p_message msg; + struct p2p_device *dev; + u16 report_config_methods = 0; + int success = 0; + + if (p2p_parse(data, len, &msg)) + return; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received Provision Discovery Response from " MACSTR + " with config methods 0x%x", + MAC2STR(sa), msg.wps_config_methods); + + dev = p2p_get_device(p2p, sa); + if (dev == NULL || !dev->req_config_methods) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Ignore Provision Discovery Response from " + MACSTR " with no pending request", MAC2STR(sa)); + p2p_parse_free(&msg); + return; + } + + if (dev->dialog_token != msg.dialog_token) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Ignore Provision Discovery Response with " + "unexpected Dialog Token %u (expected %u)", + msg.dialog_token, dev->dialog_token); + p2p_parse_free(&msg); + return; + } + + if (p2p->pending_action_state == P2P_PENDING_PD) { + os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN); + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + } + + /* + * If the response is from the peer to whom a user initiated request + * was sent earlier, we reset that state info here. + */ + if (p2p->user_initiated_pd && + os_memcmp(p2p->pending_pd_devaddr, sa, ETH_ALEN) == 0) + p2p_reset_pending_pd(p2p); + + if (msg.wps_config_methods != dev->req_config_methods) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer rejected " + "our Provision Discovery Request"); + if (p2p->cfg->prov_disc_fail) + p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa, + P2P_PROV_DISC_REJECTED); + p2p_parse_free(&msg); + goto out; + } + + report_config_methods = dev->req_config_methods; + dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY | + P2P_DEV_PD_PEER_KEYPAD); + if (dev->req_config_methods & WPS_CONFIG_DISPLAY) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR + " accepted to show a PIN on display", MAC2STR(sa)); + dev->flags |= P2P_DEV_PD_PEER_DISPLAY; + } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR + " accepted to write our PIN using keypad", + MAC2STR(sa)); + dev->flags |= P2P_DEV_PD_PEER_KEYPAD; + } + + /* Store the provisioning info */ + 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 (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); +} + + +int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, + int join, int force_freq) +{ + struct wpabuf *req; + int freq; + + if (force_freq > 0) + freq = force_freq; + else + freq = dev->listen_freq > 0 ? dev->listen_freq : + dev->oper_freq; + if (freq <= 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No Listen/Operating frequency known for the " + "peer " MACSTR " to send Provision Discovery Request", + MAC2STR(dev->info.p2p_device_addr)); + return -1; + } + + if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) { + if (!(dev->info.dev_capab & + P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Cannot use PD with P2P Device " MACSTR + " that is in a group and is not discoverable", + MAC2STR(dev->info.p2p_device_addr)); + return -1; + } + /* TODO: use device discoverability request through GO */ + } + + req = p2p_build_prov_disc_req(p2p, dev->dialog_token, + dev->req_config_methods, + join ? dev : NULL); + 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, + wpabuf_head(req), wpabuf_len(req), 200) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + wpabuf_free(req); + return -1; + } + + os_memcpy(p2p->pending_pd_devaddr, dev->info.p2p_device_addr, ETH_ALEN); + + wpabuf_free(req); + return 0; +} + + +int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, + u16 config_methods, int join, int force_freq, + int user_initiated_pd) +{ + struct p2p_device *dev; + + dev = p2p_get_device(p2p, peer_addr); + if (dev == NULL) + dev = p2p_get_device_interface(p2p, peer_addr); + if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Provision " + "Discovery Request destination " MACSTR + " not yet known", MAC2STR(peer_addr)); + return -1; + } + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Provision Discovery " + "Request with " MACSTR " (config methods 0x%x)", + MAC2STR(peer_addr), config_methods); + if (config_methods == 0) + return -1; + + /* Reset provisioning info */ + dev->wps_prov_info = 0; + + dev->req_config_methods = config_methods; + if (join) + dev->flags |= P2P_DEV_PD_FOR_JOIN; + else + dev->flags &= ~P2P_DEV_PD_FOR_JOIN; + + if (p2p->state != P2P_IDLE && p2p->state != P2P_SEARCH && + p2p->state != P2P_LISTEN_ONLY) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Busy with other " + "operations; postpone Provision Discovery Request " + "with " MACSTR " (config methods 0x%x)", + MAC2STR(peer_addr), config_methods); + return 0; + } + + p2p->user_initiated_pd = user_initiated_pd; + + if (p2p->user_initiated_pd) + p2p->pd_retries = MAX_PROV_DISC_REQ_RETRIES; + + /* + * Assign dialog token here to use the same value in each retry within + * the same PD exchange. + */ + dev->dialog_token++; + if (dev->dialog_token == 0) + dev->dialog_token = 1; + + return p2p_send_prov_disc_req(p2p, dev, join, force_freq); +} + + +void p2p_reset_pending_pd(struct p2p_data *p2p) +{ + struct p2p_device *dev; + + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { + if (os_memcmp(p2p->pending_pd_devaddr, + dev->info.p2p_device_addr, ETH_ALEN)) + continue; + if (!dev->req_config_methods) + continue; + if (dev->flags & P2P_DEV_PD_FOR_JOIN) + continue; + /* Reset the config methods of the device */ + dev->req_config_methods = 0; + } + + p2p->user_initiated_pd = 0; + os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN); + p2p->pd_retries = 0; +} diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c new file mode 100644 index 0000000000000..abe1d6b586615 --- /dev/null +++ b/src/p2p/p2p_sd.c @@ -0,0 +1,947 @@ +/* + * Wi-Fi Direct - P2P service discovery + * Copyright (c) 2009, Atheros Communications + * + * 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 "p2p_i.h" +#include "p2p.h" + + +#ifdef CONFIG_WIFI_DISPLAY +static int wfd_wsd_supported(struct wpabuf *wfd) +{ + const u8 *pos, *end; + u8 subelem; + u16 len; + + if (wfd == NULL) + return 0; + + pos = wpabuf_head(wfd); + end = pos + wpabuf_len(wfd); + + while (pos + 3 <= end) { + subelem = *pos++; + len = WPA_GET_BE16(pos); + pos += 2; + if (pos + len > end) + break; + + if (subelem == WFD_SUBELEM_DEVICE_INFO && len >= 6) { + u16 info = WPA_GET_BE16(pos); + return !!(info & 0x0040); + } + + pos += len; + } + + return 0; +} +#endif /* CONFIG_WIFI_DISPLAY */ + +struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p, + struct p2p_device *dev) +{ + struct p2p_sd_query *q; + int wsd = 0; + + if (!(dev->info.dev_capab & P2P_DEV_CAPAB_SERVICE_DISCOVERY)) + return NULL; /* peer does not support SD */ +#ifdef CONFIG_WIFI_DISPLAY + if (wfd_wsd_supported(dev->info.wfd_subelems)) + wsd = 1; +#endif /* CONFIG_WIFI_DISPLAY */ + + for (q = p2p->sd_queries; q; q = q->next) { + /* Use WSD only if the peer indicates support or it */ + if (q->wsd && !wsd) + continue; + if (q->for_all_peers && !(dev->flags & P2P_DEV_SD_INFO)) + return q; + if (!q->for_all_peers && + os_memcmp(q->peer, dev->info.p2p_device_addr, ETH_ALEN) == + 0) + return q; + } + + return NULL; +} + + +static int p2p_unlink_sd_query(struct p2p_data *p2p, + struct p2p_sd_query *query) +{ + struct p2p_sd_query *q, *prev; + q = p2p->sd_queries; + prev = NULL; + while (q) { + if (q == query) { + if (prev) + prev->next = q->next; + else + p2p->sd_queries = q->next; + if (p2p->sd_query == query) + p2p->sd_query = NULL; + return 1; + } + prev = q; + q = q->next; + } + return 0; +} + + +static void p2p_free_sd_query(struct p2p_sd_query *q) +{ + if (q == NULL) + return; + wpabuf_free(q->tlvs); + os_free(q); +} + + +void p2p_free_sd_queries(struct p2p_data *p2p) +{ + struct p2p_sd_query *q, *prev; + q = p2p->sd_queries; + p2p->sd_queries = NULL; + while (q) { + prev = q; + q = q->next; + p2p_free_sd_query(prev); + } +} + + +static struct wpabuf * p2p_build_sd_query(u16 update_indic, + struct wpabuf *tlvs) +{ + struct wpabuf *buf; + u8 *len_pos; + + buf = gas_anqp_build_initial_req(0, 100 + wpabuf_len(tlvs)); + if (buf == NULL) + return NULL; + + /* ANQP Query Request Frame */ + len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, P2P_OUI_TYPE); + wpabuf_put_le16(buf, update_indic); /* Service Update Indicator */ + wpabuf_put_buf(buf, tlvs); + gas_anqp_set_element_len(buf, len_pos); + + gas_anqp_set_len(buf); + + return buf; +} + + +static void p2p_send_gas_comeback_req(struct p2p_data *p2p, const u8 *dst, + u8 dialog_token, int freq) +{ + struct wpabuf *req; + + req = gas_build_comeback_req(dialog_token); + if (req == NULL) + return; + + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + if (p2p_send_action(p2p, freq, dst, p2p->cfg->dev_addr, dst, + wpabuf_head(req), wpabuf_len(req), 200) < 0) + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + + wpabuf_free(req); +} + + +static struct wpabuf * p2p_build_sd_response(u8 dialog_token, u16 status_code, + u16 comeback_delay, + u16 update_indic, + const struct wpabuf *tlvs) +{ + struct wpabuf *buf; + u8 *len_pos; + + buf = gas_anqp_build_initial_resp(dialog_token, status_code, + comeback_delay, + 100 + (tlvs ? wpabuf_len(tlvs) : 0)); + if (buf == NULL) + return NULL; + + if (tlvs) { + /* ANQP Query Response Frame */ + len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, P2P_OUI_TYPE); + /* Service Update Indicator */ + wpabuf_put_le16(buf, update_indic); + wpabuf_put_buf(buf, tlvs); + gas_anqp_set_element_len(buf, len_pos); + } + + gas_anqp_set_len(buf); + + return buf; +} + + +static struct wpabuf * p2p_build_gas_comeback_resp(u8 dialog_token, + u16 status_code, + u16 update_indic, + const u8 *data, size_t len, + u8 frag_id, u8 more, + u16 total_len) +{ + struct wpabuf *buf; + + buf = gas_anqp_build_comeback_resp(dialog_token, status_code, frag_id, + more, 0, 100 + len); + if (buf == NULL) + return NULL; + + if (frag_id == 0) { + /* ANQP Query Response Frame */ + wpabuf_put_le16(buf, ANQP_VENDOR_SPECIFIC); /* Info ID */ + wpabuf_put_le16(buf, 3 + 1 + 2 + total_len); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, P2P_OUI_TYPE); + /* Service Update Indicator */ + wpabuf_put_le16(buf, update_indic); + } + + wpabuf_put_data(buf, data, len); + gas_anqp_set_len(buf); + + return buf; +} + + +int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev) +{ + struct wpabuf *req; + int ret = 0; + struct p2p_sd_query *query; + int freq; + + freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; + if (freq <= 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No Listen/Operating frequency known for the " + "peer " MACSTR " to send SD Request", + MAC2STR(dev->info.p2p_device_addr)); + return -1; + } + + query = p2p_pending_sd_req(p2p, dev); + if (query == NULL) + return -1; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Start Service Discovery with " MACSTR, + MAC2STR(dev->info.p2p_device_addr)); + + req = p2p_build_sd_query(p2p->srv_update_indic, query->tlvs); + if (req == NULL) + return -1; + + p2p->sd_peer = dev; + p2p->sd_query = query; + p2p->pending_action_state = P2P_PENDING_SD; + + if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, + p2p->cfg->dev_addr, dev->info.p2p_device_addr, + wpabuf_head(req), wpabuf_len(req), 5000) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + ret = -1; + } + + wpabuf_free(req); + + return ret; +} + + +void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + const u8 *pos = data; + const u8 *end = data + len; + const u8 *next; + u8 dialog_token; + u16 slen; + int freq; + u16 update_indic; + + + if (p2p->cfg->sd_request == NULL) + return; + + if (rx_freq > 0) + freq = rx_freq; + else + freq = p2p_channel_to_freq(p2p->cfg->country, + p2p->cfg->reg_class, + p2p->cfg->channel); + if (freq < 0) + return; + + if (len < 1 + 2) + return; + + dialog_token = *pos++; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: GAS Initial Request from " MACSTR " (dialog token %u, " + "freq %d)", + MAC2STR(sa), dialog_token, rx_freq); + + if (*pos != WLAN_EID_ADV_PROTO) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unexpected IE in GAS Initial Request: %u", *pos); + return; + } + pos++; + + slen = *pos++; + next = pos + slen; + if (next > end || slen < 2) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invalid IE in GAS Initial Request"); + return; + } + pos++; /* skip QueryRespLenLimit and PAME-BI */ + + if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported GAS advertisement protocol id %u", + *pos); + return; + } + + pos = next; + /* Query Request */ + if (pos + 2 > end) + return; + slen = WPA_GET_LE16(pos); + pos += 2; + if (pos + slen > end) + return; + end = pos + slen; + + /* ANQP Query Request */ + if (pos + 4 > end) + return; + if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); + return; + } + pos += 2; + + slen = WPA_GET_LE16(pos); + pos += 2; + if (pos + slen > end || slen < 3 + 1) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invalid ANQP Query Request length"); + return; + } + + if (WPA_GET_BE24(pos) != OUI_WFA) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported ANQP OUI %06x", WPA_GET_BE24(pos)); + return; + } + pos += 3; + + if (*pos != P2P_OUI_TYPE) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported ANQP vendor type %u", *pos); + return; + } + pos++; + + if (pos + 2 > end) + return; + update_indic = WPA_GET_LE16(pos); + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Service Update Indicator: %u", update_indic); + pos += 2; + + p2p->cfg->sd_request(p2p->cfg->cb_ctx, freq, sa, dialog_token, + update_indic, pos, end - pos); + /* the response will be indicated with a call to p2p_sd_response() */ +} + + +void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst, + u8 dialog_token, const struct wpabuf *resp_tlvs) +{ + struct wpabuf *resp; + + /* TODO: fix the length limit to match with the maximum frame length */ + if (wpabuf_len(resp_tlvs) > 1400) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: SD response long " + "enough to require fragmentation"); + if (p2p->sd_resp) { + /* + * TODO: Could consider storing the fragmented response + * separately for each peer to avoid having to drop old + * one if there is more than one pending SD query. + * Though, that would eat more memory, so there are + * also benefits to just using a single buffer. + */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Drop " + "previous SD response"); + wpabuf_free(p2p->sd_resp); + } + 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_pos = 0; + p2p->sd_frag_id = 0; + resp = p2p_build_sd_response(dialog_token, WLAN_STATUS_SUCCESS, + 1, p2p->srv_update_indic, NULL); + } else { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: SD response fits " + "in initial response"); + resp = p2p_build_sd_response(dialog_token, + WLAN_STATUS_SUCCESS, 0, + p2p->srv_update_indic, resp_tlvs); + } + if (resp == NULL) + return; + + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + if (p2p_send_action(p2p, freq, dst, p2p->cfg->dev_addr, + p2p->cfg->dev_addr, + wpabuf_head(resp), wpabuf_len(resp), 200) < 0) + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + + wpabuf_free(resp); +} + + +void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + const u8 *pos = data; + const u8 *end = data + len; + const u8 *next; + u8 dialog_token; + u16 status_code; + u16 comeback_delay; + u16 slen; + u16 update_indic; + + if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL || + os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Ignore unexpected GAS Initial Response from " + MACSTR, MAC2STR(sa)); + return; + } + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p_clear_timeout(p2p); + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received GAS Initial Response from " MACSTR " (len=%d)", + MAC2STR(sa), (int) len); + + if (len < 5 + 2) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Too short GAS Initial Response frame"); + return; + } + + dialog_token = *pos++; + /* TODO: check dialog_token match */ + status_code = WPA_GET_LE16(pos); + pos += 2; + comeback_delay = WPA_GET_LE16(pos); + pos += 2; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: dialog_token=%u status_code=%u comeback_delay=%u", + dialog_token, status_code, comeback_delay); + if (status_code) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Service Discovery failed: status code %u", + status_code); + return; + } + + if (*pos != WLAN_EID_ADV_PROTO) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unexpected IE in GAS Initial Response: %u", + *pos); + return; + } + pos++; + + slen = *pos++; + next = pos + slen; + if (next > end || slen < 2) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invalid IE in GAS Initial Response"); + return; + } + pos++; /* skip QueryRespLenLimit and PAME-BI */ + + if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported GAS advertisement protocol id %u", + *pos); + return; + } + + pos = next; + /* Query Response */ + if (pos + 2 > end) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Too short Query " + "Response"); + return; + } + slen = WPA_GET_LE16(pos); + pos += 2; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Query Response Length: %d", + slen); + if (pos + slen > end) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Not enough Query " + "Response data"); + return; + } + end = pos + slen; + + if (comeback_delay) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Fragmented " + "response - request fragments"); + if (p2p->sd_rx_resp) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Drop " + "old SD reassembly buffer"); + wpabuf_free(p2p->sd_rx_resp); + p2p->sd_rx_resp = NULL; + } + p2p_send_gas_comeback_req(p2p, sa, dialog_token, rx_freq); + return; + } + + /* ANQP Query Response */ + if (pos + 4 > end) + return; + if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); + return; + } + pos += 2; + + slen = WPA_GET_LE16(pos); + pos += 2; + if (pos + slen > end || slen < 3 + 1) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invalid ANQP Query Response length"); + return; + } + + if (WPA_GET_BE24(pos) != OUI_WFA) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported ANQP OUI %06x", WPA_GET_BE24(pos)); + return; + } + pos += 3; + + if (*pos != P2P_OUI_TYPE) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported ANQP vendor type %u", *pos); + return; + } + pos++; + + if (pos + 2 > end) + return; + update_indic = WPA_GET_LE16(pos); + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Service Update Indicator: %u", update_indic); + pos += 2; + + p2p->sd_peer->flags |= P2P_DEV_SD_INFO; + p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE; + p2p->sd_peer = NULL; + + if (p2p->sd_query) { + if (!p2p->sd_query->for_all_peers) { + struct p2p_sd_query *q; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Remove completed SD query %p", + p2p->sd_query); + q = p2p->sd_query; + p2p_unlink_sd_query(p2p, p2p->sd_query); + p2p_free_sd_query(q); + } + p2p->sd_query = NULL; + } + + if (p2p->cfg->sd_response) + p2p->cfg->sd_response(p2p->cfg->cb_ctx, sa, update_indic, + pos, end - pos); + p2p_continue_find(p2p); +} + + +void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + struct wpabuf *resp; + u8 dialog_token; + size_t frag_len; + int more = 0; + + wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Request", data, len); + if (len < 1) + return; + dialog_token = *data; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Dialog Token: %u", + dialog_token); + if (dialog_token != p2p->sd_resp_dialog_token) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending SD " + "response fragment for dialog token %u", dialog_token); + return; + } + + if (p2p->sd_resp == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending SD " + "response fragment available"); + return; + } + if (os_memcmp(sa, p2p->sd_resp_addr, ETH_ALEN) != 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending SD " + "response fragment for " MACSTR, MAC2STR(sa)); + return; + } + + frag_len = wpabuf_len(p2p->sd_resp) - p2p->sd_resp_pos; + if (frag_len > 1400) { + frag_len = 1400; + more = 1; + } + resp = p2p_build_gas_comeback_resp(dialog_token, WLAN_STATUS_SUCCESS, + p2p->srv_update_indic, + wpabuf_head_u8(p2p->sd_resp) + + p2p->sd_resp_pos, frag_len, + p2p->sd_frag_id, more, + wpabuf_len(p2p->sd_resp)); + if (resp == NULL) + return; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send GAS Comeback " + "Response (frag_id %d more=%d frag_len=%d)", + p2p->sd_frag_id, more, (int) frag_len); + p2p->sd_frag_id++; + p2p->sd_resp_pos += frag_len; + + if (more) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: %d more bytes " + "remain to be sent", + (int) (wpabuf_len(p2p->sd_resp) - p2p->sd_resp_pos)); + } else { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: All fragments of " + "SD response sent"); + wpabuf_free(p2p->sd_resp); + p2p->sd_resp = NULL; + } + + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + if (p2p_send_action(p2p, rx_freq, sa, p2p->cfg->dev_addr, + p2p->cfg->dev_addr, + wpabuf_head(resp), wpabuf_len(resp), 200) < 0) + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + + wpabuf_free(resp); +} + + +void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + const u8 *pos = data; + const u8 *end = data + len; + const u8 *next; + u8 dialog_token; + u16 status_code; + u8 frag_id; + u8 more_frags; + u16 comeback_delay; + u16 slen; + + wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Response", data, len); + + if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL || + os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Ignore unexpected GAS Comeback Response from " + MACSTR, MAC2STR(sa)); + return; + } + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p_clear_timeout(p2p); + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received GAS Comeback Response from " MACSTR " (len=%d)", + MAC2STR(sa), (int) len); + + if (len < 6 + 2) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Too short GAS Comeback Response frame"); + return; + } + + dialog_token = *pos++; + /* TODO: check dialog_token match */ + status_code = WPA_GET_LE16(pos); + pos += 2; + frag_id = *pos & 0x7f; + more_frags = (*pos & 0x80) >> 7; + pos++; + comeback_delay = WPA_GET_LE16(pos); + pos += 2; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: dialog_token=%u status_code=%u frag_id=%d more_frags=%d " + "comeback_delay=%u", + dialog_token, status_code, frag_id, more_frags, + comeback_delay); + /* TODO: check frag_id match */ + if (status_code) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Service Discovery failed: status code %u", + status_code); + return; + } + + if (*pos != WLAN_EID_ADV_PROTO) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unexpected IE in GAS Comeback Response: %u", + *pos); + return; + } + pos++; + + slen = *pos++; + next = pos + slen; + if (next > end || slen < 2) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invalid IE in GAS Comeback Response"); + return; + } + pos++; /* skip QueryRespLenLimit and PAME-BI */ + + if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported GAS advertisement protocol id %u", + *pos); + return; + } + + pos = next; + /* Query Response */ + if (pos + 2 > end) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Too short Query " + "Response"); + return; + } + slen = WPA_GET_LE16(pos); + pos += 2; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Query Response Length: %d", + slen); + if (pos + slen > end) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Not enough Query " + "Response data"); + return; + } + if (slen == 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No Query Response " + "data"); + return; + } + end = pos + slen; + + if (p2p->sd_rx_resp) { + /* + * ANQP header is only included in the first fragment; rest of + * the fragments start with continue TLVs. + */ + goto skip_nqp_header; + } + + /* ANQP Query Response */ + if (pos + 4 > end) + return; + if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); + return; + } + pos += 2; + + slen = WPA_GET_LE16(pos); + pos += 2; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: ANQP Query Response " + "length: %u", slen); + if (slen < 3 + 1) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invalid ANQP Query Response length"); + return; + } + if (pos + 4 > end) + return; + + if (WPA_GET_BE24(pos) != OUI_WFA) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported ANQP OUI %06x", WPA_GET_BE24(pos)); + return; + } + pos += 3; + + if (*pos != P2P_OUI_TYPE) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported ANQP vendor type %u", *pos); + return; + } + pos++; + + if (pos + 2 > end) + return; + p2p->sd_rx_update_indic = WPA_GET_LE16(pos); + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Service Update Indicator: %u", p2p->sd_rx_update_indic); + pos += 2; + +skip_nqp_header: + if (wpabuf_resize(&p2p->sd_rx_resp, end - pos) < 0) + return; + wpabuf_put_data(p2p->sd_rx_resp, pos, end - pos); + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Current SD reassembly " + "buffer length: %u", + (unsigned int) wpabuf_len(p2p->sd_rx_resp)); + + if (more_frags) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: More fragments " + "remains"); + /* TODO: what would be a good size limit? */ + if (wpabuf_len(p2p->sd_rx_resp) > 64000) { + wpabuf_free(p2p->sd_rx_resp); + p2p->sd_rx_resp = NULL; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Too long " + "SD response - drop it"); + return; + } + p2p_send_gas_comeback_req(p2p, sa, dialog_token, rx_freq); + return; + } + + p2p->sd_peer->flags |= P2P_DEV_SD_INFO; + p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE; + p2p->sd_peer = NULL; + + if (p2p->sd_query) { + if (!p2p->sd_query->for_all_peers) { + struct p2p_sd_query *q; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Remove completed SD query %p", + p2p->sd_query); + q = p2p->sd_query; + p2p_unlink_sd_query(p2p, p2p->sd_query); + p2p_free_sd_query(q); + } + p2p->sd_query = NULL; + } + + if (p2p->cfg->sd_response) + p2p->cfg->sd_response(p2p->cfg->cb_ctx, sa, + p2p->sd_rx_update_indic, + wpabuf_head(p2p->sd_rx_resp), + wpabuf_len(p2p->sd_rx_resp)); + wpabuf_free(p2p->sd_rx_resp); + p2p->sd_rx_resp = NULL; + + p2p_continue_find(p2p); +} + + +void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst, + const struct wpabuf *tlvs) +{ + struct p2p_sd_query *q; + + q = os_zalloc(sizeof(*q)); + if (q == NULL) + return NULL; + + if (dst) + os_memcpy(q->peer, dst, ETH_ALEN); + else + q->for_all_peers = 1; + + q->tlvs = wpabuf_dup(tlvs); + if (q->tlvs == NULL) { + p2p_free_sd_query(q); + return NULL; + } + + q->next = p2p->sd_queries; + p2p->sd_queries = q; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Added SD Query %p", q); + + if (dst == NULL) { + struct p2p_device *dev; + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) + dev->flags &= ~P2P_DEV_SD_INFO; + } + + return q; +} + + +#ifdef CONFIG_WIFI_DISPLAY +void * p2p_sd_request_wfd(struct p2p_data *p2p, const u8 *dst, + const struct wpabuf *tlvs) +{ + struct p2p_sd_query *q; + q = p2p_sd_request(p2p, dst, tlvs); + if (q) + q->wsd = 1; + return q; +} +#endif /* CONFIG_WIFI_DISPLAY */ + + +void p2p_sd_service_update(struct p2p_data *p2p) +{ + p2p->srv_update_indic++; +} + + +int p2p_sd_cancel_request(struct p2p_data *p2p, void *req) +{ + if (p2p_unlink_sd_query(p2p, req)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Cancel pending SD query %p", req); + p2p_free_sd_query(req); + return 0; + } + return -1; +} diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c new file mode 100644 index 0000000000000..bcc690d8469bb --- /dev/null +++ b/src/p2p/p2p_utils.c @@ -0,0 +1,265 @@ +/* + * P2P - generic helper functions + * Copyright (c) 2009, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "p2p_i.h" + + +/** + * p2p_random - Generate random string for SSID and passphrase + * @buf: Buffer for returning the result + * @len: Number of octets to write to the buffer + * Returns: 0 on success, -1 on failure + * + * This function generates a random string using the following character set: + * 'A'-'Z', 'a'-'z', '0'-'9'. + */ +int p2p_random(char *buf, size_t len) +{ + u8 val; + size_t i; + u8 letters = 'Z' - 'A' + 1; + u8 numbers = 10; + + if (os_get_random((unsigned char *) buf, len)) + return -1; + /* Character set: 'A'-'Z', 'a'-'z', '0'-'9' */ + for (i = 0; i < len; i++) { + val = buf[i]; + val %= 2 * letters + numbers; + if (val < letters) + buf[i] = 'A' + val; + else if (val < 2 * letters) + buf[i] = 'a' + (val - letters); + else + buf[i] = '0' + (val - 2 * letters); + } + + return 0; +} + + +static int p2p_channel_to_freq_j4(int reg_class, int channel) +{ + /* Table J-4 in P802.11REVmb/D4.0 - Global operating classes */ + /* TODO: more regulatory classes */ + switch (reg_class) { + case 81: + /* channels 1..13 */ + if (channel < 1 || channel > 13) + return -1; + return 2407 + 5 * channel; + case 82: + /* channel 14 */ + if (channel != 14) + return -1; + return 2414 + 5 * channel; + case 83: /* channels 1..9; 40 MHz */ + case 84: /* channels 5..13; 40 MHz */ + if (channel < 1 || channel > 13) + return -1; + return 2407 + 5 * channel; + case 115: /* channels 36,40,44,48; indoor only */ + case 118: /* channels 52,56,60,64; dfs */ + if (channel < 36 || channel > 64) + return -1; + return 5000 + 5 * channel; + case 124: /* channels 149,153,157,161 */ + case 125: /* channels 149,153,157,161,165,169 */ + if (channel < 149 || channel > 161) + return -1; + return 5000 + 5 * channel; + case 116: /* channels 36,44; 40 MHz; indoor only */ + case 117: /* channels 40,48; 40 MHz; indoor only */ + case 119: /* channels 52,60; 40 MHz; dfs */ + case 120: /* channels 56,64; 40 MHz; dfs */ + if (channel < 36 || channel > 64) + return -1; + return 5000 + 5 * channel; + case 126: /* channels 149,157; 40 MHz */ + case 127: /* channels 153,161; 40 MHz */ + if (channel < 149 || channel > 161) + return -1; + return 5000 + 5 * channel; + } + return -1; +} + + +/** + * p2p_channel_to_freq - Convert channel info to frequency + * @country: Country code + * @reg_class: Regulatory class + * @channel: Channel number + * Returns: Frequency in MHz or -1 if the specified channel is unknown + */ +int p2p_channel_to_freq(const char *country, int reg_class, int channel) +{ + if (country[2] == 0x04) + return p2p_channel_to_freq_j4(reg_class, channel); + + /* These are mainly for backwards compatibility; to be removed */ + switch (reg_class) { + case 1: /* US/1, EU/1, JP/1 = 5 GHz, channels 36,40,44,48 */ + if (channel < 36 || channel > 48) + return -1; + return 5000 + 5 * channel; + case 3: /* US/3 = 5 GHz, channels 149,153,157,161 */ + case 5: /* US/5 = 5 GHz, channels 149,153,157,161 */ + if (channel < 149 || channel > 161) + return -1; + return 5000 + 5 * channel; + case 4: /* EU/4 = 2.407 GHz, channels 1..13 */ + case 12: /* US/12 = 2.407 GHz, channels 1..11 */ + case 30: /* JP/30 = 2.407 GHz, channels 1..13 */ + if (channel < 1 || channel > 13) + return -1; + return 2407 + 5 * channel; + case 31: /* JP/31 = 2.414 GHz, channel 14 */ + if (channel != 14) + return -1; + return 2414 + 5 * channel; + } + + return -1; +} + + +/** + * p2p_freq_to_channel - Convert frequency into channel info + * @country: Country code + * @reg_class: Buffer for returning regulatory class + * @channel: Buffer for returning channel number + * Returns: 0 on success, -1 if the specified frequency is unknown + */ +int p2p_freq_to_channel(const char *country, unsigned int freq, u8 *reg_class, + u8 *channel) +{ + /* TODO: more operating classes */ + if (freq >= 2412 && freq <= 2472) { + *reg_class = 81; /* 2.407 GHz, channels 1..13 */ + *channel = (freq - 2407) / 5; + return 0; + } + + if (freq == 2484) { + *reg_class = 82; /* channel 14 */ + *channel = 14; + return 0; + } + + if (freq >= 5180 && freq <= 5240) { + *reg_class = 115; /* 5 GHz, channels 36..48 */ + *channel = (freq - 5000) / 5; + return 0; + } + + if (freq >= 5745 && freq <= 5805) { + *reg_class = 124; /* 5 GHz, channels 149..161 */ + *channel = (freq - 5000) / 5; + return 0; + } + + return -1; +} + + +static void p2p_reg_class_intersect(const struct p2p_reg_class *a, + const struct p2p_reg_class *b, + struct p2p_reg_class *res) +{ + size_t i, j; + + res->reg_class = a->reg_class; + + for (i = 0; i < a->channels; i++) { + for (j = 0; j < b->channels; j++) { + if (a->channel[i] != b->channel[j]) + continue; + res->channel[res->channels] = a->channel[i]; + res->channels++; + if (res->channels == P2P_MAX_REG_CLASS_CHANNELS) + return; + } + } +} + + +/** + * p2p_channels_intersect - Intersection of supported channel lists + * @a: First set of supported channels + * @b: Second set of supported channels + * @res: Data structure for returning the intersection of support channels + * + * This function can be used to find a common set of supported channels. Both + * input channels sets are assumed to use the same country code. If different + * country codes are used, the regulatory class numbers may not be matched + * correctly and results are undefined. + */ +void p2p_channels_intersect(const struct p2p_channels *a, + const struct p2p_channels *b, + struct p2p_channels *res) +{ + size_t i, j; + + os_memset(res, 0, sizeof(*res)); + + for (i = 0; i < a->reg_classes; i++) { + const struct p2p_reg_class *a_reg = &a->reg_class[i]; + for (j = 0; j < b->reg_classes; j++) { + const struct p2p_reg_class *b_reg = &b->reg_class[j]; + if (a_reg->reg_class != b_reg->reg_class) + continue; + p2p_reg_class_intersect( + a_reg, b_reg, + &res->reg_class[res->reg_classes]); + if (res->reg_class[res->reg_classes].channels) { + res->reg_classes++; + if (res->reg_classes == P2P_MAX_REG_CLASSES) + return; + } + } + } +} + + +/** + * p2p_channels_includes - Check whether a channel is included in the list + * @channels: List of supported channels + * @reg_class: Regulatory class of the channel to search + * @channel: Channel number of the channel to search + * Returns: 1 if channel was found or 0 if not + */ +int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class, + u8 channel) +{ + size_t i, j; + for (i = 0; i < channels->reg_classes; i++) { + const struct p2p_reg_class *reg = &channels->reg_class[i]; + if (reg->reg_class != reg_class) + continue; + for (j = 0; j < reg->channels; j++) { + if (reg->channel[j] == channel) + return 1; + } + } + return 0; +} + + +int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq) +{ + u8 op_reg_class, op_channel; + if (p2p_freq_to_channel(p2p->cfg->country, freq, + &op_reg_class, &op_channel) < 0) + return 0; + return p2p_channels_includes(&p2p->cfg->channels, op_reg_class, + op_channel); +} diff --git a/src/radius/radius.c b/src/radius/radius.c index 70754ef5dd725..d1feec96842f5 100644 --- a/src/radius/radius.c +++ b/src/radius/radius.c @@ -1,15 +1,9 @@ /* * RADIUS message processing - * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2009, 2011-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 - * published 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 "utils/includes.h" @@ -84,8 +78,8 @@ static void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier) static int radius_msg_initialize(struct radius_msg *msg) { - msg->attr_pos = - os_zalloc(RADIUS_DEFAULT_ATTR_COUNT * sizeof(*msg->attr_pos)); + msg->attr_pos = os_calloc(RADIUS_DEFAULT_ATTR_COUNT, + sizeof(*msg->attr_pos)); if (msg->attr_pos == NULL) return -1; @@ -153,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?"; } } @@ -218,6 +218,8 @@ static struct radius_attr_type radius_attrs[] = { RADIUS_ATTR_TUNNEL_TYPE, "Tunnel-Type", RADIUS_ATTR_HEXDUMP }, { RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, "Tunnel-Medium-Type", RADIUS_ATTR_HEXDUMP }, + { RADIUS_ATTR_TUNNEL_PASSWORD, "Tunnel-Password", + RADIUS_ATTR_UNDIST }, { RADIUS_ATTR_CONNECT_INFO, "Connect-Info", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_EAP_MESSAGE, "EAP-Message", RADIUS_ATTR_UNDIST }, { RADIUS_ATTR_MESSAGE_AUTHENTICATOR, "Message-Authenticator", @@ -226,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])) @@ -266,7 +269,7 @@ static void radius_msg_dump_attr(struct radius_attr_hdr *hdr) printf(" Attribute %d (%s) length=%d\n", hdr->type, attr ? attr->name : "?Unknown?", hdr->length); - if (attr == NULL) + if (attr == NULL || hdr->length < sizeof(struct radius_attr_hdr)) return; len = hdr->length - sizeof(struct radius_attr_hdr); @@ -329,7 +332,7 @@ void radius_msg_dump(struct radius_msg *msg) printf("RADIUS message: code=%d (%s) identifier=%d length=%d\n", msg->hdr->code, radius_code_string(msg->hdr->code), - msg->hdr->identifier, ntohs(msg->hdr->length)); + msg->hdr->identifier, be_to_host16(msg->hdr->length)); for (i = 0; i < msg->attr_used; i++) { struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); @@ -354,11 +357,11 @@ int radius_msg_finish(struct radius_msg *msg, const u8 *secret, "Message-Authenticator"); return -1; } - msg->hdr->length = htons(wpabuf_len(msg->buf)); + msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); hmac_md5(secret, secret_len, wpabuf_head(msg->buf), wpabuf_len(msg->buf), (u8 *) (attr + 1)); } else - msg->hdr->length = htons(wpabuf_len(msg->buf)); + msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); if (wpabuf_len(msg->buf) > 0xffff) { wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)", @@ -384,7 +387,7 @@ int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, printf("WARNING: Could not add Message-Authenticator\n"); return -1; } - msg->hdr->length = htons(wpabuf_len(msg->buf)); + msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); os_memcpy(msg->hdr->authenticator, req_authenticator, sizeof(msg->hdr->authenticator)); hmac_md5(secret, secret_len, wpabuf_head(msg->buf), @@ -410,13 +413,52 @@ 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 = host_to_be16(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) { const u8 *addr[2]; size_t len[2]; - msg->hdr->length = htons(wpabuf_len(msg->buf)); + msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); os_memset(msg->hdr->authenticator, 0, MD5_MAC_LEN); addr[0] = wpabuf_head(msg->buf); len[0] = wpabuf_len(msg->buf); @@ -431,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) { @@ -438,8 +562,8 @@ static int radius_msg_add_attr_to_array(struct radius_msg *msg, size_t *nattr_pos; int nlen = msg->attr_size * 2; - nattr_pos = os_realloc(msg->attr_pos, - nlen * sizeof(*msg->attr_pos)); + nattr_pos = os_realloc_array(msg->attr_pos, nlen, + sizeof(*msg->attr_pos)); if (nattr_pos == NULL) return -1; @@ -509,7 +633,7 @@ struct radius_msg * radius_msg_parse(const u8 *data, size_t len) hdr = (struct radius_hdr *) data; - msg_len = ntohs(hdr->length); + msg_len = be_to_host16(hdr->length); if (msg_len < sizeof(*hdr) || msg_len > len) { wpa_printf(MSG_INFO, "RADIUS: Invalid message length"); return NULL; @@ -583,9 +707,9 @@ int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len) } -u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *eap_len) +struct wpabuf * radius_msg_get_eap(struct radius_msg *msg) { - u8 *eap, *pos; + struct wpabuf *eap; size_t len, i; struct radius_attr_hdr *attr; @@ -595,30 +719,27 @@ u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *eap_len) len = 0; for (i = 0; i < msg->attr_used; i++) { attr = radius_get_attr_hdr(msg, i); - if (attr->type == RADIUS_ATTR_EAP_MESSAGE) + if (attr->type == RADIUS_ATTR_EAP_MESSAGE && + attr->length > sizeof(struct radius_attr_hdr)) len += attr->length - sizeof(struct radius_attr_hdr); } if (len == 0) return NULL; - eap = os_malloc(len); + eap = wpabuf_alloc(len); if (eap == NULL) return NULL; - pos = eap; for (i = 0; i < msg->attr_used; i++) { attr = radius_get_attr_hdr(msg, i); - if (attr->type == RADIUS_ATTR_EAP_MESSAGE) { + if (attr->type == RADIUS_ATTR_EAP_MESSAGE && + attr->length > sizeof(struct radius_attr_hdr)) { int flen = attr->length - sizeof(*attr); - os_memcpy(pos, attr + 1, flen); - pos += flen; + wpabuf_put_data(eap, attr + 1, flen); } } - if (eap_len) - *eap_len = len; - return eap; } @@ -719,7 +840,7 @@ int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src, for (i = 0; i < src->attr_used; i++) { attr = radius_get_attr_hdr(src, i); - if (attr->type == type) { + if (attr->type == type && attr->length >= sizeof(*attr)) { if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1), attr->length - sizeof(*attr))) return -1; @@ -776,7 +897,8 @@ static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor, u32 vendor_id; struct radius_attr_vendor *vhdr; - if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC) + if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC || + attr->length < sizeof(*attr)) continue; left = attr->length - sizeof(*attr); @@ -1090,8 +1212,7 @@ radius_msg_add_attr_user_password(struct radius_msg *msg, const u8 *secret, size_t secret_len) { u8 buf[128]; - int padlen, i; - size_t buf_len, pos; + size_t padlen, i, buf_len, pos; const u8 *addr[2]; size_t len[2]; u8 hash[16]; @@ -1103,7 +1224,7 @@ radius_msg_add_attr_user_password(struct radius_msg *msg, buf_len = data_len; padlen = data_len % 16; - if (padlen) { + if (padlen && data_len < sizeof(buf)) { padlen = 16 - padlen; os_memset(buf + data_len, 0, padlen); buf_len += padlen; @@ -1150,7 +1271,7 @@ int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len) } } - if (!attr) + if (!attr || attr->length < sizeof(*attr)) return -1; dlen = attr->length - sizeof(*attr); @@ -1175,7 +1296,7 @@ int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf, } } - if (!attr) + if (!attr || attr->length < sizeof(*attr)) return -1; *buf = (u8 *) (attr + 1); @@ -1226,6 +1347,8 @@ int radius_msg_get_vlanid(struct radius_msg *msg) for (i = 0; i < msg->attr_used; i++) { attr = radius_get_attr_hdr(msg, i); + if (attr->length < sizeof(*attr)) + return -1; data = (const u8 *) (attr + 1); dlen = attr->length - sizeof(*attr); if (attr->length < 3) @@ -1276,6 +1399,123 @@ int radius_msg_get_vlanid(struct radius_msg *msg) } +/** + * radius_msg_get_tunnel_password - Parse RADIUS attribute Tunnel-Password + * @msg: Received RADIUS message + * @keylen: Length of returned password + * @secret: RADIUS shared secret + * @secret_len: Length of secret + * @sent_msg: Sent RADIUS message + * @n: Number of password attribute to return (starting with 0) + * Returns: Pointer to n-th password (free with os_free) or %NULL + */ +char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen, + const u8 *secret, size_t secret_len, + struct radius_msg *sent_msg, size_t n) +{ + u8 *buf = NULL; + size_t buflen; + const u8 *salt; + u8 *str; + const u8 *addr[3]; + size_t len[3]; + u8 hash[16]; + u8 *pos; + size_t i, j = 0; + struct radius_attr_hdr *attr; + const u8 *data; + size_t dlen; + const u8 *fdata = NULL; /* points to found item */ + size_t fdlen = -1; + char *ret = NULL; + + /* find n-th valid Tunnel-Password attribute */ + for (i = 0; i < msg->attr_used; i++) { + attr = radius_get_attr_hdr(msg, i); + if (attr == NULL || + attr->type != RADIUS_ATTR_TUNNEL_PASSWORD) { + continue; + } + if (attr->length <= 5) + continue; + data = (const u8 *) (attr + 1); + dlen = attr->length - sizeof(*attr); + if (dlen <= 3 || dlen % 16 != 3) + continue; + j++; + if (j <= n) + continue; + + fdata = data; + fdlen = dlen; + break; + } + if (fdata == NULL) + goto out; + + /* alloc writable memory for decryption */ + buf = os_malloc(fdlen); + if (buf == NULL) + goto out; + os_memcpy(buf, fdata, fdlen); + buflen = fdlen; + + /* init pointers */ + salt = buf + 1; + str = buf + 3; + + /* decrypt blocks */ + pos = buf + buflen - 16; /* last block */ + while (pos >= str + 16) { /* all but the first block */ + addr[0] = secret; + len[0] = secret_len; + addr[1] = pos - 16; + len[1] = 16; + md5_vector(2, addr, len, hash); + + for (i = 0; i < 16; i++) + pos[i] ^= hash[i]; + + pos -= 16; + } + + /* decrypt first block */ + if (str != pos) + goto out; + addr[0] = secret; + len[0] = secret_len; + addr[1] = sent_msg->hdr->authenticator; + len[1] = 16; + addr[2] = salt; + len[2] = 2; + md5_vector(3, addr, len, hash); + + for (i = 0; i < 16; i++) + pos[i] ^= hash[i]; + + /* derive plaintext length from first subfield */ + *keylen = (unsigned char) str[0]; + if ((u8 *) (str + *keylen) >= (u8 *) (buf + buflen)) { + /* decryption error - invalid key length */ + goto out; + } + if (*keylen == 0) { + /* empty password */ + goto out; + } + + /* copy passphrase into new buffer */ + ret = os_malloc(*keylen); + if (ret) + os_memcpy(ret, str + 1, *keylen); + +out: + /* return new buffer */ + os_free(buf); + return ret; +} + + void radius_free_class(struct radius_class_data *c) { size_t i; @@ -1297,7 +1537,7 @@ int radius_copy_class(struct radius_class_data *dst, if (src->attr == NULL) return 0; - dst->attr = os_zalloc(src->count * sizeof(struct radius_attr_data)); + dst->attr = os_calloc(src->count, sizeof(struct radius_attr_data)); if (dst->attr == NULL) return -1; @@ -1315,3 +1555,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 a3cdac0dac0aa..2031054b1d231 100644 --- a/src/radius/radius.h +++ b/src/radius/radius.h @@ -1,15 +1,9 @@ /* * RADIUS message processing - * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2009, 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 - * published 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. */ #ifndef RADIUS_H @@ -24,7 +18,7 @@ struct radius_hdr { u8 code; u8 identifier; - u16 length; /* including this header */ + be16 length; /* including this header */ u8 authenticator[16]; /* followed by length-20 octets of attributes */ } STRUCT_PACKED; @@ -37,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 }; @@ -82,13 +82,15 @@ enum { RADIUS_ATTR_USER_NAME = 1, RADIUS_ATTR_NAS_PORT_TYPE = 61, RADIUS_ATTR_TUNNEL_TYPE = 64, RADIUS_ATTR_TUNNEL_MEDIUM_TYPE = 65, + RADIUS_ATTR_TUNNEL_PASSWORD = 69, RADIUS_ATTR_CONNECT_INFO = 77, RADIUS_ATTR_EAP_MESSAGE = 79, RADIUS_ATTR_MESSAGE_AUTHENTICATOR = 80, RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID = 81, RADIUS_ATTR_ACCT_INTERIM_INTERVAL = 85, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY = 89, - RADIUS_ATTR_NAS_IPV6_ADDRESS = 95 + RADIUS_ATTR_NAS_IPV6_ADDRESS = 95, + RADIUS_ATTR_ERROR_CAUSE = 101 }; @@ -197,14 +199,21 @@ 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); int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len); -u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *len); +struct wpabuf * radius_msg_get_eap(struct radius_msg *msg); int radius_msg_verify(struct radius_msg *msg, const u8 *secret, size_t secret_len, struct radius_msg *sent_msg, int auth); @@ -231,6 +240,9 @@ radius_msg_add_attr_user_password(struct radius_msg *msg, const u8 *secret, size_t secret_len); int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len); int radius_msg_get_vlanid(struct radius_msg *msg); +char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen, + const u8 *secret, size_t secret_len, + struct radius_msg *sent_msg, size_t n); static inline int radius_msg_add_attr_int32(struct radius_msg *msg, u8 type, u32 value) @@ -270,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 171af2927b0fb..425ad935afbfe 100644 --- a/src/radius/radius_client.c +++ b/src/radius/radius_client.c @@ -2,14 +2,8 @@ * RADIUS client * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -287,8 +281,8 @@ int radius_client_register(struct radius_client_data *radius, num = &radius->num_auth_handlers; } - newh = os_realloc(*handlers, - (*num + 1) * sizeof(struct radius_rx_handler)); + newh = os_realloc_array(*handlers, *num + 1, + sizeof(struct radius_rx_handler)); if (newh == NULL) return -1; @@ -511,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)); } @@ -684,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; } @@ -1489,3 +1483,11 @@ int radius_client_get_mib(struct radius_client_data *radius, char *buf, return count; } + + +void radius_client_reconfig(struct radius_client_data *radius, + struct hostapd_radius_servers *conf) +{ + if (radius) + radius->conf = conf; +} diff --git a/src/radius/radius_client.h b/src/radius/radius_client.h index 644ea234fd253..3db16aa282ba7 100644 --- a/src/radius/radius_client.h +++ b/src/radius/radius_client.h @@ -2,14 +2,8 @@ * RADIUS client * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef RADIUS_CLIENT_H @@ -259,5 +253,7 @@ void radius_client_flush_auth(struct radius_client_data *radius, const u8 *addr); int radius_client_get_mib(struct radius_client_data *radius, char *buf, size_t buflen); +void radius_client_reconfig(struct radius_client_data *radius, + struct hostapd_radius_servers *conf); #endif /* RADIUS_CLIENT_H */ diff --git a/src/radius/radius_das.c b/src/radius/radius_das.c new file mode 100644 index 0000000000000..bded96519929e --- /dev/null +++ b/src/radius/radius_das.c @@ -0,0 +1,364 @@ +/* + * 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) { + if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, + error)) { + radius_msg_free(reply); + return NULL; + } + } + + 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 */ + if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, + 405)) { + radius_msg_free(reply); + reply = NULL; + break; + } + 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 0000000000000..738b18b059d63 --- /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/radius/radius_server.c b/src/radius/radius_server.c index f8780a6929476..5b2d711118109 100644 --- a/src/radius/radius_server.c +++ b/src/radius/radius_server.c @@ -1,15 +1,9 @@ /* * RADIUS authentication server - * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2005-2009, 2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -222,6 +216,13 @@ struct radius_server_data { int tnc; /** + * pwd_group - The D-H group assigned for EAP-pwd + * + * If EAP-pwd is not used it can be set to zero. + */ + u16 pwd_group; + + /** * wps - Wi-Fi Protected Setup context * * If WPS is used with an external RADIUS server (which is quite @@ -285,6 +286,10 @@ struct radius_server_data { * msg_ctx - Context data for wpa_msg() calls */ void *msg_ctx; + +#ifdef CONFIG_RADIUS_TEST + char *dump_msk_file; +#endif /* CONFIG_RADIUS_TEST */ }; @@ -505,6 +510,7 @@ radius_server_get_new_session(struct radius_server_data *data, eap_conf.eap_sim_aka_result_ind = data->eap_sim_aka_result_ind; eap_conf.tnc = data->tnc; eap_conf.wps = data->wps; + eap_conf.pwd_group = data->pwd_group; sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb, &eap_conf); if (sess->eap == NULL) { @@ -566,6 +572,24 @@ radius_server_encapsulate_eap(struct radius_server_data *data, if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->eap_if->eapKeyData) { int len; +#ifdef CONFIG_RADIUS_TEST + if (data->dump_msk_file) { + FILE *f; + char buf[2 * 64 + 1]; + f = fopen(data->dump_msk_file, "a"); + if (f) { + len = sess->eap_if->eapKeyDataLen; + if (len > 64) + len = 64; + len = wpa_snprintf_hex( + buf, sizeof(buf), + sess->eap_if->eapKeyData, len); + buf[len] = '\0'; + fprintf(f, "%s\n", buf); + fclose(f); + } + } +#endif /* CONFIG_RADIUS_TEST */ if (sess->eap_if->eapKeyDataLen > 64) { len = 32; } else { @@ -665,8 +689,7 @@ static int radius_server_request(struct radius_server_data *data, const char *from_addr, int from_port, struct radius_session *force_sess) { - u8 *eap = NULL; - size_t eap_len; + struct wpabuf *eap = NULL; int res, state_included = 0; u8 statebuf[4]; unsigned int state; @@ -730,7 +753,7 @@ static int radius_server_request(struct radius_server_data *data, return -1; } - eap = radius_msg_get_eap(msg, &eap_len); + eap = radius_msg_get_eap(msg); if (eap == NULL) { RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s", from_addr); @@ -739,7 +762,7 @@ static int radius_server_request(struct radius_server_data *data, return -1; } - RADIUS_DUMP("Received EAP data", eap, eap_len); + RADIUS_DUMP("Received EAP data", wpabuf_head(eap), wpabuf_len(eap)); /* FIX: if Code is Request, Success, or Failure, send Access-Reject; * RFC3579 Sect. 2.6.2. @@ -749,10 +772,7 @@ static int radius_server_request(struct radius_server_data *data, * Or is this already done by the EAP state machine? */ wpabuf_free(sess->eap_if->eapRespData); - sess->eap_if->eapRespData = wpabuf_alloc_ext_data(eap, eap_len); - if (sess->eap_if->eapRespData == NULL) - os_free(eap); - eap = NULL; + sess->eap_if->eapRespData = eap; sess->eap_if->eapResp = TRUE; eap_server_sm_step(sess->eap); @@ -1259,6 +1279,7 @@ radius_server_init(struct radius_server_conf *conf) data->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind; data->tnc = conf->tnc; data->wps = conf->wps; + data->pwd_group = conf->pwd_group; if (conf->eap_req_id_text) { data->eap_req_id_text = os_malloc(conf->eap_req_id_text_len); if (data->eap_req_id_text) { @@ -1268,6 +1289,11 @@ radius_server_init(struct radius_server_conf *conf) } } +#ifdef CONFIG_RADIUS_TEST + if (conf->dump_msk_file) + data->dump_msk_file = os_strdup(conf->dump_msk_file); +#endif /* CONFIG_RADIUS_TEST */ + data->clients = radius_server_read_clients(conf->client_file, conf->ipv6); if (data->clients == NULL) { @@ -1319,6 +1345,9 @@ void radius_server_deinit(struct radius_server_data *data) os_free(data->eap_fast_a_id); os_free(data->eap_fast_a_id_info); os_free(data->eap_req_id_text); +#ifdef CONFIG_RADIUS_TEST + os_free(data->dump_msk_file); +#endif /* CONFIG_RADIUS_TEST */ os_free(data); } diff --git a/src/radius/radius_server.h b/src/radius/radius_server.h index f9c951d05f241..82466c30219f3 100644 --- a/src/radius/radius_server.h +++ b/src/radius/radius_server.h @@ -1,15 +1,9 @@ /* * RADIUS authentication server - * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2005-2009, 2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef RADIUS_SERVER_H @@ -143,6 +137,13 @@ struct radius_server_conf { int tnc; /** + * pwd_group - EAP-pwd D-H group + * + * This is used to select which D-H group to use with EAP-pwd. + */ + u16 pwd_group; + + /** * wps - Wi-Fi Protected Setup context * * If WPS is used with an external RADIUS server (which is quite @@ -194,6 +195,10 @@ struct radius_server_conf { * msg_ctx - Context data for wpa_msg() calls */ void *msg_ctx; + +#ifdef CONFIG_RADIUS_TEST + const char *dump_msk_file; +#endif /* CONFIG_RADIUS_TEST */ }; diff --git a/src/rsn_supp/peerkey.c b/src/rsn_supp/peerkey.c index 9d60d4acedf57..f2bac348bce7f 100644 --- a/src/rsn_supp/peerkey.c +++ b/src/rsn_supp/peerkey.c @@ -2,14 +2,8 @@ * WPA Supplicant - PeerKey for Direct Link Setup (DLS) * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -20,6 +14,7 @@ #include "eloop.h" #include "crypto/sha1.h" #include "crypto/sha256.h" +#include "crypto/random.h" #include "common/ieee802_11_defs.h" #include "wpa.h" #include "wpa_i.h" @@ -226,6 +221,9 @@ static int wpa_supplicant_process_smk_m2( if (cipher & WPA_CIPHER_CCMP) { wpa_printf(MSG_DEBUG, "RSN: Using CCMP for PeerKey"); cipher = WPA_CIPHER_CCMP; + } else if (cipher & WPA_CIPHER_GCMP) { + wpa_printf(MSG_DEBUG, "RSN: Using GCMP for PeerKey"); + cipher = WPA_CIPHER_GCMP; } else if (cipher & WPA_CIPHER_TKIP) { wpa_printf(MSG_DEBUG, "RSN: Using TKIP for PeerKey"); cipher = WPA_CIPHER_TKIP; @@ -254,7 +252,7 @@ static int wpa_supplicant_process_smk_m2( peerkey->use_sha256 = 1; #endif /* CONFIG_IEEE80211W */ - if (os_get_random(peerkey->pnonce, WPA_NONCE_LEN)) { + if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to get random data for PNonce"); wpa_supplicant_peerkey_free(sm, peerkey); @@ -272,10 +270,7 @@ static int wpa_supplicant_process_smk_m2( /* Include only the selected cipher in pairwise cipher suite */ WPA_PUT_LE16(pos, 1); pos += 2; - if (cipher == WPA_CIPHER_CCMP) - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); - else if (cipher == WPA_CIPHER_TKIP) - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); + RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, cipher)); pos += RSN_SELECTOR_LEN; hdr->len = (pos - peerkey->rsnie_p) - 2; @@ -349,7 +344,7 @@ static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm, msg->type = EAPOL_KEY_TYPE_RSN; - if (peerkey->cipher == WPA_CIPHER_CCMP) + if (peerkey->cipher != WPA_CIPHER_TKIP) ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; @@ -357,7 +352,7 @@ static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm, key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK; WPA_PUT_BE16(msg->key_info, key_info); - if (peerkey->cipher == WPA_CIPHER_CCMP) + if (peerkey->cipher != WPA_CIPHER_TKIP) WPA_PUT_BE16(msg->key_length, 16); else WPA_PUT_BE16(msg->key_length, 32); @@ -370,7 +365,7 @@ static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm, wpa_add_kde((u8 *) (msg + 1), RSN_KEY_DATA_PMKID, peerkey->smkid, PMKID_LEN); - if (os_get_random(peerkey->inonce, WPA_NONCE_LEN)) { + if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "RSN: Failed to get random data for INonce (STK)"); os_free(mbuf); @@ -408,7 +403,7 @@ static void wpa_supplicant_send_stk_3_of_4(struct wpa_sm *sm, msg->type = EAPOL_KEY_TYPE_RSN; - if (peerkey->cipher == WPA_CIPHER_CCMP) + if (peerkey->cipher != WPA_CIPHER_TKIP) ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; @@ -417,7 +412,7 @@ static void wpa_supplicant_send_stk_3_of_4(struct wpa_sm *sm, WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE; WPA_PUT_BE16(msg->key_info, key_info); - if (peerkey->cipher == WPA_CIPHER_CCMP) + if (peerkey->cipher != WPA_CIPHER_TKIP) WPA_PUT_BE16(msg->key_length, 16); else WPA_PUT_BE16(msg->key_length, 32); @@ -505,6 +500,9 @@ static int wpa_supplicant_process_smk_m5(struct wpa_sm *sm, if (cipher & WPA_CIPHER_CCMP) { wpa_printf(MSG_DEBUG, "RSN: Using CCMP for PeerKey"); peerkey->cipher = WPA_CIPHER_CCMP; + } else if (cipher & WPA_CIPHER_GCMP) { + wpa_printf(MSG_DEBUG, "RSN: Using GCMP for PeerKey"); + peerkey->cipher = WPA_CIPHER_GCMP; } else if (cipher & WPA_CIPHER_TKIP) { wpa_printf(MSG_DEBUG, "RSN: Using TKIP for PeerKey"); peerkey->cipher = WPA_CIPHER_TKIP; @@ -697,7 +695,7 @@ static void wpa_supplicant_process_stk_1_of_4(struct wpa_sm *sm, return; } - if (os_get_random(peerkey->pnonce, WPA_NONCE_LEN)) { + if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "RSN: Failed to get random data for PNonce"); return; @@ -1021,7 +1019,7 @@ int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) return -1; } - if (sm->pairwise_cipher == WPA_CIPHER_CCMP) + if (sm->pairwise_cipher != WPA_CIPHER_TKIP) ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; @@ -1060,17 +1058,8 @@ int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) count_pos = pos; pos += 2; - count = 0; - if (sm->allowed_pairwise_cipher & WPA_CIPHER_CCMP) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); - pos += RSN_SELECTOR_LEN; - count++; - } - if (sm->allowed_pairwise_cipher & WPA_CIPHER_TKIP) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); - pos += RSN_SELECTOR_LEN; - count++; - } + count = rsn_cipher_put_suites(pos, sm->allowed_pairwise_cipher); + pos += count * RSN_SELECTOR_LEN; WPA_PUT_LE16(count_pos, count); hdr->len = (pos - peerkey->rsnie_i) - 2; @@ -1097,7 +1086,7 @@ int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) WPA_REPLAY_COUNTER_LEN); inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN); - if (os_get_random(peerkey->inonce, WPA_NONCE_LEN)) { + if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to get random data for INonce"); os_free(rbuf); @@ -1140,6 +1129,7 @@ void peerkey_deinit(struct wpa_sm *sm) peerkey = peerkey->next; os_free(prev); } + sm->peerkey = NULL; } diff --git a/src/rsn_supp/peerkey.h b/src/rsn_supp/peerkey.h index 2613127c3ea48..b8845f7100721 100644 --- a/src/rsn_supp/peerkey.h +++ b/src/rsn_supp/peerkey.h @@ -2,14 +2,8 @@ * WPA Supplicant - PeerKey for Direct Link Setup (DLS) * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef PEERKEY_H diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c index cac8c83e6eeb4..df675834c6f48 100644 --- a/src/rsn_supp/pmksa_cache.c +++ b/src/rsn_supp/pmksa_cache.c @@ -1,15 +1,9 @@ /* * WPA Supplicant - RSN PMKSA cache - * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2009, 2011-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 - * published 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" @@ -31,7 +25,7 @@ struct rsn_pmksa_cache { struct wpa_sm *sm; /* TODO: get rid of this reference(?) */ void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx, - int replace); + enum pmksa_free_reason reason); void *ctx; }; @@ -47,10 +41,11 @@ static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry *entry, - int replace) + enum pmksa_free_reason reason) { + wpa_sm_remove_pmkid(pmksa->sm, entry->aa, entry->pmkid); pmksa->pmksa_count--; - pmksa->free_cb(entry, pmksa->ctx, replace); + pmksa->free_cb(entry, pmksa->ctx, reason); _pmksa_cache_free_entry(entry); } @@ -66,7 +61,7 @@ static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) pmksa->pmksa = entry->next; wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for " MACSTR, MAC2STR(entry->aa)); - pmksa_cache_free_entry(pmksa, entry, 0); + pmksa_cache_free_entry(pmksa, entry, PMKSA_EXPIRE); } pmksa_cache_set_expiration(pmksa); @@ -98,7 +93,7 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL); entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa : - pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL); + pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL); if (entry) { sec = pmksa->pmksa->reauth_time - now.sec; if (sec < 0) @@ -169,22 +164,17 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, pmksa->pmksa = pos->next; else prev->next = pos->next; - if (pos == pmksa->sm->cur_pmksa) { - /* We are about to replace the current PMKSA - * cache entry. This happens when the PMKSA - * caching attempt fails, so we don't want to - * force pmksa_cache_free_entry() to disconnect - * at this point. Let's just make sure the old - * PMKSA cache entry will not be used in the - * future. - */ - wpa_printf(MSG_DEBUG, "RSN: replacing current " - "PMKSA entry"); - pmksa->sm->cur_pmksa = NULL; - } wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for " "the current AP"); - pmksa_cache_free_entry(pmksa, pos, 1); + pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE); + + /* + * If OKC is used, there may be other PMKSA cache + * entries based on the same PMK. These needs to be + * flushed so that a new entry can be created based on + * the new PMK. + */ + pmksa_cache_flush(pmksa, network_ctx); break; } prev = pos; @@ -194,12 +184,25 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) { /* Remove the oldest entry to make room for the new entry */ pos = pmksa->pmksa; - pmksa->pmksa = pos->next; - wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache " - "entry (for " MACSTR ") to make room for new one", - MAC2STR(pos->aa)); - wpa_sm_remove_pmkid(pmksa->sm, pos->aa, pos->pmkid); - pmksa_cache_free_entry(pmksa, pos, 0); + + if (pos == pmksa->sm->cur_pmksa) { + /* + * Never remove the current PMKSA cache entry, since + * it's in use, and removing it triggers a needless + * deauthentication. + */ + pos = pos->next; + pmksa->pmksa->next = pos ? pos->next : NULL; + } else + pmksa->pmksa = pos->next; + + if (pos) { + wpa_printf(MSG_DEBUG, "RSN: removed the oldest idle " + "PMKSA cache entry (for " MACSTR ") to " + "make room for new one", + MAC2STR(pos->aa)); + pmksa_cache_free_entry(pmksa, pos, PMKSA_FREE); + } } /* Add the new entry; order by expiration time */ @@ -220,8 +223,8 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, prev->next = entry; } pmksa->pmksa_count++; - wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR, - MAC2STR(entry->aa)); + wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR + " network_ctx=%p", MAC2STR(entry->aa), network_ctx); wpa_sm_add_pmkid(pmksa->sm, entry->aa, entry->pmkid); return entry; @@ -229,6 +232,39 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, /** + * pmksa_cache_flush - Flush PMKSA cache entries for a specific network + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + * @network_ctx: Network configuration context or %NULL to flush all entries + */ +void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx) +{ + struct rsn_pmksa_cache_entry *entry, *prev = NULL, *tmp; + int removed = 0; + + entry = pmksa->pmksa; + while (entry) { + if (entry->network_ctx == network_ctx || network_ctx == NULL) { + wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry " + "for " MACSTR, MAC2STR(entry->aa)); + if (prev) + prev->next = entry->next; + else + pmksa->pmksa = entry->next; + tmp = entry; + entry = entry->next; + pmksa_cache_free_entry(pmksa, tmp, PMKSA_FREE); + removed++; + } else { + prev = entry; + entry = entry->next; + } + } + if (removed) + pmksa_cache_set_expiration(pmksa); +} + + +/** * pmksa_cache_deinit - Free all entries in PMKSA cache * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() */ @@ -256,16 +292,19 @@ void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() * @aa: Authenticator address or %NULL to match any * @pmkid: PMKID or %NULL to match any + * @network_ctx: Network context or %NULL to match any * Returns: Pointer to PMKSA cache entry or %NULL if no match was found */ struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, - const u8 *aa, const u8 *pmkid) + const u8 *aa, const u8 *pmkid, + const void *network_ctx) { struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; while (entry) { if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) && (pmkid == NULL || - os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)) + os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) && + (network_ctx == NULL || network_ctx == entry->network_ctx)) return entry; entry = entry->next; } @@ -273,22 +312,6 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, } -/** - * pmksa_cache_notify_reconfig - Reconfiguration notification for PMKSA cache - * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() - * - * Clear references to old data structures when wpa_supplicant is reconfigured. - */ -void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa) -{ - struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; - while (entry) { - entry->network_ctx = NULL; - entry = entry->next; - } -} - - static struct rsn_pmksa_cache_entry * pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa, const struct rsn_pmksa_cache_entry *old_entry, @@ -327,6 +350,7 @@ pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx, { struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; + wpa_printf(MSG_DEBUG, "RSN: Consider " MACSTR " for OKC", MAC2STR(aa)); if (network_ctx == NULL) return NULL; while (entry) { @@ -384,20 +408,32 @@ int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, int try_opportunistic) { struct rsn_pmksa_cache *pmksa = sm->pmksa; + wpa_printf(MSG_DEBUG, "RSN: PMKSA cache search - network_ctx=%p " + "try_opportunistic=%d", network_ctx, try_opportunistic); + if (pmkid) + wpa_hexdump(MSG_DEBUG, "RSN: Search for PMKID", + pmkid, PMKID_LEN); + if (bssid) + wpa_printf(MSG_DEBUG, "RSN: Search for BSSID " MACSTR, + MAC2STR(bssid)); + sm->cur_pmksa = NULL; if (pmkid) - sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid); + sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid, + network_ctx); if (sm->cur_pmksa == NULL && bssid) - sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL); + sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL, + network_ctx); if (sm->cur_pmksa == NULL && try_opportunistic && bssid) sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa, network_ctx, bssid); if (sm->cur_pmksa) { - wpa_hexdump(MSG_DEBUG, "RSN: PMKID", + wpa_hexdump(MSG_DEBUG, "RSN: PMKSA cache entry found - PMKID", sm->cur_pmksa->pmkid, PMKID_LEN); return 0; } + wpa_printf(MSG_DEBUG, "RSN: No PMKSA cache entry found"); return -1; } @@ -458,7 +494,7 @@ int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) */ struct rsn_pmksa_cache * pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, - void *ctx, int replace), + void *ctx, enum pmksa_free_reason reason), void *ctx, struct wpa_sm *sm) { struct rsn_pmksa_cache *pmksa; diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h index a1447e526d5bc..f318c52fa5bcf 100644 --- a/src/rsn_supp/pmksa_cache.h +++ b/src/rsn_supp/pmksa_cache.h @@ -1,15 +1,9 @@ /* * wpa_supplicant - WPA2/RSN PMKSA cache functions - * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2009, 2011-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 - * published 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. */ #ifndef PMKSA_CACHE_H @@ -44,20 +38,26 @@ struct rsn_pmksa_cache_entry { struct rsn_pmksa_cache; +enum pmksa_free_reason { + PMKSA_FREE, + PMKSA_REPLACE, + PMKSA_EXPIRE, +}; + #if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2) struct rsn_pmksa_cache * pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, - void *ctx, int replace), + void *ctx, enum pmksa_free_reason reason), void *ctx, struct wpa_sm *sm); void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa); struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, - const u8 *aa, const u8 *pmkid); + const u8 *aa, const u8 *pmkid, + const void *network_ctx); int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len); struct rsn_pmksa_cache_entry * pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, void *network_ctx, int akmp); -void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa); struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm); void pmksa_cache_clear_current(struct wpa_sm *sm); int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, @@ -66,12 +66,13 @@ int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, struct rsn_pmksa_cache_entry * pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx, const u8 *aa); +void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx); #else /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ static inline struct rsn_pmksa_cache * pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, - void *ctx, int replace), + void *ctx, int reason), void *ctx, struct wpa_sm *sm) { return (void *) -1; @@ -82,7 +83,8 @@ static inline void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) } static inline struct rsn_pmksa_cache_entry * -pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid) +pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid, + const void *network_ctx) { return NULL; } @@ -106,10 +108,6 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, return NULL; } -static inline void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa) -{ -} - static inline void pmksa_cache_clear_current(struct wpa_sm *sm) { } @@ -122,6 +120,11 @@ static inline int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, return -1; } +static inline void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, + void *network_ctx) +{ +} + #endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ #endif /* PMKSA_CACHE_H */ diff --git a/src/rsn_supp/preauth.c b/src/rsn_supp/preauth.c index 6109f5e9f8734..ab61867b40718 100644 --- a/src/rsn_supp/preauth.c +++ b/src/rsn_supp/preauth.c @@ -1,15 +1,9 @@ /* * RSN pre-authentication (supplicant) - * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-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 - * published 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" @@ -22,7 +16,6 @@ #include "preauth.h" #include "pmksa_cache.h" #include "wpa_i.h" -#include "common/ieee802_11_defs.h" #if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2) @@ -312,7 +305,7 @@ void rsn_preauth_candidate_process(struct wpa_sm *sm) dl_list_for_each_safe(candidate, n, &sm->pmksa_candidates, struct rsn_pmksa_candidate, list) { struct rsn_pmksa_cache_entry *p = NULL; - p = pmksa_cache_get(sm->pmksa, candidate->bssid, NULL); + p = pmksa_cache_get(sm->pmksa, candidate->bssid, NULL, NULL); if (os_memcmp(sm->bssid, candidate->bssid, ETH_ALEN) != 0 && (p == NULL || p->opportunistic)) { wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA " @@ -459,7 +452,7 @@ void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid, if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie)) return; - pmksa = pmksa_cache_get(sm->pmksa, bssid, NULL); + pmksa = pmksa_cache_get(sm->pmksa, bssid, NULL, NULL); if (pmksa && (!pmksa->opportunistic || !(ie.capabilities & WPA_CAPABILITY_PREAUTH))) return; diff --git a/src/rsn_supp/preauth.h b/src/rsn_supp/preauth.h index f8240abef6df8..27d3112cd94e6 100644 --- a/src/rsn_supp/preauth.h +++ b/src/rsn_supp/preauth.h @@ -2,14 +2,8 @@ * wpa_supplicant - WPA2/RSN pre-authentication functions * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef PREAUTH_H diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c new file mode 100644 index 0000000000000..7646ca88671a3 --- /dev/null +++ b/src/rsn_supp/tdls.c @@ -0,0 +1,2334 @@ +/* + * wpa_supplicant - TDLS + * Copyright (c) 2010-2011, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "utils/os.h" +#include "common/ieee802_11_defs.h" +#include "crypto/sha256.h" +#include "crypto/crypto.h" +#include "crypto/aes_wrap.h" +#include "rsn_supp/wpa.h" +#include "rsn_supp/wpa_ie.h" +#include "rsn_supp/wpa_i.h" +#include "drivers/driver.h" +#include "l2_packet/l2_packet.h" + +#ifdef CONFIG_TDLS_TESTING +#define TDLS_TESTING_LONG_FRAME BIT(0) +#define TDLS_TESTING_ALT_RSN_IE BIT(1) +#define TDLS_TESTING_DIFF_BSSID BIT(2) +#define TDLS_TESTING_SHORT_LIFETIME BIT(3) +#define TDLS_TESTING_WRONG_LIFETIME_RESP BIT(4) +#define TDLS_TESTING_WRONG_LIFETIME_CONF BIT(5) +#define TDLS_TESTING_LONG_LIFETIME BIT(6) +#define TDLS_TESTING_CONCURRENT_INIT BIT(7) +#define TDLS_TESTING_NO_TPK_EXPIRATION BIT(8) +#define TDLS_TESTING_DECLINE_RESP BIT(9) +#define TDLS_TESTING_IGNORE_AP_PROHIBIT BIT(10) +unsigned int tdls_testing = 0; +#endif /* CONFIG_TDLS_TESTING */ + +#define TPK_LIFETIME 43200 /* 12 hours */ +#define TPK_RETRY_COUNT 3 +#define TPK_TIMEOUT 5000 /* in milliseconds */ + +#define TDLS_MIC_LEN 16 + +#define TDLS_TIMEOUT_LEN 4 + +struct wpa_tdls_ftie { + u8 ie_type; /* FTIE */ + u8 ie_len; + u8 mic_ctrl[2]; + u8 mic[TDLS_MIC_LEN]; + u8 Anonce[WPA_NONCE_LEN]; /* Responder Nonce in TDLS */ + u8 Snonce[WPA_NONCE_LEN]; /* Initiator Nonce in TDLS */ + /* followed by optional elements */ +} STRUCT_PACKED; + +struct wpa_tdls_timeoutie { + u8 ie_type; /* Timeout IE */ + u8 ie_len; + u8 interval_type; + u8 value[TDLS_TIMEOUT_LEN]; +} STRUCT_PACKED; + +struct wpa_tdls_lnkid { + u8 ie_type; /* Link Identifier IE */ + u8 ie_len; + u8 bssid[ETH_ALEN]; + u8 init_sta[ETH_ALEN]; + u8 resp_sta[ETH_ALEN]; +} STRUCT_PACKED; + +/* TDLS frame headers as per IEEE Std 802.11z-2010 */ +struct wpa_tdls_frame { + u8 payloadtype; /* IEEE80211_TDLS_RFTYPE */ + u8 category; /* Category */ + u8 action; /* Action (enum tdls_frame_type) */ +} STRUCT_PACKED; + +static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs); +static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx); +static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer); + + +#define TDLS_MAX_IE_LEN 80 +#define IEEE80211_MAX_SUPP_RATES 32 + +struct wpa_tdls_peer { + struct wpa_tdls_peer *next; + int initiator; /* whether this end was initiator for TDLS setup */ + u8 addr[ETH_ALEN]; /* other end MAC address */ + u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */ + u8 rnonce[WPA_NONCE_LEN]; /* Responder Nonce */ + u8 rsnie_i[TDLS_MAX_IE_LEN]; /* Initiator RSN IE */ + size_t rsnie_i_len; + u8 rsnie_p[TDLS_MAX_IE_LEN]; /* Peer RSN IE */ + size_t rsnie_p_len; + u32 lifetime; + int cipher; /* Selected cipher (WPA_CIPHER_*) */ + u8 dtoken; + + struct tpk { + u8 kck[16]; /* TPK-KCK */ + u8 tk[16]; /* TPK-TK; assuming only CCMP will be used */ + } tpk; + int tpk_set; + int tpk_success; + + struct tpk_timer { + u8 dest[ETH_ALEN]; + int count; /* Retry Count */ + int timer; /* Timeout in milliseconds */ + u8 action_code; /* TDLS frame type */ + u8 dialog_token; + u16 status_code; + int buf_len; /* length of TPK message for retransmission */ + u8 *buf; /* buffer for TPK message */ + } sm_tmr; + + u16 capability; + + u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; + size_t supp_rates_len; +}; + + +static int wpa_tdls_get_privacy(struct wpa_sm *sm) +{ + /* + * Get info needed from supplicant to check if the current BSS supports + * security. Other than OPEN mode, rest are considered secured + * WEP/WPA/WPA2 hence TDLS frames are processed for TPK handshake. + */ + return sm->pairwise_cipher != WPA_CIPHER_NONE; +} + + +static u8 * wpa_add_ie(u8 *pos, const u8 *ie, size_t ie_len) +{ + os_memcpy(pos, ie, ie_len); + return pos + ie_len; +} + + +static int wpa_tdls_del_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer) +{ + if (wpa_sm_set_key(sm, WPA_ALG_NONE, peer->addr, + 0, 0, NULL, 0, NULL, 0) < 0) { + wpa_printf(MSG_WARNING, "TDLS: Failed to delete TPK-TK from " + "the driver"); + return -1; + } + + return 0; +} + + +static int wpa_tdls_set_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer) +{ + u8 key_len; + u8 rsc[6]; + enum wpa_alg alg; + + os_memset(rsc, 0, 6); + + switch (peer->cipher) { + case WPA_CIPHER_CCMP: + alg = WPA_ALG_CCMP; + key_len = 16; + break; + case WPA_CIPHER_NONE: + wpa_printf(MSG_DEBUG, "TDLS: Pairwise Cipher Suite: " + "NONE - do not use pairwise keys"); + return -1; + default: + wpa_printf(MSG_WARNING, "TDLS: Unsupported pairwise cipher %d", + sm->pairwise_cipher); + return -1; + } + + if (wpa_sm_set_key(sm, alg, peer->addr, -1, 1, + rsc, sizeof(rsc), peer->tpk.tk, key_len) < 0) { + wpa_printf(MSG_WARNING, "TDLS: Failed to set TPK to the " + "driver"); + return -1; + } + return 0; +} + + +static int wpa_tdls_send_tpk_msg(struct wpa_sm *sm, const u8 *dst, + u8 action_code, u8 dialog_token, + u16 status_code, const u8 *buf, size_t len) +{ + return wpa_sm_send_tdls_mgmt(sm, dst, action_code, dialog_token, + status_code, buf, len); +} + + +static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code, + u8 dialog_token, u16 status_code, + const u8 *msg, size_t msg_len) +{ + struct wpa_tdls_peer *peer; + + wpa_printf(MSG_DEBUG, "TDLS: TPK send dest=" MACSTR " action_code=%u " + "dialog_token=%u status_code=%u msg_len=%u", + MAC2STR(dest), action_code, dialog_token, status_code, + (unsigned int) msg_len); + + if (wpa_tdls_send_tpk_msg(sm, dest, action_code, dialog_token, + status_code, msg, msg_len)) { + wpa_printf(MSG_INFO, "TDLS: Failed to send message " + "(action_code=%u)", action_code); + return -1; + } + + if (action_code == WLAN_TDLS_SETUP_CONFIRM || + action_code == WLAN_TDLS_TEARDOWN || + action_code == WLAN_TDLS_DISCOVERY_REQUEST || + action_code == WLAN_TDLS_DISCOVERY_RESPONSE) + return 0; /* No retries */ + + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, dest, ETH_ALEN) == 0) + break; + } + + if (peer == NULL) { + wpa_printf(MSG_INFO, "TDLS: No matching entry found for " + "retry " MACSTR, MAC2STR(dest)); + return 0; + } + + eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); + + peer->sm_tmr.count = TPK_RETRY_COUNT; + peer->sm_tmr.timer = TPK_TIMEOUT; + + /* Copy message to resend on timeout */ + os_memcpy(peer->sm_tmr.dest, dest, ETH_ALEN); + peer->sm_tmr.action_code = action_code; + peer->sm_tmr.dialog_token = dialog_token; + peer->sm_tmr.status_code = status_code; + peer->sm_tmr.buf_len = msg_len; + os_free(peer->sm_tmr.buf); + peer->sm_tmr.buf = os_malloc(msg_len); + if (peer->sm_tmr.buf == NULL) + return -1; + os_memcpy(peer->sm_tmr.buf, msg, msg_len); + + wpa_printf(MSG_DEBUG, "TDLS: Retry timeout registered " + "(action_code=%u)", action_code); + eloop_register_timeout(peer->sm_tmr.timer / 1000, 0, + wpa_tdls_tpk_retry_timeout, sm, peer); + return 0; +} + + +static int wpa_tdls_do_teardown(struct wpa_sm *sm, struct wpa_tdls_peer *peer, + u16 reason_code, int free_peer) +{ + int ret; + + if (sm->tdls_external_setup) { + ret = wpa_tdls_send_teardown(sm, peer->addr, reason_code); + + /* disable the link after teardown was sent */ + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr); + } else { + ret = wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr); + } + + if (sm->tdls_external_setup || free_peer) + wpa_tdls_peer_free(sm, peer); + + return ret; +} + + +static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx) +{ + + struct wpa_sm *sm = eloop_ctx; + struct wpa_tdls_peer *peer = timeout_ctx; + + if (peer->sm_tmr.count) { + peer->sm_tmr.count--; + peer->sm_tmr.timer = TPK_TIMEOUT; + + wpa_printf(MSG_INFO, "TDLS: Retrying sending of message " + "(action_code=%u)", + peer->sm_tmr.action_code); + + if (peer->sm_tmr.buf == NULL) { + wpa_printf(MSG_INFO, "TDLS: No retry buffer available " + "for action_code=%u", + peer->sm_tmr.action_code); + eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, + peer); + return; + } + + /* resend TPK Handshake Message to Peer */ + if (wpa_tdls_send_tpk_msg(sm, peer->sm_tmr.dest, + peer->sm_tmr.action_code, + peer->sm_tmr.dialog_token, + peer->sm_tmr.status_code, + peer->sm_tmr.buf, + peer->sm_tmr.buf_len)) { + wpa_printf(MSG_INFO, "TDLS: Failed to retry " + "transmission"); + } + + eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); + eloop_register_timeout(peer->sm_tmr.timer / 1000, 0, + wpa_tdls_tpk_retry_timeout, sm, peer); + } else { + eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); + + wpa_printf(MSG_DEBUG, "TDLS: Sending Teardown Request"); + wpa_tdls_do_teardown(sm, peer, + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, 1); + } +} + + +static void wpa_tdls_tpk_retry_timeout_cancel(struct wpa_sm *sm, + struct wpa_tdls_peer *peer, + u8 action_code) +{ + if (action_code == peer->sm_tmr.action_code) { + wpa_printf(MSG_DEBUG, "TDLS: Retry timeout cancelled for " + "action_code=%u", action_code); + + /* Cancel Timeout registered */ + eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); + + /* free all resources meant for retry */ + os_free(peer->sm_tmr.buf); + peer->sm_tmr.buf = NULL; + + peer->sm_tmr.count = 0; + peer->sm_tmr.timer = 0; + peer->sm_tmr.buf_len = 0; + peer->sm_tmr.action_code = 0xff; + } else { + wpa_printf(MSG_INFO, "TDLS: Error in cancelling retry timeout " + "(Unknown action_code=%u)", action_code); + } +} + + +static void wpa_tdls_generate_tpk(struct wpa_tdls_peer *peer, + const u8 *own_addr, const u8 *bssid) +{ + u8 key_input[SHA256_MAC_LEN]; + const u8 *nonce[2]; + size_t len[2]; + u8 data[3 * ETH_ALEN]; + + /* IEEE Std 802.11z-2010 8.5.9.1: + * TPK-Key-Input = SHA-256(min(SNonce, ANonce) || max(SNonce, ANonce)) + */ + len[0] = WPA_NONCE_LEN; + len[1] = WPA_NONCE_LEN; + if (os_memcmp(peer->inonce, peer->rnonce, WPA_NONCE_LEN) < 0) { + nonce[0] = peer->inonce; + nonce[1] = peer->rnonce; + } else { + nonce[0] = peer->rnonce; + nonce[1] = peer->inonce; + } + wpa_hexdump(MSG_DEBUG, "TDLS: min(Nonce)", nonce[0], WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "TDLS: max(Nonce)", nonce[1], WPA_NONCE_LEN); + sha256_vector(2, nonce, len, key_input); + wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-Key-Input", + key_input, SHA256_MAC_LEN); + + /* + * TPK-Key-Data = KDF-N_KEY(TPK-Key-Input, "TDLS PMK", + * min(MAC_I, MAC_R) || max(MAC_I, MAC_R) || BSSID || N_KEY) + * TODO: is N_KEY really included in KDF Context and if so, in which + * presentation format (little endian 16-bit?) is it used? It gets + * added by the KDF anyway.. + */ + + if (os_memcmp(own_addr, peer->addr, ETH_ALEN) < 0) { + os_memcpy(data, own_addr, ETH_ALEN); + os_memcpy(data + ETH_ALEN, peer->addr, ETH_ALEN); + } else { + os_memcpy(data, peer->addr, ETH_ALEN); + os_memcpy(data + ETH_ALEN, own_addr, ETH_ALEN); + } + os_memcpy(data + 2 * ETH_ALEN, bssid, ETH_ALEN); + wpa_hexdump(MSG_DEBUG, "TDLS: KDF Context", data, sizeof(data)); + + sha256_prf(key_input, SHA256_MAC_LEN, "TDLS PMK", data, sizeof(data), + (u8 *) &peer->tpk, sizeof(peer->tpk)); + wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-KCK", + peer->tpk.kck, sizeof(peer->tpk.kck)); + wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-TK", + peer->tpk.tk, sizeof(peer->tpk.tk)); + peer->tpk_set = 1; +} + + +/** + * wpa_tdls_ftie_mic - Calculate TDLS FTIE MIC + * @kck: TPK-KCK + * @lnkid: Pointer to the beginning of Link Identifier IE + * @rsnie: Pointer to the beginning of RSN IE used for handshake + * @timeoutie: Pointer to the beginning of Timeout IE used for handshake + * @ftie: Pointer to the beginning of FT IE + * @mic: Pointer for writing MIC + * + * Calculate MIC for TDLS frame. + */ +static int wpa_tdls_ftie_mic(const u8 *kck, u8 trans_seq, const u8 *lnkid, + const u8 *rsnie, const u8 *timeoutie, + const u8 *ftie, u8 *mic) +{ + u8 *buf, *pos; + struct wpa_tdls_ftie *_ftie; + const struct wpa_tdls_lnkid *_lnkid; + int ret; + int len = 2 * ETH_ALEN + 1 + 2 + lnkid[1] + 2 + rsnie[1] + + 2 + timeoutie[1] + 2 + ftie[1]; + buf = os_zalloc(len); + if (!buf) { + wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation"); + return -1; + } + + pos = buf; + _lnkid = (const struct wpa_tdls_lnkid *) lnkid; + /* 1) TDLS initiator STA MAC address */ + os_memcpy(pos, _lnkid->init_sta, ETH_ALEN); + pos += ETH_ALEN; + /* 2) TDLS responder STA MAC address */ + os_memcpy(pos, _lnkid->resp_sta, ETH_ALEN); + pos += ETH_ALEN; + /* 3) Transaction Sequence number */ + *pos++ = trans_seq; + /* 4) Link Identifier IE */ + os_memcpy(pos, lnkid, 2 + lnkid[1]); + pos += 2 + lnkid[1]; + /* 5) RSN IE */ + os_memcpy(pos, rsnie, 2 + rsnie[1]); + pos += 2 + rsnie[1]; + /* 6) Timeout Interval IE */ + os_memcpy(pos, timeoutie, 2 + timeoutie[1]); + pos += 2 + timeoutie[1]; + /* 7) FTIE, with the MIC field of the FTIE set to 0 */ + os_memcpy(pos, ftie, 2 + ftie[1]); + _ftie = (struct wpa_tdls_ftie *) pos; + os_memset(_ftie->mic, 0, TDLS_MIC_LEN); + pos += 2 + ftie[1]; + + wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf); + wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16); + ret = omac1_aes_128(kck, buf, pos - buf, mic); + os_free(buf); + wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16); + return ret; +} + + +/** + * wpa_tdls_key_mic_teardown - Calculate TDLS FTIE MIC for Teardown frame + * @kck: TPK-KCK + * @trans_seq: Transaction Sequence Number (4 - Teardown) + * @rcode: Reason code for Teardown + * @dtoken: Dialog Token used for that particular link + * @lnkid: Pointer to the beginning of Link Identifier IE + * @ftie: Pointer to the beginning of FT IE + * @mic: Pointer for writing MIC + * + * Calculate MIC for TDLS frame. + */ +static int wpa_tdls_key_mic_teardown(const u8 *kck, u8 trans_seq, u16 rcode, + u8 dtoken, const u8 *lnkid, + const u8 *ftie, u8 *mic) +{ + u8 *buf, *pos; + struct wpa_tdls_ftie *_ftie; + int ret; + int len; + + if (lnkid == NULL) + return -1; + + len = 2 + lnkid[1] + sizeof(rcode) + sizeof(dtoken) + + sizeof(trans_seq) + 2 + ftie[1]; + + buf = os_zalloc(len); + if (!buf) { + wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation"); + return -1; + } + + pos = buf; + /* 1) Link Identifier IE */ + os_memcpy(pos, lnkid, 2 + lnkid[1]); + pos += 2 + lnkid[1]; + /* 2) Reason Code */ + WPA_PUT_LE16(pos, rcode); + pos += sizeof(rcode); + /* 3) Dialog token */ + *pos++ = dtoken; + /* 4) Transaction Sequence number */ + *pos++ = trans_seq; + /* 7) FTIE, with the MIC field of the FTIE set to 0 */ + os_memcpy(pos, ftie, 2 + ftie[1]); + _ftie = (struct wpa_tdls_ftie *) pos; + os_memset(_ftie->mic, 0, TDLS_MIC_LEN); + pos += 2 + ftie[1]; + + wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf); + wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16); + ret = omac1_aes_128(kck, buf, pos - buf, mic); + os_free(buf); + wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16); + return ret; +} + + +static int wpa_supplicant_verify_tdls_mic(u8 trans_seq, + struct wpa_tdls_peer *peer, + const u8 *lnkid, const u8 *timeoutie, + const struct wpa_tdls_ftie *ftie) +{ + u8 mic[16]; + + if (peer->tpk_set) { + wpa_tdls_ftie_mic(peer->tpk.kck, trans_seq, lnkid, + peer->rsnie_p, timeoutie, (u8 *) ftie, + mic); + if (os_memcmp(mic, ftie->mic, 16) != 0) { + wpa_printf(MSG_INFO, "TDLS: Invalid MIC in FTIE - " + "dropping packet"); + wpa_hexdump(MSG_DEBUG, "TDLS: Received MIC", + ftie->mic, 16); + wpa_hexdump(MSG_DEBUG, "TDLS: Calculated MIC", + mic, 16); + return -1; + } + } else { + wpa_printf(MSG_WARNING, "TDLS: Could not verify TDLS MIC, " + "TPK not set - dropping packet"); + return -1; + } + return 0; +} + + +static int wpa_supplicant_verify_tdls_mic_teardown( + u8 trans_seq, u16 rcode, u8 dtoken, struct wpa_tdls_peer *peer, + const u8 *lnkid, const struct wpa_tdls_ftie *ftie) +{ + u8 mic[16]; + + if (peer->tpk_set) { + wpa_tdls_key_mic_teardown(peer->tpk.kck, trans_seq, rcode, + dtoken, lnkid, (u8 *) ftie, mic); + if (os_memcmp(mic, ftie->mic, 16) != 0) { + wpa_printf(MSG_INFO, "TDLS: Invalid MIC in Teardown - " + "dropping packet"); + return -1; + } + } else { + wpa_printf(MSG_INFO, "TDLS: Could not verify TDLS Teardown " + "MIC, TPK not set - dropping packet"); + return -1; + } + return 0; +} + + +static void wpa_tdls_tpk_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_sm *sm = eloop_ctx; + struct wpa_tdls_peer *peer = timeout_ctx; + + /* + * On TPK lifetime expiration, we have an option of either tearing down + * the direct link or trying to re-initiate it. The selection of what + * to do is not strictly speaking controlled by our role in the expired + * link, but for now, use that to select whether to renew or tear down + * the link. + */ + + if (peer->initiator) { + wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR + " - try to renew", MAC2STR(peer->addr)); + wpa_tdls_start(sm, peer->addr); + } else { + wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR + " - tear down", MAC2STR(peer->addr)); + wpa_tdls_do_teardown(sm, peer, + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, 1); + } +} + + +static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer) +{ + wpa_printf(MSG_DEBUG, "TDLS: Clear state for peer " MACSTR, + MAC2STR(peer->addr)); + eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer); + eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); + peer->initiator = 0; + os_free(peer->sm_tmr.buf); + peer->sm_tmr.buf = NULL; + peer->rsnie_i_len = peer->rsnie_p_len = 0; + peer->cipher = 0; + peer->tpk_set = peer->tpk_success = 0; + os_memset(&peer->tpk, 0, sizeof(peer->tpk)); + os_memset(peer->inonce, 0, WPA_NONCE_LEN); + os_memset(peer->rnonce, 0, WPA_NONCE_LEN); +} + + +static void wpa_tdls_linkid(struct wpa_sm *sm, struct wpa_tdls_peer *peer, + struct wpa_tdls_lnkid *lnkid) +{ + lnkid->ie_type = WLAN_EID_LINK_ID; + lnkid->ie_len = 3 * ETH_ALEN; + os_memcpy(lnkid->bssid, sm->bssid, ETH_ALEN); + if (peer->initiator) { + os_memcpy(lnkid->init_sta, sm->own_addr, ETH_ALEN); + os_memcpy(lnkid->resp_sta, peer->addr, ETH_ALEN); + } else { + os_memcpy(lnkid->init_sta, peer->addr, ETH_ALEN); + os_memcpy(lnkid->resp_sta, sm->own_addr, ETH_ALEN); + } +} + + +int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, u16 reason_code) +{ + struct wpa_tdls_peer *peer; + struct wpa_tdls_ftie *ftie; + struct wpa_tdls_lnkid lnkid; + u8 dialog_token; + u8 *rbuf, *pos; + int ielen; + + if (sm->tdls_disabled || !sm->tdls_supported) + return -1; + + /* Find the node and free from the list */ + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) + break; + } + + if (peer == NULL) { + wpa_printf(MSG_INFO, "TDLS: No matching entry found for " + "Teardown " MACSTR, MAC2STR(addr)); + return 0; + } + + dialog_token = peer->dtoken; + + wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown for " MACSTR, + MAC2STR(addr)); + + ielen = 0; + if (wpa_tdls_get_privacy(sm) && peer->tpk_set && peer->tpk_success) { + /* To add FTIE for Teardown request and compute MIC */ + ielen += sizeof(*ftie); +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_LONG_FRAME) + ielen += 170; +#endif /* CONFIG_TDLS_TESTING */ + } + + rbuf = os_zalloc(ielen + 1); + if (rbuf == NULL) + return -1; + pos = rbuf; + + if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success) + goto skip_ies; + + ftie = (struct wpa_tdls_ftie *) pos; + ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION; + /* Using the recent nonce which should be for CONFIRM frame */ + os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN); + os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN); + ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2; + pos = (u8 *) (ftie + 1); +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_LONG_FRAME) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to " + "FTIE"); + ftie->ie_len += 170; + *pos++ = 255; /* FTIE subelem */ + *pos++ = 168; /* FTIE subelem length */ + pos += 168; + } +#endif /* CONFIG_TDLS_TESTING */ + wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TDLS Teardown handshake", + (u8 *) ftie, pos - (u8 *) ftie); + + /* compute MIC before sending */ + wpa_tdls_linkid(sm, peer, &lnkid); + wpa_tdls_key_mic_teardown(peer->tpk.kck, 4, reason_code, + dialog_token, (u8 *) &lnkid, (u8 *) ftie, + ftie->mic); + +skip_ies: + /* TODO: register for a Timeout handler, if Teardown is not received at + * the other end, then try again another time */ + + /* request driver to send Teardown using this FTIE */ + wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_TEARDOWN, 0, + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, rbuf, + pos - rbuf); + os_free(rbuf); + + /* clear the Peerkey statemachine */ + wpa_tdls_peer_free(sm, peer); + + return 0; +} + + +int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code) +{ + struct wpa_tdls_peer *peer; + + if (sm->tdls_disabled || !sm->tdls_supported) + return -1; + + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) + break; + } + + if (peer == NULL) { + wpa_printf(MSG_DEBUG, "TDLS: Could not find peer " MACSTR + " for link Teardown", MAC2STR(addr)); + return -1; + } + + if (!peer->tpk_success) { + wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR + " not connected - cannot Teardown link", MAC2STR(addr)); + return -1; + } + + return wpa_tdls_do_teardown(sm, peer, reason_code, 0); +} + + +void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr) +{ + struct wpa_tdls_peer *peer; + + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) + break; + } + + if (peer) { + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, addr); + wpa_tdls_peer_free(sm, peer); + } +} + + +static int wpa_tdls_recv_teardown(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_tdls_peer *peer = NULL; + struct wpa_tdls_ftie *ftie; + struct wpa_tdls_lnkid *lnkid; + struct wpa_eapol_ie_parse kde; + u16 reason_code; + const u8 *pos; + int ielen; + + /* Find the node and free from the list */ + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0) + break; + } + + if (peer == NULL) { + wpa_printf(MSG_INFO, "TDLS: No matching entry found for " + "Teardown " MACSTR, MAC2STR(src_addr)); + return 0; + } + + pos = buf; + pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */; + + reason_code = WPA_GET_LE16(pos); + pos += 2; + + wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown Request from " MACSTR + " (reason code %u)", MAC2STR(src_addr), reason_code); + + ielen = len - (pos - buf); /* start of IE in buf */ + if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) { + wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in Teardown"); + return -1; + } + + if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) { + wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TDLS " + "Teardown"); + return -1; + } + lnkid = (struct wpa_tdls_lnkid *) kde.lnkid; + + if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success) + goto skip_ftie; + + if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_INFO, "TDLS: No FTIE in TDLS Teardown"); + return -1; + } + + ftie = (struct wpa_tdls_ftie *) kde.ftie; + + /* Process MIC check to see if TDLS Teardown is right */ + if (wpa_supplicant_verify_tdls_mic_teardown(4, reason_code, + peer->dtoken, peer, + (u8 *) lnkid, ftie) < 0) { + wpa_printf(MSG_DEBUG, "TDLS: MIC failure for TDLS " + "Teardown Request from " MACSTR, MAC2STR(src_addr)); + return -1; + } + +skip_ftie: + /* + * Request the driver to disable the direct link and clear associated + * keys. + */ + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); + + /* clear the Peerkey statemachine */ + wpa_tdls_peer_free(sm, peer); + + return 0; +} + + +/** + * wpa_tdls_send_error - To send suitable TDLS status response with + * appropriate status code mentioning reason for error/failure. + * @dst - MAC addr of Peer station + * @tdls_action - TDLS frame type for which error code is sent + * @status - status code mentioning reason + */ + +static int wpa_tdls_send_error(struct wpa_sm *sm, const u8 *dst, + u8 tdls_action, u8 dialog_token, u16 status) +{ + wpa_printf(MSG_DEBUG, "TDLS: Sending error to " MACSTR + " (action=%u status=%u)", + MAC2STR(dst), tdls_action, status); + return wpa_tdls_tpk_send(sm, dst, tdls_action, dialog_token, status, + NULL, 0); +} + + +static struct wpa_tdls_peer * +wpa_tdls_add_peer(struct wpa_sm *sm, const u8 *addr) +{ + struct wpa_tdls_peer *peer; + + wpa_printf(MSG_INFO, "TDLS: Creating peer entry for " MACSTR, + MAC2STR(addr)); + + peer = os_zalloc(sizeof(*peer)); + if (peer == NULL) + return NULL; + + os_memcpy(peer->addr, addr, ETH_ALEN); + peer->next = sm->tdls; + sm->tdls = peer; + + return peer; +} + + +static int wpa_tdls_send_tpk_m1(struct wpa_sm *sm, + struct wpa_tdls_peer *peer) +{ + size_t buf_len; + struct wpa_tdls_timeoutie timeoutie; + u16 rsn_capab; + struct wpa_tdls_ftie *ftie; + u8 *rbuf, *pos, *count_pos; + u16 count; + struct rsn_ie_hdr *hdr; + + if (!wpa_tdls_get_privacy(sm)) { + wpa_printf(MSG_DEBUG, "TDLS: No security used on the link"); + peer->rsnie_i_len = 0; + goto skip_rsnie; + } + + /* + * TPK Handshake Message 1: + * FTIE: ANonce=0, SNonce=initiator nonce MIC=0, DataKDs=(RSNIE_I, + * Timeout Interval IE)) + */ + + /* Filling RSN IE */ + hdr = (struct rsn_ie_hdr *) peer->rsnie_i; + hdr->elem_id = WLAN_EID_RSN; + WPA_PUT_LE16(hdr->version, RSN_VERSION); + + pos = (u8 *) (hdr + 1); + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED); + pos += RSN_SELECTOR_LEN; + count_pos = pos; + pos += 2; + + count = 0; + + /* + * AES-CCMP is the default Encryption preferred for TDLS, so + * RSN IE is filled only with CCMP CIPHER + * Note: TKIP is not used to encrypt TDLS link. + * + * Regardless of the cipher used on the AP connection, select CCMP + * here. + */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + pos += RSN_SELECTOR_LEN; + count++; + + WPA_PUT_LE16(count_pos, count); + + WPA_PUT_LE16(pos, 1); + pos += 2; + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE); + pos += RSN_SELECTOR_LEN; + + rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED; + rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2; +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_ALT_RSN_IE) { + wpa_printf(MSG_DEBUG, "TDLS: Use alternative RSN IE for " + "testing"); + rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED; + } +#endif /* CONFIG_TDLS_TESTING */ + WPA_PUT_LE16(pos, rsn_capab); + pos += 2; +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_ALT_RSN_IE) { + /* Number of PMKIDs */ + *pos++ = 0x00; + *pos++ = 0x00; + } +#endif /* CONFIG_TDLS_TESTING */ + + hdr->len = (pos - peer->rsnie_i) - 2; + peer->rsnie_i_len = pos - peer->rsnie_i; + wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for TPK handshake", + peer->rsnie_i, peer->rsnie_i_len); + +skip_rsnie: + buf_len = 0; + if (wpa_tdls_get_privacy(sm)) + buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) + + sizeof(struct wpa_tdls_timeoutie); +#ifdef CONFIG_TDLS_TESTING + if (wpa_tdls_get_privacy(sm) && + (tdls_testing & TDLS_TESTING_LONG_FRAME)) + buf_len += 170; + if (tdls_testing & TDLS_TESTING_DIFF_BSSID) + buf_len += sizeof(struct wpa_tdls_lnkid); +#endif /* CONFIG_TDLS_TESTING */ + rbuf = os_zalloc(buf_len + 1); + if (rbuf == NULL) { + wpa_tdls_peer_free(sm, peer); + return -1; + } + pos = rbuf; + + if (!wpa_tdls_get_privacy(sm)) + goto skip_ies; + + /* Initiator RSN IE */ + pos = wpa_add_ie(pos, peer->rsnie_i, peer->rsnie_i_len); + + ftie = (struct wpa_tdls_ftie *) pos; + ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION; + ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2; + + if (os_get_random(peer->inonce, WPA_NONCE_LEN)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "TDLS: Failed to get random data for initiator Nonce"); + os_free(rbuf); + wpa_tdls_peer_free(sm, peer); + return -1; + } + wpa_hexdump(MSG_DEBUG, "TDLS: Initiator Nonce for TPK handshake", + peer->inonce, WPA_NONCE_LEN); + os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN); + + wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TPK Handshake M1", + (u8 *) ftie, sizeof(struct wpa_tdls_ftie)); + + pos = (u8 *) (ftie + 1); + +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_LONG_FRAME) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to " + "FTIE"); + ftie->ie_len += 170; + *pos++ = 255; /* FTIE subelem */ + *pos++ = 168; /* FTIE subelem length */ + pos += 168; + } +#endif /* CONFIG_TDLS_TESTING */ + + /* Lifetime */ + peer->lifetime = TPK_LIFETIME; +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_SHORT_LIFETIME) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - use short TPK " + "lifetime"); + peer->lifetime = 301; + } + if (tdls_testing & TDLS_TESTING_LONG_LIFETIME) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - use long TPK " + "lifetime"); + peer->lifetime = 0xffffffff; + } +#endif /* CONFIG_TDLS_TESTING */ + pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie, + sizeof(timeoutie), peer->lifetime); + wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", peer->lifetime); + +skip_ies: + +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_DIFF_BSSID) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - use incorrect BSSID in " + "Link Identifier"); + struct wpa_tdls_lnkid *l = (struct wpa_tdls_lnkid *) pos; + wpa_tdls_linkid(sm, peer, l); + l->bssid[5] ^= 0x01; + pos += sizeof(*l); + } +#endif /* CONFIG_TDLS_TESTING */ + + wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Request / TPK " + "Handshake Message 1 (peer " MACSTR ")", + MAC2STR(peer->addr)); + + wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST, 1, 0, + rbuf, pos - rbuf); + os_free(rbuf); + + return 0; +} + + +static int wpa_tdls_send_tpk_m2(struct wpa_sm *sm, + const unsigned char *src_addr, u8 dtoken, + struct wpa_tdls_lnkid *lnkid, + const struct wpa_tdls_peer *peer) +{ + u8 *rbuf, *pos; + size_t buf_len; + u32 lifetime; + struct wpa_tdls_timeoutie timeoutie; + struct wpa_tdls_ftie *ftie; + + buf_len = 0; + if (wpa_tdls_get_privacy(sm)) { + /* Peer RSN IE, FTIE(Initiator Nonce, Responder Nonce), + * Lifetime */ + buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) + + sizeof(struct wpa_tdls_timeoutie); +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_LONG_FRAME) + buf_len += 170; +#endif /* CONFIG_TDLS_TESTING */ + } + + rbuf = os_zalloc(buf_len + 1); + if (rbuf == NULL) + return -1; + pos = rbuf; + + if (!wpa_tdls_get_privacy(sm)) + goto skip_ies; + + /* Peer RSN IE */ + pos = wpa_add_ie(pos, peer->rsnie_p, peer->rsnie_p_len); + + ftie = (struct wpa_tdls_ftie *) pos; + ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION; + /* TODO: ftie->mic_control to set 2-RESPONSE */ + os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN); + os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN); + ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2; + wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TPK M2", + (u8 *) ftie, sizeof(*ftie)); + + pos = (u8 *) (ftie + 1); + +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_LONG_FRAME) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to " + "FTIE"); + ftie->ie_len += 170; + *pos++ = 255; /* FTIE subelem */ + *pos++ = 168; /* FTIE subelem length */ + pos += 168; + } +#endif /* CONFIG_TDLS_TESTING */ + + /* Lifetime */ + lifetime = peer->lifetime; +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_WRONG_LIFETIME_RESP) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong TPK " + "lifetime in response"); + lifetime++; + } +#endif /* CONFIG_TDLS_TESTING */ + pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie, + sizeof(timeoutie), lifetime); + wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds from initiator", + lifetime); + + /* compute MIC before sending */ + wpa_tdls_ftie_mic(peer->tpk.kck, 2, (u8 *) lnkid, peer->rsnie_p, + (u8 *) &timeoutie, (u8 *) ftie, ftie->mic); + +skip_ies: + wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, 0, + rbuf, pos - rbuf); + os_free(rbuf); + + return 0; +} + + +static int wpa_tdls_send_tpk_m3(struct wpa_sm *sm, + const unsigned char *src_addr, u8 dtoken, + struct wpa_tdls_lnkid *lnkid, + const struct wpa_tdls_peer *peer) +{ + u8 *rbuf, *pos; + size_t buf_len; + struct wpa_tdls_ftie *ftie; + struct wpa_tdls_timeoutie timeoutie; + u32 lifetime; + + buf_len = 0; + if (wpa_tdls_get_privacy(sm)) { + /* Peer RSN IE, FTIE(Initiator Nonce, Responder Nonce), + * Lifetime */ + buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) + + sizeof(struct wpa_tdls_timeoutie); +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_LONG_FRAME) + buf_len += 170; +#endif /* CONFIG_TDLS_TESTING */ + } + + rbuf = os_zalloc(buf_len + 1); + if (rbuf == NULL) + return -1; + pos = rbuf; + + if (!wpa_tdls_get_privacy(sm)) + goto skip_ies; + + /* Peer RSN IE */ + pos = wpa_add_ie(pos, peer->rsnie_p, peer->rsnie_p_len); + + ftie = (struct wpa_tdls_ftie *) pos; + ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION; + /*TODO: ftie->mic_control to set 3-CONFIRM */ + os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN); + os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN); + ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2; + + pos = (u8 *) (ftie + 1); + +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_LONG_FRAME) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to " + "FTIE"); + ftie->ie_len += 170; + *pos++ = 255; /* FTIE subelem */ + *pos++ = 168; /* FTIE subelem length */ + pos += 168; + } +#endif /* CONFIG_TDLS_TESTING */ + + /* Lifetime */ + lifetime = peer->lifetime; +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_WRONG_LIFETIME_CONF) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong TPK " + "lifetime in confirm"); + lifetime++; + } +#endif /* CONFIG_TDLS_TESTING */ + pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie, + sizeof(timeoutie), lifetime); + wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", + lifetime); + + /* compute MIC before sending */ + wpa_tdls_ftie_mic(peer->tpk.kck, 3, (u8 *) lnkid, peer->rsnie_p, + (u8 *) &timeoutie, (u8 *) ftie, ftie->mic); + +skip_ies: + wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, 0, + rbuf, pos - rbuf); + os_free(rbuf); + + return 0; +} + + +static int wpa_tdls_send_discovery_response(struct wpa_sm *sm, + struct wpa_tdls_peer *peer, + u8 dialog_token) +{ + wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Discovery Response " + "(peer " MACSTR ")", MAC2STR(peer->addr)); + + return wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_DISCOVERY_RESPONSE, + dialog_token, 0, NULL, 0); +} + + +static int +wpa_tdls_process_discovery_request(struct wpa_sm *sm, const u8 *addr, + const u8 *buf, size_t len) +{ + struct wpa_eapol_ie_parse kde; + const struct wpa_tdls_lnkid *lnkid; + struct wpa_tdls_peer *peer; + size_t min_req_len = sizeof(struct wpa_tdls_frame) + + 1 /* dialog token */ + sizeof(struct wpa_tdls_lnkid); + u8 dialog_token; + + wpa_printf(MSG_DEBUG, "TDLS: Discovery Request from " MACSTR, + MAC2STR(addr)); + + if (len < min_req_len) { + wpa_printf(MSG_DEBUG, "TDLS Discovery Request is too short: " + "%d", (int) len); + return -1; + } + + dialog_token = buf[sizeof(struct wpa_tdls_frame)]; + + if (wpa_supplicant_parse_ies(buf + sizeof(struct wpa_tdls_frame) + 1, + len - (sizeof(struct wpa_tdls_frame) + 1), + &kde) < 0) + return -1; + + if (!kde.lnkid) { + wpa_printf(MSG_DEBUG, "TDLS: Link ID not found in Discovery " + "Request"); + return -1; + } + + lnkid = (const struct wpa_tdls_lnkid *) kde.lnkid; + + if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "TDLS: Discovery Request from different " + " BSS " MACSTR, MAC2STR(lnkid->bssid)); + return -1; + } + + peer = wpa_tdls_add_peer(sm, addr); + if (peer == NULL) + return -1; + + return wpa_tdls_send_discovery_response(sm, peer, dialog_token); +} + + +int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr) +{ + if (sm->tdls_disabled || !sm->tdls_supported) + return -1; + + wpa_printf(MSG_DEBUG, "TDLS: Sending Discovery Request to peer " + MACSTR, MAC2STR(addr)); + return wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_DISCOVERY_REQUEST, + 1, 0, NULL, 0); +} + + +static int copy_supp_rates(const struct wpa_eapol_ie_parse *kde, + struct wpa_tdls_peer *peer) +{ + if (!kde->supp_rates) { + wpa_printf(MSG_DEBUG, "TDLS: No supported rates received"); + return -1; + } + + peer->supp_rates_len = kde->supp_rates_len - 2; + if (peer->supp_rates_len > IEEE80211_MAX_SUPP_RATES) + peer->supp_rates_len = IEEE80211_MAX_SUPP_RATES; + os_memcpy(peer->supp_rates, kde->supp_rates + 2, peer->supp_rates_len); + + if (kde->ext_supp_rates) { + int clen = kde->ext_supp_rates_len - 2; + if (peer->supp_rates_len + clen > IEEE80211_MAX_SUPP_RATES) + clen = IEEE80211_MAX_SUPP_RATES - peer->supp_rates_len; + os_memcpy(peer->supp_rates + peer->supp_rates_len, + kde->ext_supp_rates + 2, clen); + peer->supp_rates_len += clen; + } + + return 0; +} + + +static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_tdls_peer *peer; + struct wpa_eapol_ie_parse kde; + struct wpa_ie_data ie; + int cipher; + const u8 *cpos; + struct wpa_tdls_ftie *ftie = NULL; + struct wpa_tdls_timeoutie *timeoutie; + struct wpa_tdls_lnkid *lnkid; + u32 lifetime = 0; +#if 0 + struct rsn_ie_hdr *hdr; + u8 *pos; + u16 rsn_capab; + u16 rsn_ver; +#endif + u8 dtoken; + u16 ielen; + u16 status = WLAN_STATUS_UNSPECIFIED_FAILURE; + int tdls_prohibited = sm->tdls_prohibited; + int existing_peer = 0; + + if (len < 3 + 3) + return -1; + + cpos = buf; + cpos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */; + + /* driver had already verified the frame format */ + dtoken = *cpos++; /* dialog token */ + + wpa_printf(MSG_INFO, "TDLS: Dialog Token in TPK M1 %d", dtoken); + + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0) { + existing_peer = 1; + break; + } + } + + if (peer == NULL) { + peer = wpa_tdls_add_peer(sm, src_addr); + if (peer == NULL) + goto error; + } + + /* capability information */ + peer->capability = WPA_GET_LE16(cpos); + cpos += 2; + + ielen = len - (cpos - buf); /* start of IE in buf */ + if (wpa_supplicant_parse_ies(cpos, ielen, &kde) < 0) { + wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in TPK M1"); + goto error; + } + + if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) { + wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in " + "TPK M1"); + goto error; + } + wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M1", + kde.lnkid, kde.lnkid_len); + lnkid = (struct wpa_tdls_lnkid *) kde.lnkid; + if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_INFO, "TDLS: TPK M1 from diff BSS"); + status = WLAN_STATUS_NOT_IN_SAME_BSS; + goto error; + } + + wpa_printf(MSG_DEBUG, "TDLS: TPK M1 - TPK initiator " MACSTR, + MAC2STR(src_addr)); + + if (copy_supp_rates(&kde, peer) < 0) + goto error; + +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) { + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0) + break; + } + if (peer == NULL) { + peer = wpa_tdls_add_peer(sm, src_addr); + if (peer == NULL) + goto error; + } + wpa_printf(MSG_DEBUG, "TDLS: Testing concurrent initiation of " + "TDLS setup - send own request"); + peer->initiator = 1; + wpa_tdls_send_tpk_m1(sm, peer); + } + + if ((tdls_testing & TDLS_TESTING_IGNORE_AP_PROHIBIT) && + tdls_prohibited) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - ignore AP prohibition " + "on TDLS"); + tdls_prohibited = 0; + } +#endif /* CONFIG_TDLS_TESTING */ + + if (tdls_prohibited) { + wpa_printf(MSG_INFO, "TDLS: TDLS prohibited in this BSS"); + status = WLAN_STATUS_REQUEST_DECLINED; + goto error; + } + + if (!wpa_tdls_get_privacy(sm)) { + if (kde.rsn_ie) { + wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M1 while " + "security is disabled"); + status = WLAN_STATUS_SECURITY_DISABLED; + goto error; + } + goto skip_rsn; + } + + if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie) || + kde.rsn_ie == NULL) { + wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M1"); + status = WLAN_STATUS_INVALID_PARAMETERS; + goto error; + } + + if (kde.rsn_ie_len > TDLS_MAX_IE_LEN) { + wpa_printf(MSG_INFO, "TDLS: Too long Initiator RSN IE in " + "TPK M1"); + status = WLAN_STATUS_INVALID_RSNIE; + goto error; + } + + if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) { + wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M1"); + status = WLAN_STATUS_INVALID_RSNIE; + goto error; + } + + cipher = ie.pairwise_cipher; + if (cipher & WPA_CIPHER_CCMP) { + wpa_printf(MSG_DEBUG, "TDLS: Using CCMP for direct link"); + cipher = WPA_CIPHER_CCMP; + } else { + wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M1"); + status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; + goto error; + } + + if ((ie.capabilities & + (WPA_CAPABILITY_NO_PAIRWISE | WPA_CAPABILITY_PEERKEY_ENABLED)) != + WPA_CAPABILITY_PEERKEY_ENABLED) { + wpa_printf(MSG_INFO, "TDLS: Invalid RSN Capabilities in " + "TPK M1"); + status = WLAN_STATUS_INVALID_RSN_IE_CAPAB; + goto error; + } + + /* Lifetime */ + if (kde.key_lifetime == NULL) { + wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M1"); + status = WLAN_STATUS_UNACCEPTABLE_LIFETIME; + goto error; + } + timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime; + lifetime = WPA_GET_LE32(timeoutie->value); + wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", lifetime); + if (lifetime < 300) { + wpa_printf(MSG_INFO, "TDLS: Too short TPK lifetime"); + status = WLAN_STATUS_UNACCEPTABLE_LIFETIME; + goto error; + } + +skip_rsn: + /* If found, use existing entry instead of adding a new one; + * how to handle the case where both ends initiate at the + * same time? */ + if (existing_peer) { + if (peer->tpk_success) { + wpa_printf(MSG_DEBUG, "TDLS: TDLS Setup Request while " + "direct link is enabled - tear down the " + "old link first"); +#if 0 + /* TODO: Disabling the link would be more proper + * operation here, but it seems to trigger a race with + * some drivers handling the new request frame. */ + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); +#else + if (sm->tdls_external_setup) + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, + src_addr); + else + wpa_tdls_del_key(sm, peer); +#endif + wpa_tdls_peer_free(sm, peer); + } + + /* + * An entry is already present, so check if we already sent a + * TDLS Setup Request. If so, compare MAC addresses and let the + * STA with the lower MAC address continue as the initiator. + * The other negotiation is terminated. + */ + if (peer->initiator) { + if (os_memcmp(sm->own_addr, src_addr, ETH_ALEN) < 0) { + wpa_printf(MSG_DEBUG, "TDLS: Discard request " + "from peer with higher address " + MACSTR, MAC2STR(src_addr)); + return -1; + } else { + wpa_printf(MSG_DEBUG, "TDLS: Accept request " + "from peer with lower address " + MACSTR " (terminate previously " + "initiated negotiation", + MAC2STR(src_addr)); + wpa_tdls_peer_free(sm, peer); + } + } + } + +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) { + if (os_memcmp(sm->own_addr, peer->addr, ETH_ALEN) < 0) { + /* + * The request frame from us is going to win, so do not + * replace information based on this request frame from + * the peer. + */ + goto skip_rsn_check; + } + } +#endif /* CONFIG_TDLS_TESTING */ + + peer->initiator = 0; /* Need to check */ + peer->dtoken = dtoken; + + if (!wpa_tdls_get_privacy(sm)) { + peer->rsnie_i_len = 0; + peer->rsnie_p_len = 0; + peer->cipher = WPA_CIPHER_NONE; + goto skip_rsn_check; + } + + ftie = (struct wpa_tdls_ftie *) kde.ftie; + os_memcpy(peer->inonce, ftie->Snonce, WPA_NONCE_LEN); + os_memcpy(peer->rsnie_i, kde.rsn_ie, kde.rsn_ie_len); + peer->rsnie_i_len = kde.rsn_ie_len; + peer->cipher = cipher; + + if (os_get_random(peer->rnonce, WPA_NONCE_LEN)) { + wpa_msg(sm->ctx->ctx, MSG_WARNING, + "TDLS: Failed to get random data for responder nonce"); + wpa_tdls_peer_free(sm, peer); + goto error; + } + +#if 0 + /* get version info from RSNIE received from Peer */ + hdr = (struct rsn_ie_hdr *) kde.rsn_ie; + rsn_ver = WPA_GET_LE16(hdr->version); + + /* use min(peer's version, out version) */ + if (rsn_ver > RSN_VERSION) + rsn_ver = RSN_VERSION; + + hdr = (struct rsn_ie_hdr *) peer->rsnie_p; + + hdr->elem_id = WLAN_EID_RSN; + WPA_PUT_LE16(hdr->version, rsn_ver); + pos = (u8 *) (hdr + 1); + + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED); + pos += RSN_SELECTOR_LEN; + /* Include only the selected cipher in pairwise cipher suite */ + WPA_PUT_LE16(pos, 1); + pos += 2; + if (cipher == WPA_CIPHER_CCMP) + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + pos += RSN_SELECTOR_LEN; + + WPA_PUT_LE16(pos, 1); + pos += 2; + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE); + pos += RSN_SELECTOR_LEN; + + rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED; + rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2; + WPA_PUT_LE16(pos, rsn_capab); + pos += 2; + + hdr->len = (pos - peer->rsnie_p) - 2; + peer->rsnie_p_len = pos - peer->rsnie_p; +#endif + + /* temp fix: validation of RSNIE later */ + os_memcpy(peer->rsnie_p, peer->rsnie_i, peer->rsnie_i_len); + peer->rsnie_p_len = peer->rsnie_i_len; + + wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for TPK handshake", + peer->rsnie_p, peer->rsnie_p_len); + + peer->lifetime = lifetime; + + wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid); + +skip_rsn_check: + /* add the peer to the driver as a "setup in progress" peer */ + wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, NULL, 0); + + wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Response / TPK M2"); + if (wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer) < 0) { + wpa_tdls_disable_link(sm, peer->addr); + goto error; + } + + return 0; + +error: + wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, + status); + return -1; +} + + +static void wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer) +{ + peer->tpk_success = 1; + eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer); + if (wpa_tdls_get_privacy(sm)) { + u32 lifetime = peer->lifetime; + /* + * Start the initiator process a bit earlier to avoid race + * condition with the responder sending teardown request. + */ + if (lifetime > 3 && peer->initiator) + lifetime -= 3; + eloop_register_timeout(lifetime, 0, wpa_tdls_tpk_timeout, + sm, peer); +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_NO_TPK_EXPIRATION) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - disable TPK " + "expiration"); + eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer); + } +#endif /* CONFIG_TDLS_TESTING */ + } + + /* add supported rates and capabilities to the TDLS peer */ + wpa_sm_tdls_peer_addset(sm, peer->addr, 0, peer->capability, + peer->supp_rates, peer->supp_rates_len); + + wpa_sm_tdls_oper(sm, TDLS_ENABLE_LINK, peer->addr); +} + + +static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_tdls_peer *peer; + struct wpa_eapol_ie_parse kde; + struct wpa_ie_data ie; + int cipher; + struct wpa_tdls_ftie *ftie; + struct wpa_tdls_timeoutie *timeoutie; + struct wpa_tdls_lnkid *lnkid; + u32 lifetime; + u8 dtoken; + int ielen; + u16 status; + const u8 *pos; + + wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Response / TPK M2 " + "(Peer " MACSTR ")", MAC2STR(src_addr)); + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0) + break; + } + if (peer == NULL) { + wpa_printf(MSG_INFO, "TDLS: No matching peer found for " + "TPK M2: " MACSTR, MAC2STR(src_addr)); + return -1; + } + wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_REQUEST); + + if (len < 3 + 2 + 1) + return -1; + pos = buf; + pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */; + status = WPA_GET_LE16(pos); + pos += 2 /* status code */; + + if (status != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_INFO, "TDLS: Status code in TPK M2: %u", + status); + if (sm->tdls_external_setup) + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); + return -1; + } + + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + + /* TODO: need to verify dialog token matches here or in kernel */ + dtoken = *pos++; /* dialog token */ + + wpa_printf(MSG_DEBUG, "TDLS: Dialog Token in TPK M2 %d", dtoken); + + if (len < 3 + 2 + 1 + 2) + return -1; + + /* capability information */ + peer->capability = WPA_GET_LE16(pos); + pos += 2; + + ielen = len - (pos - buf); /* start of IE in buf */ + if (wpa_supplicant_parse_ies(pos, ielen, &kde) < 0) { + wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in TPK M2"); + goto error; + } + +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_DECLINE_RESP) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - decline response"); + status = WLAN_STATUS_REQUEST_DECLINED; + goto error; + } +#endif /* CONFIG_TDLS_TESTING */ + + if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) { + wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in " + "TPK M2"); + goto error; + } + wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M2", + kde.lnkid, kde.lnkid_len); + lnkid = (struct wpa_tdls_lnkid *) kde.lnkid; + + if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_INFO, "TDLS: TPK M2 from different BSS"); + status = WLAN_STATUS_NOT_IN_SAME_BSS; + goto error; + } + + if (copy_supp_rates(&kde, peer) < 0) + goto error; + + if (!wpa_tdls_get_privacy(sm)) { + peer->rsnie_p_len = 0; + peer->cipher = WPA_CIPHER_NONE; + goto skip_rsn; + } + + if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie) || + kde.rsn_ie == NULL) { + wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M2"); + status = WLAN_STATUS_INVALID_PARAMETERS; + goto error; + } + wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2", + kde.rsn_ie, kde.rsn_ie_len); + + /* + * FIX: bitwise comparison of RSN IE is not the correct way of + * validation this. It can be different, but certain fields must + * match. Since we list only a single pairwise cipher in TPK M1, the + * memcmp is likely to work in most cases, though. + */ + if (kde.rsn_ie_len != peer->rsnie_i_len || + os_memcmp(peer->rsnie_i, kde.rsn_ie, peer->rsnie_i_len) != 0) { + wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M2 does " + "not match with RSN IE used in TPK M1"); + wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Sent in TPK M1", + peer->rsnie_i, peer->rsnie_i_len); + wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2", + kde.rsn_ie, kde.rsn_ie_len); + status = WLAN_STATUS_INVALID_RSNIE; + goto error; + } + + if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) { + wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M2"); + status = WLAN_STATUS_INVALID_RSNIE; + goto error; + } + + cipher = ie.pairwise_cipher; + if (cipher == WPA_CIPHER_CCMP) { + wpa_printf(MSG_DEBUG, "TDLS: Using CCMP for direct link"); + cipher = WPA_CIPHER_CCMP; + } else { + wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M2"); + status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; + goto error; + } + + wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M2", + kde.ftie, sizeof(*ftie)); + ftie = (struct wpa_tdls_ftie *) kde.ftie; + + if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) { + wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M2 does " + "not match with FTIE SNonce used in TPK M1"); + /* Silently discard the frame */ + return -1; + } + + /* Responder Nonce and RSN IE */ + os_memcpy(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN); + os_memcpy(peer->rsnie_p, kde.rsn_ie, kde.rsn_ie_len); + peer->rsnie_p_len = kde.rsn_ie_len; + peer->cipher = cipher; + + /* Lifetime */ + if (kde.key_lifetime == NULL) { + wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M2"); + status = WLAN_STATUS_UNACCEPTABLE_LIFETIME; + goto error; + } + timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime; + lifetime = WPA_GET_LE32(timeoutie->value); + wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds in TPK M2", + lifetime); + if (lifetime != peer->lifetime) { + wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in " + "TPK M2 (expected %u)", lifetime, peer->lifetime); + status = WLAN_STATUS_UNACCEPTABLE_LIFETIME; + goto error; + } + + wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid); + + /* Process MIC check to see if TPK M2 is right */ + if (wpa_supplicant_verify_tdls_mic(2, peer, (u8 *) lnkid, + (u8 *) timeoutie, ftie) < 0) { + /* Discard the frame */ + wpa_tdls_del_key(sm, peer); + wpa_tdls_peer_free(sm, peer); + if (sm->tdls_external_setup) + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); + return -1; + } + + wpa_tdls_set_key(sm, peer); + +skip_rsn: + peer->dtoken = dtoken; + + wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Confirm / " + "TPK Handshake Message 3"); + wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer); + + wpa_tdls_enable_link(sm, peer); + + return 0; + +error: + wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, + status); + if (sm->tdls_external_setup) + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); + return -1; +} + + +static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_tdls_peer *peer; + struct wpa_eapol_ie_parse kde; + struct wpa_tdls_ftie *ftie; + struct wpa_tdls_timeoutie *timeoutie; + struct wpa_tdls_lnkid *lnkid; + int ielen; + u16 status; + const u8 *pos; + u32 lifetime; + + wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Confirm / TPK M3 " + "(Peer " MACSTR ")", MAC2STR(src_addr)); + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0) + break; + } + if (peer == NULL) { + wpa_printf(MSG_INFO, "TDLS: No matching peer found for " + "TPK M3: " MACSTR, MAC2STR(src_addr)); + return -1; + } + wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_RESPONSE); + + if (len < 3 + 3) + return -1; + pos = buf; + pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */; + + status = WPA_GET_LE16(pos); + + if (status != 0) { + wpa_printf(MSG_INFO, "TDLS: Status code in TPK M3: %u", + status); + if (sm->tdls_external_setup) + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); + return -1; + } + pos += 2 /* status code */ + 1 /* dialog token */; + + ielen = len - (pos - buf); /* start of IE in buf */ + if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) { + wpa_printf(MSG_INFO, "TDLS: Failed to parse KDEs in TPK M3"); + return -1; + } + + if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) { + wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TPK M3"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M3", + (u8 *) kde.lnkid, kde.lnkid_len); + lnkid = (struct wpa_tdls_lnkid *) kde.lnkid; + + if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_INFO, "TDLS: TPK M3 from diff BSS"); + return -1; + } + + if (!wpa_tdls_get_privacy(sm)) + goto skip_rsn; + + if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_INFO, "TDLS: No FTIE in TPK M3"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M3", + kde.ftie, sizeof(*ftie)); + ftie = (struct wpa_tdls_ftie *) kde.ftie; + + if (kde.rsn_ie == NULL) { + wpa_printf(MSG_INFO, "TDLS: No RSN IE in TPK M3"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M3", + kde.rsn_ie, kde.rsn_ie_len); + if (kde.rsn_ie_len != peer->rsnie_p_len || + os_memcmp(kde.rsn_ie, peer->rsnie_p, peer->rsnie_p_len) != 0) { + wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M3 does not match " + "with the one sent in TPK M2"); + return -1; + } + + if (!os_memcmp(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN) == 0) { + wpa_printf(MSG_INFO, "TDLS: FTIE ANonce in TPK M3 does " + "not match with FTIE ANonce used in TPK M2"); + return -1; + } + + if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) { + wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M3 does not " + "match with FTIE SNonce used in TPK M1"); + return -1; + } + + if (kde.key_lifetime == NULL) { + wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M3"); + return -1; + } + timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime; + wpa_hexdump(MSG_DEBUG, "TDLS: Timeout IE Received from TPK M3", + (u8 *) timeoutie, sizeof(*timeoutie)); + lifetime = WPA_GET_LE32(timeoutie->value); + wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds in TPK M3", + lifetime); + if (lifetime != peer->lifetime) { + wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in " + "TPK M3 (expected %u)", lifetime, peer->lifetime); + if (sm->tdls_external_setup) + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); + return -1; + } + + if (wpa_supplicant_verify_tdls_mic(3, peer, (u8 *) lnkid, + (u8 *) timeoutie, ftie) < 0) { + wpa_tdls_del_key(sm, peer); + wpa_tdls_peer_free(sm, peer); + return -1; + } + + if (wpa_tdls_set_key(sm, peer) < 0) + return -1; + +skip_rsn: + wpa_tdls_enable_link(sm, peer); + + return 0; +} + + +static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs) +{ + struct wpa_tdls_timeoutie *lifetime = (struct wpa_tdls_timeoutie *) ie; + + os_memset(lifetime, 0, ie_len); + lifetime->ie_type = WLAN_EID_TIMEOUT_INTERVAL; + lifetime->ie_len = sizeof(struct wpa_tdls_timeoutie) - 2; + lifetime->interval_type = WLAN_TIMEOUT_KEY_LIFETIME; + WPA_PUT_LE32(lifetime->value, tsecs); + os_memcpy(pos, ie, ie_len); + return pos + ie_len; +} + + +/** + * wpa_tdls_start - Initiate TDLS handshake (send TPK Handshake Message 1) + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @peer: MAC address of the peer STA + * Returns: 0 on success, or -1 on failure + * + * Send TPK Handshake Message 1 info to driver to start TDLS + * handshake with the peer. + */ +int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr) +{ + struct wpa_tdls_peer *peer; + int tdls_prohibited = sm->tdls_prohibited; + + if (sm->tdls_disabled || !sm->tdls_supported) + return -1; + +#ifdef CONFIG_TDLS_TESTING + if ((tdls_testing & TDLS_TESTING_IGNORE_AP_PROHIBIT) && + tdls_prohibited) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - ignore AP prohibition " + "on TDLS"); + tdls_prohibited = 0; + } +#endif /* CONFIG_TDLS_TESTING */ + + if (tdls_prohibited) { + wpa_printf(MSG_DEBUG, "TDLS: TDLS is prohibited in this BSS - " + "reject request to start setup"); + return -1; + } + + /* Find existing entry and if found, use that instead of adding + * a new one */ + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) + break; + } + + if (peer == NULL) { + peer = wpa_tdls_add_peer(sm, addr); + if (peer == NULL) + return -1; + } + + peer->initiator = 1; + + /* add the peer to the driver as a "setup in progress" peer */ + wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, NULL, 0); + + if (wpa_tdls_send_tpk_m1(sm, peer) < 0) { + wpa_tdls_disable_link(sm, peer->addr); + return -1; + } + + return 0; +} + + +int wpa_tdls_reneg(struct wpa_sm *sm, const u8 *addr) +{ + struct wpa_tdls_peer *peer; + + if (sm->tdls_disabled || !sm->tdls_supported) + return -1; + + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) + break; + } + + if (peer == NULL || !peer->tpk_success) + return -1; + + if (sm->tdls_external_setup) { + /* + * Disable previous link to allow renegotiation to be completed + * on AP path. + */ + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr); + } + + return wpa_tdls_start(sm, addr); +} + + +/** + * wpa_supplicant_rx_tdls - Receive TDLS data frame + * + * This function is called to receive TDLS (ethertype = 0x890d) data frames. + */ +static void wpa_supplicant_rx_tdls(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_sm *sm = ctx; + struct wpa_tdls_frame *tf; + + wpa_hexdump(MSG_DEBUG, "TDLS: Received Data frame encapsulation", + buf, len); + + if (sm->tdls_disabled || !sm->tdls_supported) { + wpa_printf(MSG_DEBUG, "TDLS: Discard message - TDLS disabled " + "or unsupported by driver"); + return; + } + + if (os_memcmp(src_addr, sm->own_addr, ETH_ALEN) == 0) { + wpa_printf(MSG_DEBUG, "TDLS: Discard copy of own message"); + return; + } + + if (len < sizeof(*tf)) { + wpa_printf(MSG_INFO, "TDLS: Drop too short frame"); + return; + } + + /* Check to make sure its a valid encapsulated TDLS frame */ + tf = (struct wpa_tdls_frame *) buf; + if (tf->payloadtype != 2 /* TDLS_RFTYPE */ || + tf->category != WLAN_ACTION_TDLS) { + wpa_printf(MSG_INFO, "TDLS: Invalid frame - payloadtype=%u " + "category=%u action=%u", + tf->payloadtype, tf->category, tf->action); + return; + } + + switch (tf->action) { + case WLAN_TDLS_SETUP_REQUEST: + wpa_tdls_process_tpk_m1(sm, src_addr, buf, len); + break; + case WLAN_TDLS_SETUP_RESPONSE: + wpa_tdls_process_tpk_m2(sm, src_addr, buf, len); + break; + case WLAN_TDLS_SETUP_CONFIRM: + wpa_tdls_process_tpk_m3(sm, src_addr, buf, len); + break; + case WLAN_TDLS_TEARDOWN: + wpa_tdls_recv_teardown(sm, src_addr, buf, len); + break; + case WLAN_TDLS_DISCOVERY_REQUEST: + wpa_tdls_process_discovery_request(sm, src_addr, buf, len); + break; + default: + /* Kernel code will process remaining frames */ + wpa_printf(MSG_DEBUG, "TDLS: Ignore TDLS frame action code %u", + tf->action); + break; + } +} + + +/** + * wpa_tdls_init - Initialize driver interface parameters for TDLS + * @wpa_s: Pointer to wpa_supplicant data + * Returns: 0 on success, -1 on failure + * + * This function is called to initialize driver interface parameters for TDLS. + * wpa_drv_init() must have been called before this function to initialize the + * driver interface. + */ +int wpa_tdls_init(struct wpa_sm *sm) +{ + if (sm == NULL) + return -1; + + sm->l2_tdls = l2_packet_init(sm->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) { + wpa_printf(MSG_ERROR, "TDLS: Failed to open l2_packet " + "connection"); + return -1; + } + + /* + * Drivers that support TDLS but don't implement the get_capa callback + * are assumed to perform everything internally + */ + if (wpa_sm_tdls_get_capa(sm, &sm->tdls_supported, + &sm->tdls_external_setup) < 0) { + sm->tdls_supported = 1; + sm->tdls_external_setup = 0; + } + + wpa_printf(MSG_DEBUG, "TDLS: TDLS operation%s supported by " + "driver", sm->tdls_supported ? "" : " not"); + wpa_printf(MSG_DEBUG, "TDLS: Driver uses %s link setup", + sm->tdls_external_setup ? "external" : "internal"); + + return 0; +} + + +static void wpa_tdls_remove_peers(struct wpa_sm *sm) +{ + struct wpa_tdls_peer *peer, *tmp; + + peer = sm->tdls; + sm->tdls = NULL; + + while (peer) { + int res; + tmp = peer->next; + res = wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr); + wpa_printf(MSG_DEBUG, "TDLS: Remove peer " MACSTR " (res=%d)", + MAC2STR(peer->addr), res); + wpa_tdls_peer_free(sm, peer); + os_free(peer); + peer = tmp; + } +} + + +/** + * wpa_tdls_deinit - Deinitialize driver interface parameters for TDLS + * + * This function is called to recover driver interface parameters for TDLS + * and frees resources allocated for it. + */ +void wpa_tdls_deinit(struct wpa_sm *sm) +{ + if (sm == NULL) + return; + + if (sm->l2_tdls) + l2_packet_deinit(sm->l2_tdls); + sm->l2_tdls = NULL; + + wpa_tdls_remove_peers(sm); +} + + +void wpa_tdls_assoc(struct wpa_sm *sm) +{ + wpa_printf(MSG_DEBUG, "TDLS: Remove peers on association"); + wpa_tdls_remove_peers(sm); +} + + +void wpa_tdls_disassoc(struct wpa_sm *sm) +{ + wpa_printf(MSG_DEBUG, "TDLS: Remove peers on disassociation"); + wpa_tdls_remove_peers(sm); +} + + +static int wpa_tdls_prohibited(const u8 *ies, size_t len) +{ + struct wpa_eapol_ie_parse elems; + + if (ies == NULL) + return 0; + + if (wpa_supplicant_parse_ies(ies, len, &elems) < 0) + return 0; + + if (elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5) + return 0; + + /* bit 38 - TDLS Prohibited */ + return !!(elems.ext_capab[2 + 4] & 0x40); +} + + +void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len) +{ + sm->tdls_prohibited = wpa_tdls_prohibited(ies, len); + wpa_printf(MSG_DEBUG, "TDLS: TDLS is %s in the target BSS", + sm->tdls_prohibited ? "prohibited" : "allowed"); +} + + +void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len) +{ + if (!sm->tdls_prohibited && wpa_tdls_prohibited(ies, len)) { + wpa_printf(MSG_DEBUG, "TDLS: TDLS prohibited based on " + "(Re)Association Response IEs"); + sm->tdls_prohibited = 1; + } +} + + +void wpa_tdls_enable(struct wpa_sm *sm, int enabled) +{ + wpa_printf(MSG_DEBUG, "TDLS: %s", enabled ? "enabled" : "disabled"); + sm->tdls_disabled = !enabled; +} + + +int wpa_tdls_is_external_setup(struct wpa_sm *sm) +{ + return sm->tdls_external_setup; +} diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index 9439f97210b14..e50404ce78a79 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -1,15 +1,9 @@ /* * WPA Supplicant - WPA state machine and EAPOL-Key processing - * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-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 - * published 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" @@ -17,6 +11,7 @@ #include "common.h" #include "crypto/aes_wrap.h" #include "crypto/crypto.h" +#include "crypto/random.h" #include "common/ieee802_11_defs.h" #include "eapol_supp/eapol_supp_sm.h" #include "wpa.h" @@ -49,21 +44,26 @@ void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, * BSSID from the driver. */ if (wpa_sm_get_bssid(sm, sm->bssid) < 0) { - wpa_printf(MSG_DEBUG, "WPA: Failed to read BSSID for " - "EAPOL-Key destination address"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: Failed to read BSSID for " + "EAPOL-Key destination address"); } else { dest = sm->bssid; - wpa_printf(MSG_DEBUG, "WPA: Use BSSID (" MACSTR - ") as the destination for EAPOL-Key", - MAC2STR(dest)); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: Use BSSID (" MACSTR + ") as the destination for EAPOL-Key", + MAC2STR(dest)); } } if (key_mic && wpa_eapol_key_mic(kck, ver, msg, msg_len, key_mic)) { - wpa_printf(MSG_ERROR, "WPA: Failed to generate EAPOL-Key " - "version %d MIC", ver); + wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, + "WPA: Failed to generate EAPOL-Key " + "version %d MIC", ver); goto out; } + wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, 16); + wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, 16); wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key", msg, msg_len); wpa_sm_ether_send(sm, dest, proto, msg, msg_len); eapol_sm_notify_tx_eapol_key(sm->eapol); @@ -91,14 +91,14 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) if (wpa_key_mgmt_ft(sm->key_mgmt) || wpa_key_mgmt_sha256(sm->key_mgmt)) ver = WPA_KEY_INFO_TYPE_AES_128_CMAC; - else if (sm->pairwise_cipher == WPA_CIPHER_CCMP) + else if (sm->pairwise_cipher != WPA_CIPHER_TKIP) ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; if (wpa_sm_get_bssid(sm, bssid) < 0) { - wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key " - "request"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "Failed to read BSSID for EAPOL-Key request"); return; } @@ -124,9 +124,10 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) WPA_PUT_BE16(reply->key_data_length, 0); - wpa_printf(MSG_INFO, "WPA: Sending EAPOL-Key Request (error=%d " - "pairwise=%d ptk_set=%d len=%lu)", - error, pairwise, sm->ptk_set, (unsigned long) rlen); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Sending EAPOL-Key Request (error=%d " + "pairwise=%d ptk_set=%d len=%lu)", + error, pairwise, sm->ptk_set, (unsigned long) rlen); wpa_eapol_key_send(sm, sm->ptk.kck, ver, bssid, ETH_P_EAPOL, rbuf, rlen, key_info & WPA_KEY_INFO_MIC ? reply->key_mic : NULL); @@ -144,12 +145,14 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, * not have enough time to get the association information * event before receiving this 1/4 message, so try to find a * matching PMKSA cache entry here. */ - sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, pmkid); + sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, pmkid, + NULL); if (sm->cur_pmksa) { - wpa_printf(MSG_DEBUG, "RSN: found matching PMKID from " - "PMKSA cache"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: found matching PMKID from PMKSA cache"); } else { - wpa_printf(MSG_DEBUG, "RSN: no matching PMKID found"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: no matching PMKID found"); abort_cached = 1; } } @@ -187,29 +190,38 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, #endif /* CONFIG_IEEE80211R */ } if (res == 0) { + struct rsn_pmksa_cache_entry *sa = NULL; wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state " "machines", sm->pmk, pmk_len); sm->pmk_len = pmk_len; - if (sm->proto == WPA_PROTO_RSN) { - pmksa_cache_add(sm->pmksa, sm->pmk, pmk_len, - src_addr, sm->own_addr, - sm->network_ctx, sm->key_mgmt); + if (sm->proto == WPA_PROTO_RSN && + !wpa_key_mgmt_ft(sm->key_mgmt)) { + sa = pmksa_cache_add(sm->pmksa, + sm->pmk, pmk_len, + src_addr, sm->own_addr, + sm->network_ctx, + sm->key_mgmt); } if (!sm->cur_pmksa && pmkid && - pmksa_cache_get(sm->pmksa, src_addr, pmkid)) { - wpa_printf(MSG_DEBUG, "RSN: the new PMK " - "matches with the PMKID"); + pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL)) + { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: the new PMK matches with the " + "PMKID"); abort_cached = 0; } + + if (!sm->cur_pmksa) + sm->cur_pmksa = sa; } else { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to get master session key from " - "EAPOL state machines"); - wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, - "WPA: Key handshake aborted"); + "EAPOL state machines - key handshake " + "aborted"); if (sm->cur_pmksa) { - wpa_printf(MSG_DEBUG, "RSN: Cancelled PMKSA " - "caching attempt"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Cancelled PMKSA caching " + "attempt"); sm->cur_pmksa = NULL; abort_cached = 1; } else if (!abort_cached) { @@ -218,13 +230,15 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, } } - if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt)) { + if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && + !wpa_key_mgmt_ft(sm->key_mgmt)) { /* Send EAPOL-Start to trigger full EAP authentication. */ u8 *buf; size_t buflen; - wpa_printf(MSG_DEBUG, "RSN: no PMKSA entry found - trigger " - "full EAP authentication"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: no PMKSA entry found - trigger " + "full EAP authentication"); buf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_START, NULL, 0, &buflen, NULL); if (buf) { @@ -265,8 +279,8 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, u8 *rsn_ie_buf = NULL; if (wpa_ie == NULL) { - wpa_printf(MSG_WARNING, "WPA: No wpa_ie set - cannot " - "generate msg 2/4"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No wpa_ie set - " + "cannot generate msg 2/4"); return -1; } @@ -321,6 +335,8 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, os_memcpy(reply->key_length, key->key_length, 2); os_memcpy(reply->replay_counter, key->replay_counter, WPA_REPLAY_COUNTER_LEN); + wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter", reply->replay_counter, + WPA_REPLAY_COUNTER_LEN); WPA_PUT_BE16(reply->key_data_length, wpa_ie_len); os_memcpy(reply + 1, wpa_ie, wpa_ie_len); @@ -328,7 +344,7 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, os_memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN); - wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4"); wpa_eapol_key_send(sm, ptk->kck, ver, dst, ETH_P_EAPOL, rbuf, rlen, reply->key_mic); @@ -340,7 +356,7 @@ static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, struct wpa_ptk *ptk) { - size_t ptk_len = sm->pairwise_cipher == WPA_CIPHER_CCMP ? 48 : 64; + size_t ptk_len = sm->pairwise_cipher != WPA_CIPHER_TKIP ? 48 : 64; #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) return wpa_derive_ptk_ft(sm, src_addr, key, ptk, ptk_len); @@ -365,14 +381,14 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, int res; if (wpa_sm_get_network_ctx(sm) == NULL) { - wpa_printf(MSG_WARNING, "WPA: No SSID info found (msg 1 of " - "4)."); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No SSID info " + "found (msg 1 of 4)"); return; } wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE); - wpa_printf(MSG_DEBUG, "WPA: RX message 1 of 4-Way Handshake from " - MACSTR " (ver=%d)", MAC2STR(src_addr), ver); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 1 of 4-Way " + "Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver); os_memset(&ie, 0, sizeof(ie)); @@ -382,7 +398,8 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, const u8 *_buf = (const u8 *) (key + 1); size_t len = WPA_GET_BE16(key->key_data_length); wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", _buf, len); - wpa_supplicant_parse_ies(_buf, len, &ie); + if (wpa_supplicant_parse_ies(_buf, len, &ie) < 0) + goto failed; if (ie.pmkid) { wpa_hexdump(MSG_DEBUG, "RSN: PMKID from " "Authenticator", ie.pmkid, PMKID_LEN); @@ -392,15 +409,15 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, res = wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid); if (res == -2) { - wpa_printf(MSG_DEBUG, "RSN: Do not reply to msg 1/4 - " - "requesting full EAP authentication"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Do not reply to " + "msg 1/4 - requesting full EAP authentication"); return; } if (res) goto failed; if (sm->renew_snonce) { - if (os_get_random(sm->snonce, WPA_NONCE_LEN)) { + if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to get random data for SNonce"); goto failed; @@ -462,15 +479,16 @@ static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm, * Start preauthentication after a short wait to avoid a * possible race condition between the data receive and key * configuration after the 4-Way Handshake. This increases the - * likelyhood of the first preauth EAPOL-Start frame getting to + * likelihood of the first preauth EAPOL-Start frame getting to * the target AP. */ eloop_register_timeout(1, 0, wpa_sm_start_preauth, sm, NULL); } if (sm->cur_pmksa && sm->cur_pmksa->opportunistic) { - wpa_printf(MSG_DEBUG, "RSN: Authenticator accepted " - "opportunistic PMKSA entry - marking it valid"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Authenticator accepted " + "opportunistic PMKSA entry - marking it valid"); sm->cur_pmksa->opportunistic = 0; } @@ -486,7 +504,7 @@ static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm, static void wpa_sm_rekey_ptk(void *eloop_ctx, void *timeout_ctx) { struct wpa_sm *sm = eloop_ctx; - wpa_printf(MSG_DEBUG, "WPA: Request PTK rekeying"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Request PTK rekeying"); wpa_sm_key_request(sm, 0, 1); } @@ -499,29 +517,26 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, const u8 *key_rsc; u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - wpa_printf(MSG_DEBUG, "WPA: Installing PTK to the driver."); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: Installing PTK to the driver"); - switch (sm->pairwise_cipher) { - case WPA_CIPHER_CCMP: - alg = WPA_ALG_CCMP; - keylen = 16; - rsclen = 6; - break; - case WPA_CIPHER_TKIP: - alg = WPA_ALG_TKIP; - keylen = 32; - rsclen = 6; - break; - case WPA_CIPHER_NONE: - wpa_printf(MSG_DEBUG, "WPA: Pairwise Cipher Suite: " - "NONE - do not use pairwise keys"); + if (sm->pairwise_cipher == WPA_CIPHER_NONE) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Pairwise Cipher " + "Suite: NONE - do not use pairwise keys"); return 0; - default: - wpa_printf(MSG_WARNING, "WPA: Unsupported pairwise cipher %d", - sm->pairwise_cipher); + } + + if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Unsupported pairwise cipher %d", + sm->pairwise_cipher); return -1; } + alg = wpa_cipher_to_alg(sm->pairwise_cipher); + keylen = wpa_cipher_key_len(sm->pairwise_cipher); + rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher); + if (sm->proto == WPA_PROTO_RSN) { key_rsc = null_rsc; } else { @@ -531,9 +546,10 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, key_rsc, rsclen, (u8 *) sm->ptk.tk1, keylen) < 0) { - wpa_printf(MSG_WARNING, "WPA: Failed to set PTK to the " - "driver (alg=%d keylen=%d bssid=" MACSTR ")", - alg, keylen, MAC2STR(sm->bssid)); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Failed to set PTK to the " + "driver (alg=%d keylen=%d bssid=" MACSTR ")", + alg, keylen, MAC2STR(sm->bssid)); return -1; } @@ -547,59 +563,31 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, } -static int wpa_supplicant_check_group_cipher(int group_cipher, +static int wpa_supplicant_check_group_cipher(struct wpa_sm *sm, + int group_cipher, int keylen, int maxkeylen, int *key_rsc_len, enum wpa_alg *alg) { - int ret = 0; + int klen; - switch (group_cipher) { - case WPA_CIPHER_CCMP: - if (keylen != 16 || maxkeylen < 16) { - ret = -1; - break; - } - *key_rsc_len = 6; - *alg = WPA_ALG_CCMP; - break; - case WPA_CIPHER_TKIP: - if (keylen != 32 || maxkeylen < 32) { - ret = -1; - break; - } - *key_rsc_len = 6; - *alg = WPA_ALG_TKIP; - break; - case WPA_CIPHER_WEP104: - if (keylen != 13 || maxkeylen < 13) { - ret = -1; - break; - } - *key_rsc_len = 0; - *alg = WPA_ALG_WEP; - break; - case WPA_CIPHER_WEP40: - if (keylen != 5 || maxkeylen < 5) { - ret = -1; - break; - } - *key_rsc_len = 0; - *alg = WPA_ALG_WEP; - break; - default: - wpa_printf(MSG_WARNING, "WPA: Unsupported Group Cipher %d", - group_cipher); + *alg = wpa_cipher_to_alg(group_cipher); + if (*alg == WPA_ALG_NONE) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Unsupported Group Cipher %d", + group_cipher); return -1; } + *key_rsc_len = wpa_cipher_rsc_len(group_cipher); - if (ret < 0 ) { - wpa_printf(MSG_WARNING, "WPA: Unsupported %s Group Cipher key " - "length %d (%d).", - wpa_cipher_txt(group_cipher), keylen, maxkeylen); + klen = wpa_cipher_key_len(group_cipher); + if (keylen != klen || maxkeylen < klen) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Unsupported %s Group Cipher key length %d (%d)", + wpa_cipher_txt(group_cipher), keylen, maxkeylen); + return -1; } - - return ret; + return 0; } @@ -619,9 +607,9 @@ static int wpa_supplicant_install_gtk(struct wpa_sm *sm, u8 gtk_buf[32]; wpa_hexdump_key(MSG_DEBUG, "WPA: Group Key", gd->gtk, gd->gtk_len); - wpa_printf(MSG_DEBUG, "WPA: Installing GTK to the driver " - "(keyidx=%d tx=%d len=%d).", gd->keyidx, gd->tx, - gd->gtk_len); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: Installing GTK to the driver (keyidx=%d tx=%d len=%d)", + gd->keyidx, gd->tx, gd->gtk_len); wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, gd->key_rsc_len); if (sm->group_cipher == WPA_CIPHER_TKIP) { /* Swap Tx/Rx keys for Michael MIC */ @@ -631,21 +619,21 @@ static int wpa_supplicant_install_gtk(struct wpa_sm *sm, _gtk = gtk_buf; } if (sm->pairwise_cipher == WPA_CIPHER_NONE) { - if (wpa_sm_set_key(sm, gd->alg, - (u8 *) "\xff\xff\xff\xff\xff\xff", + if (wpa_sm_set_key(sm, gd->alg, NULL, gd->keyidx, 1, key_rsc, gd->key_rsc_len, _gtk, gd->gtk_len) < 0) { - wpa_printf(MSG_WARNING, "WPA: Failed to set " - "GTK to the driver (Group only)."); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Failed to set GTK to the driver " + "(Group only)"); return -1; } - } else if (wpa_sm_set_key(sm, gd->alg, - (u8 *) "\xff\xff\xff\xff\xff\xff", + } else if (wpa_sm_set_key(sm, gd->alg, broadcast_ether_addr, gd->keyidx, gd->tx, key_rsc, gd->key_rsc_len, _gtk, gd->gtk_len) < 0) { - wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to " - "the driver (alg=%d keylen=%d keyidx=%d)", - gd->alg, gd->gtk_len, gd->keyidx); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Failed to set GTK to " + "the driver (alg=%d keylen=%d keyidx=%d)", + gd->alg, gd->gtk_len, gd->keyidx); return -1; } @@ -662,8 +650,9 @@ static int wpa_supplicant_gtk_tx_bit_workaround(const struct wpa_sm *sm, * doing Group Key only APs) and without this workaround, the * data connection does not work because wpa_supplicant * configured non-zero keyidx to be used for unicast. */ - wpa_printf(MSG_INFO, "WPA: Tx bit set for GTK, but pairwise " - "keys are used - ignore Tx bit"); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Tx bit set for GTK, but pairwise " + "keys are used - ignore Tx bit"); return 0; } return tx; @@ -702,11 +691,12 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, os_memcpy(gd.gtk, gtk, gtk_len); gd.gtk_len = gtk_len; - if (wpa_supplicant_check_group_cipher(sm->group_cipher, + if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, gtk_len, gtk_len, &gd.key_rsc_len, &gd.alg) || wpa_supplicant_install_gtk(sm, &gd, key->key_rsc)) { - wpa_printf(MSG_DEBUG, "RSN: Failed to install GTK"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Failed to install GTK"); return -1; } @@ -733,22 +723,21 @@ static int ieee80211w_set_keys(struct wpa_sm *sm, return -1; igtk = (const struct wpa_igtk_kde *) ie->igtk; keyidx = WPA_GET_LE16(igtk->keyid); - wpa_printf(MSG_DEBUG, "WPA: IGTK keyid %d " - "pn %02x%02x%02x%02x%02x%02x", - keyidx, MAC2STR(igtk->pn)); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: IGTK keyid %d " + "pn %02x%02x%02x%02x%02x%02x", + keyidx, MAC2STR(igtk->pn)); wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK", igtk->igtk, WPA_IGTK_LEN); if (keyidx > 4095) { - wpa_printf(MSG_WARNING, "WPA: Invalid IGTK KeyID %d", - keyidx); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Invalid IGTK KeyID %d", keyidx); return -1; } - if (wpa_sm_set_key(sm, WPA_ALG_IGTK, - (u8 *) "\xff\xff\xff\xff\xff\xff", + if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr, keyidx, 0, igtk->pn, sizeof(igtk->pn), igtk->igtk, WPA_IGTK_LEN) < 0) { - wpa_printf(MSG_WARNING, "WPA: Failed to configure IGTK" - " to the driver"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Failed to configure IGTK to the driver"); return -1; } } @@ -774,8 +763,8 @@ static void wpa_report_ie_mismatch(struct wpa_sm *sm, } if (wpa_ie) { if (!sm->ap_wpa_ie) { - wpa_printf(MSG_INFO, "WPA: No WPA IE in " - "Beacon/ProbeResp"); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: No WPA IE in Beacon/ProbeResp"); } wpa_hexdump(MSG_INFO, "WPA: WPA IE in 3/4 msg", wpa_ie, wpa_ie_len); @@ -787,14 +776,14 @@ static void wpa_report_ie_mismatch(struct wpa_sm *sm, } if (rsn_ie) { if (!sm->ap_rsn_ie) { - wpa_printf(MSG_INFO, "WPA: No RSN IE in " - "Beacon/ProbeResp"); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: No RSN IE in Beacon/ProbeResp"); } wpa_hexdump(MSG_INFO, "WPA: RSN IE in 3/4 msg", rsn_ie, rsn_ie_len); } - wpa_sm_disassociate(sm, WLAN_REASON_IE_IN_4WAY_DIFFERS); + wpa_sm_deauthenticate(sm, WLAN_REASON_IE_IN_4WAY_DIFFERS); } @@ -811,15 +800,15 @@ static int ft_validate_mdie(struct wpa_sm *sm, if (ie->mdie == NULL || ie->mdie_len < 2 + sizeof(*mdie) || os_memcmp(mdie->mobility_domain, sm->mobility_domain, MOBILITY_DOMAIN_ID_LEN) != 0) { - wpa_printf(MSG_DEBUG, "FT: MDIE in msg 3/4 did not " - "match with the current mobility domain"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: MDIE in msg 3/4 did " + "not match with the current mobility domain"); return -1; } if (assoc_resp_mdie && (assoc_resp_mdie[1] != ie->mdie[1] || os_memcmp(assoc_resp_mdie, ie->mdie, 2 + ie->mdie[1]) != 0)) { - wpa_printf(MSG_DEBUG, "FT: MDIE mismatch"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: MDIE mismatch"); wpa_hexdump(MSG_DEBUG, "FT: MDIE in EAPOL-Key msg 3/4", ie->mdie, 2 + ie->mdie[1]); wpa_hexdump(MSG_DEBUG, "FT: MDIE in (Re)Association Response", @@ -837,7 +826,8 @@ static int ft_validate_ftie(struct wpa_sm *sm, const u8 *assoc_resp_ftie) { if (ie->ftie == NULL) { - wpa_printf(MSG_DEBUG, "FT: No FTIE in EAPOL-Key msg 3/4"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "FT: No FTIE in EAPOL-Key msg 3/4"); return -1; } @@ -846,7 +836,7 @@ static int ft_validate_ftie(struct wpa_sm *sm, if (assoc_resp_ftie[1] != ie->ftie[1] || os_memcmp(assoc_resp_ftie, ie->ftie, 2 + ie->ftie[1]) != 0) { - wpa_printf(MSG_DEBUG, "FT: FTIE mismatch"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: FTIE mismatch"); wpa_hexdump(MSG_DEBUG, "FT: FTIE in EAPOL-Key msg 3/4", ie->ftie, 2 + ie->ftie[1]); wpa_hexdump(MSG_DEBUG, "FT: FTIE in (Re)Association Response", @@ -873,14 +863,15 @@ static int ft_validate_rsnie(struct wpa_sm *sm, */ if (wpa_parse_wpa_ie_rsn(ie->rsn_ie, ie->rsn_ie_len, &rsn) < 0 || rsn.num_pmkid != 1 || rsn.pmkid == NULL) { - wpa_printf(MSG_DEBUG, "FT: No PMKR1Name in " - "FT 4-way handshake message 3/4"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: No PMKR1Name in " + "FT 4-way handshake message 3/4"); return -1; } if (os_memcmp(rsn.pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0) { - wpa_printf(MSG_DEBUG, "FT: PMKR1Name mismatch in " - "FT 4-way handshake message 3/4"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "FT: PMKR1Name mismatch in " + "FT 4-way handshake message 3/4"); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Authenticator", rsn.pmkid, WPA_PMK_NAME_LEN); wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name", @@ -932,14 +923,17 @@ static int wpa_supplicant_validate_ie(struct wpa_sm *sm, struct wpa_eapol_ie_parse *ie) { if (sm->ap_wpa_ie == NULL && sm->ap_rsn_ie == NULL) { - wpa_printf(MSG_DEBUG, "WPA: No WPA/RSN IE for this AP known. " - "Trying to get from scan results"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: No WPA/RSN IE for this AP known. " + "Trying to get from scan results"); if (wpa_sm_get_beacon_ie(sm) < 0) { - wpa_printf(MSG_WARNING, "WPA: Could not find AP from " - "the scan results"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Could not find AP from " + "the scan results"); } else { - wpa_printf(MSG_DEBUG, "WPA: Found the current AP from " - "updated scan results"); + wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: Found the current AP from " + "updated scan results"); } } @@ -1034,7 +1028,7 @@ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, if (kde) os_memcpy(reply + 1, kde, kde_len); - wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4"); wpa_eapol_key_send(sm, ptk->kck, ver, dst, ETH_P_EAPOL, rbuf, rlen, reply->key_mic); @@ -1051,29 +1045,32 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, struct wpa_eapol_ie_parse ie; wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE); - wpa_printf(MSG_DEBUG, "WPA: RX message 3 of 4-Way Handshake from " - MACSTR " (ver=%d)", MAC2STR(sm->bssid), ver); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 3 of 4-Way " + "Handshake from " MACSTR " (ver=%d)", MAC2STR(sm->bssid), ver); key_info = WPA_GET_BE16(key->key_info); pos = (const u8 *) (key + 1); len = WPA_GET_BE16(key->key_data_length); wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", pos, len); - wpa_supplicant_parse_ies(pos, len, &ie); + if (wpa_supplicant_parse_ies(pos, len, &ie) < 0) + goto failed; if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { - wpa_printf(MSG_WARNING, "WPA: GTK IE in unencrypted key data"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: GTK IE in unencrypted key data"); goto failed; } #ifdef CONFIG_IEEE80211W if (ie.igtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { - wpa_printf(MSG_WARNING, "WPA: IGTK KDE in unencrypted key " - "data"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: IGTK KDE in unencrypted key data"); goto failed; } if (ie.igtk && ie.igtk_len != sizeof(struct wpa_igtk_kde)) { - wpa_printf(MSG_WARNING, "WPA: Invalid IGTK KDE length %lu", - (unsigned long) ie.igtk_len); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Invalid IGTK KDE length %lu", + (unsigned long) ie.igtk_len); goto failed; } #endif /* CONFIG_IEEE80211W */ @@ -1082,30 +1079,20 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, goto failed; if (os_memcmp(sm->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) { - wpa_printf(MSG_WARNING, "WPA: ANonce from message 1 of 4-Way " - "Handshake differs from 3 of 4-Way Handshake - drop" - " packet (src=" MACSTR ")", MAC2STR(sm->bssid)); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: ANonce from message 1 of 4-Way Handshake " + "differs from 3 of 4-Way Handshake - drop packet (src=" + MACSTR ")", MAC2STR(sm->bssid)); goto failed; } keylen = WPA_GET_BE16(key->key_length); - switch (sm->pairwise_cipher) { - case WPA_CIPHER_CCMP: - if (keylen != 16) { - wpa_printf(MSG_WARNING, "WPA: Invalid CCMP key length " - "%d (src=" MACSTR ")", - keylen, MAC2STR(sm->bssid)); - goto failed; - } - break; - case WPA_CIPHER_TKIP: - if (keylen != 32) { - wpa_printf(MSG_WARNING, "WPA: Invalid TKIP key length " - "%d (src=" MACSTR ")", - keylen, MAC2STR(sm->bssid)); - goto failed; - } - break; + if (keylen != wpa_cipher_key_len(sm->pairwise_cipher)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Invalid %s key length %d (src=" MACSTR + ")", wpa_cipher_txt(sm->pairwise_cipher), keylen, + MAC2STR(sm->bssid)); + goto failed; } if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info, @@ -1134,15 +1121,19 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, if (ie.gtk && wpa_supplicant_pairwise_gtk(sm, key, ie.gtk, ie.gtk_len, key_info) < 0) { - wpa_printf(MSG_INFO, "RSN: Failed to configure GTK"); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: Failed to configure GTK"); goto failed; } if (ieee80211w_set_keys(sm, &ie) < 0) { - wpa_printf(MSG_INFO, "RSN: Failed to configure IGTK"); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: Failed to configure IGTK"); goto failed; } + wpa_sm_set_rekey_offload(sm); + return; failed: @@ -1160,18 +1151,21 @@ static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm, struct wpa_eapol_ie_parse ie; wpa_hexdump(MSG_DEBUG, "RSN: msg 1/2 key data", keydata, keydatalen); - wpa_supplicant_parse_ies(keydata, keydatalen, &ie); + if (wpa_supplicant_parse_ies(keydata, keydatalen, &ie) < 0) + return -1; if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { - wpa_printf(MSG_WARNING, "WPA: GTK IE in unencrypted key data"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: GTK IE in unencrypted key data"); return -1; } if (ie.gtk == NULL) { - wpa_printf(MSG_INFO, "WPA: No GTK IE in Group Key msg 1/2"); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: No GTK IE in Group Key msg 1/2"); return -1; } maxkeylen = gd->gtk_len = ie.gtk_len - 2; - if (wpa_supplicant_check_group_cipher(sm->group_cipher, + if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, gd->gtk_len, maxkeylen, &gd->key_rsc_len, &gd->alg)) return -1; @@ -1182,14 +1176,16 @@ static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm, gd->tx = wpa_supplicant_gtk_tx_bit_workaround(sm, !!(ie.gtk[0] & BIT(2))); if (ie.gtk_len - 2 > sizeof(gd->gtk)) { - wpa_printf(MSG_INFO, "RSN: Too long GTK in GTK IE " - "(len=%lu)", (unsigned long) ie.gtk_len - 2); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: Too long GTK in GTK IE (len=%lu)", + (unsigned long) ie.gtk_len - 2); return -1; } os_memcpy(gd->gtk, ie.gtk + 2, ie.gtk_len - 2); if (ieee80211w_set_keys(sm, &ie) < 0) - wpa_printf(MSG_INFO, "RSN: Failed to configure IGTK"); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: Failed to configure IGTK"); return 0; } @@ -1207,22 +1203,23 @@ static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm, gd->gtk_len = WPA_GET_BE16(key->key_length); maxkeylen = keydatalen; if (keydatalen > extra_len) { - wpa_printf(MSG_INFO, "WPA: Truncated EAPOL-Key packet:" - " key_data_length=%lu > extra_len=%lu", - (unsigned long) keydatalen, - (unsigned long) extra_len); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Truncated EAPOL-Key packet: " + "key_data_length=%lu > extra_len=%lu", + (unsigned long) keydatalen, (unsigned long) extra_len); return -1; } if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { if (maxkeylen < 8) { - wpa_printf(MSG_INFO, "WPA: Too short maxkeylen (%lu)", - (unsigned long) maxkeylen); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Too short maxkeylen (%lu)", + (unsigned long) maxkeylen); return -1; } maxkeylen -= 8; } - if (wpa_supplicant_check_group_cipher(sm->group_cipher, + if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, gd->gtk_len, maxkeylen, &gd->key_rsc_len, &gd->alg)) return -1; @@ -1233,38 +1230,42 @@ static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm, os_memcpy(ek, key->key_iv, 16); os_memcpy(ek + 16, sm->ptk.kek, 16); if (keydatalen > sizeof(gd->gtk)) { - wpa_printf(MSG_WARNING, "WPA: RC4 key data " - "too long (%lu)", - (unsigned long) keydatalen); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: RC4 key data too long (%lu)", + (unsigned long) keydatalen); return -1; } os_memcpy(gd->gtk, key + 1, keydatalen); if (rc4_skip(ek, 32, 256, gd->gtk, keydatalen)) { - wpa_printf(MSG_ERROR, "WPA: RC4 failed"); + wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, + "WPA: RC4 failed"); return -1; } } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { if (keydatalen % 8) { - wpa_printf(MSG_WARNING, "WPA: Unsupported AES-WRAP " - "len %lu", (unsigned long) keydatalen); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Unsupported AES-WRAP len %lu", + (unsigned long) keydatalen); return -1; } if (maxkeylen > sizeof(gd->gtk)) { - wpa_printf(MSG_WARNING, "WPA: AES-WRAP key data " - "too long (keydatalen=%lu maxkeylen=%lu)", - (unsigned long) keydatalen, - (unsigned long) maxkeylen); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: AES-WRAP key data " + "too long (keydatalen=%lu maxkeylen=%lu)", + (unsigned long) keydatalen, + (unsigned long) maxkeylen); return -1; } if (aes_unwrap(sm->ptk.kek, maxkeylen / 8, (const u8 *) (key + 1), gd->gtk)) { - wpa_printf(MSG_WARNING, "WPA: AES unwrap " - "failed - could not decrypt GTK"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: AES unwrap failed - could not decrypt " + "GTK"); return -1; } } else { - wpa_printf(MSG_WARNING, "WPA: Unsupported key_info type %d", - ver); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Unsupported key_info type %d", ver); return -1; } gd->tx = wpa_supplicant_gtk_tx_bit_workaround( @@ -1300,7 +1301,7 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm, WPA_PUT_BE16(reply->key_data_length, 0); - wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2"); wpa_eapol_key_send(sm, sm->ptk.kck, ver, sm->bssid, ETH_P_EAPOL, rbuf, rlen, reply->key_mic); @@ -1320,8 +1321,8 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, os_memset(&gd, 0, sizeof(gd)); rekey = wpa_sm_get_state(sm) == WPA_COMPLETED; - wpa_printf(MSG_DEBUG, "WPA: RX message 1 of Group Key Handshake from " - MACSTR " (ver=%d)", MAC2STR(src_addr), ver); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 1 of Group Key " + "Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver); key_info = WPA_GET_BE16(key->key_info); keydatalen = WPA_GET_BE16(key->key_data_length); @@ -1352,6 +1353,8 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher)); wpa_sm_cancel_auth_timeout(sm); wpa_sm_set_state(sm, WPA_COMPLETED); + + wpa_sm_set_rekey_offload(sm); } else { wpa_supplicant_key_neg_complete(sm, sm->bssid, key_info & @@ -1378,8 +1381,9 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, wpa_eapol_key_mic(sm->tptk.kck, ver, buf, len, key->key_mic); if (os_memcmp(mic, key->key_mic, 16) != 0) { - wpa_printf(MSG_WARNING, "WPA: Invalid EAPOL-Key MIC " - "when using TPTK - ignoring TPTK"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Invalid EAPOL-Key MIC " + "when using TPTK - ignoring TPTK"); } else { ok = 1; sm->tptk_set = 0; @@ -1393,16 +1397,18 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, wpa_eapol_key_mic(sm->ptk.kck, ver, buf, len, key->key_mic); if (os_memcmp(mic, key->key_mic, 16) != 0) { - wpa_printf(MSG_WARNING, "WPA: Invalid EAPOL-Key MIC " - "- dropping packet"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Invalid EAPOL-Key MIC - " + "dropping packet"); return -1; } ok = 1; } if (!ok) { - wpa_printf(MSG_WARNING, "WPA: Could not verify EAPOL-Key MIC " - "- dropping packet"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Could not verify EAPOL-Key MIC - " + "dropping packet"); return -1; } @@ -1422,8 +1428,9 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data", (u8 *) (key + 1), keydatalen); if (!sm->ptk_set) { - wpa_printf(MSG_WARNING, "WPA: PTK not available, " - "cannot decrypt EAPOL-Key key data."); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: PTK not available, cannot decrypt EAPOL-Key Key " + "Data"); return -1; } @@ -1434,37 +1441,40 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, os_memcpy(ek, key->key_iv, 16); os_memcpy(ek + 16, sm->ptk.kek, 16); if (rc4_skip(ek, 32, 256, (u8 *) (key + 1), keydatalen)) { - wpa_printf(MSG_ERROR, "WPA: RC4 failed"); + wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, + "WPA: RC4 failed"); return -1; } } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || ver == WPA_KEY_INFO_TYPE_AES_128_CMAC) { u8 *buf; if (keydatalen % 8) { - wpa_printf(MSG_WARNING, "WPA: Unsupported " - "AES-WRAP len %d", keydatalen); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Unsupported AES-WRAP len %d", + keydatalen); return -1; } keydatalen -= 8; /* AES-WRAP adds 8 bytes */ buf = os_malloc(keydatalen); if (buf == NULL) { - wpa_printf(MSG_WARNING, "WPA: No memory for " - "AES-UNWRAP buffer"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: No memory for AES-UNWRAP buffer"); return -1; } if (aes_unwrap(sm->ptk.kek, keydatalen / 8, (u8 *) (key + 1), buf)) { os_free(buf); - wpa_printf(MSG_WARNING, "WPA: AES unwrap failed - " - "could not decrypt EAPOL-Key key data"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: AES unwrap failed - " + "could not decrypt EAPOL-Key key data"); return -1; } os_memcpy(key + 1, buf, keydatalen); os_free(buf); WPA_PUT_BE16(key->key_data_length, keydatalen); } else { - wpa_printf(MSG_WARNING, "WPA: Unsupported key_info type %d", - ver); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Unsupported key_info type %d", ver); return -1; } wpa_hexdump_key(MSG_DEBUG, "WPA: decrypted EAPOL-Key key data", @@ -1480,35 +1490,38 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, void wpa_sm_aborted_cached(struct wpa_sm *sm) { if (sm && sm->cur_pmksa) { - wpa_printf(MSG_DEBUG, "RSN: Cancelling PMKSA caching attempt"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Cancelling PMKSA caching attempt"); sm->cur_pmksa = NULL; } } -static void wpa_eapol_key_dump(const struct wpa_eapol_key *key) +static void wpa_eapol_key_dump(struct wpa_sm *sm, + const struct wpa_eapol_key *key) { #ifndef CONFIG_NO_STDOUT_DEBUG u16 key_info = WPA_GET_BE16(key->key_info); - wpa_printf(MSG_DEBUG, " EAPOL-Key type=%d", key->type); - wpa_printf(MSG_DEBUG, " key_info 0x%x (ver=%d keyidx=%d rsvd=%d %s" - "%s%s%s%s%s%s%s)", - key_info, key_info & WPA_KEY_INFO_TYPE_MASK, - (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >> - WPA_KEY_INFO_KEY_INDEX_SHIFT, - (key_info & (BIT(13) | BIT(14) | BIT(15))) >> 13, - key_info & WPA_KEY_INFO_KEY_TYPE ? "Pairwise" : "Group", - key_info & WPA_KEY_INFO_INSTALL ? " Install" : "", - key_info & WPA_KEY_INFO_ACK ? " Ack" : "", - key_info & WPA_KEY_INFO_MIC ? " MIC" : "", - key_info & WPA_KEY_INFO_SECURE ? " Secure" : "", - key_info & WPA_KEY_INFO_ERROR ? " Error" : "", - key_info & WPA_KEY_INFO_REQUEST ? " Request" : "", - key_info & WPA_KEY_INFO_ENCR_KEY_DATA ? " Encr" : ""); - wpa_printf(MSG_DEBUG, " key_length=%u key_data_length=%u", - WPA_GET_BE16(key->key_length), - WPA_GET_BE16(key->key_data_length)); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, " EAPOL-Key type=%d", key->type); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + " key_info 0x%x (ver=%d keyidx=%d rsvd=%d %s%s%s%s%s%s%s%s)", + key_info, key_info & WPA_KEY_INFO_TYPE_MASK, + (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >> + WPA_KEY_INFO_KEY_INDEX_SHIFT, + (key_info & (BIT(13) | BIT(14) | BIT(15))) >> 13, + key_info & WPA_KEY_INFO_KEY_TYPE ? "Pairwise" : "Group", + key_info & WPA_KEY_INFO_INSTALL ? " Install" : "", + key_info & WPA_KEY_INFO_ACK ? " Ack" : "", + key_info & WPA_KEY_INFO_MIC ? " MIC" : "", + key_info & WPA_KEY_INFO_SECURE ? " Secure" : "", + key_info & WPA_KEY_INFO_ERROR ? " Error" : "", + key_info & WPA_KEY_INFO_REQUEST ? " Request" : "", + key_info & WPA_KEY_INFO_ENCR_KEY_DATA ? " Encr" : ""); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + " key_length=%u key_data_length=%u", + WPA_GET_BE16(key->key_length), + WPA_GET_BE16(key->key_data_length)); wpa_hexdump(MSG_DEBUG, " replay_counter", key->replay_counter, WPA_REPLAY_COUNTER_LEN); wpa_hexdump(MSG_DEBUG, " key_nonce", key->key_nonce, WPA_NONCE_LEN); @@ -1552,10 +1565,11 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, #endif /* CONFIG_IEEE80211R */ if (len < sizeof(*hdr) + sizeof(*key)) { - wpa_printf(MSG_DEBUG, "WPA: EAPOL frame too short to be a WPA " - "EAPOL-Key (len %lu, expecting at least %lu)", - (unsigned long) len, - (unsigned long) sizeof(*hdr) + sizeof(*key)); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: EAPOL frame too short to be a WPA " + "EAPOL-Key (len %lu, expecting at least %lu)", + (unsigned long) len, + (unsigned long) sizeof(*hdr) + sizeof(*key)); return 0; } @@ -1568,40 +1582,45 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, key = (struct wpa_eapol_key *) (hdr + 1); plen = be_to_host16(hdr->length); data_len = plen + sizeof(*hdr); - wpa_printf(MSG_DEBUG, "IEEE 802.1X RX: version=%d type=%d length=%lu", - hdr->version, hdr->type, (unsigned long) plen); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "IEEE 802.1X RX: version=%d type=%d length=%lu", + hdr->version, hdr->type, (unsigned long) plen); if (hdr->version < EAPOL_VERSION) { /* TODO: backwards compatibility */ } if (hdr->type != IEEE802_1X_TYPE_EAPOL_KEY) { - wpa_printf(MSG_DEBUG, "WPA: EAPOL frame (type %u) discarded, " + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: EAPOL frame (type %u) discarded, " "not a Key frame", hdr->type); ret = 0; goto out; } if (plen > len - sizeof(*hdr) || plen < sizeof(*key)) { - wpa_printf(MSG_DEBUG, "WPA: EAPOL frame payload size %lu " - "invalid (frame size %lu)", - (unsigned long) plen, (unsigned long) len); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: EAPOL frame payload size %lu " + "invalid (frame size %lu)", + (unsigned long) plen, (unsigned long) len); ret = 0; goto out; } if (key->type != EAPOL_KEY_TYPE_WPA && key->type != EAPOL_KEY_TYPE_RSN) { - wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key type (%d) unknown, " - "discarded", key->type); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: EAPOL-Key type (%d) unknown, discarded", + key->type); ret = 0; goto out; } - wpa_eapol_key_dump(key); + wpa_eapol_key_dump(sm, key); eapol_sm_notify_lower_layer_success(sm->eapol, 0); wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", tmp, len); if (data_len < len) { - wpa_printf(MSG_DEBUG, "WPA: ignoring %lu bytes after the IEEE " - "802.1X data", (unsigned long) len - data_len); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: ignoring %lu bytes after the IEEE 802.1X data", + (unsigned long) len - data_len); } key_info = WPA_GET_BE16(key->key_info); ver = key_info & WPA_KEY_INFO_TYPE_MASK; @@ -1610,8 +1629,9 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, ver != WPA_KEY_INFO_TYPE_AES_128_CMAC && #endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { - wpa_printf(MSG_INFO, "WPA: Unsupported EAPOL-Key descriptor " - "version %d.", ver); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Unsupported EAPOL-Key descriptor version %d", + ver); goto out; } @@ -1619,8 +1639,8 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, if (wpa_key_mgmt_ft(sm->key_mgmt)) { /* IEEE 802.11r uses a new key_info type (AES-128-CMAC). */ if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { - wpa_printf(MSG_INFO, "FT: AP did not use " - "AES-128-CMAC."); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "FT: AP did not use AES-128-CMAC"); goto out; } } else @@ -1628,28 +1648,37 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, #ifdef CONFIG_IEEE80211W if (wpa_key_mgmt_sha256(sm->key_mgmt)) { if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { - wpa_printf(MSG_INFO, "WPA: AP did not use the " - "negotiated AES-128-CMAC."); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: AP did not use the " + "negotiated AES-128-CMAC"); goto out; } } else #endif /* CONFIG_IEEE80211W */ if (sm->pairwise_cipher == WPA_CIPHER_CCMP && ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { - wpa_printf(MSG_INFO, "WPA: CCMP is used, but EAPOL-Key " - "descriptor version (%d) is not 2.", ver); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: CCMP is used, but EAPOL-Key " + "descriptor version (%d) is not 2", ver); if (sm->group_cipher != WPA_CIPHER_CCMP && !(key_info & WPA_KEY_INFO_KEY_TYPE)) { /* Earlier versions of IEEE 802.11i did not explicitly * require version 2 descriptor for all EAPOL-Key * packets, so allow group keys to use version 1 if * CCMP is not used for them. */ - wpa_printf(MSG_INFO, "WPA: Backwards compatibility: " - "allow invalid version for non-CCMP group " - "keys"); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Backwards compatibility: allow invalid " + "version for non-CCMP group keys"); } else goto out; } + if (sm->pairwise_cipher == WPA_CIPHER_GCMP && + ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: GCMP is used, but EAPOL-Key " + "descriptor version (%d) is not 2", ver); + goto out; + } #ifdef CONFIG_PEERKEY for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { @@ -1661,9 +1690,9 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, if (!peerkey->initiator && peerkey->replay_counter_set && os_memcmp(key->replay_counter, peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN) <= 0) { - wpa_printf(MSG_WARNING, "RSN: EAPOL-Key Replay " - "Counter did not increase (STK) - dropping " - "packet"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "RSN: EAPOL-Key Replay Counter did not " + "increase (STK) - dropping packet"); goto out; } else if (peerkey->initiator) { u8 _tmp[WPA_REPLAY_COUNTER_LEN]; @@ -1672,16 +1701,18 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, inc_byte_array(_tmp, WPA_REPLAY_COUNTER_LEN); if (os_memcmp(_tmp, peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN) != 0) { - wpa_printf(MSG_DEBUG, "RSN: EAPOL-Key Replay " - "Counter did not match (STK) - " - "dropping packet"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: EAPOL-Key Replay " + "Counter did not match (STK) - " + "dropping packet"); goto out; } } } if (peerkey && peerkey->initiator && (key_info & WPA_KEY_INFO_ACK)) { - wpa_printf(MSG_INFO, "RSN: Ack bit in key_info from STK peer"); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: Ack bit in key_info from STK peer"); goto out; } #endif /* CONFIG_PEERKEY */ @@ -1689,8 +1720,9 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, if (!peerkey && sm->rx_replay_counter_set && os_memcmp(key->replay_counter, sm->rx_replay_counter, WPA_REPLAY_COUNTER_LEN) <= 0) { - wpa_printf(MSG_WARNING, "WPA: EAPOL-Key Replay Counter did not" - " increase - dropping packet"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: EAPOL-Key Replay Counter did not increase - " + "dropping packet"); goto out; } @@ -1699,13 +1731,14 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, && (peerkey == NULL || !peerkey->initiator) #endif /* CONFIG_PEERKEY */ ) { - wpa_printf(MSG_INFO, "WPA: No Ack bit in key_info"); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: No Ack bit in key_info"); goto out; } if (key_info & WPA_KEY_INFO_REQUEST) { - wpa_printf(MSG_INFO, "WPA: EAPOL-Key with Request bit - " - "dropped"); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: EAPOL-Key with Request bit - dropped"); goto out; } @@ -1739,8 +1772,9 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, if (key_info & WPA_KEY_INFO_KEY_TYPE) { if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) { - wpa_printf(MSG_WARNING, "WPA: Ignored EAPOL-Key " - "(Pairwise) with non-zero key index"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Ignored EAPOL-Key (Pairwise) with " + "non-zero key index"); goto out; } if (peerkey) { @@ -1764,8 +1798,9 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, wpa_supplicant_process_1_of_2(sm, src_addr, key, extra_len, ver); } else { - wpa_printf(MSG_WARNING, "WPA: EAPOL-Key (Group) " - "without Mic bit - dropped"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: EAPOL-Key (Group) without Mic bit - " + "dropped"); } } @@ -1778,23 +1813,6 @@ out: #ifdef CONFIG_CTRL_IFACE -static int wpa_cipher_bits(int cipher) -{ - switch (cipher) { - case WPA_CIPHER_CCMP: - return 128; - case WPA_CIPHER_TKIP: - return 256; - case WPA_CIPHER_WEP104: - return 104; - case WPA_CIPHER_WEP40: - return 40; - default: - return 0; - } -} - - static u32 wpa_key_mgmt_suite(struct wpa_sm *sm) { switch (sm->key_mgmt) { @@ -1818,6 +1836,10 @@ static u32 wpa_key_mgmt_suite(struct wpa_sm *sm) case WPA_KEY_MGMT_PSK_SHA256: return RSN_AUTH_KEY_MGMT_PSK_SHA256; #endif /* CONFIG_IEEE80211W */ + case WPA_KEY_MGMT_CCKM: + return (sm->proto == WPA_PROTO_RSN ? + RSN_AUTH_KEY_MGMT_CCKM: + WPA_AUTH_KEY_MGMT_CCKM); case WPA_KEY_MGMT_WPA_NONE: return WPA_AUTH_KEY_MGMT_NONE; default: @@ -1826,30 +1848,6 @@ static u32 wpa_key_mgmt_suite(struct wpa_sm *sm) } -static u32 wpa_cipher_suite(struct wpa_sm *sm, int cipher) -{ - switch (cipher) { - case WPA_CIPHER_CCMP: - return (sm->proto == WPA_PROTO_RSN ? - RSN_CIPHER_SUITE_CCMP : WPA_CIPHER_SUITE_CCMP); - case WPA_CIPHER_TKIP: - return (sm->proto == WPA_PROTO_RSN ? - RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP); - case WPA_CIPHER_WEP104: - return (sm->proto == WPA_PROTO_RSN ? - RSN_CIPHER_SUITE_WEP104 : WPA_CIPHER_SUITE_WEP104); - case WPA_CIPHER_WEP40: - return (sm->proto == WPA_PROTO_RSN ? - RSN_CIPHER_SUITE_WEP40 : WPA_CIPHER_SUITE_WEP40); - case WPA_CIPHER_NONE: - return (sm->proto == WPA_PROTO_RSN ? - RSN_CIPHER_SUITE_NONE : WPA_CIPHER_SUITE_NONE); - default: - return 0; - } -} - - #define RSN_SUITE "%02x-%02x-%02x-%d" #define RSN_SUITE_ARG(s) \ ((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff @@ -1897,7 +1895,7 @@ int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen) rsna ? "TRUE" : "FALSE", rsna ? "TRUE" : "FALSE", RSN_VERSION, - wpa_cipher_bits(sm->group_cipher), + wpa_cipher_key_len(sm->group_cipher) * 8, sm->dot11RSNAConfigPMKLifetime, sm->dot11RSNAConfigPMKReauthThreshold, sm->dot11RSNAConfigSATimeout); @@ -1917,12 +1915,16 @@ int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen) "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n" "dot11RSNA4WayHandshakeFailures=%u\n", RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)), - RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->pairwise_cipher)), - RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->group_cipher)), + RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, + sm->pairwise_cipher)), + RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, + sm->group_cipher)), pmkid_txt, RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)), - RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->pairwise_cipher)), - RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->group_cipher)), + RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, + sm->pairwise_cipher)), + RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, + sm->group_cipher)), sm->dot11RSNA4WayHandshakeFailures); if (ret >= 0 && (size_t) ret < buflen) len += ret; @@ -1933,24 +1935,40 @@ int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen) static void wpa_sm_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, - void *ctx, int replace) + void *ctx, enum pmksa_free_reason reason) { struct wpa_sm *sm = ctx; + int deauth = 0; + + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA cache entry free_cb: " + MACSTR " reason=%d", MAC2STR(entry->aa), reason); + + if (sm->cur_pmksa == entry) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: %s current PMKSA entry", + reason == PMKSA_REPLACE ? "replaced" : "removed"); + pmksa_cache_clear_current(sm); + + /* + * If an entry is simply being replaced, there's no need to + * deauthenticate because it will be immediately re-added. + * This happens when EAP authentication is completed again + * (reauth or failed PMKSA caching attempt). + */ + if (reason != PMKSA_REPLACE) + deauth = 1; + } - if (sm->cur_pmksa == entry || + if (reason == PMKSA_EXPIRE && (sm->pmk_len == entry->pmk_len && os_memcmp(sm->pmk, entry->pmk, sm->pmk_len) == 0)) { - wpa_printf(MSG_DEBUG, "RSN: removed current PMKSA entry"); - sm->cur_pmksa = NULL; - - if (replace) { - /* A new entry is being added, so no need to - * deauthenticate in this case. This happens when EAP - * authentication is completed again (reauth or failed - * PMKSA caching attempt). */ - return; - } + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: deauthenticating due to expired PMK"); + pmksa_cache_clear_current(sm); + deauth = 1; + } + if (deauth) { os_memset(sm->pmk, 0, sizeof(sm->pmk)); wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); } @@ -1982,8 +2000,8 @@ struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx) sm->pmksa = pmksa_cache_init(wpa_sm_pmksa_free_cb, sm, sm); if (sm->pmksa == NULL) { - wpa_printf(MSG_ERROR, "RSN: PMKSA cache initialization " - "failed"); + wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, + "RSN: PMKSA cache initialization failed"); os_free(sm); return NULL; } @@ -2030,7 +2048,8 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) if (sm == NULL) return; - wpa_printf(MSG_DEBUG, "WPA: Association event - clear replay counter"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: Association event - clear replay counter"); os_memcpy(sm->bssid, bssid, ETH_ALEN); os_memset(sm->rx_replay_counter, 0, WPA_REPLAY_COUNTER_LEN); sm->rx_replay_counter_set = 0; @@ -2059,10 +2078,14 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) * IEEE 802.11, 8.4.10: Delete PTK SA on (re)association if * this is not part of a Fast BSS Transition. */ - wpa_printf(MSG_DEBUG, "WPA: Clear old PTK"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PTK"); sm->ptk_set = 0; sm->tptk_set = 0; } + +#ifdef CONFIG_TDLS + wpa_tdls_assoc(sm); +#endif /* CONFIG_TDLS */ } @@ -2076,8 +2099,12 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) void wpa_sm_notify_disassoc(struct wpa_sm *sm) { rsn_preauth_deinit(sm); + pmksa_cache_clear_current(sm); if (wpa_sm_get_state(sm) == WPA_4WAY_HANDSHAKE) sm->dot11RSNA4WayHandshakeFailures++; +#ifdef CONFIG_TDLS + wpa_tdls_disassoc(sm); +#endif /* CONFIG_TDLS */ } @@ -2191,8 +2218,6 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) sm->ssid_len = 0; sm->wpa_ptk_rekey = 0; } - if (config == NULL || config->network_ctx != sm->network_ctx) - pmksa_cache_notify_reconfig(sm->pmksa); } @@ -2367,6 +2392,22 @@ int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; + + if (sm->mfp != NO_MGMT_FRAME_PROTECTION && sm->ap_rsn_ie) { + struct wpa_ie_data rsn; + if (wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &rsn) + >= 0 && + rsn.capabilities & (WPA_CAPABILITY_MFPR | + WPA_CAPABILITY_MFPC)) { + ret = os_snprintf(pos, end - pos, "pmf=%d\n", + (rsn.capabilities & + WPA_CAPABILITY_MFPR) ? 2 : 1); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + } + return pos - buf; } @@ -2430,7 +2471,8 @@ int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len) os_free(sm->assoc_wpa_ie); if (ie == NULL || len == 0) { - wpa_printf(MSG_DEBUG, "WPA: clearing own WPA/RSN IE"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: clearing own WPA/RSN IE"); sm->assoc_wpa_ie = NULL; sm->assoc_wpa_ie_len = 0; } else { @@ -2464,7 +2506,8 @@ int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len) os_free(sm->ap_wpa_ie); if (ie == NULL || len == 0) { - wpa_printf(MSG_DEBUG, "WPA: clearing AP WPA IE"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: clearing AP WPA IE"); sm->ap_wpa_ie = NULL; sm->ap_wpa_ie_len = 0; } else { @@ -2498,7 +2541,8 @@ int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len) os_free(sm->ap_rsn_ie); if (ie == NULL || len == 0) { - wpa_printf(MSG_DEBUG, "WPA: clearing AP RSN IE"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: clearing AP RSN IE"); sm->ap_rsn_ie = NULL; sm->ap_rsn_ie_len = 0; } else { @@ -2526,9 +2570,12 @@ int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len) */ int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data) { - if (sm == NULL || sm->assoc_wpa_ie == NULL) { - wpa_printf(MSG_DEBUG, "WPA: No WPA/RSN IE available from " - "association info"); + if (sm == NULL) + return -1; + + if (sm->assoc_wpa_ie == NULL) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: No WPA/RSN IE available from association info"); return -1; } if (wpa_parse_wpa_ie(sm->assoc_wpa_ie, sm->assoc_wpa_ie_len, data)) @@ -2549,7 +2596,7 @@ int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len) void wpa_sm_drop_sa(struct wpa_sm *sm) { - wpa_printf(MSG_DEBUG, "WPA: Clear old PMK and PTK"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PMK and PTK"); sm->ptk_set = 0; sm->tptk_set = 0; os_memset(sm->pmk, 0, sizeof(sm->pmk)); @@ -2564,3 +2611,92 @@ int wpa_sm_has_ptk(struct wpa_sm *sm) return 0; return sm->ptk_set; } + + +void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr) +{ + os_memcpy(sm->rx_replay_counter, replay_ctr, WPA_REPLAY_COUNTER_LEN); +} + + +void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx) +{ +#ifndef CONFIG_NO_WPA2 + pmksa_cache_flush(sm->pmksa, network_ctx); +#endif /* CONFIG_NO_WPA2 */ +} + + +#ifdef CONFIG_WNM +int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf) +{ + struct wpa_gtk_data gd; +#ifdef CONFIG_IEEE80211W + struct wpa_igtk_kde igd; + u16 keyidx; +#endif /* CONFIG_IEEE80211W */ + u16 keyinfo; + u8 keylen; /* plaintext key len */ + u8 *key_rsc; + + os_memset(&gd, 0, sizeof(gd)); +#ifdef CONFIG_IEEE80211W + os_memset(&igd, 0, sizeof(igd)); +#endif /* CONFIG_IEEE80211W */ + + keylen = wpa_cipher_key_len(sm->group_cipher); + gd.key_rsc_len = wpa_cipher_rsc_len(sm->group_cipher); + gd.alg = wpa_cipher_to_alg(sm->group_cipher); + if (gd.alg == WPA_ALG_NONE) { + wpa_printf(MSG_DEBUG, "Unsupported group cipher suite"); + return -1; + } + + if (subelem_id == WNM_SLEEP_SUBELEM_GTK) { + key_rsc = buf + 5; + keyinfo = WPA_GET_LE16(buf + 2); + gd.gtk_len = keylen; + if (gd.gtk_len != buf[4]) { + wpa_printf(MSG_DEBUG, "GTK len mismatch len %d vs %d", + gd.gtk_len, buf[4]); + return -1; + } + gd.keyidx = keyinfo & 0x03; /* B0 - B1 */ + gd.tx = wpa_supplicant_gtk_tx_bit_workaround( + sm, !!(keyinfo & WPA_KEY_INFO_TXRX)); + + os_memcpy(gd.gtk, buf + 13, gd.gtk_len); + + wpa_hexdump_key(MSG_DEBUG, "Install GTK (WNM SLEEP)", + gd.gtk, gd.gtk_len); + if (wpa_supplicant_install_gtk(sm, &gd, key_rsc)) { + wpa_printf(MSG_DEBUG, "Failed to install the GTK in " + "WNM mode"); + return -1; + } +#ifdef CONFIG_IEEE80211W + } else if (subelem_id == WNM_SLEEP_SUBELEM_IGTK) { + os_memcpy(igd.keyid, buf + 2, 2); + os_memcpy(igd.pn, buf + 4, 6); + + keyidx = WPA_GET_LE16(igd.keyid); + os_memcpy(igd.igtk, buf + 10, WPA_IGTK_LEN); + + wpa_hexdump_key(MSG_DEBUG, "Install IGTK (WNM SLEEP)", + igd.igtk, WPA_IGTK_LEN); + if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr, + keyidx, 0, igd.pn, sizeof(igd.pn), + igd.igtk, WPA_IGTK_LEN) < 0) { + wpa_printf(MSG_DEBUG, "Failed to install the IGTK in " + "WNM mode"); + return -1; + } +#endif /* CONFIG_IEEE80211W */ + } else { + wpa_printf(MSG_DEBUG, "Unknown element id"); + return -1; + } + + return 0; +} +#endif /* CONFIG_WNM */ diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index f1a5554133ce7..791974c2b3036 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -2,14 +2,8 @@ * wpa_supplicant - WPA definitions * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_H @@ -30,7 +24,6 @@ struct wpa_sm_ctx { void (*set_state)(void *ctx, enum wpa_states state); enum wpa_states (*get_state)(void *ctx); void (*deauthenticate)(void * ctx, int reason_code); - void (*disassociate)(void *ctx, int reason_code); int (*set_key)(void *ctx, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, @@ -55,6 +48,19 @@ struct wpa_sm_ctx { int (*send_ft_action)(void *ctx, u8 action, const u8 *target_ap, const u8 *ies, size_t ies_len); int (*mark_authenticated)(void *ctx, const u8 *target_ap); +#ifdef CONFIG_TDLS + int (*tdls_get_capa)(void *ctx, int *tdls_supported, + int *tdls_ext_setup); + int (*send_tdls_mgmt)(void *ctx, const u8 *dst, + u8 action_code, u8 dialog_token, + u16 status_code, const u8 *buf, size_t len); + int (*tdls_oper)(void *ctx, int oper, const u8 *peer); + int (*tdls_peer_addset)(void *ctx, const u8 *addr, int add, + u16 capability, const u8 *supp_rates, + size_t supp_rates_len); +#endif /* CONFIG_TDLS */ + void (*set_rekey_offload)(void *ctx, const u8 *kek, const u8 *kck, + const u8 *replay_ctr); }; @@ -126,6 +132,10 @@ int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len); void wpa_sm_drop_sa(struct wpa_sm *sm); int wpa_sm_has_ptk(struct wpa_sm *sm); +void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr); + +void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx); + #else /* CONFIG_NO_WPA */ static inline struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx) @@ -271,6 +281,16 @@ static inline int wpa_sm_has_ptk(struct wpa_sm *sm) return 0; } +static inline void wpa_sm_update_replay_ctr(struct wpa_sm *sm, + const u8 *replay_ctr) +{ +} + +static inline void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, + void *network_ctx) +{ +} + #endif /* CONFIG_NO_WPA */ #ifdef CONFIG_PEERKEY @@ -330,4 +350,21 @@ wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len, #endif /* CONFIG_IEEE80211R */ + +/* tdls.c */ +void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len); +void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len); +int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr); +int wpa_tdls_reneg(struct wpa_sm *sm, const u8 *addr); +int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, u16 reason_code); +int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code); +int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr); +int wpa_tdls_init(struct wpa_sm *sm); +void wpa_tdls_deinit(struct wpa_sm *sm); +void wpa_tdls_enable(struct wpa_sm *sm, int enabled); +void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr); +int wpa_tdls_is_external_setup(struct wpa_sm *sm); + +int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf); + #endif /* WPA_H */ diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c index 23063bc2d472b..2df060ca87eb7 100644 --- a/src/rsn_supp/wpa_ft.c +++ b/src/rsn_supp/wpa_ft.c @@ -2,53 +2,22 @@ * WPA Supplicant - IEEE 802.11r - Fast BSS Transition * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" #include "crypto/aes_wrap.h" +#include "crypto/random.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "wpa.h" #include "wpa_i.h" -#include "wpa_ie.h" #ifdef CONFIG_IEEE80211R -struct wpa_ft_ies { - const u8 *mdie; - size_t mdie_len; - const u8 *ftie; - size_t ftie_len; - const u8 *r1kh_id; - const u8 *gtk; - size_t gtk_len; - const u8 *r0kh_id; - size_t r0kh_id_len; - const u8 *rsn; - size_t rsn_len; - const u8 *rsn_pmkid; - const u8 *tie; - size_t tie_len; - const u8 *igtk; - size_t igtk_len; - const u8 *ric; - size_t ric_len; -}; - -static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, - struct wpa_ft_ies *parse); - - int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, struct wpa_ptk *ptk, size_t ptk_len) @@ -202,16 +171,16 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, pos = (u8 *) (rsnie + 1); /* Group Suite Selector */ - if (sm->group_cipher == WPA_CIPHER_CCMP) - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); - else if (sm->group_cipher == WPA_CIPHER_TKIP) - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); - else { + if (sm->group_cipher != WPA_CIPHER_CCMP && + sm->group_cipher != WPA_CIPHER_GCMP && + sm->group_cipher != WPA_CIPHER_TKIP) { wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)", sm->group_cipher); os_free(buf); return NULL; } + RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, + sm->group_cipher)); pos += RSN_SELECTOR_LEN; /* Pairwise Suite Count */ @@ -219,16 +188,14 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, pos += 2; /* Pairwise Suite List */ - if (sm->pairwise_cipher == WPA_CIPHER_CCMP) - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); - else if (sm->pairwise_cipher == WPA_CIPHER_TKIP) - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); - else { + if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) { wpa_printf(MSG_WARNING, "FT: Invalid pairwise cipher (%d)", sm->pairwise_cipher); os_free(buf); return NULL; } + RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, + sm->pairwise_cipher)); pos += RSN_SELECTOR_LEN; /* Authenticated Key Management Suite Count */ @@ -346,155 +313,6 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, } -static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len, - struct wpa_ft_ies *parse) -{ - const u8 *end, *pos; - - parse->ftie = ie; - parse->ftie_len = ie_len; - - pos = ie + sizeof(struct rsn_ftie); - end = ie + ie_len; - - while (pos + 2 <= end && pos + 2 + pos[1] <= end) { - switch (pos[0]) { - case FTIE_SUBELEM_R1KH_ID: - if (pos[1] != FT_R1KH_ID_LEN) { - wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID " - "length in FTIE: %d", pos[1]); - return -1; - } - parse->r1kh_id = pos + 2; - break; - case FTIE_SUBELEM_GTK: - parse->gtk = pos + 2; - parse->gtk_len = pos[1]; - break; - case FTIE_SUBELEM_R0KH_ID: - if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) { - wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID " - "length in FTIE: %d", pos[1]); - return -1; - } - parse->r0kh_id = pos + 2; - parse->r0kh_id_len = pos[1]; - break; -#ifdef CONFIG_IEEE80211W - case FTIE_SUBELEM_IGTK: - parse->igtk = pos + 2; - parse->igtk_len = pos[1]; - break; -#endif /* CONFIG_IEEE80211W */ - } - - pos += 2 + pos[1]; - } - - return 0; -} - - -static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, - struct wpa_ft_ies *parse) -{ - const u8 *end, *pos; - struct wpa_ie_data data; - int ret; - const struct rsn_ftie *ftie; - int prot_ie_count = 0; - - os_memset(parse, 0, sizeof(*parse)); - if (ies == NULL) - return 0; - - pos = ies; - end = ies + ies_len; - while (pos + 2 <= end && pos + 2 + pos[1] <= end) { - switch (pos[0]) { - case WLAN_EID_RSN: - parse->rsn = pos + 2; - parse->rsn_len = pos[1]; - ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2, - parse->rsn_len + 2, - &data); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "FT: Failed to parse " - "RSN IE: %d", ret); - return -1; - } - if (data.num_pmkid == 1 && data.pmkid) - parse->rsn_pmkid = data.pmkid; - break; - case WLAN_EID_MOBILITY_DOMAIN: - parse->mdie = pos + 2; - parse->mdie_len = pos[1]; - break; - case WLAN_EID_FAST_BSS_TRANSITION: - if (pos[1] < sizeof(*ftie)) - return -1; - ftie = (const struct rsn_ftie *) (pos + 2); - prot_ie_count = ftie->mic_control[1]; - if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0) - return -1; - break; - case WLAN_EID_TIMEOUT_INTERVAL: - parse->tie = pos + 2; - parse->tie_len = pos[1]; - break; - case WLAN_EID_RIC_DATA: - if (parse->ric == NULL) - parse->ric = pos; - } - - pos += 2 + pos[1]; - } - - if (prot_ie_count == 0) - return 0; /* no MIC */ - - /* - * Check that the protected IE count matches with IEs included in the - * frame. - */ - if (parse->rsn) - prot_ie_count--; - if (parse->mdie) - prot_ie_count--; - if (parse->ftie) - prot_ie_count--; - if (parse->tie) - prot_ie_count--; - if (prot_ie_count < 0) { - wpa_printf(MSG_DEBUG, "FT: Some required IEs not included in " - "the protected IE count"); - return -1; - } - - if (prot_ie_count == 0 && parse->ric) { - wpa_printf(MSG_DEBUG, "FT: RIC IE(s) in the frame, but not " - "included in protected IE count"); - return -1; - } - - /* Determine the end of the RIC IE(s) */ - pos = parse->ric; - while (pos && pos + 2 <= end && pos + 2 + pos[1] <= end && - prot_ie_count) { - prot_ie_count--; - pos += 2 + pos[1]; - } - parse->ric_len = pos - parse->ric; - if (prot_ie_count) { - wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from " - "frame", (int) prot_ie_count); - return -1; - } - - return 0; -} - - static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid) { int keylen; @@ -503,21 +321,15 @@ static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid) wpa_printf(MSG_DEBUG, "FT: Installing PTK to the driver."); - switch (sm->pairwise_cipher) { - case WPA_CIPHER_CCMP: - alg = WPA_ALG_CCMP; - keylen = 16; - break; - case WPA_CIPHER_TKIP: - alg = WPA_ALG_TKIP; - keylen = 32; - break; - default: + if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) { wpa_printf(MSG_WARNING, "FT: Unsupported pairwise cipher %d", sm->pairwise_cipher); return -1; } + alg = wpa_cipher_to_alg(sm->pairwise_cipher); + keylen = wpa_cipher_key_len(sm->pairwise_cipher); + if (wpa_sm_set_key(sm, alg, bssid, 0, 1, null_rsc, sizeof(null_rsc), (u8 *) sm->ptk.tk1, keylen) < 0) { wpa_printf(MSG_WARNING, "FT: Failed to set PTK to the driver"); @@ -540,7 +352,7 @@ int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie) size_t ft_ies_len; /* Generate a new SNonce */ - if (os_get_random(sm->snonce, WPA_NONCE_LEN)) { + if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) { wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce"); return -1; } @@ -663,7 +475,7 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, sm->pmk_r1_name, WPA_PMK_NAME_LEN); bssid = target_ap; - ptk_len = sm->pairwise_cipher == WPA_CIPHER_CCMP ? 48 : 64; + ptk_len = sm->pairwise_cipher != WPA_CIPHER_TKIP ? 48 : 64; wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce, sm->own_addr, bssid, sm->pmk_r1_name, (u8 *) &sm->ptk, ptk_len, ptk_name); @@ -751,28 +563,10 @@ static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem, return -1; } - switch (sm->group_cipher) { - case WPA_CIPHER_CCMP: - keylen = 16; - rsc_len = 6; - alg = WPA_ALG_CCMP; - break; - case WPA_CIPHER_TKIP: - keylen = 32; - rsc_len = 6; - alg = WPA_ALG_TKIP; - break; - case WPA_CIPHER_WEP104: - keylen = 13; - rsc_len = 0; - alg = WPA_ALG_WEP; - break; - case WPA_CIPHER_WEP40: - keylen = 5; - rsc_len = 0; - alg = WPA_ALG_WEP; - break; - default: + keylen = wpa_cipher_key_len(sm->group_cipher); + rsc_len = wpa_cipher_rsc_len(sm->group_cipher); + alg = wpa_cipher_to_alg(sm->group_cipher); + if (alg == WPA_ALG_NONE) { wpa_printf(MSG_WARNING, "WPA: Unsupported Group Cipher %d", sm->group_cipher); return -1; @@ -795,9 +589,8 @@ static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem, } wpa_hexdump_key(MSG_DEBUG, "FT: GTK from Reassoc Resp", gtk, keylen); - if (wpa_sm_set_key(sm, alg, (u8 *) "\xff\xff\xff\xff\xff\xff", - keyidx, 0, gtk_elem + 3, rsc_len, gtk, keylen) < - 0) { + if (wpa_sm_set_key(sm, alg, broadcast_ether_addr, keyidx, 0, + gtk_elem + 3, rsc_len, gtk, keylen) < 0) { wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to the " "driver."); return -1; @@ -848,9 +641,8 @@ static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem, wpa_hexdump_key(MSG_DEBUG, "FT: IGTK from Reassoc Resp", igtk, WPA_IGTK_LEN); - if (wpa_sm_set_key(sm, WPA_ALG_IGTK, (u8 *) "\xff\xff\xff\xff\xff\xff", - keyidx, 0, igtk_elem + 2, 6, igtk, WPA_IGTK_LEN) < - 0) { + if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr, keyidx, 0, + igtk_elem + 2, 6, igtk, WPA_IGTK_LEN) < 0) { wpa_printf(MSG_WARNING, "WPA: Failed to set IGTK to the " "driver."); return -1; @@ -951,8 +743,8 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, } count = 3; - if (parse.tie) - count++; + if (parse.ric) + count += ieee802_11_ie_count(parse.ric, parse.ric_len); if (ftie->mic_control[1] != count) { wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC " "Control: received %u expected %u", @@ -1020,7 +812,7 @@ int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap, MAC2STR(target_ap)); /* Generate a new SNonce */ - if (os_get_random(sm->snonce, WPA_NONCE_LEN)) { + if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) { wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce"); return -1; } diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index 618c09028a450..9f9e641c38771 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -2,14 +2,8 @@ * Internal WPA/RSN supplicant state machine definitions * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_I_H @@ -18,6 +12,7 @@ #include "utils/list.h" struct wpa_peerkey; +struct wpa_tdls_peer; struct wpa_eapol_key; /** @@ -43,6 +38,7 @@ struct wpa_sm { struct l2_packet_data *l2_preauth; struct l2_packet_data *l2_preauth_br; + struct l2_packet_data *l2_tdls; u8 preauth_bssid[ETH_ALEN]; /* current RSN pre-auth peer or * 00:00:00:00:00:00 if no pre-auth is * in progress */ @@ -92,6 +88,20 @@ struct wpa_sm { #ifdef CONFIG_PEERKEY struct wpa_peerkey *peerkey; #endif /* CONFIG_PEERKEY */ +#ifdef CONFIG_TDLS + struct wpa_tdls_peer *tdls; + int tdls_prohibited; + int tdls_disabled; + + /* The driver supports TDLS */ + int tdls_supported; + + /* + * The driver requires explicit discovery/setup/teardown frames sent + * to it via tdls_mgmt. + */ + int tdls_external_setup; +#endif /* CONFIG_TDLS */ #ifdef CONFIG_IEEE80211R u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */ @@ -133,12 +143,6 @@ static inline void wpa_sm_deauthenticate(struct wpa_sm *sm, int reason_code) sm->ctx->deauthenticate(sm->ctx->ctx, reason_code); } -static inline void wpa_sm_disassociate(struct wpa_sm *sm, int reason_code) -{ - WPA_ASSERT(sm->ctx->disassociate); - sm->ctx->disassociate(sm->ctx->ctx, reason_code); -} - static inline int wpa_sm_set_key(struct wpa_sm *sm, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, @@ -237,6 +241,57 @@ static inline int wpa_sm_mark_authenticated(struct wpa_sm *sm, return -1; } +static inline void wpa_sm_set_rekey_offload(struct wpa_sm *sm) +{ + if (!sm->ctx->set_rekey_offload) + return; + sm->ctx->set_rekey_offload(sm->ctx->ctx, sm->ptk.kek, + sm->ptk.kck, sm->rx_replay_counter); +} + +#ifdef CONFIG_TDLS +static inline int wpa_sm_tdls_get_capa(struct wpa_sm *sm, + int *tdls_supported, + int *tdls_ext_setup) +{ + if (sm->ctx->tdls_get_capa) + return sm->ctx->tdls_get_capa(sm->ctx->ctx, tdls_supported, + tdls_ext_setup); + return -1; +} + +static inline int wpa_sm_send_tdls_mgmt(struct wpa_sm *sm, const u8 *dst, + u8 action_code, u8 dialog_token, + u16 status_code, const u8 *buf, + size_t len) +{ + if (sm->ctx->send_tdls_mgmt) + return sm->ctx->send_tdls_mgmt(sm->ctx->ctx, dst, action_code, + dialog_token, status_code, + buf, len); + return -1; +} + +static inline int wpa_sm_tdls_oper(struct wpa_sm *sm, int oper, + const u8 *peer) +{ + if (sm->ctx->tdls_oper) + return sm->ctx->tdls_oper(sm->ctx->ctx, oper, peer); + return -1; +} + +static inline int +wpa_sm_tdls_peer_addset(struct wpa_sm *sm, const u8 *addr, int add, + u16 capability, const u8 *supp_rates, + size_t supp_rates_len) +{ + if (sm->ctx->tdls_peer_addset) + return sm->ctx->tdls_peer_addset(sm->ctx->ctx, addr, add, + capability, supp_rates, + supp_rates_len); + return -1; +} +#endif /* CONFIG_TDLS */ void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, int ver, const u8 *dest, u16 proto, @@ -256,4 +311,7 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, struct wpa_ptk *ptk, size_t ptk_len); +void wpa_tdls_assoc(struct wpa_sm *sm); +void wpa_tdls_disassoc(struct wpa_sm *sm); + #endif /* WPA_I_H */ diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c index f447223bc3b97..3d7536595773c 100644 --- a/src/rsn_supp/wpa_ie.c +++ b/src/rsn_supp/wpa_ie.c @@ -2,14 +2,8 @@ * wpa_supplicant - WPA/RSN IE and KDE processing * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -22,144 +16,6 @@ #include "wpa_ie.h" -static int wpa_selector_to_bitfield(const u8 *s) -{ - if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_NONE) - return WPA_CIPHER_NONE; - if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP40) - return WPA_CIPHER_WEP40; - if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_TKIP) - return WPA_CIPHER_TKIP; - if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_CCMP) - return WPA_CIPHER_CCMP; - if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP104) - return WPA_CIPHER_WEP104; - return 0; -} - - -static int wpa_key_mgmt_to_bitfield(const u8 *s) -{ - if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_UNSPEC_802_1X) - return WPA_KEY_MGMT_IEEE8021X; - if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X) - return WPA_KEY_MGMT_PSK; - if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_NONE) - return WPA_KEY_MGMT_WPA_NONE; - return 0; -} - - -static int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, - struct wpa_ie_data *data) -{ - const struct wpa_ie_hdr *hdr; - const u8 *pos; - int left; - int i, count; - - os_memset(data, 0, sizeof(*data)); - data->proto = WPA_PROTO_WPA; - data->pairwise_cipher = WPA_CIPHER_TKIP; - data->group_cipher = WPA_CIPHER_TKIP; - data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; - data->capabilities = 0; - data->pmkid = NULL; - data->num_pmkid = 0; - data->mgmt_group_cipher = 0; - - if (wpa_ie_len == 0) { - /* No WPA IE - fail silently */ - return -1; - } - - if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) { - wpa_printf(MSG_DEBUG, "%s: ie len too short %lu", - __func__, (unsigned long) wpa_ie_len); - return -1; - } - - hdr = (const struct wpa_ie_hdr *) wpa_ie; - - if (hdr->elem_id != WLAN_EID_VENDOR_SPECIFIC || - hdr->len != wpa_ie_len - 2 || - RSN_SELECTOR_GET(hdr->oui) != WPA_OUI_TYPE || - WPA_GET_LE16(hdr->version) != WPA_VERSION) { - wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version", - __func__); - return -1; - } - - pos = (const u8 *) (hdr + 1); - left = wpa_ie_len - sizeof(*hdr); - - if (left >= WPA_SELECTOR_LEN) { - data->group_cipher = wpa_selector_to_bitfield(pos); - pos += WPA_SELECTOR_LEN; - left -= WPA_SELECTOR_LEN; - } else if (left > 0) { - wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much", - __func__, left); - return -1; - } - - if (left >= 2) { - data->pairwise_cipher = 0; - count = WPA_GET_LE16(pos); - pos += 2; - left -= 2; - if (count == 0 || left < count * WPA_SELECTOR_LEN) { - wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), " - "count %u left %u", __func__, count, left); - return -1; - } - for (i = 0; i < count; i++) { - data->pairwise_cipher |= wpa_selector_to_bitfield(pos); - pos += WPA_SELECTOR_LEN; - left -= WPA_SELECTOR_LEN; - } - } else if (left == 1) { - wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)", - __func__); - return -1; - } - - if (left >= 2) { - data->key_mgmt = 0; - count = WPA_GET_LE16(pos); - pos += 2; - left -= 2; - if (count == 0 || left < count * WPA_SELECTOR_LEN) { - wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), " - "count %u left %u", __func__, count, left); - return -1; - } - for (i = 0; i < count; i++) { - data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos); - pos += WPA_SELECTOR_LEN; - left -= WPA_SELECTOR_LEN; - } - } else if (left == 1) { - wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)", - __func__); - return -1; - } - - if (left >= 2) { - data->capabilities = WPA_GET_LE16(pos); - pos += 2; - left -= 2; - } - - if (left > 0) { - wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored", - __func__, left); - } - - return 0; -} - - /** * wpa_parse_wpa_ie - Parse WPA/RSN IE * @wpa_ie: Pointer to WPA or RSN IE @@ -185,6 +41,7 @@ static int wpa_gen_wpa_ie_wpa(u8 *wpa_ie, size_t wpa_ie_len, { u8 *pos; struct wpa_ie_hdr *hdr; + u32 suite; if (wpa_ie_len < sizeof(*hdr) + WPA_SELECTOR_LEN + 2 + WPA_SELECTOR_LEN + 2 + WPA_SELECTOR_LEN) @@ -196,34 +53,26 @@ static int wpa_gen_wpa_ie_wpa(u8 *wpa_ie, size_t wpa_ie_len, WPA_PUT_LE16(hdr->version, WPA_VERSION); pos = (u8 *) (hdr + 1); - if (group_cipher == WPA_CIPHER_CCMP) { - RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP); - } else if (group_cipher == WPA_CIPHER_TKIP) { - RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP); - } else if (group_cipher == WPA_CIPHER_WEP104) { - RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP104); - } else if (group_cipher == WPA_CIPHER_WEP40) { - RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP40); - } else { + suite = wpa_cipher_to_suite(WPA_PROTO_WPA, group_cipher); + if (suite == 0) { wpa_printf(MSG_WARNING, "Invalid group cipher (%d).", group_cipher); return -1; } + RSN_SELECTOR_PUT(pos, suite); pos += WPA_SELECTOR_LEN; *pos++ = 1; *pos++ = 0; - if (pairwise_cipher == WPA_CIPHER_CCMP) { - RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP); - } else if (pairwise_cipher == WPA_CIPHER_TKIP) { - RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP); - } else if (pairwise_cipher == WPA_CIPHER_NONE) { - RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE); - } else { + suite = wpa_cipher_to_suite(WPA_PROTO_WPA, pairwise_cipher); + if (suite == 0 || + (!wpa_cipher_valid_pairwise(pairwise_cipher) && + pairwise_cipher != WPA_CIPHER_NONE)) { wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).", pairwise_cipher); return -1; } + RSN_SELECTOR_PUT(pos, suite); pos += WPA_SELECTOR_LEN; *pos++ = 1; @@ -234,6 +83,8 @@ static int wpa_gen_wpa_ie_wpa(u8 *wpa_ie, size_t wpa_ie_len, RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X); } else if (key_mgmt == WPA_KEY_MGMT_WPA_NONE) { RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_NONE); + } else if (key_mgmt == WPA_KEY_MGMT_CCKM) { + RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_CCKM); } else { wpa_printf(MSG_WARNING, "Invalid key management type (%d).", key_mgmt); @@ -260,6 +111,7 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, u8 *pos; struct rsn_ie_hdr *hdr; u16 capab; + u32 suite; if (rsn_ie_len < sizeof(*hdr) + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2 + @@ -274,34 +126,26 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, WPA_PUT_LE16(hdr->version, RSN_VERSION); pos = (u8 *) (hdr + 1); - if (group_cipher == WPA_CIPHER_CCMP) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); - } else if (group_cipher == WPA_CIPHER_TKIP) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); - } else if (group_cipher == WPA_CIPHER_WEP104) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP104); - } else if (group_cipher == WPA_CIPHER_WEP40) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP40); - } else { + suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group_cipher); + if (suite == 0) { wpa_printf(MSG_WARNING, "Invalid group cipher (%d).", group_cipher); return -1; } + RSN_SELECTOR_PUT(pos, suite); pos += RSN_SELECTOR_LEN; *pos++ = 1; *pos++ = 0; - if (pairwise_cipher == WPA_CIPHER_CCMP) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); - } else if (pairwise_cipher == WPA_CIPHER_TKIP) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); - } else if (pairwise_cipher == WPA_CIPHER_NONE) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE); - } else { + suite = wpa_cipher_to_suite(WPA_PROTO_RSN, pairwise_cipher); + if (suite == 0 || + (!wpa_cipher_valid_pairwise(pairwise_cipher) && + pairwise_cipher != WPA_CIPHER_NONE)) { wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).", pairwise_cipher); return -1; } + RSN_SELECTOR_PUT(pos, suite); pos += RSN_SELECTOR_LEN; *pos++ = 1; @@ -310,6 +154,8 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X); } else if (key_mgmt == WPA_KEY_MGMT_PSK) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X); + } else if (key_mgmt == WPA_KEY_MGMT_CCKM) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_CCKM); #ifdef CONFIG_IEEE80211R } else if (key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); @@ -322,6 +168,12 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, } else if (key_mgmt == WPA_KEY_MGMT_PSK_SHA256) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256); #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_SAE + } else if (key_mgmt == WPA_KEY_MGMT_SAE) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE); + } else if (key_mgmt == WPA_KEY_MGMT_FT_SAE) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE); +#endif /* CONFIG_SAE */ } else { wpa_printf(MSG_WARNING, "Invalid key management type (%d).", key_mgmt); @@ -535,7 +387,6 @@ int wpa_supplicant_parse_ies(const u8 *buf, size_t len, ie->rsn_ie_len = pos[1] + 2; wpa_hexdump(MSG_DEBUG, "WPA: RSN IE in EAPOL-Key", ie->rsn_ie, ie->rsn_ie_len); -#ifdef CONFIG_IEEE80211R } else if (*pos == WLAN_EID_MOBILITY_DOMAIN) { ie->mdie = pos; ie->mdie_len = pos[1] + 2; @@ -562,7 +413,20 @@ int wpa_supplicant_parse_ies(const u8 *buf, size_t len, "EAPOL-Key Key Data IE", pos, 2 + pos[1]); } -#endif /* CONFIG_IEEE80211R */ + } else if (*pos == WLAN_EID_LINK_ID) { + if (pos[1] >= 18) { + ie->lnkid = pos; + ie->lnkid_len = pos[1] + 2; + } + } else if (*pos == WLAN_EID_EXT_CAPAB) { + ie->ext_capab = pos; + ie->ext_capab_len = pos[1] + 2; + } else if (*pos == WLAN_EID_SUPP_RATES) { + ie->supp_rates = pos; + ie->supp_rates_len = pos[1] + 2; + } else if (*pos == WLAN_EID_EXT_SUPP_RATES) { + ie->ext_supp_rates = pos; + ie->ext_supp_rates_len = pos[1] + 2; } else if (*pos == WLAN_EID_VENDOR_SPECIFIC) { ret = wpa_parse_generic(pos, end, ie); if (ret < 0) diff --git a/src/rsn_supp/wpa_ie.h b/src/rsn_supp/wpa_ie.h index 94518d8457872..5afdfe9fc7253 100644 --- a/src/rsn_supp/wpa_ie.h +++ b/src/rsn_supp/wpa_ie.h @@ -2,19 +2,15 @@ * wpa_supplicant - WPA/RSN IE and KDE definitions * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_IE_H #define WPA_IE_H +struct wpa_sm; + struct wpa_eapol_ie_parse { const u8 *wpa_ie; size_t wpa_ie_len; @@ -39,14 +35,20 @@ struct wpa_eapol_ie_parse { const u8 *igtk; size_t igtk_len; #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_IEEE80211R const u8 *mdie; size_t mdie_len; const u8 *ftie; size_t ftie_len; const u8 *reassoc_deadline; const u8 *key_lifetime; -#endif /* CONFIG_IEEE80211R */ + const u8 *lnkid; + size_t lnkid_len; + const u8 *ext_capab; + size_t ext_capab_len; + const u8 *supp_rates; + size_t supp_rates_len; + const u8 *ext_supp_rates; + size_t ext_supp_rates_len; }; int wpa_supplicant_parse_ies(const u8 *buf, size_t len, diff --git a/src/tls/Makefile b/src/tls/Makefile index a2da0965a5f9d..27cdfcaa97489 100644 --- a/src/tls/Makefile +++ b/src/tls/Makefile @@ -11,6 +11,8 @@ include ../lib.rules CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH CFLAGS += -DCONFIG_CRYPTO_INTERNAL +CFLAGS += -DCONFIG_TLSV11 +CFLAGS += -DCONFIG_TLSV12 LIB_OBJS= \ asn1.o \ diff --git a/src/tls/asn1.c b/src/tls/asn1.c index 3391245fe3cd2..53acd530d9178 100644 --- a/src/tls/asn1.c +++ b/src/tls/asn1.c @@ -2,14 +2,8 @@ * ASN.1 DER parsing * Copyright (c) 2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/tls/asn1.h b/src/tls/asn1.h index 2ff571ea909d3..6342c4cc79fd2 100644 --- a/src/tls/asn1.h +++ b/src/tls/asn1.h @@ -2,14 +2,8 @@ * ASN.1 DER parsing * Copyright (c) 2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef ASN1_H diff --git a/src/tls/bignum.c b/src/tls/bignum.c index 5c0fc62edeb59..f3baafe1061df 100644 --- a/src/tls/bignum.c +++ b/src/tls/bignum.c @@ -2,14 +2,8 @@ * Big number math * Copyright (c) 2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/tls/bignum.h b/src/tls/bignum.h index f25e26783a0ab..24acdce5973ca 100644 --- a/src/tls/bignum.h +++ b/src/tls/bignum.h @@ -2,14 +2,8 @@ * Big number math * Copyright (c) 2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef BIGNUM_H diff --git a/src/tls/libtommath.c b/src/tls/libtommath.c index 137426421c299..741b442ca912d 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 @@ -572,7 +580,7 @@ static int mp_mod (mp_int * a, mp_int * b, mp_int * c) /* this is a shell function that calls either the normal or Montgomery * exptmod functions. Originally the call to the montgomery code was - * embedded in the normal function but that wasted alot of stack space + * embedded in the normal function but that wasted a lot of stack space * for nothing (since 99% of the time the Montgomery code would be called) */ static int mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y) @@ -2207,7 +2215,7 @@ static int mp_2expt (mp_int * a, int b) /* zero a as per default */ mp_zero (a); - /* grow a to accomodate the single bit */ + /* grow a to accommodate the single bit */ if ((res = mp_grow (a, b / DIGIT_BIT + 1)) != MP_OKAY) { return res; } @@ -2319,7 +2327,7 @@ CLEANUP: } -/* multiplies |a| * |b| and only computes upto digs digits of result +/* multiplies |a| * |b| and only computes up to digs digits of result * HAC pp. 595, Algorithm 14.12 Modified so you can control how * many digits of output are created. */ @@ -2678,7 +2686,7 @@ mp_montgomery_setup (mp_int * n, mp_digit * rho) * * Based on Algorithm 14.32 on pp.601 of HAC. */ -int fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho) +static int fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho) { int ix, res, olduse; mp_word W[MP_WARRAY]; @@ -2829,7 +2837,7 @@ static int mp_mul_2(mp_int * a, mp_int * b) { int x, res, oldused; - /* grow to accomodate result */ + /* grow to accommodate result */ if (b->alloc < a->used + 1) { if ((res = mp_grow (b, a->used + 1)) != MP_OKAY) { return res; @@ -2891,8 +2899,8 @@ static int mp_mul_2(mp_int * a, mp_int * b) /* * shifts with subtractions when the result is greater than b. * - * The method is slightly modified to shift B unconditionally upto just under - * the leading bit of b. This saves alot of multiple precision shifting. + * The method is slightly modified to shift B unconditionally up to just under + * the leading bit of b. This saves a lot of multiple precision shifting. */ static int mp_montgomery_calc_normalization (mp_int * a, mp_int * b) { diff --git a/src/tls/pkcs1.c b/src/tls/pkcs1.c index 72ebd87648576..b6fde5ee868ab 100644 --- a/src/tls/pkcs1.c +++ b/src/tls/pkcs1.c @@ -2,14 +2,8 @@ * PKCS #1 (RSA Encryption) * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/tls/pkcs1.h b/src/tls/pkcs1.h index 68872b1502edf..ed64defaafe08 100644 --- a/src/tls/pkcs1.h +++ b/src/tls/pkcs1.h @@ -2,14 +2,8 @@ * PKCS #1 (RSA Encryption) * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef PKCS1_H diff --git a/src/tls/pkcs5.c b/src/tls/pkcs5.c index 4291b84f16d7f..8a93483781d80 100644 --- a/src/tls/pkcs5.c +++ b/src/tls/pkcs5.c @@ -2,14 +2,8 @@ * PKCS #5 (Password-based Encryption) * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -32,7 +26,7 @@ struct pkcs5_params { }; -enum pkcs5_alg pkcs5_get_alg(struct asn1_oid *oid) +static enum pkcs5_alg pkcs5_get_alg(struct asn1_oid *oid) { if (oid->len == 7 && oid->oid[0] == 1 /* iso */ && diff --git a/src/tls/pkcs5.h b/src/tls/pkcs5.h index 6ed39230b5332..20ddadc45722a 100644 --- a/src/tls/pkcs5.h +++ b/src/tls/pkcs5.h @@ -2,14 +2,8 @@ * PKCS #5 (Password-based Encryption) * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef PKCS5_H diff --git a/src/tls/pkcs8.c b/src/tls/pkcs8.c index 69ab262e5ebed..52e43a4403b9a 100644 --- a/src/tls/pkcs8.c +++ b/src/tls/pkcs8.c @@ -2,14 +2,8 @@ * PKCS #8 (Private-key information syntax) * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/tls/pkcs8.h b/src/tls/pkcs8.h index dac517c91ac94..bebf840ba75b0 100644 --- a/src/tls/pkcs8.h +++ b/src/tls/pkcs8.h @@ -2,14 +2,8 @@ * PKCS #8 (Private-key information syntax) * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef PKCS8_H diff --git a/src/tls/rsa.c b/src/tls/rsa.c index 3084adc17579a..125c4205b705c 100644 --- a/src/tls/rsa.c +++ b/src/tls/rsa.c @@ -2,14 +2,8 @@ * RSA * Copyright (c) 2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/tls/rsa.h b/src/tls/rsa.h index ac50dfd69d3e6..c236a9df44498 100644 --- a/src/tls/rsa.h +++ b/src/tls/rsa.h @@ -2,14 +2,8 @@ * RSA * Copyright (c) 2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef RSA_H diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c index afb603175a11a..12148b61ddfc4 100644 --- a/src/tls/tlsv1_client.c +++ b/src/tls/tlsv1_client.c @@ -1,15 +1,9 @@ /* - * TLSv1 client (RFC 2246) - * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246) + * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -67,7 +61,8 @@ int tls_derive_keys(struct tlsv1_client *conn, os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN); os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random, TLS_RANDOM_LEN); - if (tls_prf(pre_master_secret, pre_master_secret_len, + if (tls_prf(conn->rl.tls_version, + pre_master_secret, pre_master_secret_len, "master secret", seed, 2 * TLS_RANDOM_LEN, conn->master_secret, TLS_MASTER_SECRET_LEN)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive " @@ -80,9 +75,11 @@ int tls_derive_keys(struct tlsv1_client *conn, os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN); os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN); - key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len + - conn->rl.iv_size); - if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, + key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len); + if (conn->rl.tls_version == TLS_VERSION_1) + key_block_len += 2 * conn->rl.iv_size; + if (tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, "key expansion", seed, 2 * TLS_RANDOM_LEN, key_block, key_block_len)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block"); @@ -107,12 +104,21 @@ int tls_derive_keys(struct tlsv1_client *conn, os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len); pos += conn->rl.key_material_len; - /* client_write_IV */ - os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size); - pos += conn->rl.iv_size; - /* server_write_IV */ - os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size); - pos += conn->rl.iv_size; + if (conn->rl.tls_version == TLS_VERSION_1) { + /* client_write_IV */ + os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size); + pos += conn->rl.iv_size; + /* server_write_IV */ + os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size); + pos += conn->rl.iv_size; + } else { + /* + * Use IV field to set the mask value for TLS v1.1. A fixed + * mask of zero is used per the RFC 4346, 6.2.3.2 CBC Block + * Cipher option 2a. + */ + os_memset(conn->rl.write_iv, 0, conn->rl.iv_size); + } return 0; } @@ -126,17 +132,23 @@ int tls_derive_keys(struct tlsv1_client *conn, * @out_len: Length of the output buffer. * @appl_data: Pointer to application data pointer, or %NULL if dropped * @appl_data_len: Pointer to variable that is set to appl_data length + * @need_more_data: Set to 1 if more data would be needed to complete + * processing * Returns: Pointer to output data, %NULL on failure */ u8 * tlsv1_client_handshake(struct tlsv1_client *conn, const u8 *in_data, size_t in_len, size_t *out_len, u8 **appl_data, - size_t *appl_data_len) + size_t *appl_data_len, int *need_more_data) { const u8 *pos, *end; - u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct; + u8 *msg = NULL, *in_msg = NULL, *in_pos, *in_end, alert, ct; size_t in_msg_len; int no_appl_data; + int used; + + if (need_more_data) + *need_more_data = 0; if (conn->state == CLIENT_HELLO) { if (in_len) @@ -144,6 +156,19 @@ u8 * tlsv1_client_handshake(struct tlsv1_client *conn, return tls_send_client_hello(conn, out_len); } + if (conn->partial_input) { + if (wpabuf_resize(&conn->partial_input, in_len) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate " + "memory for pending record"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + goto failed; + } + wpabuf_put_data(conn->partial_input, in_data, in_len); + in_data = wpabuf_head(conn->partial_input); + in_len = wpabuf_len(conn->partial_input); + } + if (in_data == NULL || in_len == 0) return NULL; @@ -156,13 +181,33 @@ u8 * tlsv1_client_handshake(struct tlsv1_client *conn, /* Each received packet may include multiple records */ while (pos < end) { in_msg_len = in_len; - if (tlsv1_record_receive(&conn->rl, pos, end - pos, - in_msg, &in_msg_len, &alert)) { + used = tlsv1_record_receive(&conn->rl, pos, end - pos, + in_msg, &in_msg_len, &alert); + if (used < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Processing received " "record failed"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); goto failed; } + if (used == 0) { + struct wpabuf *partial; + wpa_printf(MSG_DEBUG, "TLSv1: Need more data"); + partial = wpabuf_alloc_copy(pos, end - pos); + wpabuf_free(conn->partial_input); + conn->partial_input = partial; + if (conn->partial_input == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to " + "allocate memory for pending " + "record"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + goto failed; + } + os_free(in_msg); + if (need_more_data) + *need_more_data = 1; + return NULL; + } ct = pos[0]; in_pos = in_msg; @@ -180,7 +225,7 @@ u8 * tlsv1_client_handshake(struct tlsv1_client *conn, in_pos += in_msg_len; } - pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3); + pos += used; } os_free(in_msg); @@ -192,6 +237,8 @@ u8 * tlsv1_client_handshake(struct tlsv1_client *conn, failed: os_free(in_msg); if (conn->alert_level) { + wpabuf_free(conn->partial_input); + conn->partial_input = NULL; conn->state = FAILED; os_free(msg); msg = tlsv1_client_send_alert(conn, conn->alert_level, @@ -202,6 +249,11 @@ failed: *out_len = 0; } + if (need_more_data == NULL || !(*need_more_data)) { + wpabuf_free(conn->partial_input); + conn->partial_input = NULL; + } + return msg; } @@ -227,10 +279,8 @@ int tlsv1_client_encrypt(struct tlsv1_client *conn, wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData", in_data, in_len); - os_memcpy(out_data + TLS_RECORD_HEADER_LEN, in_data, in_len); - if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA, - out_data, out_len, in_len, &rlen) < 0) { + out_data, out_len, in_data, in_len, &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); @@ -246,58 +296,116 @@ int tlsv1_client_encrypt(struct tlsv1_client *conn, * @conn: TLSv1 client connection data from tlsv1_client_init() * @in_data: Pointer to input buffer (encrypted TLS data) * @in_len: Input buffer length - * @out_data: Pointer to output buffer (decrypted data from TLS tunnel) - * @out_len: Maximum out_data length - * Returns: Number of bytes written to out_data, -1 on failure + * @need_more_data: Set to 1 if more data would be needed to complete + * processing + * Returns: Decrypted data or %NULL on failure * * This function is used after TLS handshake has been completed successfully to * receive data from the encrypted tunnel. */ -int tlsv1_client_decrypt(struct tlsv1_client *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len) +struct wpabuf * tlsv1_client_decrypt(struct tlsv1_client *conn, + const u8 *in_data, size_t in_len, + int *need_more_data) { const u8 *in_end, *pos; - int res; - u8 alert, *out_end, *out_pos; + int used; + u8 alert, *out_pos, ct; size_t olen; + struct wpabuf *buf = NULL; + + if (need_more_data) + *need_more_data = 0; + + if (conn->partial_input) { + if (wpabuf_resize(&conn->partial_input, in_len) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate " + "memory for pending record"); + alert = TLS_ALERT_INTERNAL_ERROR; + goto fail; + } + wpabuf_put_data(conn->partial_input, in_data, in_len); + in_data = wpabuf_head(conn->partial_input); + in_len = wpabuf_len(conn->partial_input); + } pos = in_data; in_end = in_data + in_len; - out_pos = out_data; - out_end = out_data + out_len; while (pos < in_end) { - if (pos[0] != TLS_CONTENT_TYPE_APPLICATION_DATA) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type " - "0x%x", pos[0]); - tls_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_UNEXPECTED_MESSAGE); - return -1; + ct = pos[0]; + if (wpabuf_resize(&buf, in_end - pos) < 0) { + alert = TLS_ALERT_INTERNAL_ERROR; + goto fail; } - - olen = out_end - out_pos; - res = tlsv1_record_receive(&conn->rl, pos, in_end - pos, - out_pos, &olen, &alert); - if (res < 0) { + out_pos = wpabuf_put(buf, 0); + olen = wpabuf_tailroom(buf); + used = tlsv1_record_receive(&conn->rl, pos, in_end - pos, + out_pos, &olen, &alert); + if (used < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing " "failed"); - tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); - return -1; + goto fail; } - out_pos += olen; - if (out_pos > out_end) { - wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough " - "for processing the received record"); - tls_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_INTERNAL_ERROR); - return -1; + if (used == 0) { + struct wpabuf *partial; + wpa_printf(MSG_DEBUG, "TLSv1: Need more data"); + partial = wpabuf_alloc_copy(pos, in_end - pos); + wpabuf_free(conn->partial_input); + conn->partial_input = partial; + if (conn->partial_input == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to " + "allocate memory for pending " + "record"); + alert = TLS_ALERT_INTERNAL_ERROR; + goto fail; + } + if (need_more_data) + *need_more_data = 1; + return buf; } - pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3); + if (ct == TLS_CONTENT_TYPE_ALERT) { + if (olen < 2) { + wpa_printf(MSG_DEBUG, "TLSv1: Alert " + "underflow"); + alert = TLS_ALERT_DECODE_ERROR; + goto fail; + } + wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d", + out_pos[0], out_pos[1]); + if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) { + /* Continue processing */ + pos += used; + continue; + } + + alert = out_pos[1]; + goto fail; + } + + if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type " + "0x%x when decrypting application data", + pos[0]); + alert = TLS_ALERT_UNEXPECTED_MESSAGE; + goto fail; + } + + wpabuf_put(buf, olen); + + pos += used; } - return out_pos - out_data; + wpabuf_free(conn->partial_input); + conn->partial_input = NULL; + return buf; + +fail: + wpabuf_free(buf); + wpabuf_free(conn->partial_input); + conn->partial_input = NULL; + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); + return NULL; } @@ -351,15 +459,17 @@ struct tlsv1_client * tlsv1_client_init(void) count = 0; suites = conn->cipher_suites; -#ifndef CONFIG_CRYPTO_INTERNAL + suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA256; suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA; -#endif /* CONFIG_CRYPTO_INTERNAL */ + suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256; suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA; suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA; suites[count++] = TLS_RSA_WITH_RC4_128_SHA; suites[count++] = TLS_RSA_WITH_RC4_128_MD5; conn->num_cipher_suites = count; + conn->rl.tls_version = TLS_VERSION; + return conn; } @@ -378,6 +488,7 @@ void tlsv1_client_deinit(struct tlsv1_client *conn) os_free(conn->client_hello_ext); tlsv1_client_free_dh(conn); tlsv1_cred_free(conn->cred); + wpabuf_free(conn->partial_input); os_free(conn); } @@ -421,7 +532,8 @@ int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, TLS_RANDOM_LEN); } - return tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, + return tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, label, seed, 2 * TLS_RANDOM_LEN, out, out_len); } @@ -453,15 +565,24 @@ int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf, case TLS_RSA_WITH_3DES_EDE_CBC_SHA: cipher = "DES-CBC3-SHA"; break; + case TLS_DH_anon_WITH_AES_128_CBC_SHA256: + cipher = "ADH-AES-128-SHA256"; + break; case TLS_DH_anon_WITH_AES_128_CBC_SHA: cipher = "ADH-AES-128-SHA"; break; case TLS_RSA_WITH_AES_256_CBC_SHA: cipher = "AES-256-SHA"; break; + case TLS_RSA_WITH_AES_256_CBC_SHA256: + cipher = "AES-256-SHA256"; + break; case TLS_RSA_WITH_AES_128_CBC_SHA: cipher = "AES-128-SHA"; break; + case TLS_RSA_WITH_AES_128_CBC_SHA256: + cipher = "AES-128-SHA256"; + break; default: return -1; } @@ -612,9 +733,9 @@ int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers) if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) { count = 0; suites = conn->cipher_suites; -#ifndef CONFIG_CRYPTO_INTERNAL + suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA256; suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA; -#endif /* CONFIG_CRYPTO_INTERNAL */ + suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA256; suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA; suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA; suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5; @@ -656,6 +777,12 @@ int tlsv1_client_set_cred(struct tlsv1_client *conn, } +void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled) +{ + conn->disable_time_checks = !enabled; +} + + void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn, tlsv1_client_session_ticket_cb cb, void *ctx) diff --git a/src/tls/tlsv1_client.h b/src/tls/tlsv1_client.h index 16ad57d4007fe..8ec85f1a91936 100644 --- a/src/tls/tlsv1_client.h +++ b/src/tls/tlsv1_client.h @@ -1,15 +1,9 @@ /* - * TLSv1 client (RFC 2246) - * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246) + * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TLSV1_CLIENT_H @@ -29,13 +23,13 @@ int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, u8 * tlsv1_client_handshake(struct tlsv1_client *conn, const u8 *in_data, size_t in_len, size_t *out_len, u8 **appl_data, - size_t *appl_data_len); + size_t *appl_data_len, int *need_more_data); int tlsv1_client_encrypt(struct tlsv1_client *conn, const u8 *in_data, size_t in_len, u8 *out_data, size_t out_len); -int tlsv1_client_decrypt(struct tlsv1_client *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len); +struct wpabuf * tlsv1_client_decrypt(struct tlsv1_client *conn, + const u8 *in_data, size_t in_len, + int *need_more_data); int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf, size_t buflen); int tlsv1_client_shutdown(struct tlsv1_client *conn); @@ -47,6 +41,7 @@ int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn); int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers); int tlsv1_client_set_cred(struct tlsv1_client *conn, struct tlsv1_credentials *cred); +void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled); typedef int (*tlsv1_client_session_ticket_cb) (void *ctx, const u8 *ticket, size_t len, const u8 *client_random, diff --git a/src/tls/tlsv1_client_i.h b/src/tls/tlsv1_client_i.h index 7fe179f10c63b..55fdcf8d0435c 100644 --- a/src/tls/tlsv1_client_i.h +++ b/src/tls/tlsv1_client_i.h @@ -1,15 +1,9 @@ /* * TLSv1 client - internal structures - * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TLSV1_CLIENT_I_H @@ -39,6 +33,7 @@ struct tlsv1_client { unsigned int session_resumed:1; unsigned int session_ticket_included:1; unsigned int use_session_ticket:1; + unsigned int disable_time_checks:1; struct crypto_public_key *server_rsa_key; @@ -67,6 +62,8 @@ struct tlsv1_client { tlsv1_client_session_ticket_cb session_ticket_cb; void *session_ticket_cb_ctx; + + struct wpabuf *partial_input; }; diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c index ed3f2606c8cc2..3269ecf668ee3 100644 --- a/src/tls/tlsv1_client_read.c +++ b/src/tls/tlsv1_client_read.c @@ -1,15 +1,9 @@ /* * TLSv1 client - read handshake message - * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -17,6 +11,7 @@ #include "common.h" #include "crypto/md5.h" #include "crypto/sha1.h" +#include "crypto/sha256.h" #include "crypto/tls.h" #include "x509v3.h" #include "tlsv1_common.h" @@ -38,6 +33,7 @@ static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct, const u8 *pos, *end; size_t left, len, i; u16 cipher_suite; + u16 tls_version; if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " @@ -79,15 +75,20 @@ static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct, /* ProtocolVersion server_version */ if (end - pos < 2) goto decode_error; - if (WPA_GET_BE16(pos) != TLS_VERSION) { + tls_version = WPA_GET_BE16(pos); + if (!tls_version_ok(tls_version)) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in " - "ServerHello"); + "ServerHello %u.%u", pos[0], pos[1]); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_PROTOCOL_VERSION); return -1; } pos += 2; + wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s", + tls_version_str(tls_version)); + conn->rl.tls_version = tls_version; + /* Random random */ if (end - pos < TLS_RANDOM_LEN) goto decode_error; @@ -365,7 +366,8 @@ static int tls_process_certificate(struct tlsv1_client *conn, u8 ct, if (conn->cred && x509_certificate_chain_validate(conn->cred->trusted_certs, chain, - &reason) < 0) { + &reason, conn->disable_time_checks) + < 0) { int tls_reason; wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain " "validation failed (reason=%d)", reason); @@ -815,6 +817,21 @@ static int tls_process_server_finished(struct tlsv1_client *conn, u8 ct, wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished", pos, TLS_VERIFY_DATA_LEN); +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version >= TLS_VERSION_1_2) { + hlen = SHA256_MAC_LEN; + if (conn->verify.sha256_server == NULL || + crypto_hash_finish(conn->verify.sha256_server, hash, &hlen) + < 0) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.sha256_server = NULL; + return -1; + } + conn->verify.sha256_server = NULL; + } else { +#endif /* CONFIG_TLSV12 */ + hlen = MD5_MAC_LEN; if (conn->verify.md5_server == NULL || crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) { @@ -836,9 +853,15 @@ static int tls_process_server_finished(struct tlsv1_client *conn, u8 ct, return -1; } conn->verify.sha1_server = NULL; + hlen = MD5_MAC_LEN + SHA1_MAC_LEN; + +#ifdef CONFIG_TLSV12 + } +#endif /* CONFIG_TLSV12 */ - if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, - "server finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN, + if (tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + "server finished", hash, hlen, verify_data, TLS_VERIFY_DATA_LEN)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive verify_data"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, diff --git a/src/tls/tlsv1_client_write.c b/src/tls/tlsv1_client_write.c index b47425f232b7d..d789efb4255e2 100644 --- a/src/tls/tlsv1_client_write.c +++ b/src/tls/tlsv1_client_write.c @@ -1,15 +1,9 @@ /* * TLSv1 client - write handshake message - * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -17,7 +11,9 @@ #include "common.h" #include "crypto/md5.h" #include "crypto/sha1.h" +#include "crypto/sha256.h" #include "crypto/tls.h" +#include "crypto/random.h" #include "x509v3.h" #include "tlsv1_common.h" #include "tlsv1_record.h" @@ -57,7 +53,7 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) os_get_time(&now); WPA_PUT_BE32(conn->client_random, now.sec); - if (os_get_random(conn->client_random + 4, TLS_RANDOM_LEN - 4)) { + if (random_get_bytes(conn->client_random + 4, TLS_RANDOM_LEN - 4)) { wpa_printf(MSG_ERROR, "TLSv1: Could not generate " "client_random"); return NULL; @@ -115,7 +111,8 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, - rhdr, end - rhdr, pos - hs_start, out_len) < 0) { + rhdr, end - rhdr, hs_start, pos - hs_start, + out_len) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); @@ -191,7 +188,8 @@ static int tls_write_client_certificate(struct tlsv1_client *conn, WPA_PUT_BE24(hs_length, pos - hs_length - 3); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, - rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { + rhdr, end - rhdr, hs_start, pos - hs_start, + &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); @@ -222,7 +220,7 @@ static int tlsv1_key_x_anon_dh(struct tlsv1_client *conn, u8 **pos, u8 *end) TLS_ALERT_INTERNAL_ERROR); return -1; } - if (os_get_random(csecret, csecret_len)) { + if (random_get_bytes(csecret, csecret_len)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random " "data for Diffie-Hellman"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, @@ -412,7 +410,8 @@ static int tls_write_client_key_exchange(struct tlsv1_client *conn, WPA_PUT_BE24(hs_length, pos - hs_length - 3); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, - rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { + rhdr, end - rhdr, hs_start, pos - hs_start, + &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); @@ -432,7 +431,7 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn, { u8 *pos, *rhdr, *hs_start, *hs_length, *signed_start; size_t rlen, hlen, clen; - u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos; + u8 hash[100], *hpos; enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA; pos = *msgpos; @@ -472,6 +471,40 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn, hpos = hash; +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version == TLS_VERSION_1_2) { + hlen = SHA256_MAC_LEN; + if (conn->verify.sha256_cert == NULL || + crypto_hash_finish(conn->verify.sha256_cert, hpos, &hlen) < + 0) { + conn->verify.sha256_cert = NULL; + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + conn->verify.sha256_cert = NULL; + + /* + * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5 + * + * DigestInfo ::= SEQUENCE { + * digestAlgorithm DigestAlgorithm, + * digest OCTET STRING + * } + * + * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11} + * + * DER encoded DigestInfo for SHA256 per RFC 3447: + * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || + * H + */ + os_memmove(hash + 19, hash, hlen); + hlen += 19; + os_memcpy(hash, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65" + "\x03\x04\x02\x01\x05\x00\x04\x20", 19); + } else { +#endif /* CONFIG_TLSV12 */ + if (alg == SIGN_ALG_RSA) { hlen = MD5_MAC_LEN; if (conn->verify.md5_cert == NULL || @@ -502,8 +535,29 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn, if (alg == SIGN_ALG_RSA) hlen += MD5_MAC_LEN; +#ifdef CONFIG_TLSV12 + } +#endif /* CONFIG_TLSV12 */ + wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen); +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version >= TLS_VERSION_1_2) { + /* + * RFC 5246, 4.7: + * TLS v1.2 adds explicit indication of the used signature and + * hash algorithms. + * + * struct { + * HashAlgorithm hash; + * SignatureAlgorithm signature; + * } SignatureAndHashAlgorithm; + */ + *pos++ = TLS_HASH_ALG_SHA256; + *pos++ = TLS_SIGN_ALG_RSA; + } +#endif /* CONFIG_TLSV12 */ + /* * RFC 2246, 4.7: * In digital signing, one-way hash functions are used as input for a @@ -533,7 +587,8 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn, WPA_PUT_BE24(hs_length, pos - hs_length - 3); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, - rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { + rhdr, end - rhdr, hs_start, pos - hs_start, + &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); @@ -552,17 +607,16 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn, static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn, u8 **msgpos, u8 *end) { - u8 *pos, *rhdr; size_t rlen; - - pos = *msgpos; + u8 payload[1]; wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec"); - rhdr = pos; - pos += TLS_RECORD_HEADER_LEN; - *pos = TLS_CHANGE_CIPHER_SPEC; + + payload[0] = TLS_CHANGE_CIPHER_SPEC; + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC, - rhdr, end - rhdr, 1, &rlen) < 0) { + *msgpos, end - *msgpos, payload, sizeof(payload), + &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); @@ -577,7 +631,7 @@ static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn, return -1; } - *msgpos = rhdr + rlen; + *msgpos += rlen; return 0; } @@ -586,17 +640,30 @@ static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn, static int tls_write_client_finished(struct tlsv1_client *conn, u8 **msgpos, u8 *end) { - u8 *pos, *rhdr, *hs_start, *hs_length; + u8 *pos, *hs_start; size_t rlen, hlen; - u8 verify_data[TLS_VERIFY_DATA_LEN]; + u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN]; u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; - pos = *msgpos; - wpa_printf(MSG_DEBUG, "TLSv1: Send Finished"); /* Encrypted Handshake Message: Finished */ +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version >= TLS_VERSION_1_2) { + hlen = SHA256_MAC_LEN; + if (conn->verify.sha256_client == NULL || + crypto_hash_finish(conn->verify.sha256_client, hash, &hlen) + < 0) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.sha256_client = NULL; + return -1; + } + conn->verify.sha256_client = NULL; + } else { +#endif /* CONFIG_TLSV12 */ + hlen = MD5_MAC_LEN; if (conn->verify.md5_client == NULL || crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) { @@ -618,43 +685,44 @@ static int tls_write_client_finished(struct tlsv1_client *conn, return -1; } conn->verify.sha1_client = NULL; + hlen = MD5_MAC_LEN + SHA1_MAC_LEN; + +#ifdef CONFIG_TLSV12 + } +#endif /* CONFIG_TLSV12 */ - if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, - "client finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN, - verify_data, TLS_VERIFY_DATA_LEN)) { + if (tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + "client finished", hash, hlen, + verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)", - verify_data, TLS_VERIFY_DATA_LEN); + verify_data + 1 + 3, TLS_VERIFY_DATA_LEN); - rhdr = pos; - pos += TLS_RECORD_HEADER_LEN; /* Handshake */ - hs_start = pos; + pos = hs_start = verify_data; /* HandshakeType msg_type */ *pos++ = TLS_HANDSHAKE_TYPE_FINISHED; - /* uint24 length (to be filled) */ - hs_length = pos; + /* uint24 length */ + WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN); pos += 3; - os_memcpy(pos, verify_data, TLS_VERIFY_DATA_LEN); - pos += TLS_VERIFY_DATA_LEN; - WPA_PUT_BE24(hs_length, pos - hs_length - 3); + pos += TLS_VERIFY_DATA_LEN; /* verify_data already in place */ tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, - rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { + *msgpos, end - *msgpos, hs_start, pos - hs_start, + &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } - pos = rhdr + rlen; - - *msgpos = pos; + *msgpos += rlen; return 0; } @@ -668,7 +736,7 @@ static u8 * tls_send_client_key_exchange(struct tlsv1_client *conn, *out_len = 0; - msglen = 1000; + msglen = 2000; if (conn->certificate_requested) msglen += tls_client_cert_chain_der_len(conn); @@ -777,7 +845,8 @@ u8 * tlsv1_client_send_alert(struct tlsv1_client *conn, u8 level, /* ContentType type */ *pos++ = TLS_CONTENT_TYPE_ALERT; /* ProtocolVersion version */ - WPA_PUT_BE16(pos, TLS_VERSION); + WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version : + TLS_VERSION); pos += 2; /* uint16 length (to be filled) */ length = pos; diff --git a/src/tls/tlsv1_common.c b/src/tls/tlsv1_common.c index 2f9dd0fa887d1..d21286283b83a 100644 --- a/src/tls/tlsv1_common.c +++ b/src/tls/tlsv1_common.c @@ -1,20 +1,16 @@ /* * TLSv1 common routines - * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" #include "x509v3.h" #include "tlsv1_common.h" @@ -50,7 +46,15 @@ static const struct tls_cipher_suite tls_cipher_suites[] = { { TLS_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA }, { TLS_DH_anon_WITH_AES_256_CBC_SHA, TLS_KEY_X_DH_anon, - TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA } + TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA }, + { TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_KEY_X_RSA, + TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 }, + { TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_KEY_X_RSA, + TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 }, + { TLS_DH_anon_WITH_AES_128_CBC_SHA256, TLS_KEY_X_DH_anon, + TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 }, + { TLS_DH_anon_WITH_AES_256_CBC_SHA256, TLS_KEY_X_DH_anon, + TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 } }; #define NUM_ELEMS(a) (sizeof(a) / sizeof((a)[0])) @@ -202,6 +206,19 @@ int tls_verify_hash_init(struct tls_verify_hash *verify) tls_verify_hash_free(verify); return -1; } +#ifdef CONFIG_TLSV12 + verify->sha256_client = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, + 0); + verify->sha256_server = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, + 0); + verify->sha256_cert = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, + 0); + if (verify->sha256_client == NULL || verify->sha256_server == NULL || + verify->sha256_cert == NULL) { + tls_verify_hash_free(verify); + return -1; + } +#endif /* CONFIG_TLSV12 */ return 0; } @@ -221,6 +238,14 @@ void tls_verify_hash_add(struct tls_verify_hash *verify, const u8 *buf, crypto_hash_update(verify->md5_cert, buf, len); crypto_hash_update(verify->sha1_cert, buf, len); } +#ifdef CONFIG_TLSV12 + if (verify->sha256_client) + crypto_hash_update(verify->sha256_client, buf, len); + if (verify->sha256_server) + crypto_hash_update(verify->sha256_server, buf, len); + if (verify->sha256_cert) + crypto_hash_update(verify->sha256_cert, buf, len); +#endif /* CONFIG_TLSV12 */ } @@ -238,4 +263,60 @@ void tls_verify_hash_free(struct tls_verify_hash *verify) verify->sha1_client = NULL; verify->sha1_server = NULL; verify->sha1_cert = NULL; +#ifdef CONFIG_TLSV12 + crypto_hash_finish(verify->sha256_client, NULL, NULL); + crypto_hash_finish(verify->sha256_server, NULL, NULL); + crypto_hash_finish(verify->sha256_cert, NULL, NULL); + verify->sha256_client = NULL; + verify->sha256_server = NULL; + verify->sha256_cert = NULL; +#endif /* CONFIG_TLSV12 */ +} + + +int tls_version_ok(u16 ver) +{ + if (ver == TLS_VERSION_1) + return 1; +#ifdef CONFIG_TLSV11 + if (ver == TLS_VERSION_1_1) + return 1; +#endif /* CONFIG_TLSV11 */ +#ifdef CONFIG_TLSV12 + if (ver == TLS_VERSION_1_2) + return 1; +#endif /* CONFIG_TLSV12 */ + + return 0; +} + + +const char * tls_version_str(u16 ver) +{ + switch (ver) { + case TLS_VERSION_1: + return "1.0"; + case TLS_VERSION_1_1: + return "1.1"; + case TLS_VERSION_1_2: + return "1.2"; + } + + return "?"; +} + + +int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label, + const u8 *seed, size_t seed_len, u8 *out, size_t outlen) +{ +#ifdef CONFIG_TLSV12 + if (ver >= TLS_VERSION_1_2) { + tls_prf_sha256(secret, secret_len, label, seed, seed_len, + out, outlen); + return 0; + } +#endif /* CONFIG_TLSV12 */ + + return tls_prf_sha1_md5(secret, secret_len, label, seed, seed_len, out, + outlen); } diff --git a/src/tls/tlsv1_common.h b/src/tls/tlsv1_common.h index 763a4af3d5611..f28c0cdc47906 100644 --- a/src/tls/tlsv1_common.h +++ b/src/tls/tlsv1_common.h @@ -1,15 +1,9 @@ /* * TLSv1 common definitions - * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TLSV1_COMMON_H @@ -17,7 +11,18 @@ #include "crypto/crypto.h" -#define TLS_VERSION 0x0301 /* TLSv1 */ +#define TLS_VERSION_1 0x0301 /* TLSv1 */ +#define TLS_VERSION_1_1 0x0302 /* TLSv1.1 */ +#define TLS_VERSION_1_2 0x0303 /* TLSv1.2 */ +#ifdef CONFIG_TLSV12 +#define TLS_VERSION TLS_VERSION_1_2 +#else /* CONFIG_TLSV12 */ +#ifdef CONFIG_TLSV11 +#define TLS_VERSION TLS_VERSION_1_1 +#else /* CONFIG_TLSV11 */ +#define TLS_VERSION TLS_VERSION_1 +#endif /* CONFIG_TLSV11 */ +#endif /* CONFIG_TLSV12 */ #define TLS_RANDOM_LEN 32 #define TLS_PRE_MASTER_SECRET_LEN 48 #define TLS_MASTER_SECRET_LEN 48 @@ -82,10 +87,42 @@ enum { #define TLS_DHE_DSS_WITH_AES_256_CBC_SHA 0x0038 /* RFC 3268 */ #define TLS_DHE_RSA_WITH_AES_256_CBC_SHA 0x0039 /* RFC 3268 */ #define TLS_DH_anon_WITH_AES_256_CBC_SHA 0x003A /* RFC 3268 */ +#define TLS_RSA_WITH_NULL_SHA256 0x003B /* RFC 5246 */ +#define TLS_RSA_WITH_AES_128_CBC_SHA256 0x003C /* RFC 5246 */ +#define TLS_RSA_WITH_AES_256_CBC_SHA256 0x003D /* RFC 5246 */ +#define TLS_DH_DSS_WITH_AES_128_CBC_SHA256 0x003E /* RFC 5246 */ +#define TLS_DH_RSA_WITH_AES_128_CBC_SHA256 0x003F /* RFC 5246 */ +#define TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 0x0040 /* RFC 5246 */ +#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 0x0067 /* RFC 5246 */ +#define TLS_DH_DSS_WITH_AES_256_CBC_SHA256 0x0068 /* RFC 5246 */ +#define TLS_DH_RSA_WITH_AES_256_CBC_SHA256 0x0069 /* RFC 5246 */ +#define TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 0x006A /* RFC 5246 */ +#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 0x006B /* RFC 5246 */ +#define TLS_DH_anon_WITH_AES_128_CBC_SHA256 0x006C /* RFC 5246 */ +#define TLS_DH_anon_WITH_AES_256_CBC_SHA256 0x006D /* RFC 5246 */ /* CompressionMethod */ #define TLS_COMPRESSION_NULL 0 +/* HashAlgorithm */ +enum { + TLS_HASH_ALG_NONE = 0, + TLS_HASH_ALG_MD5 = 1, + TLS_HASH_ALG_SHA1 = 2, + TLS_HASH_ALG_SHA224 = 3, + TLS_HASH_ALG_SHA256 = 4, + TLS_HASH_ALG_SHA384 = 5, + TLS_HASH_ALG_SHA512 = 6 +}; + +/* SignatureAlgorithm */ +enum { + TLS_SIGN_ALG_ANONYMOUS = 0, + TLS_SIGN_ALG_RSA = 1, + TLS_SIGN_ALG_DSA = 2, + TLS_SIGN_ALG_ECDSA = 3, +}; + /* AlertLevel */ #define TLS_ALERT_LEVEL_WARNING 1 #define TLS_ALERT_LEVEL_FATAL 2 @@ -169,7 +206,8 @@ typedef enum { typedef enum { TLS_HASH_NULL, TLS_HASH_MD5, - TLS_HASH_SHA + TLS_HASH_SHA, + TLS_HASH_SHA256 } tls_hash; struct tls_cipher_suite { @@ -197,10 +235,13 @@ struct tls_cipher_data { struct tls_verify_hash { struct crypto_hash *md5_client; struct crypto_hash *sha1_client; + struct crypto_hash *sha256_client; struct crypto_hash *md5_server; struct crypto_hash *sha1_server; + struct crypto_hash *sha256_server; struct crypto_hash *md5_cert; struct crypto_hash *sha1_cert; + struct crypto_hash *sha256_cert; }; @@ -212,5 +253,9 @@ int tls_verify_hash_init(struct tls_verify_hash *verify); void tls_verify_hash_add(struct tls_verify_hash *verify, const u8 *buf, size_t len); void tls_verify_hash_free(struct tls_verify_hash *verify); +int tls_version_ok(u16 ver); +const char * tls_version_str(u16 ver); +int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label, + const u8 *seed, size_t seed_len, u8 *out, size_t outlen); #endif /* TLSV1_COMMON_H */ diff --git a/src/tls/tlsv1_cred.c b/src/tls/tlsv1_cred.c index aa467efc8c3c2..1ea6827b898ec 100644 --- a/src/tls/tlsv1_cred.c +++ b/src/tls/tlsv1_cred.c @@ -2,14 +2,8 @@ * TLSv1 credentials * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -46,7 +40,7 @@ void tlsv1_cred_free(struct tlsv1_credentials *cred) static int tlsv1_add_cert_der(struct x509_certificate **chain, const u8 *buf, size_t len) { - struct x509_certificate *cert; + struct x509_certificate *cert, *p; char name[128]; cert = x509_certificate_parse(buf, len); @@ -56,8 +50,20 @@ static int tlsv1_add_cert_der(struct x509_certificate **chain, return -1; } - cert->next = *chain; - *chain = cert; + p = *chain; + while (p && p->next) + p = p->next; + if (p && x509_name_compare(&cert->subject, &p->issuer) == 0) { + /* + * The new certificate is the issuer of the last certificate in + * the chain - add the new certificate to the end. + */ + p->next = cert; + } else { + /* Add to the beginning of the chain */ + cert->next = *chain; + *chain = cert; + } x509_name_string(&cert->subject, name, sizeof(name)); wpa_printf(MSG_DEBUG, "TLSv1: Added certificate: %s", name); @@ -232,10 +238,17 @@ static struct crypto_private_key * tlsv1_set_key_pem(const u8 *key, size_t len) if (!end) return NULL; } else { + const u8 *pos2; pos += os_strlen(pem_key_begin); end = search_tag(pem_key_end, pos, key + len - pos); if (!end) return NULL; + pos2 = search_tag("Proc-Type: 4,ENCRYPTED", pos, end - pos); + if (pos2) { + wpa_printf(MSG_DEBUG, "TLSv1: Unsupported private key " + "format (Proc-Type/DEK-Info)"); + return NULL; + } } der = base64_decode(pos, end - pos, &der_len); diff --git a/src/tls/tlsv1_cred.h b/src/tls/tlsv1_cred.h index 8425fe4541266..68fbdc9230084 100644 --- a/src/tls/tlsv1_cred.h +++ b/src/tls/tlsv1_cred.h @@ -2,14 +2,8 @@ * TLSv1 credentials * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TLSV1_CRED_H diff --git a/src/tls/tlsv1_record.c b/src/tls/tlsv1_record.c index e811f0e33b440..3bec3be36f073 100644 --- a/src/tls/tlsv1_record.c +++ b/src/tls/tlsv1_record.c @@ -1,15 +1,9 @@ /* * TLSv1 Record Protocol - * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -17,6 +11,7 @@ #include "common.h" #include "crypto/md5.h" #include "crypto/sha1.h" +#include "crypto/sha256.h" #include "tlsv1_common.h" #include "tlsv1_record.h" @@ -52,6 +47,9 @@ int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl, } else if (suite->hash == TLS_HASH_SHA) { rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA1; rl->hash_size = SHA1_MAC_LEN; + } else if (suite->hash == TLS_HASH_SHA256) { + rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA256; + rl->hash_size = SHA256_MAC_LEN; } data = tls_get_cipher_data(suite->cipher); @@ -138,10 +136,10 @@ int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl) * tlsv1_record_send - TLS record layer: Send a message * @rl: Pointer to TLS record layer data * @content_type: Content type (TLS_CONTENT_TYPE_*) - * @buf: Buffer to send (with TLS_RECORD_HEADER_LEN octets reserved in the - * beginning for record layer to fill in; payload filled in after this and - * extra space in the end for HMAC). + * @buf: Buffer for the generated TLS message (needs to have extra space for + * header, IV (TLS v1.1), and HMAC) * @buf_size: Maximum buf size + * @payload: Payload to be sent * @payload_len: Length of the payload * @out_len: Buffer for returning the used buf length * Returns: 0 on success, -1 on failure @@ -150,29 +148,62 @@ int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl) * the data using the current write cipher. */ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, - size_t buf_size, size_t payload_len, size_t *out_len) + size_t buf_size, const u8 *payload, size_t payload_len, + size_t *out_len) { - u8 *pos, *ct_start, *length, *payload; + u8 *pos, *ct_start, *length, *cpayload; struct crypto_hash *hmac; size_t clen; + int explicit_iv; pos = buf; + if (pos + TLS_RECORD_HEADER_LEN > buf + buf_size) + return -1; + /* ContentType type */ ct_start = pos; *pos++ = content_type; /* ProtocolVersion version */ - WPA_PUT_BE16(pos, TLS_VERSION); + WPA_PUT_BE16(pos, rl->tls_version); pos += 2; /* uint16 length */ length = pos; WPA_PUT_BE16(length, payload_len); pos += 2; - /* opaque fragment[TLSPlaintext.length] */ - payload = pos; + cpayload = pos; + explicit_iv = rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL && + rl->iv_size && rl->tls_version >= TLS_VERSION_1_1; + if (explicit_iv) { + /* opaque IV[Cipherspec.block_length] */ + if (pos + rl->iv_size > buf + buf_size) + return -1; + + /* + * Use random number R per the RFC 4346, 6.2.3.2 CBC Block + * Cipher option 2a. + */ + + if (os_get_random(pos, rl->iv_size)) + return -1; + pos += rl->iv_size; + } + + /* + * opaque fragment[TLSPlaintext.length] + * (opaque content[TLSCompressed.length] in GenericBlockCipher) + */ + if (pos + payload_len > buf + buf_size) + return -1; + os_memmove(pos, payload, payload_len); pos += payload_len; if (rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL) { + /* + * MAC calculated over seq_num + TLSCompressed.type + + * TLSCompressed.version + TLSCompressed.length + + * TLSCompressed.fragment + */ hmac = crypto_hash_init(rl->hash_alg, rl->write_mac_secret, rl->hash_size); if (hmac == NULL) { @@ -182,7 +213,8 @@ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, } crypto_hash_update(hmac, rl->write_seq_num, TLS_SEQ_NUM_LEN); /* type + version + length + fragment */ - crypto_hash_update(hmac, ct_start, pos - ct_start); + crypto_hash_update(hmac, ct_start, TLS_RECORD_HEADER_LEN); + crypto_hash_update(hmac, payload, payload_len); clen = buf + buf_size - pos; if (clen < rl->hash_size) { wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Not " @@ -200,7 +232,7 @@ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, pos, clen); pos += clen; if (rl->iv_size) { - size_t len = pos - payload; + size_t len = pos - cpayload; size_t pad; pad = (len + 1) % rl->iv_size; if (pad) @@ -214,8 +246,8 @@ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, pos += pad + 1; } - if (crypto_cipher_encrypt(rl->write_cbc, payload, - payload, pos - payload) < 0) + if (crypto_cipher_encrypt(rl->write_cbc, cpayload, + cpayload, pos - cpayload) < 0) return -1; } @@ -237,7 +269,8 @@ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, * @out_len: Set to maximum out_data length by caller; used to return the * length of the used data * @alert: Buffer for returning an alert value on failure - * Returns: 0 on success, -1 on failure + * Returns: Number of bytes used from in_data on success, 0 if record was not + * complete (more data needed), or -1 on failure * * This function decrypts the received message, verifies HMAC and TLS record * layer header. @@ -250,40 +283,35 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl, u8 padlen; struct crypto_hash *hmac; u8 len[2], hash[100]; - - wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received", - in_data, in_len); + int force_mac_error = 0; + u8 ct; if (in_len < TLS_RECORD_HEADER_LEN) { - wpa_printf(MSG_DEBUG, "TLSv1: Too short record (in_len=%lu)", + wpa_printf(MSG_DEBUG, "TLSv1: Too short record (in_len=%lu) - " + "need more data", (unsigned long) in_len); - *alert = TLS_ALERT_DECODE_ERROR; - return -1; + wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received", + in_data, in_len); + return 0; } + ct = in_data[0]; + rlen = WPA_GET_BE16(in_data + 3); wpa_printf(MSG_DEBUG, "TLSv1: Received content type %d version %d.%d " - "length %d", in_data[0], in_data[1], in_data[2], - WPA_GET_BE16(in_data + 3)); - - if (in_data[0] != TLS_CONTENT_TYPE_HANDSHAKE && - in_data[0] != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC && - in_data[0] != TLS_CONTENT_TYPE_ALERT && - in_data[0] != TLS_CONTENT_TYPE_APPLICATION_DATA) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type 0x%x", - in_data[0]); - *alert = TLS_ALERT_UNEXPECTED_MESSAGE; - return -1; - } - - if (WPA_GET_BE16(in_data + 1) != TLS_VERSION) { + "length %d", ct, in_data[1], in_data[2], (int) rlen); + + /* + * TLS v1.0 and v1.1 RFCs were not exactly clear on the use of the + * protocol version in record layer. As such, accept any {03,xx} value + * to remain compatible with existing implementations. + */ + if (in_data[1] != 0x03) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version " - "%d.%d", in_data[1], in_data[2]); + "%u.%u", in_data[1], in_data[2]); *alert = TLS_ALERT_PROTOCOL_VERSION; return -1; } - rlen = WPA_GET_BE16(in_data + 3); - /* TLSCiphertext must not be more than 2^14+2048 bytes */ if (TLS_RECORD_HEADER_LEN + rlen > 18432) { wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)", @@ -299,7 +327,19 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl, wpa_printf(MSG_DEBUG, "TLSv1: Not all record data included " "(rlen=%lu > in_len=%lu)", (unsigned long) rlen, (unsigned long) in_len); - *alert = TLS_ALERT_DECODE_ERROR; + return 0; + } + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received", + in_data, rlen); + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE && + ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC && + ct != TLS_CONTENT_TYPE_ALERT && + ct != TLS_CONTENT_TYPE_APPLICATION_DATA) { + wpa_printf(MSG_DEBUG, "TLSv1: Ignore record with unknown " + "content type 0x%x", ct); + *alert = TLS_ALERT_UNEXPECTED_MESSAGE; return -1; } @@ -312,58 +352,86 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl, return -1; } - os_memcpy(out_data, in_data, in_len); - *out_len = in_len; - if (rl->read_cipher_suite != TLS_NULL_WITH_NULL_NULL) { - if (crypto_cipher_decrypt(rl->read_cbc, out_data, + size_t plen; + if (crypto_cipher_decrypt(rl->read_cbc, in_data, out_data, in_len) < 0) { *alert = TLS_ALERT_DECRYPTION_FAILED; return -1; } + plen = in_len; + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - Decrypted " + "data", out_data, plen); + if (rl->iv_size) { - if (in_len == 0) { + /* + * TLS v1.0 defines different alert values for various + * failures. That may information to aid in attacks, so + * use the same bad_record_mac alert regardless of the + * issues. + * + * In addition, instead of returning immediately on + * error, run through the MAC check to make timing + * attacks more difficult. + */ + + if (rl->tls_version >= TLS_VERSION_1_1) { + /* Remove opaque IV[Cipherspec.block_length] */ + if (plen < rl->iv_size) { + wpa_printf(MSG_DEBUG, "TLSv1.1: Not " + "enough room for IV"); + force_mac_error = 1; + goto check_mac; + } + os_memmove(out_data, out_data + rl->iv_size, + plen - rl->iv_size); + plen -= rl->iv_size; + } + + /* Verify and remove padding */ + if (plen == 0) { wpa_printf(MSG_DEBUG, "TLSv1: Too short record" " (no pad)"); - *alert = TLS_ALERT_DECODE_ERROR; - return -1; + force_mac_error = 1; + goto check_mac; } - padlen = out_data[in_len - 1]; - if (padlen >= in_len) { + padlen = out_data[plen - 1]; + if (padlen >= plen) { wpa_printf(MSG_DEBUG, "TLSv1: Incorrect pad " - "length (%u, in_len=%lu) in " + "length (%u, plen=%lu) in " "received record", - padlen, (unsigned long) in_len); - *alert = TLS_ALERT_DECRYPTION_FAILED; - return -1; + padlen, (unsigned long) plen); + force_mac_error = 1; + goto check_mac; } - for (i = in_len - padlen; i < in_len; i++) { + for (i = plen - padlen - 1; i < plen - 1; i++) { if (out_data[i] != padlen) { wpa_hexdump(MSG_DEBUG, "TLSv1: Invalid pad in " "received record", - out_data + in_len - padlen, - padlen); - *alert = TLS_ALERT_DECRYPTION_FAILED; - return -1; + out_data + plen - padlen - + 1, padlen + 1); + force_mac_error = 1; + goto check_mac; } } - *out_len -= padlen + 1; - } + plen -= padlen + 1; - wpa_hexdump(MSG_MSGDUMP, - "TLSv1: Record Layer - Decrypted data", - out_data, in_len); + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - " + "Decrypted data with IV and padding " + "removed", out_data, plen); + } - if (*out_len < rl->hash_size) { + check_mac: + if (plen < rl->hash_size) { wpa_printf(MSG_DEBUG, "TLSv1: Too short record; no " "hash value"); - *alert = TLS_ALERT_INTERNAL_ERROR; + *alert = TLS_ALERT_BAD_RECORD_MAC; return -1; } - *out_len -= rl->hash_size; + plen -= rl->hash_size; hmac = crypto_hash_init(rl->hash_alg, rl->read_mac_secret, rl->hash_size); @@ -377,22 +445,30 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl, crypto_hash_update(hmac, rl->read_seq_num, TLS_SEQ_NUM_LEN); /* type + version + length + fragment */ crypto_hash_update(hmac, in_data - TLS_RECORD_HEADER_LEN, 3); - WPA_PUT_BE16(len, *out_len); + WPA_PUT_BE16(len, plen); crypto_hash_update(hmac, len, 2); - crypto_hash_update(hmac, out_data, *out_len); + crypto_hash_update(hmac, out_data, plen); hlen = sizeof(hash); if (crypto_hash_finish(hmac, hash, &hlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " "to calculate HMAC"); + *alert = TLS_ALERT_INTERNAL_ERROR; return -1; } if (hlen != rl->hash_size || - os_memcmp(hash, out_data + *out_len, hlen) != 0) { + os_memcmp(hash, out_data + plen, hlen) != 0 || + force_mac_error) { wpa_printf(MSG_DEBUG, "TLSv1: Invalid HMAC value in " - "received message"); + "received message (force_mac_error=%d)", + force_mac_error); *alert = TLS_ALERT_BAD_RECORD_MAC; return -1; } + + *out_len = plen; + } else { + os_memcpy(out_data, in_data, in_len); + *out_len = in_len; } /* TLSCompressed must not be more than 2^14+1024 bytes */ @@ -405,5 +481,5 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl, inc_byte_array(rl->read_seq_num, TLS_SEQ_NUM_LEN); - return 0; + return TLS_RECORD_HEADER_LEN + rlen; } diff --git a/src/tls/tlsv1_record.h b/src/tls/tlsv1_record.h index 9c7c0a4e644e2..48abcb0d25c18 100644 --- a/src/tls/tlsv1_record.h +++ b/src/tls/tlsv1_record.h @@ -1,15 +1,9 @@ /* * TLSv1 Record Protocol - * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TLSV1_RECORD_H @@ -17,7 +11,7 @@ #include "crypto/crypto.h" -#define TLS_MAX_WRITE_MAC_SECRET_LEN 20 +#define TLS_MAX_WRITE_MAC_SECRET_LEN 32 #define TLS_MAX_WRITE_KEY_LEN 32 #define TLS_MAX_IV_LEN 16 #define TLS_MAX_KEY_BLOCK_LEN (2 * (TLS_MAX_WRITE_MAC_SECRET_LEN + \ @@ -35,6 +29,8 @@ enum { }; struct tlsv1_record_layer { + u16 tls_version; + u8 write_mac_secret[TLS_MAX_WRITE_MAC_SECRET_LEN]; u8 read_mac_secret[TLS_MAX_WRITE_MAC_SECRET_LEN]; u8 write_key[TLS_MAX_WRITE_KEY_LEN]; @@ -66,7 +62,8 @@ int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl, int tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl); int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl); int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, - size_t buf_size, size_t payload_len, size_t *out_len); + size_t buf_size, const u8 *payload, size_t payload_len, + size_t *out_len); int tlsv1_record_receive(struct tlsv1_record_layer *rl, const u8 *in_data, size_t in_len, u8 *out_data, size_t *out_len, u8 *alert); diff --git a/src/tls/tlsv1_server.c b/src/tls/tlsv1_server.c index 6a6123564553c..2880309ebf515 100644 --- a/src/tls/tlsv1_server.c +++ b/src/tls/tlsv1_server.c @@ -1,15 +1,9 @@ /* - * TLSv1 server (RFC 2246) - * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246) + * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -49,7 +43,8 @@ int tlsv1_server_derive_keys(struct tlsv1_server *conn, os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN); os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random, TLS_RANDOM_LEN); - if (tls_prf(pre_master_secret, pre_master_secret_len, + if (tls_prf(conn->rl.tls_version, + pre_master_secret, pre_master_secret_len, "master secret", seed, 2 * TLS_RANDOM_LEN, conn->master_secret, TLS_MASTER_SECRET_LEN)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive " @@ -64,7 +59,8 @@ int tlsv1_server_derive_keys(struct tlsv1_server *conn, os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN); key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len + conn->rl.iv_size); - if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, + if (tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, "key expansion", seed, 2 * TLS_RANDOM_LEN, key_block, key_block_len)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block"); @@ -115,6 +111,7 @@ u8 * tlsv1_server_handshake(struct tlsv1_server *conn, const u8 *pos, *end; u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct; size_t in_msg_len; + int used; if (in_data == NULL || in_len == 0) { wpa_printf(MSG_DEBUG, "TLSv1: No input data to server"); @@ -130,13 +127,21 @@ u8 * tlsv1_server_handshake(struct tlsv1_server *conn, /* Each received packet may include multiple records */ while (pos < end) { in_msg_len = in_len; - if (tlsv1_record_receive(&conn->rl, pos, end - pos, - in_msg, &in_msg_len, &alert)) { + used = tlsv1_record_receive(&conn->rl, pos, end - pos, + in_msg, &in_msg_len, &alert); + if (used < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Processing received " "record failed"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); goto failed; } + if (used == 0) { + /* need more data */ + wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not " + "yet supported"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); + goto failed; + } ct = pos[0]; in_pos = in_msg; @@ -152,7 +157,7 @@ u8 * tlsv1_server_handshake(struct tlsv1_server *conn, in_pos += in_msg_len; } - pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3); + pos += used; } os_free(in_msg); @@ -201,10 +206,8 @@ int tlsv1_server_encrypt(struct tlsv1_server *conn, wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData", in_data, in_len); - os_memcpy(out_data + TLS_RECORD_HEADER_LEN, in_data, in_len); - if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA, - out_data, out_len, in_len, &rlen) < 0) { + out_data, out_len, in_data, in_len, &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); @@ -232,8 +235,8 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn, u8 *out_data, size_t out_len) { const u8 *in_end, *pos; - int res; - u8 alert, *out_end, *out_pos; + int used; + u8 alert, *out_end, *out_pos, ct; size_t olen; pos = in_data; @@ -242,7 +245,46 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn, out_end = out_data + out_len; while (pos < in_end) { - if (pos[0] != TLS_CONTENT_TYPE_APPLICATION_DATA) { + ct = pos[0]; + olen = out_end - out_pos; + used = tlsv1_record_receive(&conn->rl, pos, in_end - pos, + out_pos, &olen, &alert); + if (used < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing " + "failed"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); + return -1; + } + if (used == 0) { + /* need more data */ + wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not " + "yet supported"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); + return -1; + } + + if (ct == TLS_CONTENT_TYPE_ALERT) { + if (olen < 2) { + wpa_printf(MSG_DEBUG, "TLSv1: Alert " + "underflow"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d", + out_pos[0], out_pos[1]); + if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) { + /* Continue processing */ + pos += used; + continue; + } + + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + out_pos[1]); + return -1; + } + + if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type " "0x%x", pos[0]); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, @@ -250,15 +292,6 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn, return -1; } - olen = out_end - out_pos; - res = tlsv1_record_receive(&conn->rl, pos, in_end - pos, - out_pos, &olen, &alert); - if (res < 0) { - wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing " - "failed"); - tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); - return -1; - } out_pos += olen; if (out_pos > out_end) { wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough " @@ -268,7 +301,7 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn, return -1; } - pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3); + pos += used; } return out_pos - out_data; @@ -328,9 +361,7 @@ struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred) count = 0; suites = conn->cipher_suites; -#ifndef CONFIG_CRYPTO_INTERNAL suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA; -#endif /* CONFIG_CRYPTO_INTERNAL */ suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA; suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA; suites[count++] = TLS_RSA_WITH_RC4_128_SHA; @@ -412,7 +443,8 @@ int tlsv1_server_prf(struct tlsv1_server *conn, const char *label, TLS_RANDOM_LEN); } - return tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, + return tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, label, seed, 2 * TLS_RANDOM_LEN, out, out_len); } @@ -553,16 +585,12 @@ int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers) if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) { count = 0; suites = conn->cipher_suites; -#ifndef CONFIG_CRYPTO_INTERNAL suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA; -#endif /* CONFIG_CRYPTO_INTERNAL */ suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA; suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA; suites[count++] = TLS_RSA_WITH_RC4_128_SHA; suites[count++] = TLS_RSA_WITH_RC4_128_MD5; -#ifndef CONFIG_CRYPTO_INTERNAL suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA; -#endif /* CONFIG_CRYPTO_INTERNAL */ suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA; suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA; suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5; diff --git a/src/tls/tlsv1_server.h b/src/tls/tlsv1_server.h index 00c536c3ee2b8..a18c69e37c16a 100644 --- a/src/tls/tlsv1_server.h +++ b/src/tls/tlsv1_server.h @@ -1,15 +1,9 @@ /* - * TLSv1 server (RFC 2246) - * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246) + * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TLSV1_SERVER_H diff --git a/src/tls/tlsv1_server_i.h b/src/tls/tlsv1_server_i.h index d11ea7559f3fe..1f61533a5aa07 100644 --- a/src/tls/tlsv1_server_i.h +++ b/src/tls/tlsv1_server_i.h @@ -2,14 +2,8 @@ * TLSv1 server - internal structures * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TLSV1_SERVER_I_H diff --git a/src/tls/tlsv1_server_read.c b/src/tls/tlsv1_server_read.c index 49e811ffcff51..6f6539b1bfb5d 100644 --- a/src/tls/tlsv1_server_read.c +++ b/src/tls/tlsv1_server_read.c @@ -1,15 +1,9 @@ /* * TLSv1 server - read handshake message - * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -17,6 +11,7 @@ #include "common.h" #include "crypto/md5.h" #include "crypto/sha1.h" +#include "crypto/sha256.h" #include "crypto/tls.h" #include "x509v3.h" #include "tlsv1_common.h" @@ -85,15 +80,30 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, conn->client_version = WPA_GET_BE16(pos); wpa_printf(MSG_DEBUG, "TLSv1: Client version %d.%d", conn->client_version >> 8, conn->client_version & 0xff); - if (conn->client_version < TLS_VERSION) { + if (conn->client_version < TLS_VERSION_1) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in " - "ClientHello"); + "ClientHello %u.%u", + conn->client_version >> 8, + conn->client_version & 0xff); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_PROTOCOL_VERSION); return -1; } pos += 2; + if (TLS_VERSION == TLS_VERSION_1) + conn->rl.tls_version = TLS_VERSION_1; +#ifdef CONFIG_TLSV12 + else if (conn->client_version >= TLS_VERSION_1_2) + conn->rl.tls_version = TLS_VERSION_1_2; +#endif /* CONFIG_TLSV12 */ + else if (conn->client_version > TLS_VERSION_1_1) + conn->rl.tls_version = TLS_VERSION_1_1; + else + conn->rl.tls_version = conn->client_version; + wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s", + tls_version_str(conn->rl.tls_version)); + /* Random random */ if (end - pos < TLS_RANDOM_LEN) goto decode_error; @@ -424,7 +434,7 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, } if (x509_certificate_chain_validate(conn->cred->trusted_certs, chain, - &reason) < 0) { + &reason, 0) < 0) { int tls_reason; wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain " "validation failed (reason=%d)", reason); @@ -483,6 +493,14 @@ static int tls_process_client_key_exchange_rsa( encr_len = WPA_GET_BE16(pos); pos += 2; + if (pos + encr_len > end) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid ClientKeyExchange " + "format: encr_len=%u left=%u", + encr_len, (unsigned int) (end - pos)); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } outbuflen = outlen = end - pos; out = os_malloc(outlen >= TLS_PRE_MASTER_SECRET_LEN ? @@ -512,21 +530,21 @@ static int tls_process_client_key_exchange_rsa( */ if (crypto_private_key_decrypt_pkcs1_v15(conn->cred->key, - pos, end - pos, + pos, encr_len, out, &outlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt " - "PreMasterSecret (encr_len=%d outlen=%lu)", - (int) (end - pos), (unsigned long) outlen); + "PreMasterSecret (encr_len=%u outlen=%lu)", + encr_len, (unsigned long) outlen); use_random = 1; } - if (outlen != TLS_PRE_MASTER_SECRET_LEN) { + if (!use_random && outlen != TLS_PRE_MASTER_SECRET_LEN) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected PreMasterSecret " "length %lu", (unsigned long) outlen); use_random = 1; } - if (WPA_GET_BE16(out) != conn->client_version) { + if (!use_random && WPA_GET_BE16(out) != conn->client_version) { wpa_printf(MSG_DEBUG, "TLSv1: Client version in " "ClientKeyExchange does not match with version in " "ClientHello"); @@ -822,6 +840,47 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, hpos = hash; +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version == TLS_VERSION_1_2) { + /* + * RFC 5246, 4.7: + * TLS v1.2 adds explicit indication of the used signature and + * hash algorithms. + * + * struct { + * HashAlgorithm hash; + * SignatureAlgorithm signature; + * } SignatureAndHashAlgorithm; + */ + if (end - pos < 2) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + if (pos[0] != TLS_HASH_ALG_SHA256 || + pos[1] != TLS_SIGN_ALG_RSA) { + wpa_printf(MSG_DEBUG, "TLSv1.2: Unsupported hash(%u)/" + "signature(%u) algorithm", + pos[0], pos[1]); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + pos += 2; + + hlen = SHA256_MAC_LEN; + if (conn->verify.sha256_cert == NULL || + crypto_hash_finish(conn->verify.sha256_cert, hpos, &hlen) < + 0) { + conn->verify.sha256_cert = NULL; + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + conn->verify.sha256_cert = NULL; + } else { +#endif /* CONFIG_TLSV12 */ + if (alg == SIGN_ALG_RSA) { hlen = MD5_MAC_LEN; if (conn->verify.md5_cert == NULL || @@ -852,6 +911,10 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, if (alg == SIGN_ALG_RSA) hlen += MD5_MAC_LEN; +#ifdef CONFIG_TLSV12 + } +#endif /* CONFIG_TLSV12 */ + wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen); if (end - pos < 2) { @@ -891,6 +954,41 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Decrypted Signature", buf, buflen); +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version >= TLS_VERSION_1_2) { + /* + * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5 + * + * DigestInfo ::= SEQUENCE { + * digestAlgorithm DigestAlgorithm, + * digest OCTET STRING + * } + * + * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11} + * + * DER encoded DigestInfo for SHA256 per RFC 3447: + * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || + * H + */ + if (buflen >= 19 + 32 && + os_memcmp(buf, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01" + "\x65\x03\x04\x02\x01\x05\x00\x04\x20", 19) == 0) + { + wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = " + "SHA-256"); + os_memmove(buf, buf + 19, buflen - 19); + buflen -= 19; + } else { + wpa_printf(MSG_DEBUG, "TLSv1.2: Unrecognized " + "DigestInfo"); + os_free(buf); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECRYPT_ERROR); + return -1; + } + } +#endif /* CONFIG_TLSV12 */ + if (buflen != hlen || os_memcmp(buf, hash, buflen) != 0) { wpa_printf(MSG_DEBUG, "TLSv1: Invalid Signature in " "CertificateVerify - did not match with calculated " @@ -1022,6 +1120,21 @@ static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct, wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished", pos, TLS_VERIFY_DATA_LEN); +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version >= TLS_VERSION_1_2) { + hlen = SHA256_MAC_LEN; + if (conn->verify.sha256_client == NULL || + crypto_hash_finish(conn->verify.sha256_client, hash, &hlen) + < 0) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.sha256_client = NULL; + return -1; + } + conn->verify.sha256_client = NULL; + } else { +#endif /* CONFIG_TLSV12 */ + hlen = MD5_MAC_LEN; if (conn->verify.md5_client == NULL || crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) { @@ -1043,9 +1156,15 @@ static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct, return -1; } conn->verify.sha1_client = NULL; + hlen = MD5_MAC_LEN + SHA1_MAC_LEN; + +#ifdef CONFIG_TLSV12 + } +#endif /* CONFIG_TLSV12 */ - if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, - "client finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN, + if (tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + "client finished", hash, hlen, verify_data, TLS_VERIFY_DATA_LEN)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive verify_data"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, diff --git a/src/tls/tlsv1_server_write.c b/src/tls/tlsv1_server_write.c index 6d1df7ff34f4d..6d8e55ed49a09 100644 --- a/src/tls/tlsv1_server_write.c +++ b/src/tls/tlsv1_server_write.c @@ -1,15 +1,9 @@ /* * TLSv1 server - write handshake message - * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -17,7 +11,9 @@ #include "common.h" #include "crypto/md5.h" #include "crypto/sha1.h" +#include "crypto/sha256.h" #include "crypto/tls.h" +#include "crypto/random.h" #include "x509v3.h" #include "tlsv1_common.h" #include "tlsv1_record.h" @@ -58,7 +54,7 @@ static int tls_write_server_hello(struct tlsv1_server *conn, os_get_time(&now); WPA_PUT_BE32(conn->server_random, now.sec); - if (os_get_random(conn->server_random + 4, TLS_RANDOM_LEN - 4)) { + if (random_get_bytes(conn->server_random + 4, TLS_RANDOM_LEN - 4)) { wpa_printf(MSG_ERROR, "TLSv1: Could not generate " "server_random"); return -1; @@ -67,7 +63,7 @@ static int tls_write_server_hello(struct tlsv1_server *conn, conn->server_random, TLS_RANDOM_LEN); conn->session_id_len = TLS_SESSION_ID_MAX_LEN; - if (os_get_random(conn->session_id, conn->session_id_len)) { + if (random_get_bytes(conn->session_id, conn->session_id_len)) { wpa_printf(MSG_ERROR, "TLSv1: Could not generate " "session_id"); return -1; @@ -86,7 +82,7 @@ static int tls_write_server_hello(struct tlsv1_server *conn, pos += 3; /* body - ServerHello */ /* ProtocolVersion server_version */ - WPA_PUT_BE16(pos, TLS_VERSION); + WPA_PUT_BE16(pos, conn->rl.tls_version); pos += 2; /* Random random: uint32 gmt_unix_time, opaque random_bytes */ os_memcpy(pos, conn->server_random, TLS_RANDOM_LEN); @@ -142,7 +138,8 @@ static int tls_write_server_hello(struct tlsv1_server *conn, tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, - rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { + rhdr, end - rhdr, hs_start, pos - hs_start, + &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); @@ -226,7 +223,8 @@ static int tls_write_server_certificate(struct tlsv1_server *conn, WPA_PUT_BE24(hs_length, pos - hs_length - 3); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, - rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { + rhdr, end - rhdr, hs_start, pos - hs_start, + &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); @@ -287,7 +285,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, TLS_ALERT_INTERNAL_ERROR); return -1; } - if (os_get_random(conn->dh_secret, conn->dh_secret_len)) { + if (random_get_bytes(conn->dh_secret, conn->dh_secret_len)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random " "data for Diffie-Hellman"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, @@ -417,7 +415,8 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, WPA_PUT_BE24(hs_length, pos - hs_length - 3); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, - rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { + rhdr, end - rhdr, hs_start, pos - hs_start, + &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); @@ -482,7 +481,8 @@ static int tls_write_server_certificate_request(struct tlsv1_server *conn, WPA_PUT_BE24(hs_length, pos - hs_length - 3); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, - rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { + rhdr, end - rhdr, hs_start, pos - hs_start, + &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); @@ -501,40 +501,35 @@ static int tls_write_server_certificate_request(struct tlsv1_server *conn, static int tls_write_server_hello_done(struct tlsv1_server *conn, u8 **msgpos, u8 *end) { - u8 *pos, *rhdr, *hs_start, *hs_length; + u8 *pos; size_t rlen; - - pos = *msgpos; + u8 payload[4]; wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHelloDone"); - rhdr = pos; - pos += TLS_RECORD_HEADER_LEN; /* opaque fragment[TLSPlaintext.length] */ /* Handshake */ - hs_start = pos; + pos = payload; /* HandshakeType msg_type */ *pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE; - /* uint24 length (to be filled) */ - hs_length = pos; + /* uint24 length */ + WPA_PUT_BE24(pos, 0); pos += 3; /* body - ServerHelloDone (empty) */ - WPA_PUT_BE24(hs_length, pos - hs_length - 3); - if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, - rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { + *msgpos, end - *msgpos, payload, pos - payload, + &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } - pos = rhdr + rlen; - tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + tls_verify_hash_add(&conn->verify, payload, pos - payload); - *msgpos = pos; + *msgpos += rlen; return 0; } @@ -543,17 +538,16 @@ static int tls_write_server_hello_done(struct tlsv1_server *conn, static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn, u8 **msgpos, u8 *end) { - u8 *pos, *rhdr; size_t rlen; - - pos = *msgpos; + u8 payload[1]; wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec"); - rhdr = pos; - pos += TLS_RECORD_HEADER_LEN; - *pos = TLS_CHANGE_CIPHER_SPEC; + + payload[0] = TLS_CHANGE_CIPHER_SPEC; + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC, - rhdr, end - rhdr, 1, &rlen) < 0) { + *msgpos, end - *msgpos, payload, sizeof(payload), + &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); @@ -568,7 +562,7 @@ static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn, return -1; } - *msgpos = rhdr + rlen; + *msgpos += rlen; return 0; } @@ -577,9 +571,9 @@ static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn, static int tls_write_server_finished(struct tlsv1_server *conn, u8 **msgpos, u8 *end) { - u8 *pos, *rhdr, *hs_start, *hs_length; + u8 *pos, *hs_start; size_t rlen, hlen; - u8 verify_data[TLS_VERIFY_DATA_LEN]; + u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN]; u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; pos = *msgpos; @@ -588,6 +582,21 @@ static int tls_write_server_finished(struct tlsv1_server *conn, /* Encrypted Handshake Message: Finished */ +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version >= TLS_VERSION_1_2) { + hlen = SHA256_MAC_LEN; + if (conn->verify.sha256_server == NULL || + crypto_hash_finish(conn->verify.sha256_server, hash, &hlen) + < 0) { + conn->verify.sha256_server = NULL; + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + conn->verify.sha256_server = NULL; + } else { +#endif /* CONFIG_TLSV12 */ + hlen = MD5_MAC_LEN; if (conn->verify.md5_server == NULL || crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) { @@ -609,43 +618,44 @@ static int tls_write_server_finished(struct tlsv1_server *conn, return -1; } conn->verify.sha1_server = NULL; + hlen = MD5_MAC_LEN + SHA1_MAC_LEN; + +#ifdef CONFIG_TLSV12 + } +#endif /* CONFIG_TLSV12 */ - if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, - "server finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN, - verify_data, TLS_VERIFY_DATA_LEN)) { + if (tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + "server finished", hash, hlen, + verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)", - verify_data, TLS_VERIFY_DATA_LEN); + verify_data + 1 + 3, TLS_VERIFY_DATA_LEN); - rhdr = pos; - pos += TLS_RECORD_HEADER_LEN; /* Handshake */ - hs_start = pos; + pos = hs_start = verify_data; /* HandshakeType msg_type */ *pos++ = TLS_HANDSHAKE_TYPE_FINISHED; - /* uint24 length (to be filled) */ - hs_length = pos; + /* uint24 length */ + WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN); pos += 3; - os_memcpy(pos, verify_data, TLS_VERIFY_DATA_LEN); pos += TLS_VERIFY_DATA_LEN; - WPA_PUT_BE24(hs_length, pos - hs_length - 3); tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, - rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { + *msgpos, end - *msgpos, hs_start, pos - hs_start, + &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } - pos = rhdr + rlen; - - *msgpos = pos; + *msgpos += rlen; return 0; } @@ -770,7 +780,8 @@ u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level, /* ContentType type */ *pos++ = TLS_CONTENT_TYPE_ALERT; /* ProtocolVersion version */ - WPA_PUT_BE16(pos, TLS_VERSION); + WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version : + TLS_VERSION); pos += 2; /* uint16 length (to be filled) */ length = pos; diff --git a/src/tls/x509v3.c b/src/tls/x509v3.c index bc93df6837873..87c51784ea719 100644 --- a/src/tls/x509v3.c +++ b/src/tls/x509v3.c @@ -1,15 +1,9 @@ /* * X.509v3 certificate parsing and processing (RFC 3280 profile) - * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -1834,7 +1828,7 @@ static int x509_valid_issuer(const struct x509_certificate *cert) */ int x509_certificate_chain_validate(struct x509_certificate *trusted, struct x509_certificate *chain, - int *reason) + int *reason, int disable_time_checks) { long unsigned idx; int chain_trusted = 0; @@ -1854,10 +1848,11 @@ int x509_certificate_chain_validate(struct x509_certificate *trusted, if (chain_trusted) continue; - if ((unsigned long) now.sec < - (unsigned long) cert->not_before || - (unsigned long) now.sec > - (unsigned long) cert->not_after) { + if (!disable_time_checks && + ((unsigned long) now.sec < + (unsigned long) cert->not_before || + (unsigned long) now.sec > + (unsigned long) cert->not_after)) { wpa_printf(MSG_INFO, "X509: Certificate not valid " "(now=%lu not_before=%lu not_after=%lu)", now.sec, cert->not_before, cert->not_after); diff --git a/src/tls/x509v3.h b/src/tls/x509v3.h index 37292d7e7dec9..91a35baf92b18 100644 --- a/src/tls/x509v3.h +++ b/src/tls/x509v3.h @@ -1,15 +1,9 @@ /* * X.509v3 certificate parsing and processing - * Copyright (c) 2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef X509V3_H @@ -120,7 +114,7 @@ int x509_certificate_check_signature(struct x509_certificate *issuer, struct x509_certificate *cert); int x509_certificate_chain_validate(struct x509_certificate *trusted, struct x509_certificate *chain, - int *reason); + int *reason, int disable_time_checks); struct x509_certificate * x509_certificate_get_subject(struct x509_certificate *chain, struct x509_name *name); diff --git a/src/utils/Makefile b/src/utils/Makefile index 527cf3e73bb8b..0f1f191099953 100644 --- a/src/utils/Makefile +++ b/src/utils/Makefile @@ -28,6 +28,9 @@ LIB_OBJS += os_unix.o # Pick correct event loop implementation LIB_OBJS += eloop.o +# Pick correct edit implementation +LIB_OBJS += edit.o + #LIB_OBJS += pcsc_funcs.o libutils.a: $(LIB_OBJS) diff --git a/src/utils/base64.c b/src/utils/base64.c index 155bfce83aa57..af1307fc4e650 100644 --- a/src/utils/base64.c +++ b/src/utils/base64.c @@ -1,15 +1,9 @@ /* * Base64 encoding/decoding (RFC1341) - * Copyright (c) 2005, Jouni Malinen <j@w1.fi> + * Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -103,8 +97,9 @@ unsigned char * base64_encode(const unsigned char *src, size_t len, unsigned char * base64_decode(const unsigned char *src, size_t len, size_t *out_len) { - unsigned char dtable[256], *out, *pos, in[4], block[4], tmp; + unsigned char dtable[256], *out, *pos, block[4], tmp; size_t i, count, olen; + int pad = 0; os_memset(dtable, 0x80, 256); for (i = 0; i < sizeof(base64_table) - 1; i++) @@ -131,7 +126,8 @@ unsigned char * base64_decode(const unsigned char *src, size_t len, if (tmp == 0x80) continue; - in[count] = src[i]; + if (src[i] == '=') + pad++; block[count] = tmp; count++; if (count == 4) { @@ -139,16 +135,21 @@ unsigned char * base64_decode(const unsigned char *src, size_t len, *pos++ = (block[1] << 4) | (block[2] >> 2); *pos++ = (block[2] << 6) | block[3]; count = 0; + if (pad) { + if (pad == 1) + pos--; + else if (pad == 2) + pos -= 2; + else { + /* Invalid padding */ + os_free(out); + return NULL; + } + break; + } } } - if (pos > out) { - if (in[2] == '=') - pos -= 2; - else if (in[3] == '=') - pos--; - } - *out_len = pos - out; return out; } diff --git a/src/utils/base64.h b/src/utils/base64.h index 73312dde8f647..aa21fd0fc1b76 100644 --- a/src/utils/base64.h +++ b/src/utils/base64.h @@ -2,18 +2,12 @@ * Base64 encoding/decoding (RFC1341) * Copyright (c) 2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef BASE64_H -#define BASE64_h +#define BASE64_H unsigned char * base64_encode(const unsigned char *src, size_t len, size_t *out_len); diff --git a/src/utils/build_config.h b/src/utils/build_config.h index 366677849231f..f947388552dbe 100644 --- a/src/utils/build_config.h +++ b/src/utils/build_config.h @@ -2,14 +2,8 @@ * wpa_supplicant/hostapd - Build time configuration defines * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This header file can be used to define configuration defines that were * originally defined in Makefile. This is mainly meant for IDE use or for @@ -53,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.c b/src/utils/common.c index 1b8ea80e7d05c..e63698491444b 100644 --- a/src/utils/common.c +++ b/src/utils/common.c @@ -2,14 +2,8 @@ * wpa_supplicant/hostapd / common helper functions, etc. * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -29,7 +23,7 @@ static int hex2num(char c) } -static int hex2byte(const char *hex) +int hex2byte(const char *hex) { int a, b; a = hex2num(*hex++); @@ -69,6 +63,30 @@ int hwaddr_aton(const char *txt, u8 *addr) return 0; } +/** + * hwaddr_compact_aton - Convert ASCII string to MAC address (no colon delimitors format) + * @txt: MAC address as a string (e.g., "001122334455") + * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes) + * Returns: 0 on success, -1 on failure (e.g., string not a MAC address) + */ +int hwaddr_compact_aton(const char *txt, u8 *addr) +{ + int i; + + for (i = 0; i < 6; i++) { + int a, b; + + a = hex2num(*txt++); + if (a < 0) + return -1; + b = hex2num(*txt++); + if (b < 0) + return -1; + *addr++ = (a << 4) | b; + } + + return 0; +} /** * hwaddr_aton2 - Convert ASCII string to MAC address (in any known format) @@ -326,6 +344,135 @@ TCHAR * wpa_strdup_tchar(const char *str) #endif /* CONFIG_NATIVE_WINDOWS */ +void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len) +{ + char *end = txt + maxlen; + size_t i; + + for (i = 0; i < len; i++) { + if (txt + 4 > end) + break; + + switch (data[i]) { + case '\"': + *txt++ = '\\'; + *txt++ = '\"'; + break; + case '\\': + *txt++ = '\\'; + *txt++ = '\\'; + break; + case '\e': + *txt++ = '\\'; + *txt++ = 'e'; + break; + case '\n': + *txt++ = '\\'; + *txt++ = 'n'; + break; + case '\r': + *txt++ = '\\'; + *txt++ = 'r'; + break; + case '\t': + *txt++ = '\\'; + *txt++ = 't'; + break; + default: + if (data[i] >= 32 && data[i] <= 127) { + *txt++ = data[i]; + } else { + txt += os_snprintf(txt, end - txt, "\\x%02x", + data[i]); + } + break; + } + } + + *txt = '\0'; +} + + +size_t printf_decode(u8 *buf, size_t maxlen, const char *str) +{ + const char *pos = str; + size_t len = 0; + int val; + + while (*pos) { + if (len == maxlen) + break; + switch (*pos) { + case '\\': + pos++; + switch (*pos) { + case '\\': + buf[len++] = '\\'; + pos++; + break; + case '"': + buf[len++] = '"'; + pos++; + break; + case 'n': + buf[len++] = '\n'; + pos++; + break; + case 'r': + buf[len++] = '\r'; + pos++; + break; + case 't': + buf[len++] = '\t'; + pos++; + break; + case 'e': + buf[len++] = '\e'; + pos++; + break; + case 'x': + pos++; + val = hex2byte(pos); + if (val < 0) { + val = hex2num(*pos); + if (val < 0) + break; + buf[len++] = val; + pos++; + } else { + buf[len++] = val; + pos += 2; + } + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + val = *pos++ - '0'; + if (*pos >= '0' && *pos <= '7') + val = val * 8 + (*pos++ - '0'); + if (*pos >= '0' && *pos <= '7') + val = val * 8 + (*pos++ - '0'); + buf[len++] = val; + break; + default: + break; + } + break; + default: + buf[len++] = *pos++; + break; + } + } + + return len; +} + + /** * wpa_ssid_txt - Convert SSID to a printable string * @ssid: SSID (32-octet string) @@ -342,17 +489,14 @@ TCHAR * wpa_strdup_tchar(const char *str) */ const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len) { - static char ssid_txt[33]; - char *pos; - - if (ssid_len > 32) - ssid_len = 32; - os_memcpy(ssid_txt, ssid, ssid_len); - ssid_txt[ssid_len] = '\0'; - for (pos = ssid_txt; *pos != '\0'; pos++) { - if ((u8) *pos < 32 || (u8) *pos >= 127) - *pos = '_'; + static char ssid_txt[32 * 4 + 1]; + + if (ssid == NULL) { + ssid_txt[0] = '\0'; + return ssid_txt; } + + printf_encode(ssid_txt, sizeof(ssid_txt), ssid, ssid_len); return ssid_txt; } @@ -361,3 +505,108 @@ void * __hide_aliasing_typecast(void *foo) { return foo; } + + +char * wpa_config_parse_string(const char *value, size_t *len) +{ + if (*value == '"') { + const char *pos; + char *str; + value++; + pos = os_strrchr(value, '"'); + if (pos == NULL || pos[1] != '\0') + return NULL; + *len = pos - value; + str = os_malloc(*len + 1); + if (str == NULL) + return NULL; + os_memcpy(str, value, *len); + str[*len] = '\0'; + return str; + } else if (*value == 'P' && value[1] == '"') { + const char *pos; + char *tstr, *str; + size_t tlen; + value += 2; + pos = os_strrchr(value, '"'); + if (pos == NULL || pos[1] != '\0') + return NULL; + tlen = pos - value; + tstr = os_malloc(tlen + 1); + if (tstr == NULL) + return NULL; + os_memcpy(tstr, value, tlen); + tstr[tlen] = '\0'; + + str = os_malloc(tlen + 1); + if (str == NULL) { + os_free(tstr); + return NULL; + } + + *len = printf_decode((u8 *) str, tlen + 1, tstr); + os_free(tstr); + + return str; + } else { + u8 *str; + size_t tlen, hlen = os_strlen(value); + if (hlen & 1) + return NULL; + tlen = hlen / 2; + str = os_malloc(tlen + 1); + if (str == NULL) + return NULL; + if (hexstr2bin(value, str, tlen)) { + os_free(str); + return NULL; + } + str[tlen] = '\0'; + *len = tlen; + return (char *) str; + } +} + + +int is_hex(const u8 *data, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) { + if (data[i] < 32 || data[i] >= 127) + return 1; + } + return 0; +} + + +size_t merge_byte_arrays(u8 *res, size_t res_len, + const u8 *src1, size_t src1_len, + const u8 *src2, size_t src2_len) +{ + size_t len = 0; + + os_memset(res, 0, res_len); + + if (src1) { + if (src1_len >= res_len) { + os_memcpy(res, src1, res_len); + return res_len; + } + + os_memcpy(res, src1, src1_len); + len += src1_len; + } + + if (src2) { + if (len + src2_len >= res_len) { + os_memcpy(res + len, src2, res_len - len); + return res_len; + } + + os_memcpy(res + len, src2, src2_len); + len += src2_len; + } + + return len; +} diff --git a/src/utils/common.h b/src/utils/common.h index f17bf69ff5471..5fc916c0ab934 100644 --- a/src/utils/common.h +++ b/src/utils/common.h @@ -2,14 +2,8 @@ * wpa_supplicant/hostapd / common helper functions, etc. * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef COMMON_H @@ -69,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> @@ -138,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> @@ -320,6 +298,9 @@ static inline unsigned int wpa_swap_32(unsigned int v) #ifndef ETH_P_ALL #define ETH_P_ALL 0x0003 #endif +#ifndef ETH_P_80211_ENCAP +#define ETH_P_80211_ENCAP 0x890d /* TDLS comes under this category */ +#endif #ifndef ETH_P_PAE #define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ #endif /* ETH_P_PAE */ @@ -402,6 +383,12 @@ void perror(const char *s); #ifndef MAC2STR #define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] #define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" + +/* + * Compact form for string representation of MAC address + * To be used, e.g., for constructing dbus paths for P2P Devices + */ +#define COMPACT_MACSTR "%02x%02x%02x%02x%02x%02x" #endif #ifndef BIT @@ -436,7 +423,9 @@ typedef u64 __bitwise le64; #endif /* __must_check */ int hwaddr_aton(const char *txt, u8 *addr); +int hwaddr_compact_aton(const char *txt, u8 *addr); int hwaddr_aton2(const char *txt, u8 *addr); +int hex2byte(const char *hex); int hexstr2bin(const char *hex, u8 *buf, size_t len); void inc_byte_array(u8 *counter, size_t len); void wpa_get_ntp_timestamp(u8 *buf); @@ -452,13 +441,29 @@ TCHAR * wpa_strdup_tchar(const char *str); #define wpa_strdup_tchar(s) strdup((s)) #endif /* CONFIG_NATIVE_WINDOWS */ +void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len); +size_t printf_decode(u8 *buf, size_t maxlen, const char *str); + const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len); +char * wpa_config_parse_string(const char *value, size_t *len); +int is_hex(const u8 *data, size_t len); +size_t merge_byte_arrays(u8 *res, size_t res_len, + const u8 *src1, size_t src1_len, + const u8 *src2, size_t src2_len); + static inline int is_zero_ether_addr(const u8 *a) { return !(a[0] | a[1] | a[2] | a[3] | a[4] | a[5]); } +static inline int is_broadcast_ether_addr(const u8 *a) +{ + return (a[0] & a[1] & a[2] & a[3] & a[4] & a[5]) == 0xff; +} + +#define broadcast_ether_addr (const u8 *) "\xff\xff\xff\xff\xff\xff" + #include "wpa_debug.h" @@ -474,4 +479,11 @@ static inline int is_zero_ether_addr(const u8 *a) void * __hide_aliasing_typecast(void *foo); #define aliasing_hide_typecast(a,t) (t *) __hide_aliasing_typecast((a)) +#ifdef CONFIG_VALGRIND +#include <valgrind/memcheck.h> +#define WPA_MEM_DEFINED(ptr, len) VALGRIND_MAKE_MEM_DEFINED((ptr), (len)) +#else /* CONFIG_VALGRIND */ +#define WPA_MEM_DEFINED(ptr, len) do { } while (0) +#endif /* CONFIG_VALGRIND */ + #endif /* COMMON_H */ diff --git a/src/utils/edit.c b/src/utils/edit.c new file mode 100644 index 0000000000000..b01e08dfc2a92 --- /dev/null +++ b/src/utils/edit.c @@ -0,0 +1,1174 @@ +/* + * Command line editing and history + * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include <termios.h> + +#include "common.h" +#include "eloop.h" +#include "list.h" +#include "edit.h" + +#define CMD_BUF_LEN 256 +static char cmdbuf[CMD_BUF_LEN]; +static int cmdbuf_pos = 0; +static int cmdbuf_len = 0; +static char currbuf[CMD_BUF_LEN]; +static int currbuf_valid = 0; +static const char *ps2 = NULL; + +#define HISTORY_MAX 100 + +struct edit_history { + struct dl_list list; + char str[1]; +}; + +static struct dl_list history_list; +static struct edit_history *history_curr; + +static void *edit_cb_ctx; +static void (*edit_cmd_cb)(void *ctx, char *cmd); +static void (*edit_eof_cb)(void *ctx); +static char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) = + NULL; + +static struct termios prevt, newt; + + +#define CLEAR_END_LINE "\e[K" + + +void edit_clear_line(void) +{ + int i; + putchar('\r'); + for (i = 0; i < cmdbuf_len + 2 + (ps2 ? (int) os_strlen(ps2) : 0); i++) + putchar(' '); +} + + +static void move_start(void) +{ + cmdbuf_pos = 0; + edit_redraw(); +} + + +static void move_end(void) +{ + cmdbuf_pos = cmdbuf_len; + edit_redraw(); +} + + +static void move_left(void) +{ + if (cmdbuf_pos > 0) { + cmdbuf_pos--; + edit_redraw(); + } +} + + +static void move_right(void) +{ + if (cmdbuf_pos < cmdbuf_len) { + cmdbuf_pos++; + edit_redraw(); + } +} + + +static void move_word_left(void) +{ + while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] == ' ') + cmdbuf_pos--; + while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] != ' ') + cmdbuf_pos--; + edit_redraw(); +} + + +static void move_word_right(void) +{ + while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] == ' ') + cmdbuf_pos++; + while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] != ' ') + cmdbuf_pos++; + edit_redraw(); +} + + +static void delete_left(void) +{ + if (cmdbuf_pos == 0) + return; + + edit_clear_line(); + os_memmove(cmdbuf + cmdbuf_pos - 1, cmdbuf + cmdbuf_pos, + cmdbuf_len - cmdbuf_pos); + cmdbuf_pos--; + cmdbuf_len--; + edit_redraw(); +} + + +static void delete_current(void) +{ + if (cmdbuf_pos == cmdbuf_len) + return; + + edit_clear_line(); + os_memmove(cmdbuf + cmdbuf_pos, cmdbuf + cmdbuf_pos + 1, + cmdbuf_len - cmdbuf_pos); + cmdbuf_len--; + edit_redraw(); +} + + +static void delete_word(void) +{ + int pos; + + edit_clear_line(); + pos = cmdbuf_pos; + while (pos > 0 && cmdbuf[pos - 1] == ' ') + pos--; + while (pos > 0 && cmdbuf[pos - 1] != ' ') + pos--; + os_memmove(cmdbuf + pos, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos); + cmdbuf_len -= cmdbuf_pos - pos; + cmdbuf_pos = pos; + edit_redraw(); +} + + +static void clear_left(void) +{ + if (cmdbuf_pos == 0) + return; + + edit_clear_line(); + os_memmove(cmdbuf, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos); + cmdbuf_len -= cmdbuf_pos; + cmdbuf_pos = 0; + edit_redraw(); +} + + +static void clear_right(void) +{ + if (cmdbuf_pos == cmdbuf_len) + return; + + edit_clear_line(); + cmdbuf_len = cmdbuf_pos; + edit_redraw(); +} + + +static void history_add(const char *str) +{ + struct edit_history *h, *match = NULL, *last = NULL; + size_t len, count = 0; + + if (str[0] == '\0') + return; + + dl_list_for_each(h, &history_list, struct edit_history, list) { + if (os_strcmp(str, h->str) == 0) { + match = h; + break; + } + last = h; + count++; + } + + if (match) { + dl_list_del(&h->list); + dl_list_add(&history_list, &h->list); + history_curr = h; + return; + } + + if (count >= HISTORY_MAX && last) { + dl_list_del(&last->list); + os_free(last); + } + + len = os_strlen(str); + h = os_zalloc(sizeof(*h) + len); + if (h == NULL) + return; + dl_list_add(&history_list, &h->list); + os_strlcpy(h->str, str, len + 1); + history_curr = h; +} + + +static void history_use(void) +{ + edit_clear_line(); + cmdbuf_len = cmdbuf_pos = os_strlen(history_curr->str); + os_memcpy(cmdbuf, history_curr->str, cmdbuf_len); + edit_redraw(); +} + + +static void history_prev(void) +{ + if (history_curr == NULL) + return; + + if (history_curr == + dl_list_first(&history_list, struct edit_history, list)) { + if (!currbuf_valid) { + cmdbuf[cmdbuf_len] = '\0'; + os_memcpy(currbuf, cmdbuf, cmdbuf_len + 1); + currbuf_valid = 1; + history_use(); + return; + } + } + + if (history_curr == + dl_list_last(&history_list, struct edit_history, list)) + return; + + history_curr = dl_list_entry(history_curr->list.next, + struct edit_history, list); + history_use(); +} + + +static void history_next(void) +{ + if (history_curr == NULL || + history_curr == + dl_list_first(&history_list, struct edit_history, list)) { + if (currbuf_valid) { + currbuf_valid = 0; + edit_clear_line(); + cmdbuf_len = cmdbuf_pos = os_strlen(currbuf); + os_memcpy(cmdbuf, currbuf, cmdbuf_len); + edit_redraw(); + } + return; + } + + history_curr = dl_list_entry(history_curr->list.prev, + struct edit_history, list); + history_use(); +} + + +static void history_read(const char *fname) +{ + FILE *f; + char buf[CMD_BUF_LEN], *pos; + + f = fopen(fname, "r"); + if (f == NULL) + return; + + while (fgets(buf, CMD_BUF_LEN, f)) { + for (pos = buf; *pos; pos++) { + if (*pos == '\r' || *pos == '\n') { + *pos = '\0'; + break; + } + } + history_add(buf); + } + + fclose(f); +} + + +static void history_write(const char *fname, + int (*filter_cb)(void *ctx, const char *cmd)) +{ + FILE *f; + struct edit_history *h; + + f = fopen(fname, "w"); + if (f == NULL) + return; + + dl_list_for_each_reverse(h, &history_list, struct edit_history, list) { + if (filter_cb && filter_cb(edit_cb_ctx, h->str)) + continue; + fprintf(f, "%s\n", h->str); + } + + fclose(f); +} + + +static void history_debug_dump(void) +{ + struct edit_history *h; + edit_clear_line(); + printf("\r"); + dl_list_for_each_reverse(h, &history_list, struct edit_history, list) + printf("%s%s\n", h == history_curr ? "[C]" : "", h->str); + if (currbuf_valid) + printf("{%s}\n", currbuf); + edit_redraw(); +} + + +static void insert_char(int c) +{ + if (cmdbuf_len >= (int) sizeof(cmdbuf) - 1) + return; + if (cmdbuf_len == cmdbuf_pos) { + cmdbuf[cmdbuf_pos++] = c; + cmdbuf_len++; + putchar(c); + fflush(stdout); + } else { + os_memmove(cmdbuf + cmdbuf_pos + 1, cmdbuf + cmdbuf_pos, + cmdbuf_len - cmdbuf_pos); + cmdbuf[cmdbuf_pos++] = c; + cmdbuf_len++; + edit_redraw(); + } +} + + +static void process_cmd(void) +{ + + if (cmdbuf_len == 0) { + printf("\n%s> ", ps2 ? ps2 : ""); + fflush(stdout); + return; + } + printf("\n"); + cmdbuf[cmdbuf_len] = '\0'; + history_add(cmdbuf); + cmdbuf_pos = 0; + cmdbuf_len = 0; + edit_cmd_cb(edit_cb_ctx, cmdbuf); + printf("%s> ", ps2 ? ps2 : ""); + fflush(stdout); +} + + +static void free_completions(char **c) +{ + int i; + if (c == NULL) + return; + for (i = 0; c[i]; i++) + os_free(c[i]); + os_free(c); +} + + +static int filter_strings(char **c, char *str, size_t len) +{ + int i, j; + + for (i = 0, j = 0; c[j]; j++) { + if (os_strncasecmp(c[j], str, len) == 0) { + if (i != j) { + c[i] = c[j]; + c[j] = NULL; + } + i++; + } else { + os_free(c[j]); + c[j] = NULL; + } + } + c[i] = NULL; + return i; +} + + +static int common_len(const char *a, const char *b) +{ + int len = 0; + while (a[len] && a[len] == b[len]) + len++; + return len; +} + + +static int max_common_length(char **c) +{ + int len, i; + + len = os_strlen(c[0]); + for (i = 1; c[i]; i++) { + int same = common_len(c[0], c[i]); + if (same < len) + len = same; + } + + return len; +} + + +static int cmp_str(const void *a, const void *b) +{ + return os_strcmp(* (const char **) a, * (const char **) b); +} + +static void complete(int list) +{ + char **c; + int i, len, count; + int start, end; + int room, plen, add_space; + + if (edit_completion_cb == NULL) + return; + + cmdbuf[cmdbuf_len] = '\0'; + c = edit_completion_cb(edit_cb_ctx, cmdbuf, cmdbuf_pos); + if (c == NULL) + return; + + end = cmdbuf_pos; + start = end; + while (start > 0 && cmdbuf[start - 1] != ' ') + start--; + plen = end - start; + + count = filter_strings(c, &cmdbuf[start], plen); + if (count == 0) { + free_completions(c); + return; + } + + len = max_common_length(c); + if (len <= plen && count > 1) { + if (list) { + qsort(c, count, sizeof(char *), cmp_str); + edit_clear_line(); + printf("\r"); + for (i = 0; c[i]; i++) + printf("%s%s", i > 0 ? " " : "", c[i]); + printf("\n"); + edit_redraw(); + } + free_completions(c); + return; + } + len -= plen; + + room = sizeof(cmdbuf) - 1 - cmdbuf_len; + if (room < len) + len = room; + add_space = count == 1 && len < room; + + os_memmove(cmdbuf + cmdbuf_pos + len + add_space, cmdbuf + cmdbuf_pos, + cmdbuf_len - cmdbuf_pos); + os_memcpy(&cmdbuf[cmdbuf_pos - plen], c[0], plen + len); + if (add_space) + cmdbuf[cmdbuf_pos + len] = ' '; + + cmdbuf_pos += len + add_space; + cmdbuf_len += len + add_space; + + edit_redraw(); + + free_completions(c); +} + + +enum edit_key_code { + EDIT_KEY_NONE = 256, + EDIT_KEY_TAB, + EDIT_KEY_UP, + EDIT_KEY_DOWN, + EDIT_KEY_RIGHT, + EDIT_KEY_LEFT, + EDIT_KEY_ENTER, + EDIT_KEY_BACKSPACE, + EDIT_KEY_INSERT, + EDIT_KEY_DELETE, + EDIT_KEY_HOME, + EDIT_KEY_END, + EDIT_KEY_PAGE_UP, + EDIT_KEY_PAGE_DOWN, + EDIT_KEY_F1, + EDIT_KEY_F2, + EDIT_KEY_F3, + EDIT_KEY_F4, + EDIT_KEY_F5, + EDIT_KEY_F6, + EDIT_KEY_F7, + EDIT_KEY_F8, + EDIT_KEY_F9, + EDIT_KEY_F10, + EDIT_KEY_F11, + EDIT_KEY_F12, + EDIT_KEY_CTRL_UP, + EDIT_KEY_CTRL_DOWN, + EDIT_KEY_CTRL_RIGHT, + EDIT_KEY_CTRL_LEFT, + EDIT_KEY_CTRL_A, + EDIT_KEY_CTRL_B, + EDIT_KEY_CTRL_D, + EDIT_KEY_CTRL_E, + EDIT_KEY_CTRL_F, + EDIT_KEY_CTRL_G, + EDIT_KEY_CTRL_H, + EDIT_KEY_CTRL_J, + EDIT_KEY_CTRL_K, + EDIT_KEY_CTRL_L, + EDIT_KEY_CTRL_N, + EDIT_KEY_CTRL_O, + EDIT_KEY_CTRL_P, + EDIT_KEY_CTRL_R, + EDIT_KEY_CTRL_T, + EDIT_KEY_CTRL_U, + EDIT_KEY_CTRL_V, + EDIT_KEY_CTRL_W, + EDIT_KEY_ALT_UP, + EDIT_KEY_ALT_DOWN, + EDIT_KEY_ALT_RIGHT, + EDIT_KEY_ALT_LEFT, + EDIT_KEY_SHIFT_UP, + EDIT_KEY_SHIFT_DOWN, + EDIT_KEY_SHIFT_RIGHT, + EDIT_KEY_SHIFT_LEFT, + EDIT_KEY_ALT_SHIFT_UP, + EDIT_KEY_ALT_SHIFT_DOWN, + EDIT_KEY_ALT_SHIFT_RIGHT, + EDIT_KEY_ALT_SHIFT_LEFT, + EDIT_KEY_EOF +}; + +static void show_esc_buf(const char *esc_buf, char c, int i) +{ + edit_clear_line(); + printf("\rESC buffer '%s' c='%c' [%d]\n", esc_buf, c, i); + edit_redraw(); +} + + +static enum edit_key_code esc_seq_to_key1_no(char last) +{ + switch (last) { + case 'A': + return EDIT_KEY_UP; + case 'B': + return EDIT_KEY_DOWN; + case 'C': + return EDIT_KEY_RIGHT; + case 'D': + return EDIT_KEY_LEFT; + default: + return EDIT_KEY_NONE; + } +} + + +static enum edit_key_code esc_seq_to_key1_shift(char last) +{ + switch (last) { + case 'A': + return EDIT_KEY_SHIFT_UP; + case 'B': + return EDIT_KEY_SHIFT_DOWN; + case 'C': + return EDIT_KEY_SHIFT_RIGHT; + case 'D': + return EDIT_KEY_SHIFT_LEFT; + default: + return EDIT_KEY_NONE; + } +} + + +static enum edit_key_code esc_seq_to_key1_alt(char last) +{ + switch (last) { + case 'A': + return EDIT_KEY_ALT_UP; + case 'B': + return EDIT_KEY_ALT_DOWN; + case 'C': + return EDIT_KEY_ALT_RIGHT; + case 'D': + return EDIT_KEY_ALT_LEFT; + default: + return EDIT_KEY_NONE; + } +} + + +static enum edit_key_code esc_seq_to_key1_alt_shift(char last) +{ + switch (last) { + case 'A': + return EDIT_KEY_ALT_SHIFT_UP; + case 'B': + return EDIT_KEY_ALT_SHIFT_DOWN; + case 'C': + return EDIT_KEY_ALT_SHIFT_RIGHT; + case 'D': + return EDIT_KEY_ALT_SHIFT_LEFT; + default: + return EDIT_KEY_NONE; + } +} + + +static enum edit_key_code esc_seq_to_key1_ctrl(char last) +{ + switch (last) { + case 'A': + return EDIT_KEY_CTRL_UP; + case 'B': + return EDIT_KEY_CTRL_DOWN; + case 'C': + return EDIT_KEY_CTRL_RIGHT; + case 'D': + return EDIT_KEY_CTRL_LEFT; + default: + return EDIT_KEY_NONE; + } +} + + +static enum edit_key_code esc_seq_to_key1(int param1, int param2, char last) +{ + /* ESC-[<param1>;<param2><last> */ + + if (param1 < 0 && param2 < 0) + return esc_seq_to_key1_no(last); + + if (param1 == 1 && param2 == 2) + return esc_seq_to_key1_shift(last); + + if (param1 == 1 && param2 == 3) + return esc_seq_to_key1_alt(last); + + if (param1 == 1 && param2 == 4) + return esc_seq_to_key1_alt_shift(last); + + if (param1 == 1 && param2 == 5) + return esc_seq_to_key1_ctrl(last); + + if (param2 < 0) { + if (last != '~') + return EDIT_KEY_NONE; + switch (param1) { + case 2: + return EDIT_KEY_INSERT; + case 3: + return EDIT_KEY_DELETE; + case 5: + return EDIT_KEY_PAGE_UP; + case 6: + return EDIT_KEY_PAGE_DOWN; + case 15: + return EDIT_KEY_F5; + case 17: + return EDIT_KEY_F6; + case 18: + return EDIT_KEY_F7; + case 19: + return EDIT_KEY_F8; + case 20: + return EDIT_KEY_F9; + case 21: + return EDIT_KEY_F10; + case 23: + return EDIT_KEY_F11; + case 24: + return EDIT_KEY_F12; + } + } + + return EDIT_KEY_NONE; +} + + +static enum edit_key_code esc_seq_to_key2(int param1, int param2, char last) +{ + /* ESC-O<param1>;<param2><last> */ + + if (param1 >= 0 || param2 >= 0) + return EDIT_KEY_NONE; + + switch (last) { + case 'F': + return EDIT_KEY_END; + case 'H': + return EDIT_KEY_HOME; + case 'P': + return EDIT_KEY_F1; + case 'Q': + return EDIT_KEY_F2; + case 'R': + return EDIT_KEY_F3; + case 'S': + return EDIT_KEY_F4; + default: + return EDIT_KEY_NONE; + } +} + + +static enum edit_key_code esc_seq_to_key(char *seq) +{ + char last, *pos; + int param1 = -1, param2 = -1; + enum edit_key_code ret = EDIT_KEY_NONE; + + last = '\0'; + for (pos = seq; *pos; pos++) + last = *pos; + + if (seq[1] >= '0' && seq[1] <= '9') { + param1 = atoi(&seq[1]); + pos = os_strchr(seq, ';'); + if (pos) + param2 = atoi(pos + 1); + } + + if (seq[0] == '[') + ret = esc_seq_to_key1(param1, param2, last); + else if (seq[0] == 'O') + ret = esc_seq_to_key2(param1, param2, last); + + if (ret != EDIT_KEY_NONE) + return ret; + + edit_clear_line(); + printf("\rUnknown escape sequence '%s'\n", seq); + edit_redraw(); + return EDIT_KEY_NONE; +} + + +static enum edit_key_code edit_read_key(int sock) +{ + int c; + unsigned char buf[1]; + int res; + static int esc = -1; + static char esc_buf[7]; + + res = read(sock, buf, 1); + if (res < 0) + perror("read"); + if (res <= 0) + return EDIT_KEY_EOF; + + c = buf[0]; + + if (esc >= 0) { + if (c == 27 /* ESC */) { + esc = 0; + return EDIT_KEY_NONE; + } + + if (esc == 6) { + show_esc_buf(esc_buf, c, 0); + esc = -1; + } else { + esc_buf[esc++] = c; + esc_buf[esc] = '\0'; + } + } + + if (esc == 1) { + if (esc_buf[0] != '[' && esc_buf[0] != 'O') { + show_esc_buf(esc_buf, c, 1); + esc = -1; + return EDIT_KEY_NONE; + } else + return EDIT_KEY_NONE; /* Escape sequence continues */ + } + + if (esc > 1) { + if ((c >= '0' && c <= '9') || c == ';') + return EDIT_KEY_NONE; /* Escape sequence continues */ + + if (c == '~' || (c >= 'A' && c <= 'Z')) { + esc = -1; + return esc_seq_to_key(esc_buf); + } + + show_esc_buf(esc_buf, c, 2); + esc = -1; + return EDIT_KEY_NONE; + } + + switch (c) { + case 1: + return EDIT_KEY_CTRL_A; + case 2: + return EDIT_KEY_CTRL_B; + case 4: + return EDIT_KEY_CTRL_D; + case 5: + return EDIT_KEY_CTRL_E; + case 6: + return EDIT_KEY_CTRL_F; + case 7: + return EDIT_KEY_CTRL_G; + case 8: + return EDIT_KEY_CTRL_H; + case 9: + return EDIT_KEY_TAB; + case 10: + return EDIT_KEY_CTRL_J; + case 13: /* CR */ + return EDIT_KEY_ENTER; + case 11: + return EDIT_KEY_CTRL_K; + case 12: + return EDIT_KEY_CTRL_L; + case 14: + return EDIT_KEY_CTRL_N; + case 15: + return EDIT_KEY_CTRL_O; + case 16: + return EDIT_KEY_CTRL_P; + case 18: + return EDIT_KEY_CTRL_R; + case 20: + return EDIT_KEY_CTRL_T; + case 21: + return EDIT_KEY_CTRL_U; + case 22: + return EDIT_KEY_CTRL_V; + case 23: + return EDIT_KEY_CTRL_W; + case 27: /* ESC */ + esc = 0; + return EDIT_KEY_NONE; + case 127: + return EDIT_KEY_BACKSPACE; + default: + return c; + } +} + + +static char search_buf[21]; +static int search_skip; + +static char * search_find(void) +{ + struct edit_history *h; + size_t len = os_strlen(search_buf); + int skip = search_skip; + + if (len == 0) + return NULL; + + dl_list_for_each(h, &history_list, struct edit_history, list) { + if (os_strstr(h->str, search_buf)) { + if (skip == 0) + return h->str; + skip--; + } + } + + search_skip = 0; + return NULL; +} + + +static void search_redraw(void) +{ + char *match = search_find(); + printf("\rsearch '%s': %s" CLEAR_END_LINE, + search_buf, match ? match : ""); + printf("\rsearch '%s", search_buf); + fflush(stdout); +} + + +static void search_start(void) +{ + edit_clear_line(); + search_buf[0] = '\0'; + search_skip = 0; + search_redraw(); +} + + +static void search_clear(void) +{ + search_redraw(); + printf("\r" CLEAR_END_LINE); +} + + +static void search_stop(void) +{ + char *match = search_find(); + search_buf[0] = '\0'; + search_clear(); + if (match) { + os_strlcpy(cmdbuf, match, CMD_BUF_LEN); + cmdbuf_len = os_strlen(cmdbuf); + cmdbuf_pos = cmdbuf_len; + } + edit_redraw(); +} + + +static void search_cancel(void) +{ + search_buf[0] = '\0'; + search_clear(); + edit_redraw(); +} + + +static void search_backspace(void) +{ + size_t len; + len = os_strlen(search_buf); + if (len == 0) + return; + search_buf[len - 1] = '\0'; + search_skip = 0; + search_redraw(); +} + + +static void search_next(void) +{ + search_skip++; + search_find(); + search_redraw(); +} + + +static void search_char(char c) +{ + size_t len; + len = os_strlen(search_buf); + if (len == sizeof(search_buf) - 1) + return; + search_buf[len] = c; + search_buf[len + 1] = '\0'; + search_skip = 0; + search_redraw(); +} + + +static enum edit_key_code search_key(enum edit_key_code c) +{ + switch (c) { + case EDIT_KEY_ENTER: + case EDIT_KEY_CTRL_J: + case EDIT_KEY_LEFT: + case EDIT_KEY_RIGHT: + case EDIT_KEY_HOME: + case EDIT_KEY_END: + case EDIT_KEY_CTRL_A: + case EDIT_KEY_CTRL_E: + search_stop(); + return c; + case EDIT_KEY_DOWN: + case EDIT_KEY_UP: + search_cancel(); + return EDIT_KEY_EOF; + case EDIT_KEY_CTRL_H: + case EDIT_KEY_BACKSPACE: + search_backspace(); + break; + case EDIT_KEY_CTRL_R: + search_next(); + break; + default: + if (c >= 32 && c <= 255) + search_char(c); + break; + } + + return EDIT_KEY_NONE; +} + + +static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx) +{ + static int last_tab = 0; + static int search = 0; + enum edit_key_code c; + + c = edit_read_key(sock); + + if (search) { + c = search_key(c); + if (c == EDIT_KEY_NONE) + return; + search = 0; + if (c == EDIT_KEY_EOF) + return; + } + + if (c != EDIT_KEY_TAB && c != EDIT_KEY_NONE) + last_tab = 0; + + switch (c) { + case EDIT_KEY_NONE: + break; + case EDIT_KEY_EOF: + edit_eof_cb(edit_cb_ctx); + break; + case EDIT_KEY_TAB: + complete(last_tab); + last_tab = 1; + break; + case EDIT_KEY_UP: + case EDIT_KEY_CTRL_P: + history_prev(); + break; + case EDIT_KEY_DOWN: + case EDIT_KEY_CTRL_N: + history_next(); + break; + case EDIT_KEY_RIGHT: + case EDIT_KEY_CTRL_F: + move_right(); + break; + case EDIT_KEY_LEFT: + case EDIT_KEY_CTRL_B: + move_left(); + break; + case EDIT_KEY_CTRL_RIGHT: + move_word_right(); + break; + case EDIT_KEY_CTRL_LEFT: + move_word_left(); + break; + case EDIT_KEY_DELETE: + delete_current(); + break; + case EDIT_KEY_END: + move_end(); + break; + case EDIT_KEY_HOME: + case EDIT_KEY_CTRL_A: + move_start(); + break; + case EDIT_KEY_F2: + history_debug_dump(); + break; + case EDIT_KEY_CTRL_D: + if (cmdbuf_len > 0) { + delete_current(); + return; + } + printf("\n"); + edit_eof_cb(edit_cb_ctx); + break; + case EDIT_KEY_CTRL_E: + move_end(); + break; + case EDIT_KEY_CTRL_H: + case EDIT_KEY_BACKSPACE: + delete_left(); + break; + case EDIT_KEY_ENTER: + case EDIT_KEY_CTRL_J: + process_cmd(); + break; + case EDIT_KEY_CTRL_K: + clear_right(); + break; + case EDIT_KEY_CTRL_L: + edit_clear_line(); + edit_redraw(); + break; + case EDIT_KEY_CTRL_R: + search = 1; + search_start(); + break; + case EDIT_KEY_CTRL_U: + clear_left(); + break; + case EDIT_KEY_CTRL_W: + delete_word(); + break; + default: + if (c >= 32 && c <= 255) + insert_char(c); + break; + } +} + + +int edit_init(void (*cmd_cb)(void *ctx, char *cmd), + void (*eof_cb)(void *ctx), + char ** (*completion_cb)(void *ctx, const char *cmd, int pos), + void *ctx, const char *history_file, const char *ps) +{ + currbuf[0] = '\0'; + dl_list_init(&history_list); + history_curr = NULL; + if (history_file) + history_read(history_file); + + edit_cb_ctx = ctx; + edit_cmd_cb = cmd_cb; + edit_eof_cb = eof_cb; + edit_completion_cb = completion_cb; + + tcgetattr(STDIN_FILENO, &prevt); + newt = prevt; + newt.c_lflag &= ~(ICANON | ECHO); + tcsetattr(STDIN_FILENO, TCSANOW, &newt); + + eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL); + + ps2 = ps; + printf("%s> ", ps2 ? ps2 : ""); + fflush(stdout); + + return 0; +} + + +void edit_deinit(const char *history_file, + int (*filter_cb)(void *ctx, const char *cmd)) +{ + struct edit_history *h; + if (history_file) + history_write(history_file, filter_cb); + while ((h = dl_list_first(&history_list, struct edit_history, list))) { + dl_list_del(&h->list); + os_free(h); + } + edit_clear_line(); + putchar('\r'); + fflush(stdout); + eloop_unregister_read_sock(STDIN_FILENO); + tcsetattr(STDIN_FILENO, TCSANOW, &prevt); +} + + +void edit_redraw(void) +{ + char tmp; + cmdbuf[cmdbuf_len] = '\0'; + printf("\r%s> %s", ps2 ? ps2 : "", cmdbuf); + if (cmdbuf_pos != cmdbuf_len) { + tmp = cmdbuf[cmdbuf_pos]; + cmdbuf[cmdbuf_pos] = '\0'; + printf("\r%s> %s", ps2 ? ps2 : "", cmdbuf); + cmdbuf[cmdbuf_pos] = tmp; + } + fflush(stdout); +} diff --git a/src/utils/edit.h b/src/utils/edit.h new file mode 100644 index 0000000000000..ad27f1c7a4ada --- /dev/null +++ b/src/utils/edit.h @@ -0,0 +1,21 @@ +/* + * Command line editing and history + * Copyright (c) 2010, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EDIT_H +#define EDIT_H + +int edit_init(void (*cmd_cb)(void *ctx, char *cmd), + void (*eof_cb)(void *ctx), + char ** (*completion_cb)(void *ctx, const char *cmd, int pos), + void *ctx, const char *history_file, const char *ps); +void edit_deinit(const char *history_file, + int (*filter_cb)(void *ctx, const char *cmd)); +void edit_clear_line(void); +void edit_redraw(void); + +#endif /* EDIT_H */ diff --git a/src/utils/edit_readline.c b/src/utils/edit_readline.c new file mode 100644 index 0000000000000..add26fa33737a --- /dev/null +++ b/src/utils/edit_readline.c @@ -0,0 +1,192 @@ +/* + * Command line editing and history wrapper for readline + * Copyright (c) 2010, 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 <readline/readline.h> +#include <readline/history.h> + +#include "common.h" +#include "eloop.h" +#include "edit.h" + + +static void *edit_cb_ctx; +static void (*edit_cmd_cb)(void *ctx, char *cmd); +static void (*edit_eof_cb)(void *ctx); +static char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) = + NULL; + +static char **pending_completions = NULL; + + +static void readline_free_completions(void) +{ + int i; + if (pending_completions == NULL) + return; + for (i = 0; pending_completions[i]; i++) + os_free(pending_completions[i]); + os_free(pending_completions); + pending_completions = NULL; +} + + +static char * readline_completion_func(const char *text, int state) +{ + static int pos = 0; + static size_t len = 0; + + if (pending_completions == NULL) { + rl_attempted_completion_over = 1; + return NULL; + } + + if (state == 0) { + pos = 0; + len = os_strlen(text); + } + for (; pending_completions[pos]; pos++) { + if (strncmp(pending_completions[pos], text, len) == 0) + return strdup(pending_completions[pos++]); + } + + rl_attempted_completion_over = 1; + return NULL; +} + + +static char ** readline_completion(const char *text, int start, int end) +{ + readline_free_completions(); + if (edit_completion_cb) + pending_completions = edit_completion_cb(edit_cb_ctx, + rl_line_buffer, end); + return rl_completion_matches(text, readline_completion_func); +} + + +static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx) +{ + rl_callback_read_char(); +} + + +static void trunc_nl(char *str) +{ + char *pos = str; + while (*pos != '\0') { + if (*pos == '\n') { + *pos = '\0'; + break; + } + pos++; + } +} + + +static void readline_cmd_handler(char *cmd) +{ + if (cmd && *cmd) { + HIST_ENTRY *h; + while (next_history()) + ; + h = previous_history(); + if (h == NULL || os_strcmp(cmd, h->line) != 0) + add_history(cmd); + next_history(); + } + if (cmd == NULL) { + edit_eof_cb(edit_cb_ctx); + return; + } + trunc_nl(cmd); + edit_cmd_cb(edit_cb_ctx, cmd); +} + + +int edit_init(void (*cmd_cb)(void *ctx, char *cmd), + void (*eof_cb)(void *ctx), + char ** (*completion_cb)(void *ctx, const char *cmd, int pos), + void *ctx, const char *history_file, const char *ps) +{ + edit_cb_ctx = ctx; + edit_cmd_cb = cmd_cb; + edit_eof_cb = eof_cb; + edit_completion_cb = completion_cb; + + rl_attempted_completion_function = readline_completion; + if (history_file) { + read_history(history_file); + stifle_history(100); + } + + eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL); + + if (ps) { + size_t blen = os_strlen(ps) + 3; + char *ps2 = os_malloc(blen); + if (ps2) { + os_snprintf(ps2, blen, "%s> ", ps); + rl_callback_handler_install(ps2, readline_cmd_handler); + os_free(ps2); + return 0; + } + } + + rl_callback_handler_install("> ", readline_cmd_handler); + + return 0; +} + + +void edit_deinit(const char *history_file, + int (*filter_cb)(void *ctx, const char *cmd)) +{ + rl_set_prompt(""); + rl_replace_line("", 0); + rl_redisplay(); + rl_callback_handler_remove(); + readline_free_completions(); + + eloop_unregister_read_sock(STDIN_FILENO); + + if (history_file) { + /* Save command history, excluding lines that may contain + * passwords. */ + HIST_ENTRY *h; + history_set_pos(0); + while ((h = current_history())) { + char *p = h->line; + while (*p == ' ' || *p == '\t') + p++; + if (filter_cb && filter_cb(edit_cb_ctx, p)) { + h = remove_history(where_history()); + if (h) { + os_free(h->line); + free(h->data); + os_free(h); + } else + next_history(); + } else + next_history(); + } + write_history(history_file); + } +} + + +void edit_clear_line(void) +{ +} + + +void edit_redraw(void) +{ + rl_on_new_line(); + rl_redisplay(); +} diff --git a/src/utils/edit_simple.c b/src/utils/edit_simple.c new file mode 100644 index 0000000000000..a095ea6abec5a --- /dev/null +++ b/src/utils/edit_simple.c @@ -0,0 +1,92 @@ +/* + * Minimal command line editing + * Copyright (c) 2010, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "edit.h" + + +#define CMD_BUF_LEN 256 +static char cmdbuf[CMD_BUF_LEN]; +static int cmdbuf_pos = 0; +static const char *ps2 = NULL; + +static void *edit_cb_ctx; +static void (*edit_cmd_cb)(void *ctx, char *cmd); +static void (*edit_eof_cb)(void *ctx); + + +static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx) +{ + int c; + unsigned char buf[1]; + int res; + + res = read(sock, buf, 1); + if (res < 0) + perror("read"); + if (res <= 0) { + edit_eof_cb(edit_cb_ctx); + return; + } + c = buf[0]; + + if (c == '\r' || c == '\n') { + cmdbuf[cmdbuf_pos] = '\0'; + cmdbuf_pos = 0; + edit_cmd_cb(edit_cb_ctx, cmdbuf); + printf("%s> ", ps2 ? ps2 : ""); + fflush(stdout); + return; + } + + if (c >= 32 && c <= 255) { + if (cmdbuf_pos < (int) sizeof(cmdbuf) - 1) { + cmdbuf[cmdbuf_pos++] = c; + } + } +} + + +int edit_init(void (*cmd_cb)(void *ctx, char *cmd), + void (*eof_cb)(void *ctx), + char ** (*completion_cb)(void *ctx, const char *cmd, int pos), + void *ctx, const char *history_file, const char *ps) +{ + edit_cb_ctx = ctx; + edit_cmd_cb = cmd_cb; + edit_eof_cb = eof_cb; + eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL); + ps2 = ps; + + printf("%s> ", ps2 ? ps2 : ""); + fflush(stdout); + + return 0; +} + + +void edit_deinit(const char *history_file, + int (*filter_cb)(void *ctx, const char *cmd)) +{ + eloop_unregister_read_sock(STDIN_FILENO); +} + + +void edit_clear_line(void) +{ +} + + +void edit_redraw(void) +{ + cmdbuf[cmdbuf_pos] = '\0'; + printf("\r> %s", cmdbuf); +} diff --git a/src/utils/eloop.c b/src/utils/eloop.c index 4b615989c0bbb..d01ae64f8a628 100644 --- a/src/utils/eloop.c +++ b/src/utils/eloop.c @@ -2,14 +2,8 @@ * Event loop based on select() loop * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -19,6 +13,11 @@ #include "list.h" #include "eloop.h" +#ifdef CONFIG_ELOOP_POLL +#include <assert.h> +#include <poll.h> +#endif /* CONFIG_ELOOP_POLL */ + struct eloop_sock { int sock; @@ -57,6 +56,13 @@ struct eloop_sock_table { struct eloop_data { int max_sock; + int count; /* sum of all table counts */ +#ifdef CONFIG_ELOOP_POLL + int max_pollfd_map; /* number of pollfds_map currently allocated */ + int max_poll_fds; /* number of pollfds currently allocated */ + struct pollfd *pollfds; + struct pollfd **pollfds_map; +#endif /* CONFIG_ELOOP_POLL */ struct eloop_sock_table readers; struct eloop_sock_table writers; struct eloop_sock_table exceptions; @@ -134,14 +140,44 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table, void *eloop_data, void *user_data) { struct eloop_sock *tmp; + int new_max_sock; + + if (sock > eloop.max_sock) + new_max_sock = sock; + else + new_max_sock = eloop.max_sock; if (table == NULL) return -1; +#ifdef CONFIG_ELOOP_POLL + if (new_max_sock >= eloop.max_pollfd_map) { + struct pollfd **nmap; + nmap = os_realloc_array(eloop.pollfds_map, new_max_sock + 50, + sizeof(struct pollfd *)); + if (nmap == NULL) + return -1; + + eloop.max_pollfd_map = new_max_sock + 50; + eloop.pollfds_map = nmap; + } + + if (eloop.count + 1 > eloop.max_poll_fds) { + struct pollfd *n; + int nmax = eloop.count + 1 + 50; + n = os_realloc_array(eloop.pollfds, nmax, + sizeof(struct pollfd)); + if (n == NULL) + return -1; + + eloop.max_poll_fds = nmax; + eloop.pollfds = n; + } +#endif /* CONFIG_ELOOP_POLL */ + eloop_trace_sock_remove_ref(table); - tmp = (struct eloop_sock *) - os_realloc(table->table, - (table->count + 1) * sizeof(struct eloop_sock)); + tmp = os_realloc_array(table->table, table->count + 1, + sizeof(struct eloop_sock)); if (tmp == NULL) return -1; @@ -152,8 +188,8 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table, wpa_trace_record(&tmp[table->count]); table->count++; table->table = tmp; - if (sock > eloop.max_sock) - eloop.max_sock = sock; + eloop.max_sock = new_max_sock; + eloop.count++; table->changed = 1; eloop_trace_sock_add_ref(table); @@ -182,11 +218,152 @@ static void eloop_sock_table_remove_sock(struct eloop_sock_table *table, sizeof(struct eloop_sock)); } table->count--; + eloop.count--; table->changed = 1; eloop_trace_sock_add_ref(table); } +#ifdef CONFIG_ELOOP_POLL + +static struct pollfd * find_pollfd(struct pollfd **pollfds_map, int fd, int mx) +{ + if (fd < mx && fd >= 0) + return pollfds_map[fd]; + return NULL; +} + + +static int eloop_sock_table_set_fds(struct eloop_sock_table *readers, + struct eloop_sock_table *writers, + struct eloop_sock_table *exceptions, + struct pollfd *pollfds, + struct pollfd **pollfds_map, + int max_pollfd_map) +{ + int i; + int nxt = 0; + int fd; + struct pollfd *pfd; + + /* Clear pollfd lookup map. It will be re-populated below. */ + os_memset(pollfds_map, 0, sizeof(struct pollfd *) * max_pollfd_map); + + if (readers && readers->table) { + for (i = 0; i < readers->count; i++) { + fd = readers->table[i].sock; + assert(fd >= 0 && fd < max_pollfd_map); + pollfds[nxt].fd = fd; + pollfds[nxt].events = POLLIN; + pollfds[nxt].revents = 0; + pollfds_map[fd] = &(pollfds[nxt]); + nxt++; + } + } + + if (writers && writers->table) { + for (i = 0; i < writers->count; i++) { + /* + * See if we already added this descriptor, update it + * if so. + */ + fd = writers->table[i].sock; + assert(fd >= 0 && fd < max_pollfd_map); + pfd = pollfds_map[fd]; + if (!pfd) { + pfd = &(pollfds[nxt]); + pfd->events = 0; + pfd->fd = fd; + pollfds[i].revents = 0; + pollfds_map[fd] = pfd; + nxt++; + } + pfd->events |= POLLOUT; + } + } + + /* + * Exceptions are always checked when using poll, but I suppose it's + * possible that someone registered a socket *only* for exception + * handling. Set the POLLIN bit in this case. + */ + if (exceptions && exceptions->table) { + for (i = 0; i < exceptions->count; i++) { + /* + * See if we already added this descriptor, just use it + * if so. + */ + fd = exceptions->table[i].sock; + assert(fd >= 0 && fd < max_pollfd_map); + pfd = pollfds_map[fd]; + if (!pfd) { + pfd = &(pollfds[nxt]); + pfd->events = POLLIN; + pfd->fd = fd; + pollfds[i].revents = 0; + pollfds_map[fd] = pfd; + nxt++; + } + } + } + + return nxt; +} + + +static int eloop_sock_table_dispatch_table(struct eloop_sock_table *table, + struct pollfd **pollfds_map, + int max_pollfd_map, + short int revents) +{ + int i; + struct pollfd *pfd; + + if (!table || !table->table) + return 0; + + table->changed = 0; + for (i = 0; i < table->count; i++) { + pfd = find_pollfd(pollfds_map, table->table[i].sock, + max_pollfd_map); + if (!pfd) + continue; + + if (!(pfd->revents & revents)) + continue; + + table->table[i].handler(table->table[i].sock, + table->table[i].eloop_data, + table->table[i].user_data); + if (table->changed) + return 1; + } + + return 0; +} + + +static void eloop_sock_table_dispatch(struct eloop_sock_table *readers, + struct eloop_sock_table *writers, + struct eloop_sock_table *exceptions, + struct pollfd **pollfds_map, + int max_pollfd_map) +{ + if (eloop_sock_table_dispatch_table(readers, pollfds_map, + max_pollfd_map, POLLIN | POLLERR | + POLLHUP)) + return; /* pollfds may be invalid at this point */ + + if (eloop_sock_table_dispatch_table(writers, pollfds_map, + max_pollfd_map, POLLOUT)) + return; /* pollfds may be invalid at this point */ + + eloop_sock_table_dispatch_table(exceptions, pollfds_map, + max_pollfd_map, POLLERR | POLLHUP); +} + +#else /* CONFIG_ELOOP_POLL */ + static void eloop_sock_table_set_fds(struct eloop_sock_table *table, fd_set *fds) { @@ -222,6 +399,8 @@ static void eloop_sock_table_dispatch(struct eloop_sock_table *table, } } +#endif /* CONFIG_ELOOP_POLL */ + static void eloop_sock_table_destroy(struct eloop_sock_table *table) { @@ -300,6 +479,7 @@ int eloop_register_timeout(unsigned int secs, unsigned int usecs, void *eloop_data, void *user_data) { struct eloop_timeout *timeout, *tmp; + os_time_t now_sec; timeout = os_zalloc(sizeof(*timeout)); if (timeout == NULL) @@ -308,7 +488,18 @@ int eloop_register_timeout(unsigned int secs, unsigned int usecs, os_free(timeout); return -1; } + now_sec = timeout->time.sec; timeout->time.sec += secs; + if (timeout->time.sec < now_sec) { + /* + * Integer overflow - assume long enough timeout to be assumed + * to be infinite, i.e., the timeout would never happen. + */ + wpa_printf(MSG_DEBUG, "ELOOP: Too long timeout (secs=%u) to " + "ever happen - ignore it", secs); + os_free(timeout); + return 0; + } timeout->time.usec += usecs; while (timeout->time.usec >= 1000000) { timeout->time.sec++; @@ -448,10 +639,8 @@ int eloop_register_signal(int sig, eloop_signal_handler handler, { struct eloop_signal *tmp; - tmp = (struct eloop_signal *) - os_realloc(eloop.signals, - (eloop.signal_count + 1) * - sizeof(struct eloop_signal)); + tmp = os_realloc_array(eloop.signals, eloop.signal_count + 1, + sizeof(struct eloop_signal)); if (tmp == NULL) return -1; @@ -490,16 +679,23 @@ int eloop_register_signal_reconfig(eloop_signal_handler handler, void eloop_run(void) { +#ifdef CONFIG_ELOOP_POLL + int num_poll_fds; + int timeout_ms = 0; +#else /* CONFIG_ELOOP_POLL */ fd_set *rfds, *wfds, *efds; - int res; struct timeval _tv; +#endif /* CONFIG_ELOOP_POLL */ + int res; struct os_time tv, now; +#ifndef CONFIG_ELOOP_POLL rfds = os_malloc(sizeof(*rfds)); wfds = os_malloc(sizeof(*wfds)); efds = os_malloc(sizeof(*efds)); if (rfds == NULL || wfds == NULL || efds == NULL) goto out; +#endif /* CONFIG_ELOOP_POLL */ while (!eloop.terminate && (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 || @@ -513,10 +709,27 @@ void eloop_run(void) os_time_sub(&timeout->time, &now, &tv); else tv.sec = tv.usec = 0; +#ifdef CONFIG_ELOOP_POLL + timeout_ms = tv.sec * 1000 + tv.usec / 1000; +#else /* CONFIG_ELOOP_POLL */ _tv.tv_sec = tv.sec; _tv.tv_usec = tv.usec; +#endif /* CONFIG_ELOOP_POLL */ } +#ifdef CONFIG_ELOOP_POLL + num_poll_fds = eloop_sock_table_set_fds( + &eloop.readers, &eloop.writers, &eloop.exceptions, + eloop.pollfds, eloop.pollfds_map, + eloop.max_pollfd_map); + res = poll(eloop.pollfds, num_poll_fds, + timeout ? timeout_ms : -1); + + if (res < 0 && errno != EINTR && errno != 0) { + perror("poll"); + goto out; + } +#else /* CONFIG_ELOOP_POLL */ eloop_sock_table_set_fds(&eloop.readers, rfds); eloop_sock_table_set_fds(&eloop.writers, wfds); eloop_sock_table_set_fds(&eloop.exceptions, efds); @@ -526,6 +739,7 @@ void eloop_run(void) perror("select"); goto out; } +#endif /* CONFIG_ELOOP_POLL */ eloop_process_pending_signals(); /* check if some registered timeouts have occurred */ @@ -547,15 +761,24 @@ void eloop_run(void) if (res <= 0) continue; +#ifdef CONFIG_ELOOP_POLL + eloop_sock_table_dispatch(&eloop.readers, &eloop.writers, + &eloop.exceptions, eloop.pollfds_map, + eloop.max_pollfd_map); +#else /* CONFIG_ELOOP_POLL */ eloop_sock_table_dispatch(&eloop.readers, rfds); eloop_sock_table_dispatch(&eloop.writers, wfds); eloop_sock_table_dispatch(&eloop.exceptions, efds); +#endif /* CONFIG_ELOOP_POLL */ } out: +#ifndef CONFIG_ELOOP_POLL os_free(rfds); os_free(wfds); os_free(efds); +#endif /* CONFIG_ELOOP_POLL */ + return; } @@ -593,6 +816,11 @@ void eloop_destroy(void) eloop_sock_table_destroy(&eloop.writers); eloop_sock_table_destroy(&eloop.exceptions); os_free(eloop.signals); + +#ifdef CONFIG_ELOOP_POLL + os_free(eloop.pollfds); + os_free(eloop.pollfds_map); +#endif /* CONFIG_ELOOP_POLL */ } @@ -604,6 +832,18 @@ int eloop_terminated(void) void eloop_wait_for_read_sock(int sock) { +#ifdef CONFIG_ELOOP_POLL + struct pollfd pfd; + + if (sock < 0) + return; + + os_memset(&pfd, 0, sizeof(pfd)); + pfd.fd = sock; + pfd.events = POLLIN; + + poll(&pfd, 1, -1); +#else /* CONFIG_ELOOP_POLL */ fd_set rfds; if (sock < 0) @@ -612,4 +852,5 @@ void eloop_wait_for_read_sock(int sock) FD_ZERO(&rfds); FD_SET(sock, &rfds); select(sock + 1, &rfds, NULL, NULL, NULL); +#endif /* CONFIG_ELOOP_POLL */ } diff --git a/src/utils/eloop.h b/src/utils/eloop.h index 1228f24d22c8c..db03a735596f8 100644 --- a/src/utils/eloop.h +++ b/src/utils/eloop.h @@ -2,14 +2,8 @@ * Event loop * Copyright (c) 2002-2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file defines an event loop interface that supports processing events * from registered timeouts (i.e., do something after N seconds), sockets @@ -144,7 +138,7 @@ void eloop_unregister_sock(int sock, eloop_event_type type); * Returns: 0 on success, -1 on failure * * Register an event handler for the given event. This function is used to - * register eloop implementation specific events which are mainly targetted for + * register eloop implementation specific events which are mainly targeted for * operating system specific code (driver interface and l2_packet) since the * portable code will not be able to use such an OS-specific call. The handler * function will be called whenever the event is triggered. The handler diff --git a/src/utils/eloop_none.c b/src/utils/eloop_none.c index 18eae4e5a7788..c67ece4d715ee 100644 --- a/src/utils/eloop_none.c +++ b/src/utils/eloop_none.c @@ -2,14 +2,8 @@ * Event loop - empty template (basic structure, but no OS specific operations) * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/utils/eloop_win.c b/src/utils/eloop_win.c index 94cc72d04c6b6..1fafeb2d98146 100644 --- a/src/utils/eloop_win.c +++ b/src/utils/eloop_win.c @@ -2,14 +2,8 @@ * Event loop based on Windows events and WaitForMultipleObjects * Copyright (c) 2002-2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -104,8 +98,8 @@ static int eloop_prepare_handles(void) if (eloop.num_handles > eloop.reader_count + eloop.event_count + 8) return 0; - n = os_realloc(eloop.handles, - eloop.num_handles * 2 * sizeof(eloop.handles[0])); + n = os_realloc_array(eloop.handles, eloop.num_handles * 2, + sizeof(eloop.handles[0])); if (n == NULL) return -1; eloop.handles = n; @@ -134,8 +128,8 @@ int eloop_register_read_sock(int sock, eloop_sock_handler handler, WSACloseEvent(event); return -1; } - tmp = os_realloc(eloop.readers, - (eloop.reader_count + 1) * sizeof(struct eloop_sock)); + tmp = os_realloc_array(eloop.readers, eloop.reader_count + 1, + sizeof(struct eloop_sock)); if (tmp == NULL) { WSAEventSelect(sock, event, 0); WSACloseEvent(event); @@ -197,8 +191,8 @@ int eloop_register_event(void *event, size_t event_size, if (eloop_prepare_handles()) return -1; - tmp = os_realloc(eloop.events, - (eloop.event_count + 1) * sizeof(struct eloop_event)); + tmp = os_realloc_array(eloop.events, eloop.event_count + 1, + sizeof(struct eloop_event)); if (tmp == NULL) return -1; @@ -243,12 +237,24 @@ int eloop_register_timeout(unsigned int secs, unsigned int usecs, void *eloop_data, void *user_data) { struct eloop_timeout *timeout, *tmp, *prev; + os_time_t now_sec; timeout = os_malloc(sizeof(*timeout)); if (timeout == NULL) return -1; os_get_time(&timeout->time); + now_sec = timeout->time.sec; timeout->time.sec += secs; + if (timeout->time.sec < now_sec) { + /* + * Integer overflow - assume long enough timeout to be assumed + * to be infinite, i.e., the timeout would never happen. + */ + wpa_printf(MSG_DEBUG, "ELOOP: Too long timeout (secs=%u) to " + "ever happen - ignore it", secs); + os_free(timeout); + return 0; + } timeout->time.usec += usecs; while (timeout->time.usec >= 1000000) { timeout->time.sec++; @@ -386,9 +392,8 @@ int eloop_register_signal(int sig, eloop_signal_handler handler, { struct eloop_signal *tmp; - tmp = os_realloc(eloop.signals, - (eloop.signal_count + 1) * - sizeof(struct eloop_signal)); + tmp = os_realloc_array(eloop.signals, eloop.signal_count + 1, + sizeof(struct eloop_signal)); if (tmp == NULL) return -1; diff --git a/src/utils/ext_password.c b/src/utils/ext_password.c new file mode 100644 index 0000000000000..06131197a3113 --- /dev/null +++ b/src/utils/ext_password.c @@ -0,0 +1,116 @@ +/* + * External password backend + * Copyright (c) 2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#ifdef __linux__ +#include <sys/mman.h> +#endif /* __linux__ */ + +#include "common.h" +#include "ext_password_i.h" + + +#ifdef CONFIG_EXT_PASSWORD_TEST +extern struct ext_password_backend ext_password_test; +#endif /* CONFIG_EXT_PASSWORD_TEST */ + +static const struct ext_password_backend *backends[] = { +#ifdef CONFIG_EXT_PASSWORD_TEST + &ext_password_test, +#endif /* CONFIG_EXT_PASSWORD_TEST */ + NULL +}; + +struct ext_password_data { + const struct ext_password_backend *backend; + void *priv; +}; + + +struct ext_password_data * ext_password_init(const char *backend, + const char *params) +{ + struct ext_password_data *data; + int i; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + for (i = 0; backends[i]; i++) { + if (os_strcmp(backends[i]->name, backend) == 0) { + data->backend = backends[i]; + break; + } + } + + if (!data->backend) { + os_free(data); + return NULL; + } + + data->priv = data->backend->init(params); + if (data->priv == NULL) { + os_free(data); + return NULL; + } + + return data; +} + + +void ext_password_deinit(struct ext_password_data *data) +{ + if (data && data->backend && data->priv) + data->backend->deinit(data->priv); + os_free(data); +} + + +struct wpabuf * ext_password_get(struct ext_password_data *data, + const char *name) +{ + if (data == NULL) + return NULL; + return data->backend->get(data->priv, name); +} + + +struct wpabuf * ext_password_alloc(size_t len) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(len); + if (buf == NULL) + return NULL; + +#ifdef __linux__ + if (mlock(wpabuf_head(buf), wpabuf_len(buf)) < 0) { + wpa_printf(MSG_ERROR, "EXT PW: mlock failed: %s", + strerror(errno)); + } +#endif /* __linux__ */ + + return buf; +} + + +void ext_password_free(struct wpabuf *pw) +{ + if (pw == NULL) + return; + os_memset(wpabuf_mhead(pw), 0, wpabuf_len(pw)); +#ifdef __linux__ + if (munlock(wpabuf_head(pw), wpabuf_len(pw)) < 0) { + wpa_printf(MSG_ERROR, "EXT PW: munlock failed: %s", + strerror(errno)); + } +#endif /* __linux__ */ + wpabuf_free(pw); +} diff --git a/src/utils/ext_password.h b/src/utils/ext_password.h new file mode 100644 index 0000000000000..e3e46ea0843e2 --- /dev/null +++ b/src/utils/ext_password.h @@ -0,0 +1,33 @@ +/* + * External password backend + * Copyright (c) 2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EXT_PASSWORD_H +#define EXT_PASSWORD_H + +struct ext_password_data; + +#ifdef CONFIG_EXT_PASSWORD + +struct ext_password_data * ext_password_init(const char *backend, + const char *params); +void ext_password_deinit(struct ext_password_data *data); + +struct wpabuf * ext_password_get(struct ext_password_data *data, + const char *name); +void ext_password_free(struct wpabuf *pw); + +#else /* CONFIG_EXT_PASSWORD */ + +#define ext_password_init(b, p) ((void *) 1) +#define ext_password_deinit(d) do { } while (0) +#define ext_password_get(d, n) (NULL) +#define ext_password_free(p) do { } while (0) + +#endif /* CONFIG_EXT_PASSWORD */ + +#endif /* EXT_PASSWORD_H */ diff --git a/src/utils/ext_password_i.h b/src/utils/ext_password_i.h new file mode 100644 index 0000000000000..043e7312c62fb --- /dev/null +++ b/src/utils/ext_password_i.h @@ -0,0 +1,23 @@ +/* + * External password backend - internal definitions + * Copyright (c) 2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EXT_PASSWORD_I_H +#define EXT_PASSWORD_I_H + +#include "ext_password.h" + +struct ext_password_backend { + const char *name; + void * (*init)(const char *params); + void (*deinit)(void *ctx); + struct wpabuf * (*get)(void *ctx, const char *name); +}; + +struct wpabuf * ext_password_alloc(size_t len); + +#endif /* EXT_PASSWORD_I_H */ diff --git a/src/utils/ext_password_test.c b/src/utils/ext_password_test.c new file mode 100644 index 0000000000000..3801bb85449fe --- /dev/null +++ b/src/utils/ext_password_test.c @@ -0,0 +1,90 @@ +/* + * External password backend + * Copyright (c) 2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "ext_password_i.h" + + +struct ext_password_test_data { + char *params; +}; + + +static void * ext_password_test_init(const char *params) +{ + struct ext_password_test_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + if (params) + data->params = os_strdup(params); + + return data; +} + + +static void ext_password_test_deinit(void *ctx) +{ + struct ext_password_test_data *data = ctx; + + os_free(data->params); + os_free(data); +} + + +static struct wpabuf * ext_password_test_get(void *ctx, const char *name) +{ + struct ext_password_test_data *data = ctx; + char *pos, *pos2; + size_t nlen; + + wpa_printf(MSG_DEBUG, "EXT PW TEST: get(%s)", name); + + pos = data->params; + if (pos == NULL) + return NULL; + nlen = os_strlen(name); + + while (pos && *pos) { + if (os_strncmp(pos, name, nlen) == 0 && pos[nlen] == '=') { + struct wpabuf *buf; + pos += nlen + 1; + pos2 = pos; + while (*pos2 != '|' && *pos2 != '\0') + pos2++; + buf = ext_password_alloc(pos2 - pos); + if (buf == NULL) + return NULL; + wpabuf_put_data(buf, pos, pos2 - pos); + wpa_hexdump_ascii_key(MSG_DEBUG, "EXT PW TEST: value", + wpabuf_head(buf), + wpabuf_len(buf)); + return buf; + } + + pos = os_strchr(pos + 1, '|'); + if (pos) + pos++; + } + + wpa_printf(MSG_DEBUG, "EXT PW TEST: get(%s) - not found", name); + + return NULL; +} + + +const struct ext_password_backend ext_password_test = { + .name = "test", + .init = ext_password_test_init, + .deinit = ext_password_test_deinit, + .get = ext_password_test_get, +}; diff --git a/src/utils/includes.h b/src/utils/includes.h index 63b5c23d84907..6c6ec87d0eaf4 100644 --- a/src/utils/includes.h +++ b/src/utils/includes.h @@ -2,14 +2,8 @@ * wpa_supplicant/hostapd - Default include files * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This header file is included into all C files so that commonly used header * files can be selected with OS specific ifdef blocks in one place instead of @@ -34,7 +28,6 @@ #include <errno.h> #endif /* _WIN32_WCE */ #include <ctype.h> -#include <time.h> #ifndef CONFIG_TI_COMPILER #ifndef _MSC_VER @@ -48,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/ip_addr.c b/src/utils/ip_addr.c index 158fd57ed0170..3647c764e6721 100644 --- a/src/utils/ip_addr.c +++ b/src/utils/ip_addr.c @@ -2,14 +2,8 @@ * IP address processing * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/utils/ip_addr.h b/src/utils/ip_addr.h index 28ccaefdea2bc..79ac20cdbdc9e 100644 --- a/src/utils/ip_addr.h +++ b/src/utils/ip_addr.h @@ -2,14 +2,8 @@ * IP address processing * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef IP_ADDR_H diff --git a/src/utils/list.h b/src/utils/list.h index ed7c0227264f8..6881130957ffa 100644 --- a/src/utils/list.h +++ b/src/utils/list.h @@ -2,14 +2,8 @@ * Doubly-linked list * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef LIST_H @@ -75,6 +69,10 @@ static inline unsigned int dl_list_len(struct dl_list *list) (dl_list_empty((list)) ? NULL : \ dl_list_entry((list)->next, type, member)) +#define dl_list_last(list, type, member) \ + (dl_list_empty((list)) ? NULL : \ + dl_list_entry((list)->prev, type, member)) + #define dl_list_for_each(item, list, type, member) \ for (item = dl_list_entry((list)->next, type, member); \ &item->member != (list); \ @@ -86,4 +84,12 @@ static inline unsigned int dl_list_len(struct dl_list *list) &item->member != (list); \ item = n, n = dl_list_entry(n->member.next, type, member)) +#define dl_list_for_each_reverse(item, list, type, member) \ + for (item = dl_list_entry((list)->prev, type, member); \ + &item->member != (list); \ + item = dl_list_entry(item->member.prev, type, member)) + +#define DEFINE_DL_LIST(name) \ + struct dl_list name = { &(name), &(name) } + #endif /* LIST_H */ diff --git a/src/utils/os.h b/src/utils/os.h index f4723d87525d7..ad208341fa94d 100644 --- a/src/utils/os.h +++ b/src/utils/os.h @@ -2,14 +2,8 @@ * OS specific functions * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef OS_H @@ -70,6 +64,16 @@ int os_get_time(struct os_time *t); int os_mktime(int year, int month, int day, int hour, int min, int sec, os_time_t *t); +struct os_tm { + int sec; /* 0..59 or 60 for leap seconds */ + int min; /* 0..59 */ + int hour; /* 0..23 */ + int day; /* 1..31 */ + int month; /* 1..12 */ + int year; /* Four digit year */ +}; + +int os_gmtime(os_time_t t, struct os_tm *tm); /** * os_daemonize - Run in the background (detach from the controlling terminal) @@ -176,6 +180,25 @@ char * os_readfile(const char *name, size_t *len); */ void * os_zalloc(size_t size); +/** + * os_calloc - Allocate and zero memory for an array + * @nmemb: Number of members in the array + * @size: Number of bytes in each member + * Returns: Pointer to allocated and zeroed memory or %NULL on failure + * + * This function can be used as a wrapper for os_zalloc(nmemb * size) when an + * allocation is used for an array. The main benefit over os_zalloc() is in + * having an extra check to catch integer overflows in multiplication. + * + * Caller is responsible for freeing the returned buffer with os_free(). + */ +static inline void * os_calloc(size_t nmemb, size_t size) +{ + if (size && nmemb > (~(size_t) 0) / size) + return NULL; + return os_zalloc(nmemb * size); +} + /* * The following functions are wrapper for standard ANSI C or POSIX functions. @@ -463,6 +486,14 @@ char * os_strdup(const char *s); #endif /* OS_NO_C_LIB_DEFINES */ +static inline void * os_realloc_array(void *ptr, size_t nmemb, size_t size) +{ + if (size && nmemb > (~(size_t) 0) / size) + return NULL; + return os_realloc(ptr, nmemb * size); +} + + /** * os_strlcpy - Copy a string with size bound and NUL-termination * @dest: Destination diff --git a/src/utils/os_internal.c b/src/utils/os_internal.c index 5260e232101f2..e4b7fdb18ad35 100644 --- a/src/utils/os_internal.c +++ b/src/utils/os_internal.c @@ -2,14 +2,8 @@ * wpa_supplicant/hostapd / Internal implementation of OS specific functions * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file is an example of operating system specific wrapper functions. * This version implements many of the functions internally, so it can be used @@ -70,6 +64,24 @@ int os_mktime(int year, int month, int day, int hour, int min, int sec, } +int os_gmtime(os_time_t t, struct os_tm *tm) +{ + struct tm *tm2; + time_t t2 = t; + + tm2 = gmtime(&t2); + if (tm2 == NULL) + return -1; + tm->sec = tm2->tm_sec; + tm->min = tm2->tm_min; + tm->hour = tm2->tm_hour; + tm->day = tm2->tm_mday; + tm->month = tm2->tm_mon + 1; + tm->year = tm2->tm_year + 1900; + return 0; +} + + int os_daemonize(const char *pid_file) { if (daemon(0, 0)) { diff --git a/src/utils/os_none.c b/src/utils/os_none.c index bab8f178c05af..cabf73bd87fc6 100644 --- a/src/utils/os_none.c +++ b/src/utils/os_none.c @@ -2,14 +2,8 @@ * wpa_supplicant/hostapd / Empty OS specific functions * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file can be used as a starting point when adding a new OS target. The * functions here do not really work as-is since they are just empty or only @@ -38,6 +32,11 @@ int os_mktime(int year, int month, int day, int hour, int min, int sec, return -1; } +int os_gmtime(os_time_t t, struct os_tm *tm) +{ + return -1; +} + int os_daemonize(const char *pid_file) { diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c index 6f58fa46cf014..23a93becea43c 100644 --- a/src/utils/os_unix.c +++ b/src/utils/os_unix.c @@ -2,26 +2,28 @@ * OS specific functions for UNIX/POSIX systems * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" +#include <time.h> + +#ifdef ANDROID +#include <linux/capability.h> +#include <linux/prctl.h> +#include <private/android_filesystem_config.h> +#endif /* ANDROID */ + #include "os.h" #ifdef WPA_TRACE #include "common.h" -#include "list.h" #include "wpa_debug.h" #include "trace.h" +#include "list.h" static struct dl_list alloc_list; @@ -98,6 +100,24 @@ int os_mktime(int year, int month, int day, int hour, int min, int sec, } +int os_gmtime(os_time_t t, struct os_tm *tm) +{ + struct tm *tm2; + time_t t2 = t; + + tm2 = gmtime(&t2); + if (tm2 == NULL) + return -1; + tm->sec = tm2->tm_sec; + tm->min = tm2->tm_min; + tm->hour = tm2->tm_hour; + tm->day = tm2->tm_mday; + tm->month = tm2->tm_mon + 1; + tm->year = tm2->tm_year + 1900; + return 0; +} + + #ifdef __APPLE__ #include <fcntl.h> static int os_daemon(int nochdir, int noclose) @@ -135,9 +155,9 @@ static int os_daemon(int nochdir, int noclose) int os_daemonize(const char *pid_file) { -#ifdef __uClinux__ +#if defined(__uClinux__) || defined(__sun__) return -1; -#else /* __uClinux__ */ +#else /* defined(__uClinux__) || defined(__sun__) */ if (os_daemon(0, 0)) { perror("daemon"); return -1; @@ -152,7 +172,7 @@ int os_daemonize(const char *pid_file) } return -0; -#endif /* __uClinux__ */ +#endif /* defined(__uClinux__) || defined(__sun__) */ } @@ -232,6 +252,30 @@ char * os_rel2abs_path(const char *rel_path) int os_program_init(void) { +#ifdef ANDROID + /* + * We ignore errors here since errors are normal if we + * are already running as non-root. + */ + gid_t groups[] = { AID_INET, AID_WIFI, AID_KEYSTORE }; + struct __user_cap_header_struct header; + struct __user_cap_data_struct cap; + + setgroups(sizeof(groups)/sizeof(groups[0]), groups); + + prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); + + setgid(AID_WIFI); + setuid(AID_WIFI); + + header.version = _LINUX_CAPABILITY_VERSION; + header.pid = 0; + cap.effective = cap.permitted = + (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW); + cap.inheritable = 0; + capset(&header, &cap); +#endif /* ANDROID */ + #ifdef WPA_TRACE dl_list_init(&alloc_list); #endif /* WPA_TRACE */ @@ -285,14 +329,21 @@ char * os_readfile(const char *name, size_t *len) { FILE *f; char *buf; + long pos; f = fopen(name, "rb"); if (f == NULL) return NULL; - fseek(f, 0, SEEK_END); - *len = ftell(f); - fseek(f, 0, SEEK_SET); + if (fseek(f, 0, SEEK_END) < 0 || (pos = ftell(f)) < 0) { + fclose(f); + return NULL; + } + *len = pos; + if (fseek(f, 0, SEEK_SET) < 0) { + fclose(f); + return NULL; + } buf = os_malloc(*len); if (buf == NULL) { diff --git a/src/utils/os_win32.c b/src/utils/os_win32.c index 074096480a405..163cebefce265 100644 --- a/src/utils/os_win32.c +++ b/src/utils/os_win32.c @@ -2,17 +2,12 @@ * wpa_supplicant/hostapd / OS specific functions for Win32 systems * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" +#include <time.h> #include <winsock2.h> #include <wincrypt.h> @@ -92,6 +87,24 @@ int os_mktime(int year, int month, int day, int hour, int min, int sec, } +int os_gmtime(os_time_t t, struct os_tm *tm) +{ + struct tm *tm2; + time_t t2 = t; + + tm2 = gmtime(&t2); + if (tm2 == NULL) + return -1; + tm->sec = tm2->tm_sec; + tm->min = tm2->tm_min; + tm->hour = tm2->tm_hour; + tm->day = tm2->tm_mday; + tm->month = tm2->tm_mon + 1; + tm->year = tm2->tm_year + 1900; + return 0; +} + + int os_daemonize(const char *pid_file) { /* TODO */ diff --git a/src/utils/pcsc_funcs.c b/src/utils/pcsc_funcs.c index bf9f04a719b1d..08510d015d9df 100644 --- a/src/utils/pcsc_funcs.c +++ b/src/utils/pcsc_funcs.c @@ -1,15 +1,9 @@ /* * 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 program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published 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. * * This file implements wrapper functions for accessing GSM SIM and 3GPP USIM * cards through PC/SC smartcard library. These functions are used to implement @@ -76,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 @@ -87,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 { @@ -240,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 @@ -279,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; } @@ -334,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; @@ -423,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 @@ -431,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; @@ -488,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 - wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%S'", readers); + /* TODO */ #else /* UNICODE */ - wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%s'", readers); + 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; + } - ret = SCardConnect(scard->ctx, readers, SCARD_SHARE_SHARED, - SCARD_PROTOCOL_T0, &scard->card, &scard->protocol); +#ifdef UNICODE + wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%S'", &readers[pos]); +#else /* UNICODE */ + wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%s'", &readers[pos]); +#endif /* UNICODE */ + + 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."); @@ -588,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); @@ -812,7 +900,7 @@ static int scard_get_record_len(struct scard_data *scard, unsigned char recnum, wpa_hexdump(MSG_DEBUG, "SCARD: file length determination response", buf, blen); - if (blen < 2 || buf[0] != 0x6c) { + if (blen < 2 || (buf[0] != 0x6c && buf[0] != 0x67)) { wpa_printf(MSG_DEBUG, "SCARD: unexpected response to file " "length determination"); return -1; @@ -945,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() @@ -1024,6 +1152,61 @@ int scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len) /** + * scard_get_mnc_len - Read length of MNC in the IMSI from SIM/USIM card + * @scard: Pointer to private data from scard_init() + * Returns: length (>0) on success, -1 if administrative data file cannot be + * selected, -2 if administrative data file selection returns invalid result + * code, -3 if parsing FSP template file fails (USIM only), -4 if length of + * the file is unexpected, -5 if reading file fails, -6 if MNC length is not + * in range (i.e. 2 or 3), -7 if MNC length is not available. + * + */ +int scard_get_mnc_len(struct scard_data *scard) +{ + unsigned char buf[100]; + size_t blen; + int file_size; + + wpa_printf(MSG_DEBUG, "SCARD: reading MNC len from (GSM) EF-AD"); + blen = sizeof(buf); + if (scard_select_file(scard, SCARD_FILE_GSM_EF_AD, buf, &blen)) + return -1; + if (blen < 4) { + wpa_printf(MSG_WARNING, "SCARD: too short (GSM) EF-AD " + "header (len=%ld)", (long) blen); + return -2; + } + + if (scard->sim_type == SCARD_GSM_SIM) { + file_size = (buf[2] << 8) | buf[3]; + } else { + if (scard_parse_fsp_templ(buf, blen, NULL, &file_size)) + return -3; + } + if (file_size == 3) { + wpa_printf(MSG_DEBUG, "SCARD: MNC length not available"); + return -7; + } + if (file_size < 4 || file_size > (int) sizeof(buf)) { + wpa_printf(MSG_DEBUG, "SCARD: invalid file length=%ld", + (long) file_size); + return -4; + } + + if (scard_read_file(scard, buf, file_size)) + return -5; + buf[3] = buf[3] & 0x0f; /* upper nibble reserved for future use */ + if (buf[3] < 2 || buf[3] > 3) { + wpa_printf(MSG_DEBUG, "SCARD: invalid MNC length=%ld", + (long) buf[3]); + return -6; + } + wpa_printf(MSG_DEBUG, "SCARD: MNC length=%ld", (long) buf[3]); + return buf[3]; +} + + +/** * scard_gsm_auth - Run GSM authentication command on SIM card * @scard: Pointer to private data from scard_init() * @_rand: 16-byte RAND value from HLR/AuC @@ -1236,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 543f7c598419a..b4ebc99835b6e 100644 --- a/src/utils/pcsc_funcs.h +++ b/src/utils/pcsc_funcs.h @@ -1,39 +1,14 @@ /* * 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 program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published 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. */ #ifndef PCSC_FUNCS_H #define PCSC_FUNCS_H -/* GSM files - * File type in first octet: - * 3F = Master File - * 7F = Dedicated File - * 2F = Elementary File under the Master File - * 6F = Elementary File under a Dedicated File - */ -#define SCARD_FILE_MF 0x3F00 -#define SCARD_FILE_GSM_DF 0x7F20 -#define SCARD_FILE_UMTS_DF 0x7F50 -#define SCARD_FILE_GSM_EF_IMSI 0x6F07 -#define SCARD_FILE_EF_DIR 0x2F00 -#define SCARD_FILE_EF_ICCID 0x2FE2 -#define SCARD_FILE_EF_CK 0x6FE1 -#define SCARD_FILE_EF_IK 0x6FE2 - -#define SCARD_CHV1_OFFSET 13 -#define SCARD_CHV1_FLAG 0x80 - typedef enum { SCARD_GSM_SIM_ONLY, SCARD_USIM_ONLY, @@ -42,26 +17,32 @@ 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); int scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len); +int scard_get_mnc_len(struct scard_data *scard); int scard_gsm_auth(struct scard_data *scard, const unsigned char *_rand, unsigned char *sres, unsigned char *kc); int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand, const unsigned char *autn, unsigned char *res, size_t *res_len, unsigned char *ik, unsigned char *ck, unsigned char *auts); +int scard_get_pin_retry_counter(struct scard_data *scard); +int scard_supports_umts(struct scard_data *scard); #else /* PCSC_FUNCS */ -#define scard_init(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/radiotap.h b/src/utils/radiotap.h index ba23ed38e7a00..137288f9a1e01 100644 --- a/src/utils/radiotap.h +++ b/src/utils/radiotap.h @@ -1,4 +1,4 @@ -/* $FreeBSD$ */ +/* $FreeBSD: src/sys/net80211/ieee80211_radiotap.h,v 1.5 2005/01/22 20:12:05 sam Exp $ */ /* $NetBSD: ieee80211_radiotap.h,v 1.11 2005/06/22 06:16:02 dyoung Exp $ */ /*- @@ -238,5 +238,6 @@ enum ieee80211_radiotap_type { * retries */ #define IEEE80211_RADIOTAP_F_TX_CTS 0x0002 /* used cts 'protection' */ #define IEEE80211_RADIOTAP_F_TX_RTS 0x0004 /* used rts/cts handshake */ +#define IEEE80211_RADIOTAP_F_TX_NOACK 0x0008 /* don't expect an ACK */ #endif /* IEEE80211_RADIOTAP_H */ diff --git a/src/utils/radiotap_iter.h b/src/utils/radiotap_iter.h index 92a798a67023a..2e0e87296ef57 100644 --- a/src/utils/radiotap_iter.h +++ b/src/utils/radiotap_iter.h @@ -1,3 +1,18 @@ +/* + * Radiotap parser + * + * Copyright 2007 Andy Green <andy@warmcat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + #ifndef __RADIOTAP_ITER_H #define __RADIOTAP_ITER_H diff --git a/src/utils/state_machine.h b/src/utils/state_machine.h index 31f667217f1ac..a51431578455c 100644 --- a/src/utils/state_machine.h +++ b/src/utils/state_machine.h @@ -2,14 +2,8 @@ * wpa_supplicant/hostapd - State machine definitions * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file includes a set of pre-processor macros that can be used to * implement a state machine. In addition to including this header file, each diff --git a/src/utils/trace.c b/src/utils/trace.c index bb3eb24d4c01c..6795d417d5d40 100644 --- a/src/utils/trace.c +++ b/src/utils/trace.c @@ -2,14 +2,8 @@ * Backtrace debugging * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/utils/trace.h b/src/utils/trace.h index 22d3de035acb1..38f43fbfab96f 100644 --- a/src/utils/trace.h +++ b/src/utils/trace.h @@ -2,14 +2,8 @@ * Backtrace debugging * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TRACE_H diff --git a/src/utils/uuid.c b/src/utils/uuid.c index d8cc26754b39d..2aa4bcb5fa19d 100644 --- a/src/utils/uuid.c +++ b/src/utils/uuid.c @@ -2,14 +2,8 @@ * Universally Unique IDentifier (UUID) * Copyright (c) 2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/utils/uuid.h b/src/utils/uuid.h index 0759165253578..5e860cbc59366 100644 --- a/src/utils/uuid.h +++ b/src/utils/uuid.h @@ -2,14 +2,8 @@ * Universally Unique IDentifier (UUID) * Copyright (c) 2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef UUID_H diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c index 6f6fc69ccc44a..5511ef193b0c6 100644 --- a/src/utils/wpa_debug.c +++ b/src/utils/wpa_debug.c @@ -2,14 +2,8 @@ * wpa_supplicant/hostapd / Debug prints * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -22,19 +16,55 @@ 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 */ + -#ifdef CONFIG_DEBUG_FILE -static FILE *out_file = NULL; -#endif /* CONFIG_DEBUG_FILE */ int wpa_debug_level = MSG_INFO; int wpa_debug_show_keys = 0; int wpa_debug_timestamp = 0; +#ifdef CONFIG_ANDROID_LOG + +#include <android/log.h> + +#ifndef ANDROID_LOG_NAME +#define ANDROID_LOG_NAME "wpa_supplicant" +#endif /* ANDROID_LOG_NAME */ + +static int wpa_to_android_level(int level) +{ + if (level == MSG_ERROR) + return ANDROID_LOG_ERROR; + if (level == MSG_WARNING) + return ANDROID_LOG_WARN; + if (level == MSG_INFO) + return ANDROID_LOG_INFO; + return ANDROID_LOG_DEBUG; +} + +#endif /* CONFIG_ANDROID_LOG */ + #ifndef CONFIG_NO_STDOUT_DEBUG +#ifdef CONFIG_DEBUG_FILE +static FILE *out_file = NULL; +#endif /* CONFIG_DEBUG_FILE */ + + void wpa_debug_print_timestamp(void) { +#ifndef CONFIG_ANDROID_LOG struct os_time tv; if (!wpa_debug_timestamp) @@ -48,13 +78,18 @@ 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 */ } #ifdef CONFIG_DEBUG_SYSLOG +#ifndef LOG_HOSTAPD +#define LOG_HOSTAPD LOG_DAEMON +#endif /* LOG_HOSTAPD */ + void wpa_debug_open_syslog(void) { - openlog("wpa_supplicant", LOG_PID | LOG_NDELAY, LOG_DAEMON); + openlog("wpa_supplicant", LOG_PID | LOG_NDELAY, LOG_HOSTAPD); wpa_debug_syslog++; } @@ -84,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 @@ -101,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); @@ -121,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 */ } @@ -130,8 +252,97 @@ 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; + char *strbuf = NULL; + + if (buf == NULL) { + display = " [NULL]"; + } else if (len == 0) { + display = ""; + } else if (show && len) { + strbuf = os_malloc(1 + 3 * len); + if (strbuf == NULL) { + wpa_printf(MSG_ERROR, "wpa_hexdump: Failed to " + "allocate message buffer"); + return; + } + + for (i = 0; i < len; i++) + os_snprintf(&strbuf[i * 3], 4, " %02x", + buf[i]); + + display = strbuf; + } else { + display = " [REMOVED]"; + } + + syslog(syslog_priority(level), "%s - hexdump(len=%lu):%s", + title, (unsigned long) len, display); + os_free(strbuf); + return; + } +#endif /* CONFIG_DEBUG_SYSLOG */ wpa_debug_print_timestamp(); #ifdef CONFIG_DEBUG_FILE if (out_file) { @@ -161,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) @@ -182,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) { @@ -257,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 */ } @@ -273,11 +508,43 @@ void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, } +#ifdef CONFIG_DEBUG_FILE +static char *last_path = NULL; +#endif /* CONFIG_DEBUG_FILE */ + +int wpa_debug_reopen_file(void) +{ +#ifdef CONFIG_DEBUG_FILE + int rv; + if (last_path) { + char *tmp = os_strdup(last_path); + wpa_debug_close_file(); + rv = wpa_debug_open_file(tmp); + os_free(tmp); + } else { + wpa_printf(MSG_ERROR, "Last-path was not set, cannot " + "re-open log file."); + rv = -1; + } + return rv; +#else /* CONFIG_DEBUG_FILE */ + return 0; +#endif /* CONFIG_DEBUG_FILE */ +} + + int wpa_debug_open_file(const char *path) { #ifdef CONFIG_DEBUG_FILE if (!path) return 0; + + if (last_path == NULL || os_strcmp(last_path, path) != 0) { + /* Save our path to enable re-open */ + os_free(last_path); + last_path = os_strdup(path); + } + out_file = fopen(path, "a"); if (out_file == NULL) { wpa_printf(MSG_ERROR, "wpa_debug_open_file: Failed to open " @@ -299,6 +566,8 @@ void wpa_debug_close_file(void) return; fclose(out_file); out_file = NULL; + os_free(last_path); + last_path = NULL; #endif /* CONFIG_DEBUG_FILE */ } @@ -314,12 +583,21 @@ void wpa_msg_register_cb(wpa_msg_cb_func func) } +static wpa_msg_get_ifname_func wpa_msg_ifname_cb = NULL; + +void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func) +{ + wpa_msg_ifname_cb = func; +} + + void wpa_msg(void *ctx, int level, const char *fmt, ...) { va_list ap; char *buf; const int buflen = 2048; int len; + char prefix[130]; buf = os_malloc(buflen); if (buf == NULL) { @@ -328,9 +606,19 @@ void wpa_msg(void *ctx, int level, const char *fmt, ...) return; } va_start(ap, fmt); + prefix[0] = '\0'; + if (wpa_msg_ifname_cb) { + const char *ifname = wpa_msg_ifname_cb(ctx); + if (ifname) { + int res = os_snprintf(prefix, sizeof(prefix), "%s: ", + ifname); + if (res < 0 || res >= (int) sizeof(prefix)) + prefix[0] = '\0'; + } + } len = vsnprintf(buf, buflen, fmt, ap); va_end(ap); - wpa_printf(level, "%s", buf); + wpa_printf(level, "%s%s", prefix, buf); if (wpa_msg_cb) wpa_msg_cb(ctx, level, buf, len); os_free(buf); diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h index 6e5e79e281c41..339c749ce5820 100644 --- a/src/utils/wpa_debug.h +++ b/src/utils/wpa_debug.h @@ -2,14 +2,8 @@ * wpa_supplicant/hostapd / Debug prints * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_DEBUG_H @@ -20,7 +14,9 @@ /* Debugging function - conditional printf and hex dump. Driver wrappers can * use these for debugging purposes. */ -enum { MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR }; +enum { + MSG_EXCESSIVE, MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR +}; #ifdef CONFIG_NO_STDOUT_DEBUG @@ -34,10 +30,17 @@ enum { MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR }; #define wpa_hexdump_ascii_key(l,t,b,le) do { } while (0) #define wpa_debug_open_file(p) do { } while (0) #define wpa_debug_close_file() do { } while (0) +#define wpa_dbg(args...) do { } while (0) + +static inline int wpa_debug_reopen_file(void) +{ + return 0; +} #else /* CONFIG_NO_STDOUT_DEBUG */ int wpa_debug_open_file(const char *path); +int wpa_debug_reopen_file(void); void wpa_debug_close_file(void); /** @@ -79,7 +82,8 @@ void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len); static inline void wpa_hexdump_buf(int level, const char *title, const struct wpabuf *buf) { - wpa_hexdump(level, title, wpabuf_head(buf), wpabuf_len(buf)); + wpa_hexdump(level, title, buf ? wpabuf_head(buf) : NULL, + buf ? wpabuf_len(buf) : 0); } /** @@ -100,7 +104,8 @@ void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len); static inline void wpa_hexdump_buf_key(int level, const char *title, const struct wpabuf *buf) { - wpa_hexdump_key(level, title, wpabuf_head(buf), wpabuf_len(buf)); + wpa_hexdump_key(level, title, buf ? wpabuf_head(buf) : NULL, + buf ? wpabuf_len(buf) : 0); } /** @@ -136,6 +141,14 @@ void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, size_t len); +/* + * wpa_dbg() behaves like wpa_msg(), but it can be removed from build to reduce + * binary size. As such, it should be used with debugging messages that are not + * needed in the control interface while wpa_msg() has to be used for anything + * that needs to shown to control interface monitors. + */ +#define wpa_dbg(args...) wpa_msg(args) + #endif /* CONFIG_NO_STDOUT_DEBUG */ @@ -143,6 +156,7 @@ void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, #define wpa_msg(args...) do { } while (0) #define wpa_msg_ctrl(args...) do { } while (0) #define wpa_msg_register_cb(f) do { } while (0) +#define wpa_msg_register_ifname_cb(f) do { } while (0) #else /* CONFIG_NO_WPA_MSG */ /** * wpa_msg - Conditional printf for default target and ctrl_iface monitors @@ -183,8 +197,11 @@ typedef void (*wpa_msg_cb_func)(void *ctx, int level, const char *txt, * @func: Callback function (%NULL to unregister) */ void wpa_msg_register_cb(wpa_msg_cb_func func); -#endif /* CONFIG_NO_WPA_MSG */ +typedef const char * (*wpa_msg_get_ifname_func)(void *ctx); +void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func); + +#endif /* CONFIG_NO_WPA_MSG */ #ifdef CONFIG_NO_HOSTAPD_LOGGER #define hostapd_logger(args...) do { } while (0) @@ -238,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/utils/wpabuf.c b/src/utils/wpabuf.c index eda779eaff4fd..b257b365c756c 100644 --- a/src/utils/wpabuf.c +++ b/src/utils/wpabuf.c @@ -1,15 +1,9 @@ /* * Dynamic data buffer - * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-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 - * published 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" @@ -74,12 +68,12 @@ int wpabuf_resize(struct wpabuf **_buf, size_t add_len) if (buf->used + add_len > buf->size) { unsigned char *nbuf; - if (buf->ext_data) { - nbuf = os_realloc(buf->ext_data, buf->used + add_len); + if (buf->flags & WPABUF_FLAG_EXT_DATA) { + nbuf = os_realloc(buf->buf, buf->used + add_len); if (nbuf == NULL) return -1; os_memset(nbuf + buf->used, 0, add_len); - buf->ext_data = nbuf; + buf->buf = nbuf; } else { #ifdef WPA_TRACE nbuf = os_realloc(trace, sizeof(struct wpabuf_trace) + @@ -101,6 +95,7 @@ int wpabuf_resize(struct wpabuf **_buf, size_t add_len) os_memset(nbuf + sizeof(struct wpabuf) + buf->used, 0, add_len); #endif /* WPA_TRACE */ + buf->buf = (u8 *) (buf + 1); *_buf = buf; } buf->size = buf->used + add_len; @@ -132,6 +127,7 @@ struct wpabuf * wpabuf_alloc(size_t len) #endif /* WPA_TRACE */ buf->size = len; + buf->buf = (u8 *) (buf + 1); return buf; } @@ -154,7 +150,8 @@ struct wpabuf * wpabuf_alloc_ext_data(u8 *data, size_t len) buf->size = len; buf->used = len; - buf->ext_data = data; + buf->buf = data; + buf->flags |= WPABUF_FLAG_EXT_DATA; return buf; } @@ -195,12 +192,14 @@ void wpabuf_free(struct wpabuf *buf) wpa_trace_show("wpabuf_free magic mismatch"); abort(); } - os_free(buf->ext_data); + if (buf->flags & WPABUF_FLAG_EXT_DATA) + os_free(buf->buf); os_free(trace); #else /* WPA_TRACE */ if (buf == NULL) return; - os_free(buf->ext_data); + if (buf->flags & WPABUF_FLAG_EXT_DATA) + os_free(buf->buf); os_free(buf); #endif /* WPA_TRACE */ } diff --git a/src/utils/wpabuf.h b/src/utils/wpabuf.h index a150455a5a65b..dbce925ca1bbb 100644 --- a/src/utils/wpabuf.h +++ b/src/utils/wpabuf.h @@ -1,20 +1,17 @@ /* * Dynamic data buffer - * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-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 - * published 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. */ #ifndef WPABUF_H #define WPABUF_H +/* wpabuf::buf is a pointer to external data */ +#define WPABUF_FLAG_EXT_DATA BIT(0) + /* * Internal data structure for wpabuf. Please do not touch this directly from * elsewhere. This is only defined in header file to allow inline functions @@ -23,8 +20,8 @@ struct wpabuf { size_t size; /* total size of the allocated buffer */ size_t used; /* length of data in the buffer */ - u8 *ext_data; /* pointer to external data; NULL if data follows - * struct wpabuf */ + u8 *buf; /* pointer to the head of the buffer */ + unsigned int flags; /* optionally followed by the allocated buffer */ }; @@ -78,9 +75,7 @@ static inline size_t wpabuf_tailroom(const struct wpabuf *buf) */ static inline const void * wpabuf_head(const struct wpabuf *buf) { - if (buf->ext_data) - return buf->ext_data; - return buf + 1; + return buf->buf; } static inline const u8 * wpabuf_head_u8(const struct wpabuf *buf) @@ -95,9 +90,7 @@ static inline const u8 * wpabuf_head_u8(const struct wpabuf *buf) */ static inline void * wpabuf_mhead(struct wpabuf *buf) { - if (buf->ext_data) - return buf->ext_data; - return buf + 1; + return buf->buf; } static inline u8 * wpabuf_mhead_u8(struct wpabuf *buf) @@ -117,6 +110,12 @@ static inline void wpabuf_put_le16(struct wpabuf *buf, u16 data) WPA_PUT_LE16(pos, data); } +static inline void wpabuf_put_le32(struct wpabuf *buf, u32 data) +{ + u8 *pos = wpabuf_put(buf, 4); + WPA_PUT_LE32(pos, data); +} + static inline void wpabuf_put_be16(struct wpabuf *buf, u16 data) { u8 *pos = wpabuf_put(buf, 2); @@ -150,7 +149,8 @@ static inline void wpabuf_put_buf(struct wpabuf *dst, static inline void wpabuf_set(struct wpabuf *buf, const void *data, size_t len) { - buf->ext_data = (u8 *) data; + buf->buf = (u8 *) data; + buf->flags = WPABUF_FLAG_EXT_DATA; buf->size = buf->used = len; } diff --git a/src/wps/http_client.c b/src/wps/http_client.c index fea2a04ea973e..c6d6c7fdab2af 100644 --- a/src/wps/http_client.c +++ b/src/wps/http_client.c @@ -2,14 +2,8 @@ * http_client - HTTP client * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -21,7 +15,7 @@ #include "http_client.h" -#define HTTP_CLIENT_TIMEOUT 30 +#define HTTP_CLIENT_TIMEOUT_SEC 30 struct http_client { @@ -42,7 +36,7 @@ struct http_client { static void http_client_timeout(void *eloop_data, void *user_ctx) { struct http_client *c = eloop_data; - wpa_printf(MSG_DEBUG, "HTTP: Timeout"); + wpa_printf(MSG_DEBUG, "HTTP: Timeout (c=%p)", c); c->cb(c->cb_ctx, c, HTTP_CLIENT_TIMEOUT); } @@ -52,6 +46,9 @@ static void http_client_got_response(struct httpread *handle, void *cookie, { struct http_client *c = cookie; + wpa_printf(MSG_DEBUG, "HTTP: httpread callback: handle=%p cookie=%p " + "e=%d", handle, cookie, e); + eloop_cancel_timeout(http_client_timeout, c, NULL); switch (e) { case HTTPREAD_EVENT_FILE_READY: @@ -122,7 +119,7 @@ static void http_client_tx_ready(int sock, void *eloop_ctx, void *sock_ctx) c->req = NULL; c->hread = httpread_create(c->sd, http_client_got_response, c, - c->max_response, HTTP_CLIENT_TIMEOUT); + c->max_response, HTTP_CLIENT_TIMEOUT_SEC); if (c->hread == NULL) { c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED); return; @@ -181,8 +178,8 @@ struct http_client * http_client_addr(struct sockaddr_in *dst, return NULL; } - if (eloop_register_timeout(HTTP_CLIENT_TIMEOUT, 0, http_client_timeout, - c, NULL)) { + if (eloop_register_timeout(HTTP_CLIENT_TIMEOUT_SEC, 0, + http_client_timeout, c, NULL)) { http_client_free(c); return NULL; } diff --git a/src/wps/http_client.h b/src/wps/http_client.h index 924d6ab4a2a46..ddee2adb699a6 100644 --- a/src/wps/http_client.h +++ b/src/wps/http_client.h @@ -2,14 +2,8 @@ * http_client - HTTP client * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef HTTP_CLIENT_H diff --git a/src/wps/http_server.c b/src/wps/http_server.c index 356f599abe059..6ca32140ae83a 100644 --- a/src/wps/http_server.c +++ b/src/wps/http_server.c @@ -2,14 +2,8 @@ * http_server - HTTP server * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * 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/http_server.h b/src/wps/http_server.h index 219941c5ab5a5..4b2b749f16fcf 100644 --- a/src/wps/http_server.h +++ b/src/wps/http_server.h @@ -2,14 +2,8 @@ * http_server - HTTP server * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef HTTP_SERVER_H diff --git a/src/wps/httpread.c b/src/wps/httpread.c index 40422e4651d35..ad4f4a1dcb8a1 100644 --- a/src/wps/httpread.c +++ b/src/wps/httpread.c @@ -3,14 +3,8 @@ * Author: Ted Merrill * Copyright 2008 Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * The files are buffered via internal callbacks from eloop, then presented to * an application callback routine when completely read into memory. May also diff --git a/src/wps/httpread.h b/src/wps/httpread.h index 51aa214946659..454b618bf4aed 100644 --- a/src/wps/httpread.h +++ b/src/wps/httpread.h @@ -3,14 +3,8 @@ * Author: Ted Merrill * Copyright 2008 Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef HTTPREAD_H diff --git a/src/wps/ndef.c b/src/wps/ndef.c index 9baec7f4b27c2..a48a2d7cea1c6 100644 --- a/src/wps/ndef.c +++ b/src/wps/ndef.c @@ -1,34 +1,28 @@ /* * 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" #include "common.h" #include "wps/wps.h" -#include "wps/wps_i.h" #define FLAG_MESSAGE_BEGIN (1 << 7) #define FLAG_MESSAGE_END (1 << 6) #define FLAG_CHUNK (1 << 5) #define FLAG_SHORT_RECORD (1 << 4) #define FLAG_ID_LENGTH_PRESENT (1 << 3) +#define FLAG_TNF_NFC_FORUM (0x01) #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,90 @@ 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); +} + + +struct wpabuf * ndef_build_wifi_hr(void) +{ + struct wpabuf *rn, *cr, *ac_payload, *ac, *hr_payload, *hr; + struct wpabuf *carrier, *hc; + + rn = wpabuf_alloc(2); + if (rn == NULL) + return NULL; + wpabuf_put_be16(rn, os_random() & 0xffff); + + cr = ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_TNF_NFC_FORUM, "cr", 2, + NULL, 0, rn); + wpabuf_free(rn); + + if (cr == NULL) + return NULL; + + ac_payload = wpabuf_alloc(4); + if (ac_payload == NULL) { + wpabuf_free(cr); + return NULL; + } + wpabuf_put_u8(ac_payload, 0x01); /* Carrier Flags: CRS=1 "active" */ + wpabuf_put_u8(ac_payload, 0x01); /* Carrier Data Reference Length */ + wpabuf_put_u8(ac_payload, '0'); /* Carrier Data Reference: "0" */ + wpabuf_put_u8(ac_payload, 0); /* Aux Data Reference Count */ + + ac = ndef_build_record(FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "ac", 2, + NULL, 0, ac_payload); + wpabuf_free(ac_payload); + if (ac == NULL) { + wpabuf_free(cr); + return NULL; + } + + hr_payload = wpabuf_alloc(1 + wpabuf_len(cr) + wpabuf_len(ac)); + if (hr_payload == NULL) { + wpabuf_free(cr); + wpabuf_free(ac); + return NULL; + } + + wpabuf_put_u8(hr_payload, 0x12); /* Connection Handover Version 1.2 */ + wpabuf_put_buf(hr_payload, cr); + wpabuf_put_buf(hr_payload, ac); + wpabuf_free(cr); + wpabuf_free(ac); + + hr = ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_TNF_NFC_FORUM, "Hr", 2, + NULL, 0, hr_payload); + wpabuf_free(hr_payload); + if (hr == NULL) + return NULL; + + carrier = wpabuf_alloc(2 + os_strlen(wifi_handover_type)); + if (carrier == NULL) { + wpabuf_free(hr); + return NULL; + } + wpabuf_put_u8(carrier, 0x02); /* Carrier Type Format */ + wpabuf_put_u8(carrier, os_strlen(wifi_handover_type)); + wpabuf_put_str(carrier, wifi_handover_type); + + hc = ndef_build_record(FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "Hc", 2, + "0", 1, carrier); + wpabuf_free(carrier); + if (hc == NULL) { + wpabuf_free(hr); + return NULL; + } + + return wpabuf_concat(hr, hc); } diff --git a/src/wps/upnp_xml.c b/src/wps/upnp_xml.c index b1b1e2b165dd0..a9958eeda80d1 100644 --- a/src/wps/upnp_xml.c +++ b/src/wps/upnp_xml.c @@ -75,8 +75,8 @@ * Note that angle brackets present in the original data must have been encoded * as < and > so they will not trouble us. */ -static int xml_next_tag(const char *in, const char **out, - const char **out_tagname, const char **end) +int xml_next_tag(const char *in, const char **out, + const char **out_tagname, const char **end) { while (*in && *in != '<') in++; diff --git a/src/wps/upnp_xml.h b/src/wps/upnp_xml.h index 62dbe602de6f3..616af3dadc804 100644 --- a/src/wps/upnp_xml.h +++ b/src/wps/upnp_xml.h @@ -16,6 +16,8 @@ void xml_data_encode(struct wpabuf *buf, const char *data, int len); void xml_add_tagged_data(struct wpabuf *buf, const char *tag, const char *data); +int xml_next_tag(const char *in, const char **out, + const char **out_tagname, const char **end); char * xml_get_first_item(const char *doc, const char *item); struct wpabuf * xml_get_base64_item(const char *data, const char *name, enum http_reply_code *ret); diff --git a/src/wps/wps.c b/src/wps/wps.c index 619af158df597..2575705819cbf 100644 --- a/src/wps/wps.c +++ b/src/wps/wps.c @@ -2,14 +2,8 @@ * Wi-Fi Protected Setup * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -21,6 +15,12 @@ #include "wps_dev_attr.h" +#ifdef CONFIG_WPS_TESTING +int wps_version_number = 0x20; +int wps_testing_dummy_cred = 0; +#endif /* CONFIG_WPS_TESTING */ + + /** * wps_init - Initialize WPS Registration protocol data * @cfg: WPS configuration @@ -45,8 +45,7 @@ struct wps_data * wps_init(const struct wps_config *cfg) os_memcpy(data->uuid_e, cfg->wps->uuid, WPS_UUID_LEN); } if (cfg->pin) { - data->dev_pw_id = data->wps->oob_dev_pw_id == 0 ? - DEV_PW_DEFAULT : data->wps->oob_dev_pw_id; + data->dev_pw_id = cfg->dev_pw_id; data->dev_password = os_malloc(cfg->pin_len); if (data->dev_password == NULL) { os_free(data); @@ -56,17 +55,33 @@ 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 */ data->dev_pw_id = DEV_PW_PUSHBUTTON; os_free(data->dev_password); - data->dev_password = os_malloc(8); + data->dev_password = (u8 *) os_strdup("00000000"); if (data->dev_password == NULL) { os_free(data); return NULL; } - os_memset(data->dev_password, '0', 8); data->dev_password_len = 8; } @@ -94,6 +109,7 @@ struct wps_data * wps_init(const struct wps_config *cfg) data->new_ap_settings = os_malloc(sizeof(*data->new_ap_settings)); if (data->new_ap_settings == NULL) { + os_free(data->dev_password); os_free(data); return NULL; } @@ -103,8 +119,11 @@ struct wps_data * wps_init(const struct wps_config *cfg) if (cfg->peer_addr) os_memcpy(data->peer_dev.mac_addr, cfg->peer_addr, ETH_ALEN); + if (cfg->p2p_dev_addr) + os_memcpy(data->p2p_dev_addr, cfg->p2p_dev_addr, ETH_ALEN); data->use_psk_key = cfg->use_psk_key; + data->pbc_in_m1 = cfg->pbc_in_m1; return data; } @@ -116,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"); @@ -134,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); } @@ -201,19 +227,19 @@ int wps_is_selected_pbc_registrar(const struct wpabuf *msg) WPA_GET_BE16(attr.dev_password_id) != DEV_PW_PUSHBUTTON) return 0; +#ifdef CONFIG_WPS_STRICT + if (!attr.sel_reg_config_methods || + !(WPA_GET_BE16(attr.sel_reg_config_methods) & + WPS_CONFIG_PUSHBUTTON)) + return 0; +#endif /* CONFIG_WPS_STRICT */ + return 1; } -/** - * wps_is_selected_pin_registrar - Check whether WPS IE indicates active PIN - * @msg: WPS IE contents from Beacon or Probe Response frame - * Returns: 1 if PIN Registrar is active, 0 if not - */ -int wps_is_selected_pin_registrar(const struct wpabuf *msg) +static int is_selected_pin_registrar(struct wps_parse_attr *attr) { - struct wps_parse_attr attr; - /* * In theory, this could also verify that attr.sel_reg_config_methods * includes WPS_CONFIG_LABEL, WPS_CONFIG_DISPLAY, or WPS_CONFIG_KEYPAD, @@ -222,21 +248,114 @@ int wps_is_selected_pin_registrar(const struct wpabuf *msg) * Device Password ID here. */ - if (wps_parse_msg(msg, &attr) < 0) + if (!attr->selected_registrar || *attr->selected_registrar == 0) return 0; - if (!attr.selected_registrar || *attr.selected_registrar == 0) + if (attr->dev_password_id != NULL && + WPA_GET_BE16(attr->dev_password_id) == DEV_PW_PUSHBUTTON) return 0; - if (attr.dev_password_id != NULL && - WPA_GET_BE16(attr.dev_password_id) == DEV_PW_PUSHBUTTON) +#ifdef CONFIG_WPS_STRICT + if (!attr->sel_reg_config_methods || + !(WPA_GET_BE16(attr->sel_reg_config_methods) & + (WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD))) return 0; +#endif /* CONFIG_WPS_STRICT */ return 1; } /** + * wps_is_selected_pin_registrar - Check whether WPS IE indicates active PIN + * @msg: WPS IE contents from Beacon or Probe Response frame + * Returns: 1 if PIN Registrar is active, 0 if not + */ +int wps_is_selected_pin_registrar(const struct wpabuf *msg) +{ + struct wps_parse_attr attr; + + if (wps_parse_msg(msg, &attr) < 0) + return 0; + + return is_selected_pin_registrar(&attr); +} + + +/** + * wps_is_addr_authorized - Check whether WPS IE authorizes MAC address + * @msg: WPS IE contents from Beacon or Probe Response frame + * @addr: MAC address to search for + * @ver1_compat: Whether to use version 1 compatibility mode + * Returns: 2 if the specified address is explicit authorized, 1 if address is + * authorized (broadcast), 0 if not + */ +int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr, + int ver1_compat) +{ + struct wps_parse_attr attr; + unsigned int i; + const u8 *pos; + const u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + if (wps_parse_msg(msg, &attr) < 0) + return 0; + + if (!attr.version2 && ver1_compat) { + /* + * Version 1.0 AP - AuthorizedMACs not used, so revert back to + * old mechanism of using SelectedRegistrar. + */ + return is_selected_pin_registrar(&attr); + } + + if (!attr.authorized_macs) + return 0; + + pos = attr.authorized_macs; + for (i = 0; i < attr.authorized_macs_len / ETH_ALEN; i++) { + if (os_memcmp(pos, addr, ETH_ALEN) == 0) + return 2; + if (os_memcmp(pos, bcast, ETH_ALEN) == 0) + return 1; + pos += ETH_ALEN; + } + + return 0; +} + + +/** + * wps_ap_priority_compar - Prioritize WPS IE from two APs + * @wps_a: WPS IE contents from Beacon or Probe Response frame + * @wps_b: WPS IE contents from Beacon or Probe Response frame + * Returns: 1 if wps_b is considered more likely selection for WPS + * provisioning, -1 if wps_a is considered more like, or 0 if no preference + */ +int wps_ap_priority_compar(const struct wpabuf *wps_a, + const struct wpabuf *wps_b) +{ + struct wps_parse_attr attr_a, attr_b; + int sel_a, sel_b; + + if (wps_a == NULL || wps_parse_msg(wps_a, &attr_a) < 0) + return 1; + if (wps_b == NULL || wps_parse_msg(wps_b, &attr_b) < 0) + return -1; + + sel_a = attr_a.selected_registrar && *attr_a.selected_registrar != 0; + sel_b = attr_b.selected_registrar && *attr_b.selected_registrar != 0; + + if (sel_a && !sel_b) + return -1; + if (!sel_a && sel_b) + return 1; + + return 0; +} + + +/** * wps_get_uuid_e - Get UUID-E from WPS IE * @msg: WPS IE contents from Beacon or Probe Response frame * Returns: Pointer to UUID-E or %NULL if not included @@ -255,6 +374,19 @@ const u8 * wps_get_uuid_e(const struct wpabuf *msg) /** + * wps_is_20 - Check whether WPS attributes claim support for WPS 2.0 + */ +int wps_is_20(const struct wpabuf *msg) +{ + struct wps_parse_attr attr; + + if (msg == NULL || wps_parse_msg(msg, &attr) < 0) + return 0; + return attr.version2 != NULL; +} + + +/** * wps_build_assoc_req_ie - Build WPS IE for (Re)Association Request * @req_type: Value for Request Type attribute * Returns: WPS IE or %NULL on failure @@ -277,7 +409,8 @@ struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type) wpabuf_put_be32(ie, WPS_DEV_OUI_WFA); if (wps_build_version(ie) || - wps_build_req_type(ie, req_type)) { + wps_build_req_type(ie, req_type) || + wps_build_wfa_ext(ie, 0, NULL, 0)) { wpabuf_free(ie); return NULL; } @@ -310,7 +443,8 @@ struct wpabuf * wps_build_assoc_resp_ie(void) wpabuf_put_be32(ie, WPS_DEV_OUI_WFA); if (wps_build_version(ie) || - wps_build_resp_type(ie, WPS_RESP_AP)) { + wps_build_resp_type(ie, WPS_RESP_AP) || + wps_build_wfa_ext(ie, 0, NULL, 0)) { wpabuf_free(ie); return NULL; } @@ -323,62 +457,64 @@ 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 + * @num_req_dev_types: Number of requested device types + * @req_dev_types: Requested device types (8 * num_req_dev_types octets) or + * %NULL if none * Returns: WPS IE or %NULL on failure * * The caller is responsible for freeing the buffer. */ -struct wpabuf * wps_build_probe_req_ie(int pbc, struct wps_device_data *dev, +struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev, const u8 *uuid, - enum wps_request_type req_type) + enum wps_request_type req_type, + unsigned int num_req_dev_types, + const u8 *req_dev_types) { struct wpabuf *ie; - u8 *len; - u16 methods; wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for Probe Request"); - ie = wpabuf_alloc(200); + ie = wpabuf_alloc(500); if (ie == NULL) return NULL; - wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); - len = wpabuf_put(ie, 1); - wpabuf_put_be32(ie, WPS_DEV_OUI_WFA); - - if (pbc) - methods = WPS_CONFIG_PUSHBUTTON; - else { - methods = WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY | - WPS_CONFIG_KEYPAD; -#ifdef CONFIG_WPS_UFD - methods |= WPS_CONFIG_USBA; -#endif /* CONFIG_WPS_UFD */ -#ifdef CONFIG_WPS_NFC - methods |= WPS_CONFIG_NFC_INTERFACE; -#endif /* CONFIG_WPS_NFC */ - } - if (wps_build_version(ie) || wps_build_req_type(ie, req_type) || - wps_build_config_methods(ie, methods) || + wps_build_config_methods(ie, dev->config_methods) || wps_build_uuid_e(ie, uuid) || wps_build_primary_dev_type(dev, ie) || wps_build_rf_bands(dev, ie) || 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) || + wps_build_model_number(dev, ie) || + wps_build_dev_name(dev, ie) || + wps_build_wfa_ext(ie, req_type == WPS_REQ_ENROLLEE, NULL, 0) || +#endif /* CONFIG_WPS2 */ + wps_build_req_dev_type(dev, ie, num_req_dev_types, req_dev_types) + || + wps_build_secondary_dev_type(dev, ie) + ) { wpabuf_free(ie); return NULL; } - *len = wpabuf_len(ie) - 2; +#ifndef CONFIG_WPS2 + if (dev->p2p && wps_build_dev_name(dev, ie)) { + wpabuf_free(ie); + return NULL; + } +#endif /* CONFIG_WPS2 */ - return ie; + return wps_ie_encapsulate(ie); } diff --git a/src/wps/wps.h b/src/wps/wps.h index 1fd1e52bbd77c..c6b7099bfdb89 100644 --- a/src/wps/wps.h +++ b/src/wps/wps.h @@ -1,15 +1,9 @@ /* * Wi-Fi Protected Setup - * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-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 - * published 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. */ #ifndef WPS_H @@ -33,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 @@ -47,6 +42,7 @@ struct wps_er; * @cred_attr: Unparsed Credential attribute data (used only in cred_cb()); * this may be %NULL, if not used * @cred_attr_len: Length of cred_attr in octets + * @ap_channel: AP channel */ struct wps_credential { u8 ssid[32]; @@ -59,10 +55,18 @@ struct wps_credential { u8 mac_addr[ETH_ALEN]; const u8 *cred_attr; size_t cred_attr_len; + u16 ap_channel; }; #define WPS_DEV_TYPE_LEN 8 #define WPS_DEV_TYPE_BUFSIZE 21 +#define WPS_SEC_DEV_TYPE_MAX_LEN 128 +/* maximum number of advertised WPS vendor extension attributes */ +#define MAX_WPS_VENDOR_EXTENSIONS 10 +/* maximum size of WPS Vendor extension attribute */ +#define WPS_MAX_VENDOR_EXT_LEN 1024 +/* maximum number of parsed WPS vendor extension attributes */ +#define MAX_WPS_PARSE_VENDOR_EXT 10 /** * struct wps_device_data - WPS Device Data @@ -73,8 +77,11 @@ struct wps_credential { * @model_number: Model Number (0..32 octets encoded in UTF-8) * @serial_number: Serial Number (0..32 octets encoded in UTF-8) * @pri_dev_type: Primary Device Type + * @sec_dev_type: Array of secondary device types + * @num_sec_dev_type: Number of secondary device types * @os_version: OS Version * @rf_bands: RF bands (WPS_RF_24GHZ, WPS_RF_50GHZ flags) + * @p2p: Whether the device is a P2P device */ struct wps_device_data { u8 mac_addr[ETH_ALEN]; @@ -84,19 +91,16 @@ struct wps_device_data { char *model_number; char *serial_number; u8 pri_dev_type[WPS_DEV_TYPE_LEN]; +#define WPS_SEC_DEVICE_TYPES 5 + u8 sec_dev_type[WPS_SEC_DEVICE_TYPES][WPS_DEV_TYPE_LEN]; + u8 num_sec_dev_types; u32 os_version; u8 rf_bands; -}; + u16 config_methods; + struct wpabuf *vendor_ext_m1; + struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS]; -struct oob_conf_data { - enum { - OOB_METHOD_UNKNOWN = 0, - OOB_METHOD_DEV_PWD_E, - OOB_METHOD_DEV_PWD_R, - OOB_METHOD_CRED, - } oob_method; - struct wpabuf *dev_password; - struct wpabuf *pubkey_hash; + int p2p; }; /** @@ -156,6 +160,29 @@ struct wps_config { * struct wpa_context::psk. */ int use_psk_key; + + /** + * dev_pw_id - Device Password ID for Enrollee when PIN is used + */ + u16 dev_pw_id; + + /** + * p2p_dev_addr - P2P Device Address from (Re)Association Request + * + * On AP/GO, this is set to the P2P Device Address of the associating + * P2P client if a P2P IE is included in the (Re)Association Request + * frame and the P2P Device Address is included. Otherwise, this is set + * to %NULL to indicate the station does not have a P2P Device Address. + */ + const u8 *p2p_dev_addr; + + /** + * pbc_in_m1 - Do not remove PushButton config method in M1 (AP) + * + * This can be used to enable a workaround to allow Windows 7 to use + * PBC with the AP. + */ + int pbc_in_m1; }; struct wps_data * wps_init(const struct wps_config *cfg); @@ -195,13 +222,20 @@ struct wpabuf * wps_get_msg(struct wps_data *wps, enum wsc_op_code *op_code); int wps_is_selected_pbc_registrar(const struct wpabuf *msg); int wps_is_selected_pin_registrar(const struct wpabuf *msg); +int wps_ap_priority_compar(const struct wpabuf *wps_a, + const struct wpabuf *wps_b); +int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr, + int ver1_compat); const u8 * wps_get_uuid_e(const struct wpabuf *msg); +int wps_is_20(const struct wpabuf *msg); struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type); struct wpabuf * wps_build_assoc_resp_ie(void); -struct wpabuf * wps_build_probe_req_ie(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); + enum wps_request_type req_type, + unsigned int num_req_dev_types, + const u8 *req_dev_types); /** @@ -253,12 +287,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 @@ -340,6 +377,11 @@ struct wps_registrar_config { * static_wep_only - Whether the BSS supports only static WEP */ int static_wep_only; + + /** + * dualband - Whether this is a concurrent dualband AP + */ + int dualband; }; @@ -395,7 +437,22 @@ enum wps_event { /** * WPS_EV_ER_ENROLLEE_REMOVE - ER: Enrollee removed */ - WPS_EV_ER_ENROLLEE_REMOVE + WPS_EV_ER_ENROLLEE_REMOVE, + + /** + * WPS_EV_ER_AP_SETTINGS - ER: AP Settings learned + */ + WPS_EV_ER_AP_SETTINGS, + + /** + * WPS_EV_ER_SET_SELECTED_REGISTRAR - ER: SetSelectedRegistrar event + */ + WPS_EV_ER_SET_SELECTED_REGISTRAR, + + /** + * WPS_EV_AP_PIN_SUCCESS - External Registrar used correct AP PIN + */ + WPS_EV_AP_PIN_SUCCESS }; /** @@ -428,6 +485,8 @@ union wps_event_data { */ struct wps_event_fail { int msg; + u16 config_error; + u16 error_indication; } fail; struct wps_event_pwd_auth_fail { @@ -464,6 +523,23 @@ union wps_event_data { const char *model_number; const char *serial_number; } enrollee; + + struct wps_event_er_ap_settings { + const u8 *uuid; + const struct wps_credential *cred; + } ap_settings; + + struct wps_event_er_set_selected_registrar { + const u8 *uuid; + int sel_reg; + u16 dev_passwd_id; + u16 sel_reg_config_methods; + enum { + WPS_ER_SET_SEL_REG_START, + WPS_ER_SET_SEL_REG_DONE, + WPS_ER_SET_SEL_REG_FAILED + } state; + } set_sel_reg; }; /** @@ -532,16 +608,6 @@ struct wps_context { struct wps_device_data dev; /** - * oob_conf - OOB Config data - */ - struct oob_conf_data oob_conf; - - /** - * oob_dev_pw_id - OOB Device password id - */ - u16 oob_dev_pw_id; - - /** * dh_ctx - Context data for Diffie-Hellman operation */ void *dh_ctx; @@ -672,53 +738,57 @@ struct wps_context { /* Pending messages from UPnP PutWLANResponse */ struct upnp_pending_message *upnp_msgs; -}; - -struct oob_device_data { - char *device_name; - char *device_path; - void * (*init_func)(struct wps_context *, struct oob_device_data *, - int); - struct wpabuf * (*read_func)(void *); - int (*write_func)(void *, struct wpabuf *); - void (*deinit_func)(void *); -}; -struct oob_nfc_device_data { - int (*init_func)(char *); - void * (*read_func)(size_t *); - int (*write_func)(void *, size_t); - void (*deinit_func)(void); + u16 ap_nfc_dev_pw_id; + struct wpabuf *ap_nfc_dh_pubkey; + struct wpabuf *ap_nfc_dh_privkey; + struct wpabuf *ap_nfc_dev_pw; }; struct wps_registrar * wps_registrar_init(struct wps_context *wps, const struct wps_registrar_config *cfg); void wps_registrar_deinit(struct wps_registrar *reg); -int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid, - const u8 *pin, size_t pin_len, int timeout); +int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *addr, + const u8 *uuid, const u8 *pin, size_t pin_len, + int timeout); int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid); +int wps_registrar_wps_cancel(struct wps_registrar *reg); int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid); -int wps_registrar_button_pushed(struct wps_registrar *reg); +int wps_registrar_button_pushed(struct wps_registrar *reg, + const u8 *p2p_dev_addr); +void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e, + const u8 *dev_pw, size_t dev_pw_len); void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr, - const struct wpabuf *wps_data); + const struct wpabuf *wps_data, + int p2p_wildcard); int wps_registrar_update_ie(struct wps_registrar *reg); int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr, char *buf, size_t buflen); +int wps_registrar_config_ap(struct wps_registrar *reg, + struct wps_credential *cred); +int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg, + const u8 *pubkey_hash, u16 pw_id, + const u8 *dev_pw, size_t dev_pw_len); +int 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); 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); -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); +struct wps_er * wps_er_init(struct wps_context *wps, const char *ifname, + const char *filter); void wps_er_refresh(struct wps_er *er); void wps_er_deinit(struct wps_er *er, void (*cb)(void *ctx), void *ctx); void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id, @@ -726,11 +796,173 @@ void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id, int wps_er_pbc(struct wps_er *er, const u8 *uuid); int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *pin, size_t pin_len); +int wps_er_set_config(struct wps_er *er, const u8 *uuid, + const struct wps_credential *cred); +int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *pin, + size_t pin_len, const struct wps_credential *cred); +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); +struct wpabuf * ndef_build_wifi_hr(void); + +#ifdef CONFIG_WPS_STRICT +int wps_validate_beacon(const struct wpabuf *wps_ie); +int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie, int probe, + const u8 *addr); +int wps_validate_probe_req(const struct wpabuf *wps_ie, const u8 *addr); +int wps_validate_assoc_req(const struct wpabuf *wps_ie); +int wps_validate_assoc_resp(const struct wpabuf *wps_ie); +int wps_validate_m1(const struct wpabuf *tlvs); +int wps_validate_m2(const struct wpabuf *tlvs); +int wps_validate_m2d(const struct wpabuf *tlvs); +int wps_validate_m3(const struct wpabuf *tlvs); +int wps_validate_m4(const struct wpabuf *tlvs); +int wps_validate_m4_encr(const struct wpabuf *tlvs, int wps2); +int wps_validate_m5(const struct wpabuf *tlvs); +int wps_validate_m5_encr(const struct wpabuf *tlvs, int wps2); +int wps_validate_m6(const struct wpabuf *tlvs); +int wps_validate_m6_encr(const struct wpabuf *tlvs, int wps2); +int wps_validate_m7(const struct wpabuf *tlvs); +int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap, int wps2); +int wps_validate_m8(const struct wpabuf *tlvs); +int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap, int wps2); +int wps_validate_wsc_ack(const struct wpabuf *tlvs); +int wps_validate_wsc_nack(const struct wpabuf *tlvs); +int wps_validate_wsc_done(const struct wpabuf *tlvs); +int wps_validate_upnp_set_selected_registrar(const struct wpabuf *tlvs); +#else /* CONFIG_WPS_STRICT */ +static inline int wps_validate_beacon(const struct wpabuf *wps_ie){ + return 0; +} + +static inline int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie, + int probe, const u8 *addr) +{ + return 0; +} + +static inline int wps_validate_probe_req(const struct wpabuf *wps_ie, + const u8 *addr) +{ + return 0; +} + +static inline int wps_validate_assoc_req(const struct wpabuf *wps_ie) +{ + return 0; +} + +static inline int wps_validate_assoc_resp(const struct wpabuf *wps_ie) +{ + return 0; +} + +static inline int wps_validate_m1(const struct wpabuf *tlvs) +{ + return 0; +} + +static inline int wps_validate_m2(const struct wpabuf *tlvs) +{ + return 0; +} + +static inline int wps_validate_m2d(const struct wpabuf *tlvs) +{ + return 0; +} + +static inline int wps_validate_m3(const struct wpabuf *tlvs) +{ + return 0; +} + +static inline int wps_validate_m4(const struct wpabuf *tlvs) +{ + return 0; +} + +static inline int wps_validate_m4_encr(const struct wpabuf *tlvs, int wps2) +{ + return 0; +} + +static inline int wps_validate_m5(const struct wpabuf *tlvs) +{ + return 0; +} + +static inline int wps_validate_m5_encr(const struct wpabuf *tlvs, int wps2) +{ + return 0; +} + +static inline int wps_validate_m6(const struct wpabuf *tlvs) +{ + return 0; +} + +static inline int wps_validate_m6_encr(const struct wpabuf *tlvs, int wps2) +{ + return 0; +} + +static inline int wps_validate_m7(const struct wpabuf *tlvs) +{ + return 0; +} + +static inline int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap, + int wps2) +{ + return 0; +} + +static inline int wps_validate_m8(const struct wpabuf *tlvs) +{ + return 0; +} + +static inline int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap, + int wps2) +{ + return 0; +} + +static inline int wps_validate_wsc_ack(const struct wpabuf *tlvs) +{ + return 0; +} + +static inline int wps_validate_wsc_nack(const struct wpabuf *tlvs) +{ + return 0; +} + +static inline int wps_validate_wsc_done(const struct wpabuf *tlvs) +{ + return 0; +} + +static inline int wps_validate_upnp_set_selected_registrar( + const struct wpabuf *tlvs) +{ + return 0; +} +#endif /* CONFIG_WPS_STRICT */ #endif /* WPS_H */ diff --git a/src/wps/wps_attr_build.c b/src/wps/wps_attr_build.c index 9da556addd05d..29aee8eeca945 100644 --- a/src/wps/wps_attr_build.c +++ b/src/wps/wps_attr_build.c @@ -2,14 +2,8 @@ * Wi-Fi Protected Setup - attribute building * Copyright (c) 2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -19,6 +13,8 @@ #include "crypto/crypto.h" #include "crypto/dh_group5.h" #include "crypto/sha256.h" +#include "crypto/random.h" +#include "common/ieee802_11_defs.h" #include "wps_i.h" @@ -34,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; @@ -47,6 +51,8 @@ int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg) wpabuf_free(pubkey); return -1; } + wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH Private Key", wps->dh_privkey); + wpa_hexdump_buf(MSG_DEBUG, "WPS: DH own Public Key", pubkey); wpabuf_put_be16(msg, ATTR_PUBLIC_KEY); wpabuf_put_be16(msg, wpabuf_len(pubkey)); @@ -156,10 +162,65 @@ int wps_build_authenticator(struct wps_data *wps, struct wpabuf *msg) int wps_build_version(struct wpabuf *msg) { - wpa_printf(MSG_DEBUG, "WPS: * Version"); + /* + * Note: This attribute is deprecated and set to hardcoded 0x10 for + * backwards compatibility reasons. The real version negotiation is + * done with Version2. + */ + wpa_printf(MSG_DEBUG, "WPS: * Version (hardcoded 0x10)"); wpabuf_put_be16(msg, ATTR_VERSION); wpabuf_put_be16(msg, 1); + wpabuf_put_u8(msg, 0x10); + return 0; +} + + +int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll, + const u8 *auth_macs, size_t auth_macs_count) +{ +#ifdef CONFIG_WPS2 + u8 *len; + + wpabuf_put_be16(msg, ATTR_VENDOR_EXT); + len = wpabuf_put(msg, 2); /* to be filled */ + wpabuf_put_be24(msg, WPS_VENDOR_ID_WFA); + + wpa_printf(MSG_DEBUG, "WPS: * Version2 (0x%x)", WPS_VERSION); + wpabuf_put_u8(msg, WFA_ELEM_VERSION2); + wpabuf_put_u8(msg, 1); wpabuf_put_u8(msg, WPS_VERSION); + + if (req_to_enroll) { + wpa_printf(MSG_DEBUG, "WPS: * Request to Enroll (1)"); + wpabuf_put_u8(msg, WFA_ELEM_REQUEST_TO_ENROLL); + wpabuf_put_u8(msg, 1); + wpabuf_put_u8(msg, 1); + } + + if (auth_macs && auth_macs_count) { + size_t i; + wpa_printf(MSG_DEBUG, "WPS: * AuthorizedMACs (count=%d)", + (int) auth_macs_count); + wpabuf_put_u8(msg, WFA_ELEM_AUTHORIZEDMACS); + wpabuf_put_u8(msg, auth_macs_count * ETH_ALEN); + wpabuf_put_data(msg, auth_macs, auth_macs_count * ETH_ALEN); + for (i = 0; i < auth_macs_count; i++) + wpa_printf(MSG_DEBUG, "WPS: AuthorizedMAC: " MACSTR, + MAC2STR(&auth_macs[i * ETH_ALEN])); + } + + WPA_PUT_BE16(len, (u8 *) wpabuf_put(msg, 0) - len - 2); +#endif /* CONFIG_WPS2 */ + +#ifdef CONFIG_WPS_TESTING + if (WPS_VERSION > 0x20) { + wpa_printf(MSG_DEBUG, "WPS: * Extensibility Testing - extra " + "attribute"); + wpabuf_put_be16(msg, ATTR_EXTENSIBILITY_TEST); + wpabuf_put_be16(msg, 1); + wpabuf_put_u8(msg, 42); + } +#endif /* CONFIG_WPS_TESTING */ return 0; } @@ -196,20 +257,28 @@ int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg) int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg) { + u16 auth_types = WPS_AUTH_TYPES; +#ifdef CONFIG_WPS2 + auth_types &= ~WPS_AUTH_SHARED; +#endif /* CONFIG_WPS2 */ wpa_printf(MSG_DEBUG, "WPS: * Authentication Type Flags"); wpabuf_put_be16(msg, ATTR_AUTH_TYPE_FLAGS); wpabuf_put_be16(msg, 2); - wpabuf_put_be16(msg, WPS_AUTH_TYPES); + wpabuf_put_be16(msg, auth_types); return 0; } int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg) { + u16 encr_types = WPS_ENCR_TYPES; +#ifdef CONFIG_WPS2 + encr_types &= ~WPS_ENCR_WEP; +#endif /* CONFIG_WPS2 */ wpa_printf(MSG_DEBUG, "WPS: * Encryption Type Flags"); wpabuf_put_be16(msg, ATTR_ENCR_TYPE_FLAGS); wpabuf_put_be16(msg, 2); - wpabuf_put_be16(msg, WPS_ENCR_TYPES); + wpabuf_put_be16(msg, encr_types); return 0; } @@ -266,7 +335,7 @@ int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg, wpabuf_put_be16(msg, block_size + wpabuf_len(plain)); iv = wpabuf_put(msg, block_size); - if (os_get_random(iv, block_size) < 0) + if (random_get_bytes(iv, block_size) < 0) return -1; data = wpabuf_put(msg, 0); @@ -279,44 +348,56 @@ 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]; - 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); + addr[0] = wpabuf_head(pubkey); + hash_len = wpabuf_len(pubkey); sha256_vector(1, addr, &hash_len, pubkey_hash); - if (os_get_random((u8 *) &wps->oob_dev_pw_id, sizeof(u16)) < 0) { - wpa_printf(MSG_ERROR, "WPS: device password id " - "generation error"); - return -1; - } - wps->oob_dev_pw_id |= 0x0010; - - if (os_get_random(dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN) < 0) { - wpa_printf(MSG_ERROR, "WPS: OOB device password " - "generation error"); - return -1; - } - wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD); - wpabuf_put_be16(msg, WPS_OOB_DEVICE_PASSWORD_ATTR_LEN); + wpabuf_put_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, 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); + wpabuf_put_be16(msg, dev_pw_id); + wpabuf_put_data(msg, dev_pw, dev_pw_len); return 0; } #endif /* CONFIG_WPS_OOB */ + + +/* Encapsulate WPS IE data with one (or more, if needed) IE headers */ +struct wpabuf * wps_ie_encapsulate(struct wpabuf *data) +{ + struct wpabuf *ie; + const u8 *pos, *end; + + ie = wpabuf_alloc(wpabuf_len(data) + 100); + if (ie == NULL) { + wpabuf_free(data); + return NULL; + } + + pos = wpabuf_head(data); + end = pos + wpabuf_len(data); + + while (end > pos) { + size_t frag_len = end - pos; + if (frag_len > 251) + frag_len = 251; + wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(ie, 4 + frag_len); + wpabuf_put_be32(ie, WPS_DEV_OUI_WFA); + wpabuf_put_data(ie, pos, frag_len); + pos += frag_len; + } + + wpabuf_free(data); + + return ie; +} diff --git a/src/wps/wps_attr_parse.c b/src/wps/wps_attr_parse.c index 30b0e79211ae1..3999b1b88108d 100644 --- a/src/wps/wps_attr_parse.c +++ b/src/wps/wps_attr_parse.c @@ -2,22 +2,132 @@ * Wi-Fi Protected Setup - attribute parsing * Copyright (c) 2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "wps_i.h" +#include "wps_defs.h" +#include "wps_attr_parse.h" +#ifndef CONFIG_WPS_STRICT #define WPS_WORKAROUNDS +#endif /* CONFIG_WPS_STRICT */ + + +static int wps_set_vendor_ext_wfa_subelem(struct wps_parse_attr *attr, + u8 id, u8 len, const u8 *pos) +{ + wpa_printf(MSG_EXCESSIVE, "WPS: WFA subelement id=%u len=%u", + id, len); + switch (id) { + case WFA_ELEM_VERSION2: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Version2 length " + "%u", len); + return -1; + } + attr->version2 = pos; + break; + case WFA_ELEM_AUTHORIZEDMACS: + attr->authorized_macs = pos; + attr->authorized_macs_len = len; + break; + case WFA_ELEM_NETWORK_KEY_SHAREABLE: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key " + "Shareable length %u", len); + return -1; + } + attr->network_key_shareable = pos; + break; + case WFA_ELEM_REQUEST_TO_ENROLL: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Request to Enroll " + "length %u", len); + return -1; + } + attr->request_to_enroll = pos; + break; + case WFA_ELEM_SETTINGS_DELAY_TIME: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Settings Delay " + "Time length %u", len); + return -1; + } + attr->settings_delay_time = pos; + break; + default: + wpa_printf(MSG_MSGDUMP, "WPS: Skipped unknown WFA Vendor " + "Extension subelement %u", id); + break; + } + + return 0; +} + + +static int wps_parse_vendor_ext_wfa(struct wps_parse_attr *attr, const u8 *pos, + u16 len) +{ + const u8 *end = pos + len; + u8 id, elen; + + while (pos + 2 < end) { + id = *pos++; + elen = *pos++; + if (pos + elen > end) + break; + if (wps_set_vendor_ext_wfa_subelem(attr, id, elen, pos) < 0) + return -1; + pos += elen; + } + + return 0; +} + + +static int wps_parse_vendor_ext(struct wps_parse_attr *attr, const u8 *pos, + u16 len) +{ + u32 vendor_id; + + if (len < 3) { + wpa_printf(MSG_DEBUG, "WPS: Skip invalid Vendor Extension"); + return 0; + } + + vendor_id = WPA_GET_BE24(pos); + switch (vendor_id) { + case WPS_VENDOR_ID_WFA: + return wps_parse_vendor_ext_wfa(attr, pos + 3, len - 3); + } + + /* Handle unknown vendor extensions */ + + wpa_printf(MSG_MSGDUMP, "WPS: Unknown Vendor Extension (Vendor ID %u)", + vendor_id); + + if (len > WPS_MAX_VENDOR_EXT_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Too long Vendor Extension (%u)", + len); + return -1; + } + + if (attr->num_vendor_ext >= MAX_WPS_PARSE_VENDOR_EXT) { + wpa_printf(MSG_DEBUG, "WPS: Skipped Vendor Extension " + "attribute (max %d vendor extensions)", + MAX_WPS_PARSE_VENDOR_EXT); + return -1; + } + attr->vendor_ext[attr->num_vendor_ext] = pos; + attr->vendor_ext_len[attr->num_vendor_ext] = len; + attr->num_vendor_ext++; + + return 0; +} static int wps_set_attr(struct wps_parse_attr *attr, u16 type, @@ -153,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) { @@ -399,6 +513,43 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type, } attr->ap_setup_locked = pos; break; + case ATTR_REQUESTED_DEV_TYPE: + if (len != WPS_DEV_TYPE_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Requested Device " + "Type length %u", len); + return -1; + } + if (attr->num_req_dev_type >= MAX_REQ_DEV_TYPE_COUNT) { + wpa_printf(MSG_DEBUG, "WPS: Skipped Requested Device " + "Type attribute (max %u types)", + MAX_REQ_DEV_TYPE_COUNT); + break; + } + attr->req_dev_type[attr->num_req_dev_type] = pos; + attr->num_req_dev_type++; + break; + case ATTR_SECONDARY_DEV_TYPE_LIST: + if (len > WPS_SEC_DEV_TYPE_MAX_LEN || + (len % WPS_DEV_TYPE_LEN) > 0) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Secondary Device " + "Type length %u", len); + return -1; + } + attr->sec_dev_type_list = pos; + attr->sec_dev_type_list_len = len; + break; + case ATTR_VENDOR_EXT: + if (wps_parse_vendor_ext(attr, pos, len) < 0) + return -1; + break; + case ATTR_AP_CHANNEL: + if (len != 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid AP Channel " + "length %u", len); + return -1; + } + attr->ap_channel = pos; + break; default: wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x " "len=%u", type, len); @@ -413,6 +564,9 @@ int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr) { const u8 *pos, *end; u16 type, len; +#ifdef WPS_WORKAROUNDS + u16 prev_type = 0; +#endif /* WPS_WORKAROUNDS */ os_memset(attr, 0, sizeof(*attr)); pos = wpabuf_head(msg); @@ -430,10 +584,28 @@ int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr) pos += 2; len = WPA_GET_BE16(pos); pos += 2; - wpa_printf(MSG_MSGDUMP, "WPS: attr type=0x%x len=%u", + wpa_printf(MSG_EXCESSIVE, "WPS: attr type=0x%x len=%u", type, len); if (len > end - pos) { wpa_printf(MSG_DEBUG, "WPS: Attribute overflow"); + wpa_hexdump_buf(MSG_MSGDUMP, "WPS: Message data", msg); +#ifdef WPS_WORKAROUNDS + /* + * Some deployed APs seem to have a bug in encoding of + * Network Key attribute in the Credential attribute + * where they add an extra octet after the Network Key + * attribute at least when open network is being + * provisioned. + */ + if ((type & 0xff00) != 0x1000 && + prev_type == ATTR_NETWORK_KEY) { + wpa_printf(MSG_DEBUG, "WPS: Workaround - try " + "to skip unexpected octet after " + "Network Key"); + pos -= 3; + continue; + } +#endif /* WPS_WORKAROUNDS */ return -1; } @@ -459,6 +631,9 @@ int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr) if (wps_set_attr(attr, type, pos, len) < 0) return -1; +#ifdef WPS_WORKAROUNDS + prev_type = type; +#endif /* WPS_WORKAROUNDS */ pos += len; } diff --git a/src/wps/wps_attr_parse.h b/src/wps/wps_attr_parse.h new file mode 100644 index 0000000000000..88e51a4494455 --- /dev/null +++ b/src/wps/wps_attr_parse.h @@ -0,0 +1,108 @@ +/* + * 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) */ + const u8 *ap_channel; /* 2 octets */ + + /* 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_attr_process.c b/src/wps/wps_attr_process.c index 4751bbce82322..b81f106e7710d 100644 --- a/src/wps/wps_attr_process.c +++ b/src/wps/wps_attr_process.c @@ -2,14 +2,8 @@ * Wi-Fi Protected Setup - attribute processing * Copyright (c) 2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -264,11 +258,31 @@ static int wps_process_cred_802_1x_enabled(struct wps_credential *cred, } -static void wps_workaround_cred_key(struct wps_credential *cred) +static int wps_process_cred_ap_channel(struct wps_credential *cred, + const u8 *ap_channel) +{ + if (ap_channel == NULL) + return 0; /* optional attribute */ + + cred->ap_channel = WPA_GET_BE16(ap_channel); + wpa_printf(MSG_DEBUG, "WPS: AP Channel: %u", cred->ap_channel); + + return 0; +} + + +static int wps_workaround_cred_key(struct wps_credential *cred) { if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK) && cred->key_len > 8 && cred->key_len < 64 && cred->key[cred->key_len - 1] == 0) { +#ifdef CONFIG_WPS_STRICT + wpa_printf(MSG_INFO, "WPS: WPA/WPA2-Personal passphrase uses " + "forbidden NULL termination"); + wpa_hexdump_ascii_key(MSG_INFO, "WPS: Network Key", + cred->key, cred->key_len); + return -1; +#else /* CONFIG_WPS_STRICT */ /* * A deployed external registrar is known to encode ASCII * passphrases incorrectly. Remove the extra NULL termination @@ -277,7 +291,9 @@ static void wps_workaround_cred_key(struct wps_credential *cred) wpa_printf(MSG_DEBUG, "WPS: Workaround - remove NULL " "termination from ASCII passphrase"); cred->key_len--; +#endif /* CONFIG_WPS_STRICT */ } + return 0; } @@ -300,12 +316,11 @@ int wps_process_cred(struct wps_parse_attr *attr, wps_process_cred_eap_identity(cred, attr->eap_identity, attr->eap_identity_len) || wps_process_cred_key_prov_auto(cred, attr->key_prov_auto) || - wps_process_cred_802_1x_enabled(cred, attr->dot1x_enabled)) + wps_process_cred_802_1x_enabled(cred, attr->dot1x_enabled) || + wps_process_cred_ap_channel(cred, attr->ap_channel)) return -1; - wps_workaround_cred_key(cred); - - return 0; + return wps_workaround_cred_key(cred); } @@ -324,7 +339,5 @@ int wps_process_ap_settings(struct wps_parse_attr *attr, wps_process_cred_mac_addr(cred, attr->mac_addr)) return -1; - wps_workaround_cred_key(cred); - - return 0; + return wps_workaround_cred_key(cred); } diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c index 6ef14dbbe7da5..68d9f0a099727 100644 --- a/src/wps/wps_common.c +++ b/src/wps/wps_common.c @@ -1,15 +1,9 @@ /* * 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 program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published 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" @@ -20,8 +14,8 @@ #include "crypto/dh_group5.h" #include "crypto/sha1.h" #include "crypto/sha256.h" +#include "crypto/random.h" #include "wps_i.h" -#include "wps_dev_attr.h" void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len, @@ -81,6 +75,8 @@ int wps_derive_keys(struct wps_data *wps) return -1; } + wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH Private Key", wps->dh_privkey); + wpa_hexdump_buf(MSG_DEBUG, "WPS: DH peer Public Key", pubkey); dh_shared = dh5_derive_shared(wps->dh_ctx, pubkey, wps->dh_privkey); dh5_free(wps->dh_ctx); wps->dh_ctx = NULL; @@ -241,7 +237,7 @@ unsigned int wps_generate_pin(void) unsigned int val; /* Generate seven random digits for the PIN */ - if (os_get_random((unsigned char *) &val, sizeof(val)) < 0) { + if (random_get_bytes((unsigned char *) &val, sizeof(val)) < 0) { struct os_time now; os_get_time(&now); val = os_random() ^ now.sec ^ now.usec; @@ -253,7 +249,24 @@ unsigned int wps_generate_pin(void) } -void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg) +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) { union wps_event_data data; @@ -262,6 +275,8 @@ void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg) os_memset(&data, 0, sizeof(data)); data.fail.msg = msg; + data.fail.config_error = config_error; + data.fail.error_indication = error_indication; wps->event_cb(wps->cb_ctx, WPS_EV_FAIL, &data); } @@ -309,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; @@ -325,7 +340,9 @@ static struct wpabuf * wps_get_oob_cred(struct wps_context *wps) data.wps = wps; data.auth_type = wps->auth_types; data.encr_type = wps->encr_types; - if (wps_build_version(plain) || wps_build_cred(&data, plain)) { + if (wps_build_version(plain) || + wps_build_cred(&data, plain) || + wps_build_wfa_ext(plain, 0, NULL, 0)) { wpabuf_free(plain); return NULL; } @@ -334,31 +351,22 @@ static struct wpabuf * wps_get_oob_cred(struct wps_context *wps) } -static struct wpabuf * wps_get_oob_dev_pwd(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(9 + WPS_OOB_DEVICE_PASSWORD_ATTR_LEN); - if (data == NULL) { - wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB " - "device password attribute"); + data = wpabuf_alloc(200); + if (data == NULL) return NULL; - } - - wpabuf_free(wps->oob_conf.dev_password); - wps->oob_conf.dev_password = - wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1); - if (wps->oob_conf.dev_password == NULL) { - wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB " - "device password"); - wpabuf_free(data); - return NULL; - } if (wps_build_version(data) || - wps_build_oob_dev_password(data, wps)) { - wpa_printf(MSG_ERROR, "WPS: Build OOB device password " - "attribute error"); + 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; } @@ -367,66 +375,17 @@ static struct wpabuf * wps_get_oob_dev_pwd(struct wps_context *wps) } -static int wps_parse_oob_dev_pwd(struct wps_context *wps, - struct wpabuf *data) -{ - struct oob_conf_data *oob_conf = &wps->oob_conf; - struct wps_parse_attr attr; - const u8 *pos; - - if (wps_parse_msg(data, &attr) < 0 || - attr.oob_dev_password == NULL) { - wpa_printf(MSG_ERROR, "WPS: OOB device password not found"); - return -1; - } - - pos = attr.oob_dev_password; - - oob_conf->pubkey_hash = - wpabuf_alloc_copy(pos, WPS_OOB_PUBKEY_HASH_LEN); - if (oob_conf->pubkey_hash == NULL) { - wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB " - "public key hash"); - return -1; - } - pos += WPS_OOB_PUBKEY_HASH_LEN; - - wps->oob_dev_pw_id = WPA_GET_BE16(pos); - pos += sizeof(wps->oob_dev_pw_id); - - oob_conf->dev_password = - wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1); - if (oob_conf->dev_password == NULL) { - wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB " - "device password"); - return -1; - } - wpa_snprintf_hex_uppercase(wpabuf_put(oob_conf->dev_password, - wpabuf_size(oob_conf->dev_password)), - wpabuf_size(oob_conf->dev_password), pos, - WPS_OOB_DEVICE_PASSWORD_LEN); - - return 0; -} - - -static int wps_parse_oob_cred(struct wps_context *wps, struct wpabuf *data) +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 " @@ -440,94 +399,6 @@ static int wps_parse_oob_cred(struct wps_context *wps, struct wpabuf *data) } -int wps_process_oob(struct wps_context *wps, struct oob_device_data *oob_dev, - int registrar) -{ - struct wpabuf *data; - int ret, write_f, oob_method = wps->oob_conf.oob_method; - void *oob_priv; - - write_f = oob_method == OOB_METHOD_DEV_PWD_E ? !registrar : registrar; - - oob_priv = oob_dev->init_func(wps, oob_dev, registrar); - if (oob_priv == NULL) { - wpa_printf(MSG_ERROR, "WPS: Failed to initialize OOB device"); - return -1; - } - - if (write_f) { - if (oob_method == OOB_METHOD_CRED) - data = wps_get_oob_cred(wps); - else - data = wps_get_oob_dev_pwd(wps); - - ret = 0; - if (data == NULL || oob_dev->write_func(oob_priv, data) < 0) - ret = -1; - } else { - data = oob_dev->read_func(oob_priv); - if (data == NULL) - ret = -1; - else { - if (oob_method == OOB_METHOD_CRED) - ret = wps_parse_oob_cred(wps, data); - else - ret = wps_parse_oob_dev_pwd(wps, data); - } - } - wpabuf_free(data); - oob_dev->deinit_func(oob_priv); - - if (ret < 0) { - wpa_printf(MSG_ERROR, "WPS: Failed to process OOB data"); - return -1; - } - - return 0; -} - - -struct oob_device_data * wps_get_oob_device(char *device_type) -{ -#ifdef CONFIG_WPS_UFD - if (os_strstr(device_type, "ufd") != NULL) - return &oob_ufd_device_data; -#endif /* CONFIG_WPS_UFD */ -#ifdef CONFIG_WPS_NFC - if (os_strstr(device_type, "nfc") != NULL) - return &oob_nfc_device_data; -#endif /* CONFIG_WPS_NFC */ - - return NULL; -} - - -#ifdef CONFIG_WPS_NFC -struct oob_nfc_device_data * wps_get_oob_nfc_device(char *device_name) -{ - if (device_name == NULL) - return NULL; -#ifdef CONFIG_WPS_NFC_PN531 - if (os_strstr(device_name, "pn531") != NULL) - return &oob_nfc_pn531_device_data; -#endif /* CONFIG_WPS_NFC_PN531 */ - - return NULL; -} -#endif /* CONFIG_WPS_NFC */ - - -int wps_get_oob_method(char *method) -{ - if (os_strstr(method, "pin-e") != NULL) - return OOB_METHOD_DEV_PWD_E; - if (os_strstr(method, "pin-r") != NULL) - return OOB_METHOD_DEV_PWD_R; - if (os_strstr(method, "cred") != NULL) - return OOB_METHOD_CRED; - return OOB_METHOD_UNKNOWN; -} - #endif /* CONFIG_WPS_OOB */ @@ -604,15 +475,13 @@ u16 wps_config_methods_str2bin(const char *str) if (str == NULL) { /* Default to enabling methods based on build configuration */ methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; -#ifdef CONFIG_WPS_UFD - methods |= WPS_CONFIG_USBA; -#endif /* CONFIG_WPS_UFD */ +#ifdef CONFIG_WPS2 + methods |= WPS_CONFIG_VIRT_DISPLAY; +#endif /* CONFIG_WPS2 */ #ifdef CONFIG_WPS_NFC methods |= WPS_CONFIG_NFC_INTERFACE; #endif /* CONFIG_WPS_NFC */ } else { - if (os_strstr(str, "usba")) - methods |= WPS_CONFIG_USBA; if (os_strstr(str, "ethernet")) methods |= WPS_CONFIG_ETHERNET; if (os_strstr(str, "label")) @@ -629,7 +498,114 @@ u16 wps_config_methods_str2bin(const char *str) methods |= WPS_CONFIG_PUSHBUTTON; if (os_strstr(str, "keypad")) methods |= WPS_CONFIG_KEYPAD; +#ifdef CONFIG_WPS2 + if (os_strstr(str, "virtual_display")) + methods |= WPS_CONFIG_VIRT_DISPLAY; + if (os_strstr(str, "physical_display")) + methods |= WPS_CONFIG_PHY_DISPLAY; + if (os_strstr(str, "virtual_push_button")) + methods |= WPS_CONFIG_VIRT_PUSHBUTTON; + if (os_strstr(str, "physical_push_button")) + methods |= WPS_CONFIG_PHY_PUSHBUTTON; +#endif /* CONFIG_WPS2 */ } return methods; } + + +struct wpabuf * wps_build_wsc_ack(struct wps_data *wps) +{ + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_ACK"); + + msg = wpabuf_alloc(1000); + if (msg == NULL) + return NULL; + + if (wps_build_version(msg) || + wps_build_msg_type(msg, WPS_WSC_ACK) || + wps_build_enrollee_nonce(wps, msg) || + wps_build_registrar_nonce(wps, msg) || + wps_build_wfa_ext(msg, 0, NULL, 0)) { + wpabuf_free(msg); + return NULL; + } + + return msg; +} + + +struct wpabuf * wps_build_wsc_nack(struct wps_data *wps) +{ + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_NACK"); + + msg = wpabuf_alloc(1000); + if (msg == NULL) + return NULL; + + if (wps_build_version(msg) || + wps_build_msg_type(msg, WPS_WSC_NACK) || + wps_build_enrollee_nonce(wps, msg) || + wps_build_registrar_nonce(wps, msg) || + wps_build_config_error(msg, wps->config_error) || + wps_build_wfa_ext(msg, 0, NULL, 0)) { + wpabuf_free(msg); + return NULL; + } + + return msg; +} + + +#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 750ca413ec0f2..2f42603a78392 100644 --- a/src/wps/wps_defs.h +++ b/src/wps/wps_defs.h @@ -2,20 +2,28 @@ * Wi-Fi Protected Setup - message definitions * Copyright (c) 2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPS_DEFS_H #define WPS_DEFS_H +#ifdef CONFIG_WPS_TESTING + +extern int wps_version_number; +extern int wps_testing_dummy_cred; +#define WPS_VERSION wps_version_number + +#else /* CONFIG_WPS_TESTING */ + +#ifdef CONFIG_WPS2 +#define WPS_VERSION 0x20 +#else /* CONFIG_WPS2 */ #define WPS_VERSION 0x10 +#endif /* CONFIG_WPS2 */ + +#endif /* CONFIG_WPS_TESTING */ /* Diffie-Hellman 1536-bit MODP Group; RFC 3526, Group 5 */ #define WPS_DH_GROUP 5 @@ -33,7 +41,7 @@ #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 @@ -124,7 +132,20 @@ enum wps_attribute { ATTR_KEY_PROVIDED_AUTO = 0x1061, ATTR_802_1X_ENABLED = 0x1062, ATTR_APPSESSIONKEY = 0x1063, - ATTR_WEPTRANSMITKEY = 0x1064 + ATTR_WEPTRANSMITKEY = 0x1064, + ATTR_REQUESTED_DEV_TYPE = 0x106a, + ATTR_EXTENSIBILITY_TEST = 0x10fa /* _NOT_ defined in the spec */ +}; + +#define WPS_VENDOR_ID_WFA 14122 + +/* WFA Vendor Extension subelements */ +enum { + WFA_ELEM_VERSION2 = 0x00, + WFA_ELEM_AUTHORIZEDMACS = 0x01, + WFA_ELEM_NETWORK_KEY_SHAREABLE = 0x02, + WFA_ELEM_REQUEST_TO_ENROLL = 0x03, + WFA_ELEM_SETTINGS_DELAY_TIME = 0x04 }; /* Device Password ID */ @@ -197,6 +218,14 @@ enum wps_config_error { WPS_CFG_DEV_PASSWORD_AUTH_FAILURE = 18 }; +/* Vendor specific Error Indication for WPS event messages */ +enum wps_error_indication { + WPS_EI_NO_ERROR, + WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED, + WPS_EI_SECURITY_WEP_PROHIBITED, + NUM_WPS_EI_VALUES +}; + /* RF Bands */ #define WPS_RF_24GHZ 0x01 #define WPS_RF_50GHZ 0x02 @@ -211,6 +240,12 @@ enum wps_config_error { #define WPS_CONFIG_NFC_INTERFACE 0x0040 #define WPS_CONFIG_PUSHBUTTON 0x0080 #define WPS_CONFIG_KEYPAD 0x0100 +#ifdef CONFIG_WPS2 +#define WPS_CONFIG_VIRT_PUSHBUTTON 0x0280 +#define WPS_CONFIG_PHY_PUSHBUTTON 0x0480 +#define WPS_CONFIG_VIRT_DISPLAY 0x2008 +#define WPS_CONFIG_PHY_DISPLAY 0x4008 +#endif /* CONFIG_WPS2 */ /* Connection Type Flags */ #define WPS_CONN_ESS 0x01 @@ -290,4 +325,6 @@ enum wps_response_type { /* Walk Time for push button configuration (in seconds) */ #define WPS_PBC_WALK_TIME 120 +#define WPS_MAX_AUTHORIZED_MACS 5 + #endif /* WPS_DEFS_H */ diff --git a/src/wps/wps_dev_attr.c b/src/wps/wps_dev_attr.c index 090bfa2bfc2b5..3c94a43467a1a 100644 --- a/src/wps/wps_dev_attr.c +++ b/src/wps/wps_dev_attr.c @@ -2,14 +2,8 @@ * Wi-Fi Protected Setup - device attributes * Copyright (c) 2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -19,71 +13,74 @@ #include "wps_dev_attr.h" -static int wps_build_manufacturer(struct wps_device_data *dev, - struct wpabuf *msg) +int wps_build_manufacturer(struct wps_device_data *dev, struct wpabuf *msg) { size_t len; wpa_printf(MSG_DEBUG, "WPS: * Manufacturer"); wpabuf_put_be16(msg, ATTR_MANUFACTURER); len = dev->manufacturer ? os_strlen(dev->manufacturer) : 0; +#ifndef CONFIG_WPS_STRICT if (len == 0) { /* * Some deployed WPS implementations fail to parse zero-length - * attributes. As a workaround, send a null character if the + * attributes. As a workaround, send a space character if the * device attribute string is empty. */ wpabuf_put_be16(msg, 1); - wpabuf_put_u8(msg, '\0'); - } else { - wpabuf_put_be16(msg, len); - wpabuf_put_data(msg, dev->manufacturer, len); + wpabuf_put_u8(msg, ' '); + return 0; } +#endif /* CONFIG_WPS_STRICT */ + wpabuf_put_be16(msg, len); + wpabuf_put_data(msg, dev->manufacturer, len); return 0; } -static int wps_build_model_name(struct wps_device_data *dev, - struct wpabuf *msg) +int wps_build_model_name(struct wps_device_data *dev, struct wpabuf *msg) { size_t len; wpa_printf(MSG_DEBUG, "WPS: * Model Name"); wpabuf_put_be16(msg, ATTR_MODEL_NAME); len = dev->model_name ? os_strlen(dev->model_name) : 0; +#ifndef CONFIG_WPS_STRICT if (len == 0) { /* * Some deployed WPS implementations fail to parse zero-length - * attributes. As a workaround, send a null character if the + * attributes. As a workaround, send a space character if the * device attribute string is empty. */ wpabuf_put_be16(msg, 1); - wpabuf_put_u8(msg, '\0'); - } else { - wpabuf_put_be16(msg, len); - wpabuf_put_data(msg, dev->model_name, len); + wpabuf_put_u8(msg, ' '); + return 0; } +#endif /* CONFIG_WPS_STRICT */ + wpabuf_put_be16(msg, len); + wpabuf_put_data(msg, dev->model_name, len); return 0; } -static int wps_build_model_number(struct wps_device_data *dev, - struct wpabuf *msg) +int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg) { size_t len; wpa_printf(MSG_DEBUG, "WPS: * Model Number"); wpabuf_put_be16(msg, ATTR_MODEL_NUMBER); len = dev->model_number ? os_strlen(dev->model_number) : 0; +#ifndef CONFIG_WPS_STRICT if (len == 0) { /* * Some deployed WPS implementations fail to parse zero-length - * attributes. As a workaround, send a null character if the + * attributes. As a workaround, send a space character if the * device attribute string is empty. */ wpabuf_put_be16(msg, 1); - wpabuf_put_u8(msg, '\0'); - } else { - wpabuf_put_be16(msg, len); - wpabuf_put_data(msg, dev->model_number, len); + wpabuf_put_u8(msg, ' '); + return 0; } +#endif /* CONFIG_WPS_STRICT */ + wpabuf_put_be16(msg, len); + wpabuf_put_data(msg, dev->model_number, len); return 0; } @@ -95,18 +92,20 @@ static int wps_build_serial_number(struct wps_device_data *dev, wpa_printf(MSG_DEBUG, "WPS: * Serial Number"); wpabuf_put_be16(msg, ATTR_SERIAL_NUMBER); len = dev->serial_number ? os_strlen(dev->serial_number) : 0; +#ifndef CONFIG_WPS_STRICT if (len == 0) { /* * Some deployed WPS implementations fail to parse zero-length - * attributes. As a workaround, send a null character if the + * attributes. As a workaround, send a space character if the * device attribute string is empty. */ wpabuf_put_be16(msg, 1); - wpabuf_put_u8(msg, '\0'); - } else { - wpabuf_put_be16(msg, len); - wpabuf_put_data(msg, dev->serial_number, len); + wpabuf_put_u8(msg, ' '); + return 0; } +#endif /* CONFIG_WPS_STRICT */ + wpabuf_put_be16(msg, len); + wpabuf_put_data(msg, dev->serial_number, len); return 0; } @@ -121,24 +120,62 @@ int wps_build_primary_dev_type(struct wps_device_data *dev, struct wpabuf *msg) } -static int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg) +int wps_build_secondary_dev_type(struct wps_device_data *dev, + struct wpabuf *msg) +{ + if (!dev->num_sec_dev_types) + return 0; + + wpa_printf(MSG_DEBUG, "WPS: * Secondary Device Type"); + wpabuf_put_be16(msg, ATTR_SECONDARY_DEV_TYPE_LIST); + wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN * dev->num_sec_dev_types); + wpabuf_put_data(msg, dev->sec_dev_type, + WPS_DEV_TYPE_LEN * dev->num_sec_dev_types); + + return 0; +} + + +int wps_build_req_dev_type(struct wps_device_data *dev, struct wpabuf *msg, + unsigned int num_req_dev_types, + const u8 *req_dev_types) +{ + unsigned int i; + + for (i = 0; i < num_req_dev_types; i++) { + wpa_hexdump(MSG_DEBUG, "WPS: * Requested Device Type", + req_dev_types + i * WPS_DEV_TYPE_LEN, + WPS_DEV_TYPE_LEN); + wpabuf_put_be16(msg, ATTR_REQUESTED_DEV_TYPE); + wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN); + wpabuf_put_data(msg, req_dev_types + i * WPS_DEV_TYPE_LEN, + WPS_DEV_TYPE_LEN); + } + + return 0; +} + + +int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg) { size_t len; wpa_printf(MSG_DEBUG, "WPS: * Device Name"); wpabuf_put_be16(msg, ATTR_DEV_NAME); len = dev->device_name ? os_strlen(dev->device_name) : 0; +#ifndef CONFIG_WPS_STRICT if (len == 0) { /* * Some deployed WPS implementations fail to parse zero-length - * attributes. As a workaround, send a null character if the + * attributes. As a workaround, send a space character if the * device attribute string is empty. */ wpabuf_put_be16(msg, 1); - wpabuf_put_u8(msg, '\0'); - } else { - wpabuf_put_be16(msg, len); - wpabuf_put_data(msg, dev->device_name, len); + wpabuf_put_u8(msg, ' '); + return 0; } +#endif /* CONFIG_WPS_STRICT */ + wpabuf_put_be16(msg, len); + wpabuf_put_data(msg, dev->device_name, len); return 0; } @@ -166,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); @@ -176,6 +227,25 @@ int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg) } +int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg) +{ + int i; + + for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) { + if (dev->vendor_ext[i] == NULL) + continue; + wpa_hexdump(MSG_DEBUG, "WPS: * Vendor Extension", + wpabuf_head_u8(dev->vendor_ext[i]), + wpabuf_len(dev->vendor_ext[i])); + wpabuf_put_be16(msg, ATTR_VENDOR_EXT); + wpabuf_put_be16(msg, wpabuf_len(dev->vendor_ext[i])); + wpabuf_put_buf(msg, dev->vendor_ext[i]); + } + + return 0; +} + + static int wps_process_manufacturer(struct wps_device_data *dev, const u8 *str, size_t str_len) { diff --git a/src/wps/wps_dev_attr.h b/src/wps/wps_dev_attr.h index a9c16ea22f2b7..200c9c45adcf5 100644 --- a/src/wps/wps_dev_attr.h +++ b/src/wps/wps_dev_attr.h @@ -2,14 +2,8 @@ * Wi-Fi Protected Setup - device attributes * Copyright (c) 2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPS_DEV_ATTR_H @@ -17,11 +11,19 @@ struct wps_parse_attr; +int wps_build_manufacturer(struct wps_device_data *dev, struct wpabuf *msg); +int wps_build_model_name(struct wps_device_data *dev, struct wpabuf *msg); +int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg); +int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg); int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg); int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg); +int wps_build_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); +int wps_build_secondary_dev_type(struct wps_device_data *dev, + struct wpabuf *msg); +int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg); int wps_process_device_attrs(struct wps_device_data *dev, struct wps_parse_attr *attr); int wps_process_os_version(struct wps_device_data *dev, const u8 *ver); @@ -29,5 +31,9 @@ int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands); void wps_device_data_dup(struct wps_device_data *dst, const struct wps_device_data *src); void wps_device_data_free(struct wps_device_data *dev); +int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg); +int wps_build_req_dev_type(struct wps_device_data *dev, struct wpabuf *msg, + unsigned int num_req_dev_types, + const u8 *req_dev_types); #endif /* WPS_DEV_ATTR_H */ diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c index dff24d491cee1..837b9412e8ffb 100644 --- a/src/wps/wps_enrollee.c +++ b/src/wps/wps_enrollee.c @@ -2,14 +2,8 @@ * Wi-Fi Protected Setup - Enrollee * Copyright (c) 2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -17,6 +11,7 @@ #include "common.h" #include "crypto/crypto.h" #include "crypto/sha256.h" +#include "crypto/random.h" #include "wps_i.h" #include "wps_dev_attr.h" @@ -53,7 +48,7 @@ static int wps_build_e_hash(struct wps_data *wps, struct wpabuf *msg) const u8 *addr[4]; size_t len[4]; - if (os_get_random(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0) + if (random_get_bytes(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0) return -1; wpa_hexdump(MSG_DEBUG, "WPS: E-S1", wps->snonce, WPS_SECRET_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "WPS: E-S2", @@ -119,8 +114,9 @@ static int wps_build_e_snonce2(struct wps_data *wps, struct wpabuf *msg) static struct wpabuf * wps_build_m1(struct wps_data *wps) { struct wpabuf *msg; + u16 config_methods; - if (os_get_random(wps->nonce_e, WPS_NONCE_LEN) < 0) + if (random_get_bytes(wps->nonce_e, WPS_NONCE_LEN) < 0) return NULL; wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce", wps->nonce_e, WPS_NONCE_LEN); @@ -130,6 +126,26 @@ static struct wpabuf * wps_build_m1(struct wps_data *wps) if (msg == NULL) return NULL; + config_methods = wps->wps->config_methods; + if (wps->wps->ap && !wps->pbc_in_m1 && + (wps->dev_password_len != 0 || + (config_methods & WPS_CONFIG_DISPLAY))) { + /* + * These are the methods that the AP supports as an Enrollee + * for adding external Registrars, so remove PushButton. + * + * As a workaround for Windows 7 mechanism for probing WPS + * capabilities from M1, leave PushButton option if no PIN + * method is available or if WPS configuration enables PBC + * workaround. + */ + config_methods &= ~WPS_CONFIG_PUSHBUTTON; +#ifdef CONFIG_WPS2 + config_methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON | + WPS_CONFIG_PHY_PUSHBUTTON); +#endif /* CONFIG_WPS2 */ + } + if (wps_build_version(msg) || wps_build_msg_type(msg, WPS_M1) || wps_build_uuid_e(msg, wps->uuid_e) || @@ -139,14 +155,16 @@ static struct wpabuf * wps_build_m1(struct wps_data *wps) wps_build_auth_type_flags(wps, msg) || wps_build_encr_type_flags(wps, msg) || wps_build_conn_type_flags(wps, msg) || - wps_build_config_methods(msg, wps->wps->config_methods) || + wps_build_config_methods(msg, config_methods) || wps_build_wps_state(wps, msg) || wps_build_device_attrs(&wps->wps->dev, msg) || wps_build_rf_bands(&wps->wps->dev, msg) || wps_build_assoc_state(wps, msg) || wps_build_dev_password_id(msg, wps->dev_pw_id) || wps_build_config_error(msg, WPS_CFG_NO_ERROR) || - wps_build_os_version(&wps->wps->dev, msg)) { + wps_build_os_version(&wps->wps->dev, msg) || + wps_build_wfa_ext(msg, 0, NULL, 0) || + wps_build_vendor_ext_m1(&wps->wps->dev, msg)) { wpabuf_free(msg); return NULL; } @@ -176,6 +194,7 @@ static struct wpabuf * wps_build_m3(struct wps_data *wps) wps_build_msg_type(msg, WPS_M3) || wps_build_registrar_nonce(wps, msg) || wps_build_e_hash(wps, msg) || + wps_build_wfa_ext(msg, 0, NULL, 0) || wps_build_authenticator(wps, msg)) { wpabuf_free(msg); return NULL; @@ -208,6 +227,7 @@ static struct wpabuf * wps_build_m5(struct wps_data *wps) wps_build_e_snonce1(wps, plain) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain) || + wps_build_wfa_ext(msg, 0, NULL, 0) || wps_build_authenticator(wps, msg)) { wpabuf_free(plain); wpabuf_free(msg); @@ -232,20 +252,47 @@ static int wps_build_cred_ssid(struct wps_data *wps, struct wpabuf *msg) static int wps_build_cred_auth_type(struct wps_data *wps, struct wpabuf *msg) { - wpa_printf(MSG_DEBUG, "WPS: * Authentication Type"); + u16 auth_type = wps->wps->auth_types; + + /* Select the best authentication type */ + if (auth_type & WPS_AUTH_WPA2PSK) + auth_type = WPS_AUTH_WPA2PSK; + else if (auth_type & WPS_AUTH_WPAPSK) + auth_type = WPS_AUTH_WPAPSK; + else if (auth_type & WPS_AUTH_OPEN) + auth_type = WPS_AUTH_OPEN; + else if (auth_type & WPS_AUTH_SHARED) + auth_type = WPS_AUTH_SHARED; + + wpa_printf(MSG_DEBUG, "WPS: * Authentication Type (0x%x)", auth_type); wpabuf_put_be16(msg, ATTR_AUTH_TYPE); wpabuf_put_be16(msg, 2); - wpabuf_put_be16(msg, wps->wps->auth_types); + wpabuf_put_be16(msg, auth_type); return 0; } static int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg) { - wpa_printf(MSG_DEBUG, "WPS: * Encryption Type"); + u16 encr_type = wps->wps->encr_types; + + /* Select the best encryption type */ + if (wps->wps->auth_types & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) { + if (encr_type & WPS_ENCR_AES) + encr_type = WPS_ENCR_AES; + else if (encr_type & WPS_ENCR_TKIP) + encr_type = WPS_ENCR_TKIP; + } else { + if (encr_type & WPS_ENCR_WEP) + encr_type = WPS_ENCR_WEP; + else if (encr_type & WPS_ENCR_NONE) + encr_type = WPS_ENCR_NONE; + } + + wpa_printf(MSG_DEBUG, "WPS: * Encryption Type (0x%x)", encr_type); wpabuf_put_be16(msg, ATTR_ENCR_TYPE); wpabuf_put_be16(msg, 2); - wpabuf_put_be16(msg, wps->wps->encr_types); + wpabuf_put_be16(msg, encr_type); return 0; } @@ -310,6 +357,7 @@ static struct wpabuf * wps_build_m7(struct wps_data *wps) (wps->wps->ap && wps_build_ap_settings(wps, plain)) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain) || + wps_build_wfa_ext(msg, 0, NULL, 0) || wps_build_authenticator(wps, msg)) { wpabuf_free(plain); wpabuf_free(msg); @@ -345,7 +393,8 @@ static struct wpabuf * wps_build_wsc_done(struct wps_data *wps) if (wps_build_version(msg) || wps_build_msg_type(msg, WPS_WSC_DONE) || wps_build_enrollee_nonce(wps, msg) || - wps_build_registrar_nonce(wps, msg)) { + wps_build_registrar_nonce(wps, msg) || + wps_build_wfa_ext(msg, 0, NULL, 0)) { wpabuf_free(msg); return NULL; } @@ -360,51 +409,6 @@ static struct wpabuf * wps_build_wsc_done(struct wps_data *wps) } -static struct wpabuf * wps_build_wsc_ack(struct wps_data *wps) -{ - struct wpabuf *msg; - - wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_ACK"); - - msg = wpabuf_alloc(1000); - if (msg == NULL) - return NULL; - - if (wps_build_version(msg) || - wps_build_msg_type(msg, WPS_WSC_ACK) || - wps_build_enrollee_nonce(wps, msg) || - wps_build_registrar_nonce(wps, msg)) { - wpabuf_free(msg); - return NULL; - } - - return msg; -} - - -static struct wpabuf * wps_build_wsc_nack(struct wps_data *wps) -{ - struct wpabuf *msg; - - wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_NACK"); - - msg = wpabuf_alloc(1000); - if (msg == NULL) - return NULL; - - if (wps_build_version(msg) || - wps_build_msg_type(msg, WPS_WSC_NACK) || - wps_build_enrollee_nonce(wps, msg) || - wps_build_registrar_nonce(wps, msg) || - wps_build_config_error(msg, wps->config_error)) { - wpabuf_free(msg); - return NULL; - } - - return msg; -} - - struct wpabuf * wps_enrollee_get_msg(struct wps_data *wps, enum wsc_op_code *op_code) { @@ -519,23 +523,6 @@ static int wps_process_pubkey(struct wps_data *wps, const u8 *pk, return -1; } -#ifdef CONFIG_WPS_OOB - if (wps->dev_pw_id != DEV_PW_DEFAULT && - wps->wps->oob_conf.pubkey_hash) { - const u8 *addr[1]; - u8 hash[WPS_HASH_LEN]; - - addr[0] = pk; - sha256_vector(1, addr, &pk_len, hash); - if (os_memcmp(hash, - wpabuf_head(wps->wps->oob_conf.pubkey_hash), - WPS_OOB_PUBKEY_HASH_LEN) != 0) { - wpa_printf(MSG_ERROR, "WPS: Public Key hash error"); - return -1; - } - } -#endif /* CONFIG_WPS_OOB */ - wpabuf_free(wps->dh_pubkey_r); wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len); if (wps->dh_pubkey_r == NULL) @@ -657,10 +644,11 @@ static int wps_process_r_snonce2(struct wps_data *wps, const u8 *r_snonce2) static int wps_process_cred_e(struct wps_data *wps, const u8 *cred, - size_t cred_len) + size_t cred_len, int wps2) { struct wps_parse_attr attr; struct wpabuf msg; + int ret = 0; wpa_printf(MSG_DEBUG, "WPS: Received Credential"); os_memset(&wps->cred, 0, sizeof(wps->cred)); @@ -682,24 +670,48 @@ static int wps_process_cred_e(struct wps_data *wps, const u8 *cred, * reasons, allow this to be processed since we do not really * use the MAC Address information for anything. */ +#ifdef CONFIG_WPS_STRICT + if (wps2) { + wpa_printf(MSG_INFO, "WPS: Do not accept incorrect " + "MAC Address in AP Settings"); + return -1; + } +#endif /* CONFIG_WPS_STRICT */ } +#ifdef CONFIG_WPS2 + if (!(wps->cred.encr_type & + (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES))) { + if (wps->cred.encr_type & WPS_ENCR_WEP) { + wpa_printf(MSG_INFO, "WPS: Reject Credential " + "due to WEP configuration"); + wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED; + return -2; + } + + wpa_printf(MSG_INFO, "WPS: Reject Credential due to " + "invalid encr_type 0x%x", wps->cred.encr_type); + return -1; + } +#endif /* CONFIG_WPS2 */ + if (wps->wps->cred_cb) { wps->cred.cred_attr = cred - 4; wps->cred.cred_attr_len = cred_len + 4; - wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred); + ret = wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred); wps->cred.cred_attr = NULL; wps->cred.cred_attr_len = 0; } - return 0; + return ret; } static int wps_process_creds(struct wps_data *wps, const u8 *cred[], - size_t cred_len[], size_t num_cred) + size_t cred_len[], size_t num_cred, int wps2) { size_t i; + int ok = 0; if (wps->wps->ap) return 0; @@ -711,17 +723,29 @@ static int wps_process_creds(struct wps_data *wps, const u8 *cred[], } for (i = 0; i < num_cred; i++) { - if (wps_process_cred_e(wps, cred[i], cred_len[i])) + int res; + res = wps_process_cred_e(wps, cred[i], cred_len[i], wps2); + if (res == 0) + ok++; + else if (res == -2) + wpa_printf(MSG_DEBUG, "WPS: WEP credential skipped"); + else return -1; } + if (ok == 0) { + wpa_printf(MSG_DEBUG, "WPS: No valid Credential attribute " + "received"); + return -1; + } + return 0; } static int wps_process_ap_settings_e(struct wps_data *wps, struct wps_parse_attr *attr, - struct wpabuf *attrs) + struct wpabuf *attrs, int wps2) { struct wps_credential cred; @@ -747,7 +771,61 @@ static int wps_process_ap_settings_e(struct wps_data *wps, * reasons, allow this to be processed since we do not really * use the MAC Address information for anything. */ +#ifdef CONFIG_WPS_STRICT + if (wps2) { + wpa_printf(MSG_INFO, "WPS: Do not accept incorrect " + "MAC Address in AP Settings"); + return -1; + } +#endif /* CONFIG_WPS_STRICT */ + } + +#ifdef CONFIG_WPS2 + if (!(cred.encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES))) + { + if (cred.encr_type & WPS_ENCR_WEP) { + wpa_printf(MSG_INFO, "WPS: Reject new AP settings " + "due to WEP configuration"); + wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED; + return -1; + } + + wpa_printf(MSG_INFO, "WPS: Reject new AP settings due to " + "invalid encr_type 0x%x", cred.encr_type); + return -1; + } +#endif /* CONFIG_WPS2 */ + +#ifdef CONFIG_WPS_STRICT + if (wps2) { + if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) == + WPS_ENCR_TKIP || + (cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) == + WPS_AUTH_WPAPSK) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC 2.0 " + "AP Settings: WPA-Personal/TKIP only"); + wps->error_indication = + WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED; + return -1; + } + } +#endif /* CONFIG_WPS_STRICT */ + +#ifdef CONFIG_WPS2 + if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) == WPS_ENCR_TKIP) + { + wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> " + "TKIP+AES"); + cred.encr_type |= WPS_ENCR_AES; + } + + if ((cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) == + WPS_AUTH_WPAPSK) { + wpa_printf(MSG_DEBUG, "WPS: Upgrade auth_type WPAPSK -> " + "WPAPSK+WPA2PSK"); + cred.auth_type |= WPS_AUTH_WPA2PSK; } +#endif /* CONFIG_WPS2 */ if (wps->wps->cred_cb) { cred.cred_attr = wpabuf_head(attrs); @@ -779,8 +857,15 @@ static enum wps_process_res wps_process_m2(struct wps_data *wps, return WPS_CONTINUE; } + /* + * Stop here on an AP as an Enrollee if AP Setup is locked unless the + * special locked mode is used to allow protocol run up to M7 in order + * to support external Registrars that only learn the current AP + * configuration without changing it. + */ if (wps->wps->ap && - (wps->wps->ap_setup_locked || wps->dev_password == NULL)) { + ((wps->wps->ap_setup_locked && wps->wps->ap_setup_locked != 2) || + wps->dev_password == NULL)) { wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse " "registration of a new Registrar"); wps->config_error = WPS_CFG_SETUP_LOCKED; @@ -888,6 +973,12 @@ static enum wps_process_res wps_process_m4(struct wps_data *wps, return WPS_CONTINUE; } + if (wps_validate_m4_encr(decrypted, attr->version2 != NULL) < 0) { + wpabuf_free(decrypted); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings " "attribute"); if (wps_parse_msg(decrypted, &eattr) < 0 || @@ -935,6 +1026,12 @@ static enum wps_process_res wps_process_m6(struct wps_data *wps, return WPS_CONTINUE; } + if (wps_validate_m6_encr(decrypted, attr->version2 != NULL) < 0) { + wpabuf_free(decrypted); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings " "attribute"); if (wps_parse_msg(decrypted, &eattr) < 0 || @@ -946,6 +1043,10 @@ static enum wps_process_res wps_process_m6(struct wps_data *wps, } wpabuf_free(decrypted); + if (wps->wps->ap) + wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_AP_PIN_SUCCESS, + NULL); + wps->state = SEND_M7; return WPS_CONTINUE; } @@ -973,6 +1074,19 @@ static enum wps_process_res wps_process_m8(struct wps_data *wps, return WPS_CONTINUE; } + if (wps->wps->ap && wps->wps->ap_setup_locked) { + /* + * Stop here if special ap_setup_locked == 2 mode allowed the + * protocol to continue beyond M2. This allows ER to learn the + * current AP settings without changing them. + */ + wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse " + "registration of a new Registrar"); + wps->config_error = WPS_CFG_SETUP_LOCKED; + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings, attr->encr_settings_len); if (decrypted == NULL) { @@ -982,13 +1096,21 @@ static enum wps_process_res wps_process_m8(struct wps_data *wps, return WPS_CONTINUE; } + if (wps_validate_m8_encr(decrypted, wps->wps->ap, + attr->version2 != NULL) < 0) { + wpabuf_free(decrypted); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings " "attribute"); if (wps_parse_msg(decrypted, &eattr) < 0 || wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) || wps_process_creds(wps, eattr.cred, eattr.cred_len, - eattr.num_cred) || - wps_process_ap_settings_e(wps, &eattr, decrypted)) { + eattr.num_cred, attr->version2 != NULL) || + wps_process_ap_settings_e(wps, &eattr, decrypted, + attr->version2 != NULL)) { wpabuf_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; @@ -1011,44 +1133,52 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps, if (wps_parse_msg(msg, &attr) < 0) return WPS_FAILURE; - if (!wps_version_supported(attr.version)) { - wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x", - attr.version ? *attr.version : 0); - return WPS_FAILURE; - } - if (attr.enrollee_nonce == NULL || - os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); return WPS_FAILURE; } if (attr.msg_type == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute"); - return WPS_FAILURE; + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; } switch (*attr.msg_type) { case WPS_M2: + if (wps_validate_m2(msg) < 0) + return WPS_FAILURE; ret = wps_process_m2(wps, msg, &attr); break; case WPS_M2D: + if (wps_validate_m2d(msg) < 0) + return WPS_FAILURE; ret = wps_process_m2d(wps, &attr); break; case WPS_M4: + if (wps_validate_m4(msg) < 0) + return WPS_FAILURE; ret = wps_process_m4(wps, msg, &attr); if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) - wps_fail_event(wps->wps, WPS_M4); + wps_fail_event(wps->wps, WPS_M4, wps->config_error, + wps->error_indication); break; case WPS_M6: + if (wps_validate_m6(msg) < 0) + return WPS_FAILURE; ret = wps_process_m6(wps, msg, &attr); if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) - wps_fail_event(wps->wps, WPS_M6); + wps_fail_event(wps->wps, WPS_M6, wps->config_error, + wps->error_indication); break; case WPS_M8: + if (wps_validate_m8(msg) < 0) + return WPS_FAILURE; ret = wps_process_m8(wps, msg, &attr); if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) - wps_fail_event(wps->wps, WPS_M8); + wps_fail_event(wps->wps, WPS_M8, wps->config_error, + wps->error_indication); break; default: wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d", @@ -1084,12 +1214,6 @@ static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps, if (wps_parse_msg(msg, &attr) < 0) return WPS_FAILURE; - if (!wps_version_supported(attr.version)) { - wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x", - attr.version ? *attr.version : 0); - return WPS_FAILURE; - } - if (attr.msg_type == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute"); return WPS_FAILURE; @@ -1102,14 +1226,14 @@ static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps, } if (attr.registrar_nonce == NULL || - os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0)) + os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); return WPS_FAILURE; } if (attr.enrollee_nonce == NULL || - os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); return WPS_FAILURE; } @@ -1130,18 +1254,13 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps, const struct wpabuf *msg) { struct wps_parse_attr attr; + u16 config_error; wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK"); if (wps_parse_msg(msg, &attr) < 0) return WPS_FAILURE; - if (!wps_version_supported(attr.version)) { - wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x", - attr.version ? *attr.version : 0); - return WPS_FAILURE; - } - if (attr.msg_type == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute"); return WPS_FAILURE; @@ -1154,7 +1273,7 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps, } if (attr.registrar_nonce == NULL || - os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0)) + os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); wpa_hexdump(MSG_DEBUG, "WPS: Received Registrar Nonce", @@ -1165,7 +1284,7 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps, } if (attr.enrollee_nonce == NULL || - os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); wpa_hexdump(MSG_DEBUG, "WPS: Received Enrollee Nonce", attr.enrollee_nonce, WPS_NONCE_LEN); @@ -1180,18 +1299,22 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps, return WPS_FAILURE; } + config_error = WPA_GET_BE16(attr.config_error); wpa_printf(MSG_DEBUG, "WPS: Registrar terminated negotiation with " - "Configuration Error %d", WPA_GET_BE16(attr.config_error)); + "Configuration Error %d", config_error); switch (wps->state) { case RECV_M4: - wps_fail_event(wps->wps, WPS_M3); + wps_fail_event(wps->wps, WPS_M3, config_error, + wps->error_indication); break; case RECV_M6: - wps_fail_event(wps->wps, WPS_M5); + wps_fail_event(wps->wps, WPS_M5, config_error, + wps->error_indication); break; case RECV_M8: - wps_fail_event(wps->wps, WPS_M7); + wps_fail_event(wps->wps, WPS_M7, config_error, + wps->error_indication); break; default: break; @@ -1230,8 +1353,12 @@ enum wps_process_res wps_enrollee_process_msg(struct wps_data *wps, case WSC_UPnP: return wps_process_wsc_msg(wps, msg); case WSC_ACK: + if (wps_validate_wsc_ack(msg) < 0) + return WPS_FAILURE; return wps_process_wsc_ack(wps, msg); case WSC_NACK: + if (wps_validate_wsc_nack(msg) < 0) + return WPS_FAILURE; return wps_process_wsc_nack(wps, msg); default: wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code); diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c index e0cdd1dfacd84..95a0dec0533cb 100644 --- a/src/wps/wps_er.c +++ b/src/wps/wps_er.c @@ -1,15 +1,9 @@ /* * Wi-Fi Protected Setup - External Registrar - * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2009-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 - * published 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" @@ -62,11 +56,15 @@ static void wps_er_sta_event(struct wps_context *wps, struct wps_er_sta *sta, } -static struct wps_er_sta * wps_er_sta_get(struct wps_er_ap *ap, const u8 *addr) +static struct wps_er_sta * wps_er_sta_get(struct wps_er_ap *ap, const u8 *addr, + const u8 *uuid) { struct wps_er_sta *sta; dl_list_for_each(sta, &ap->sta, struct wps_er_sta, list) { - if (os_memcmp(sta->addr, addr, ETH_ALEN) == 0) + if ((addr == NULL || + os_memcmp(sta->addr, addr, ETH_ALEN) == 0) && + (uuid == NULL || + os_memcmp(uuid, sta->uuid, WPS_UUID_LEN) == 0)) return sta; } return NULL; @@ -275,6 +273,64 @@ fail: wps_er_ap_unsubscribed(ap->er, ap); } + +static struct wps_er_ap_settings * wps_er_ap_get_settings(struct wps_er *er, + const u8 *uuid) +{ + struct wps_er_ap_settings *s; + dl_list_for_each(s, &er->ap_settings, struct wps_er_ap_settings, list) + if (os_memcmp(uuid, s->uuid, WPS_UUID_LEN) == 0) + return s; + return NULL; +} + + +int wps_er_ap_cache_settings(struct wps_er *er, struct in_addr *addr) +{ + struct wps_er_ap *ap; + struct wps_er_ap_settings *settings; + + ap = wps_er_ap_get(er, addr, NULL); + if (ap == NULL || ap->ap_settings == NULL) + return -1; + + settings = wps_er_ap_get_settings(er, ap->uuid); + if (!settings) { + settings = os_zalloc(sizeof(*settings)); + if (settings == NULL) + return -1; + os_memcpy(settings->uuid, ap->uuid, WPS_UUID_LEN); + dl_list_add(&er->ap_settings, &settings->list); + } + os_memcpy(&settings->ap_settings, ap->ap_settings, + sizeof(struct wps_credential)); + + return 0; +} + + +static int wps_er_ap_use_cached_settings(struct wps_er *er, + struct wps_er_ap *ap) +{ + struct wps_er_ap_settings *s; + + if (ap->ap_settings) + return 0; + + s = wps_er_ap_get_settings(ap->er, ap->uuid); + if (!s) + return -1; + + ap->ap_settings = os_malloc(sizeof(*ap->ap_settings)); + if (ap->ap_settings == NULL) + return -1; + + os_memcpy(ap->ap_settings, &s->ap_settings, sizeof(*ap->ap_settings)); + wpa_printf(MSG_DEBUG, "WPS ER: Use cached AP settings"); + return 0; +} + + static void wps_er_ap_remove_entry(struct wps_er *er, struct wps_er_ap *ap) { wpa_printf(MSG_DEBUG, "WPS ER: Removing AP entry for %s (%s)", @@ -352,6 +408,7 @@ static void wps_er_http_subscribe_cb(void *ctx, struct http_client *c, wpa_printf(MSG_DEBUG, "WPS ER: Subscribed to events"); ap->subscribed = 1; wps_er_get_sid(ap, http_client_get_hdr_line(c, "SID")); + wps_er_ap_use_cached_settings(ap->er, ap); wps_er_ap_event(ap->er->wps, ap, WPS_EV_ER_AP_ADD); break; case HTTP_CLIENT_FAILED: @@ -439,16 +496,61 @@ static void wps_er_get_device_info(struct wps_er_ap *ap) } +static const char * wps_er_find_wfadevice(const char *data) +{ + const char *tag, *tagname, *end; + char *val; + int found = 0; + + while (!found) { + /* Find next <device> */ + for (;;) { + if (xml_next_tag(data, &tag, &tagname, &end)) + return NULL; + data = end; + if (!os_strncasecmp(tagname, "device", 6) && + *tag != '/' && + (tagname[6] == '>' || !isgraph(tagname[6]))) { + break; + } + } + + /* Check whether deviceType is WFADevice */ + val = xml_get_first_item(data, "deviceType"); + if (val == NULL) + return NULL; + wpa_printf(MSG_DEBUG, "WPS ER: Found deviceType '%s'", val); + found = os_strcasecmp(val, "urn:schemas-wifialliance-org:" + "device:WFADevice:1") == 0; + os_free(val); + } + + return data; +} + + static void wps_er_parse_device_description(struct wps_er_ap *ap, struct wpabuf *reply) { /* Note: reply includes null termination after the buffer data */ - const char *data = wpabuf_head(reply); + const char *tmp, *data = wpabuf_head(reply); char *pos; wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Device info", wpabuf_head(reply), wpabuf_len(reply)); + /* + * The root device description may include multiple devices, so first + * find the beginning of the WFADevice description to allow the + * simplistic parser to pick the correct entries. + */ + tmp = wps_er_find_wfadevice(data); + if (tmp == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: WFADevice:1 device not found - " + "trying to parse invalid data"); + } else + data = tmp; + ap->friendly_name = xml_get_first_item(data, "friendlyName"); wpa_printf(MSG_DEBUG, "WPS ER: friendlyName='%s'", ap->friendly_name); @@ -583,8 +685,12 @@ void wps_er_ap_remove(struct wps_er *er, struct in_addr *addr) static void wps_er_ap_remove_all(struct wps_er *er) { struct wps_er_ap *prev, *ap; + struct wps_er_ap_settings *prev_s, *s; dl_list_for_each_safe(ap, prev, &er->ap, struct wps_er_ap, list) wps_er_ap_remove_entry(er, ap); + dl_list_for_each_safe(s, prev_s, &er->ap_settings, + struct wps_er_ap_settings, list) + os_free(s); } @@ -649,7 +755,7 @@ static struct wps_er_sta * wps_er_add_sta_data(struct wps_er_ap *ap, struct wps_parse_attr *attr, int probe_req) { - struct wps_er_sta *sta = wps_er_sta_get(ap, addr); + struct wps_er_sta *sta = wps_er_sta_get(ap, addr, NULL); int new_sta = 0; int m1; @@ -756,6 +862,12 @@ static void wps_er_process_wlanevent_probe_req(struct wps_er_ap *ap, wpa_hexdump_buf(MSG_MSGDUMP, "WPS ER: WLANEvent - Enrollee's message " "(TLVs from Probe Request)", msg); + if (wps_validate_probe_req(msg, addr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: ER: Ignore invalid proxied " + "Probe Request frame from " MACSTR, MAC2STR(addr)); + return; + } + if (wps_parse_msg(msg, &attr) < 0) { wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse TLVs in " "WLANEvent message"); @@ -763,6 +875,7 @@ static void wps_er_process_wlanevent_probe_req(struct wps_er_ap *ap, } wps_er_add_sta_data(ap, addr, &attr, 1); + wps_registrar_probe_req_rx(ap->er->wps->registrar, addr, msg, 0); } @@ -1151,7 +1264,7 @@ static void wps_er_http_req(void *ctx, struct http_request *req) struct wps_er * -wps_er_init(struct wps_context *wps, const char *ifname) +wps_er_init(struct wps_context *wps, const char *ifname, const char *filter) { struct wps_er *er; struct in_addr addr; @@ -1161,6 +1274,7 @@ wps_er_init(struct wps_context *wps, const char *ifname) return NULL; dl_list_init(&er->ap); dl_list_init(&er->ap_unsubscribing); + dl_list_init(&er->ap_settings); er->multicast_sd = -1; er->ssdp_sd = -1; @@ -1175,6 +1289,16 @@ wps_er_init(struct wps_context *wps, const char *ifname) /* Limit event_id to < 32 bits to avoid issues with atoi() */ er->event_id &= 0x0fffffff; + if (filter) { + if (inet_aton(filter, &er->filter_addr) == 0) { + wpa_printf(MSG_INFO, "WPS UPnP: Invalid filter " + "address %s", filter); + wps_er_deinit(er, NULL, NULL); + return NULL; + } + wpa_printf(MSG_DEBUG, "WPS UPnP: Only accepting connections " + "with %s", filter); + } if (get_netif_info(ifname, &er->ip_addr, &er->ip_addr_text, er->mac_addr)) { wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address " @@ -1184,6 +1308,7 @@ wps_er_init(struct wps_context *wps, const char *ifname) } if (wps_er_ssdp_init(er) < 0) { + wpa_printf(MSG_INFO, "WPS UPnP: SSDP initialization failed"); wps_er_deinit(er, NULL, NULL); return NULL; } @@ -1191,6 +1316,7 @@ wps_er_init(struct wps_context *wps, const char *ifname) addr.s_addr = er->ip_addr; er->http_srv = http_server_init(&addr, -1, wps_er_http_req, er); if (er->http_srv == NULL) { + wpa_printf(MSG_INFO, "WPS UPnP: HTTP initialization failed"); wps_er_deinit(er, NULL, NULL); return NULL; } @@ -1256,19 +1382,30 @@ static void wps_er_http_set_sel_reg_cb(void *ctx, struct http_client *c, enum http_client_event event) { struct wps_er_ap *ap = ctx; + union wps_event_data data; + + os_memset(&data, 0, sizeof(data)); switch (event) { case HTTP_CLIENT_OK: wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar OK"); + data.set_sel_reg.state = WPS_ER_SET_SEL_REG_DONE; + data.set_sel_reg.uuid = ap->uuid; break; case HTTP_CLIENT_FAILED: case HTTP_CLIENT_INVALID_REPLY: case HTTP_CLIENT_TIMEOUT: wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar failed"); + data.set_sel_reg.state = WPS_ER_SET_SEL_REG_FAILED; + data.set_sel_reg.uuid = ap->uuid; break; } http_client_free(ap->http); ap->http = NULL; + + if (data.set_sel_reg.uuid) + ap->er->wps->event_cb(ap->er->wps->cb_ctx, + WPS_EV_ER_SET_SELECTED_REGISTRAR, &data); } @@ -1290,6 +1427,12 @@ static void wps_er_send_set_sel_reg(struct wps_er_ap *ap, struct wpabuf *msg) return; } + if (ap->wps) { + wpa_printf(MSG_DEBUG, "WPS ER: Pending WPS operation for AP - " + "skip SetSelectedRegistrar"); + return; + } + url = http_client_url_parse(ap->control_url, &dst, &path); if (url == NULL) { wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL"); @@ -1339,26 +1482,74 @@ static int wps_er_build_sel_reg_config_methods(struct wpabuf *msg, } +static int wps_er_build_uuid_r(struct wpabuf *msg, const u8 *uuid_r) +{ +#ifdef CONFIG_WPS2 + wpabuf_put_be16(msg, ATTR_UUID_R); + wpabuf_put_be16(msg, WPS_UUID_LEN); + wpabuf_put_data(msg, uuid_r, WPS_UUID_LEN); +#endif /* CONFIG_WPS2 */ + return 0; +} + + void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id, u16 sel_reg_config_methods) { struct wpabuf *msg; struct wps_er_ap *ap; + struct wps_registrar *reg = er->wps->registrar; + const u8 *auth_macs; +#ifdef CONFIG_WPS2 + u8 bcast[ETH_ALEN]; +#endif /* CONFIG_WPS2 */ + size_t count; + union wps_event_data data; + + if (er->skip_set_sel_reg) { + wpa_printf(MSG_DEBUG, "WPS ER: Skip SetSelectedRegistrar"); + return; + } msg = wpabuf_alloc(500); if (msg == NULL) return; + auth_macs = wps_authorized_macs(reg, &count); +#ifdef CONFIG_WPS2 + if (count == 0) { + os_memset(bcast, 0xff, ETH_ALEN); + auth_macs = bcast; + count = 1; + } +#endif /* CONFIG_WPS2 */ + if (wps_build_version(msg) || wps_er_build_selected_registrar(msg, sel_reg) || wps_er_build_dev_password_id(msg, dev_passwd_id) || - wps_er_build_sel_reg_config_methods(msg, sel_reg_config_methods)) { + wps_er_build_sel_reg_config_methods(msg, sel_reg_config_methods) || + wps_build_wfa_ext(msg, 0, auth_macs, count) || + wps_er_build_uuid_r(msg, er->wps->uuid)) { wpabuf_free(msg); return; } - dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) + os_memset(&data, 0, sizeof(data)); + data.set_sel_reg.sel_reg = sel_reg; + data.set_sel_reg.dev_passwd_id = dev_passwd_id; + data.set_sel_reg.sel_reg_config_methods = sel_reg_config_methods; + data.set_sel_reg.state = WPS_ER_SET_SEL_REG_START; + + dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { + if (er->set_sel_reg_uuid_filter && + os_memcmp(ap->uuid, er->set_sel_reg_uuid_filter, + WPS_UUID_LEN) != 0) + continue; + data.set_sel_reg.uuid = ap->uuid; + er->wps->event_cb(er->wps->cb_ctx, + WPS_EV_ER_SET_SELECTED_REGISTRAR, &data); wps_er_send_set_sel_reg(ap, msg); + } wpabuf_free(msg); } @@ -1366,16 +1557,41 @@ void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id, int wps_er_pbc(struct wps_er *er, const u8 *uuid) { + int res; + struct wps_er_ap *ap; + if (er == NULL || er->wps == NULL) return -1; - /* - * TODO: Should enable PBC mode only in a single AP based on which AP - * the Enrollee (uuid) is using. Now, we may end up enabling multiple - * APs in PBC mode which could result in session overlap at the - * Enrollee. - */ - if (wps_registrar_button_pushed(er->wps->registrar)) + if (wps_registrar_pbc_overlap(er->wps->registrar, NULL, NULL)) { + wpa_printf(MSG_DEBUG, "WPS ER: PBC overlap - do not start PBC " + "mode"); + return -2; + } + + ap = wps_er_ap_get(er, NULL, uuid); + if (ap == NULL) { + struct wps_er_sta *sta = NULL; + dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { + sta = wps_er_sta_get(ap, NULL, uuid); + if (sta) { + uuid = ap->uuid; + break; + } + } + if (sta == NULL) + return -3; /* Unknown UUID */ + } + + if (ap->ap_settings == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: AP settings not known"); + return -4; + } + + er->set_sel_reg_uuid_filter = uuid; + res = wps_registrar_button_pushed(er->wps->registrar, NULL); + er->set_sel_reg_uuid_filter = NULL; + if (res) return -1; return 0; @@ -1385,6 +1601,8 @@ int wps_er_pbc(struct wps_er *er, const u8 *uuid) static void wps_er_ap_settings_cb(void *ctx, const struct wps_credential *cred) { struct wps_er_ap *ap = ctx; + union wps_event_data data; + wpa_printf(MSG_DEBUG, "WPS ER: AP Settings received"); os_free(ap->ap_settings); ap->ap_settings = os_malloc(sizeof(*cred)); @@ -1393,7 +1611,11 @@ static void wps_er_ap_settings_cb(void *ctx, const struct wps_credential *cred) ap->ap_settings->cred_attr = NULL; } - /* TODO: send info through ctrl_iface */ + os_memset(&data, 0, sizeof(data)); + data.ap_settings.uuid = ap->uuid; + data.ap_settings.cred = cred; + ap->er->wps->event_cb(ap->er->wps->cb_ctx, WPS_EV_ER_AP_SETTINGS, + &data); } @@ -1436,6 +1658,8 @@ static void wps_er_http_put_message_cb(void *ctx, struct http_client *c, if (buf == NULL) { wpa_printf(MSG_DEBUG, "WPS ER: Could not extract " "NewOutMessage from PutMessage response"); + wps_deinit(ap->wps); + ap->wps = NULL; return; } wps_er_ap_process(ap, buf); @@ -1487,10 +1711,26 @@ static void wps_er_ap_put_message(struct wps_er_ap *ap, static void wps_er_ap_process(struct wps_er_ap *ap, struct wpabuf *msg) { enum wps_process_res res; + struct wps_parse_attr attr; + enum wsc_op_code op_code; - res = wps_process_msg(ap->wps, WSC_MSG, msg); + op_code = WSC_MSG; + if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type) { + switch (*attr.msg_type) { + case WPS_WSC_ACK: + op_code = WSC_ACK; + break; + case WPS_WSC_NACK: + op_code = WSC_NACK; + break; + case WPS_WSC_DONE: + op_code = WSC_Done; + break; + } + } + + res = wps_process_msg(ap->wps, op_code, msg); if (res == WPS_CONTINUE) { - enum wsc_op_code op_code; struct wpabuf *next = wps_get_msg(ap->wps, &op_code); if (next) { wps_er_ap_put_message(ap, next); @@ -1501,6 +1741,10 @@ static void wps_er_ap_process(struct wps_er_ap *ap, struct wpabuf *msg) wps_deinit(ap->wps); ap->wps = NULL; } + } else if (res == WPS_DONE) { + wpa_printf(MSG_DEBUG, "WPS ER: Protocol run done"); + wps_deinit(ap->wps); + ap->wps = NULL; } else { wpa_printf(MSG_DEBUG, "WPS ER: Failed to process message from " "AP (res=%d)", res); @@ -1656,8 +1900,137 @@ int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *pin, if (wps_er_send_get_device_info(ap, wps_er_ap_learn_m1) < 0) return -1; - /* TODO: add PIN without SetSelectedRegistrar trigger to all APs */ - wps_registrar_add_pin(er->wps->registrar, uuid, pin, pin_len, 0); + er->skip_set_sel_reg = 1; + wps_registrar_add_pin(er->wps->registrar, NULL, uuid, pin, pin_len, 0); + er->skip_set_sel_reg = 0; + + return 0; +} + + +int wps_er_set_config(struct wps_er *er, const u8 *uuid, + const struct wps_credential *cred) +{ + struct wps_er_ap *ap; + + if (er == NULL) + return -1; + + ap = wps_er_ap_get(er, NULL, uuid); + if (ap == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: AP not found for set config " + "request"); + return -1; + } + + os_free(ap->ap_settings); + ap->ap_settings = os_malloc(sizeof(*cred)); + if (ap->ap_settings == NULL) + return -1; + os_memcpy(ap->ap_settings, cred, sizeof(*cred)); + ap->ap_settings->cred_attr = NULL; + wpa_printf(MSG_DEBUG, "WPS ER: Updated local AP settings based set " + "config request"); + + return 0; +} + + +static void wps_er_ap_config_m1(struct wps_er_ap *ap, struct wpabuf *m1) +{ + struct wps_config cfg; + + if (ap->wps) { + wpa_printf(MSG_DEBUG, "WPS ER: Protocol run already in " + "progress with this AP"); + return; + } + + os_memset(&cfg, 0, sizeof(cfg)); + cfg.wps = ap->er->wps; + cfg.registrar = 1; + cfg.new_ap_settings = ap->ap_settings; + ap->wps = wps_init(&cfg); + if (ap->wps == NULL) + return; + ap->wps->ap_settings_cb = NULL; + ap->wps->ap_settings_cb_ctx = NULL; + + wps_er_ap_process(ap, m1); +} + + +int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *pin, + size_t pin_len, const struct wps_credential *cred) +{ + struct wps_er_ap *ap; + + if (er == NULL) + return -1; + + ap = wps_er_ap_get(er, NULL, uuid); + if (ap == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: AP not found for config " + "request"); + return -1; + } + if (ap->wps) { + wpa_printf(MSG_DEBUG, "WPS ER: Pending operation ongoing " + "with the AP - cannot start config"); + return -1; + } + + os_free(ap->ap_settings); + ap->ap_settings = os_malloc(sizeof(*cred)); + if (ap->ap_settings == NULL) + return -1; + os_memcpy(ap->ap_settings, cred, sizeof(*cred)); + ap->ap_settings->cred_attr = NULL; + + if (wps_er_send_get_device_info(ap, wps_er_ap_config_m1) < 0) + return -1; + + er->skip_set_sel_reg = 1; + wps_registrar_add_pin(er->wps->registrar, NULL, uuid, pin, pin_len, 0); + er->skip_set_sel_reg = 0; return 0; } + + +#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_er.h b/src/wps/wps_er.h index b13b950fbeaa6..611964741e4b9 100644 --- a/src/wps/wps_er.h +++ b/src/wps/wps_er.h @@ -2,14 +2,8 @@ * Wi-Fi Protected Setup - External Registrar * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPS_ER_H @@ -73,6 +67,12 @@ struct wps_er_ap { void (*m1_handler)(struct wps_er_ap *ap, struct wpabuf *m1); }; +struct wps_er_ap_settings { + struct dl_list list; + u8 uuid[WPS_UUID_LEN]; + struct wps_credential ap_settings; +}; + struct wps_er { struct wps_context *wps; char ifname[17]; @@ -83,6 +83,7 @@ struct wps_er { int ssdp_sd; struct dl_list ap; struct dl_list ap_unsubscribing; + struct dl_list ap_settings; struct http_server *http_srv; int http_port; unsigned int next_ap_id; @@ -90,6 +91,9 @@ struct wps_er { int deinitializing; void (*deinit_done_cb)(void *ctx); void *deinit_done_ctx; + struct in_addr filter_addr; + int skip_set_sel_reg; + const u8 *set_sel_reg_uuid_filter; }; @@ -97,6 +101,7 @@ struct wps_er { void wps_er_ap_add(struct wps_er *er, const u8 *uuid, struct in_addr *addr, const char *location, int max_age); void wps_er_ap_remove(struct wps_er *er, struct in_addr *addr); +int wps_er_ap_cache_settings(struct wps_er *er, struct in_addr *addr); /* wps_er_ssdp.c */ int wps_er_ssdp_init(struct wps_er *er); diff --git a/src/wps/wps_er_ssdp.c b/src/wps/wps_er_ssdp.c index f108435d1eb92..f9f6e6c1a4b63 100644 --- a/src/wps/wps_er_ssdp.c +++ b/src/wps/wps_er_ssdp.c @@ -2,14 +2,8 @@ * Wi-Fi Protected Setup - External Registrar (SSDP) * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -41,6 +35,9 @@ static void wps_er_ssdp_rx(int sd, void *eloop_ctx, void *sock_ctx) if (nread <= 0) return; buf[nread] = '\0'; + if (er->filter_addr.s_addr && + er->filter_addr.s_addr != addr.sin_addr.s_addr) + return; wpa_printf(MSG_DEBUG, "WPS ER: Received SSDP from %s", inet_ntoa(addr.sin_addr)); @@ -110,6 +107,7 @@ static void wps_er_ssdp_rx(int sd, void *eloop_ctx, void *sock_ctx) return; /* Not WPS advertisement/reply */ if (byebye) { + wps_er_ap_cache_settings(er, &addr.sin_addr); wps_er_ap_remove(er, &addr.sin_addr); return; } @@ -162,16 +160,25 @@ void wps_er_send_ssdp_msearch(struct wps_er *er) int wps_er_ssdp_init(struct wps_er *er) { - if (add_ssdp_network(er->ifname)) + if (add_ssdp_network(er->ifname)) { + wpa_printf(MSG_INFO, "WPS ER: Failed to add routing entry for " + "SSDP"); return -1; + } er->multicast_sd = ssdp_open_multicast_sock(er->ip_addr); - if (er->multicast_sd < 0) + if (er->multicast_sd < 0) { + wpa_printf(MSG_INFO, "WPS ER: Failed to open multicast socket " + "for SSDP"); return -1; + } er->ssdp_sd = ssdp_listener_open(); - if (er->ssdp_sd < 0) + if (er->ssdp_sd < 0) { + wpa_printf(MSG_INFO, "WPS ER: Failed to open SSDP listener " + "socket"); return -1; + } if (eloop_register_sock(er->multicast_sd, EVENT_TYPE_READ, wps_er_ssdp_rx, er, NULL) || diff --git a/src/wps/wps_i.h b/src/wps/wps_i.h index 50e66f6c23f3e..8110894e3af9d 100644 --- a/src/wps/wps_i.h +++ b/src/wps/wps_i.h @@ -1,21 +1,18 @@ /* * 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 program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published 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. */ #ifndef WPS_I_H #define WPS_I_H #include "wps.h" +#include "wps_attr_parse.h" + +struct wps_nfc_pw_token; /** * struct wps_data - WPS registration protocol data @@ -102,6 +99,7 @@ struct wps_data { * config_error - Configuration Error value to be used in NACK */ u16 config_error; + u16 error_indication; int ext_reg; int int_reg; @@ -116,84 +114,14 @@ struct wps_data { struct wps_credential *use_cred; int use_psk_key; -}; - + u8 p2p_dev_addr[ETH_ALEN]; /* P2P Device Address of the client or + * 00:00:00:00:00:00 if not a P2p client */ + int pbc_in_m1; -struct wps_parse_attr { - /* fixed length fields */ - const u8 *version; /* 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 */ - - /* 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; - - /* 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; + 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); @@ -202,18 +130,15 @@ void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd, size_t dev_passwd_len); struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr, size_t encr_len); -void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg); +void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg, + u16 config_error, u16 error_indication); void wps_success_event(struct wps_context *wps); void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part); void wps_pbc_overlap_event(struct wps_context *wps); void wps_pbc_timeout_event(struct wps_context *wps); -extern struct oob_device_data oob_ufd_device_data; -extern struct oob_device_data oob_nfc_device_data; -extern struct oob_nfc_device_data oob_nfc_pn531_device_data; - -/* wps_attr_parse.c */ -int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr); +struct wpabuf * wps_build_wsc_ack(struct wps_data *wps); +struct wpabuf * wps_build_wsc_nack(struct wps_data *wps); /* wps_attr_build.c */ int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg); @@ -228,6 +153,8 @@ int wps_build_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg); int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg, struct wpabuf *plain); int wps_build_version(struct wpabuf *msg); +int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll, + const u8 *auth_macs, size_t auth_macs_count); int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type); int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg); int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg); @@ -235,7 +162,10 @@ int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg); int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg); int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg); int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg); -int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps); +int wps_build_oob_dev_pw(struct wpabuf *msg, u16 dev_pw_id, + const struct wpabuf *pubkey, const u8 *dev_pw, + size_t dev_pw_len); +struct wpabuf * wps_ie_encapsulate(struct wpabuf *data); /* wps_attr_process.c */ int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator, @@ -264,15 +194,10 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg); int wps_device_store(struct wps_registrar *reg, struct wps_device_data *dev, const u8 *uuid); void wps_registrar_selected_registrar_changed(struct wps_registrar *reg); - -/* ndef.c */ -struct wpabuf * ndef_parse_wifi(struct wpabuf *buf); -struct wpabuf * ndef_build_wifi(struct wpabuf *buf); - -static inline int wps_version_supported(const u8 *version) -{ - /* Require major version match, but allow minor version differences */ - return version && (*version & 0xf0) == (WPS_VERSION & 0xf0); -} +const u8 * wps_authorized_macs(struct wps_registrar *reg, size_t *count); +int wps_registrar_pbc_overlap(struct wps_registrar *reg, + const u8 *addr, const u8 *uuid_e); +void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg, + struct wps_nfc_pw_token *token); #endif /* WPS_I_H */ diff --git a/src/wps/wps_nfc.c b/src/wps/wps_nfc.c deleted file mode 100644 index ff120002b7294..0000000000000 --- a/src/wps/wps_nfc.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - * NFC routines for Wi-Fi Protected Setup - * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" -#include "common.h" - -#include "wps/wps.h" -#include "wps_i.h" - - -struct wps_nfc_data { - struct oob_nfc_device_data *oob_nfc_dev; -}; - - -static void * init_nfc(struct wps_context *wps, - struct oob_device_data *oob_dev, int registrar) -{ - struct oob_nfc_device_data *oob_nfc_dev; - struct wps_nfc_data *data; - - oob_nfc_dev = wps_get_oob_nfc_device(oob_dev->device_name); - if (oob_nfc_dev == NULL) { - wpa_printf(MSG_ERROR, "WPS (NFC): Unknown NFC device (%s)", - oob_dev->device_name); - return NULL; - } - - if (oob_nfc_dev->init_func(oob_dev->device_path) < 0) - return NULL; - - data = os_zalloc(sizeof(*data)); - if (data == NULL) { - wpa_printf(MSG_ERROR, "WPS (NFC): Failed to allocate " - "nfc data area"); - return NULL; - } - data->oob_nfc_dev = oob_nfc_dev; - return data; -} - - -static struct wpabuf * read_nfc(void *priv) -{ - struct wps_nfc_data *data = priv; - struct wpabuf *wifi, *buf; - char *raw_data; - size_t len; - - raw_data = data->oob_nfc_dev->read_func(&len); - if (raw_data == NULL) - return NULL; - - wifi = wpabuf_alloc_copy(raw_data, len); - os_free(raw_data); - if (wifi == NULL) { - wpa_printf(MSG_ERROR, "WPS (NFC): Failed to allocate " - "nfc read area"); - return NULL; - } - - buf = ndef_parse_wifi(wifi); - wpabuf_free(wifi); - if (buf == NULL) - wpa_printf(MSG_ERROR, "WPS (NFC): Failed to unwrap"); - return buf; -} - - -static int write_nfc(void *priv, struct wpabuf *buf) -{ - struct wps_nfc_data *data = priv; - struct wpabuf *wifi; - int ret; - - wifi = ndef_build_wifi(buf); - if (wifi == NULL) { - wpa_printf(MSG_ERROR, "WPS (NFC): Failed to wrap"); - return -1; - } - - ret = data->oob_nfc_dev->write_func(wpabuf_mhead(wifi), - wpabuf_len(wifi)); - wpabuf_free(wifi); - return ret; -} - - -static void deinit_nfc(void *priv) -{ - struct wps_nfc_data *data = priv; - - data->oob_nfc_dev->deinit_func(); - - os_free(data); -} - - -struct oob_device_data oob_nfc_device_data = { - .device_name = NULL, - .device_path = NULL, - .init_func = init_nfc, - .read_func = read_nfc, - .write_func = write_nfc, - .deinit_func = deinit_nfc, -}; diff --git a/src/wps/wps_nfc_pn531.c b/src/wps/wps_nfc_pn531.c deleted file mode 100644 index 7e05e4dd5d881..0000000000000 --- a/src/wps/wps_nfc_pn531.c +++ /dev/null @@ -1,113 +0,0 @@ -/* - * NFC PN531 routines for Wi-Fi Protected Setup - * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" -#include "common.h" - -#include "wps/wps.h" -#include "wps_i.h" - -#include "WpsNfcType.h" -#include "WpsNfc.h" - - -static int init_nfc_pn531(char *path) -{ - u32 ret; - - ret = WpsNfcInit(); - if (ret != WPS_NFCLIB_ERR_SUCCESS) { - wpa_printf(MSG_ERROR, "WPS (PN531): Failed to initialize " - "NFC Library: 0x%08x", ret); - return -1; - } - - ret = WpsNfcOpenDevice((int8 *) path); - if (ret != WPS_NFCLIB_ERR_SUCCESS) { - wpa_printf(MSG_ERROR, "WPS (PN531): Failed to open " - "NFC Device(%s): 0x%08x", path, ret); - goto fail; - } - - ret = WpsNfcTokenDiscovery(); - if (ret != WPS_NFCLIB_ERR_SUCCESS) { - wpa_printf(MSG_ERROR, "WPS (PN531): Failed to discover " - "token: 0x%08x", ret); - WpsNfcCloseDevice(); - goto fail; - } - - return 0; - -fail: - WpsNfcDeinit(); - return -1; -} - - -static void * read_nfc_pn531(size_t *size) -{ - uint32 len; - u32 ret; - int8 *data; - - ret = WpsNfcRawReadToken(&data, &len); - if (ret != WPS_NFCLIB_ERR_SUCCESS) { - wpa_printf(MSG_ERROR, "WPS (PN531): Failed to read: 0x%08x", - ret); - return NULL; - } - - *size = len; - return data; -} - - -static int write_nfc_pn531(void *data, size_t len) -{ - u32 ret; - - ret = WpsNfcRawWriteToken(data, len); - if (ret != WPS_NFCLIB_ERR_SUCCESS) { - wpa_printf(MSG_ERROR, "WPS (PN531): Failed to write: 0x%08x", - ret); - return -1; - } - - return 0; -} - - -static void deinit_nfc_pn531(void) -{ - u32 ret; - - ret = WpsNfcCloseDevice(); - if (ret != WPS_NFCLIB_ERR_SUCCESS) - wpa_printf(MSG_ERROR, "WPS (PN531): Failed to close " - "NFC Device: 0x%08x", ret); - - ret = WpsNfcDeinit(); - if (ret != WPS_NFCLIB_ERR_SUCCESS) - wpa_printf(MSG_ERROR, "WPS (PN531): Failed to deinitialize " - "NFC Library: 0x%08x", ret); -} - - -struct oob_nfc_device_data oob_nfc_pn531_device_data = { - .init_func = init_nfc_pn531, - .read_func = read_nfc_pn531, - .write_func = write_nfc_pn531, - .deinit_func = deinit_nfc_pn531, -}; diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c index 81ddf3a2b57b1..b650a3c0ab361 100644 --- a/src/wps/wps_registrar.c +++ b/src/wps/wps_registrar.c @@ -1,15 +1,9 @@ /* * Wi-Fi Protected Setup - Registrar - * Copyright (c) 2008-2009, 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 - * published 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 "utils/includes.h" @@ -21,13 +15,63 @@ #include "utils/list.h" #include "crypto/crypto.h" #include "crypto/sha256.h" +#include "crypto/random.h" #include "common/ieee802_11_defs.h" #include "wps_i.h" #include "wps_dev_attr.h" #include "wps_upnp.h" #include "wps_upnp_i.h" +#ifndef CONFIG_WPS_STRICT #define WPS_WORKAROUNDS +#endif /* CONFIG_WPS_STRICT */ + +#ifdef CONFIG_WPS_NFC + +struct wps_nfc_pw_token { + struct dl_list list; + u8 pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN]; + 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; @@ -39,6 +83,7 @@ struct wps_uuid_pin { #define PIN_EXPIRES BIT(1) int flags; struct os_time expiration; + u8 enrollee_addr[ETH_ALEN]; }; @@ -104,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, @@ -114,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; @@ -123,10 +170,19 @@ struct wps_registrar { int sel_reg_dev_password_id_override; int sel_reg_config_methods_override; int static_wep_only; + int dualband; struct wps_registrar_device *devices; int force_pbc_overlap; + + u8 authorized_macs[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN]; + u8 authorized_macs_union[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN]; + + u8 p2p_dev_addr[ETH_ALEN]; + + u8 pbc_ignore_uuid[WPS_UUID_LEN]; + struct os_time pbc_ignore_start; }; @@ -134,6 +190,54 @@ static int wps_set_ie(struct wps_registrar *reg); static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx); static void wps_registrar_set_selected_timeout(void *eloop_ctx, void *timeout_ctx); +static void wps_registrar_remove_pin(struct wps_registrar *reg, + struct wps_uuid_pin *pin); + + +static void wps_registrar_add_authorized_mac(struct wps_registrar *reg, + const u8 *addr) +{ + int i; + wpa_printf(MSG_DEBUG, "WPS: Add authorized MAC " MACSTR, + MAC2STR(addr)); + for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++) + if (os_memcmp(reg->authorized_macs[i], addr, ETH_ALEN) == 0) { + wpa_printf(MSG_DEBUG, "WPS: Authorized MAC was " + "already in the list"); + return; /* already in list */ + } + for (i = WPS_MAX_AUTHORIZED_MACS - 1; i > 0; i--) + os_memcpy(reg->authorized_macs[i], reg->authorized_macs[i - 1], + ETH_ALEN); + os_memcpy(reg->authorized_macs[0], addr, ETH_ALEN); + wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs", + (u8 *) reg->authorized_macs, sizeof(reg->authorized_macs)); +} + + +static void wps_registrar_remove_authorized_mac(struct wps_registrar *reg, + const u8 *addr) +{ + int i; + wpa_printf(MSG_DEBUG, "WPS: Remove authorized MAC " MACSTR, + MAC2STR(addr)); + for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++) { + if (os_memcmp(reg->authorized_macs, addr, ETH_ALEN) == 0) + break; + } + if (i == WPS_MAX_AUTHORIZED_MACS) { + wpa_printf(MSG_DEBUG, "WPS: Authorized MAC was not in the " + "list"); + return; /* not in the list */ + } + for (; i + 1 < WPS_MAX_AUTHORIZED_MACS; i++) + os_memcpy(reg->authorized_macs[i], reg->authorized_macs[i + 1], + ETH_ALEN); + os_memset(reg->authorized_macs[WPS_MAX_AUTHORIZED_MACS - 1], 0, + ETH_ALEN); + wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs", + (u8 *) reg->authorized_macs, sizeof(reg->authorized_macs)); +} static void wps_free_devices(struct wps_registrar_device *dev) @@ -254,20 +358,29 @@ static void wps_registrar_add_pbc_session(struct wps_registrar *reg, static void wps_registrar_remove_pbc_session(struct wps_registrar *reg, - const u8 *addr, const u8 *uuid_e) + const u8 *uuid_e, + const u8 *p2p_dev_addr) { - struct wps_pbc_session *pbc, *prev = NULL; + struct wps_pbc_session *pbc, *prev = NULL, *tmp; pbc = reg->pbc_sessions; while (pbc) { - if (os_memcmp(pbc->addr, addr, ETH_ALEN) == 0 && - os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0) { + if (os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0 || + (p2p_dev_addr && !is_zero_ether_addr(reg->p2p_dev_addr) && + os_memcmp(reg->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) == + 0)) { if (prev) prev->next = pbc->next; else reg->pbc_sessions = pbc->next; - os_free(pbc); - break; + tmp = pbc; + pbc = pbc->next; + wpa_printf(MSG_DEBUG, "WPS: Removing PBC session for " + "addr=" MACSTR, MAC2STR(tmp->addr)); + wpa_hexdump(MSG_DEBUG, "WPS: Removed UUID-E", + tmp->uuid_e, WPS_UUID_LEN); + os_free(tmp); + continue; } prev = pbc; pbc = pbc->next; @@ -275,26 +388,50 @@ static void wps_registrar_remove_pbc_session(struct wps_registrar *reg, } -static int wps_registrar_pbc_overlap(struct wps_registrar *reg, - const u8 *addr, const u8 *uuid_e) +int wps_registrar_pbc_overlap(struct wps_registrar *reg, + const u8 *addr, const u8 *uuid_e) { int count = 0; struct wps_pbc_session *pbc; + struct wps_pbc_session *first = NULL; struct os_time now; os_get_time(&now); + wpa_printf(MSG_DEBUG, "WPS: Checking active PBC sessions for overlap"); + + if (uuid_e) { + wpa_printf(MSG_DEBUG, "WPS: Add one for the requested UUID"); + wpa_hexdump(MSG_DEBUG, "WPS: Requested UUID", + uuid_e, WPS_UUID_LEN); + count++; + } + for (pbc = reg->pbc_sessions; pbc; pbc = pbc->next) { - if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME) + wpa_printf(MSG_DEBUG, "WPS: Consider PBC session with " MACSTR, + MAC2STR(pbc->addr)); + wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", + pbc->uuid_e, WPS_UUID_LEN); + if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME) { + wpa_printf(MSG_DEBUG, "WPS: PBC walk time has " + "expired"); break; - if (addr == NULL || os_memcmp(addr, pbc->addr, ETH_ALEN) || - uuid_e == NULL || - os_memcmp(uuid_e, pbc->uuid_e, WPS_UUID_LEN)) + } + if (first && + os_memcmp(pbc->uuid_e, first->uuid_e, WPS_UUID_LEN) == 0) { + wpa_printf(MSG_DEBUG, "WPS: Same Enrollee"); + continue; /* same Enrollee */ + } + if (uuid_e == NULL || + os_memcmp(uuid_e, pbc->uuid_e, WPS_UUID_LEN)) { + wpa_printf(MSG_DEBUG, "WPS: New Enrollee"); count++; + } + if (first == NULL) + first = pbc; } - if (addr || uuid_e) - count++; + wpa_printf(MSG_DEBUG, "WPS: %u active PBC session(s) found", count); return count > 1 ? 1 : 0; } @@ -339,7 +476,7 @@ static void wps_registrar_free_pending_m2(struct wps_context *wps) static int wps_build_ap_setup_locked(struct wps_context *wps, struct wpabuf *msg) { - if (wps->ap_setup_locked) { + if (wps->ap_setup_locked && wps->ap_setup_locked != 2) { wpa_printf(MSG_DEBUG, "WPS: * AP Setup Locked"); wpabuf_put_be16(msg, ATTR_AP_SETUP_LOCKED); wpabuf_put_be16(msg, 1); @@ -378,15 +515,59 @@ static int wps_build_sel_reg_dev_password_id(struct wps_registrar *reg, } +static int wps_build_sel_pbc_reg_uuid_e(struct wps_registrar *reg, + struct wpabuf *msg) +{ + u16 id = reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT; + if (!reg->sel_reg_union) + return 0; + if (reg->sel_reg_dev_password_id_override >= 0) + id = reg->sel_reg_dev_password_id_override; + if (id != DEV_PW_PUSHBUTTON || !reg->dualband) + return 0; + return wps_build_uuid_e(msg, reg->wps->uuid); +} + + +static void wps_set_pushbutton(u16 *methods, u16 conf_methods) +{ + *methods |= WPS_CONFIG_PUSHBUTTON; +#ifdef CONFIG_WPS2 + if ((conf_methods & WPS_CONFIG_VIRT_PUSHBUTTON) == + WPS_CONFIG_VIRT_PUSHBUTTON) + *methods |= WPS_CONFIG_VIRT_PUSHBUTTON; + if ((conf_methods & WPS_CONFIG_PHY_PUSHBUTTON) == + WPS_CONFIG_PHY_PUSHBUTTON) + *methods |= WPS_CONFIG_PHY_PUSHBUTTON; + if ((*methods & WPS_CONFIG_VIRT_PUSHBUTTON) != + WPS_CONFIG_VIRT_PUSHBUTTON && + (*methods & WPS_CONFIG_PHY_PUSHBUTTON) != + WPS_CONFIG_PHY_PUSHBUTTON) { + /* + * Required to include virtual/physical flag, but we were not + * configured with push button type, so have to default to one + * of them. + */ + *methods |= WPS_CONFIG_PHY_PUSHBUTTON; + } +#endif /* CONFIG_WPS2 */ +} + + static int wps_build_sel_reg_config_methods(struct wps_registrar *reg, struct wpabuf *msg) { u16 methods; if (!reg->sel_reg_union) return 0; - methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON; + methods = reg->wps->config_methods; + methods &= ~WPS_CONFIG_PUSHBUTTON; +#ifdef CONFIG_WPS2 + methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON | + WPS_CONFIG_PHY_PUSHBUTTON); +#endif /* CONFIG_WPS2 */ if (reg->pbc) - methods |= WPS_CONFIG_PUSHBUTTON; + wps_set_pushbutton(&methods, reg->wps->config_methods); if (reg->sel_reg_config_methods_override >= 0) methods = reg->sel_reg_config_methods_override; wpa_printf(MSG_DEBUG, "WPS: * Selected Registrar Config Methods (%x)", @@ -407,6 +588,10 @@ static int wps_build_probe_config_methods(struct wps_registrar *reg, * external Registrars. */ methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON; +#ifdef CONFIG_WPS2 + methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON | + WPS_CONFIG_PHY_PUSHBUTTON); +#endif /* CONFIG_WPS2 */ wpa_printf(MSG_DEBUG, "WPS: * Config Methods (%x)", methods); wpabuf_put_be16(msg, ATTR_CONFIG_METHODS); wpabuf_put_be16(msg, 2); @@ -418,11 +603,23 @@ static int wps_build_probe_config_methods(struct wps_registrar *reg, static int wps_build_config_methods_r(struct wps_registrar *reg, struct wpabuf *msg) { - u16 methods; - methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON; - if (reg->pbc) - methods |= WPS_CONFIG_PUSHBUTTON; - return wps_build_config_methods(msg, methods); + return wps_build_config_methods(msg, reg->wps->config_methods); +} + + +const u8 * wps_authorized_macs(struct wps_registrar *reg, size_t *count) +{ + *count = 0; + +#ifdef CONFIG_WPS2 + while (*count < WPS_MAX_AUTHORIZED_MACS) { + if (is_zero_ether_addr(reg->authorized_macs_union[*count])) + break; + (*count)++; + } +#endif /* CONFIG_WPS2 */ + + return (const u8 *) reg->authorized_macs_union; } @@ -447,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; @@ -468,6 +666,7 @@ wps_registrar_init(struct wps_context *wps, reg->sel_reg_dev_password_id_override = -1; reg->sel_reg_config_methods_override = -1; reg->static_wep_only = cfg->static_wep_only; + reg->dualband = cfg->dualband; if (wps_set_ie(reg)) { wps_registrar_deinit(reg); @@ -489,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); @@ -496,23 +696,42 @@ void wps_registrar_deinit(struct wps_registrar *reg) } +static void wps_registrar_invalidate_unused(struct wps_registrar *reg) +{ + struct wps_uuid_pin *pin; + + dl_list_for_each(pin, ®->pins, struct wps_uuid_pin, list) { + if (pin->wildcard_uuid == 1 && !(pin->flags & PIN_LOCKED)) { + wpa_printf(MSG_DEBUG, "WPS: Invalidate previously " + "configured wildcard PIN"); + wps_registrar_remove_pin(reg, pin); + break; + } + } +} + + /** * wps_registrar_add_pin - Configure a new PIN for Registrar * @reg: Registrar data from wps_registrar_init() + * @addr: Enrollee MAC address or %NULL if not known * @uuid: UUID-E or %NULL for wildcard (any UUID) * @pin: PIN (Device Password) * @pin_len: Length of pin in octets * @timeout: Time (in seconds) when the PIN will be invalidated; 0 = no timeout * Returns: 0 on success, -1 on failure */ -int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid, - const u8 *pin, size_t pin_len, int timeout) +int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *addr, + const u8 *uuid, const u8 *pin, size_t pin_len, + int timeout) { struct wps_uuid_pin *p; p = os_zalloc(sizeof(*p)); if (p == NULL) return -1; + if (addr) + os_memcpy(p->enrollee_addr, addr, ETH_ALEN); if (uuid == NULL) p->wildcard_uuid = 1; else @@ -531,6 +750,9 @@ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid, p->expiration.sec += timeout; } + if (p->wildcard_uuid) + wps_registrar_invalidate_unused(reg); + dl_list_add(®->pins, &p->list); wpa_printf(MSG_DEBUG, "WPS: A new PIN configured (timeout=%d)", @@ -539,6 +761,11 @@ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid, wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: PIN", pin, pin_len); reg->selected_registrar = 1; reg->pbc = 0; + if (addr) + wps_registrar_add_authorized_mac(reg, addr); + else + wps_registrar_add_authorized_mac( + reg, (u8 *) "\xff\xff\xff\xff\xff\xff"); wps_registrar_selected_registrar_changed(reg); eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); eloop_register_timeout(WPS_PBC_WALK_TIME, 0, @@ -549,6 +776,22 @@ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid, } +static void wps_registrar_remove_pin(struct wps_registrar *reg, + struct wps_uuid_pin *pin) +{ + u8 *addr; + u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + if (is_zero_ether_addr(pin->enrollee_addr)) + addr = bcast; + else + addr = pin->enrollee_addr; + wps_registrar_remove_authorized_mac(reg, addr); + wps_remove_pin(pin); + wps_registrar_selected_registrar_changed(reg); +} + + static void wps_registrar_expire_pins(struct wps_registrar *reg) { struct wps_uuid_pin *pin, *prev; @@ -561,9 +804,40 @@ static void wps_registrar_expire_pins(struct wps_registrar *reg) os_time_before(&pin->expiration, &now)) { wpa_hexdump(MSG_DEBUG, "WPS: Expired PIN for UUID", pin->uuid, WPS_UUID_LEN); - wps_remove_pin(pin); + wps_registrar_remove_pin(reg, pin); + } + } +} + + +/** + * wps_registrar_invalidate_wildcard_pin - Invalidate a wildcard PIN + * @reg: Registrar data from wps_registrar_init() + * @dev_pw: PIN to search for or %NULL to match any + * @dev_pw_len: Length of dev_pw in octets + * Returns: 0 on success, -1 if not wildcard PIN is enabled + */ +static int wps_registrar_invalidate_wildcard_pin(struct wps_registrar *reg, + const u8 *dev_pw, + size_t dev_pw_len) +{ + struct wps_uuid_pin *pin, *prev; + + dl_list_for_each_safe(pin, prev, ®->pins, struct wps_uuid_pin, list) + { + if (dev_pw && pin->pin && + (dev_pw_len != pin->pin_len || + os_memcmp(dev_pw, pin->pin, dev_pw_len) != 0)) + continue; /* different PIN */ + if (pin->wildcard_uuid) { + wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID", + pin->uuid, WPS_UUID_LEN); + wps_registrar_remove_pin(reg, pin); + return 0; } } + + return -1; } @@ -582,7 +856,7 @@ int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid) if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) { wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID", pin->uuid, WPS_UUID_LEN); - wps_remove_pin(pin); + wps_registrar_remove_pin(reg, pin); return 0; } } @@ -610,10 +884,11 @@ static const u8 * wps_registrar_get_pin(struct wps_registrar *reg, /* Check for wildcard UUIDs since none of the UUID-specific * PINs matched */ dl_list_for_each(pin, ®->pins, struct wps_uuid_pin, list) { - if (pin->wildcard_uuid == 1) { + if (pin->wildcard_uuid == 1 || + pin->wildcard_uuid == 2) { wpa_printf(MSG_DEBUG, "WPS: Found a wildcard " "PIN. Assigned it for this UUID-E"); - pin->wildcard_uuid = 2; + pin->wildcard_uuid++; os_memcpy(pin->uuid, uuid, WPS_UUID_LEN); found = pin; break; @@ -655,7 +930,7 @@ int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid) dl_list_for_each(pin, ®->pins, struct wps_uuid_pin, list) { if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) { - if (pin->wildcard_uuid == 2) { + if (pin->wildcard_uuid == 3) { wpa_printf(MSG_DEBUG, "WPS: Invalidating used " "wildcard PIN"); return wps_registrar_invalidate_pin(reg, uuid); @@ -673,6 +948,9 @@ static void wps_registrar_stop_pbc(struct wps_registrar *reg) { reg->selected_registrar = 0; reg->pbc = 0; + os_memset(reg->p2p_dev_addr, 0, ETH_ALEN); + wps_registrar_remove_authorized_mac(reg, + (u8 *) "\xff\xff\xff\xff\xff\xff"); wps_registrar_selected_registrar_changed(reg); } @@ -690,26 +968,40 @@ static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx) /** * wps_registrar_button_pushed - Notify Registrar that AP button was pushed * @reg: Registrar data from wps_registrar_init() - * Returns: 0 on success, -1 on failure + * @p2p_dev_addr: Limit allowed PBC devices to the specified P2P device, %NULL + * indicates no such filtering + * Returns: 0 on success, -1 on failure, -2 on session overlap * * This function is called on an AP when a push button is pushed to activate * PBC mode. The PBC mode will be stopped after walk time (2 minutes) timeout - * or when a PBC registration is completed. + * or when a PBC registration is completed. If more than one Enrollee in active + * PBC mode has been detected during the monitor time (previous 2 minutes), the + * PBC mode is not activated and -2 is returned to indicate session overlap. + * This is skipped if a specific Enrollee is selected. */ -int wps_registrar_button_pushed(struct wps_registrar *reg) +int wps_registrar_button_pushed(struct wps_registrar *reg, + const u8 *p2p_dev_addr) { - if (wps_registrar_pbc_overlap(reg, NULL, NULL)) { + if (p2p_dev_addr == NULL && + wps_registrar_pbc_overlap(reg, NULL, NULL)) { wpa_printf(MSG_DEBUG, "WPS: PBC overlap - do not start PBC " "mode"); wps_pbc_overlap_event(reg->wps); - return -1; + return -2; } wpa_printf(MSG_DEBUG, "WPS: Button pushed - PBC mode started"); reg->force_pbc_overlap = 0; reg->selected_registrar = 1; reg->pbc = 1; + if (p2p_dev_addr) + os_memcpy(reg->p2p_dev_addr, p2p_dev_addr, ETH_ALEN); + else + os_memset(reg->p2p_dev_addr, 0, ETH_ALEN); + wps_registrar_add_authorized_mac(reg, + (u8 *) "\xff\xff\xff\xff\xff\xff"); wps_registrar_selected_registrar_changed(reg); + eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL); eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wps_registrar_pbc_timeout, reg, NULL); @@ -734,6 +1026,46 @@ static void wps_registrar_pin_completed(struct wps_registrar *reg) } +void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e, + const u8 *dev_pw, size_t dev_pw_len) +{ + if (registrar->pbc) { + wps_registrar_remove_pbc_session(registrar, + uuid_e, NULL); + wps_registrar_pbc_completed(registrar); + os_get_time(®istrar->pbc_ignore_start); + os_memcpy(registrar->pbc_ignore_uuid, uuid_e, WPS_UUID_LEN); + } else { + wps_registrar_pin_completed(registrar); + } + + if (dev_pw && + wps_registrar_invalidate_wildcard_pin(registrar, dev_pw, + dev_pw_len) == 0) { + wpa_hexdump_key(MSG_DEBUG, "WPS: Invalidated wildcard PIN", + dev_pw, dev_pw_len); + } +} + + +int wps_registrar_wps_cancel(struct wps_registrar *reg) +{ + if (reg->pbc) { + wpa_printf(MSG_DEBUG, "WPS: PBC is set - cancelling it"); + wps_registrar_pbc_timeout(reg, NULL); + eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL); + return 1; + } else if (reg->selected_registrar) { + /* PIN Method */ + wpa_printf(MSG_DEBUG, "WPS: PIN is set - cancelling it"); + wps_registrar_pin_completed(reg); + wps_registrar_invalidate_wildcard_pin(reg, NULL, 0); + return 1; + } + return 0; +} + + /** * wps_registrar_probe_req_rx - Notify Registrar of Probe Request * @reg: Registrar data from wps_registrar_init() @@ -745,9 +1077,11 @@ static void wps_registrar_pin_completed(struct wps_registrar *reg) * situation with other WPS APs. */ void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr, - const struct wpabuf *wps_data) + const struct wpabuf *wps_data, + int p2p_wildcard) { struct wps_parse_attr attr; + int skip_add = 0; wpa_hexdump_buf(MSG_MSGDUMP, "WPS: Probe Request with WPS data received", @@ -755,11 +1089,6 @@ void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr, if (wps_parse_msg(wps_data, &attr) < 0) return; - if (!wps_version_supported(attr.version)) { - wpa_printf(MSG_DEBUG, "WPS: Unsupported ProbeReq WPS IE " - "version 0x%x", attr.version ? *attr.version : 0); - return; - } if (attr.config_methods == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Config Methods attribute in " @@ -774,7 +1103,7 @@ void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr, } if (reg->enrollee_seen_cb && attr.uuid_e && - attr.primary_dev_type && attr.request_type) { + attr.primary_dev_type && attr.request_type && !p2p_wildcard) { char *dev_name = NULL; if (attr.dev_name) { dev_name = os_zalloc(attr.dev_name_len + 1); @@ -801,8 +1130,27 @@ void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr, "UUID-E included"); return; } + wpa_hexdump(MSG_DEBUG, "WPS: UUID-E from Probe Request", attr.uuid_e, + WPS_UUID_LEN); - wps_registrar_add_pbc_session(reg, addr, attr.uuid_e); +#ifdef WPS_WORKAROUNDS + if (reg->pbc_ignore_start.sec && + os_memcmp(attr.uuid_e, reg->pbc_ignore_uuid, WPS_UUID_LEN) == 0) { + struct os_time now, dur; + os_get_time(&now); + os_time_sub(&now, ®->pbc_ignore_start, &dur); + if (dur.sec >= 0 && dur.sec < 5) { + wpa_printf(MSG_DEBUG, "WPS: Ignore PBC activation " + "based on Probe Request from the Enrollee " + "that just completed PBC provisioning"); + skip_add = 1; + } else + reg->pbc_ignore_start.sec = 0; + } +#endif /* WPS_WORKAROUNDS */ + + if (!skip_add) + wps_registrar_add_pbc_session(reg, addr, attr.uuid_e); if (wps_registrar_pbc_overlap(reg, addr, attr.uuid_e)) { wpa_printf(MSG_DEBUG, "WPS: PBC session overlap detected"); reg->force_pbc_overlap = 1; @@ -832,12 +1180,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); } @@ -856,74 +1205,84 @@ static void wps_cb_set_sel_reg(struct wps_registrar *reg) if (reg->selected_registrar) { methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON; +#ifdef CONFIG_WPS2 + methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON | + WPS_CONFIG_PHY_PUSHBUTTON); +#endif /* CONFIG_WPS2 */ if (reg->pbc) - methods |= WPS_CONFIG_PUSHBUTTON; + wps_set_pushbutton(&methods, reg->wps->config_methods); } + wpa_printf(MSG_DEBUG, "WPS: wps_cb_set_sel_reg: sel_reg=%d " + "config_methods=0x%x pbc=%d methods=0x%x", + reg->selected_registrar, reg->wps->config_methods, + reg->pbc, methods); + reg->set_sel_reg_cb(reg->cb_ctx, reg->selected_registrar, reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT, methods); } -/* Encapsulate WPS IE data with one (or more, if needed) IE headers */ -static struct wpabuf * wps_ie_encapsulate(struct wpabuf *data) -{ - struct wpabuf *ie; - const u8 *pos, *end; - - ie = wpabuf_alloc(wpabuf_len(data) + 100); - if (ie == NULL) { - wpabuf_free(data); - return NULL; - } - - pos = wpabuf_head(data); - end = pos + wpabuf_len(data); - - while (end > pos) { - size_t frag_len = end - pos; - if (frag_len > 251) - frag_len = 251; - wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); - wpabuf_put_u8(ie, 4 + frag_len); - wpabuf_put_be32(ie, WPS_DEV_OUI_WFA); - wpabuf_put_data(ie, pos, frag_len); - pos += frag_len; - } - - wpabuf_free(data); - - return ie; -} - - static int wps_set_ie(struct wps_registrar *reg) { struct wpabuf *beacon; struct wpabuf *probe; + const u8 *auth_macs; + size_t count; + size_t vendor_len = 0; + int i; if (reg->set_ie_cb == NULL) return 0; - wpa_printf(MSG_DEBUG, "WPS: Build Beacon and Probe Response IEs"); + for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) { + if (reg->wps->dev.vendor_ext[i]) { + vendor_len += 2 + 2; + vendor_len += wpabuf_len(reg->wps->dev.vendor_ext[i]); + } + } - beacon = wpabuf_alloc(300); + beacon = wpabuf_alloc(400 + vendor_len); if (beacon == NULL) return -1; - probe = wpabuf_alloc(400); + probe = wpabuf_alloc(500 + vendor_len); if (probe == NULL) { wpabuf_free(beacon); return -1; } + auth_macs = wps_authorized_macs(reg, &count); + + wpa_printf(MSG_DEBUG, "WPS: Build Beacon IEs"); + if (wps_build_version(beacon) || wps_build_wps_state(reg->wps, beacon) || wps_build_ap_setup_locked(reg->wps, beacon) || wps_build_selected_registrar(reg, beacon) || wps_build_sel_reg_dev_password_id(reg, beacon) || wps_build_sel_reg_config_methods(reg, beacon) || - wps_build_version(probe) || + wps_build_sel_pbc_reg_uuid_e(reg, beacon) || + (reg->dualband && wps_build_rf_bands(®->wps->dev, beacon)) || + wps_build_wfa_ext(beacon, 0, auth_macs, count) || + wps_build_vendor_ext(®->wps->dev, beacon)) { + wpabuf_free(beacon); + wpabuf_free(probe); + return -1; + } + +#ifdef CONFIG_P2P + if (wps_build_dev_name(®->wps->dev, beacon) || + wps_build_primary_dev_type(®->wps->dev, beacon)) { + wpabuf_free(beacon); + wpabuf_free(probe); + return -1; + } +#endif /* CONFIG_P2P */ + + wpa_printf(MSG_DEBUG, "WPS: Build Probe Response IEs"); + + if (wps_build_version(probe) || wps_build_wps_state(reg->wps, probe) || wps_build_ap_setup_locked(reg->wps, probe) || wps_build_selected_registrar(reg, probe) || @@ -934,7 +1293,9 @@ static int wps_set_ie(struct wps_registrar *reg) wps_build_uuid_e(probe, reg->wps->uuid) || wps_build_device_attrs(®->wps->dev, probe) || wps_build_probe_config_methods(reg, probe) || - wps_build_rf_bands(®->wps->dev, probe)) { + (reg->dualband && wps_build_rf_bands(®->wps->dev, probe)) || + wps_build_wfa_ext(probe, 0, auth_macs, count) || + wps_build_vendor_ext(®->wps->dev, probe)) { wpabuf_free(beacon); wpabuf_free(probe); return -1; @@ -987,6 +1348,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); @@ -1025,7 +1393,7 @@ static int wps_build_r_hash(struct wps_data *wps, struct wpabuf *msg) const u8 *addr[4]; size_t len[4]; - if (os_get_random(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0) + if (random_get_bytes(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0) return -1; wpa_hexdump(MSG_DEBUG, "WPS: R-S1", wps->snonce, WPS_SECRET_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "WPS: R-S2", @@ -1091,7 +1459,7 @@ static int wps_build_r_snonce2(struct wps_data *wps, struct wpabuf *msg) static int wps_build_cred_network_idx(struct wpabuf *msg, const struct wps_credential *cred) { - wpa_printf(MSG_DEBUG, "WPS: * Network Index"); + wpa_printf(MSG_DEBUG, "WPS: * Network Index (1)"); wpabuf_put_be16(msg, ATTR_NETWORK_INDEX); wpabuf_put_be16(msg, 1); wpabuf_put_u8(msg, 1); @@ -1103,6 +1471,8 @@ static int wps_build_cred_ssid(struct wpabuf *msg, const struct wps_credential *cred) { wpa_printf(MSG_DEBUG, "WPS: * SSID"); + wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID for Credential", + cred->ssid, cred->ssid_len); wpabuf_put_be16(msg, ATTR_SSID); wpabuf_put_be16(msg, cred->ssid_len); wpabuf_put_data(msg, cred->ssid, cred->ssid_len); @@ -1139,6 +1509,8 @@ static int wps_build_cred_network_key(struct wpabuf *msg, { wpa_printf(MSG_DEBUG, "WPS: * Network Key (len=%d)", (int) cred->key_len); + wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key", + cred->key, cred->key_len); wpabuf_put_be16(msg, ATTR_NETWORK_KEY); wpabuf_put_be16(msg, cred->key_len); wpabuf_put_data(msg, cred->key, cred->key_len); @@ -1172,6 +1544,25 @@ static int wps_build_credential(struct wpabuf *msg, } +int wps_build_credential_wrap(struct wpabuf *msg, + const struct wps_credential *cred) +{ + struct wpabuf *wbuf; + wbuf = wpabuf_alloc(200); + if (wbuf == NULL) + return -1; + if (wps_build_credential(wbuf, cred)) { + wpabuf_free(wbuf); + return -1; + } + wpabuf_put_be16(msg, ATTR_CRED); + wpabuf_put_be16(msg, wpabuf_len(wbuf)); + wpabuf_put_buf(msg, wbuf); + wpabuf_free(wbuf); + return 0; +} + + int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) { struct wpabuf *cred; @@ -1237,7 +1628,7 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) !wps->wps->registrar->disable_auto_conf) { u8 r[16]; /* Generate a random passphrase */ - if (os_get_random(r, sizeof(r)) < 0) + if (random_get_bytes(r, sizeof(r)) < 0) return -1; os_free(wps->new_psk); wps->new_psk = base64_encode(r, sizeof(r), &wps->new_psk_len); @@ -1269,7 +1660,7 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) wps->new_psk = os_malloc(wps->new_psk_len); if (wps->new_psk == NULL) return -1; - if (os_get_random(wps->new_psk, wps->new_psk_len) < 0) { + if (random_get_bytes(wps->new_psk, wps->new_psk_len) < 0) { os_free(wps->new_psk); wps->new_psk = NULL; return -1; @@ -1283,6 +1674,33 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) } use_provided: +#ifdef CONFIG_WPS_TESTING + if (wps_testing_dummy_cred) + cred = wpabuf_alloc(200); + else + cred = NULL; + if (cred) { + struct wps_credential dummy; + wpa_printf(MSG_DEBUG, "WPS: Add dummy credential"); + os_memset(&dummy, 0, sizeof(dummy)); + os_memcpy(dummy.ssid, "dummy", 5); + dummy.ssid_len = 5; + dummy.auth_type = WPS_AUTH_WPA2PSK; + dummy.encr_type = WPS_ENCR_AES; + os_memcpy(dummy.key, "dummy psk", 9); + dummy.key_len = 9; + os_memcpy(dummy.mac_addr, wps->mac_addr_e, ETH_ALEN); + wps_build_credential(cred, &dummy); + wpa_hexdump_buf(MSG_DEBUG, "WPS: Dummy Credential", cred); + + wpabuf_put_be16(msg, ATTR_CRED); + wpabuf_put_be16(msg, wpabuf_len(cred)); + wpabuf_put_buf(msg, cred); + + wpabuf_free(cred); + } +#endif /* CONFIG_WPS_TESTING */ + cred = wpabuf_alloc(200); if (cred == NULL) return -1; @@ -1318,11 +1736,40 @@ static int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *msg) } +static struct wpabuf * wps_build_ap_cred(struct wps_data *wps) +{ + struct wpabuf *msg, *plain; + + msg = wpabuf_alloc(1000); + if (msg == NULL) + return NULL; + + plain = wpabuf_alloc(200); + if (plain == NULL) { + wpabuf_free(msg); + return NULL; + } + + if (wps_build_ap_settings(wps, plain)) { + wpabuf_free(plain); + wpabuf_free(msg); + return NULL; + } + + wpabuf_put_be16(msg, ATTR_CRED); + wpabuf_put_be16(msg, wpabuf_len(plain)); + wpabuf_put_buf(msg, plain); + wpabuf_free(plain); + + return msg; +} + + static struct wpabuf * wps_build_m2(struct wps_data *wps) { struct wpabuf *msg; - if (os_get_random(wps->nonce_r, WPS_NONCE_LEN) < 0) + if (random_get_bytes(wps->nonce_r, WPS_NONCE_LEN) < 0) return NULL; wpa_hexdump(MSG_DEBUG, "WPS: Registrar Nonce", wps->nonce_r, WPS_NONCE_LEN); @@ -1350,6 +1797,7 @@ static struct wpabuf * wps_build_m2(struct wps_data *wps) wps_build_config_error(msg, WPS_CFG_NO_ERROR) || wps_build_dev_password_id(msg, wps->dev_pw_id) || wps_build_os_version(&wps->wps->dev, msg) || + wps_build_wfa_ext(msg, 0, NULL, 0) || wps_build_authenticator(wps, msg)) { wpabuf_free(msg); return NULL; @@ -1388,7 +1836,8 @@ static struct wpabuf * wps_build_m2d(struct wps_data *wps) wps_build_rf_bands(&wps->wps->dev, msg) || wps_build_assoc_state(wps, msg) || wps_build_config_error(msg, err) || - wps_build_os_version(&wps->wps->dev, msg)) { + wps_build_os_version(&wps->wps->dev, msg) || + wps_build_wfa_ext(msg, 0, NULL, 0)) { wpabuf_free(msg); return NULL; } @@ -1423,6 +1872,7 @@ static struct wpabuf * wps_build_m4(struct wps_data *wps) wps_build_r_snonce1(wps, plain) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain) || + wps_build_wfa_ext(msg, 0, NULL, 0) || wps_build_authenticator(wps, msg)) { wpabuf_free(plain); wpabuf_free(msg); @@ -1457,6 +1907,7 @@ static struct wpabuf * wps_build_m6(struct wps_data *wps) wps_build_r_snonce2(wps, plain) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain) || + wps_build_wfa_ext(msg, 0, NULL, 0) || wps_build_authenticator(wps, msg)) { wpabuf_free(plain); wpabuf_free(msg); @@ -1493,6 +1944,7 @@ static struct wpabuf * wps_build_m8(struct wps_data *wps) (!wps->wps->ap && !wps->er && wps_build_ap_settings(wps, plain)) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain) || + wps_build_wfa_ext(msg, 0, NULL, 0) || wps_build_authenticator(wps, msg)) { wpabuf_free(plain); wpabuf_free(msg); @@ -1505,51 +1957,6 @@ static struct wpabuf * wps_build_m8(struct wps_data *wps) } -static struct wpabuf * wps_build_wsc_ack(struct wps_data *wps) -{ - struct wpabuf *msg; - - wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_ACK"); - - msg = wpabuf_alloc(1000); - if (msg == NULL) - return NULL; - - if (wps_build_version(msg) || - wps_build_msg_type(msg, WPS_WSC_ACK) || - wps_build_enrollee_nonce(wps, msg) || - wps_build_registrar_nonce(wps, msg)) { - wpabuf_free(msg); - return NULL; - } - - return msg; -} - - -static struct wpabuf * wps_build_wsc_nack(struct wps_data *wps) -{ - struct wpabuf *msg; - - wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_NACK"); - - msg = wpabuf_alloc(1000); - if (msg == NULL) - return NULL; - - if (wps_build_version(msg) || - wps_build_msg_type(msg, WPS_WSC_NACK) || - wps_build_enrollee_nonce(wps, msg) || - wps_build_registrar_nonce(wps, msg) || - wps_build_config_error(msg, wps->config_error)) { - wpabuf_free(msg); - return NULL; - } - - return msg; -} - - struct wpabuf * wps_registrar_get_msg(struct wps_data *wps, enum wsc_op_code *op_code) { @@ -1814,6 +2221,13 @@ static int wps_process_e_snonce2(struct wps_data *wps, const u8 *e_snonce2) wps->wps_pin_revealed = 0; wps_registrar_unlock_pin(wps->wps->registrar, wps->uuid_e); + /* + * In case wildcard PIN is used and WPS handshake succeeds in the first + * attempt, wps_registrar_unlock_pin() would not free the PIN, so make + * sure the PIN gets invalidated here. + */ + wps_registrar_invalidate_pin(wps->wps->registrar, wps->uuid_e); + return 0; } @@ -1842,22 +2256,6 @@ static int wps_process_pubkey(struct wps_data *wps, const u8 *pk, return -1; } -#ifdef CONFIG_WPS_OOB - if (wps->wps->oob_conf.pubkey_hash != NULL) { - const u8 *addr[1]; - u8 hash[WPS_HASH_LEN]; - - addr[0] = pk; - sha256_vector(1, addr, &pk_len, hash); - if (os_memcmp(hash, - wpabuf_head(wps->wps->oob_conf.pubkey_hash), - WPS_OOB_PUBKEY_HASH_LEN) != 0) { - wpa_printf(MSG_ERROR, "WPS: Public Key hash error"); - return -1; - } - } -#endif /* CONFIG_WPS_OOB */ - wpabuf_free(wps->dh_pubkey_e); wps->dh_pubkey_e = wpabuf_alloc_copy(pk, pk_len); if (wps->dh_pubkey_e == NULL) @@ -2047,6 +2445,45 @@ static int wps_process_config_error(struct wps_data *wps, const u8 *err) } +static int wps_registrar_p2p_dev_addr_match(struct wps_data *wps) +{ +#ifdef CONFIG_P2P + struct wps_registrar *reg = wps->wps->registrar; + + if (is_zero_ether_addr(reg->p2p_dev_addr)) + return 1; /* no filtering in use */ + + if (os_memcmp(reg->p2p_dev_addr, wps->p2p_dev_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "WPS: No match on P2P Device Address " + "filtering for PBC: expected " MACSTR " was " + MACSTR " - indicate PBC session overlap", + MAC2STR(reg->p2p_dev_addr), + MAC2STR(wps->p2p_dev_addr)); + return 0; + } +#endif /* CONFIG_P2P */ + return 1; +} + + +static int wps_registrar_skip_overlap(struct wps_data *wps) +{ +#ifdef CONFIG_P2P + struct wps_registrar *reg = wps->wps->registrar; + + if (is_zero_ether_addr(reg->p2p_dev_addr)) + return 0; /* no specific Enrollee selected */ + + if (os_memcmp(reg->p2p_dev_addr, wps->p2p_dev_addr, ETH_ALEN) == 0) { + wpa_printf(MSG_DEBUG, "WPS: Skip PBC overlap due to selected " + "Enrollee match"); + return 1; + } +#endif /* CONFIG_P2P */ + return 0; +} + + static enum wps_process_res wps_process_m1(struct wps_data *wps, struct wps_parse_attr *attr) { @@ -2088,25 +2525,46 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps, return WPS_CONTINUE; } -#ifdef CONFIG_WPS_OOB - if (wps->dev_pw_id >= 0x10 && - wps->dev_pw_id != wps->wps->oob_dev_pw_id) { - wpa_printf(MSG_DEBUG, "WPS: OOB Device Password ID " - "%d mismatch", wps->dev_pw_id); - wps->state = SEND_M2D; - return WPS_CONTINUE; +#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_OOB */ +#endif /* CONFIG_WPS_NFC */ if (wps->dev_pw_id == DEV_PW_PUSHBUTTON) { - if (wps->wps->registrar->force_pbc_overlap || - wps_registrar_pbc_overlap(wps->wps->registrar, - wps->mac_addr_e, wps->uuid_e)) { + if ((wps->wps->registrar->force_pbc_overlap || + wps_registrar_pbc_overlap(wps->wps->registrar, + wps->mac_addr_e, wps->uuid_e) || + !wps_registrar_p2p_dev_addr_match(wps)) && + !wps_registrar_skip_overlap(wps)) { wpa_printf(MSG_DEBUG, "WPS: PBC overlap - deny PBC " "negotiation"); wps->state = SEND_M2D; wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED; wps_pbc_overlap_event(wps->wps); + wps_fail_event(wps->wps, WPS_M1, + WPS_CFG_MULTIPLE_PBC_DETECTED, + WPS_EI_NO_ERROR); wps->wps->registrar->force_pbc_overlap = 1; return WPS_CONTINUE; } @@ -2150,7 +2608,8 @@ static enum wps_process_res wps_process_m3(struct wps_data *wps, return WPS_CONTINUE; } - if (wps->pbc && wps->wps->registrar->force_pbc_overlap) { + if (wps->pbc && wps->wps->registrar->force_pbc_overlap && + !wps_registrar_skip_overlap(wps)) { wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC " "session overlap"); wps->state = SEND_WSC_NACK; @@ -2187,7 +2646,8 @@ static enum wps_process_res wps_process_m5(struct wps_data *wps, return WPS_CONTINUE; } - if (wps->pbc && wps->wps->registrar->force_pbc_overlap) { + if (wps->pbc && wps->wps->registrar->force_pbc_overlap && + !wps_registrar_skip_overlap(wps)) { wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC " "session overlap"); wps->state = SEND_WSC_NACK; @@ -2210,6 +2670,12 @@ static enum wps_process_res wps_process_m5(struct wps_data *wps, return WPS_CONTINUE; } + if (wps_validate_m5_encr(decrypted, attr->version2 != NULL) < 0) { + wpabuf_free(decrypted); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings " "attribute"); if (wps_parse_msg(decrypted, &eattr) < 0 || @@ -2264,6 +2730,8 @@ static void wps_cred_update(struct wps_credential *dst, static int wps_process_ap_settings_r(struct wps_data *wps, struct wps_parse_attr *attr) { + struct wpabuf *msg; + if (wps->wps->ap || wps->er) return 0; @@ -2283,12 +2751,31 @@ static int wps_process_ap_settings_r(struct wps_data *wps, * Use the AP PIN only to receive the current AP settings, not * to reconfigure the AP. */ + + /* + * Clear selected registrar here since we do not get to + * WSC_Done in this protocol run. + */ + wps_registrar_pin_completed(wps->wps->registrar); + + msg = wps_build_ap_cred(wps); + if (msg == NULL) + return -1; + wps->cred.cred_attr = wpabuf_head(msg); + wps->cred.cred_attr_len = wpabuf_len(msg); + if (wps->ap_settings_cb) { wps->ap_settings_cb(wps->ap_settings_cb_ctx, &wps->cred); + wpabuf_free(msg); return 1; } wps_sta_cred_cb(wps); + + wps->cred.cred_attr = NULL; + wps->cred.cred_attr_len = 0; + wpabuf_free(msg); + return 1; } } @@ -2310,7 +2797,8 @@ static enum wps_process_res wps_process_m7(struct wps_data *wps, return WPS_CONTINUE; } - if (wps->pbc && wps->wps->registrar->force_pbc_overlap) { + if (wps->pbc && wps->wps->registrar->force_pbc_overlap && + !wps_registrar_skip_overlap(wps)) { wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC " "session overlap"); wps->state = SEND_WSC_NACK; @@ -2333,6 +2821,13 @@ static enum wps_process_res wps_process_m7(struct wps_data *wps, return WPS_CONTINUE; } + if (wps_validate_m7_encr(decrypted, wps->wps->ap || wps->er, + attr->version2 != NULL) < 0) { + wpabuf_free(decrypted); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings " "attribute"); if (wps_parse_msg(decrypted, &eattr) < 0 || @@ -2362,27 +2857,24 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps, if (wps_parse_msg(msg, &attr) < 0) return WPS_FAILURE; - if (!wps_version_supported(attr.version)) { - wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x", - attr.version ? *attr.version : 0); - return WPS_FAILURE; - } - if (attr.msg_type == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute"); - return WPS_FAILURE; + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; } if (*attr.msg_type != WPS_M1 && (attr.registrar_nonce == NULL || os_memcmp(wps->nonce_r, attr.registrar_nonce, - WPS_NONCE_LEN != 0))) { + WPS_NONCE_LEN) != 0)) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); return WPS_FAILURE; } switch (*attr.msg_type) { case WPS_M1: + if (wps_validate_m1(msg) < 0) + return WPS_FAILURE; #ifdef CONFIG_WPS_UPNP if (wps->wps->wps_upnp && attr.mac_addr) { /* Remove old pending messages when starting new run */ @@ -2397,19 +2889,28 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps, ret = wps_process_m1(wps, &attr); break; case WPS_M3: + if (wps_validate_m3(msg) < 0) + return WPS_FAILURE; ret = wps_process_m3(wps, msg, &attr); if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) - wps_fail_event(wps->wps, WPS_M3); + wps_fail_event(wps->wps, WPS_M3, wps->config_error, + wps->error_indication); break; case WPS_M5: + if (wps_validate_m5(msg) < 0) + return WPS_FAILURE; ret = wps_process_m5(wps, msg, &attr); if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) - wps_fail_event(wps->wps, WPS_M5); + wps_fail_event(wps->wps, WPS_M5, wps->config_error, + wps->error_indication); break; case WPS_M7: + if (wps_validate_m7(msg) < 0) + return WPS_FAILURE; ret = wps_process_m7(wps, msg, &attr); if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) - wps_fail_event(wps->wps, WPS_M7); + wps_fail_event(wps->wps, WPS_M7, wps->config_error, + wps->error_indication); break; default: wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d", @@ -2438,12 +2939,6 @@ static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps, if (wps_parse_msg(msg, &attr) < 0) return WPS_FAILURE; - if (!wps_version_supported(attr.version)) { - wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x", - attr.version ? *attr.version : 0); - return WPS_FAILURE; - } - if (attr.msg_type == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute"); return WPS_FAILURE; @@ -2467,14 +2962,14 @@ static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps, #endif /* CONFIG_WPS_UPNP */ if (attr.registrar_nonce == NULL || - os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0)) + os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); return WPS_FAILURE; } if (attr.enrollee_nonce == NULL || - os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); return WPS_FAILURE; } @@ -2506,6 +3001,7 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps, { struct wps_parse_attr attr; int old_state; + u16 config_error; wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK"); @@ -2515,12 +3011,6 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps, if (wps_parse_msg(msg, &attr) < 0) return WPS_FAILURE; - if (!wps_version_supported(attr.version)) { - wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x", - attr.version ? *attr.version : 0); - return WPS_FAILURE; - } - if (attr.msg_type == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute"); return WPS_FAILURE; @@ -2541,14 +3031,14 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps, #endif /* CONFIG_WPS_UPNP */ if (attr.registrar_nonce == NULL || - os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0)) + os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); return WPS_FAILURE; } if (attr.enrollee_nonce == NULL || - os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); return WPS_FAILURE; } @@ -2559,21 +3049,26 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps, return WPS_FAILURE; } + config_error = WPA_GET_BE16(attr.config_error); wpa_printf(MSG_DEBUG, "WPS: Enrollee terminated negotiation with " - "Configuration Error %d", WPA_GET_BE16(attr.config_error)); + "Configuration Error %d", config_error); switch (old_state) { case RECV_M3: - wps_fail_event(wps->wps, WPS_M2); + wps_fail_event(wps->wps, WPS_M2, config_error, + wps->error_indication); break; case RECV_M5: - wps_fail_event(wps->wps, WPS_M4); + wps_fail_event(wps->wps, WPS_M4, config_error, + wps->error_indication); break; case RECV_M7: - wps_fail_event(wps->wps, WPS_M6); + wps_fail_event(wps->wps, WPS_M6, config_error, + wps->error_indication); break; case RECV_DONE: - wps_fail_event(wps->wps, WPS_M8); + wps_fail_event(wps->wps, WPS_M8, config_error, + wps->error_indication); break; default: break; @@ -2600,12 +3095,6 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps, if (wps_parse_msg(msg, &attr) < 0) return WPS_FAILURE; - if (!wps_version_supported(attr.version)) { - wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x", - attr.version ? *attr.version : 0); - return WPS_FAILURE; - } - if (attr.msg_type == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute"); return WPS_FAILURE; @@ -2628,14 +3117,14 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps, #endif /* CONFIG_WPS_UPNP */ if (attr.registrar_nonce == NULL || - os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0)) + os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); return WPS_FAILURE; } if (attr.enrollee_nonce == NULL || - os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); return WPS_FAILURE; } @@ -2683,15 +3172,22 @@ 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, - wps->mac_addr_e, wps->uuid_e); + wps->uuid_e, + wps->p2p_dev_addr); wps_registrar_pbc_completed(wps->wps->registrar); + os_get_time(&wps->wps->registrar->pbc_ignore_start); + os_memcpy(wps->wps->registrar->pbc_ignore_uuid, wps->uuid_e, + WPS_UUID_LEN); } else { wps_registrar_pin_completed(wps->wps->registrar); } + /* TODO: maintain AuthorizedMACs somewhere separately for each ER and + * merge them into APs own list.. */ wps_success_event(wps->wps); @@ -2747,14 +3243,22 @@ enum wps_process_res wps_registrar_process_msg(struct wps_data *wps, case WSC_MSG: return wps_process_wsc_msg(wps, msg); case WSC_ACK: + if (wps_validate_wsc_ack(msg) < 0) + return WPS_FAILURE; return wps_process_wsc_ack(wps, msg); case WSC_NACK: + if (wps_validate_wsc_nack(msg) < 0) + return WPS_FAILURE; return wps_process_wsc_nack(wps, msg); case WSC_Done: + if (wps_validate_wsc_done(msg) < 0) + return WPS_FAILURE; ret = wps_process_wsc_done(wps, msg); if (ret == WPS_FAILURE) { wps->state = SEND_WSC_NACK; - wps_fail_event(wps->wps, WPS_WSC_DONE); + wps_fail_event(wps->wps, WPS_WSC_DONE, + wps->config_error, + wps->error_indication); } return ret; default: @@ -2787,6 +3291,7 @@ static void wps_registrar_set_selected_timeout(void *eloop_ctx, static void wps_registrar_sel_reg_add(struct wps_registrar *reg, struct subscription *s) { + int i, j; wpa_printf(MSG_DEBUG, "WPS: External Registrar selected (dev_pw_id=%d " "config_methods=0x%x)", s->dev_password_id, s->config_methods); @@ -2796,6 +3301,22 @@ static void wps_registrar_sel_reg_add(struct wps_registrar *reg, if (reg->sel_reg_config_methods_override == -1) reg->sel_reg_config_methods_override = 0; reg->sel_reg_config_methods_override |= s->config_methods; + for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++) + if (is_zero_ether_addr(reg->authorized_macs_union[i])) + break; + for (j = 0; i < WPS_MAX_AUTHORIZED_MACS && j < WPS_MAX_AUTHORIZED_MACS; + j++) { + if (is_zero_ether_addr(s->authorized_macs[j])) + break; + wpa_printf(MSG_DEBUG, "WPS: Add authorized MAC into union: " + MACSTR, MAC2STR(s->authorized_macs[j])); + os_memcpy(reg->authorized_macs_union[i], + s->authorized_macs[j], ETH_ALEN); + i++; + } + wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs union", + (u8 *) reg->authorized_macs_union, + sizeof(reg->authorized_macs_union)); } #endif /* CONFIG_WPS_UPNP */ @@ -2841,17 +3362,27 @@ void wps_registrar_selected_registrar_changed(struct wps_registrar *reg) reg->sel_reg_union = reg->selected_registrar; reg->sel_reg_dev_password_id_override = -1; reg->sel_reg_config_methods_override = -1; + os_memcpy(reg->authorized_macs_union, reg->authorized_macs, + WPS_MAX_AUTHORIZED_MACS * ETH_ALEN); + wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs union (start with own)", + (u8 *) reg->authorized_macs_union, + sizeof(reg->authorized_macs_union)); if (reg->selected_registrar) { - reg->sel_reg_config_methods_override = - reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON; + u16 methods; + + methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON; +#ifdef CONFIG_WPS2 + methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON | + WPS_CONFIG_PHY_PUSHBUTTON); +#endif /* CONFIG_WPS2 */ if (reg->pbc) { reg->sel_reg_dev_password_id_override = DEV_PW_PUSHBUTTON; - reg->sel_reg_config_methods_override |= - WPS_CONFIG_PUSHBUTTON; + wps_set_pushbutton(&methods, reg->wps->config_methods); } wpa_printf(MSG_DEBUG, "WPS: Internal Registrar selected " "(pbc=%d)", reg->pbc); + reg->sel_reg_config_methods_override = methods; } else wpa_printf(MSG_DEBUG, "WPS: Internal Registrar not selected"); @@ -2898,3 +3429,124 @@ int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr, return len; } + + +int wps_registrar_config_ap(struct wps_registrar *reg, + struct wps_credential *cred) +{ +#ifdef CONFIG_WPS2 + printf("encr_type=0x%x\n", cred->encr_type); + if (!(cred->encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP | + WPS_ENCR_AES))) { + if (cred->encr_type & WPS_ENCR_WEP) { + wpa_printf(MSG_INFO, "WPS: Reject new AP settings " + "due to WEP configuration"); + return -1; + } + + wpa_printf(MSG_INFO, "WPS: Reject new AP settings due to " + "invalid encr_type 0x%x", cred->encr_type); + return -1; + } + + if ((cred->encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) == + WPS_ENCR_TKIP) { + wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> " + "TKIP+AES"); + cred->encr_type |= WPS_ENCR_AES; + } + + if ((cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) == + WPS_AUTH_WPAPSK) { + wpa_printf(MSG_DEBUG, "WPS: Upgrade auth_type WPAPSK -> " + "WPAPSK+WPA2PSK"); + cred->auth_type |= WPS_AUTH_WPA2PSK; + } +#endif /* CONFIG_WPS2 */ + + if (reg->wps->cred_cb) + return reg->wps->cred_cb(reg->wps->cb_ctx, cred); + + return -1; +} + + +#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 deleted file mode 100644 index 1a911e1d4ceb2..0000000000000 --- a/src/wps/wps_ufd.c +++ /dev/null @@ -1,235 +0,0 @@ -/* - * UFD routines for Wi-Fi Protected Setup - * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" -#include "common.h" -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/wait.h> -#include <fcntl.h> -#include <dirent.h> - -#include "wps/wps.h" -#include "wps/wps_i.h" - -#ifdef CONFIG_NATIVE_WINDOWS -#define UFD_DIR1 "%s\\SMRTNTKY" -#define UFD_DIR2 UFD_DIR1 "\\WFAWSC" -#define UFD_FILE UFD_DIR2 "\\%s" -#else /* CONFIG_NATIVE_WINDOWS */ -#define UFD_DIR1 "%s/SMRTNTKY" -#define UFD_DIR2 UFD_DIR1 "/WFAWSC" -#define UFD_FILE UFD_DIR2 "/%s" -#endif /* CONFIG_NATIVE_WINDOWS */ - - -struct wps_ufd_data { - int ufd_fd; -}; - - -static int dev_pwd_e_file_filter(const struct dirent *entry) -{ - unsigned int prefix; - char ext[5]; - - if (sscanf(entry->d_name, "%8x.%4s", &prefix, ext) != 2) - return 0; - if (prefix == 0) - return 0; - if (os_strcasecmp(ext, "WFA") != 0) - return 0; - - return 1; -} - - -static int wps_get_dev_pwd_e_file_name(char *path, char *file_name) -{ - struct dirent **namelist; - int i, file_num; - - file_num = scandir(path, &namelist, &dev_pwd_e_file_filter, - alphasort); - if (file_num < 0) { - wpa_printf(MSG_ERROR, "WPS: OOB file not found: %d (%s)", - errno, strerror(errno)); - return -1; - } - if (file_num == 0) { - wpa_printf(MSG_ERROR, "WPS: OOB file not found"); - os_free(namelist); - return -1; - } - os_strlcpy(file_name, namelist[0]->d_name, 13); - for (i = 0; i < file_num; i++) - os_free(namelist[i]); - os_free(namelist); - return 0; -} - - -static int get_file_name(struct wps_context *wps, int registrar, - const char *path, char *file_name) -{ - switch (wps->oob_conf.oob_method) { - case OOB_METHOD_CRED: - os_snprintf(file_name, 13, "00000000.WSC"); - break; - case OOB_METHOD_DEV_PWD_E: - if (registrar) { - char temp[128]; - os_snprintf(temp, sizeof(temp), UFD_DIR2, path); - if (wps_get_dev_pwd_e_file_name(temp, file_name) < 0) - return -1; - } else { - u8 *mac_addr = wps->dev.mac_addr; - - os_snprintf(file_name, 13, "%02X%02X%02X%02X.WFA", - mac_addr[2], mac_addr[3], mac_addr[4], - mac_addr[5]); - } - break; - case OOB_METHOD_DEV_PWD_R: - os_snprintf(file_name, 13, "00000000.WFA"); - break; - default: - wpa_printf(MSG_ERROR, "WPS: Invalid USBA OOB method"); - return -1; - } - return 0; -} - - -static int ufd_mkdir(const char *path) -{ - if (mkdir(path, S_IRWXU) < 0 && errno != EEXIST) { - wpa_printf(MSG_ERROR, "WPS (UFD): Failed to create directory " - "'%s': %d (%s)", path, errno, strerror(errno)); - return -1; - } - return 0; -} - - -static void * init_ufd(struct wps_context *wps, - struct oob_device_data *oob_dev, int registrar) -{ - int write_f; - char temp[128]; - char *path = oob_dev->device_path; - char filename[13]; - struct wps_ufd_data *data; - int ufd_fd; - - if (path == NULL) - return NULL; - - write_f = wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E ? - !registrar : registrar; - - if (get_file_name(wps, registrar, path, filename) < 0) { - wpa_printf(MSG_ERROR, "WPS (UFD): Failed to get file name"); - return NULL; - } - - if (write_f) { - os_snprintf(temp, sizeof(temp), UFD_DIR1, path); - if (ufd_mkdir(temp)) - return NULL; - os_snprintf(temp, sizeof(temp), UFD_DIR2, path); - if (ufd_mkdir(temp)) - return NULL; - } - - os_snprintf(temp, sizeof(temp), UFD_FILE, path, filename); - if (write_f) - ufd_fd = open(temp, O_WRONLY | O_CREAT | O_TRUNC, - S_IRUSR | S_IWUSR); - else - ufd_fd = open(temp, O_RDONLY); - if (ufd_fd < 0) { - wpa_printf(MSG_ERROR, "WPS (UFD): Failed to open %s: %s", - temp, strerror(errno)); - return NULL; - } - - data = os_zalloc(sizeof(*data)); - if (data == NULL) - return NULL; - data->ufd_fd = ufd_fd; - return data; -} - - -static struct wpabuf * read_ufd(void *priv) -{ - struct wps_ufd_data *data = priv; - struct wpabuf *buf; - struct stat s; - size_t file_size; - - if (fstat(data->ufd_fd, &s) < 0) { - wpa_printf(MSG_ERROR, "WPS (UFD): Failed to get file size"); - return NULL; - } - - file_size = s.st_size; - buf = wpabuf_alloc(file_size); - if (buf == NULL) { - wpa_printf(MSG_ERROR, "WPS (UFD): Failed to alloc read " - "buffer"); - return NULL; - } - - if (read(data->ufd_fd, wpabuf_mhead(buf), file_size) != - (int) file_size) { - wpabuf_free(buf); - wpa_printf(MSG_ERROR, "WPS (UFD): Failed to read"); - return NULL; - } - wpabuf_put(buf, file_size); - return buf; -} - - -static int write_ufd(void *priv, struct wpabuf *buf) -{ - struct wps_ufd_data *data = priv; - - if (write(data->ufd_fd, wpabuf_mhead(buf), wpabuf_len(buf)) != - (int) wpabuf_len(buf)) { - wpa_printf(MSG_ERROR, "WPS (UFD): Failed to write"); - return -1; - } - return 0; -} - - -static void deinit_ufd(void *priv) -{ - struct wps_ufd_data *data = priv; - close(data->ufd_fd); - os_free(data); -} - - -struct oob_device_data oob_ufd_device_data = { - .device_name = NULL, - .device_path = NULL, - .init_func = init_ufd, - .read_func = read_ufd, - .write_func = write_ufd, - .deinit_func = deinit_ufd, -}; diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c index f99b8592ca36b..09a46a215a9f7 100644 --- a/src/wps/wps_upnp.c +++ b/src/wps/wps_upnp.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2003 Intel Corporation * Copyright (c) 2006-2007 Sony Corporation * Copyright (c) 2008-2009 Atheros Communications - * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi> * * See below for more details on licensing and code history. */ @@ -47,7 +47,7 @@ * -- Needs renaming with module prefix to avoid polluting the debugger * namespace and causing possible collisions with other static fncs * and structure declarations when using the debugger. - * -- The http error code generation is pretty bogus, hopefully noone cares. + * -- The http error code generation is pretty bogus, hopefully no one cares. * * Author: Ted Merrill, Atheros Communications, based upon earlier work * as explained above and below. @@ -172,7 +172,7 @@ #include "includes.h" -#include <assert.h> +#include <time.h> #include <net/if.h> #include <netdb.h> #include <sys/ioctl.h> @@ -209,6 +209,12 @@ #define MAX_SUBSCRIPTIONS 4 /* how many subscribing clients we handle */ #define MAX_ADDR_PER_SUBSCRIPTION 8 +/* Maximum number of Probe Request events per second */ +#define MAX_EVENTS_PER_SEC 5 + + +static struct upnp_wps_device_sm *shared_upnp_device = NULL; + /* Write the current date/time per RFC */ void format_date(struct wpabuf *buf) @@ -270,7 +276,7 @@ static void uuid_make(u8 uuid[UUID_LEN]) /* subscr_addr_delete -- delete single unlinked subscriber address * (be sure to unlink first if need be) */ -static void subscr_addr_delete(struct subscr_addr *a) +void subscr_addr_delete(struct subscr_addr *a) { /* * Note: do NOT free domain_and_port or path because they point to @@ -293,50 +299,46 @@ static void subscr_addr_free_all(struct subscription *s) /* subscr_addr_add_url -- add address(es) for one url to subscription */ -static void subscr_addr_add_url(struct subscription *s, const char *url) +static void subscr_addr_add_url(struct subscription *s, const char *url, + size_t url_len) { int alloc_len; char *scratch_mem = NULL; char *mem; - char *domain_and_port; + char *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; - struct subscr_addr *a = NULL; + size_t host_len, path_len; /* url MUST begin with http: */ - if (os_strncasecmp(url, "http://", 7)) + if (url_len < 7 || os_strncasecmp(url, "http://", 7)) goto fail; url += 7; + url_len -= 7; - /* allocate memory for the extra stuff we need */ - alloc_len = (2 * (os_strlen(url) + 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; - strcpy(mem, url); - 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 = 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); } /* @@ -359,14 +361,24 @@ 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; + /* Limit no. of address to avoid denial of service attack */ if (dl_list_len(&s->addr_list) >= MAX_ADDR_PER_SUBSCRIPTION) { wpa_printf(MSG_INFO, "WPS UPnP: subscr_addr_add_url: " @@ -376,28 +388,26 @@ 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 + 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); dl_list_add(&s->addr_list, &a->list); - a = NULL; /* don't free it below */ } fail: if (result) freeaddrinfo(result); os_free(scratch_mem); - os_free(a); } @@ -407,7 +417,8 @@ fail: static void subscr_addr_list_create(struct subscription *s, const char *url_list) { - char *end; + const char *end; + wpa_printf(MSG_DEBUG, "WPS UPnP: Parsing URL list '%s'", url_list); for (;;) { while (*url_list == ' ' || *url_list == '\t') url_list++; @@ -417,9 +428,8 @@ static void subscr_addr_list_create(struct subscription *s, end = os_strchr(url_list, '>'); if (end == NULL) break; - *end++ = 0; - subscr_addr_add_url(s, url_list); - url_list = end; + subscr_addr_add_url(s, url_list, end - url_list); + url_list = end + 1; } } @@ -472,12 +482,38 @@ static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm) "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" "<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n"; const char *format_tail = "</e:propertyset>\n"; + struct os_time now; if (dl_list_empty(&sm->subscriptions)) { /* optimize */ return; } + if (os_get_time(&now) == 0) { + if (now.sec != sm->last_event_sec) { + sm->last_event_sec = now.sec; + sm->num_events_in_sec = 1; + } else { + sm->num_events_in_sec++; + /* + * In theory, this should apply to all WLANEvent + * notifications, but EAP messages are of much higher + * priority and Probe Request notifications should not + * be allowed to drop EAP messages, so only throttle + * Probe Request notifications. + */ + if (sm->num_events_in_sec > MAX_EVENTS_PER_SEC && + sm->wlanevent_type == + UPNP_WPS_WLANEVENT_TYPE_PROBE) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Throttle " + "event notifications (%u seen " + "during one second)", + sm->num_events_in_sec); + return; + } + } + } + /* Determine buffer size needed first */ buf_size += os_strlen(format_head); buf_size += 50 + 2 * os_strlen("WLANEvent"); @@ -497,12 +533,8 @@ static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm) dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription, list) { - if (event_add(s, buf)) { - wpa_printf(MSG_INFO, "WPS UPnP: Dropping " - "subscriber due to event backlog"); - dl_list_del(&s->list); - subscription_destroy(s); - } + event_add(s, buf, + sm->wlanevent_type == UPNP_WPS_WLANEVENT_TYPE_PROBE); } wpabuf_free(buf); @@ -520,10 +552,13 @@ static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm) */ void subscription_destroy(struct subscription *s) { + struct upnp_wps_device_interface *iface; wpa_printf(MSG_DEBUG, "WPS UPnP: Destroy subscription %p", s); subscr_addr_free_all(s); event_delete_all(s); - upnp_er_remove_notification(s); + dl_list_for_each(iface, &s->sm->interfaces, + struct upnp_wps_device_interface, list) + upnp_er_remove_notification(iface->wps->registrar, s); os_free(s); } @@ -578,6 +613,7 @@ static struct wpabuf * build_fake_wsc_ack(void) wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE); wpabuf_put_be16(msg, WPS_NONCE_LEN); wpabuf_put(msg, WPS_NONCE_LEN); + wps_build_wfa_ext(msg, 0, NULL, 0); return msg; } @@ -605,6 +641,7 @@ static int subscription_first_event(struct subscription *s) "<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n"; const char *tail = "</e:propertyset>\n"; char txt[10]; + int ret; if (s->sm->wlanevent == NULL) { /* @@ -636,7 +673,7 @@ static int subscription_first_event(struct subscription *s) } buf = wpabuf_alloc(500 + os_strlen(wlan_event)); if (buf == NULL) - return 1; + return -1; wpabuf_put_str(buf, head); wpabuf_put_property(buf, "STAStatus", "1"); @@ -646,9 +683,10 @@ static int subscription_first_event(struct subscription *s) wpabuf_put_property(buf, "WLANEvent", wlan_event); wpabuf_put_str(buf, tail); - if (event_add(s, buf)) { + ret = event_add(s, buf, 0); + if (ret) { wpabuf_free(buf); - return 1; + return ret; } wpabuf_free(buf); @@ -692,6 +730,13 @@ struct subscription * subscription_start(struct upnp_wps_device_sm *sm, s->timeout_time = expire; uuid_make(s->uuid); subscr_addr_list_create(s, callback_urls); + if (dl_list_empty(&s->addr_list)) { + wpa_printf(MSG_DEBUG, "WPS UPnP: No valid callback URLs in " + "'%s' - drop subscription", callback_urls); + subscription_destroy(s); + return NULL; + } + /* Add to end of list, since it has the highest expiration time */ dl_list_add_tail(&sm->subscriptions, &s->list); /* Queue up immediate event message (our last event) @@ -786,6 +831,7 @@ int upnp_wps_device_send_wlan_event(struct upnp_wps_device_sm *sm, os_free(sm->wlanevent); sm->wlanevent = val; + sm->wlanevent_type = ev_type; upnp_wps_device_send_event(sm); ret = 0; @@ -914,10 +960,13 @@ static void upnp_wps_free_msearchreply(struct dl_list *head) } -static void upnp_wps_free_subscriptions(struct dl_list *head) +static void upnp_wps_free_subscriptions(struct dl_list *head, + struct wps_registrar *reg) { struct subscription *s, *tmp; dl_list_for_each_safe(s, tmp, head, struct subscription, list) { + if (reg && s->reg != reg) + continue; dl_list_del(&s->list); subscription_destroy(s); } @@ -928,7 +977,7 @@ static void upnp_wps_free_subscriptions(struct dl_list *head) * upnp_wps_device_stop - Stop WPS UPnP operations on an interface * @sm: WPS UPnP state machine from upnp_wps_device_init() */ -void upnp_wps_device_stop(struct upnp_wps_device_sm *sm) +static void upnp_wps_device_stop(struct upnp_wps_device_sm *sm) { if (!sm || !sm->started) return; @@ -936,7 +985,7 @@ void upnp_wps_device_stop(struct upnp_wps_device_sm *sm) wpa_printf(MSG_DEBUG, "WPS UPnP: Stop device"); web_listener_stop(sm); upnp_wps_free_msearchreply(&sm->msearch_replies); - upnp_wps_free_subscriptions(&sm->subscriptions); + upnp_wps_free_subscriptions(&sm->subscriptions, NULL); advertisement_state_machine_stop(sm, 1); @@ -960,7 +1009,7 @@ void upnp_wps_device_stop(struct upnp_wps_device_sm *sm) * @net_if: Selected network interface name * Returns: 0 on success, -1 on failure */ -int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if) +static int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if) { if (!sm || !net_if) return -1; @@ -1015,24 +1064,59 @@ fail: } +static struct upnp_wps_device_interface * +upnp_wps_get_iface(struct upnp_wps_device_sm *sm, void *priv) +{ + struct upnp_wps_device_interface *iface; + dl_list_for_each(iface, &sm->interfaces, + struct upnp_wps_device_interface, list) { + if (iface->priv == priv) + return iface; + } + return NULL; +} + + /** * upnp_wps_device_deinit - Deinitialize WPS UPnP * @sm: WPS UPnP state machine from upnp_wps_device_init() + * @priv: External context data that was used in upnp_wps_device_init() call */ -void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm) +void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm, void *priv) { + struct upnp_wps_device_interface *iface; + if (!sm) return; - upnp_wps_device_stop(sm); - - if (sm->peer.wps) - wps_deinit(sm->peer.wps); - os_free(sm->root_dir); - os_free(sm->desc_url); - os_free(sm->ctx->ap_pin); - os_free(sm->ctx); - os_free(sm); + iface = upnp_wps_get_iface(sm, priv); + if (iface == NULL) { + wpa_printf(MSG_ERROR, "WPS UPnP: Could not find the interface " + "instance to deinit"); + return; + } + wpa_printf(MSG_DEBUG, "WPS UPnP: Deinit interface instance %p", iface); + if (dl_list_len(&sm->interfaces) == 1) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Deinitializing last instance " + "- free global device instance"); + upnp_wps_device_stop(sm); + } else + upnp_wps_free_subscriptions(&sm->subscriptions, + iface->wps->registrar); + dl_list_del(&iface->list); + + if (iface->peer.wps) + wps_deinit(iface->peer.wps); + os_free(iface->ctx->ap_pin); + os_free(iface->ctx); + os_free(iface); + + if (dl_list_empty(&sm->interfaces)) { + os_free(sm->root_dir); + os_free(sm->desc_url); + os_free(sm); + shared_upnp_device = NULL; + } } @@ -1041,25 +1125,59 @@ void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm) * @ctx: callback table; we must eventually free it * @wps: Pointer to longterm WPS context * @priv: External context data that will be used in callbacks + * @net_if: Selected network interface name * Returns: WPS UPnP state or %NULL on failure */ struct upnp_wps_device_sm * upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps, - void *priv) + void *priv, char *net_if) { struct upnp_wps_device_sm *sm; + struct upnp_wps_device_interface *iface; + int start = 0; - sm = os_zalloc(sizeof(*sm)); - if (!sm) { - wpa_printf(MSG_ERROR, "WPS UPnP: upnp_wps_device_init failed"); + iface = os_zalloc(sizeof(*iface)); + if (iface == NULL) { + os_free(ctx->ap_pin); + os_free(ctx); + return NULL; + } + wpa_printf(MSG_DEBUG, "WPS UPnP: Init interface instance %p", iface); + + iface->ctx = ctx; + iface->wps = wps; + iface->priv = priv; + + if (shared_upnp_device) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Share existing device " + "context"); + sm = shared_upnp_device; + } else { + wpa_printf(MSG_DEBUG, "WPS UPnP: Initialize device context"); + sm = os_zalloc(sizeof(*sm)); + if (!sm) { + wpa_printf(MSG_ERROR, "WPS UPnP: upnp_wps_device_init " + "failed"); + os_free(iface); + os_free(ctx->ap_pin); + os_free(ctx); + return NULL; + } + shared_upnp_device = sm; + + dl_list_init(&sm->msearch_replies); + dl_list_init(&sm->subscriptions); + dl_list_init(&sm->interfaces); + start = 1; + } + + dl_list_add(&sm->interfaces, &iface->list); + + if (start && upnp_wps_device_start(sm, net_if)) { + upnp_wps_device_deinit(sm, priv); return NULL; } - sm->ctx = ctx; - sm->wps = wps; - sm->priv = priv; - dl_list_init(&sm->msearch_replies); - dl_list_init(&sm->subscriptions); return sm; } @@ -1078,16 +1196,20 @@ int upnp_wps_subscribers(struct upnp_wps_device_sm *sm) int upnp_wps_set_ap_pin(struct upnp_wps_device_sm *sm, const char *ap_pin) { + struct upnp_wps_device_interface *iface; if (sm == NULL) return 0; - os_free(sm->ctx->ap_pin); - if (ap_pin) { - sm->ctx->ap_pin = os_strdup(ap_pin); - if (sm->ctx->ap_pin == NULL) - return -1; - } else - sm->ctx->ap_pin = NULL; + dl_list_for_each(iface, &sm->interfaces, + struct upnp_wps_device_interface, list) { + os_free(iface->ctx->ap_pin); + if (ap_pin) { + iface->ctx->ap_pin = os_strdup(ap_pin); + if (iface->ctx->ap_pin == NULL) + return -1; + } else + iface->ctx->ap_pin = NULL; + } return 0; } diff --git a/src/wps/wps_upnp.h b/src/wps/wps_upnp.h index 06bc31fe7ffdf..87b7ab14160b7 100644 --- a/src/wps/wps_upnp.h +++ b/src/wps/wps_upnp.h @@ -35,11 +35,8 @@ struct upnp_wps_device_ctx { struct upnp_wps_device_sm * upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps, - void *priv); -void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm); - -int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if); -void upnp_wps_device_stop(struct upnp_wps_device_sm *sm); + void *priv, char *net_if); +void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm, void *priv); int upnp_wps_device_send_wlan_event(struct upnp_wps_device_sm *sm, const u8 from_mac_addr[ETH_ALEN], diff --git a/src/wps/wps_upnp_ap.c b/src/wps/wps_upnp_ap.c index 93746dae1c566..54ed98f0833f9 100644 --- a/src/wps/wps_upnp_ap.c +++ b/src/wps/wps_upnp_ap.c @@ -2,14 +2,8 @@ * Wi-Fi Protected Setup - UPnP AP functionality * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -25,9 +19,10 @@ static void upnp_er_set_selected_timeout(void *eloop_ctx, void *timeout_ctx) { struct subscription *s = eloop_ctx; + struct wps_registrar *reg = timeout_ctx; wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar from ER timed out"); s->selected_registrar = 0; - wps_registrar_selected_registrar_changed(s->reg); + wps_registrar_selected_registrar_changed(reg); } @@ -39,18 +34,16 @@ int upnp_er_set_selected_registrar(struct wps_registrar *reg, wpa_hexdump_buf(MSG_MSGDUMP, "WPS: SetSelectedRegistrar attributes", msg); + if (wps_validate_upnp_set_selected_registrar(msg) < 0) + return -1; if (wps_parse_msg(msg, &attr) < 0) return -1; - if (!wps_version_supported(attr.version)) { - wpa_printf(MSG_DEBUG, "WPS: Unsupported SetSelectedRegistrar " - "version 0x%x", attr.version ? *attr.version : 0); - return -1; - } s->reg = reg; - eloop_cancel_timeout(upnp_er_set_selected_timeout, s, NULL); + eloop_cancel_timeout(upnp_er_set_selected_timeout, s, reg); + os_memset(s->authorized_macs, 0, sizeof(s->authorized_macs)); if (attr.selected_registrar == NULL || *attr.selected_registrar == 0) { wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar: Disable " "Selected Registrar"); @@ -61,8 +54,21 @@ int upnp_er_set_selected_registrar(struct wps_registrar *reg, WPA_GET_BE16(attr.dev_password_id) : DEV_PW_DEFAULT; s->config_methods = attr.sel_reg_config_methods ? WPA_GET_BE16(attr.sel_reg_config_methods) : -1; + if (attr.authorized_macs) { + int count = attr.authorized_macs_len / ETH_ALEN; + if (count > WPS_MAX_AUTHORIZED_MACS) + count = WPS_MAX_AUTHORIZED_MACS; + os_memcpy(s->authorized_macs, attr.authorized_macs, + count * ETH_ALEN); + } else if (!attr.version2) { +#ifdef CONFIG_WPS2 + wpa_printf(MSG_DEBUG, "WPS: Add broadcast " + "AuthorizedMACs for WPS 1.0 ER"); + os_memset(s->authorized_macs, 0xff, ETH_ALEN); +#endif /* CONFIG_WPS2 */ + } eloop_register_timeout(WPS_PBC_WALK_TIME, 0, - upnp_er_set_selected_timeout, s, NULL); + upnp_er_set_selected_timeout, s, reg); } wps_registrar_selected_registrar_changed(reg); @@ -71,10 +77,11 @@ int upnp_er_set_selected_registrar(struct wps_registrar *reg, } -void upnp_er_remove_notification(struct subscription *s) +void upnp_er_remove_notification(struct wps_registrar *reg, + struct subscription *s) { s->selected_registrar = 0; - eloop_cancel_timeout(upnp_er_set_selected_timeout, s, NULL); - if (s->reg) - wps_registrar_selected_registrar_changed(s->reg); + eloop_cancel_timeout(upnp_er_set_selected_timeout, s, reg); + if (reg) + wps_registrar_selected_registrar_changed(reg); } diff --git a/src/wps/wps_upnp_event.c b/src/wps/wps_upnp_event.c index ae5efdb0800b8..2c8ed4f13996b 100644 --- a/src/wps/wps_upnp_event.c +++ b/src/wps/wps_upnp_event.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2003 Intel Corporation * Copyright (c) 2006-2007 Sony Corporation * Copyright (c) 2008-2009 Atheros Communications - * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi> * * See wps_upnp.c for more details on licensing and code history. */ @@ -31,7 +31,7 @@ */ #define MAX_EVENTS_QUEUED 20 /* How far behind queued events */ -#define EVENT_TIMEOUT_SEC 30 /* Drop sending event after timeout */ +#define MAX_FAILURES 10 /* Drop subscription after this many failures */ /* How long to wait before sending event */ #define EVENT_DELAY_SECONDS 0 @@ -73,6 +73,7 @@ static void event_clean(struct wps_event_ *e) */ static void event_delete(struct wps_event_ *e) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Delete event %p", e); event_clean(e); wpabuf_free(e->data); os_free(e); @@ -86,8 +87,11 @@ static struct wps_event_ *event_dequeue(struct subscription *s) { struct wps_event_ *e; e = dl_list_first(&s->event_queue, struct wps_event_, list); - if (e) + if (e) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Dequeue event %p for " + "subscription %p", e, s); dl_list_del(&e->list); + } return e; } @@ -115,14 +119,22 @@ static void event_retry(struct wps_event_ *e, int do_next_address) struct subscription *s = e->s; struct upnp_wps_device_sm *sm = s->sm; + wpa_printf(MSG_DEBUG, "WPS UPnP: Retry event %p for subscription %p", + e, s); event_clean(e); /* will set: s->current_event = NULL; */ - if (do_next_address) + if (do_next_address) { e->retry++; + wpa_printf(MSG_DEBUG, "WPS UPnP: Try address %d", e->retry); + } if (e->retry >= dl_list_len(&s->addr_list)) { wpa_printf(MSG_DEBUG, "WPS UPnP: Giving up on sending event " "for %s", e->addr->domain_and_port); + event_delete(e); + s->last_event_failed = 1; + if (!dl_list_empty(&s->event_queue)) + event_send_all_later(s->sm); return; } dl_list_add(&s->event_queue, &e->list); @@ -158,17 +170,60 @@ static struct wpabuf * event_build_message(struct wps_event_ *e) } +static void event_addr_failure(struct wps_event_ *e) +{ + struct subscription *s = e->s; + + e->addr->num_failures++; + wpa_printf(MSG_DEBUG, "WPS UPnP: Failed to send event %p to %s " + "(num_failures=%u)", + e, e->addr->domain_and_port, e->addr->num_failures); + + if (e->addr->num_failures < MAX_FAILURES) { + /* Try other addresses, if available */ + event_retry(e, 1); + return; + } + + /* + * If other side doesn't like what we say, forget about them. + * (There is no way to tell other side that we are dropping them...). + */ + wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription %p " + "address %s due to errors", s, e->addr->domain_and_port); + dl_list_del(&e->addr->list); + subscr_addr_delete(e->addr); + e->addr = NULL; + + if (dl_list_empty(&s->addr_list)) { + /* if we've given up on all addresses */ + wpa_printf(MSG_DEBUG, "WPS UPnP: Removing subscription %p " + "with no addresses", s); + dl_list_del(&s->list); + subscription_destroy(s); + return; + } + + /* Try other addresses, if available */ + event_retry(e, 0); +} + + static void event_http_cb(void *ctx, struct http_client *c, enum http_client_event event) { struct wps_event_ *e = ctx; struct subscription *s = e->s; + wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP client callback: e=%p c=%p " + "event=%d", e, c, event); switch (event) { case HTTP_CLIENT_OK: wpa_printf(MSG_DEBUG, - "WPS UPnP: Got event reply OK from " - "%s", e->addr->domain_and_port); + "WPS UPnP: Got event %p reply OK from %s", + e, e->addr->domain_and_port); + e->addr->num_failures = 0; + s->last_event_failed = 0; event_delete(e); /* Schedule sending more if there is more to send */ @@ -176,24 +231,17 @@ static void event_http_cb(void *ctx, struct http_client *c, event_send_all_later(s->sm); break; case HTTP_CLIENT_FAILED: + wpa_printf(MSG_DEBUG, "WPS UPnP: Event send failure"); + event_addr_failure(e); + break; case HTTP_CLIENT_INVALID_REPLY: - wpa_printf(MSG_DEBUG, "WPS UPnP: Failed to send event to %s", - e->addr->domain_and_port); - - /* - * If other side doesn't like what we say, forget about them. - * (There is no way to tell other side that we are dropping - * them...). - * Alternately, we could just do event_delete(e) - */ - wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription due to " - "errors"); - dl_list_del(&s->list); - subscription_destroy(s); + wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid reply"); + event_addr_failure(e); break; case HTTP_CLIENT_TIMEOUT: wpa_printf(MSG_DEBUG, "WPS UPnP: Event send timeout"); - event_retry(e, 1); + event_addr_failure(e); + break; } } @@ -228,9 +276,12 @@ static int event_send_start(struct subscription *s) * Assume we are called ONLY with no current event and ONLY with * nonempty event queue and ONLY with at least one address to send to. */ - assert(!dl_list_empty(&s->addr_list)); - assert(s->current_event == NULL); - assert(!dl_list_empty(&s->event_queue)); + if (dl_list_empty(&s->addr_list)) + return -1; + if (s->current_event) + return -1; + if (dl_list_empty(&s->event_queue)) + return -1; s->current_event = e = event_dequeue(s); @@ -270,18 +321,10 @@ static void event_send_all_later_handler(void *eloop_data, void *user_ctx) sm->event_send_all_queued = 0; dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription, list) { - if (dl_list_empty(&s->addr_list)) { - /* if we've given up on all addresses */ - wpa_printf(MSG_DEBUG, "WPS UPnP: Removing " - "subscription with no addresses"); - dl_list_del(&s->list); - subscription_destroy(s); - } else { - if (s->current_event == NULL /* not busy */ && - !dl_list_empty(&s->event_queue) /* more to do */) { - if (event_send_start(s)) - nerrors++; - } + if (s->current_event == NULL /* not busy */ && + !dl_list_empty(&s->event_queue) /* more to do */) { + if (event_send_start(s)) + nerrors++; } } @@ -326,31 +369,54 @@ void event_send_stop_all(struct upnp_wps_device_sm *sm) * event_add - Add a new event to a queue * @s: Subscription * @data: Event data (is copied; caller retains ownership) - * Returns: 0 on success, 1 on error + * @probereq: Whether this is a Probe Request event + * Returns: 0 on success, -1 on error, 1 on max event queue limit reached */ -int event_add(struct subscription *s, const struct wpabuf *data) +int event_add(struct subscription *s, const struct wpabuf *data, int probereq) { struct wps_event_ *e; + unsigned int len; - if (dl_list_len(&s->event_queue) >= MAX_EVENTS_QUEUED) { + len = dl_list_len(&s->event_queue); + if (len >= MAX_EVENTS_QUEUED) { wpa_printf(MSG_DEBUG, "WPS UPnP: Too many events queued for " - "subscriber"); - return 1; + "subscriber %p", s); + if (probereq) + return 1; + + /* Drop oldest entry to allow EAP event to be stored. */ + e = event_dequeue(s); + if (!e) + return 1; + event_delete(e); + } + + if (s->last_event_failed && probereq && len > 0) { + /* + * Avoid queuing frames for subscribers that may have left + * without unsubscribing. + */ + wpa_printf(MSG_DEBUG, "WPS UPnP: Do not queue more Probe " + "Request frames for subscription %p since last " + "delivery failed", s); + return -1; } e = os_zalloc(sizeof(*e)); if (e == NULL) - return 1; + return -1; dl_list_init(&e->list); e->s = s; e->data = wpabuf_dup(data); if (e->data == NULL) { os_free(e); - return 1; + return -1; } e->subscriber_sequence = s->next_subscriber_sequence++; if (s->next_subscriber_sequence == 0) s->next_subscriber_sequence++; + wpa_printf(MSG_DEBUG, "WPS UPnP: Queue event %p for subscriber %p " + "(queue len %u)", e, s, len + 1); dl_list_add_tail(&s->event_queue, &e->list); event_send_all_later(s->sm); return 0; diff --git a/src/wps/wps_upnp_i.h b/src/wps/wps_upnp_i.h index b31875ac9a098..7f3c56109e199 100644 --- a/src/wps/wps_upnp_i.h +++ b/src/wps/wps_upnp_i.h @@ -67,6 +67,7 @@ struct subscr_addr { char *domain_and_port; /* domain and port part of url */ char *path; /* "filepath" part of url (from "mem") */ struct sockaddr_in saddr; /* address for doing connect */ + unsigned num_failures; }; @@ -91,25 +92,37 @@ struct subscription { struct dl_list event_queue; /* Queued event messages. */ struct wps_event_ *current_event; /* non-NULL if being sent (not in q) */ + int last_event_failed; /* Whether delivery of last event failed */ /* Information from SetSelectedRegistrar action */ u8 selected_registrar; u16 dev_password_id; u16 config_methods; + u8 authorized_macs[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN]; struct wps_registrar *reg; }; +struct upnp_wps_device_interface { + struct dl_list list; + struct upnp_wps_device_ctx *ctx; /* callback table */ + struct wps_context *wps; + void *priv; + + /* FIX: maintain separate structures for each UPnP peer */ + struct upnp_wps_peer peer; +}; + /* - * Our instance data corresponding to one WiFi network interface - * (multiple might share the same wired network interface!). + * Our instance data corresponding to the AP device. Note that there may be + * multiple wireless interfaces sharing the same UPnP device instance. Each + * such interface is stored in the list of struct upnp_wps_device_interface + * instances. * * This is known as an opaque struct declaration to users of the WPS UPnP code. */ struct upnp_wps_device_sm { - struct upnp_wps_device_ctx *ctx; /* callback table */ - struct wps_context *wps; - void *priv; + struct dl_list interfaces; /* struct upnp_wps_device_interface */ char *root_dir; char *desc_url; int started; /* nonzero if we are active */ @@ -130,9 +143,9 @@ struct upnp_wps_device_sm { */ char *wlanevent; /* the last WLANEvent data */ - - /* FIX: maintain separate structures for each UPnP peer */ - struct upnp_wps_peer peer; + enum upnp_wps_wlanevent_type wlanevent_type; + os_time_t last_event_sec; + unsigned int num_events_in_sec; }; /* wps_upnp.c */ @@ -144,6 +157,7 @@ struct subscription * subscription_renew(struct upnp_wps_device_sm *sm, void subscription_destroy(struct subscription *s); struct subscription * subscription_find(struct upnp_wps_device_sm *sm, const u8 uuid[UUID_LEN]); +void subscr_addr_delete(struct subscr_addr *a); int send_wpabuf(int fd, struct wpabuf *buf); int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text, u8 mac[ETH_ALEN]); @@ -165,7 +179,7 @@ int web_listener_start(struct upnp_wps_device_sm *sm); void web_listener_stop(struct upnp_wps_device_sm *sm); /* wps_upnp_event.c */ -int event_add(struct subscription *s, const struct wpabuf *data); +int event_add(struct subscription *s, const struct wpabuf *data, int probereq); void event_delete_all(struct subscription *s); void event_send_all_later(struct upnp_wps_device_sm *sm); void event_send_stop_all(struct upnp_wps_device_sm *sm); @@ -174,6 +188,7 @@ void event_send_stop_all(struct upnp_wps_device_sm *sm); int upnp_er_set_selected_registrar(struct wps_registrar *reg, struct subscription *s, const struct wpabuf *msg); -void upnp_er_remove_notification(struct subscription *s); +void upnp_er_remove_notification(struct wps_registrar *reg, + struct subscription *s); #endif /* WPS_UPNP_I_H */ diff --git a/src/wps/wps_upnp_ssdp.c b/src/wps/wps_upnp_ssdp.c index 8505d05852bc2..17a82074a7fb0 100644 --- a/src/wps/wps_upnp_ssdp.c +++ b/src/wps/wps_upnp_ssdp.c @@ -97,16 +97,6 @@ static int line_length(const char *l) } -/* No. of chars excluding trailing whitespace */ -static int line_length_stripped(const char *l) -{ - const char *lp = l + line_length(l); - while (lp > l && !isgraph(lp[-1])) - lp--; - return lp - l; -} - - static int str_starts(const char *str, const char *start) { return os_strncmp(str, start, os_strlen(start)) == 0; @@ -136,9 +126,12 @@ next_advertisement(struct upnp_wps_device_sm *sm, struct wpabuf *msg; char *NTString = ""; char uuid_string[80]; + struct upnp_wps_device_interface *iface; *islast = 0; - uuid_bin2str(sm->wps->uuid, uuid_string, sizeof(uuid_string)); + iface = dl_list_first(&sm->interfaces, + struct upnp_wps_device_interface, list); + uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string)); msg = wpabuf_alloc(800); /* more than big enough */ if (msg == NULL) goto fail; @@ -527,7 +520,6 @@ static void ssdp_parse_msearch(struct upnp_wps_device_sm *sm, #ifndef CONFIG_NO_STDOUT_DEBUG const char *start = data; #endif /* CONFIG_NO_STDOUT_DEBUG */ - const char *end; int got_host = 0; int got_st = 0, st_match = 0; int got_man = 0; @@ -542,7 +534,6 @@ static void ssdp_parse_msearch(struct upnp_wps_device_sm *sm, /* Parse remaining lines */ for (; *data != '\0'; data += line_length(data)) { - end = data + line_length_stripped(data); if (token_eq(data, "host")) { /* The host line indicates who the packet * is addressed to... but do we really care? @@ -588,8 +579,13 @@ static void ssdp_parse_msearch(struct upnp_wps_device_sm *sm, } if (str_starts(data, "uuid:")) { char uuid_string[80]; + struct upnp_wps_device_interface *iface; + iface = dl_list_first( + &sm->interfaces, + struct upnp_wps_device_interface, + list); data += os_strlen("uuid:"); - uuid_bin2str(sm->wps->uuid, uuid_string, + uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string)); if (str_starts(data, uuid_string)) st_match = 1; @@ -870,16 +866,26 @@ 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))) + &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))) + &ttl, sizeof(ttl))) { + wpa_printf(MSG_DEBUG, "WPS: setsockopt(IP_MULTICAST_TTL): " + "%d (%s)", errno, strerror(errno)); + close(sd); return -1; + } #if 0 /* not needed, because we don't receive using multicast_sd */ { @@ -896,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; } } diff --git a/src/wps/wps_upnp_web.c b/src/wps/wps_upnp_web.c index 9a6b36e021704..ce0bede8342d0 100644 --- a/src/wps/wps_upnp_web.c +++ b/src/wps/wps_upnp_web.c @@ -184,6 +184,10 @@ static void format_wps_device_xml(struct upnp_wps_device_sm *sm, { const char *s; char uuid_string[80]; + struct upnp_wps_device_interface *iface; + + iface = dl_list_first(&sm->interfaces, + struct upnp_wps_device_interface, list); wpabuf_put_str(buf, wps_device_xml_prefix); @@ -191,38 +195,38 @@ static void format_wps_device_xml(struct upnp_wps_device_sm *sm, * Add required fields with default values if not configured. Add * optional and recommended fields only if configured. */ - s = sm->wps->friendly_name; + s = iface->wps->friendly_name; s = ((s && *s) ? s : "WPS Access Point"); xml_add_tagged_data(buf, "friendlyName", s); - s = sm->wps->dev.manufacturer; + s = iface->wps->dev.manufacturer; s = ((s && *s) ? s : ""); xml_add_tagged_data(buf, "manufacturer", s); - if (sm->wps->manufacturer_url) + if (iface->wps->manufacturer_url) xml_add_tagged_data(buf, "manufacturerURL", - sm->wps->manufacturer_url); + iface->wps->manufacturer_url); - if (sm->wps->model_description) + if (iface->wps->model_description) xml_add_tagged_data(buf, "modelDescription", - sm->wps->model_description); + iface->wps->model_description); - s = sm->wps->dev.model_name; + s = iface->wps->dev.model_name; s = ((s && *s) ? s : ""); xml_add_tagged_data(buf, "modelName", s); - if (sm->wps->dev.model_number) + if (iface->wps->dev.model_number) xml_add_tagged_data(buf, "modelNumber", - sm->wps->dev.model_number); + iface->wps->dev.model_number); - if (sm->wps->model_url) - xml_add_tagged_data(buf, "modelURL", sm->wps->model_url); + if (iface->wps->model_url) + xml_add_tagged_data(buf, "modelURL", iface->wps->model_url); - if (sm->wps->dev.serial_number) + if (iface->wps->dev.serial_number) xml_add_tagged_data(buf, "serialNumber", - sm->wps->dev.serial_number); + iface->wps->dev.serial_number); - uuid_bin2str(sm->wps->uuid, uuid_string, sizeof(uuid_string)); + uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string)); s = uuid_string; /* Need "uuid:" prefix, thus we can't use xml_add_tagged_data() * easily... @@ -231,8 +235,8 @@ static void format_wps_device_xml(struct upnp_wps_device_sm *sm, xml_data_encode(buf, s, os_strlen(s)); wpabuf_put_str(buf, "</UDN>\n"); - if (sm->wps->upc) - xml_add_tagged_data(buf, "UPC", sm->wps->upc); + if (iface->wps->upc) + xml_add_tagged_data(buf, "UPC", iface->wps->upc); wpabuf_put_str(buf, wps_device_xml_postfix); } @@ -311,6 +315,10 @@ static void web_connection_parse_get(struct upnp_wps_device_sm *sm, size_t extra_len = 0; int body_length; char len_buf[10]; + struct upnp_wps_device_interface *iface; + + iface = dl_list_first(&sm->interfaces, + struct upnp_wps_device_interface, list); /* * It is not required that filenames be case insensitive but it is @@ -322,16 +330,16 @@ static void web_connection_parse_get(struct upnp_wps_device_sm *sm, wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for device XML"); req = GET_DEVICE_XML_FILE; extra_len = 3000; - if (sm->wps->friendly_name) - extra_len += os_strlen(sm->wps->friendly_name); - if (sm->wps->manufacturer_url) - extra_len += os_strlen(sm->wps->manufacturer_url); - if (sm->wps->model_description) - extra_len += os_strlen(sm->wps->model_description); - if (sm->wps->model_url) - extra_len += os_strlen(sm->wps->model_url); - if (sm->wps->upc) - extra_len += os_strlen(sm->wps->upc); + if (iface->wps->friendly_name) + extra_len += os_strlen(iface->wps->friendly_name); + if (iface->wps->manufacturer_url) + extra_len += os_strlen(iface->wps->manufacturer_url); + if (iface->wps->model_description) + extra_len += os_strlen(iface->wps->model_description); + if (iface->wps->model_url) + extra_len += os_strlen(iface->wps->model_url); + if (iface->wps->upc) + extra_len += os_strlen(iface->wps->upc); } else if (!os_strcasecmp(filename, UPNP_WPS_SCPD_XML_FILE)) { wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for SCPD XML"); req = GET_SCPD_XML_FILE; @@ -408,11 +416,16 @@ web_process_get_device_info(struct upnp_wps_device_sm *sm, { static const char *name = "NewDeviceInfo"; struct wps_config cfg; - struct upnp_wps_peer *peer = &sm->peer; + struct upnp_wps_device_interface *iface; + struct upnp_wps_peer *peer; + + iface = dl_list_first(&sm->interfaces, + struct upnp_wps_device_interface, list); + peer = &iface->peer; wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo"); - if (sm->ctx->ap_pin == NULL) + if (iface->ctx->ap_pin == NULL) return HTTP_INTERNAL_SERVER_ERROR; /* @@ -427,9 +440,9 @@ web_process_get_device_info(struct upnp_wps_device_sm *sm, wps_deinit(peer->wps); os_memset(&cfg, 0, sizeof(cfg)); - cfg.wps = sm->wps; - cfg.pin = (u8 *) sm->ctx->ap_pin; - cfg.pin_len = os_strlen(sm->ctx->ap_pin); + cfg.wps = iface->wps; + cfg.pin = (u8 *) iface->ctx->ap_pin; + cfg.pin_len = os_strlen(iface->ctx->ap_pin); peer->wps = wps_init(&cfg); if (peer->wps) { enum wsc_op_code op_code; @@ -458,6 +471,10 @@ web_process_put_message(struct upnp_wps_device_sm *sm, char *data, enum http_reply_code ret; enum wps_process_res res; enum wsc_op_code op_code; + struct upnp_wps_device_interface *iface; + + iface = dl_list_first(&sm->interfaces, + struct upnp_wps_device_interface, list); /* * PutMessage is used by external UPnP-based Registrar to perform WPS @@ -468,11 +485,11 @@ web_process_put_message(struct upnp_wps_device_sm *sm, char *data, msg = xml_get_base64_item(data, "NewInMessage", &ret); if (msg == NULL) return ret; - res = wps_process_msg(sm->peer.wps, WSC_UPnP, msg); + res = wps_process_msg(iface->peer.wps, WSC_UPnP, msg); if (res == WPS_FAILURE) *reply = NULL; else - *reply = wps_get_msg(sm->peer.wps, &op_code); + *reply = wps_get_msg(iface->peer.wps, &op_code); wpabuf_free(msg); if (*reply == NULL) return HTTP_INTERNAL_SERVER_ERROR; @@ -491,6 +508,8 @@ web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data, int ev_type; int type; char *val; + struct upnp_wps_device_interface *iface; + int ok = 0; /* * External UPnP-based Registrar is passing us a message to be proxied @@ -523,6 +542,16 @@ web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data, if (hwaddr_aton(val, macaddr)) { wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid NewWLANEventMAC in " "PutWLANResponse: '%s'", val); +#ifdef CONFIG_WPS_STRICT + { + struct wps_parse_attr attr; + if (wps_parse_msg(msg, &attr) < 0 || attr.version2) { + wpabuf_free(msg); + os_free(val); + return UPNP_ARG_VALUE_INVALID; + } + } +#endif /* CONFIG_WPS_STRICT */ if (hwaddr_aton2(val, macaddr) > 0) { /* * At least some versions of Intel PROset seem to be @@ -530,7 +559,8 @@ web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data, */ wpa_printf(MSG_DEBUG, "WPS UPnP: Workaround - allow " "incorrect MAC address format in " - "NewWLANEventMAC"); + "NewWLANEventMAC: %s -> " MACSTR, + val, MAC2STR(macaddr)); } else { wpabuf_free(msg); os_free(val); @@ -548,9 +578,16 @@ web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data, wpa_printf(MSG_DEBUG, "WPS UPnP: Message Type %d", type); } else type = -1; - if (!sm->ctx->rx_req_put_wlan_response || - sm->ctx->rx_req_put_wlan_response(sm->priv, ev_type, macaddr, msg, - type)) { + dl_list_for_each(iface, &sm->interfaces, + struct upnp_wps_device_interface, list) { + if (iface->ctx->rx_req_put_wlan_response && + iface->ctx->rx_req_put_wlan_response(iface->priv, ev_type, + macaddr, msg, type) + == 0) + ok = 1; + } + + if (!ok) { wpa_printf(MSG_INFO, "WPS UPnP: Fail: sm->ctx->" "rx_req_put_wlan_response"); wpabuf_free(msg); @@ -595,6 +632,8 @@ web_process_set_selected_registrar(struct upnp_wps_device_sm *sm, struct wpabuf *msg; enum http_reply_code ret; struct subscription *s; + struct upnp_wps_device_interface *iface; + int err = 0; wpa_printf(MSG_DEBUG, "WPS UPnP: SetSelectedRegistrar"); s = find_er(sm, cli); @@ -606,11 +645,15 @@ web_process_set_selected_registrar(struct upnp_wps_device_sm *sm, msg = xml_get_base64_item(data, "NewMessage", &ret); if (msg == NULL) return ret; - if (upnp_er_set_selected_registrar(sm->wps->registrar, s, msg)) { - wpabuf_free(msg); - return HTTP_INTERNAL_SERVER_ERROR; + dl_list_for_each(iface, &sm->interfaces, + struct upnp_wps_device_interface, list) { + if (upnp_er_set_selected_registrar(iface->wps->registrar, s, + msg)) + err = 1; } wpabuf_free(msg); + if (err) + return HTTP_INTERNAL_SERVER_ERROR; *replyname = NULL; *reply = NULL; return HTTP_OK; @@ -890,6 +933,9 @@ static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm, return; } + wpa_hexdump_ascii(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE", + (u8 *) hdr, os_strlen(hdr)); + /* Parse/validate headers */ h = hdr; /* First line: SUBSCRIBE /wps_event HTTP/1.1 @@ -910,7 +956,7 @@ static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm, break; /* no unterminated lines allowed */ /* NT assures that it is our type of subscription; - * not used for a renewl. + * not used for a renewal. **/ match = "NT:"; match_len = os_strlen(match); @@ -989,16 +1035,22 @@ static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm, if (got_uuid) { /* renewal */ + wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewal"); if (callback_urls) { ret = HTTP_BAD_REQUEST; goto error; } s = subscription_renew(sm, uuid); if (s == NULL) { + char str[80]; + uuid_bin2str(uuid, str, sizeof(str)); + wpa_printf(MSG_DEBUG, "WPS UPnP: Could not find " + "SID %s", str); ret = HTTP_PRECONDITION_FAILED; goto error; } } else if (callback_urls) { + wpa_printf(MSG_DEBUG, "WPS UPnP: New subscription"); if (!got_nt) { ret = HTTP_PRECONDITION_FAILED; goto error; @@ -1022,6 +1074,7 @@ static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm, /* subscription id */ b = wpabuf_put(buf, 0); uuid_bin2str(s->uuid, b, 80); + wpa_printf(MSG_DEBUG, "WPS UPnP: Assigned SID %s", b); wpabuf_put(buf, os_strlen(b)); wpabuf_put_str(buf, "\r\n"); wpabuf_printf(buf, "Timeout: Second-%d\r\n", UPNP_SUBSCRIBE_SEC); @@ -1055,6 +1108,7 @@ error: * HTTP 500-series error code. * 599 Too many subscriptions (not a standard HTTP error) */ + wpa_printf(MSG_DEBUG, "WPS UPnP: SUBSCRIBE failed - return %d", ret); http_put_empty(buf, ret); http_request_send_and_deinit(req, buf); os_free(callback_urls); diff --git a/src/wps/wps_validate.c b/src/wps/wps_validate.c new file mode 100644 index 0000000000000..e3662562b40ae --- /dev/null +++ b/src/wps/wps_validate.c @@ -0,0 +1,1975 @@ +/* + * Wi-Fi Protected Setup - Strict protocol validation routines + * Copyright (c) 2010, Atheros Communications, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "wps_i.h" +#include "wps.h" + + +#ifndef WPS_STRICT_ALL +#define WPS_STRICT_WPS2 +#endif /* WPS_STRICT_ALL */ + + +static int wps_validate_version(const u8 *version, int mandatory) +{ + if (version == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Version attribute " + "missing"); + return -1; + } + return 0; + } + if (*version != 0x10) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Version attribute " + "value 0x%x", *version); + return -1; + } + return 0; +} + + +static int wps_validate_version2(const u8 *version2, int mandatory) +{ + if (version2 == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Version2 attribute " + "missing"); + return -1; + } + return 0; + } + if (*version2 < 0x20) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Version2 attribute " + "value 0x%x", *version2); + return -1; + } + return 0; +} + + +static int wps_validate_request_type(const u8 *request_type, int mandatory) +{ + if (request_type == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Request Type " + "attribute missing"); + return -1; + } + return 0; + } + if (*request_type > 0x03) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Request Type " + "attribute value 0x%x", *request_type); + return -1; + } + return 0; +} + + +static int wps_validate_response_type(const u8 *response_type, int mandatory) +{ + if (response_type == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Response Type " + "attribute missing"); + return -1; + } + return 0; + } + if (*response_type > 0x03) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Response Type " + "attribute value 0x%x", *response_type); + return -1; + } + return 0; +} + + +static int valid_config_methods(u16 val, int wps2) +{ + if (wps2) { + if ((val & 0x6000) && !(val & WPS_CONFIG_DISPLAY)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Physical/Virtual " + "Display flag without old Display flag " + "set"); + return 0; + } + if (!(val & 0x6000) && (val & WPS_CONFIG_DISPLAY)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Display flag " + "without Physical/Virtual Display flag"); + return 0; + } + if ((val & 0x0600) && !(val & WPS_CONFIG_PUSHBUTTON)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Physical/Virtual " + "PushButton flag without old PushButton " + "flag set"); + return 0; + } + if (!(val & 0x0600) && (val & WPS_CONFIG_PUSHBUTTON)) { + wpa_printf(MSG_INFO, "WPS-STRICT: PushButton flag " + "without Physical/Virtual PushButton flag"); + return 0; + } + } + + return 1; +} + + +static int wps_validate_config_methods(const u8 *config_methods, int wps2, + int mandatory) +{ + u16 val; + + if (config_methods == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Configuration " + "Methods attribute missing"); + return -1; + } + return 0; + } + + val = WPA_GET_BE16(config_methods); + if (!valid_config_methods(val, wps2)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration " + "Methods attribute value 0x%04x", val); + return -1; + } + return 0; +} + + +static int wps_validate_ap_config_methods(const u8 *config_methods, int wps2, + int mandatory) +{ + u16 val; + + if (wps_validate_config_methods(config_methods, wps2, mandatory) < 0) + return -1; + if (config_methods == NULL) + return 0; + val = WPA_GET_BE16(config_methods); + if (val & WPS_CONFIG_PUSHBUTTON) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration " + "Methods attribute value 0x%04x in AP info " + "(PushButton not allowed for registering new ER)", + val); + return -1; + } + return 0; +} + + +static int wps_validate_uuid_e(const u8 *uuid_e, int mandatory) +{ + if (uuid_e == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: UUID-E " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_uuid_r(const u8 *uuid_r, int mandatory) +{ + if (uuid_r == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: UUID-R " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_primary_dev_type(const u8 *primary_dev_type, + int mandatory) +{ + if (primary_dev_type == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Primary Device Type " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_rf_bands(const u8 *rf_bands, int mandatory) +{ + if (rf_bands == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: RF Bands " + "attribute missing"); + return -1; + } + return 0; + } + if (*rf_bands != WPS_RF_24GHZ && *rf_bands != WPS_RF_50GHZ && + *rf_bands != (WPS_RF_24GHZ | WPS_RF_50GHZ)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Rf Bands " + "attribute value 0x%x", *rf_bands); + return -1; + } + return 0; +} + + +static int wps_validate_assoc_state(const u8 *assoc_state, int mandatory) +{ + u16 val; + if (assoc_state == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Association State " + "attribute missing"); + return -1; + } + return 0; + } + val = WPA_GET_BE16(assoc_state); + if (val > 4) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Association State " + "attribute value 0x%04x", val); + return -1; + } + return 0; +} + + +static int wps_validate_config_error(const u8 *config_error, int mandatory) +{ + u16 val; + + if (config_error == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Configuration Error " + "attribute missing"); + return -1; + } + return 0; + } + val = WPA_GET_BE16(config_error); + if (val > 18) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration Error " + "attribute value 0x%04x", val); + return -1; + } + return 0; +} + + +static int wps_validate_dev_password_id(const u8 *dev_password_id, + int mandatory) +{ + u16 val; + + if (dev_password_id == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Device Password ID " + "attribute missing"); + return -1; + } + return 0; + } + val = WPA_GET_BE16(dev_password_id); + if (val >= 0x0006 && val <= 0x000f) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Device Password ID " + "attribute value 0x%04x", val); + return -1; + } + return 0; +} + + +static int wps_validate_manufacturer(const u8 *manufacturer, size_t len, + int mandatory) +{ + if (manufacturer == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Manufacturer " + "attribute missing"); + return -1; + } + return 0; + } + if (len > 0 && manufacturer[len - 1] == 0) { + wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Manufacturer " + "attribute value", manufacturer, len); + return -1; + } + return 0; +} + + +static int wps_validate_model_name(const u8 *model_name, size_t len, + int mandatory) +{ + if (model_name == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Model Name " + "attribute missing"); + return -1; + } + return 0; + } + if (len > 0 && model_name[len - 1] == 0) { + wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Model Name " + "attribute value", model_name, len); + return -1; + } + return 0; +} + + +static int wps_validate_model_number(const u8 *model_number, size_t len, + int mandatory) +{ + if (model_number == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Model Number " + "attribute missing"); + return -1; + } + return 0; + } + if (len > 0 && model_number[len - 1] == 0) { + wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Model Number " + "attribute value", model_number, len); + return -1; + } + return 0; +} + + +static int wps_validate_serial_number(const u8 *serial_number, size_t len, + int mandatory) +{ + if (serial_number == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Serial Number " + "attribute missing"); + return -1; + } + return 0; + } + if (len > 0 && serial_number[len - 1] == 0) { + wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Serial " + "Number attribute value", + serial_number, len); + return -1; + } + return 0; +} + + +static int wps_validate_dev_name(const u8 *dev_name, size_t len, + int mandatory) +{ + if (dev_name == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Device Name " + "attribute missing"); + return -1; + } + return 0; + } + if (len > 0 && dev_name[len - 1] == 0) { + wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Device Name " + "attribute value", dev_name, len); + return -1; + } + return 0; +} + + +static int wps_validate_request_to_enroll(const u8 *request_to_enroll, + int mandatory) +{ + if (request_to_enroll == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Request to Enroll " + "attribute missing"); + return -1; + } + return 0; + } + if (*request_to_enroll > 0x01) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Request to Enroll " + "attribute value 0x%x", *request_to_enroll); + return -1; + } + return 0; +} + + +static int wps_validate_req_dev_type(const u8 *req_dev_type[], size_t num, + int mandatory) +{ + if (num == 0) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Requested Device " + "Type attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_wps_state(const u8 *wps_state, int mandatory) +{ + if (wps_state == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Wi-Fi Protected " + "Setup State attribute missing"); + return -1; + } + return 0; + } + if (*wps_state != WPS_STATE_NOT_CONFIGURED && + *wps_state != WPS_STATE_CONFIGURED) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Wi-Fi Protected " + "Setup State attribute value 0x%x", *wps_state); + return -1; + } + return 0; +} + + +static int wps_validate_ap_setup_locked(const u8 *ap_setup_locked, + int mandatory) +{ + if (ap_setup_locked == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: AP Setup Locked " + "attribute missing"); + return -1; + } + return 0; + } + if (*ap_setup_locked > 1) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid AP Setup Locked " + "attribute value 0x%x", *ap_setup_locked); + return -1; + } + return 0; +} + + +static int wps_validate_selected_registrar(const u8 *selected_registrar, + int mandatory) +{ + if (selected_registrar == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Selected Registrar " + "attribute missing"); + return -1; + } + return 0; + } + if (*selected_registrar > 1) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Selected Registrar " + "attribute value 0x%x", *selected_registrar); + return -1; + } + return 0; +} + + +static int wps_validate_sel_reg_config_methods(const u8 *config_methods, + int wps2, int mandatory) +{ + u16 val; + + if (config_methods == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Selected Registrar " + "Configuration Methods attribute missing"); + return -1; + } + return 0; + } + + val = WPA_GET_BE16(config_methods); + if (!valid_config_methods(val, wps2)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Selected Registrar " + "Configuration Methods attribute value 0x%04x", + val); + return -1; + } + return 0; +} + + +static int wps_validate_authorized_macs(const u8 *authorized_macs, size_t len, + int mandatory) +{ + if (authorized_macs == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Authorized MACs " + "attribute missing"); + return -1; + } + return 0; + } + if (len > 30 && (len % ETH_ALEN) != 0) { + wpa_hexdump(MSG_INFO, "WPS-STRICT: Invalid Authorized " + "MACs attribute value", authorized_macs, len); + return -1; + } + return 0; +} + + +static int wps_validate_msg_type(const u8 *msg_type, int mandatory) +{ + if (msg_type == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Message Type " + "attribute missing"); + return -1; + } + return 0; + } + if (*msg_type < WPS_Beacon || *msg_type > WPS_WSC_DONE) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Message Type " + "attribute value 0x%x", *msg_type); + return -1; + } + return 0; +} + + +static int wps_validate_mac_addr(const u8 *mac_addr, int mandatory) +{ + if (mac_addr == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: MAC Address " + "attribute missing"); + return -1; + } + return 0; + } + if (mac_addr[0] & 0x01) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid MAC Address " + "attribute value " MACSTR, MAC2STR(mac_addr)); + return -1; + } + return 0; +} + + +static int wps_validate_enrollee_nonce(const u8 *enrollee_nonce, int mandatory) +{ + if (enrollee_nonce == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Enrollee Nonce " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_registrar_nonce(const u8 *registrar_nonce, + int mandatory) +{ + if (registrar_nonce == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Registrar Nonce " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_public_key(const u8 *public_key, size_t len, + int mandatory) +{ + if (public_key == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Public Key " + "attribute missing"); + return -1; + } + return 0; + } + if (len != 192) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Public Key " + "attribute length %d", (int) len); + return -1; + } + return 0; +} + + +static int num_bits_set(u16 val) +{ + int c; + for (c = 0; val; c++) + val &= val - 1; + return c; +} + + +static int wps_validate_auth_type_flags(const u8 *flags, int mandatory) +{ + u16 val; + + if (flags == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Authentication Type " + "Flags attribute missing"); + return -1; + } + return 0; + } + val = WPA_GET_BE16(flags); + if ((val & ~WPS_AUTH_TYPES) || !(val & WPS_AUTH_WPA2PSK)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Authentication Type " + "Flags attribute value 0x%04x", val); + return -1; + } + return 0; +} + + +static int wps_validate_auth_type(const u8 *type, int mandatory) +{ + u16 val; + + if (type == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Authentication Type " + "attribute missing"); + return -1; + } + return 0; + } + val = WPA_GET_BE16(type); + if ((val & ~WPS_AUTH_TYPES) || val == 0 || + (num_bits_set(val) > 1 && + val != (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK))) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Authentication Type " + "attribute value 0x%04x", val); + return -1; + } + return 0; +} + + +static int wps_validate_encr_type_flags(const u8 *flags, int mandatory) +{ + u16 val; + + if (flags == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Encryption Type " + "Flags attribute missing"); + return -1; + } + return 0; + } + val = WPA_GET_BE16(flags); + if ((val & ~WPS_ENCR_TYPES) || !(val & WPS_ENCR_AES)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Encryption Type " + "Flags attribute value 0x%04x", val); + return -1; + } + return 0; +} + + +static int wps_validate_encr_type(const u8 *type, int mandatory) +{ + u16 val; + + if (type == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Encryption Type " + "attribute missing"); + return -1; + } + return 0; + } + val = WPA_GET_BE16(type); + if ((val & ~WPS_ENCR_TYPES) || val == 0 || + (num_bits_set(val) > 1 && val != (WPS_ENCR_TKIP | WPS_ENCR_AES))) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Encryption Type " + "attribute value 0x%04x", val); + return -1; + } + return 0; +} + + +static int wps_validate_conn_type_flags(const u8 *flags, int mandatory) +{ + if (flags == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Connection Type " + "Flags attribute missing"); + return -1; + } + return 0; + } + if ((*flags & ~(WPS_CONN_ESS | WPS_CONN_IBSS)) || + !(*flags & WPS_CONN_ESS)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Connection Type " + "Flags attribute value 0x%02x", *flags); + return -1; + } + return 0; +} + + +static int wps_validate_os_version(const u8 *os_version, int mandatory) +{ + if (os_version == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: OS Version " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_authenticator(const u8 *authenticator, int mandatory) +{ + if (authenticator == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Authenticator " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_e_hash1(const u8 *hash, int mandatory) +{ + if (hash == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: E-Hash1 " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_e_hash2(const u8 *hash, int mandatory) +{ + if (hash == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: E-Hash2 " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_r_hash1(const u8 *hash, int mandatory) +{ + if (hash == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: R-Hash1 " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_r_hash2(const u8 *hash, int mandatory) +{ + if (hash == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: R-Hash2 " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_encr_settings(const u8 *encr_settings, size_t len, + int mandatory) +{ + if (encr_settings == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Encrypted Settings " + "attribute missing"); + return -1; + } + return 0; + } + if (len < 16) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Encrypted Settings " + "attribute length %d", (int) len); + return -1; + } + return 0; +} + + +static int wps_validate_settings_delay_time(const u8 *delay, int mandatory) +{ + if (delay == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Settings Delay Time " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_r_snonce1(const u8 *nonce, int mandatory) +{ + if (nonce == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: R-SNonce1 " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_r_snonce2(const u8 *nonce, int mandatory) +{ + if (nonce == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: R-SNonce2 " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_e_snonce1(const u8 *nonce, int mandatory) +{ + if (nonce == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: E-SNonce1 " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_e_snonce2(const u8 *nonce, int mandatory) +{ + if (nonce == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: E-SNonce2 " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_key_wrap_auth(const u8 *auth, int mandatory) +{ + if (auth == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Key Wrap " + "Authenticator attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_ssid(const u8 *ssid, size_t ssid_len, int mandatory) +{ + if (ssid == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: SSID " + "attribute missing"); + return -1; + } + return 0; + } + if (ssid_len == 0 || ssid[ssid_len - 1] == 0) { + wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid SSID " + "attribute value", ssid, ssid_len); + return -1; + } + return 0; +} + + +static int wps_validate_network_key_index(const u8 *idx, int mandatory) +{ + if (idx == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Network Key Index " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_network_idx(const u8 *idx, int mandatory) +{ + if (idx == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Network Index " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_network_key(const u8 *key, size_t key_len, + const u8 *encr_type, int mandatory) +{ + if (key == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Network Key " + "attribute missing"); + return -1; + } + return 0; + } + if (((encr_type == NULL || WPA_GET_BE16(encr_type) != WPS_ENCR_WEP) && + key_len > 8 && key_len < 64 && key[key_len - 1] == 0) || + key_len > 64) { + wpa_hexdump_ascii_key(MSG_INFO, "WPS-STRICT: Invalid Network " + "Key attribute value", key, key_len); + return -1; + } + return 0; +} + + +static int wps_validate_network_key_shareable(const u8 *val, int mandatory) +{ + if (val == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Network Key " + "Shareable attribute missing"); + return -1; + } + return 0; + } + if (*val > 1) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Network Key " + "Shareable attribute value 0x%x", *val); + return -1; + } + return 0; +} + + +static int wps_validate_cred(const u8 *cred, size_t len) +{ + struct wps_parse_attr attr; + struct wpabuf buf; + + if (cred == NULL) + return -1; + wpabuf_set(&buf, cred, len); + if (wps_parse_msg(&buf, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse Credential"); + return -1; + } + + if (wps_validate_network_idx(attr.network_idx, 1) || + wps_validate_ssid(attr.ssid, attr.ssid_len, 1) || + wps_validate_auth_type(attr.auth_type, 1) || + wps_validate_encr_type(attr.encr_type, 1) || + wps_validate_network_key_index(attr.network_key_idx, 0) || + wps_validate_network_key(attr.network_key, attr.network_key_len, + attr.encr_type, 1) || + wps_validate_mac_addr(attr.mac_addr, 1) || + wps_validate_network_key_shareable(attr.network_key_shareable, 0)) + { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Credential"); + return -1; + } + + + return 0; +} + + +static int wps_validate_credential(const u8 *cred[], size_t len[], size_t num, + int mandatory) +{ + size_t i; + + if (num == 0) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Credential " + "attribute missing"); + return -1; + } + return 0; + } + + for (i = 0; i < num; i++) { + if (wps_validate_cred(cred[i], len[i]) < 0) + return -1; + } + + return 0; +} + + +int wps_validate_beacon(const struct wpabuf *wps_ie) +{ + struct wps_parse_attr attr; + int wps2, sel_reg; + + if (wps_ie == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in Beacon frame"); + return -1; + } + if (wps_parse_msg(wps_ie, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in " + "Beacon frame"); + return -1; + } + + wps2 = attr.version2 != NULL; + sel_reg = attr.selected_registrar != NULL && + *attr.selected_registrar != 0; + if (wps_validate_version(attr.version, 1) || + wps_validate_wps_state(attr.wps_state, 1) || + wps_validate_ap_setup_locked(attr.ap_setup_locked, 0) || + wps_validate_selected_registrar(attr.selected_registrar, 0) || + wps_validate_dev_password_id(attr.dev_password_id, sel_reg) || + wps_validate_sel_reg_config_methods(attr.sel_reg_config_methods, + wps2, sel_reg) || + wps_validate_uuid_e(attr.uuid_e, 0) || + wps_validate_rf_bands(attr.rf_bands, 0) || + wps_validate_version2(attr.version2, wps2) || + wps_validate_authorized_macs(attr.authorized_macs, + attr.authorized_macs_len, 0)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Beacon frame"); + return -1; + } + + return 0; +} + + +int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie, int probe, + const u8 *addr) +{ + struct wps_parse_attr attr; + int wps2, sel_reg; + + if (wps_ie == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in " + "%sProbe Response frame", probe ? "" : "Beacon/"); + return -1; + } + if (wps_parse_msg(wps_ie, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in " + "%sProbe Response frame", probe ? "" : "Beacon/"); + return -1; + } + + wps2 = attr.version2 != NULL; + sel_reg = attr.selected_registrar != NULL && + *attr.selected_registrar != 0; + if (wps_validate_version(attr.version, 1) || + wps_validate_wps_state(attr.wps_state, 1) || + wps_validate_ap_setup_locked(attr.ap_setup_locked, 0) || + wps_validate_selected_registrar(attr.selected_registrar, 0) || + wps_validate_dev_password_id(attr.dev_password_id, sel_reg) || + wps_validate_sel_reg_config_methods(attr.sel_reg_config_methods, + wps2, sel_reg) || + wps_validate_response_type(attr.response_type, probe) || + wps_validate_uuid_e(attr.uuid_e, probe) || + wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len, + probe) || + wps_validate_model_name(attr.model_name, attr.model_name_len, + probe) || + wps_validate_model_number(attr.model_number, attr.model_number_len, + probe) || + wps_validate_serial_number(attr.serial_number, + attr.serial_number_len, probe) || + wps_validate_primary_dev_type(attr.primary_dev_type, probe) || + wps_validate_dev_name(attr.dev_name, attr.dev_name_len, probe) || + wps_validate_ap_config_methods(attr.config_methods, wps2, probe) || + wps_validate_rf_bands(attr.rf_bands, 0) || + wps_validate_version2(attr.version2, wps2) || + wps_validate_authorized_macs(attr.authorized_macs, + attr.authorized_macs_len, 0)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid %sProbe Response " + "frame from " MACSTR, probe ? "" : "Beacon/", + MAC2STR(addr)); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_probe_req(const struct wpabuf *wps_ie, const u8 *addr) +{ + struct wps_parse_attr attr; + int wps2; + + if (wps_ie == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in " + "Probe Request frame"); + return -1; + } + if (wps_parse_msg(wps_ie, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in " + "Probe Request frame"); + return -1; + } + + wps2 = attr.version2 != NULL; + if (wps_validate_version(attr.version, 1) || + wps_validate_request_type(attr.request_type, 1) || + wps_validate_config_methods(attr.config_methods, wps2, 1) || + wps_validate_uuid_e(attr.uuid_e, attr.uuid_r == NULL) || + wps_validate_uuid_r(attr.uuid_r, attr.uuid_e == NULL) || + wps_validate_primary_dev_type(attr.primary_dev_type, 1) || + wps_validate_rf_bands(attr.rf_bands, 1) || + wps_validate_assoc_state(attr.assoc_state, 1) || + wps_validate_config_error(attr.config_error, 1) || + wps_validate_dev_password_id(attr.dev_password_id, 1) || + wps_validate_version2(attr.version2, wps2) || + wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len, + wps2) || + wps_validate_model_name(attr.model_name, attr.model_name_len, + wps2) || + wps_validate_model_number(attr.model_number, attr.model_number_len, + wps2) || + wps_validate_dev_name(attr.dev_name, attr.dev_name_len, wps2) || + wps_validate_request_to_enroll(attr.request_to_enroll, 0) || + wps_validate_req_dev_type(attr.req_dev_type, attr.num_req_dev_type, + 0)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Probe Request " + "frame from " MACSTR, MAC2STR(addr)); + return -1; + } + + return 0; +} + + +int wps_validate_assoc_req(const struct wpabuf *wps_ie) +{ + struct wps_parse_attr attr; + int wps2; + + if (wps_ie == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in " + "(Re)Association Request frame"); + return -1; + } + if (wps_parse_msg(wps_ie, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in " + "(Re)Association Request frame"); + return -1; + } + + wps2 = attr.version2 != NULL; + if (wps_validate_version(attr.version, 1) || + wps_validate_request_type(attr.request_type, 1) || + wps_validate_version2(attr.version2, wps2)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid (Re)Association " + "Request frame"); + return -1; + } + + return 0; +} + + +int wps_validate_assoc_resp(const struct wpabuf *wps_ie) +{ + struct wps_parse_attr attr; + int wps2; + + if (wps_ie == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in " + "(Re)Association Response frame"); + return -1; + } + if (wps_parse_msg(wps_ie, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in " + "(Re)Association Response frame"); + return -1; + } + + wps2 = attr.version2 != NULL; + if (wps_validate_version(attr.version, 1) || + wps_validate_response_type(attr.response_type, 1) || + wps_validate_version2(attr.version2, wps2)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid (Re)Association " + "Response frame"); + return -1; + } + + return 0; +} + + +int wps_validate_m1(const struct wpabuf *tlvs) +{ + struct wps_parse_attr attr; + int wps2; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M1"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in M1"); + return -1; + } + + wps2 = attr.version2 != NULL; + if (wps_validate_version(attr.version, 1) || + wps_validate_msg_type(attr.msg_type, 1) || + wps_validate_uuid_e(attr.uuid_e, 1) || + wps_validate_mac_addr(attr.mac_addr, 1) || + wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) || + wps_validate_public_key(attr.public_key, attr.public_key_len, 1) || + wps_validate_auth_type_flags(attr.auth_type_flags, 1) || + wps_validate_encr_type_flags(attr.encr_type_flags, 1) || + wps_validate_conn_type_flags(attr.conn_type_flags, 1) || + wps_validate_config_methods(attr.config_methods, wps2, 1) || + wps_validate_wps_state(attr.wps_state, 1) || + wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len, + 1) || + wps_validate_model_name(attr.model_name, attr.model_name_len, 1) || + wps_validate_model_number(attr.model_number, attr.model_number_len, + 1) || + wps_validate_serial_number(attr.serial_number, + attr.serial_number_len, 1) || + wps_validate_primary_dev_type(attr.primary_dev_type, 1) || + wps_validate_dev_name(attr.dev_name, attr.dev_name_len, 1) || + wps_validate_rf_bands(attr.rf_bands, 1) || + wps_validate_assoc_state(attr.assoc_state, 1) || + wps_validate_dev_password_id(attr.dev_password_id, 1) || + wps_validate_config_error(attr.config_error, 1) || + wps_validate_os_version(attr.os_version, 1) || + wps_validate_version2(attr.version2, wps2) || + wps_validate_request_to_enroll(attr.request_to_enroll, 0)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M1"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_m2(const struct wpabuf *tlvs) +{ + struct wps_parse_attr attr; + int wps2; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M2"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in M2"); + return -1; + } + + wps2 = attr.version2 != NULL; + if (wps_validate_version(attr.version, 1) || + wps_validate_msg_type(attr.msg_type, 1) || + wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) || + wps_validate_registrar_nonce(attr.registrar_nonce, 1) || + wps_validate_uuid_r(attr.uuid_r, 1) || + wps_validate_public_key(attr.public_key, attr.public_key_len, 1) || + wps_validate_auth_type_flags(attr.auth_type_flags, 1) || + wps_validate_encr_type_flags(attr.encr_type_flags, 1) || + wps_validate_conn_type_flags(attr.conn_type_flags, 1) || + wps_validate_config_methods(attr.config_methods, wps2, 1) || + wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len, + 1) || + wps_validate_model_name(attr.model_name, attr.model_name_len, 1) || + wps_validate_model_number(attr.model_number, attr.model_number_len, + 1) || + wps_validate_serial_number(attr.serial_number, + attr.serial_number_len, 1) || + wps_validate_primary_dev_type(attr.primary_dev_type, 1) || + wps_validate_dev_name(attr.dev_name, attr.dev_name_len, 1) || + wps_validate_rf_bands(attr.rf_bands, 1) || + wps_validate_assoc_state(attr.assoc_state, 1) || + wps_validate_config_error(attr.config_error, 1) || + wps_validate_dev_password_id(attr.dev_password_id, 1) || + wps_validate_os_version(attr.os_version, 1) || + wps_validate_version2(attr.version2, wps2) || + wps_validate_authenticator(attr.authenticator, 1)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M2"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_m2d(const struct wpabuf *tlvs) +{ + struct wps_parse_attr attr; + int wps2; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M2D"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in M2D"); + return -1; + } + + wps2 = attr.version2 != NULL; + if (wps_validate_version(attr.version, 1) || + wps_validate_msg_type(attr.msg_type, 1) || + wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) || + wps_validate_registrar_nonce(attr.registrar_nonce, 1) || + wps_validate_uuid_r(attr.uuid_r, 1) || + wps_validate_auth_type_flags(attr.auth_type_flags, 1) || + wps_validate_encr_type_flags(attr.encr_type_flags, 1) || + wps_validate_conn_type_flags(attr.conn_type_flags, 1) || + wps_validate_config_methods(attr.config_methods, wps2, 1) || + wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len, + 1) || + wps_validate_model_name(attr.model_name, attr.model_name_len, 1) || + wps_validate_model_number(attr.model_number, attr.model_number_len, + 1) || + wps_validate_serial_number(attr.serial_number, + attr.serial_number_len, 1) || + wps_validate_primary_dev_type(attr.primary_dev_type, 1) || + wps_validate_dev_name(attr.dev_name, attr.dev_name_len, 1) || + wps_validate_rf_bands(attr.rf_bands, 1) || + wps_validate_assoc_state(attr.assoc_state, 1) || + wps_validate_config_error(attr.config_error, 1) || + wps_validate_os_version(attr.os_version, 1) || + wps_validate_version2(attr.version2, wps2)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M2D"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_m3(const struct wpabuf *tlvs) +{ + struct wps_parse_attr attr; + int wps2; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M3"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in M3"); + return -1; + } + + wps2 = attr.version2 != NULL; + if (wps_validate_version(attr.version, 1) || + wps_validate_msg_type(attr.msg_type, 1) || + wps_validate_registrar_nonce(attr.registrar_nonce, 1) || + wps_validate_e_hash1(attr.e_hash1, 1) || + wps_validate_e_hash2(attr.e_hash2, 1) || + wps_validate_version2(attr.version2, wps2) || + wps_validate_authenticator(attr.authenticator, 1)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M3"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_m4(const struct wpabuf *tlvs) +{ + struct wps_parse_attr attr; + int wps2; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M4"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in M4"); + return -1; + } + + wps2 = attr.version2 != NULL; + if (wps_validate_version(attr.version, 1) || + wps_validate_msg_type(attr.msg_type, 1) || + wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) || + wps_validate_r_hash1(attr.r_hash1, 1) || + wps_validate_r_hash2(attr.r_hash2, 1) || + wps_validate_encr_settings(attr.encr_settings, + attr.encr_settings_len, 1) || + wps_validate_version2(attr.version2, wps2) || + wps_validate_authenticator(attr.authenticator, 1)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M4"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_m4_encr(const struct wpabuf *tlvs, int wps2) +{ + struct wps_parse_attr attr; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M4 encrypted " + "settings"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in M4 encrypted settings"); + return -1; + } + + if (wps_validate_r_snonce1(attr.r_snonce1, 1) || + wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M4 encrypted " + "settings"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_m5(const struct wpabuf *tlvs) +{ + struct wps_parse_attr attr; + int wps2; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M5"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in M5"); + return -1; + } + + wps2 = attr.version2 != NULL; + if (wps_validate_version(attr.version, 1) || + wps_validate_msg_type(attr.msg_type, 1) || + wps_validate_registrar_nonce(attr.registrar_nonce, 1) || + wps_validate_encr_settings(attr.encr_settings, + attr.encr_settings_len, 1) || + wps_validate_version2(attr.version2, wps2) || + wps_validate_authenticator(attr.authenticator, 1)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M5"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_m5_encr(const struct wpabuf *tlvs, int wps2) +{ + struct wps_parse_attr attr; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M5 encrypted " + "settings"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in M5 encrypted settings"); + return -1; + } + + if (wps_validate_e_snonce1(attr.e_snonce1, 1) || + wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M5 encrypted " + "settings"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_m6(const struct wpabuf *tlvs) +{ + struct wps_parse_attr attr; + int wps2; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M6"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in M6"); + return -1; + } + + wps2 = attr.version2 != NULL; + if (wps_validate_version(attr.version, 1) || + wps_validate_msg_type(attr.msg_type, 1) || + wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) || + wps_validate_encr_settings(attr.encr_settings, + attr.encr_settings_len, 1) || + wps_validate_version2(attr.version2, wps2) || + wps_validate_authenticator(attr.authenticator, 1)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M6"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_m6_encr(const struct wpabuf *tlvs, int wps2) +{ + struct wps_parse_attr attr; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M6 encrypted " + "settings"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in M6 encrypted settings"); + return -1; + } + + if (wps_validate_r_snonce2(attr.r_snonce2, 1) || + wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M6 encrypted " + "settings"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_m7(const struct wpabuf *tlvs) +{ + struct wps_parse_attr attr; + int wps2; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M7"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in M7"); + return -1; + } + + wps2 = attr.version2 != NULL; + if (wps_validate_version(attr.version, 1) || + wps_validate_msg_type(attr.msg_type, 1) || + wps_validate_registrar_nonce(attr.registrar_nonce, 1) || + wps_validate_encr_settings(attr.encr_settings, + attr.encr_settings_len, 1) || + wps_validate_settings_delay_time(attr.settings_delay_time, 0) || + wps_validate_version2(attr.version2, wps2) || + wps_validate_authenticator(attr.authenticator, 1)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M7"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap, int wps2) +{ + struct wps_parse_attr attr; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M7 encrypted " + "settings"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in M7 encrypted settings"); + return -1; + } + + if (wps_validate_e_snonce2(attr.e_snonce2, 1) || + wps_validate_ssid(attr.ssid, attr.ssid_len, !ap) || + wps_validate_mac_addr(attr.mac_addr, !ap) || + wps_validate_auth_type(attr.auth_type, !ap) || + wps_validate_encr_type(attr.encr_type, !ap) || + wps_validate_network_key_index(attr.network_key_idx, 0) || + wps_validate_network_key(attr.network_key, attr.network_key_len, + attr.encr_type, !ap) || + wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M7 encrypted " + "settings"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_m8(const struct wpabuf *tlvs) +{ + struct wps_parse_attr attr; + int wps2; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M8"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in M8"); + return -1; + } + + wps2 = attr.version2 != NULL; + if (wps_validate_version(attr.version, 1) || + wps_validate_msg_type(attr.msg_type, 1) || + wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) || + wps_validate_encr_settings(attr.encr_settings, + attr.encr_settings_len, 1) || + wps_validate_version2(attr.version2, wps2) || + wps_validate_authenticator(attr.authenticator, 1)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M8"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap, int wps2) +{ + struct wps_parse_attr attr; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M8 encrypted " + "settings"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in M8 encrypted settings"); + return -1; + } + + if (wps_validate_ssid(attr.ssid, attr.ssid_len, ap) || + wps_validate_auth_type(attr.auth_type, ap) || + wps_validate_encr_type(attr.encr_type, ap) || + wps_validate_network_key_index(attr.network_key_idx, 0) || + wps_validate_mac_addr(attr.mac_addr, ap) || + wps_validate_credential(attr.cred, attr.cred_len, attr.num_cred, + !ap) || + wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M8 encrypted " + "settings"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_wsc_ack(const struct wpabuf *tlvs) +{ + struct wps_parse_attr attr; + int wps2; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in WSC_ACK"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in WSC_ACK"); + return -1; + } + + wps2 = attr.version2 != NULL; + if (wps_validate_version(attr.version, 1) || + wps_validate_msg_type(attr.msg_type, 1) || + wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) || + wps_validate_registrar_nonce(attr.registrar_nonce, 1) || + wps_validate_version2(attr.version2, wps2)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC_ACK"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_wsc_nack(const struct wpabuf *tlvs) +{ + struct wps_parse_attr attr; + int wps2; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in WSC_NACK"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in WSC_NACK"); + return -1; + } + + wps2 = attr.version2 != NULL; + if (wps_validate_version(attr.version, 1) || + wps_validate_msg_type(attr.msg_type, 1) || + wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) || + wps_validate_registrar_nonce(attr.registrar_nonce, 1) || + wps_validate_config_error(attr.config_error, 1) || + wps_validate_version2(attr.version2, wps2)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC_NACK"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_wsc_done(const struct wpabuf *tlvs) +{ + struct wps_parse_attr attr; + int wps2; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in WSC_Done"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in WSC_Done"); + return -1; + } + + wps2 = attr.version2 != NULL; + if (wps_validate_version(attr.version, 1) || + wps_validate_msg_type(attr.msg_type, 1) || + wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) || + wps_validate_registrar_nonce(attr.registrar_nonce, 1) || + wps_validate_version2(attr.version2, wps2)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC_Done"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_upnp_set_selected_registrar(const struct wpabuf *tlvs) +{ + struct wps_parse_attr attr; + int wps2; + int sel_reg; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in " + "SetSelectedRegistrar"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in SetSelectedRegistrar"); + return -1; + } + + wps2 = attr.version2 != NULL; + sel_reg = attr.selected_registrar != NULL && + *attr.selected_registrar != 0; + if (wps_validate_version(attr.version, 1) || + wps_validate_dev_password_id(attr.dev_password_id, sel_reg) || + wps_validate_sel_reg_config_methods(attr.sel_reg_config_methods, + wps2, sel_reg) || + wps_validate_version2(attr.version2, wps2) || + wps_validate_authorized_macs(attr.authorized_macs, + attr.authorized_macs_len, wps2) || + wps_validate_uuid_r(attr.uuid_r, wps2)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid " + "SetSelectedRegistrar"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} |