#include "common/gas.h"
#include "common/wpa_ctrl.h"
#include "utils/pcsc_funcs.h"
+#include "utils/eloop.h"
#include "drivers/driver.h"
#include "eap_common/eap_defs.h"
#include "eap_peer/eap.h"
#endif
static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s);
+static struct wpa_cred * interworking_credentials_available_realm(
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
+static struct wpa_cred * interworking_credentials_available_3gpp(
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
static void interworking_reconnect(struct wpa_supplicant *wpa_s)
wpa_s->disconnected = 0;
wpa_s->reassociate = 1;
- if (wpa_s->last_scan_res_used > 0) {
- struct os_time now;
- os_get_time(&now);
- if (now.sec - wpa_s->last_scan.sec <= 5) {
- wpa_printf(MSG_DEBUG, "Interworking: Old scan results "
- "are fresh - connect without new scan");
- if (wpas_select_network_from_last_scan(wpa_s) >= 0)
- return;
- }
- }
+ if (wpa_supplicant_fast_associate(wpa_s) >= 0)
+ return;
wpa_supplicant_req_scan(wpa_s, 0, 0);
}
}
+static void interworking_continue_anqp(void *eloop_ctx, void *sock_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ interworking_next_anqp_fetch(wpa_s);
+}
+
+
static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss)
{
if (res < 0) {
wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
ret = -1;
+ eloop_register_timeout(0, 0, interworking_continue_anqp, wpa_s,
+ NULL);
} else
wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
"%u", res);
return 0;
}
- if (eap->method == EAP_TYPE_PEAP &&
- eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL)
- return 0;
+ if (eap->method == EAP_TYPE_PEAP) {
+ if (eap->inner_method &&
+ eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL)
+ return 0;
+ if (!eap->inner_method &&
+ eap_get_name(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2) == NULL)
+ return 0;
+ }
if (eap->method == EAP_TYPE_TTLS) {
if (eap->inner_method == 0 && eap->inner_non_eap == 0)
- return 0;
+ return 1; /* Assume TTLS/MSCHAPv2 is used */
if (eap->inner_method &&
eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL)
return 0;
#endif /* INTERWORKING_3GPP */
-static int interworking_set_hs20_params(struct wpa_ssid *ssid)
+static int interworking_set_hs20_params(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
{
- if (wpa_config_set(ssid, "key_mgmt", "WPA-EAP", 0) < 0)
+ if (wpa_config_set(ssid, "key_mgmt",
+ wpa_s->conf->pmf != NO_MGMT_FRAME_PROTECTION ?
+ "WPA-EAP WPA-EAP-SHA256" : "WPA-EAP", 0) < 0)
return -1;
if (wpa_config_set(ssid, "proto", "RSN", 0) < 0)
return -1;
static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s,
+ struct wpa_cred *cred,
struct wpa_bss *bss)
{
#ifdef INTERWORKING_3GPP
- struct wpa_cred *cred;
struct wpa_ssid *ssid;
const u8 *ie;
int eap_type;
if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL)
return -1;
- for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
- char *sep;
- const char *imsi;
- int mnc_len;
-
-#ifdef PCSC_FUNCS
- if (cred->pcsc && wpa_s->conf->pcsc_reader && wpa_s->scard &&
- wpa_s->imsi[0]) {
- imsi = wpa_s->imsi;
- mnc_len = wpa_s->mnc_len;
- goto compare;
- }
-#endif /* PCSC_FUNCS */
-
- if (cred->imsi == NULL || !cred->imsi[0] ||
- cred->milenage == NULL || !cred->milenage[0])
- continue;
-
- sep = os_strchr(cred->imsi, '-');
- if (sep == NULL ||
- (sep - cred->imsi != 5 && sep - cred->imsi != 6))
- continue;
- mnc_len = sep - cred->imsi - 3;
- imsi = cred->imsi;
-
-#ifdef PCSC_FUNCS
- compare:
-#endif /* PCSC_FUNCS */
- if (plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len))
- break;
- }
- if (cred == NULL)
- return -1;
-
ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
if (ie == NULL)
return -1;
os_memcpy(ssid->ssid, ie + 2, ie[1]);
ssid->ssid_len = ie[1];
- if (interworking_set_hs20_params(ssid) < 0)
+ if (interworking_set_hs20_params(wpa_s, ssid) < 0)
goto fail;
eap_type = EAP_TYPE_SIM;
}
+static int cred_excluded_ssid(struct wpa_cred *cred, struct wpa_bss *bss)
+{
+ size_t i;
+
+ if (!cred->excluded_ssid)
+ return 0;
+
+ for (i = 0; i < cred->num_excluded_ssid; i++) {
+ struct excluded_ssid *e = &cred->excluded_ssid[i];
+ if (bss->ssid_len == e->ssid_len &&
+ os_memcmp(bss->ssid, e->ssid, e->ssid_len) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+
static struct wpa_cred * interworking_credentials_available_roaming_consortium(
struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
{
cred->roaming_consortium_len))
continue;
+ if (cred_excluded_ssid(cred, bss))
+ continue;
+
if (selected == NULL ||
selected->priority < cred->priority)
selected = cred;
os_memcpy(ssid->ssid, ssid_ie + 2, ssid_ie[1]);
ssid->ssid_len = ssid_ie[1];
- if (interworking_set_hs20_params(ssid) < 0)
+ if (interworking_set_hs20_params(wpa_s, ssid) < 0)
goto fail;
if (cred->eap_method == NULL) {
int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
{
- struct wpa_cred *cred;
+ struct wpa_cred *cred, *cred_rc, *cred_3gpp;
struct wpa_ssid *ssid;
struct nai_realm *realm;
struct nai_realm_eap *eap = NULL;
return -1;
}
- cred = interworking_credentials_available_roaming_consortium(wpa_s,
- bss);
- if (cred)
- return interworking_connect_roaming_consortium(wpa_s, cred,
+ cred_rc = interworking_credentials_available_roaming_consortium(wpa_s,
+ bss);
+ if (cred_rc) {
+ wpa_printf(MSG_DEBUG, "Interworking: Highest roaming "
+ "consortium matching credential priority %d",
+ cred_rc->priority);
+ }
+
+ cred = interworking_credentials_available_realm(wpa_s, bss);
+ if (cred) {
+ wpa_printf(MSG_DEBUG, "Interworking: Highest NAI Realm list "
+ "matching credential priority %d",
+ cred->priority);
+ }
+
+ cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss);
+ if (cred_3gpp) {
+ wpa_printf(MSG_DEBUG, "Interworking: Highest 3GPP matching "
+ "credential priority %d", cred_3gpp->priority);
+ }
+
+ if (cred_rc &&
+ (cred == NULL || cred_rc->priority >= cred->priority) &&
+ (cred_3gpp == NULL || cred_rc->priority >= cred_3gpp->priority))
+ return interworking_connect_roaming_consortium(wpa_s, cred_rc,
bss, ie);
+ if (cred_3gpp &&
+ (cred == NULL || cred_3gpp->priority >= cred->priority)) {
+ return interworking_connect_3gpp(wpa_s, cred_3gpp, bss);
+ }
+
+ if (cred == NULL) {
+ wpa_printf(MSG_DEBUG, "Interworking: No matching credentials "
+ "found for " MACSTR, MAC2STR(bss->bssid));
+ return -1;
+ }
+
realm = nai_realm_parse(bss->anqp ? bss->anqp->nai_realm : NULL,
&count);
if (realm == NULL) {
wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
"Realm list from " MACSTR, MAC2STR(bss->bssid));
- count = 0;
+ return -1;
}
- for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
- for (i = 0; i < count; i++) {
- if (!nai_realm_match(&realm[i], cred->realm))
- continue;
- eap = nai_realm_find_eap(cred, &realm[i]);
- if (eap)
- break;
- }
+ for (i = 0; i < count; i++) {
+ if (!nai_realm_match(&realm[i], cred->realm))
+ continue;
+ eap = nai_realm_find_eap(cred, &realm[i]);
if (eap)
break;
}
if (!eap) {
- if (interworking_connect_3gpp(wpa_s, bss) == 0) {
- if (realm)
- nai_realm_free(realm, count);
- return 0;
- }
-
wpa_printf(MSG_DEBUG, "Interworking: No matching credentials "
"and EAP method found for " MACSTR,
MAC2STR(bss->bssid));
os_memcpy(ssid->ssid, ie + 2, ie[1]);
ssid->ssid_len = ie[1];
- if (interworking_set_hs20_params(ssid) < 0)
+ if (interworking_set_hs20_params(wpa_s, ssid) < 0)
goto fail;
if (wpa_config_set(ssid, "eap", eap_get_name(EAP_VENDOR_IETF,
0) < 0)
goto fail;
break;
+ default:
+ /* EAP params were not set - assume TTLS/MSCHAPv2 */
+ if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAPV2\"",
+ 0) < 0)
+ goto fail;
+ break;
}
break;
case EAP_TYPE_PEAP:
os_snprintf(buf, sizeof(buf), "\"auth=%s\"",
- eap_get_name(EAP_VENDOR_IETF, eap->inner_method));
+ eap_get_name(EAP_VENDOR_IETF,
+ eap->inner_method ?
+ eap->inner_method :
+ EAP_TYPE_MSCHAPV2));
if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
goto fail;
break;
static struct wpa_cred * interworking_credentials_available_3gpp(
struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
{
- struct wpa_cred *cred, *selected = NULL;
+ struct wpa_cred *selected = NULL;
+#ifdef INTERWORKING_3GPP
+ struct wpa_cred *cred;
int ret;
-#ifdef INTERWORKING_3GPP
if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL)
return NULL;
ret = plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len);
wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not ");
if (ret) {
+ if (cred_excluded_ssid(cred, bss))
+ continue;
if (selected == NULL ||
selected->priority < cred->priority)
selected = cred;
if (!nai_realm_match(&realm[i], cred->realm))
continue;
if (nai_realm_find_eap(cred, &realm[i])) {
+ if (cred_excluded_ssid(cred, bss))
+ continue;
if (selected == NULL ||
selected->priority < cred->priority)
selected = cred;
mnc_len = wpa_s->mnc_len;
}
#endif /* CONFIG_PCSC */
- if (imsi && build_root_nai(nai, sizeof(nai), imsi, mnc_len, 0) == 0) {
+ if (domain_names &&
+ imsi && build_root_nai(nai, sizeof(nai), imsi, mnc_len, 0) == 0) {
realm = os_strchr(nai, '@');
if (realm)
realm++;
}
#endif /* INTERWORKING_3GPP */
- if (cred->domain == NULL)
+ if (domain_names == NULL || cred->domain == NULL)
return 0;
wpa_printf(MSG_DEBUG, "Interworking: Search for match with "
continue;
if (other->anqp == NULL)
continue;
+ if (other->anqp->roaming_consortium == NULL &&
+ other->anqp->nai_realm == NULL &&
+ other->anqp->anqp_3gpp == NULL &&
+ other->anqp->domain_name == NULL)
+ continue;
if (!(other->flags & WPA_BSS_ANQP_FETCH_TRIED))
continue;
if (os_memcmp(bss->hessid, other->hessid, ETH_ALEN) != 0)
int found = 0;
const u8 *ie;
- if (!wpa_s->fetch_anqp_in_progress)
+ if (eloop_terminated() || !wpa_s->fetch_anqp_in_progress)
return;
dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
wpa_printf(MSG_DEBUG, "Interworking: Start scan for network "
"selection");
wpa_s->scan_res_handler = interworking_scan_res_handler;
- wpa_s->scan_req = 2;
+ wpa_s->scan_req = MANUAL_SCAN_REQ;
wpa_supplicant_req_scan(wpa_s, 0, 0);
return 0;