diff options
author | Dmitry Shmidt <dimitrysh@google.com> | 2012-12-03 15:08:10 -0800 |
---|---|---|
committer | Dmitry Shmidt <dimitrysh@google.com> | 2012-12-20 10:35:12 -0800 |
commit | d5e4923d04122f81300fa68fb07d64ede28fd44d (patch) | |
tree | 29f99bf419804daf82e32e67911628df0c2e5f39 /src/ap | |
parent | 90f44d5ff88189e0972bcd376b662d898e4c3c2e (diff) | |
download | android_external_wpa_supplicant_8-d5e4923d04122f81300fa68fb07d64ede28fd44d.tar.gz android_external_wpa_supplicant_8-d5e4923d04122f81300fa68fb07d64ede28fd44d.tar.bz2 android_external_wpa_supplicant_8-d5e4923d04122f81300fa68fb07d64ede28fd44d.zip |
Accumulative patch from commit f5f37d3a4fc2df2a24676b4f95afca15ed793cba
Author: Jouni Malinen <j@w1.fi>
Date: Sun Nov 25 22:05:32 2012 +0200
Fix REAUTHENTICATE command after PMKSA caching
The current PMKSA cache entry needs to be clear to allow EAPOL
reauthentication to be started in case this association used PMKSA
caching.
- Remove old WPS_OOB NCF
- WPS: Add preliminary NFC connection handover support for Enrollee
- WPS: Reenable the networks disabled during wpa_wpas_reassoc
- P2P: Avoid multi-channel scans when they are not needed
- P2P: Allow discoverable interval for p2p_find to be configured
- P2P: Allow all channels with multi-channel concurrency
- Bonjour changes
- Remove disassociate
- HS 2.0 changes
- Add preliminary support for using SQLite for eap_user database
- Add SAE support
- Add disallow_aps parameter to disallow BSSIDs/SSIDs
Change-Id: I85358a05b39d46b8db49acdad667e771c580b05c
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
Diffstat (limited to 'src/ap')
-rw-r--r-- | src/ap/ap_config.c | 55 | ||||
-rw-r--r-- | src/ap/ap_config.h | 10 | ||||
-rw-r--r-- | src/ap/authsrv.c | 2 | ||||
-rw-r--r-- | src/ap/ctrl_iface_ap.c | 26 | ||||
-rw-r--r-- | src/ap/drv_callbacks.c | 18 | ||||
-rw-r--r-- | src/ap/eap_user_db.c | 270 | ||||
-rw-r--r-- | src/ap/hostapd.c | 12 | ||||
-rw-r--r-- | src/ap/hostapd.h | 9 | ||||
-rw-r--r-- | src/ap/ieee802_11.c | 210 | ||||
-rw-r--r-- | src/ap/ieee802_11_auth.c | 135 | ||||
-rw-r--r-- | src/ap/ieee802_11_auth.h | 5 | ||||
-rw-r--r-- | src/ap/ieee802_11_vht.c | 2 | ||||
-rw-r--r-- | src/ap/ieee802_1x.c | 7 | ||||
-rw-r--r-- | src/ap/pmksa_cache_auth.c | 6 | ||||
-rw-r--r-- | src/ap/sta_info.c | 4 | ||||
-rw-r--r-- | src/ap/sta_info.h | 11 | ||||
-rw-r--r-- | src/ap/tkip_countermeasures.c | 11 | ||||
-rw-r--r-- | src/ap/tkip_countermeasures.h | 4 | ||||
-rw-r--r-- | src/ap/wpa_auth.c | 28 | ||||
-rw-r--r-- | src/ap/wpa_auth.h | 4 | ||||
-rw-r--r-- | src/ap/wpa_auth_glue.c | 18 | ||||
-rw-r--r-- | src/ap/wpa_auth_ie.c | 24 | ||||
-rw-r--r-- | src/ap/wps_hostapd.c | 58 | ||||
-rw-r--r-- | src/ap/wps_hostapd.h | 2 |
24 files changed, 723 insertions, 208 deletions
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index 31e1c19c..3c699f71 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -408,6 +408,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); @@ -619,57 +620,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 f5e4a6a5..71313c02 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -96,6 +96,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; @@ -192,6 +197,7 @@ 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; @@ -505,6 +511,7 @@ struct hostapd_config { int require_vht; u8 vht_oper_chwidth; u8 vht_oper_centr_freq_seg0_idx; + u8 vht_oper_centr_freq_seg1_idx; }; @@ -523,9 +530,6 @@ 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); diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c index 5c03f45c..d66d97e4 100644 --- a/src/ap/authsrv.c +++ b/src/ap/authsrv.c @@ -92,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; diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c index ab9c83eb..c55d3fe3 100644 --- a/src/ap/ctrl_iface_ap.c +++ b/src/ap/ctrl_iface_ap.c @@ -21,6 +21,28 @@ #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, struct sta_info *sta, char *buf, size_t buflen) @@ -58,6 +80,10 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, if (res >= 0) len += res; + res = hostapd_get_sta_conn_time(sta, buf + len, buflen - len); + if (res >= 0) + len += res; + return len; } diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index 23fa241a..86139752 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -109,6 +109,15 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, } #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 @@ -672,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; + } } } diff --git a/src/ap/eap_user_db.c b/src/ap/eap_user_db.c new file mode 100644 index 00000000..79d50e51 --- /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/hostapd.c b/src/ap/hostapd.c index 34292587..cef9dafc 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -273,6 +273,11 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd) #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 */ } @@ -1113,12 +1118,13 @@ int hostapd_reload_iface(struct hostapd_iface *hapd_iface) int hostapd_disable_iface(struct hostapd_iface *hapd_iface) { size_t j; - struct hostapd_bss_config *bss = hapd_iface->bss[0]->conf; + 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; @@ -1373,8 +1379,10 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, /* 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); diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index 71f476c0..f1e7d9ff 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -10,6 +10,7 @@ #define HOSTAPD_H #include "common/defs.h" +#include "ap_config.h" struct wpa_driver_ops; struct wpa_ctrl_dst; @@ -187,6 +188,10 @@ struct hostapd_data { #ifdef CONFIG_INTERWORKING size_t gas_frag_limit; #endif /* CONFIG_INTERWORKING */ + +#ifdef CONFIG_SQLITE + struct hostapd_eap_user tmp_eap_user; +#endif /* CONFIG_SQLITE */ }; @@ -297,4 +302,8 @@ int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da, 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/ieee802_11.c b/src/ap/ieee802_11.c index ce20e5f8..a13a135e 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -49,6 +49,8 @@ u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *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) { /* rest of the rates are encoded in Extended supported * rates element */ @@ -66,9 +68,15 @@ u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) pos++; } - if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && - hapd->iface->num_rates < 8) + 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; } @@ -85,6 +93,8 @@ u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *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; @@ -103,9 +113,17 @@ u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid) pos++; } - if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && - hapd->iface->num_rates >= 8) - *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY; + 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; } @@ -296,6 +314,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) { @@ -307,8 +461,7 @@ static void handle_auth(struct hostapd_data *hapd, const u8 *challenge = NULL; u32 session_timeout, acct_interim_interval; int vlan_id = 0; - u8 psk[PMK_LEN]; - int has_psk = 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; @@ -348,6 +501,10 @@ static void handle_auth(struct hostapd_data *hapd, (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", @@ -356,7 +513,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); @@ -374,7 +531,7 @@ 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, - psk, &has_psk, &identity, &radius_cui); + &psk, &identity, &radius_cui); if (res == HOSTAPD_ACL_REJECT) { printf("Station " MACSTR " not allowed to authenticate.\n", @@ -413,13 +570,11 @@ static void handle_auth(struct hostapd_data *hapd, HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id); } - if (has_psk && hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) { - os_free(sta->psk); - sta->psk = os_malloc(PMK_LEN); - if (sta->psk) - os_memcpy(sta->psk, psk, PMK_LEN); + hostapd_free_psk_list(sta->psk); + if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) { + sta->psk = psk; + psk = NULL; } else { - os_free(sta->psk); sta->psk = NULL; } @@ -486,11 +641,17 @@ 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); @@ -779,6 +940,16 @@ 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 | WLAN_STA_VHT)) && wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) { @@ -807,6 +978,15 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, 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; } diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c index 63ae3452..c311e559 100644 --- a/src/ap/ieee802_11_auth.c +++ b/src/ap/ieee802_11_auth.c @@ -36,8 +36,7 @@ struct hostapd_cached_radius_acl { u32 session_timeout; u32 acct_interim_interval; int vlan_id; - int has_psk; - u8 psk[PMK_LEN]; + struct hostapd_sta_wpa_psk_short *psk; char *identity; char *radius_cui; }; @@ -58,6 +57,7 @@ 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); } @@ -74,11 +74,34 @@ static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache) } +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, - u8 *psk, int *has_psk, char **identity, - char **radius_cui) + struct hostapd_sta_wpa_psk_short **psk, + char **identity, char **radius_cui) { struct hostapd_cached_radius_acl *entry; struct os_time now; @@ -99,10 +122,7 @@ static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr, entry->acct_interim_interval; if (vlan_id) *vlan_id = entry->vlan_id; - if (psk) - os_memcpy(psk, entry->psk, PMK_LEN); - if (has_psk) - *has_psk = entry->has_psk; + copy_psk_list(psk, entry->psk); if (identity) { if (entry->identity) *identity = os_strdup(entry->identity); @@ -200,8 +220,7 @@ 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: Buffer for returning WPA PSK - * @has_psk: Buffer for indicating whether psk was filled + * @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 @@ -212,8 +231,8 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, 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, - u8 *psk, int *has_psk, char **identity, - char **radius_cui) + struct hostapd_sta_wpa_psk_short **psk, + char **identity, char **radius_cui) { if (session_timeout) *session_timeout = 0; @@ -221,10 +240,8 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, *acct_interim_interval = 0; if (vlan_id) *vlan_id = 0; - if (has_psk) - *has_psk = 0; if (psk) - os_memset(psk, 0, PMK_LEN); + *psk = NULL; if (identity) *identity = NULL; if (radius_cui) @@ -253,7 +270,7 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, /* 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, psk, has_psk, + vlan_id, psk, identity, radius_cui); if (res == HOSTAPD_ACL_ACCEPT || res == HOSTAPD_ACL_ACCEPT_TIMEOUT) @@ -396,6 +413,54 @@ static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx) } +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 @@ -454,8 +519,6 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, cache->timestamp = t.sec; os_memcpy(cache->addr, query->addr, sizeof(cache->addr)); if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) { - int passphraselen; - char *passphrase; u8 *buf; size_t len; @@ -478,27 +541,9 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, cache->vlan_id = radius_msg_get_vlanid(msg); - passphrase = radius_msg_get_tunnel_password( - msg, &passphraselen, - hapd->conf->radius->auth_server->shared_secret, - hapd->conf->radius->auth_server->shared_secret_len, - req); - cache->has_psk = passphrase != NULL; - if (passphrase != NULL) { - /* passphrase does not contain the NULL termination. - * Add it here as pbkdf2_sha1 requires it. */ - char *strpassphrase = os_zalloc(passphraselen + 1); - if (strpassphrase) { - os_memcpy(strpassphrase, passphrase, - passphraselen); - pbkdf2_sha1(strpassphrase, - hapd->conf->ssid.ssid, - hapd->conf->ssid.ssid_len, 4096, - cache->psk, PMK_LEN); - os_free(strpassphrase); - } - os_free(passphrase); - } + 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); @@ -514,7 +559,7 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, } if (hapd->conf->wpa_psk_radius == PSK_RADIUS_REQUIRED && - !cache->has_psk) + !cache->psk) cache->accepted = HOSTAPD_ACL_REJECT; } else cache->accepted = HOSTAPD_ACL_REJECT; @@ -586,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 0e8d1cb1..2bc1065a 100644 --- a/src/ap/ieee802_11_auth.h +++ b/src/ap/ieee802_11_auth.h @@ -19,9 +19,10 @@ 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, - u8 *psk, int *has_psk, char **identity, - char **radius_cui); + 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_vht.c b/src/ap/ieee802_11_vht.c index 7599ef8d..b21c2b7f 100644 --- a/src/ap/ieee802_11_vht.c +++ b/src/ap/ieee802_11_vht.c @@ -68,6 +68,8 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid) */ 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; diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c index c4d3da82..e1b11ba1 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -99,8 +99,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); + } } @@ -1684,8 +1686,7 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity, const struct hostapd_eap_user *eap_user; 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; diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c index ba2c033a..3a9cc7b4 100644 --- a/src/ap/pmksa_cache_auth.c +++ b/src/ap/pmksa_cache_auth.c @@ -95,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); diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index d61177fd..6bc43d2d 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -20,6 +20,7 @@ #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" @@ -232,9 +233,10 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) wpabuf_free(sta->wps_ie); wpabuf_free(sta->p2p_ie); + wpabuf_free(sta->hs20_ie); os_free(sta->ht_capabilities); - os_free(sta->psk); + hostapd_free_psk_list(sta->psk); os_free(sta->identity); os_free(sta->radius_cui); diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index b3c57b43..d5e92faa 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -95,7 +95,8 @@ struct sta_info { struct hostapd_ssid *ssid_probe; /* SSID selection based on ProbeReq */ int vlan_id; - u8 *psk; /* PSK from RADIUS authentication server */ + /* 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 */ @@ -121,6 +122,14 @@ struct sta_info { 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 */ }; diff --git a/src/ap/tkip_countermeasures.c b/src/ap/tkip_countermeasures.c index dd5aa687..4a2ea066 100644 --- a/src/ap/tkip_countermeasures.c +++ b/src/ap/tkip_countermeasures.c @@ -66,9 +66,10 @@ void ieee80211_tkip_countermeasures_deinit(struct hostapd_data *hapd) } -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) { struct os_time now; + int ret = 0; if (addr && local) { struct sta_info *sta = ap_get_sta(hapd, addr); @@ -84,7 +85,7 @@ 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; } } @@ -93,8 +94,12 @@ void michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local) 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.sec; + + return ret; } diff --git a/src/ap/tkip_countermeasures.h b/src/ap/tkip_countermeasures.h index f7a66246..d3eaed3c 100644 --- a/src/ap/tkip_countermeasures.h +++ b/src/ap/tkip_countermeasures.h @@ -1,6 +1,6 @@ /* * hostapd / TKIP countermeasures - * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -9,7 +9,7 @@ #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/wpa_auth.c b/src/ap/wpa_auth.c index 49d81757..0816b25b 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -54,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; } @@ -700,8 +701,8 @@ static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth, #endif /* CONFIG_IEEE80211R */ -static void wpa_receive_error_report(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, int group) +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, @@ -718,7 +719,8 @@ static void wpa_receive_error_report(struct wpa_authenticator *wpa_auth, "ignore Michael MIC failure report since " "pairwise cipher is not TKIP"); } else { - wpa_auth_mic_failure_report(wpa_auth, sm->addr); + if (wpa_auth_mic_failure_report(wpa_auth, sm->addr) > 0) + return 1; /* STA entry was removed */ sm->dot11RSNAStatsTKIPRemoteMICFailures++; wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++; } @@ -728,6 +730,7 @@ static void wpa_receive_error_report(struct wpa_authenticator *wpa_auth, * Authenticator may do it, let's change the keys now anyway. */ wpa_request_new_ptk(sm); + return 0; } @@ -1081,9 +1084,10 @@ continue_processing: #endif /* CONFIG_PEERKEY */ return; } else if (key_info & WPA_KEY_INFO_ERROR) { - wpa_receive_error_report( - wpa_auth, sm, - !(key_info & WPA_KEY_INFO_KEY_TYPE)); + 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 " @@ -3056,3 +3060,11 @@ void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth, 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 91ba4991..6ab170d1 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -177,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); @@ -291,4 +291,6 @@ int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos); #endif /* CONFIG_IEEE80211W */ #endif /* CONFIG_IEEE80211V */ +int wpa_auth_uses_sae(struct wpa_state_machine *sm); + #endif /* WPA_AUTH_H */ diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index bdc89e4f..76c61ea1 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -112,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,10 +188,18 @@ static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr, /* * 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; sta->psk) + * logic list (all hostapd_get_psk; all sta->psk) */ - if (sta && sta->psk && !psk && sta->psk != prev_psk) - psk = 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; } diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c index 17862305..4fd0135f 100644 --- a/src/ap/wpa_auth_ie.c +++ b/src/ap/wpa_auth_ie.c @@ -188,6 +188,18 @@ 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) { @@ -407,6 +419,12 @@ 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) @@ -479,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 diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c index 5e44c72c..85633ec5 100644 --- a/src/ap/wps_hostapd.c +++ b/src/ap/wps_hostapd.c @@ -11,8 +11,6 @@ #include "utils/common.h" #include "utils/eloop.h" #include "utils/uuid.h" -#include "crypto/dh_groups.h" -#include "crypto/dh_group5.h" #include "common/wpa_ctrl.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" @@ -1036,8 +1034,6 @@ 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); @@ -1155,60 +1151,6 @@ int hostapd_wps_cancel(struct hostapd_data *hapd) } -#ifdef CONFIG_WPS_OOB -int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type, - char *path, char *method, char *name) -{ - struct wps_context *wps = hapd->wps; - struct oob_device_data *oob_dev; - - 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; - - 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, NULL, "any", - wpabuf_head(wps->oob_conf.dev_password), 0) < - 0) - goto error; - - return 0; - -error: - wpabuf_free(wps->dh_pubkey); - wps->dh_pubkey = NULL; - wpabuf_free(wps->dh_privkey); - wps->dh_privkey = NULL; - return -1; -} -#endif /* CONFIG_WPS_OOB */ - - 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, diff --git a/src/ap/wps_hostapd.h b/src/ap/wps_hostapd.h index f968e151..4e5026b4 100644 --- a/src/ap/wps_hostapd.h +++ b/src/ap/wps_hostapd.h @@ -21,8 +21,6 @@ int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr, int hostapd_wps_button_pushed(struct hostapd_data *hapd, const u8 *p2p_dev_addr); int hostapd_wps_cancel(struct hostapd_data *hapd); -int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type, - char *path, char *method, char *name); int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr, char *buf, size_t buflen); void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd); |