Merge "Cumulative patch from commit 3e949655c"
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index b1ab13e..b26da71 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -2049,6 +2049,24 @@
}
+static int hs20_parse_osu_nai2(struct hostapd_bss_config *bss,
+ char *pos, int line)
+{
+ if (bss->last_osu == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
+ return -1;
+ }
+
+ os_free(bss->last_osu->osu_nai2);
+ bss->last_osu->osu_nai2 = os_strdup(pos);
+ if (bss->last_osu->osu_nai2 == NULL)
+ return -1;
+ bss->hs20_osu_providers_nai_count++;
+
+ return 0;
+}
+
+
static int hs20_parse_osu_method_list(struct hostapd_bss_config *bss, char *pos,
int line)
{
@@ -3761,6 +3779,9 @@
} else if (os_strcmp(buf, "osu_nai") == 0) {
if (hs20_parse_osu_nai(bss, pos, line) < 0)
return 1;
+ } else if (os_strcmp(buf, "osu_nai2") == 0) {
+ if (hs20_parse_osu_nai2(bss, pos, line) < 0)
+ return 1;
} else if (os_strcmp(buf, "osu_method_list") == 0) {
if (hs20_parse_osu_method_list(bss, pos, line) < 0)
return 1;
@@ -4087,6 +4108,8 @@
line, pos);
return 1;
}
+ } else if (os_strcmp(buf, "coloc_intf_reporting") == 0) {
+ bss->coloc_intf_reporting = atoi(pos);
#endif /* CONFIG_OWE */
} else {
wpa_printf(MSG_ERROR,
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index 2d68e88..e539a09 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -992,6 +992,42 @@
return ret;
}
+
+static int hostapd_ctrl_iface_coloc_intf_req(struct hostapd_data *hapd,
+ const char *cmd)
+{
+ u8 addr[ETH_ALEN];
+ struct sta_info *sta;
+ const char *pos;
+ unsigned int auto_report, timeout;
+
+ if (hwaddr_aton(cmd, addr)) {
+ wpa_printf(MSG_DEBUG, "Invalid STA MAC address");
+ return -1;
+ }
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG, "Station " MACSTR
+ " not found for Collocated Interference Request",
+ MAC2STR(addr));
+ return -1;
+ }
+
+ pos = cmd + 17;
+ if (*pos != ' ')
+ return -1;
+ pos++;
+ auto_report = atoi(pos);
+ pos = os_strchr(pos, ' ');
+ if (!pos)
+ return -1;
+ pos++;
+ timeout = atoi(pos);
+
+ return wnm_send_coloc_intf_req(hapd, sta, auto_report, timeout);
+}
+
#endif /* CONFIG_WNM_AP */
@@ -1386,6 +1422,12 @@
hostapd_disassoc_deny_mac(hapd);
} else if (os_strcasecmp(cmd, "accept_mac_file") == 0) {
hostapd_disassoc_accept_mac(hapd);
+ } else if (os_strncmp(cmd, "wme_ac_", 7) == 0 ||
+ os_strncmp(cmd, "wmm_ac_", 7) == 0) {
+ hapd->parameter_set_count++;
+ if (ieee802_11_update_beacons(hapd->iface))
+ wpa_printf(MSG_DEBUG,
+ "Failed to update beacons with WMM parameters");
}
}
@@ -2955,6 +2997,9 @@
} else if (os_strncmp(buf, "BSS_TM_REQ ", 11) == 0) {
if (hostapd_ctrl_iface_bss_tm_req(hapd, buf + 11))
reply_len = -1;
+ } else if (os_strncmp(buf, "COLOC_INTF_REQ ", 15) == 0) {
+ if (hostapd_ctrl_iface_coloc_intf_req(hapd, buf + 15))
+ reply_len = -1;
#endif /* CONFIG_WNM_AP */
} else if (os_strcmp(buf, "GET_CONFIG") == 0) {
reply_len = hostapd_ctrl_iface_get_config(hapd, reply,
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 70f9713..a005217 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -2227,12 +2227,15 @@
# OSU Providers
# One or more sets of following parameter. Each OSU provider is started by the
# mandatory osu_server_uri item. The other parameters add information for the
-# last added OSU provider.
+# last added OSU provider. osu_nai specifies the OSU_NAI value for OSEN
+# authentication when using a standalone OSU BSS. osu_nai2 specifies the OSU_NAI
+# value for OSEN authentication when using a shared BSS (Single SSID) for OSU.
#
#osu_server_uri=https://example.com/osu/
#osu_friendly_name=eng:Example operator
#osu_friendly_name=fin:Esimerkkipalveluntarjoaja
#osu_nai=anonymous@example.com
+#osu_nai2=anonymous@example.com
#osu_method_list=1 0
#osu_icon=icon32
#osu_icon=icon64
diff --git a/hs20/client/est.c b/hs20/client/est.c
index 9f1519b..b1aacb8 100644
--- a/hs20/client/est.c
+++ b/hs20/client/est.c
@@ -666,7 +666,6 @@
char *buf, *resp, *req, *req2;
size_t buflen, resp_len, len, pkcs7_len;
unsigned char *pkcs7;
- FILE *f;
char client_cert_buf[200];
char client_key_buf[200];
const char *client_cert = NULL, *client_key = NULL;
@@ -721,11 +720,6 @@
return -1;
}
wpa_printf(MSG_DEBUG, "EST simpleenroll response: %s", resp);
- f = fopen("Cert/est-resp.raw", "w");
- if (f) {
- fwrite(resp, resp_len, 1, f);
- fclose(f);
- }
pkcs7 = base64_decode((unsigned char *) resp, resp_len, &pkcs7_len);
if (pkcs7 == NULL) {
diff --git a/hs20/client/osu_client.c b/hs20/client/osu_client.c
index a7ddd19..9e1b0c7 100644
--- a/hs20/client/osu_client.c
+++ b/hs20/client/osu_client.c
@@ -293,7 +293,6 @@
unlink("Cert/est-req.b64");
unlink("Cert/est-req.pem");
- unlink("Cert/est-resp.raw");
rmdir("Cert");
return 0;
@@ -437,7 +436,7 @@
if (node == NULL) {
wpa_printf(MSG_INFO, "No Policy/PolicyUpdate/TrustRoot/CertURL found from PPS");
xml_node_free(ctx->xml, pps);
- return -1;
+ return -2;
}
ret = download_cert(ctx, node, ca_fname);
@@ -464,7 +463,7 @@
if (node == NULL) {
wpa_printf(MSG_INFO, "No AAAServerTrustRoot/CertURL found from PPS");
xml_node_free(ctx->xml, pps);
- return -1;
+ return -2;
}
aaa = xml_node_first_child(ctx->xml, node);
@@ -486,7 +485,7 @@
{
char *dir, *pos;
char fname[300];
- int ret;
+ int ret, ret1;
dir = os_strdup(pps_fname);
if (dir == NULL)
@@ -501,9 +500,13 @@
snprintf(fname, sizeof(fname), "%s/ca.pem", dir);
ret = cmd_dl_osu_ca(ctx, pps_fname, fname);
snprintf(fname, sizeof(fname), "%s/polupd-ca.pem", dir);
- cmd_dl_polupd_ca(ctx, pps_fname, fname);
+ ret1 = cmd_dl_polupd_ca(ctx, pps_fname, fname);
+ if (ret == 0 && ret1 == -1)
+ ret = -1;
snprintf(fname, sizeof(fname), "%s/aaa-ca.pem", dir);
- cmd_dl_aaa_ca(ctx, pps_fname, fname);
+ ret1 = cmd_dl_aaa_ca(ctx, pps_fname, fname);
+ if (ret == 0 && ret1 == -1)
+ ret = -1;
os_free(dir);
@@ -1986,7 +1989,9 @@
char url[256];
unsigned int methods;
char osu_ssid[33];
+ char osu_ssid2[33];
char osu_nai[256];
+ char osu_nai2[256];
struct osu_lang_text friendly_name[MAX_OSU_VALS];
size_t friendly_name_count;
struct osu_lang_text serv_desc[MAX_OSU_VALS];
@@ -2045,12 +2050,24 @@
continue;
}
+ if (strncmp(buf, "osu_ssid2=", 10) == 0) {
+ snprintf(last->osu_ssid2, sizeof(last->osu_ssid2),
+ "%s", buf + 10);
+ continue;
+ }
+
if (os_strncmp(buf, "osu_nai=", 8) == 0) {
os_snprintf(last->osu_nai, sizeof(last->osu_nai),
"%s", buf + 8);
continue;
}
+ if (os_strncmp(buf, "osu_nai2=", 9) == 0) {
+ os_snprintf(last->osu_nai2, sizeof(last->osu_nai2),
+ "%s", buf + 9);
+ continue;
+ }
+
if (strncmp(buf, "friendly_name=", 14) == 0) {
struct osu_lang_text *txt;
if (last->friendly_name_count == MAX_OSU_VALS)
@@ -2126,9 +2143,9 @@
static int osu_connect(struct hs20_osu_client *ctx, const char *bssid,
- const char *ssid, const char *url,
+ const char *ssid, const char *ssid2, const char *url,
unsigned int methods, int no_prod_assoc,
- const char *osu_nai)
+ const char *osu_nai, const char *osu_nai2)
{
int id;
const char *ifname = ctx->ifname;
@@ -2136,17 +2153,41 @@
struct wpa_ctrl *mon;
int res;
+ if (ssid2 && ssid2[0] == '\0')
+ ssid2 = NULL;
+
+ if (ctx->osu_ssid) {
+ if (os_strcmp(ssid, ctx->osu_ssid) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "Enforced OSU SSID matches ANQP info");
+ ssid2 = NULL;
+ } else if (ssid2 && os_strcmp(ssid2, ctx->osu_ssid) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "Enforced OSU SSID matches RSN[OSEN] info");
+ ssid = ssid2;
+ } else {
+ wpa_printf(MSG_INFO, "Enforced OSU SSID did not match");
+ write_summary(ctx, "Enforced OSU SSID did not match");
+ return -1;
+ }
+ }
+
id = add_network(ifname);
if (id < 0)
return -1;
if (set_network_quoted(ifname, id, "ssid", ssid) < 0)
return -1;
+ if (ssid2)
+ osu_nai = osu_nai2;
if (osu_nai && os_strlen(osu_nai) > 0) {
char dir[255], fname[300];
if (getcwd(dir, sizeof(dir)) == NULL)
return -1;
os_snprintf(fname, sizeof(fname), "%s/osu-ca.pem", dir);
+ if (ssid2 && set_network_quoted(ifname, id, "ssid", ssid2) < 0)
+ return -1;
+
if (set_network(ifname, id, "proto", "OSEN") < 0 ||
set_network(ifname, id, "key_mgmt", "OSEN") < 0 ||
set_network(ifname, id, "pairwise", "CCMP") < 0 ||
@@ -2156,6 +2197,10 @@
set_network_quoted(ifname, id, "identity", osu_nai) < 0 ||
set_network_quoted(ifname, id, "ca_cert", fname) < 0)
return -1;
+ } else if (ssid2) {
+ wpa_printf(MSG_INFO, "No OSU_NAI set for RSN[OSEN]");
+ write_summary(ctx, "No OSU_NAI set for RSN[OSEN]");
+ return -1;
} else {
if (set_network(ifname, id, "key_mgmt", "NONE") < 0)
return -1;
@@ -2331,8 +2376,12 @@
fprintf(f, "</table></a><br><small>BSSID: %s<br>\n"
"SSID: %s<br>\n",
last->bssid, last->osu_ssid);
+ if (last->osu_ssid2[0])
+ fprintf(f, "SSID2: %s<br>\n", last->osu_ssid2);
if (last->osu_nai[0])
fprintf(f, "NAI: %s<br>\n", last->osu_nai);
+ if (last->osu_nai2[0])
+ fprintf(f, "NAI2: %s<br>\n", last->osu_nai2);
fprintf(f, "URL: %s<br>\n"
"methods:%s%s<br>\n"
"</small></p>\n",
@@ -2359,6 +2408,8 @@
ret = 0;
wpa_printf(MSG_INFO, "BSSID: %s", last->bssid);
wpa_printf(MSG_INFO, "SSID: %s", last->osu_ssid);
+ if (last->osu_ssid2[0])
+ wpa_printf(MSG_INFO, "SSID2: %s", last->osu_ssid2);
wpa_printf(MSG_INFO, "URL: %s", last->url);
write_summary(ctx, "Selected OSU provider id=%d BSSID=%s SSID=%s URL=%s",
ret, last->bssid, last->osu_ssid, last->url);
@@ -2413,10 +2464,13 @@
"No supported OSU provisioning method");
ret = -1;
}
- } else if (connect)
+ } else if (connect) {
ret = osu_connect(ctx, last->bssid, last->osu_ssid,
+ last->osu_ssid2,
last->url, last->methods,
- no_prod_assoc, last->osu_nai);
+ no_prod_assoc, last->osu_nai,
+ last->osu_nai2);
+ }
} else
ret = -1;
@@ -3134,7 +3188,7 @@
return -1;
for (;;) {
- c = getopt(argc, argv, "df:hKNO:qr:s:S:tw:x:");
+ c = getopt(argc, argv, "df:hKNo:O:qr:s:S:tw:x:");
if (c < 0)
break;
switch (c) {
@@ -3151,6 +3205,9 @@
case 'N':
no_prod_assoc = 1;
break;
+ case 'o':
+ ctx.osu_ssid = optarg;
+ break;
case 'O':
friendly_name = optarg;
break;
diff --git a/hs20/client/osu_client.h b/hs20/client/osu_client.h
index 9a7059e..5c8e6d0 100644
--- a/hs20/client/osu_client.h
+++ b/hs20/client/osu_client.h
@@ -47,6 +47,7 @@
int client_cert_present;
char **server_dnsname;
size_t server_dnsname_count;
+ const char *osu_ssid; /* Enforced OSU_SSID for testing purposes */
#define WORKAROUND_OCSP_OPTIONAL 0x00000001
unsigned long int workarounds;
};
diff --git a/hs20/server/hs20_spp_server.c b/hs20/server/hs20_spp_server.c
index 591f66b..abd6867 100644
--- a/hs20/server/hs20_spp_server.c
+++ b/hs20/server/hs20_spp_server.c
@@ -70,6 +70,10 @@
ctx->addr = getenv("HS20ADDR");
if (ctx->addr)
debug_print(ctx, 1, "Connection from %s", ctx->addr);
+ ctx->test = getenv("HS20TEST");
+ if (ctx->test)
+ debug_print(ctx, 1, "Requested test functionality: %s",
+ ctx->test);
user = getenv("HS20USER");
if (user && strlen(user) == 0)
diff --git a/hs20/server/spp_server.c b/hs20/server/spp_server.c
index 51c1d96..e5af4c2 100644
--- a/hs20/server/spp_server.c
+++ b/hs20/server/spp_server.c
@@ -57,19 +57,26 @@
const char *user, const char *realm,
const char *sessionid, const char *pw,
const char *redirect_uri,
- enum hs20_session_operation operation)
+ enum hs20_session_operation operation,
+ const u8 *mac_addr)
{
char *sql;
int ret = 0;
+ char addr[20];
+ if (mac_addr)
+ snprintf(addr, sizeof(addr), MACSTR, MAC2STR(mac_addr));
+ else
+ addr[0] = '\0';
sql = sqlite3_mprintf("INSERT INTO sessions(timestamp,id,user,realm,"
- "operation,password,redirect_uri) "
+ "operation,password,redirect_uri,mac_addr,test) "
"VALUES "
"(strftime('%%Y-%%m-%%d %%H:%%M:%%f','now'),"
- "%Q,%Q,%Q,%d,%Q,%Q)",
+ "%Q,%Q,%Q,%d,%Q,%Q,%Q,%Q)",
sessionid, user ? user : "", realm ? realm : "",
operation, pw ? pw : "",
- redirect_uri ? redirect_uri : "");
+ redirect_uri ? redirect_uri : "",
+ addr, ctx->test);
if (sql == NULL)
return -1;
debug_print(ctx, 1, "DB: %s", sql);
@@ -329,6 +336,29 @@
}
+static void add_text_node_conf_corrupt(struct hs20_svc *ctx, const char *realm,
+ xml_node_t *parent, const char *name,
+ const char *field)
+{
+ char *val;
+
+ val = db_get_osu_config_val(ctx, realm, field);
+ if (val) {
+ size_t len;
+
+ len = os_strlen(val);
+ if (len > 0) {
+ if (val[len - 1] == '0')
+ val[len - 1] = '1';
+ else
+ val[len - 1] = '0';
+ }
+ }
+ xml_node_create_text(ctx->xml, parent, NULL, name, val ? val : "");
+ os_free(val);
+}
+
+
static int new_password(char *buf, int buflen)
{
int i;
@@ -533,7 +563,8 @@
static int add_username_password(struct hs20_svc *ctx, xml_node_t *cred,
- const char *user, const char *pw)
+ const char *user, const char *pw,
+ int machine_managed)
{
xml_node_t *node;
@@ -541,7 +572,8 @@
if (node == NULL)
return -1;
- add_text_node(ctx, node, "MachineManaged", "TRUE");
+ add_text_node(ctx, node, "MachineManaged",
+ machine_managed ? "TRUE" : "FALSE");
add_text_node(ctx, node, "SoftTokenApp", "");
add_eap_ttls(ctx, node);
@@ -566,7 +598,7 @@
static xml_node_t * build_credential_pw(struct hs20_svc *ctx,
const char *user, const char *realm,
- const char *pw)
+ const char *pw, int machine_managed)
{
xml_node_t *cred;
@@ -576,7 +608,7 @@
return NULL;
}
add_creation_date(ctx, cred);
- if (add_username_password(ctx, cred, user, pw) < 0) {
+ if (add_username_password(ctx, cred, user, pw, machine_managed) < 0) {
xml_node_free(ctx->xml, cred);
return NULL;
}
@@ -593,7 +625,7 @@
if (new_password(new_pw, new_pw_len) < 0)
return NULL;
debug_print(ctx, 1, "Update password to '%s'", new_pw);
- return build_credential_pw(ctx, user, realm, new_pw);
+ return build_credential_pw(ctx, user, realm, new_pw, 1);
}
@@ -703,8 +735,23 @@
cred = build_credential_cert(ctx, real_user ? real_user : user,
realm, cert);
} else {
- cred = build_credential(ctx, real_user ? real_user : user,
- realm, new_pw, sizeof(new_pw));
+ char *pw;
+
+ pw = db_get_session_val(ctx, user, realm, session_id,
+ "password");
+ if (pw && pw[0]) {
+ debug_print(ctx, 1, "New password from the user: '%s'",
+ pw);
+ snprintf(new_pw, sizeof(new_pw), "%s", pw);
+ free(pw);
+ cred = build_credential_pw(ctx,
+ real_user ? real_user : user,
+ realm, new_pw, 0);
+ } else {
+ cred = build_credential(ctx,
+ real_user ? real_user : user,
+ realm, new_pw, sizeof(new_pw));
+ }
}
free(real_user);
if (!cred) {
@@ -721,7 +768,7 @@
}
snprintf(buf, sizeof(buf),
- "./Wi-Fi/%s/PerProviderSubscription/Credential1/Credential",
+ "./Wi-Fi/%s/PerProviderSubscription/Cred01/Credential",
realm);
if (add_update_node(ctx, spp_node, ns, buf, cred) < 0) {
@@ -742,7 +789,7 @@
debug_print(ctx, 1, "Request DB password update on success "
"notification");
db_add_session(ctx, user, realm, session_id, new_pw, NULL,
- UPDATE_PASSWORD);
+ UPDATE_PASSWORD, NULL);
}
return spp_node;
@@ -771,7 +818,7 @@
"requires policy remediation", NULL);
db_add_session(ctx, user, realm, session_id, NULL, NULL,
- POLICY_REMEDIATION);
+ POLICY_REMEDIATION, NULL);
policy = build_policy(ctx, user, realm, dmacc);
if (!policy) {
@@ -787,7 +834,7 @@
return NULL;
snprintf(buf, sizeof(buf),
- "./Wi-Fi/%s/PerProviderSubscription/Credential1/Policy",
+ "./Wi-Fi/%s/PerProviderSubscription/Cred01/Policy",
realm);
if (add_update_node(ctx, spp_node, ns, buf, policy) < 0) {
@@ -844,7 +891,7 @@
return NULL;
db_add_session(ctx, user, realm, session_id, NULL, redirect_uri,
- USER_REMEDIATION);
+ USER_REMEDIATION, NULL);
snprintf(uri, sizeof(uri), "%s%s", val, session_id);
os_free(val);
@@ -866,7 +913,7 @@
return NULL;
db_add_session(ctx, user, realm, session_id, NULL, redirect_uri,
- FREE_REMEDIATION);
+ FREE_REMEDIATION, NULL);
snprintf(uri, sizeof(uri), "%s%s", val, session_id);
os_free(val);
@@ -940,8 +987,10 @@
redirect_uri);
else if (type && strcmp(type, "policy") == 0)
ret = policy_remediation(ctx, user, realm, session_id, dmacc);
- else
+ else if (type && strcmp(type, "machine") == 0)
ret = machine_remediation(ctx, user, realm, session_id, dmacc);
+ else
+ ret = no_sub_rem(ctx, user, realm, session_id);
free(type);
return ret;
@@ -1033,7 +1082,8 @@
"No update available at this time", NULL);
}
- db_add_session(ctx, user, realm, session_id, NULL, NULL, POLICY_UPDATE);
+ db_add_session(ctx, user, realm, session_id, NULL, NULL, POLICY_UPDATE,
+ NULL);
status = "Update complete, request sppUpdateResponse";
spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
@@ -1042,7 +1092,7 @@
return NULL;
snprintf(buf, sizeof(buf),
- "./Wi-Fi/%s/PerProviderSubscription/Credential1/Policy",
+ "./Wi-Fi/%s/PerProviderSubscription/Cred01/Policy",
realm);
if (add_update_node(ctx, spp_node, ns, buf, policy) < 0) {
@@ -1146,14 +1196,15 @@
static xml_node_t * hs20_subscription_registration(struct hs20_svc *ctx,
const char *realm,
const char *session_id,
- const char *redirect_uri)
+ const char *redirect_uri,
+ const u8 *mac_addr)
{
xml_namespace_t *ns;
xml_node_t *spp_node, *exec_node;
char uri[300], *val;
if (db_add_session(ctx, NULL, realm, session_id, NULL, redirect_uri,
- SUBSCRIPTION_REGISTRATION) < 0)
+ SUBSCRIPTION_REGISTRATION, mac_addr) < 0)
return NULL;
val = db_get_osu_config_val(ctx, realm, "signup_url");
if (val == NULL)
@@ -1213,9 +1264,9 @@
static xml_node_t * build_pps(struct hs20_svc *ctx,
const char *user, const char *realm,
const char *pw, const char *cert,
- int machine_managed)
+ int machine_managed, const char *test)
{
- xml_node_t *pps, *c, *trust, *aaa, *aaa1, *upd, *homesp;
+ xml_node_t *pps, *c, *trust, *aaa, *aaa1, *upd, *homesp, *p;
xml_node_t *cred, *eap, *userpw;
pps = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
@@ -1225,7 +1276,7 @@
add_text_node(ctx, pps, "UpdateIdentifier", "1");
- c = xml_node_create(ctx->xml, pps, NULL, "Credential1");
+ c = xml_node_create(ctx->xml, pps, NULL, "Cred01");
add_text_node(ctx, c, "CredentialPriority", "1");
@@ -1233,18 +1284,51 @@
aaa1 = xml_node_create(ctx->xml, aaa, NULL, "AAA1");
add_text_node_conf(ctx, realm, aaa1, "CertURL",
"aaa_trust_root_cert_url");
- add_text_node_conf(ctx, realm, aaa1, "CertSHA256Fingerprint",
- "aaa_trust_root_cert_fingerprint");
+ if (test && os_strcmp(test, "corrupt_aaa_hash") == 0) {
+ debug_print(ctx, 1,
+ "TEST: Corrupt PPS/Cred*/AAAServerTrustRoot/Root*/CertSHA256FingerPrint");
+ add_text_node_conf_corrupt(ctx, realm, aaa1,
+ "CertSHA256Fingerprint",
+ "aaa_trust_root_cert_fingerprint");
+ } else {
+ add_text_node_conf(ctx, realm, aaa1, "CertSHA256Fingerprint",
+ "aaa_trust_root_cert_fingerprint");
+ }
+
+ if (test && os_strcmp(test, "corrupt_polupd_hash") == 0) {
+ debug_print(ctx, 1,
+ "TEST: Corrupt PPS/Cred*/Policy/PolicyUpdate/Trustroot/CertSHA256FingerPrint");
+ p = xml_node_create(ctx->xml, c, NULL, "Policy");
+ upd = xml_node_create(ctx->xml, p, NULL, "PolicyUpdate");
+ add_text_node(ctx, upd, "UpdateInterval", "30");
+ add_text_node(ctx, upd, "UpdateMethod", "SPP-ClientInitiated");
+ add_text_node(ctx, upd, "Restriction", "Unrestricted");
+ add_text_node_conf(ctx, realm, upd, "URI", "policy_url");
+ trust = xml_node_create(ctx->xml, upd, NULL, "TrustRoot");
+ add_text_node_conf(ctx, realm, trust, "CertURL",
+ "policy_trust_root_cert_url");
+ add_text_node_conf_corrupt(ctx, realm, trust,
+ "CertSHA256Fingerprint",
+ "policy_trust_root_cert_fingerprint");
+ }
upd = xml_node_create(ctx->xml, c, NULL, "SubscriptionUpdate");
add_text_node(ctx, upd, "UpdateInterval", "4294967295");
- add_text_node(ctx, upd, "UpdateMethod", "ClientInitiated");
+ add_text_node(ctx, upd, "UpdateMethod", "SPP-ClientInitiated");
add_text_node(ctx, upd, "Restriction", "HomeSP");
add_text_node_conf(ctx, realm, upd, "URI", "spp_http_auth_url");
trust = xml_node_create(ctx->xml, upd, NULL, "TrustRoot");
add_text_node_conf(ctx, realm, trust, "CertURL", "trust_root_cert_url");
- add_text_node_conf(ctx, realm, trust, "CertSHA256Fingerprint",
- "trust_root_cert_fingerprint");
+ if (test && os_strcmp(test, "corrupt_subrem_hash") == 0) {
+ debug_print(ctx, 1,
+ "TEST: Corrupt PPS/Cred*/SubscriptionUpdate/Trustroot/CertSHA256FingerPrint");
+ add_text_node_conf_corrupt(ctx, realm, trust,
+ "CertSHA256Fingerprint",
+ "trust_root_cert_fingerprint");
+ } else {
+ add_text_node_conf(ctx, realm, trust, "CertSHA256Fingerprint",
+ "trust_root_cert_fingerprint");
+ }
homesp = xml_node_create(ctx->xml, c, NULL, "HomeSP");
add_text_node_conf(ctx, realm, homesp, "FriendlyName", "friendly_name");
@@ -1328,7 +1412,7 @@
xml_node_t *pps, *tnds;
char buf[400];
char *str;
- char *user, *realm, *pw, *type, *mm;
+ char *user, *realm, *pw, *type, *mm, *test;
const char *status;
int cert = 0;
int machine_managed = 0;
@@ -1387,9 +1471,15 @@
return NULL;
fingerprint = db_get_session_val(ctx, NULL, NULL, session_id, "cert");
+ test = db_get_session_val(ctx, NULL, NULL, session_id, "test");
+ if (test)
+ debug_print(ctx, 1, "TEST: Requested special behavior: %s",
+ test);
pps = build_pps(ctx, user, realm, pw,
- fingerprint ? fingerprint : NULL, machine_managed);
+ fingerprint ? fingerprint : NULL, machine_managed,
+ test);
free(fingerprint);
+ free(test);
if (!pps) {
xml_node_free(ctx->xml, spp_node);
free(user);
@@ -1460,7 +1550,7 @@
return NULL;
}
- cred = build_credential_pw(ctx, free_account, realm, pw);
+ cred = build_credential_pw(ctx, free_account, realm, pw, 1);
free(free_account);
free(pw);
if (!cred) {
@@ -1475,7 +1565,7 @@
return NULL;
snprintf(buf, sizeof(buf),
- "./Wi-Fi/%s/PerProviderSubscription/Credential1/Credential",
+ "./Wi-Fi/%s/PerProviderSubscription/Cred01/Credential",
realm);
if (add_update_node(ctx, spp_node, ns, buf, cred) < 0) {
@@ -1606,11 +1696,12 @@
char *req_reason_buf = NULL;
char str[200];
xml_node_t *ret = NULL, *devinfo = NULL, *devdetail = NULL;
- xml_node_t *mo;
+ xml_node_t *mo, *macaddr;
char *version;
int valid;
char *supp, *pos;
char *err;
+ u8 wifi_mac_addr[ETH_ALEN];
version = xml_node_get_attr_value_ns(ctx->xml, node, SPP_NS_URI,
"sppVersion");
@@ -1716,6 +1807,29 @@
goto out;
}
os_free(err);
+
+ os_memset(wifi_mac_addr, 0, ETH_ALEN);
+ macaddr = get_node(ctx->xml, devdetail,
+ "Ext/org.wi-fi/Wi-Fi/Wi-FiMACAddress");
+ if (macaddr) {
+ char *addr, buf[50];
+
+ addr = xml_node_get_text(ctx->xml, macaddr);
+ if (addr && hwaddr_compact_aton(addr, wifi_mac_addr) == 0) {
+ snprintf(buf, sizeof(buf), "DevDetail MAC address: "
+ MACSTR, MAC2STR(wifi_mac_addr));
+ hs20_eventlog(ctx, user, realm, session_id, buf, NULL);
+ xml_node_get_text_free(ctx->xml, addr);
+ } else {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "Could not extract MAC address from DevDetail",
+ NULL);
+ }
+ } else {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "No MAC address in DevDetail", NULL);
+ }
+
if (user)
db_update_mo(ctx, user, realm, "devdetail", devdetail);
@@ -1762,7 +1876,7 @@
else
oper = NO_OPERATION;
if (db_add_session(ctx, user, realm, session_id, NULL,
- NULL, oper) < 0)
+ NULL, oper, NULL) < 0)
goto out;
ret = spp_exec_upload_mo(ctx, session_id,
@@ -1799,7 +1913,8 @@
if (strcasecmp(req_reason, "Subscription registration") == 0) {
ret = hs20_subscription_registration(ctx, realm, session_id,
- redirect_uri);
+ redirect_uri,
+ wifi_mac_addr);
hs20_eventlog_node(ctx, user, realm, session_id,
"subscription registration response",
ret);
@@ -1948,13 +2063,15 @@
goto out;
}
- sql = sqlite3_mprintf("INSERT INTO users(identity,realm,phase2,"
- "methods,cert,cert_pem,machine_managed) VALUES "
- "(%Q,%Q,1,%Q,%Q,%Q,%d)",
+ str = db_get_session_val(ctx, NULL, NULL, session_id, "mac_addr");
+
+ sql = sqlite3_mprintf("INSERT INTO users(identity,realm,phase2,methods,cert,cert_pem,machine_managed,mac_addr) VALUES (%Q,%Q,1,%Q,%Q,%Q,%d,%Q)",
user, realm, cert ? "TLS" : "TTLS-MSCHAPV2",
fingerprint ? fingerprint : "",
cert_pem ? cert_pem : "",
- pw_mm && atoi(pw_mm) ? 1 : 0);
+ pw_mm && atoi(pw_mm) ? 1 : 0,
+ str ? str : "");
+ free(str);
if (sql == NULL)
goto out;
debug_print(ctx, 1, "DB: %s", sql);
@@ -1996,6 +2113,32 @@
free(str);
}
+ if (cert && user) {
+ const char *serialnum;
+
+ str = db_get_session_val(ctx, NULL, NULL, session_id,
+ "mac_addr");
+
+ if (os_strncmp(user, "cert-", 5) == 0)
+ serialnum = user + 5;
+ else
+ serialnum = "";
+ sql = sqlite3_mprintf("INSERT OR REPLACE INTO cert_enroll (mac_addr,user,realm,serialnum) VALUES(%Q,%Q,%Q,%Q)",
+ str ? str : "", user, realm ? realm : "",
+ serialnum);
+ free(str);
+ if (sql) {
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) !=
+ SQLITE_OK) {
+ debug_print(ctx, 1,
+ "Failed to add cert_enroll entry into sqlite database: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+ }
+ }
+
if (ret == 0) {
hs20_eventlog(ctx, user, realm, session_id,
"completed subscription registration", NULL);
@@ -2119,6 +2262,9 @@
"", dmacc);
free(val);
}
+ if (oper == POLICY_UPDATE)
+ db_update_val(ctx, user, realm, "polupd_done", "1",
+ dmacc);
ret = build_spp_exchange_complete(
ctx, session_id,
"Exchange complete, release TLS connection", NULL);
diff --git a/hs20/server/spp_server.h b/hs20/server/spp_server.h
index 7b27be3..3556f5c 100644
--- a/hs20/server/spp_server.h
+++ b/hs20/server/spp_server.h
@@ -16,6 +16,7 @@
FILE *debug_log;
sqlite3 *db;
const char *addr;
+ const char *test;
};
diff --git a/hs20/server/sql.txt b/hs20/server/sql.txt
index 74d9f4a..666ef13 100644
--- a/hs20/server/sql.txt
+++ b/hs20/server/sql.txt
@@ -22,7 +22,9 @@
devinfo TEXT,
devdetail TEXT,
cert TEXT,
- cert_pem TEXT
+ cert_pem TEXT,
+ mac_addr TEXT,
+ test TEXT
);
CREATE index sessions_id_index ON sessions(id);
@@ -51,7 +53,10 @@
shared INTEGER,
cert TEXT,
cert_pem TEXT,
- t_c_timestamp INTEGER
+ t_c_timestamp INTEGER,
+ mac_addr TEXT,
+ last_msk TEXT,
+ polupd_done TEXT,
);
CREATE TABLE wildcards(
@@ -81,3 +86,10 @@
waiting_coa_ack BOOLEAN,
coa_ack_received BOOLEAN
);
+
+CREATE TABLE cert_enroll(
+ mac_addr TEXT PRIMARY KEY,
+ user TEXT,
+ realm TEXT,
+ serialnum TEXT
+);
diff --git a/hs20/server/www/est.php b/hs20/server/www/est.php
index a45648b..6983ec9 100644
--- a/hs20/server/www/est.php
+++ b/hs20/server/www/est.php
@@ -2,7 +2,7 @@
require('config.php');
-$params = split("/", $_SERVER["PATH_INFO"], 3);
+$params = explode("/", $_SERVER["PATH_INFO"], 3);
$realm = $params[1];
$cmd = $params[2];
$method = $_SERVER["REQUEST_METHOD"];
diff --git a/hs20/server/www/remediation-pw.php b/hs20/server/www/remediation-pw.php
new file mode 100644
index 0000000..76fdccb
--- /dev/null
+++ b/hs20/server/www/remediation-pw.php
@@ -0,0 +1,41 @@
+<?php
+
+require('config.php');
+
+$db = new PDO($osu_db);
+if (!$db) {
+ die($sqliteerror);
+}
+
+if (isset($_POST["id"]))
+ $id = preg_replace("/[^a-fA-F0-9]/", "", $_POST["id"]);
+else
+ die("Missing session id");
+
+$pw = $_POST["password"];
+if (strlen($id) < 32 || !isset($pw)) {
+ die("Invalid POST data");
+}
+
+$row = $db->query("SELECT rowid,* FROM sessions WHERE id='$id'")->fetch();
+if ($row == false) {
+ die("Session not found");
+}
+$user = $row['user'];
+$realm = $row['realm'];
+
+$uri = $row['redirect_uri'];
+$rowid = $row['rowid'];
+
+if (!$db->exec("UPDATE sessions SET password='$pw' WHERE rowid=$rowid")) {
+ die("Failed to update session database");
+}
+
+$db->exec("INSERT INTO eventlog(user,realm,sessionid,timestamp,notes) " .
+ "VALUES ('$user', '$realm', '$id', " .
+ "strftime('%Y-%m-%d %H:%M:%f','now'), " .
+ "'completed user input response for subscription remediation')");
+
+header("Location: $uri", true, 302);
+
+?>
diff --git a/hs20/server/www/remediation.php b/hs20/server/www/remediation.php
index 392a7bd..3628065 100644
--- a/hs20/server/www/remediation.php
+++ b/hs20/server/www/remediation.php
@@ -6,13 +6,50 @@
<?php
-echo "SessionID: " . $_GET["session_id"] . "<br>\n";
+require('config.php');
-echo "<a href=\"redirect.php?id=" . $_GET["session_id"] . "\">Complete user subscription remediation</a><br>\n";
+$db = new PDO($osu_db);
+if (!$db) {
+ die($sqliteerror);
+}
+
+if (isset($_GET["session_id"]))
+ $id = preg_replace("/[^a-fA-F0-9]/", "", $_GET["session_id"]);
+else
+ $id = 0;
+echo "SessionID: " . $id . "<br>\n";
+
+$row = $db->query("SELECT * FROM sessions WHERE id='$id'")->fetch();
+if ($row == false) {
+ die("Session not found");
+}
+
+$username = $row['user'];
+echo "User: " . $username . "@" . $row['realm'] . "<br>\n";
+
+$user = $db->query("SELECT machine_managed,methods FROM users WHERE identity='$username'")->fetch();
+if ($user == false) {
+ die("User not found");
+}
+
+echo "<hr><br>\n";
+
+$cert = $user['methods'] == "TLS" || strncmp($username, "cert-", 5) == 0;
+
+if ($cert) {
+ echo "<a href=\"redirect.php?id=" . $_GET["session_id"] . "\">Complete user subscription remediation</a><br>\n";
+} else if ($user['machine_managed'] == "1") {
+ echo "<a href=\"redirect.php?id=" . $_GET["session_id"] . "\">Complete user subscription remediation</a><br>\n";
+ echo "This will provide a new machine-generated password.<br>\n";
+} else {
+ echo "<form action=\"remediation-pw.php\" method=\"POST\">\n";
+ echo "<input type=\"hidden\" name=\"id\" value=\"$id\">\n";
+ echo "New password: <input type=\"password\" name=\"password\"><br>\n";
+ echo "<input type=\"submit\" value=\"Change password\">\n";
+ echo "</form>\n";
+}
?>
-This will provide a new machine-generated password.
-
</body>
</html>
diff --git a/hs20/server/www/signup.php b/hs20/server/www/signup.php
index aeb2f68..80a9d40 100644
--- a/hs20/server/www/signup.php
+++ b/hs20/server/www/signup.php
@@ -15,19 +15,30 @@
die($sqliteerror);
}
-$row = $db->query("SELECT realm FROM sessions WHERE id='$id'")->fetch();
+$row = $db->query("SELECT realm,test FROM sessions WHERE id='$id'")->fetch();
if ($row == false) {
die("Session not found for id: $id");
}
$realm = $row['realm'];
+$test = $row['test'];
+
+if (strlen($test) > 0) {
+ echo "<p style=\"color:#FF0000\">Special test functionality: $test</red></big></p>\n";
+}
echo "<h3>Sign up for a subscription - $realm</h3>\n";
+echo "<p>This page can be used to select between three different types of subscriptions for testing purposes.</p>\n";
+
+echo "<h4>Option 1 - shared free access credential</h4>\n";
+
$row = $db->query("SELECT value FROM osu_config WHERE realm='$realm' AND field='free_account'")->fetch();
if ($row && strlen($row['value']) > 0) {
echo "<p><a href=\"free.php?session_id=$id\">Sign up for free access</a></p>\n";
}
+echo "<h4>Option 2 - username/password credential</h4>\n";
+
echo "<form action=\"add-mo.php\" method=\"POST\">\n";
echo "<input type=\"hidden\" name=\"id\" value=\"$id\">\n";
?>
@@ -39,6 +50,8 @@
</form>
<?php
+echo "<h4>Option 3 - client certificate credential</h4>\n";
+
echo "<p><a href=\"cert-enroll.php?id=$id\">Enroll a client certificate</a></p>\n"
?>
diff --git a/hs20/server/www/spp.php b/hs20/server/www/spp.php
index 002d028..f10e5ab 100644
--- a/hs20/server/www/spp.php
+++ b/hs20/server/www/spp.php
@@ -20,6 +20,11 @@
die("Realm not specified");
}
+if (isset($_GET["test"]))
+ $test = PREG_REPLACE("/[^0-9a-zA-Z\_\-]/i", '', $_GET["test"]);
+else
+ $test = "";
+
unset($user);
putenv("HS20CERT");
@@ -100,6 +105,7 @@
putenv("HS20POST=$postdata");
$addr = $_SERVER["REMOTE_ADDR"];
putenv("HS20ADDR=$addr");
+putenv("HS20TEST=$test");
$last = exec("$osu_root/spp/hs20_spp_server -r$osu_root -f/tmp/hs20_spp_server.log", $output, $ret);
diff --git a/hs20/server/www/terms.php b/hs20/server/www/terms.php
index e360be5..e269b3c 100644
--- a/hs20/server/www/terms.php
+++ b/hs20/server/www/terms.php
@@ -59,8 +59,10 @@
if (!$row) {
die("No current session for the specified MAC address");
}
- $waiting = $row[0] == 1;
- $ack = $row[1] == 1;
+ if (strlen($row[0]) > 0)
+ $waiting = $row[0] == 1;
+ if (strlen($row[1]) > 0)
+ $ack = $row[1] == 1;
$res->closeCursor();
if (!$waiting)
break;
diff --git a/hs20/server/www/users.php b/hs20/server/www/users.php
index c265372..f546de3 100644
--- a/hs20/server/www/users.php
+++ b/hs20/server/www/users.php
@@ -191,6 +191,9 @@
}
echo "<br>\n";
+if (strncmp($row['identity'], "cert-", 5) != 0)
+ echo "Machine managed: " . ($row['machine_managed'] == "1" ? "TRUE" : "FALSE") . "<br>\n";
+
echo "<form>Policy: <select name=\"policy\" " .
"onChange=\"window.location='users.php?cmd=policy&id=" .
$row['rowid'] . "&policy=' + this.value;\">\n";
@@ -313,10 +316,10 @@
echo "[<a href=\"users.php?cmd=eventlog&limit=50\">Eventlog</a>] ";
echo "<br>\n";
-echo "<table border=1>\n";
-echo "<tr><th>User<th>Realm<th>Remediation<th>Policy<th>Account type<th>Phase 2 method(s)<th>DevId<th>T&C\n";
+echo "<table border=1 cellspacing=0 cellpadding=0>\n";
+echo "<tr><th>User<th>Realm<th><small>Remediation</small><th>Policy<th><small>Account type</small><th><small>Phase 2 method(s)</small><th>DevId<th>MAC Address<th>T&C\n";
-$res = $db->query('SELECT rowid,* FROM users WHERE phase2=1');
+$res = $db->query('SELECT rowid,* FROM users WHERE phase2=1 ORDER BY identity');
foreach ($res as $row) {
echo "<tr><td><a href=\"users.php?id=" . $row['rowid'] . "\"> " .
$row['identity'] . " </a>";
@@ -324,7 +327,7 @@
$rem = $row['remediation'];
echo "<td>";
if ($rem == "") {
- echo "Not required";
+ echo "-";
} else if ($rem == "user") {
echo "User";
} else if ($rem == "policy") {
@@ -339,17 +342,18 @@
echo "<td>shared";
else
echo "<td>default";
- echo "<td>" . $row['methods'];
+ echo "<td><small>" . $row['methods'] . "</small>";
echo "<td>";
$xml = xml_parser_create();
xml_parse_into_struct($xml, $row['devinfo'], $devinfo);
foreach($devinfo as $k) {
if ($k['tag'] == 'DEVID') {
- echo $k['value'];
+ echo "<small>" . $k['value'] . "</small>";
break;
}
}
- echo "<td>" . $row['t_c_timestamp'];
+ echo "<td><small>" . $row['mac_addr'] . "</small>";
+ echo "<td><small>" . $row['t_c_timestamp'] . "</small>";
echo "\n";
}
echo "</table>\n";
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 820cba9..f9b6f29 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -631,6 +631,7 @@
os_free(p->icons[j]);
os_free(p->icons);
os_free(p->osu_nai);
+ os_free(p->osu_nai2);
os_free(p->service_desc);
}
os_free(conf->hs20_osu_providers);
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 5b71126..778366d 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -585,10 +585,12 @@
char **icons;
size_t icons_count;
char *osu_nai;
+ char *osu_nai2;
unsigned int service_desc_count;
struct hostapd_lang_string *service_desc;
} *hs20_osu_providers, *last_osu;
size_t hs20_osu_providers_count;
+ size_t hs20_osu_providers_nai_count;
char **hs20_operator_icon;
size_t hs20_operator_icon_count;
unsigned int hs20_deauth_req_timeout;
@@ -682,6 +684,8 @@
char owe_transition_ifname[IFNAMSIZ + 1];
int *owe_groups;
#endif /* CONFIG_OWE */
+
+ int coloc_intf_reporting;
};
/**
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
index 66d255c..95d004e 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -155,6 +155,40 @@
#endif /* RADIUS_SERVER */
+#ifdef EAP_TLS_FUNCS
+static void authsrv_tls_event(void *ctx, enum tls_event ev,
+ union tls_event_data *data)
+{
+ switch (ev) {
+ case TLS_CERT_CHAIN_SUCCESS:
+ wpa_printf(MSG_DEBUG, "authsrv: remote certificate verification success");
+ break;
+ case TLS_CERT_CHAIN_FAILURE:
+ wpa_printf(MSG_INFO, "authsrv: certificate chain failure: reason=%d depth=%d subject='%s' err='%s'",
+ data->cert_fail.reason,
+ data->cert_fail.depth,
+ data->cert_fail.subject,
+ data->cert_fail.reason_txt);
+ break;
+ case TLS_PEER_CERTIFICATE:
+ wpa_printf(MSG_DEBUG, "authsrv: peer certificate: depth=%d serial_num=%s subject=%s",
+ data->peer_cert.depth,
+ data->peer_cert.serial_num ? data->peer_cert.serial_num : "N/A",
+ data->peer_cert.subject);
+ break;
+ case TLS_ALERT:
+ if (data->alert.is_local)
+ wpa_printf(MSG_DEBUG, "authsrv: local TLS alert: %s",
+ data->alert.description);
+ else
+ wpa_printf(MSG_DEBUG, "authsrv: remote TLS alert: %s",
+ data->alert.description);
+ break;
+ }
+}
+#endif /* EAP_TLS_FUNCS */
+
+
int authsrv_init(struct hostapd_data *hapd)
{
#ifdef EAP_TLS_FUNCS
@@ -167,6 +201,8 @@
os_memset(&conf, 0, sizeof(conf));
conf.tls_session_lifetime = hapd->conf->tls_session_lifetime;
conf.tls_flags = hapd->conf->tls_flags;
+ conf.event_cb = authsrv_tls_event;
+ conf.cb_ctx = hapd;
hapd->ssl_ctx = tls_init(&conf);
if (hapd->ssl_ctx == NULL) {
wpa_printf(MSG_ERROR, "Failed to initialize TLS");
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 7d079d2..59bd4af 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -453,8 +453,9 @@
/* Extended supported rates */
pos = hostapd_eid_ext_supp_rates(hapd, pos);
- /* RSN, MDIE, WPA */
- pos = hostapd_eid_wpa(hapd, pos, epos - pos);
+ /* RSN, MDIE */
+ if (hapd->conf->wpa != WPA_PROTO_WPA)
+ pos = hostapd_eid_wpa(hapd, pos, epos - pos);
pos = hostapd_eid_bss_load(hapd, pos, epos - pos);
@@ -517,6 +518,10 @@
pos = hostapd_eid_vendor_vht(hapd, pos);
#endif /* CONFIG_IEEE80211AC */
+ /* WPA */
+ if (hapd->conf->wpa == WPA_PROTO_WPA)
+ pos = hostapd_eid_wpa(hapd, pos, epos - pos);
+
/* Wi-Fi Alliance WMM */
pos = hostapd_eid_wmm(hapd, pos);
@@ -1152,9 +1157,11 @@
/* Extended supported rates */
tailpos = hostapd_eid_ext_supp_rates(hapd, tailpos);
- /* RSN, MDIE, WPA */
- tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE -
- tailpos);
+ /* RSN, MDIE */
+ if (hapd->conf->wpa != WPA_PROTO_WPA)
+ tailpos = hostapd_eid_wpa(hapd, tailpos,
+ tail + BEACON_TAIL_BUF_SIZE -
+ tailpos);
tailpos = hostapd_eid_rm_enabled_capab(hapd, tailpos,
tail + BEACON_TAIL_BUF_SIZE -
@@ -1223,6 +1230,12 @@
tailpos = hostapd_eid_vendor_vht(hapd, tailpos);
#endif /* CONFIG_IEEE80211AC */
+ /* WPA */
+ if (hapd->conf->wpa == WPA_PROTO_WPA)
+ tailpos = hostapd_eid_wpa(hapd, tailpos,
+ tail + BEACON_TAIL_BUF_SIZE -
+ tailpos);
+
/* Wi-Fi Alliance WMM */
tailpos = hostapd_eid_wmm(hapd, tailpos);
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 98a2eec..1135aea 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -338,10 +338,14 @@
goto fail;
}
#ifdef CONFIG_IEEE80211W
- if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out &&
+ if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) ==
+ (WLAN_STA_ASSOC | 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 &&
+ if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) ==
+ (WLAN_STA_ASSOC | WLAN_STA_MFP) &&
+ !sta->sa_query_timed_out &&
(sta->auth_alg != WLAN_AUTH_FT)) {
/*
* STA has already been associated with MFP and SA
diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
index 04fb3e1..a7df810 100644
--- a/src/ap/gas_serv.c
+++ b/src/ap/gas_serv.c
@@ -181,6 +181,8 @@
wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
if (hapd->conf->hs20_osu_providers_count)
wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
+ if (hapd->conf->hs20_osu_providers_nai_count)
+ wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST);
if (hapd->conf->hs20_icons_count)
wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
if (hapd->conf->hs20_operator_icon_count)
@@ -817,6 +819,40 @@
}
+static void anqp_add_osu_provider_nai(struct wpabuf *buf,
+ struct hs20_osu_provider *p)
+{
+ /* OSU_NAI for shared BSS (Single SSID) */
+ if (p->osu_nai2) {
+ wpabuf_put_u8(buf, os_strlen(p->osu_nai2));
+ wpabuf_put_str(buf, p->osu_nai2);
+ } else {
+ wpabuf_put_u8(buf, 0);
+ }
+}
+
+
+static void anqp_add_osu_providers_nai_list(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ if (hapd->conf->hs20_osu_providers_nai_count) {
+ size_t i;
+ 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_OSU_PROVIDERS_NAI_LIST);
+ wpabuf_put_u8(buf, 0); /* Reserved */
+
+ for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) {
+ anqp_add_osu_provider_nai(
+ buf, &hapd->conf->hs20_osu_providers[i]);
+ }
+
+ gas_anqp_set_element_len(buf, len);
+ }
+}
+
+
static void anqp_add_icon_binary_file(struct hostapd_data *hapd,
struct wpabuf *buf,
const u8 *name, size_t name_len)
@@ -1024,6 +1060,8 @@
anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len);
if (request & ANQP_REQ_OPERATOR_ICON_METADATA)
anqp_add_operator_icon_metadata(hapd, buf);
+ if (request & ANQP_REQ_OSU_PROVIDERS_NAI_LIST)
+ anqp_add_osu_providers_nai_list(hapd, buf);
#endif /* CONFIG_HS20 */
#ifdef CONFIG_MBO
@@ -1216,6 +1254,11 @@
"Operator Icon Metadata",
hapd->conf->hs20_operator_icon_count, qi);
break;
+ case HS20_STYPE_OSU_PROVIDERS_NAI_LIST:
+ set_anqp_req(ANQP_REQ_OSU_PROVIDERS_NAI_LIST,
+ "OSU Providers NAI List",
+ hapd->conf->hs20_osu_providers_nai_count, qi);
+ break;
default:
wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
subtype);
diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h
index 0afdcb1..2cf1817 100644
--- a/src/ap/gas_serv.h
+++ b/src/ap/gas_serv.h
@@ -62,6 +62,8 @@
(0x10000 << HS20_STYPE_ICON_REQUEST)
#define ANQP_REQ_OPERATOR_ICON_METADATA \
(0x10000 << HS20_STYPE_OPERATOR_ICON_METADATA)
+#define ANQP_REQ_OSU_PROVIDERS_NAI_LIST \
+ (0x10000 << HS20_STYPE_OSU_PROVIDERS_NAI_LIST)
/* The first MBO ANQP-element can be included in the optimized bitmap. */
#define ANQP_REQ_MBO_CELL_DATA_CONN_PREF \
(BIT(29) << MBO_ANQP_SUBTYPE_CELL_CONN_PREF)
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 23d2720..117ee08 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -2620,6 +2620,11 @@
!!(hapd_iface->drv_flags &
WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
+#ifdef NEED_AP_MLME
+ for (j = 0; j < hapd_iface->num_bss; j++)
+ hostapd_cleanup_cs_params(hapd_iface->bss[j]);
+#endif /* NEED_AP_MLME */
+
/* same as hostapd_interface_deinit without deinitializing ctrl-iface */
for (j = 0; j < hapd_iface->num_bss; j++) {
struct hostapd_data *hapd = hapd_iface->bss[j];
@@ -3428,7 +3433,6 @@
const struct hostapd_freq_params *freq_params)
{
int vht_seg0_idx = 0, vht_seg1_idx = 0, vht_bw = VHT_CHANWIDTH_USE_HT;
- unsigned int i;
wpa_printf(MSG_DEBUG, "Restarting all CSA-related BSSes");
@@ -3470,10 +3474,8 @@
/*
* cs_params must not be cleared earlier because the freq_params
* argument may actually point to one of these.
+ * These params will be cleared during interface disable below.
*/
- for (i = 0; i < iface->num_bss; i++)
- hostapd_cleanup_cs_params(iface->bss[i]);
-
hostapd_disable_iface(iface);
hostapd_enable_iface(iface);
}
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 84e74ee..5279abc 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -679,7 +679,8 @@
if (!ieee80211n_supported_ht_capab(iface))
return -1;
#ifdef CONFIG_IEEE80211AC
- if (!ieee80211ac_supported_vht_capab(iface))
+ if (iface->conf->ieee80211ac &&
+ !ieee80211ac_supported_vht_capab(iface))
return -1;
#endif /* CONFIG_IEEE80211AC */
ret = ieee80211n_check_40mhz(iface);
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index d8b34fa..d2d6b17 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -1665,9 +1665,11 @@
is_probe_req);
if (res == HOSTAPD_ACL_REJECT) {
- wpa_printf(MSG_INFO,
- "Station " MACSTR " not allowed to authenticate",
- MAC2STR(addr));
+ if (!is_probe_req)
+ wpa_printf(MSG_DEBUG,
+ "Station " MACSTR
+ " not allowed to authenticate",
+ MAC2STR(addr));
return HOSTAPD_ACL_REJECT;
}
@@ -2583,10 +2585,14 @@
if (resp != WLAN_STATUS_SUCCESS)
return resp;
#ifdef CONFIG_IEEE80211W
- if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out &&
+ if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) ==
+ (WLAN_STA_ASSOC | 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 &&
+ if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) ==
+ (WLAN_STA_ASSOC | WLAN_STA_MFP) &&
+ !sta->sa_query_timed_out &&
(!reassoc || sta->auth_alg != WLAN_AUTH_FT)) {
/*
* STA has already been associated with MFP and SA
@@ -4052,7 +4058,8 @@
sta = ap_get_sta(hapd, mgmt->da);
if (!sta) {
- wpa_printf(MSG_INFO, "handle_auth_cb: STA " MACSTR " not found",
+ wpa_printf(MSG_DEBUG, "handle_auth_cb: STA " MACSTR
+ " not found",
MAC2STR(mgmt->da));
return;
}
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index a3f8609..c481399 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -178,6 +178,10 @@
case 1: /* Bits 8-15 */
if (hapd->conf->proxy_arp)
*pos |= 0x10; /* Bit 12 - Proxy ARP */
+ if (hapd->conf->coloc_intf_reporting) {
+ /* Bit 13 - Collocated Interference Reporting */
+ *pos |= 0x20;
+ }
break;
case 2: /* Bits 16-23 */
if (hapd->conf->wnm_sleep_mode)
diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c
index 31e4fc6..01fecee 100644
--- a/src/ap/vlan_init.c
+++ b/src/ap/vlan_init.c
@@ -138,6 +138,8 @@
!hapd->conf->vlan) {
/* dynamic vlans enabled but no (or empty) vlan_file given */
struct hostapd_vlan *vlan;
+ int ret;
+
vlan = os_zalloc(sizeof(*vlan));
if (vlan == NULL) {
wpa_printf(MSG_ERROR, "Out of memory while assigning "
@@ -146,8 +148,16 @@
}
vlan->vlan_id = VLAN_ID_WILDCARD;
- os_snprintf(vlan->ifname, sizeof(vlan->ifname), "%s.#",
- hapd->conf->iface);
+ ret = os_snprintf(vlan->ifname, sizeof(vlan->ifname), "%s.#",
+ hapd->conf->iface);
+ if (ret >= (int) sizeof(vlan->ifname)) {
+ wpa_printf(MSG_WARNING,
+ "VLAN: Interface name was truncated to %s",
+ vlan->ifname);
+ } else if (ret < 0) {
+ os_free(vlan);
+ return ret;
+ }
vlan->next = hapd->conf->vlan;
hapd->conf->vlan = vlan;
}
diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
index 7b43c7d..61d2f65 100644
--- a/src/ap/wnm_ap.c
+++ b/src/ap/wnm_ap.c
@@ -460,6 +460,48 @@
}
+static void ieee802_11_rx_wnm_coloc_intf_report(struct hostapd_data *hapd,
+ const u8 *addr, const u8 *buf,
+ size_t len)
+{
+ u8 dialog_token;
+ char *hex;
+ size_t hex_len;
+
+ if (!hapd->conf->coloc_intf_reporting) {
+ wpa_printf(MSG_DEBUG,
+ "WNM: Ignore unexpected Collocated Interference Report from "
+ MACSTR, MAC2STR(addr));
+ return;
+ }
+
+ if (len < 1) {
+ wpa_printf(MSG_DEBUG,
+ "WNM: Ignore too short Collocated Interference Report from "
+ MACSTR, MAC2STR(addr));
+ return;
+ }
+ dialog_token = *buf++;
+ len--;
+
+ wpa_printf(MSG_DEBUG,
+ "WNM: Received Collocated Interference Report frame from "
+ MACSTR " (dialog_token=%u)",
+ MAC2STR(addr), dialog_token);
+ wpa_hexdump(MSG_MSGDUMP, "WNM: Collocated Interference Report Elements",
+ buf, len);
+
+ hex_len = 2 * len + 1;
+ hex = os_malloc(hex_len);
+ if (!hex)
+ return;
+ wpa_snprintf_hex(hex, hex_len, buf, len);
+ wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, COLOC_INTF_REPORT MACSTR " %d %s",
+ MAC2STR(addr), dialog_token, hex);
+ os_free(hex);
+}
+
+
int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len)
{
@@ -490,6 +532,10 @@
ieee802_11_rx_wnm_notification_req(hapd, mgmt->sa, payload,
plen);
return 0;
+ case WNM_COLLOCATED_INTERFERENCE_REPORT:
+ ieee802_11_rx_wnm_coloc_intf_report(hapd, mgmt->sa, payload,
+ plen);
+ return 0;
}
wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR,
@@ -688,3 +734,40 @@
return 0;
}
+
+
+int wnm_send_coloc_intf_req(struct hostapd_data *hapd, struct sta_info *sta,
+ unsigned int auto_report, unsigned int timeout)
+{
+ u8 buf[100], *pos;
+ struct ieee80211_mgmt *mgmt;
+ u8 dialog_token = 1;
+
+ if (auto_report > 3 || timeout > 63)
+ return -1;
+ os_memset(buf, 0, sizeof(buf));
+ mgmt = (struct ieee80211_mgmt *) buf;
+ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memcpy(mgmt->da, sta->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_WNM;
+ mgmt->u.action.u.coloc_intf_req.action =
+ WNM_COLLOCATED_INTERFERENCE_REQ;
+ mgmt->u.action.u.coloc_intf_req.dialog_token = dialog_token;
+ mgmt->u.action.u.coloc_intf_req.req_info = auto_report | (timeout << 2);
+ pos = &mgmt->u.action.u.coloc_intf_req.req_info;
+ pos++;
+
+ wpa_printf(MSG_DEBUG, "WNM: Sending Collocated Interference Request to "
+ MACSTR " (dialog_token=%u auto_report=%u timeout=%u)",
+ MAC2STR(sta->addr), dialog_token, auto_report, timeout);
+ if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "WNM: Failed to send Collocated Interference Request frame");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/ap/wnm_ap.h b/src/ap/wnm_ap.h
index 56d0f88..1806ba0 100644
--- a/src/ap/wnm_ap.h
+++ b/src/ap/wnm_ap.h
@@ -24,5 +24,7 @@
const u8 *nei_rep, size_t nei_rep_len,
const u8 *mbo_attrs, size_t mbo_len);
void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx);
+int wnm_send_coloc_intf_req(struct hostapd_data *hapd, struct sta_info *sta,
+ unsigned int auto_report, unsigned int timeout);
#endif /* WNM_AP_H */
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index 421dd5a..253fe6e 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -751,6 +751,7 @@
#ifdef CONFIG_SAE
if (wpa_auth->conf.ieee80211w == MGMT_FRAME_PROTECTION_OPTIONAL &&
+ wpa_auth->conf.sae_require_mfp &&
wpa_key_mgmt_sae(sm->wpa_key_mgmt) &&
!(data.capabilities & WPA_CAPABILITY_MFPC)) {
wpa_printf(MSG_DEBUG,
diff --git a/src/common/dpp.c b/src/common/dpp.c
index 74a524d..677f586 100644
--- a/src/common/dpp.c
+++ b/src/common/dpp.c
@@ -840,6 +840,7 @@
if (sha256_vector(1, (const u8 **) &data, &data_len,
bi->pubkey_hash) < 0) {
wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
+ os_free(data);
return -1;
}
wpa_hexdump(MSG_DEBUG, "DPP: Public key hash",
@@ -2601,6 +2602,7 @@
ret = 0;
fail:
EC_POINT_clear_free(l);
+ EC_POINT_clear_free(sum);
EC_KEY_free(bI);
EC_KEY_free(BR);
EC_KEY_free(PR);
@@ -5562,6 +5564,7 @@
if (!conf->curve) {
wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s",
curve);
+ os_free(conf);
return NULL;
}
}
@@ -6219,14 +6222,14 @@
if (EC_POINT_set_affine_coordinates_GFp(group, point, x, y,
ctx) != 1) {
-#if OPENSSL_VERSION_NUMBER >= 0x10101000L || defined(OPENSSL_IS_BORINGSSL)
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L || defined(OPENSSL_IS_BORINGSSL)
/* Unlike older OpenSSL versions, OpenSSL 1.1.1 and BoringSSL
* return an error from EC_POINT_set_affine_coordinates_GFp()
* when the point is not on the curve. */
break;
-#else /* >=1.1.1 or OPENSSL_IS_BORINGSSL */
+#else /* >=1.1.0 or OPENSSL_IS_BORINGSSL */
goto fail;
-#endif /* >= 1.1.1 or OPENSSL_IS_BORINGSSL */
+#endif /* >= 1.1.0 or OPENSSL_IS_BORINGSSL */
}
if (!EC_POINT_is_on_curve(group, point, ctx))
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index e03a095..762e731 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -922,6 +922,16 @@
u8 variable[];
} STRUCT_PACKED bss_tm_query;
struct {
+ u8 action; /* 11 */
+ u8 dialog_token;
+ u8 req_info;
+ } STRUCT_PACKED coloc_intf_req;
+ struct {
+ u8 action; /* 12 */
+ u8 dialog_token;
+ u8 variable[];
+ } STRUCT_PACKED coloc_intf_report;
+ struct {
u8 action; /* 15 */
u8 variable[];
} STRUCT_PACKED slf_prot_action;
@@ -1331,6 +1341,7 @@
#define HS20_STYPE_ICON_REQUEST 10
#define HS20_STYPE_ICON_BINARY_FILE 11
#define HS20_STYPE_OPERATOR_ICON_METADATA 12
+#define HS20_STYPE_OSU_PROVIDERS_NAI_LIST 13
#define HS20_DGAF_DISABLED 0x01
#define HS20_PPS_MO_ID_PRESENT 0x02
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index 75a7cc0..7a4da46 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -463,6 +463,17 @@
* extendable to send more information. The newer version carries the
* legacy blob encapsulated within an attribute and can be extended with
* additional vendor attributes that can enhance the NAN command interface.
+ * @QCA_NL80211_VENDOR_SUBCMD_ROAM_SCAN_EVENT: Event to indicate scan triggered
+ * or stopped within driver/firmware in order to initiate roaming. The
+ * attributes used with this event are defined in enum
+ * qca_wlan_vendor_attr_roam_scan. Some drivers may not send these events
+ * in few cases, e.g., if the host processor is sleeping when this event
+ * is generated in firmware.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG: This command is used to
+ * configure parameters per peer to capture Channel Frequency Response
+ * (CFR) and enable Periodic CFR capture. The attributes for this command
+ * are defined in enum qca_wlan_vendor_peer_cfr_capture_attr.
*/
enum qca_nl80211_vendor_subcmds {
QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
@@ -623,6 +634,8 @@
/* Frame filter operations for other BSSs/unassociated STAs */
QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER = 170,
QCA_NL80211_VENDOR_SUBCMD_NAN_EXT = 171,
+ QCA_NL80211_VENDOR_SUBCMD_ROAM_SCAN_EVENT = 172,
+ QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG = 173,
};
enum qca_wlan_vendor_attr {
@@ -4938,6 +4951,10 @@
* and ndp confirm.
*/
QCA_WLAN_VENDOR_ATTR_NDP_TRANSPORT_PROTOCOL = 29,
+ /* Unsigned 8-bit value indicating if NDP remote peer supports NAN NDPE.
+ * 1:support 0:not support
+ */
+ QCA_WLAN_VENDOR_ATTR_PEER_NDPE_SUPPORT = 30,
/* keep last */
QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_AFTER_LAST,
@@ -5553,6 +5570,40 @@
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_OVERRIDE_MU_EDCA = 21,
+ /* 8-bit unsigned value to configure the support for receiving
+ * an MPDU that contains an operating mode control subfield.
+ * This attribute is used to configure the testbed device.
+ * 1-enable, 0-disable.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_SUPP = 22,
+
+ /* Nested attribute values required to setup the TWT session.
+ * enum qca_wlan_vendor_attr_twt_setup provides the necessary
+ * information to set up the session. It contains broadcast flags,
+ * set_up flags, trigger value, flow type, flow ID, wake interval
+ * exponent, protection, target wake time, wake duration, wake interval
+ * mantissa. These nested attributes are used to setup a host triggered
+ * TWT session.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SETUP = 23,
+
+ /* This nested attribute is used to terminate the current TWT session.
+ * It does not currently carry any attributes.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_TERMINATE = 24,
+
+ /* This nested attribute is used to suspend the current TWT session.
+ * It does not currently carry any attributes.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SUSPEND = 25,
+
+ /* Nested attribute values to indicate the request for resume.
+ * This attribute is used to resume the TWT session.
+ * enum qca_wlan_vendor_attr_twt_resume provides the necessary
+ * parameters required to resume the TWT session.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_RESUME = 26,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX =
@@ -5706,4 +5757,237 @@
QCA_WLAN_VENDOR_ATTR_NAN_PARAMS_AFTER_LAST - 1
};
+/**
+ * enum qca_wlan_vendor_attr_twt_setup: Represents attributes for
+ * TWT (Target Wake Time) setup request. These attributes are sent as part of
+ * %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SETUP and
+ * %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST: Flag attribute.
+ * Disable (flag attribute not present) - Individual TWT
+ * Enable (flag attribute present) - Broadcast TWT.
+ * Individual means the session is between the STA and the AP.
+ * This session is established using a separate negotiation between
+ * STA and AP.
+ * Broadcast means the session is across multiple STAs and an AP. The
+ * configuration parameters are announced in Beacon frames by the AP.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_REQ_TYPE: Required (u8).
+ * Unsigned 8-bit qca_wlan_vendor_twt_setup_req_type to
+ * specify the TWT request type
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TRIGGER: Flag attribute
+ * Enable (flag attribute present) - TWT with trigger support.
+ * Disable (flag attribute not present) - TWT without trigger support.
+ * Trigger means the AP will send the trigger frame to allow STA to send data.
+ * Without trigger, the STA will wait for the MU EDCA timer before
+ * transmitting the data.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_TYPE: Required (u8)
+ * 0 - Announced TWT - In this mode, STA may skip few service periods to
+ * save more power. If STA wants to wake up, it will send a PS-POLL/QoS
+ * NULL frame to AP.
+ * 1 - Unannounced TWT - The STA will wakeup during every SP.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_ID: Optional (u8)
+ * Flow ID is the unique identifier for each TWT session.
+ * Currently this is not required and dialog ID will be set to zero.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_EXP: Required (u8)
+ * This attribute (exp) is used along with the mantissa to derive the
+ * wake interval using the following formula:
+ * pow(2,exp) = wake_intvl_us/wake_intvl_mantis
+ * Wake interval is the interval between 2 successive SP.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PROTECTION: Flag attribute
+ * Enable (flag attribute present) - Protection required.
+ * Disable (flag attribute not present) - Protection not required.
+ * If protection is enabled, then the AP will use protection
+ * mechanism using RTS/CTS to self to reserve the airtime.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_TIME: Optional (u32)
+ * This attribute is used as the SP offset which is the offset from
+ * TSF after which the wake happens. The units are in microseconds. If
+ * this attribute is not provided, then the value will be set to zero.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_DURATION: Required (u32)
+ * This is the duration of the service period. The units are in TU.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_MANTISSA: Required (u32)
+ * This attribute is used to configure wake interval mantissa.
+ * The units are in TU.
+ */
+enum qca_wlan_vendor_attr_twt_setup {
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST = 1,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_REQ_TYPE = 2,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TRIGGER = 3,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_TYPE = 4,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_ID = 5,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_EXP = 6,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PROTECTION = 7,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_TIME = 8,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_DURATION = 9,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_MANTISSA = 10,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX =
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_twt_resume: Represents attributes for
+ * TWT (Target Wake Time) resume request. These attributes are sent as part of
+ * %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_RESUME and
+ * %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT: Optional (u8)
+ * This attribute is used as the SP offset which is the offset from
+ * TSF after which the wake happens. The units are in microseconds.
+ * If this attribute is not provided, then the value will be set to
+ * zero.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT_SIZE: Required (u32)
+ * This attribute represents the next TWT subfield size.
+ */
+enum qca_wlan_vendor_attr_twt_resume {
+ QCA_WLAN_VENDOR_ATTR_TWT_RESUME_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT = 1,
+ QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT_SIZE = 2,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_TWT_RESUME_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TWT_RESUME_MAX =
+ QCA_WLAN_VENDOR_ATTR_TWT_RESUME_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_twt_setup_req_type - Required (u8)
+ * Represents the setup type being requested for TWT.
+ * @QCA_WLAN_VENDOR_TWT_SETUP_REQUEST: STA is not specifying all the TWT
+ * parameters but relying on AP to fill the parameters during the negotiation.
+ * @QCA_WLAN_VENDOR_TWT_SETUP_SUGGEST: STA will provide all the suggested
+ * values which the AP may accept or AP may provide alternative parameters
+ * which the STA may accept.
+ * @QCA_WLAN_VENDOR_TWT_SETUP_DEMAND: STA is not willing to accept any
+ * alternate parameters than the requested ones.
+ */
+enum qca_wlan_vendor_twt_setup_req_type {
+ QCA_WLAN_VENDOR_TWT_SETUP_REQUEST = 1,
+ QCA_WLAN_VENDOR_TWT_SETUP_SUGGEST = 2,
+ QCA_WLAN_VENDOR_TWT_SETUP_DEMAND = 3,
+};
+
+/**
+ * enum qca_wlan_roam_scan_event_type - Type of roam scan event
+ *
+ * Indicates the type of roam scan event sent by firmware/driver.
+ *
+ * @QCA_WLAN_ROAM_SCAN_TRIGGER_EVENT: Roam scan trigger event type.
+ * @QCA_WLAN_ROAM_SCAN_STOP_EVENT: Roam scan stopped event type.
+ */
+enum qca_wlan_roam_scan_event_type {
+ QCA_WLAN_ROAM_SCAN_TRIGGER_EVENT = 0,
+ QCA_WLAN_ROAM_SCAN_STOP_EVENT = 1,
+};
+
+/**
+ * enum qca_wlan_roam_scan_trigger_reason - Roam scan trigger reason
+ *
+ * Indicates the reason for triggering roam scan by firmware/driver.
+ *
+ * @QCA_WLAN_ROAM_SCAN_TRIGGER_REASON_LOW_RSSI: Due to low RSSI of current AP.
+ * @QCA_WLAN_ROAM_SCAN_TRIGGER_REASON_HIGH_PER: Due to high packet error rate.
+ */
+enum qca_wlan_roam_scan_trigger_reason {
+ QCA_WLAN_ROAM_SCAN_TRIGGER_REASON_LOW_RSSI = 0,
+ QCA_WLAN_ROAM_SCAN_TRIGGER_REASON_HIGH_PER = 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_roam_scan - Vendor subcmd attributes to report
+ * roam scan related details from driver/firmware to user space. enum values
+ * are used for NL attributes sent with
+ * %QCA_NL80211_VENDOR_SUBCMD_ROAM_SCAN_EVENT sub command.
+ */
+enum qca_wlan_vendor_attr_roam_scan {
+ QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_INVALID = 0,
+ /* Encapsulates type of roam scan event being reported. enum
+ * qca_wlan_roam_scan_event_type describes the possible range of
+ * values. u32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_EVENT_TYPE = 1,
+ /* Encapsulates reason for triggering roam scan. enum
+ * qca_wlan_roam_scan_trigger_reason describes the possible range of
+ * values. u32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_TRIGGER_REASON = 2,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_MAX =
+ QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_cfr_method - QCA vendor CFR methods used by
+ * attribute QCA_WLAN_VENDOR_ATTR_PEER_CFR_METHOD as part of vendor
+ * command QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG.
+ */
+enum qca_wlan_vendor_cfr_method {
+ /* CFR method using QOS Null frame */
+ QCA_WLAN_VENDOR_CFR_METHOD_QOS_NULL = 0,
+};
+
+/**
+ * enum qca_wlan_vendor_peer_cfr_capture_attr - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG to configure peer
+ * Channel Frequency Response capture parameters and enable periodic CFR
+ * capture.
+ */
+enum qca_wlan_vendor_peer_cfr_capture_attr {
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_CAPTURE_INVALID = 0,
+ /* 6-byte MAC address of the peer.
+ * This attribute is mandatory.
+ */
+ QCA_WLAN_VENDOR_ATTR_CFR_PEER_MAC_ADDR = 1,
+ /* Enable peer CFR Capture, flag attribute.
+ * This attribute is mandatory to enable peer CFR capture.
+ * If this attribute is not present, peer CFR capture is disabled.
+ */
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE = 2,
+ /* BW of measurement, attribute uses the values in enum nl80211_chan_width
+ * Supported values: 20, 40, 80, 80+80, 160.
+ * Note that all targets may not support all bandwidths.
+ * u8 attribute. This attribute is mandatory if attribute
+ * QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE is used.
+ */
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_BANDWIDTH = 3,
+ /* Periodicity of CFR measurement in msec.
+ * Periodicity should be a multiple of Base timer.
+ * Current Base timer value supported is 10 msecs (default).
+ * 0 for one shot capture. u32 attribute.
+ * This attribute is mandatory if attribute
+ * QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE is used.
+ */
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_PERIODICITY = 4,
+ /* Method used to capture Channel Frequency Response.
+ * Attribute uses the values defined in enum qca_wlan_vendor_cfr_method.
+ * u8 attribute. This attribute is mandatory if attribute
+ * QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE is used.
+ */
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_METHOD = 5,
+ /* Enable periodic CFR capture, flag attribute.
+ * This attribute is mandatory to enable Periodic CFR capture.
+ * If this attribute is not present, periodic CFR capture is disabled.
+ */
+ QCA_WLAN_VENDOR_ATTR_PERIODIC_CFR_CAPTURE_ENABLE = 6,
+
+ /* Keep last */
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_MAX =
+ QCA_WLAN_VENDOR_ATTR_PEER_CFR_AFTER_LAST - 1,
+};
+
#endif /* QCA_VENDOR_H */
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 4eb7356..f65077e 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -275,6 +275,9 @@
#define RX_HS20_ICON "RX-HS20-ICON "
#define RX_MBO_ANQP "RX-MBO-ANQP "
+/* parameters: <Venue Number> <Venue URL> */
+#define RX_VENUE_URL "RX-VENUE-URL "
+
#define HS20_SUBSCRIPTION_REMEDIATION "HS20-SUBSCRIPTION-REMEDIATION "
#define HS20_DEAUTH_IMMINENT_NOTICE "HS20-DEAUTH-IMMINENT-NOTICE "
#define HS20_T_C_ACCEPTANCE "HS20-T-C-ACCEPTANCE "
@@ -329,6 +332,13 @@
/* BSS Transition Management Response frame received */
#define BSS_TM_RESP "BSS-TM-RESP "
+/* Collocated Interference Request frame received;
+ * parameters: <dialog token> <automatic report enabled> <report timeout> */
+#define COLOC_INTF_REQ "COLOC-INTF-REQ "
+/* Collocated Interference Report frame received;
+ * parameters: <STA address> <dialog token> <hexdump of report elements> */
+#define COLOC_INTF_REPORT "COLOC-INTF-REPORT "
+
/* MBO IE with cellular data connection preference received */
#define MBO_CELL_PREFERENCE "MBO-CELL-PREFERENCE "
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index 585db8b..481b346 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -64,6 +64,7 @@
size_t hash_len;
const char *altsubject[TLS_MAX_ALT_SUBJECT];
int num_altsubject;
+ const char *serial_num;
} peer_cert;
struct {
@@ -253,6 +254,18 @@
int tls_connection_established(void *tls_ctx, struct tls_connection *conn);
/**
+ * tls_connection_peer_serial_num - Fetch peer certificate serial number
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: Allocated string buffer containing the peer certificate serial
+ * number or %NULL on error.
+ *
+ * The caller is responsible for freeing the returned buffer with os_free().
+ */
+char * tls_connection_peer_serial_num(void *tls_ctx,
+ struct tls_connection *conn);
+
+/**
* tls_connection_shutdown - Shutdown TLS connection
* @tls_ctx: TLS context data from tls_init()
* @conn: Connection context data from tls_connection_init()
diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c
index 7ee3fa3..36dafd2 100644
--- a/src/crypto/tls_gnutls.c
+++ b/src/crypto/tls_gnutls.c
@@ -295,6 +295,14 @@
}
+char * tls_connection_peer_serial_num(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ /* TODO */
+ return NULL;
+}
+
+
int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn)
{
struct tls_global *global = ssl_ctx;
diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c
index c7cb5de..d289c94 100644
--- a/src/crypto/tls_internal.c
+++ b/src/crypto/tls_internal.c
@@ -177,6 +177,14 @@
}
+char * tls_connection_peer_serial_num(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ /* TODO */
+ return NULL;
+}
+
+
int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
{
#ifdef CONFIG_TLS_INTERNAL_CLIENT
diff --git a/src/crypto/tls_none.c b/src/crypto/tls_none.c
index dd5681e..5d0c6bd 100644
--- a/src/crypto/tls_none.c
+++ b/src/crypto/tls_none.c
@@ -45,6 +45,13 @@
}
+char * tls_connection_peer_serial_num(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ return NULL;
+}
+
+
int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
{
return -1;
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index 1aee10c..5bb14e2 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -111,6 +111,12 @@
return BN_num_bits(r->n);
}
#endif /* CONFIG_SUITEB */
+
+
+static const unsigned char * ASN1_STRING_get0_data(const ASN1_STRING *x)
+{
+ return ASN1_STRING_data((ASN1_STRING *) x);
+}
#endif
#ifdef ANDROID
@@ -1554,6 +1560,31 @@
}
+char * tls_connection_peer_serial_num(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ ASN1_INTEGER *ser;
+ char *serial_num;
+ size_t len;
+
+ if (!conn->peer_cert)
+ return NULL;
+
+ ser = X509_get_serialNumber(conn->peer_cert);
+ if (!ser)
+ return NULL;
+
+ len = ASN1_STRING_length(ser) * 2 + 1;
+ serial_num = os_malloc(len);
+ if (!serial_num)
+ return NULL;
+ wpa_snprintf_hex_uppercase(serial_num, len,
+ ASN1_STRING_get0_data(ser),
+ ASN1_STRING_length(ser));
+ return serial_num;
+}
+
+
int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn)
{
if (conn == NULL)
@@ -1842,6 +1873,8 @@
GENERAL_NAME *gen;
void *ext;
stack_index_t i;
+ ASN1_INTEGER *ser;
+ char serial_num[128];
#ifdef CONFIG_SHA256
u8 hash[32];
#endif /* CONFIG_SHA256 */
@@ -1870,6 +1903,14 @@
ev.peer_cert.depth = depth;
ev.peer_cert.subject = subject;
+ ser = X509_get_serialNumber(err_cert);
+ if (ser) {
+ wpa_snprintf_hex_uppercase(serial_num, sizeof(serial_num),
+ ASN1_STRING_get0_data(ser),
+ ASN1_STRING_length(ser));
+ ev.peer_cert.serial_num = serial_num;
+ }
+
ext = X509_get_ext_d2i(err_cert, NID_subject_alt_name, NULL, NULL);
for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) {
char *pos;
diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c
index 9544e2f..cc8c704 100644
--- a/src/crypto/tls_wolfssl.c
+++ b/src/crypto/tls_wolfssl.c
@@ -347,6 +347,14 @@
}
+char * tls_connection_peer_serial_num(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ /* TODO */
+ return NULL;
+}
+
+
int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
{
WOLFSSL_SESSION *session;
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 22a37cc..d34c679 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -1577,6 +1577,8 @@
#define WPA_DRIVER_FLAGS_OCE_STA_CFON 0x0020000000000000ULL
/** Driver supports MFP-optional in the connect command */
#define WPA_DRIVER_FLAGS_MFP_OPTIONAL 0x0040000000000000ULL
+/** Driver is a self-managed regulatory device */
+#define WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY 0x0080000000000000ULL
u64 flags;
#define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 39a02d3..771e766 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -40,6 +40,29 @@
#include "driver_nl80211.h"
+/* support for extack if compilation headers are too old */
+#ifndef NETLINK_EXT_ACK
+#define NETLINK_EXT_ACK 11
+enum nlmsgerr_attrs {
+ NLMSGERR_ATTR_UNUSED,
+ NLMSGERR_ATTR_MSG,
+ NLMSGERR_ATTR_OFFS,
+ NLMSGERR_ATTR_COOKIE,
+
+ __NLMSGERR_ATTR_MAX,
+ NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1
+};
+#endif
+#ifndef NLM_F_CAPPED
+#define NLM_F_CAPPED 0x100
+#endif
+#ifndef NLM_F_ACK_TLVS
+#define NLM_F_ACK_TLVS 0x200
+#endif
+#ifndef SOL_NETLINK
+#define SOL_NETLINK 270
+#endif
+
#ifndef CONFIG_LIBNL20
/*
* libnl 1.1 has a bug, it tries to allocate socket numbers densely
@@ -302,8 +325,35 @@
static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
void *arg)
{
+ struct nlmsghdr *nlh = (struct nlmsghdr *) err - 1;
+ int len = nlh->nlmsg_len;
+ struct nlattr *attrs;
+ struct nlattr *tb[NLMSGERR_ATTR_MAX + 1];
int *ret = arg;
+ int ack_len = sizeof(*nlh) + sizeof(int) + sizeof(*nlh);
+
*ret = err->error;
+
+ if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS))
+ return NL_SKIP;
+
+ if (!(nlh->nlmsg_flags & NLM_F_CAPPED))
+ ack_len += err->msg.nlmsg_len - sizeof(*nlh);
+
+ if (len <= ack_len)
+ return NL_STOP;
+
+ attrs = (void *) ((unsigned char *) nlh + ack_len);
+ len -= ack_len;
+
+ nla_parse(tb, NLMSGERR_ATTR_MAX, attrs, len, NULL);
+ if (tb[NLMSGERR_ATTR_MSG]) {
+ len = strnlen((char *) nla_data(tb[NLMSGERR_ATTR_MSG]),
+ nla_len(tb[NLMSGERR_ATTR_MSG]));
+ wpa_printf(MSG_ERROR, "nl80211: kernel reports: %*s",
+ len, (char *) nla_data(tb[NLMSGERR_ATTR_MSG]));
+ }
+
return NL_SKIP;
}
@@ -342,7 +392,7 @@
void *valid_data)
{
struct nl_cb *cb;
- int err = -ENOMEM;
+ int err = -ENOMEM, opt;
if (!msg)
return -ENOMEM;
@@ -351,6 +401,11 @@
if (!cb)
goto out;
+ /* try to set NETLINK_EXT_ACK to 1, ignoring errors */
+ opt = 1;
+ setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK,
+ NETLINK_EXT_ACK, &opt, sizeof(opt));
+
err = nl_send_auto_complete(nl_handle, msg);
if (err < 0)
goto out;
@@ -2145,6 +2200,11 @@
/* WNM-Sleep Mode Response */
if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x11", 2) < 0)
ret = -1;
+#ifdef CONFIG_WNM
+ /* WNM - Collocated Interference Request */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x0b", 2) < 0)
+ ret = -1;
+#endif /* CONFIG_WNM */
#ifdef CONFIG_HS20
/* WNM-Notification */
@@ -6525,8 +6585,15 @@
struct wpa_driver_nl80211_data *drv = bss->drv;
char name[IFNAMSIZ + 1];
union wpa_event_data event;
+ int ret;
- os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid);
+ ret = os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid);
+ if (ret >= (int) sizeof(name))
+ wpa_printf(MSG_WARNING,
+ "nl80211: WDS interface name was truncated");
+ else if (ret < 0)
+ return ret;
+
if (ifname_wds)
os_strlcpy(ifname_wds, name, IFNAMSIZ + 1);
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index a51b50d..7b360d2 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -818,6 +818,9 @@
capa->max_csa_counters =
nla_get_u8(tb[NL80211_ATTR_MAX_CSA_COUNTERS]);
+ if (tb[NL80211_ATTR_WIPHY_SELF_MANAGED_REG])
+ capa->flags |= WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY;
+
return NL_SKIP;
}
@@ -1918,6 +1921,13 @@
return -ENOMEM;
nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG);
+ if (drv->capa.flags & WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY) {
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, drv->wiphy_idx)) {
+ nlmsg_free(msg);
+ return -1;
+ }
+ }
+
return send_and_recv_msgs(drv, msg, nl80211_get_reg, results);
}
diff --git a/src/drivers/driver_nl80211_monitor.c b/src/drivers/driver_nl80211_monitor.c
index 9376d11..f25cd79 100644
--- a/src/drivers/driver_nl80211_monitor.c
+++ b/src/drivers/driver_nl80211_monitor.c
@@ -361,8 +361,17 @@
*/
snprintf(buf, IFNAMSIZ, "mon-%s", drv->first_bss->ifname + 4);
} else {
+ int ret;
+
/* Non-P2P interface with AP functionality. */
- snprintf(buf, IFNAMSIZ, "mon.%s", drv->first_bss->ifname);
+ ret = os_snprintf(buf, IFNAMSIZ, "mon.%s",
+ drv->first_bss->ifname);
+ if (ret >= (int) sizeof(buf))
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Monitor interface name has been truncated to %s",
+ buf);
+ else if (ret < 0)
+ return ret;
}
buf[IFNAMSIZ - 1] = '\0';
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index e55e2d5..974c475 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -670,6 +670,9 @@
}
+/* Note: If ext_session and/or ext_emsk are passed to this function, they are
+ * expected to point to allocated memory and those allocations will be freed
+ * unconditionally. */
void eap_peer_erp_init(struct eap_sm *sm, u8 *ext_session_id,
size_t ext_session_id_len, u8 *ext_emsk,
size_t ext_emsk_len)
@@ -688,7 +691,7 @@
realm = eap_home_realm(sm);
if (!realm)
- return;
+ goto fail;
realm_len = os_strlen(realm);
wpa_printf(MSG_DEBUG, "EAP: Realm for ERP keyName-NAI: %s", realm);
eap_erp_remove_keys_realm(sm, realm);
@@ -775,7 +778,10 @@
dl_list_add(&sm->erp_keys, &erp->list);
erp = NULL;
fail:
- bin_clear_free(emsk, emsk_len);
+ if (ext_emsk)
+ bin_clear_free(ext_emsk, ext_emsk_len);
+ else
+ bin_clear_free(emsk, emsk_len);
bin_clear_free(ext_session_id, ext_session_id_len);
bin_clear_free(erp, sizeof(*erp));
os_free(realm);
diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c
index 90ac3cf..761c16a 100644
--- a/src/eap_peer/eap_pwd.c
+++ b/src/eap_peer/eap_pwd.c
@@ -696,7 +696,7 @@
const struct wpabuf *reqData,
const u8 *payload, size_t payload_len)
{
- struct crypto_hash *hash;
+ struct crypto_hash *hash = NULL;
u32 cs;
u16 grp;
u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
@@ -783,6 +783,7 @@
/* random function fin */
eap_pwd_h_final(hash, conf);
+ hash = NULL;
ptr = (u8 *) payload;
if (os_memcmp_const(conf, ptr, SHA256_MAC_LEN)) {
@@ -836,6 +837,7 @@
/* all done */
eap_pwd_h_final(hash, conf);
+ hash = NULL;
if (compute_keys(data->grp, data->k,
data->my_scalar, data->server_scalar, conf, ptr,
@@ -860,6 +862,10 @@
} else {
eap_pwd_state(data, SUCCESS_ON_FRAG_COMPLETION);
}
+
+ /* clean allocated memory */
+ if (hash)
+ eap_pwd_h_final(hash, conf);
}
diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h
index bb3641f..4fbc661 100644
--- a/src/eap_server/eap.h
+++ b/src/eap_server/eap.h
@@ -152,6 +152,7 @@
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);
+const char * eap_get_serial_num(struct eap_sm *sm);
struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm);
void eap_server_clear_identity(struct eap_sm *sm);
void eap_server_mschap_rx_callback(struct eap_sm *sm, const char *source,
diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h
index 3d6f8d5..cf8a9f0 100644
--- a/src/eap_server/eap_i.h
+++ b/src/eap_server/eap_i.h
@@ -159,6 +159,7 @@
void *eap_method_priv;
u8 *identity;
size_t identity_len;
+ char *serial_num;
/* Whether Phase 2 method should validate identity match */
int require_identity_match;
int lastId; /* Identifier used in the last EAP-Packet */
diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c
index c9da72e..38a1b5c 100644
--- a/src/eap_server/eap_server.c
+++ b/src/eap_server/eap_server.c
@@ -1920,6 +1920,7 @@
wpabuf_free(sm->lastReqData);
wpabuf_free(sm->eap_if.eapRespData);
os_free(sm->identity);
+ os_free(sm->serial_num);
os_free(sm->pac_opaque_encr_key);
os_free(sm->eap_fast_a_id);
os_free(sm->eap_fast_a_id_info);
@@ -1991,6 +1992,17 @@
}
+/**
+ * eap_get_serial_num - Get the serial number of user certificate
+ * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+ * Returns: Pointer to the serial number or %NULL if not available
+ */
+const char * eap_get_serial_num(struct eap_sm *sm)
+{
+ return sm->serial_num;
+}
+
+
void eap_erp_update_identity(struct eap_sm *sm, const u8 *eap, size_t len)
{
#ifdef CONFIG_ERP
diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c
index 4f9cb08..0ae7867 100644
--- a/src/eap_server/eap_server_tls_common.c
+++ b/src/eap_server/eap_server_tls_common.c
@@ -341,6 +341,11 @@
data->tls_v13 = os_strcmp(buf, "TLSv1.3") == 0;
}
+ if (!sm->serial_num &&
+ tls_connection_established(sm->ssl_ctx, data->conn))
+ sm->serial_num = tls_connection_peer_serial_num(sm->ssl_ctx,
+ data->conn);
+
return 0;
}
diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c
index bfbc995..9f029b0 100644
--- a/src/eapol_supp/eapol_supp_sm.c
+++ b/src/eapol_supp/eapol_supp_sm.c
@@ -503,10 +503,14 @@
session_id = eap_proxy_get_eap_session_id(
sm->eap_proxy, &session_id_len);
emsk = eap_proxy_get_emsk(sm->eap_proxy, &emsk_len);
- if (sm->config->erp && session_id && emsk)
+ if (sm->config->erp && session_id && emsk) {
eap_peer_erp_init(sm->eap, session_id,
session_id_len, emsk,
emsk_len);
+ } else {
+ os_free(session_id);
+ bin_clear_free(emsk, emsk_len);
+ }
}
return;
}
diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c
index d66f766..e3afc0d 100644
--- a/src/radius/radius_server.c
+++ b/src/radius/radius_server.c
@@ -818,6 +818,54 @@
}
+static void db_update_last_msk(struct radius_session *sess, const char *msk)
+{
+#ifdef CONFIG_RADIUS_TEST
+#ifdef CONFIG_SQLITE
+ char *sql = NULL;
+ char *id_str = NULL;
+ const u8 *id;
+ size_t id_len;
+ const char *serial_num;
+
+ if (!sess->server->db)
+ return;
+
+ serial_num = eap_get_serial_num(sess->eap);
+ if (serial_num) {
+ id_len = 5 + os_strlen(serial_num) + 1;
+ id_str = os_malloc(id_len);
+ if (!id_str)
+ return;
+ os_snprintf(id_str, id_len, "cert-%s", serial_num);
+ } else {
+ id = eap_get_identity(sess->eap, &id_len);
+ if (!id)
+ return;
+ id_str = os_malloc(id_len + 1);
+ if (!id_str)
+ return;
+ os_memcpy(id_str, id, id_len);
+ id_str[id_len] = '\0';
+ }
+
+ sql = sqlite3_mprintf("UPDATE users SET last_msk=%Q WHERE identity=%Q",
+ msk, id_str);
+ os_free(id_str);
+ if (!sql)
+ return;
+
+ if (sqlite3_exec(sess->server->db, sql, NULL, NULL, NULL) !=
+ SQLITE_OK) {
+ RADIUS_DEBUG("Failed to update last_msk: %s",
+ sqlite3_errmsg(sess->server->db));
+ }
+ sqlite3_free(sql);
+#endif /* CONFIG_SQLITE */
+#endif /* CONFIG_RADIUS_TEST */
+}
+
+
static struct radius_msg *
radius_server_encapsulate_eap(struct radius_server_data *data,
struct radius_client *client,
@@ -863,9 +911,18 @@
if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->eap_if->eapKeyData) {
int len;
#ifdef CONFIG_RADIUS_TEST
+ char buf[2 * 64 + 1];
+
+ 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';
+
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;
@@ -879,6 +936,8 @@
fclose(f);
}
}
+
+ db_update_last_msk(sess, buf);
#endif /* CONFIG_RADIUS_TEST */
if (sess->eap_if->eapKeyDataLen > 64) {
len = 32;
@@ -1336,10 +1395,12 @@
if (sess->eap_if->eapSuccess || sess->eap_if->eapFail)
is_complete = 1;
- if (sess->eap_if->eapFail)
+ if (sess->eap_if->eapFail) {
srv_log(sess, "EAP authentication failed");
- else if (sess->eap_if->eapSuccess)
+ db_update_last_msk(sess, "FAIL");
+ } else if (sess->eap_if->eapSuccess) {
srv_log(sess, "EAP authentication succeeded");
+ }
if (sess->eap_if->eapSuccess)
radius_server_hs20_t_c_check(sess, msg);
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 2b4d292..72a2d66 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -1007,7 +1007,7 @@
}
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
- "WPA: IGTK keyid %d pn %02x%02x%02x%02x%02x%02x",
+ "WPA: IGTK keyid %d pn " COMPACT_MACSTR,
keyidx, MAC2STR(igtk->pn));
wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK", igtk->igtk, len);
if (keyidx > 4095) {
@@ -1461,7 +1461,13 @@
if (ie.gtk)
wpa_sm_set_rekey_offload(sm);
- if (sm->proto == WPA_PROTO_RSN && wpa_key_mgmt_suite_b(sm->key_mgmt)) {
+ /* Add PMKSA cache entry for Suite B AKMs here since PMKID can be
+ * calculated only after KCK has been derived. Though, do not replace an
+ * existing PMKSA entry after each 4-way handshake (i.e., new KCK/PMKID)
+ * to avoid unnecessary changes of PMKID while continuing to use the
+ * same PMK. */
+ if (sm->proto == WPA_PROTO_RSN && wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+ !sm->cur_pmksa) {
struct rsn_pmksa_cache_entry *sa;
sa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, NULL,
diff --git a/src/utils/browser-wpadebug.c b/src/utils/browser-wpadebug.c
index 062e6fe..dfb4b67 100644
--- a/src/utils/browser-wpadebug.c
+++ b/src/utils/browser-wpadebug.c
@@ -97,6 +97,7 @@
if (pid == 0) {
/* run the external command in the child process */
char *argv[14];
+ char *envp[] = { "PATH=/system/bin:/vendor/bin", NULL };
argv[0] = "browser-wpadebug";
argv[1] = "start";
@@ -113,8 +114,8 @@
argv[12] = "-3"; /* USER_CURRENT_OR_SELF */
argv[13] = NULL;
- execv("/system/bin/am", argv);
- wpa_printf(MSG_ERROR, "execv: %s", strerror(errno));
+ execve("/system/bin/am", argv, envp);
+ wpa_printf(MSG_ERROR, "execve: %s", strerror(errno));
exit(0);
return -1;
}
diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c
index 62758d8..a56462b 100644
--- a/src/utils/wpa_debug.c
+++ b/src/utils/wpa_debug.c
@@ -58,6 +58,10 @@
#ifndef CONFIG_NO_STDOUT_DEBUG
#ifdef CONFIG_DEBUG_FILE
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
static FILE *out_file = NULL;
#endif /* CONFIG_DEBUG_FILE */
@@ -539,6 +543,8 @@
int wpa_debug_open_file(const char *path)
{
#ifdef CONFIG_DEBUG_FILE
+ int out_fd;
+
if (!path)
return 0;
@@ -548,10 +554,28 @@
last_path = os_strdup(path);
}
- out_file = fopen(path, "a");
+ out_fd = open(path, O_CREAT | O_APPEND | O_WRONLY,
+ S_IRUSR | S_IWUSR | S_IRGRP);
+ if (out_fd < 0) {
+ wpa_printf(MSG_ERROR,
+ "%s: Failed to open output file descriptor, using standard output",
+ __func__);
+ return -1;
+ }
+
+#ifdef __linux__
+ if (fcntl(out_fd, F_SETFD, FD_CLOEXEC) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "%s: Failed to set FD_CLOEXEC - continue without: %s",
+ __func__, strerror(errno));
+ }
+#endif /* __linux__ */
+
+ out_file = fdopen(out_fd, "a");
if (out_file == NULL) {
wpa_printf(MSG_ERROR, "wpa_debug_open_file: Failed to open "
"output file, using standard output");
+ close(out_fd);
return -1;
}
#ifndef _WIN32
diff --git a/src/utils/xml-utils.c b/src/utils/xml-utils.c
index a37a92d..dae91fe 100644
--- a/src/utils/xml-utils.c
+++ b/src/utils/xml-utils.c
@@ -246,7 +246,9 @@
xml_node_create_text(ctx, tnds, NULL, "Path", uri);
val = get_val(ctx, node);
- xml_node_create_text(ctx, tnds, NULL, "Value", val ? val : "");
+ if (val || !xml_node_first_child(ctx, node))
+ xml_node_create_text(ctx, tnds, NULL, "Value",
+ val ? val : "");
xml_node_get_text_free(ctx, val);
new_uri = add_path(uri, name);
diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c
index 2e34721..bcae1ba 100644
--- a/src/wps/wps_common.c
+++ b/src/wps/wps_common.c
@@ -654,6 +654,7 @@
pub = wpabuf_zeropad(pub, 192);
if (pub == NULL) {
wpabuf_free(priv);
+ dh5_free(dh_ctx);
return -1;
}
wpa_hexdump_buf(MSG_DEBUG, "WPS: Generated new DH pubkey", pub);
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index 5aa07ea..3a41db9 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -103,6 +103,7 @@
ANQP_DUP(hs20_operating_class);
ANQP_DUP(hs20_osu_providers_list);
ANQP_DUP(hs20_operator_icon_metadata);
+ ANQP_DUP(hs20_osu_providers_nai_list);
#endif /* CONFIG_HS20 */
#undef ANQP_DUP
@@ -187,6 +188,7 @@
wpabuf_free(anqp->hs20_operating_class);
wpabuf_free(anqp->hs20_osu_providers_list);
wpabuf_free(anqp->hs20_operator_icon_metadata);
+ wpabuf_free(anqp->hs20_osu_providers_nai_list);
#endif /* CONFIG_HS20 */
os_free(anqp);
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index 9179385..5251b2c 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -51,6 +51,7 @@
struct wpabuf *hs20_operating_class;
struct wpabuf *hs20_osu_providers_list;
struct wpabuf *hs20_operator_icon_metadata;
+ struct wpabuf *hs20_osu_providers_nai_list;
#endif /* CONFIG_HS20 */
};
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index be6fc5d..5cdc059 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -2637,7 +2637,6 @@
#ifdef CONFIG_MBO
os_free(config->non_pref_chan);
#endif /* CONFIG_MBO */
- os_free(config->p2p_device_persistent_mac_addr);
os_free(config);
}
@@ -3156,14 +3155,16 @@
}
roaming_consortiums_len[num_roaming_consortiums] = len / 2;
num_roaming_consortiums++;
- if (num_roaming_consortiums > MAX_ROAMING_CONS) {
+
+ if (!end)
+ break;
+
+ if (num_roaming_consortiums >= MAX_ROAMING_CONS) {
wpa_printf(MSG_INFO,
"Too many roaming_consortiums OIs");
return -1;
}
- if (!end)
- break;
pos = end + 1;
}
@@ -4439,6 +4440,20 @@
return 0;
}
+static int wpa_config_process_p2p_device_persistent_mac_addr(
+ const struct global_parse_data *data,
+ struct wpa_config *config, int line, const char *pos)
+{
+ if (hwaddr_aton2(pos, config->p2p_device_persistent_mac_addr) < 0) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid p2p_device_persistent_mac_addr '%s'",
+ line, pos);
+ return -1;
+ }
+
+ return 0;
+}
+
#endif /* CONFIG_P2P */
@@ -4680,6 +4695,9 @@
{ IPV4(ip_addr_start), 0 },
{ IPV4(ip_addr_end), 0 },
{ INT_RANGE(p2p_cli_probe, 0, 1), 0 },
+ { INT(p2p_device_random_mac_addr), 0 },
+ { FUNC(p2p_device_persistent_mac_addr), 0 },
+ { INT(p2p_interface_random_mac_addr), 0 },
#endif /* CONFIG_P2P */
{ FUNC(country), CFG_CHANGED_COUNTRY },
{ INT(bss_max_count), 0 },
@@ -4754,9 +4772,8 @@
{ INT(gas_rand_addr_lifetime), 0 },
{ INT_RANGE(gas_rand_mac_addr, 0, 2), 0 },
{ INT_RANGE(dpp_config_processing, 0, 2), 0 },
- { INT(p2p_device_random_mac_addr), 0 },
- { STR(p2p_device_persistent_mac_addr), 0 },
- { INT(p2p_interface_random_mac_addr), 0 },
+ { INT_RANGE(bss_no_flush_when_down, 0, 1), 0 },
+ { INT_RANGE(coloc_intf_reporting, 0, 1), 0 },
{ INT_RANGE(bss_no_flush_when_down, 0, 1), 0 },
};
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index e431b9c..6eb0074 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -1471,6 +1471,15 @@
int dpp_config_processing;
/**
+ * coloc_intf_reporting - Colocated interference reporting
+ *
+ * dot11CoLocIntfReportingActivated
+ * 0 = disabled (false)
+ * 1 = enabled (true)
+ */
+ int coloc_intf_reporting;
+
+ /**
* p2p_device_random_mac_addr - P2P Device MAC address policy default
*
* 0 = use permanent MAC address
@@ -1487,7 +1496,7 @@
* and need to restore to last used MAC address.
* format: aa:bb:cc:dd:ee:ff
*/
- char *p2p_device_persistent_mac_addr;
+ u8 p2p_device_persistent_mac_addr[ETH_ALEN];
/**
* p2p_interface_random_mac_addr - P2P Interface MAC address policy default
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 1cdfbc2..becc809 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -1511,19 +1511,21 @@
if (config->dpp_config_processing)
fprintf(f, "dpp_config_processing=%d\n",
config->dpp_config_processing);
+ if (config->coloc_intf_reporting)
+ fprintf(f, "coloc_intf_reporting=%d\n",
+ config->coloc_intf_reporting);
if (config->p2p_device_random_mac_addr)
fprintf(f, "p2p_device_random_mac_addr=%d\n",
config->p2p_device_random_mac_addr);
- if (config->p2p_device_persistent_mac_addr)
- fprintf(f, "p2p_device_persistent_mac_addr=%s\n",
- config->p2p_device_persistent_mac_addr);
+ if (!is_zero_ether_addr(config->p2p_device_persistent_mac_addr))
+ fprintf(f, "p2p_device_persistent_mac_addr=" MACSTR "\n",
+ MAC2STR(config->p2p_device_persistent_mac_addr));
if (config->p2p_interface_random_mac_addr)
fprintf(f, "p2p_interface_random_mac_addr=%d\n",
config->p2p_interface_random_mac_addr);
if (config->bss_no_flush_when_down)
fprintf(f, "bss_no_flush_when_down=%d\n",
config->bss_no_flush_when_down);
-
}
#endif /* CONFIG_NO_CONFIG_WRITE */
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index 3000c43..d2a52d7 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -927,6 +927,16 @@
* 1 = disable transition mode (allow connection only with OWE)
*/
int owe_only;
+
+ /**
+ * owe_transition_bss_select_count - OWE transition BSS select count
+ *
+ * This is an internally used variable (i.e., not used in external
+ * configuration) to track the number of selection attempts done for
+ * OWE BSS in transition mode. This allows fallback to an open BSS if
+ * the selection attempts for OWE BSS exceed the configured threshold.
+ */
+ int owe_transition_bss_select_count;
};
#endif /* CONFIG_SSID_H */
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index fe39c25..77a3133 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -750,6 +750,15 @@
ret = wpas_ctrl_iface_set_ric_ies(wpa_s, value);
} else if (os_strcasecmp(cmd, "roaming") == 0) {
ret = wpa_drv_roaming(wpa_s, atoi(value), NULL);
+#ifdef CONFIG_WNM
+ } else if (os_strcasecmp(cmd, "coloc_intf_elems") == 0) {
+ struct wpabuf *elems;
+
+ elems = wpabuf_parse_bin(value);
+ if (!elems)
+ return -1;
+ wnm_set_coloc_intf_elems(wpa_s, elems);
+#endif /* CONFIG_WNM */
} else {
value[-1] = '=';
ret = wpa_config_process_global(wpa_s->conf, cmd, -1);
@@ -4809,6 +4818,8 @@
anqp->hs20_osu_providers_list);
pos = anqp_add_hex(pos, end, "hs20_operator_icon_metadata",
anqp->hs20_operator_icon_metadata);
+ pos = anqp_add_hex(pos, end, "hs20_osu_providers_nai_list",
+ anqp->hs20_osu_providers_nai_list);
#endif /* CONFIG_HS20 */
dl_list_for_each(elem, &anqp->anqp_elems,
@@ -7435,6 +7446,22 @@
list);
}
+
+static int wpas_ctrl_iface_coloc_intf_report(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ struct wpabuf *elems;
+ int ret;
+
+ elems = wpabuf_parse_bin(cmd);
+ if (!elems)
+ return -1;
+
+ ret = wnm_send_coloc_intf_report(wpa_s, 0, elems);
+ wpabuf_free(elems);
+ return ret;
+}
+
#endif /* CONFIG_WNM */
@@ -10421,6 +10448,9 @@
} else if (os_strncmp(buf, "WNM_BSS_QUERY ", 14) == 0) {
if (wpas_ctrl_iface_wnm_bss_query(wpa_s, buf + 14))
reply_len = -1;
+ } else if (os_strncmp(buf, "COLOC_INTF_REPORT ", 18) == 0) {
+ if (wpas_ctrl_iface_coloc_intf_report(wpa_s, buf + 18))
+ reply_len = -1;
#endif /* CONFIG_WNM */
} else if (os_strcmp(buf, "FLUSH") == 0) {
wpa_supplicant_ctrl_iface_flush(wpa_s);
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 7eb25ea..ef28ffe 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -50,6 +50,9 @@
#include "dpp_supplicant.h"
+#define MAX_OWE_TRANSITION_BSS_SELECT_COUNT 5
+
+
#ifndef CONFIG_NO_SCAN_PROCESSING
static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s,
int new_scan, int own_request);
@@ -314,6 +317,7 @@
wpas_rrm_reset(wpa_s);
wpa_s->wnmsleep_used = 0;
+ wnm_clear_coloc_intf_reporting(wpa_s);
#ifdef CONFIG_TESTING_OPTIONS
wpa_s->last_tk_alg = WPA_ALG_NONE;
@@ -703,6 +707,19 @@
#ifdef CONFIG_OWE
if ((ssid->key_mgmt & WPA_KEY_MGMT_OWE) && !ssid->owe_only &&
!wpa_ie && !rsn_ie) {
+ if (wpa_s->owe_transition_select &&
+ wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE) &&
+ ssid->owe_transition_bss_select_count + 1 <=
+ MAX_OWE_TRANSITION_BSS_SELECT_COUNT) {
+ ssid->owe_transition_bss_select_count++;
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip OWE transition BSS (selection count %d does not exceed %d)",
+ ssid->owe_transition_bss_select_count,
+ MAX_OWE_TRANSITION_BSS_SELECT_COUNT);
+ wpa_s->owe_transition_search = 1;
+ return 0;
+ }
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" allow in OWE transition mode");
@@ -1387,8 +1404,11 @@
for (i = 0; i < wpa_s->last_scan_res_used; i++) {
struct wpa_bss *bss = wpa_s->last_scan_res[i];
+
+ wpa_s->owe_transition_select = 1;
*selected_ssid = wpa_scan_res_match(wpa_s, i, bss, group,
only_first_ssid, 1);
+ wpa_s->owe_transition_select = 0;
if (!*selected_ssid)
continue;
wpa_dbg(wpa_s, MSG_DEBUG, " selected BSS " MACSTR
@@ -1935,6 +1955,7 @@
if (wpa_s->p2p_mgmt)
return 0; /* no normal connection on p2p_mgmt interface */
+ wpa_s->owe_transition_search = 0;
selected = wpa_supplicant_pick_network(wpa_s, &ssid);
#ifdef CONFIG_MESH
@@ -2036,6 +2057,17 @@
return 0;
}
#endif /* CONFIG_WPS */
+#ifdef CONFIG_OWE
+ if (wpa_s->owe_transition_search) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "OWE: Use shorter wait during transition mode search");
+ timeout_sec = 0;
+ timeout_usec = 500000;
+ wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
+ timeout_usec);
+ return 0;
+ }
+#endif /* CONFIG_OWE */
if (wpa_supplicant_req_sched_scan(wpa_s))
wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
timeout_usec);
@@ -3970,6 +4002,16 @@
}
#endif /* CONFIG_SAE */
+#ifdef CONFIG_DPP
+ if (wpa_s->current_ssid &&
+ wpa_s->current_ssid->key_mgmt == WPA_KEY_MGMT_DPP &&
+ !data->assoc_reject.timed_out) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "DPP: Drop PMKSA cache entry");
+ wpa_sm_aborted_cached(wpa_s->wpa);
+ wpa_sm_pmksa_cache_flush(wpa_s->wpa, wpa_s->current_ssid);
+ }
+#endif /* CONFIG_DPP */
+
#ifdef CONFIG_FILS
/* Update ERP next sequence number */
if (wpa_s->auth_alg == WPA_AUTH_ALG_FILS) {
@@ -4299,6 +4341,7 @@
#endif /* CONFIG_AP */
wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_CS);
+ wnm_clear_coloc_intf_reporting(wpa_s);
break;
#ifdef CONFIG_AP
#ifdef NEED_AP_MLME
diff --git a/wpa_supplicant/hs20_supplicant.c b/wpa_supplicant/hs20_supplicant.c
index af28e88..814d18e 100644
--- a/wpa_supplicant/hs20_supplicant.c
+++ b/wpa_supplicant/hs20_supplicant.c
@@ -50,9 +50,12 @@
u8 bssid[ETH_ALEN];
u8 osu_ssid[SSID_MAX_LEN];
u8 osu_ssid_len;
+ u8 osu_ssid2[SSID_MAX_LEN];
+ u8 osu_ssid2_len;
char server_uri[256];
u32 osu_methods; /* bit 0 = OMA-DM, bit 1 = SOAP-XML SPP */
char osu_nai[256];
+ char osu_nai2[256];
struct osu_lang_string friendly_name[OSU_MAX_ITEMS];
size_t friendly_name_count;
struct osu_lang_string serv_desc[OSU_MAX_ITEMS];
@@ -676,6 +679,15 @@
wpabuf_alloc_copy(pos, slen);
}
break;
+ case HS20_STYPE_OSU_PROVIDERS_NAI_LIST:
+ wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR
+ " OSU Providers NAI List", MAC2STR(sa));
+ if (anqp) {
+ wpabuf_free(anqp->hs20_osu_providers_nai_list);
+ anqp->hs20_osu_providers_nai_list =
+ wpabuf_alloc_copy(pos, slen);
+ }
+ break;
default:
wpa_printf(MSG_DEBUG, "HS20: Unsupported subtype %u", subtype);
break;
@@ -755,8 +767,15 @@
wpa_ssid_txt(osu->osu_ssid,
osu->osu_ssid_len));
}
+ if (osu->osu_ssid2_len) {
+ fprintf(f, "osu_ssid2=%s\n",
+ wpa_ssid_txt(osu->osu_ssid2,
+ osu->osu_ssid2_len));
+ }
if (osu->osu_nai[0])
fprintf(f, "osu_nai=%s\n", osu->osu_nai);
+ if (osu->osu_nai2[0])
+ fprintf(f, "osu_nai2=%s\n", osu->osu_nai2);
for (j = 0; j < osu->friendly_name_count; j++) {
fprintf(f, "friendly_name=%s:%s\n",
osu->friendly_name[j].lang,
@@ -820,6 +839,7 @@
static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
const u8 *osu_ssid, u8 osu_ssid_len,
+ const u8 *osu_ssid2, u8 osu_ssid2_len,
const u8 *pos, size_t len)
{
struct osu_provider *prov;
@@ -841,6 +861,9 @@
os_memcpy(prov->bssid, bss->bssid, ETH_ALEN);
os_memcpy(prov->osu_ssid, osu_ssid, osu_ssid_len);
prov->osu_ssid_len = osu_ssid_len;
+ if (osu_ssid2)
+ os_memcpy(prov->osu_ssid2, osu_ssid2, osu_ssid2_len);
+ prov->osu_ssid2_len = osu_ssid2_len;
/* OSU Friendly Name Length */
if (end - pos < 2) {
@@ -1022,18 +1045,30 @@
struct wpabuf *prov_anqp;
const u8 *pos, *end;
u16 len;
- const u8 *osu_ssid;
- u8 osu_ssid_len;
+ const u8 *osu_ssid, *osu_ssid2;
+ u8 osu_ssid_len, osu_ssid2_len;
u8 num_providers;
hs20_free_osu_prov(wpa_s);
dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ struct wpa_ie_data data;
+ const u8 *ie;
+
if (bss->anqp == NULL)
continue;
prov_anqp = bss->anqp->hs20_osu_providers_list;
if (prov_anqp == NULL)
continue;
+ ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+ if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &data) == 0 &&
+ (data.key_mgmt & WPA_KEY_MGMT_OSEN)) {
+ osu_ssid2 = bss->ssid;
+ osu_ssid2_len = bss->ssid_len;
+ } else {
+ osu_ssid2 = NULL;
+ osu_ssid2_len = 0;
+ }
wpa_printf(MSG_DEBUG, "HS 2.0: Parsing OSU Providers list from "
MACSTR, MAC2STR(bss->bssid));
wpa_hexdump_buf(MSG_DEBUG, "HS 2.0: OSU Providers list",
@@ -1075,7 +1110,8 @@
if (len > (unsigned int) (end - pos))
break;
hs20_osu_add_prov(wpa_s, bss, osu_ssid,
- osu_ssid_len, pos, len);
+ osu_ssid_len, osu_ssid2,
+ osu_ssid2_len, pos, len);
pos += len;
}
@@ -1084,6 +1120,35 @@
"extra data after OSU Providers",
(int) (end - pos));
}
+
+ prov_anqp = bss->anqp->hs20_osu_providers_nai_list;
+ if (!prov_anqp)
+ continue;
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: Parsing OSU Providers NAI List from "
+ MACSTR, MAC2STR(bss->bssid));
+ wpa_hexdump_buf(MSG_DEBUG, "HS 2.0: OSU Providers NAI List",
+ prov_anqp);
+ pos = wpabuf_head(prov_anqp);
+ end = pos + wpabuf_len(prov_anqp);
+ num_providers = 0;
+ while (end - pos > 0) {
+ len = *pos++;
+ if (end - pos < len) {
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: Not enough room for OSU_NAI");
+ break;
+ }
+ if (num_providers >= wpa_s->osu_prov_count) {
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: Ignore unexpected OSU Provider NAI List entries");
+ break;
+ }
+ os_memcpy(wpa_s->osu_prov[num_providers].osu_nai2,
+ pos, len);
+ pos += len;
+ num_providers++;
+ }
}
wpa_s->fetch_osu_icon_in_progress = 1;
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index 44a828d..ebb3a50 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -303,8 +303,10 @@
wpabuf_put_u8(extra, HS20_STYPE_CONNECTION_CAPABILITY);
if (all)
wpabuf_put_u8(extra, HS20_STYPE_OPERATING_CLASS);
- if (all)
+ if (all) {
wpabuf_put_u8(extra, HS20_STYPE_OSU_PROVIDERS_LIST);
+ wpabuf_put_u8(extra, HS20_STYPE_OSU_PROVIDERS_NAI_LIST);
+ }
gas_anqp_set_element_len(extra, len_pos);
}
#endif /* CONFIG_HS20 */
@@ -2844,6 +2846,31 @@
}
+static void interworking_parse_venue_url(struct wpa_supplicant *wpa_s,
+ const u8 *data, size_t len)
+{
+ const u8 *pos = data, *end = data + len;
+ char url[255];
+
+ while (end - pos >= 2) {
+ u8 slen, num;
+
+ slen = *pos++;
+ if (slen < 1 || slen > end - pos) {
+ wpa_printf(MSG_DEBUG,
+ "ANQP: Truncated Venue URL Duple field");
+ return;
+ }
+
+ num = *pos++;
+ os_memcpy(url, pos, slen - 1);
+ url[slen - 1] = '\0';
+ wpa_msg(wpa_s, MSG_INFO, RX_VENUE_URL "%u %s", num, url);
+ pos += slen - 1;
+ }
+}
+
+
static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, const u8 *sa,
u16 info_id,
@@ -2950,6 +2977,18 @@
}
break;
#endif /* CONFIG_FILS */
+ case ANQP_VENUE_URL:
+ wpa_msg(wpa_s, MSG_INFO, RX_ANQP MACSTR " Venue URL",
+ MAC2STR(sa));
+ anqp_add_extra(wpa_s, anqp, info_id, pos, slen);
+
+ if (!wpa_sm_pmf_enabled(wpa_s->wpa)) {
+ wpa_printf(MSG_DEBUG,
+ "ANQP: Ignore Venue URL since PMF was not enabled");
+ break;
+ }
+ interworking_parse_venue_url(wpa_s, pos, slen);
+ break;
case ANQP_VENDOR_SPECIFIC:
if (slen < 3)
return;
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index 3d3296f..3626ea3 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -4358,22 +4358,18 @@
return -EINVAL;
}
- // store generated MAC address.
- if (wpa_s->conf->p2p_device_persistent_mac_addr)
- os_free(wpa_s->conf->p2p_device_persistent_mac_addr);
- size_t mac_addr_str_len = sizeof("00:00:00:00:00:00");
- wpa_s->conf->p2p_device_persistent_mac_addr =
- os_zalloc(mac_addr_str_len + 1);
- os_snprintf(wpa_s->conf->p2p_device_persistent_mac_addr,
- mac_addr_str_len, MACSTR, MAC2STR(addr));
+ /* Store generated MAC address. */
+ os_memcpy(wpa_s->conf->p2p_device_persistent_mac_addr, addr,
+ ETH_ALEN);
} else {
- // If there are existing saved groups, restore last MAC address.
- // if there is no last used MAC address, the last one is factory MAC.
- if (!wpa_s->conf->p2p_device_persistent_mac_addr)
+ /* If there are existing saved groups, restore last MAC address.
+ * if there is no last used MAC address, the last one is
+ * factory MAC. */
+ if (is_zero_ether_addr(
+ wpa_s->conf->p2p_device_persistent_mac_addr))
return 0;
-
- if (hwaddr_aton(wpa_s->conf->p2p_device_persistent_mac_addr, addr) < 0)
- return -EINVAL;
+ os_memcpy(addr, wpa_s->conf->p2p_device_persistent_mac_addr,
+ ETH_ALEN);
wpa_msg(wpa_s, MSG_DEBUG, "Restore last used MAC address.");
}
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index 56d53fb..ee39e0c 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -677,6 +677,87 @@
}
+static void wpa_add_scan_ssid(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_scan_params *params,
+ size_t max_ssids, const u8 *ssid, size_t ssid_len)
+{
+ unsigned int j;
+
+ for (j = 0; j < params->num_ssids; j++) {
+ if (params->ssids[j].ssid_len == ssid_len &&
+ params->ssids[j].ssid &&
+ os_memcmp(params->ssids[j].ssid, ssid, ssid_len) == 0)
+ return; /* already in the list */
+ }
+
+ if (params->num_ssids + 1 > max_ssids) {
+ wpa_printf(MSG_DEBUG, "Over max scan SSIDs for manual request");
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "Scan SSID (manual request): %s",
+ wpa_ssid_txt(ssid, ssid_len));
+
+ params->ssids[params->num_ssids].ssid = ssid;
+ params->ssids[params->num_ssids].ssid_len = ssid_len;
+ params->num_ssids++;
+}
+
+
+static void wpa_add_owe_scan_ssid(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_scan_params *params,
+ struct wpa_ssid *ssid, size_t max_ssids)
+{
+#ifdef CONFIG_OWE
+ struct wpa_bss *bss;
+
+ if (!(ssid->key_mgmt & WPA_KEY_MGMT_OWE))
+ return;
+
+ wpa_printf(MSG_DEBUG, "OWE: Look for transition mode AP. ssid=%s",
+ wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ const u8 *owe, *pos, *end;
+ const u8 *owe_ssid;
+ size_t owe_ssid_len;
+
+ if (bss->ssid_len != ssid->ssid_len ||
+ os_memcmp(bss->ssid, ssid->ssid, ssid->ssid_len) != 0)
+ continue;
+
+ owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE);
+ if (!owe || owe[1] < 4)
+ continue;
+
+ pos = owe + 6;
+ end = owe + 2 + owe[1];
+
+ /* Must include BSSID and ssid_len */
+ if (end - pos < ETH_ALEN + 1)
+ return;
+
+ /* Skip BSSID */
+ pos += ETH_ALEN;
+ owe_ssid_len = *pos++;
+ owe_ssid = pos;
+
+ if ((size_t) (end - pos) < owe_ssid_len ||
+ owe_ssid_len > SSID_MAX_LEN)
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "OWE: scan_ssids: transition mode OWE ssid=%s",
+ wpa_ssid_txt(owe_ssid, owe_ssid_len));
+
+ wpa_add_scan_ssid(wpa_s, params, max_ssids,
+ owe_ssid, owe_ssid_len);
+ return;
+ }
+#endif /* CONFIG_OWE */
+}
+
+
static void wpa_set_scan_ssids(struct wpa_supplicant *wpa_s,
struct wpa_driver_scan_params *params,
size_t max_ssids)
@@ -691,33 +772,17 @@
max_ssids = max_ssids > 1 ? max_ssids - 1 : max_ssids;
for (i = 0; i < wpa_s->scan_id_count; i++) {
- unsigned int j;
-
ssid = wpa_config_get_network(wpa_s->conf, wpa_s->scan_id[i]);
- if (!ssid || !ssid->scan_ssid)
+ if (!ssid)
continue;
-
- for (j = 0; j < params->num_ssids; j++) {
- if (params->ssids[j].ssid_len == ssid->ssid_len &&
- params->ssids[j].ssid &&
- os_memcmp(params->ssids[j].ssid, ssid->ssid,
- ssid->ssid_len) == 0)
- break;
- }
- if (j < params->num_ssids)
- continue; /* already in the list */
-
- if (params->num_ssids + 1 > max_ssids) {
- wpa_printf(MSG_DEBUG,
- "Over max scan SSIDs for manual request");
- break;
- }
-
- wpa_printf(MSG_DEBUG, "Scan SSID (manual request): %s",
- wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
- params->ssids[params->num_ssids].ssid = ssid->ssid;
- params->ssids[params->num_ssids].ssid_len = ssid->ssid_len;
- params->num_ssids++;
+ if (ssid->scan_ssid)
+ wpa_add_scan_ssid(wpa_s, params, max_ssids,
+ ssid->ssid, ssid->ssid_len);
+ /*
+ * Also add the SSID of the OWE BSS, to allow discovery of
+ * transition mode APs more quickly.
+ */
+ wpa_add_owe_scan_ssid(wpa_s, params, ssid, max_ssids);
}
wpa_s->scan_id_count = 0;
@@ -984,6 +1049,17 @@
if (params.num_ssids + 1 >= max_ssids)
break;
}
+
+ if (!wpas_network_disabled(wpa_s, ssid)) {
+ /*
+ * Also add the SSID of the OWE BSS, to allow
+ * discovery of transition mode APs more
+ * quickly.
+ */
+ wpa_add_owe_scan_ssid(wpa_s, ¶ms, ssid,
+ max_ssids);
+ }
+
ssid = ssid->next;
if (ssid == start)
break;
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index 708a347..d23dc06 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -240,6 +240,8 @@
u8 ext_capab[18];
int ext_capab_len;
int skip_auth;
+ u8 *wpa_ie;
+ size_t wpa_ie_len;
#ifdef CONFIG_MBO
const u8 *mbo_ie;
#endif /* CONFIG_MBO */
@@ -391,6 +393,28 @@
wpa_s->sme.assoc_req_ie_len = 0;
}
+ /* In case the WPA vendor IE is used, it should be placed after all the
+ * non-vendor IEs, as the lower layer expects the IEs to be ordered as
+ * defined in the standard. Store the WPA IE so it can later be
+ * inserted at the correct location.
+ */
+ wpa_ie = NULL;
+ wpa_ie_len = 0;
+ if (wpa_s->wpa_proto == WPA_PROTO_WPA) {
+ wpa_ie = os_memdup(wpa_s->sme.assoc_req_ie,
+ wpa_s->sme.assoc_req_ie_len);
+ if (wpa_ie) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Storing WPA IE");
+
+ wpa_ie_len = wpa_s->sme.assoc_req_ie_len;
+ wpa_s->sme.assoc_req_ie_len = 0;
+ } else {
+ wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed copy WPA IE");
+ wpas_connect_work_done(wpa_s);
+ return;
+ }
+ }
+
#ifdef CONFIG_IEEE80211R
ie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN);
if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN)
@@ -527,6 +551,26 @@
}
#endif /* CONFIG_HS20 */
+ if (wpa_ie) {
+ size_t len;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Reinsert WPA IE");
+
+ len = sizeof(wpa_s->sme.assoc_req_ie) -
+ wpa_s->sme.assoc_req_ie_len;
+
+ if (len > wpa_ie_len) {
+ os_memcpy(wpa_s->sme.assoc_req_ie +
+ wpa_s->sme.assoc_req_ie_len,
+ wpa_ie, wpa_ie_len);
+ wpa_s->sme.assoc_req_ie_len += wpa_ie_len;
+ } else {
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Failed to add WPA IE");
+ }
+
+ os_free(wpa_ie);
+ }
+
if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) {
struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ];
size_t len;
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index 7c410e7..6b68fc9 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -338,6 +338,9 @@
wpa_s->wnm_num_neighbor_report = 0;
os_free(wpa_s->wnm_neighbor_report_elements);
wpa_s->wnm_neighbor_report_elements = NULL;
+
+ wpabuf_free(wpa_s->coloc_intf_elems);
+ wpa_s->coloc_intf_elems = NULL;
}
@@ -1717,6 +1720,46 @@
}
+static void ieee802_11_rx_wnm_coloc_intf_req(struct wpa_supplicant *wpa_s,
+ const u8 *sa, const u8 *frm,
+ int len)
+{
+ u8 dialog_token, req_info, auto_report, timeout;
+
+ if (!wpa_s->conf->coloc_intf_reporting)
+ return;
+
+ /* Dialog Token [1] | Request Info [1] */
+
+ if (len < 2)
+ return;
+ dialog_token = frm[0];
+ req_info = frm[1];
+ auto_report = req_info & 0x03;
+ timeout = req_info >> 2;
+
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "WNM: Received Collocated Interference Request (dialog_token %u auto_report %u timeout %u sa " MACSTR ")",
+ dialog_token, auto_report, timeout, MAC2STR(sa));
+
+ if (dialog_token == 0)
+ return; /* only nonzero values are used for request */
+
+ if (wpa_s->wpa_state != WPA_COMPLETED ||
+ os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "WNM: Collocated Interference Request frame not from current AP - ignore it");
+ return;
+ }
+
+ wpa_msg(wpa_s, MSG_INFO, COLOC_INTF_REQ "%u %u %u",
+ dialog_token, auto_report, timeout);
+ wpa_s->coloc_intf_dialog_token = dialog_token;
+ wpa_s->coloc_intf_auto_report = auto_report;
+ wpa_s->coloc_intf_timeout = timeout;
+}
+
+
void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
const struct ieee80211_mgmt *mgmt, size_t len)
{
@@ -1750,8 +1793,75 @@
case WNM_NOTIFICATION_REQ:
ieee802_11_rx_wnm_notif_req(wpa_s, mgmt->sa, pos, end - pos);
break;
+ case WNM_COLLOCATED_INTERFERENCE_REQ:
+ ieee802_11_rx_wnm_coloc_intf_req(wpa_s, mgmt->sa, pos,
+ end - pos);
+ break;
default:
wpa_printf(MSG_ERROR, "WNM: Unknown request");
break;
}
}
+
+
+int wnm_send_coloc_intf_report(struct wpa_supplicant *wpa_s, u8 dialog_token,
+ const struct wpabuf *elems)
+{
+ struct wpabuf *buf;
+ int ret;
+
+ if (wpa_s->wpa_state < WPA_ASSOCIATED || !elems)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "WNM: Send Collocated Interference Report to "
+ MACSTR " (dialog token %u)",
+ MAC2STR(wpa_s->bssid), dialog_token);
+
+ buf = wpabuf_alloc(3 + wpabuf_len(elems));
+ if (!buf)
+ return -1;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+ wpabuf_put_u8(buf, WNM_COLLOCATED_INTERFERENCE_REPORT);
+ wpabuf_put_u8(buf, dialog_token);
+ wpabuf_put_buf(buf, elems);
+
+ ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+ wpa_s->own_addr, wpa_s->bssid,
+ wpabuf_head_u8(buf), wpabuf_len(buf), 0);
+ wpabuf_free(buf);
+ return ret;
+}
+
+
+void wnm_set_coloc_intf_elems(struct wpa_supplicant *wpa_s,
+ struct wpabuf *elems)
+{
+ wpabuf_free(wpa_s->coloc_intf_elems);
+ if (elems && wpabuf_len(elems) == 0) {
+ wpabuf_free(elems);
+ elems = NULL;
+ }
+ wpa_s->coloc_intf_elems = elems;
+
+ if (wpa_s->conf->coloc_intf_reporting && wpa_s->coloc_intf_elems &&
+ wpa_s->coloc_intf_dialog_token &&
+ (wpa_s->coloc_intf_auto_report == 1 ||
+ wpa_s->coloc_intf_auto_report == 3)) {
+ /* TODO: Check that there has not been less than
+ * wpa_s->coloc_intf_timeout * 200 TU from the last report.
+ */
+ wnm_send_coloc_intf_report(wpa_s,
+ wpa_s->coloc_intf_dialog_token,
+ wpa_s->coloc_intf_elems);
+ }
+}
+
+
+void wnm_clear_coloc_intf_reporting(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_WNM
+ wpa_s->coloc_intf_dialog_token = 0;
+ wpa_s->coloc_intf_auto_report = 0;
+#endif /* CONFIG_WNM */
+}
diff --git a/wpa_supplicant/wnm_sta.h b/wpa_supplicant/wnm_sta.h
index 02cd1cd..29625f8 100644
--- a/wpa_supplicant/wnm_sta.h
+++ b/wpa_supplicant/wnm_sta.h
@@ -65,11 +65,16 @@
int cand_list);
void wnm_deallocate_memory(struct wpa_supplicant *wpa_s);
+int wnm_send_coloc_intf_report(struct wpa_supplicant *wpa_s, u8 dialog_token,
+ const struct wpabuf *elems);
+void wnm_set_coloc_intf_elems(struct wpa_supplicant *wpa_s,
+ struct wpabuf *elems);
#ifdef CONFIG_WNM
int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail);
+void wnm_clear_coloc_intf_reporting(struct wpa_supplicant *wpa_s);
#else /* CONFIG_WNM */
@@ -79,6 +84,10 @@
return 0;
}
+static inline void wnm_clear_coloc_intf_reporting(struct wpa_supplicant *wpa_s)
+{
+}
+
#endif /* CONFIG_WNM */
#endif /* WNM_STA_H */
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index fc13a57..4432ec3 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -1646,6 +1646,10 @@
case 0: /* Bits 0-7 */
break;
case 1: /* Bits 8-15 */
+ if (wpa_s->conf->coloc_intf_reporting) {
+ /* Bit 13 - Collocated Interference Reporting */
+ *pos |= 0x20;
+ }
break;
case 2: /* Bits 16-23 */
#ifdef CONFIG_WNM
@@ -3076,6 +3080,11 @@
"MFP: require MFP");
params.mgmt_frame_protection =
MGMT_FRAME_PROTECTION_REQUIRED;
+#ifdef CONFIG_OWE
+ } else if (!rsn && (ssid->key_mgmt & WPA_KEY_MGMT_OWE) &&
+ !ssid->owe_only) {
+ params.mgmt_frame_protection = NO_MGMT_FRAME_PROTECTION;
+#endif /* CONFIG_OWE */
}
}
#endif /* CONFIG_IEEE80211W */
@@ -3303,6 +3312,7 @@
return;
ssid->disabled = 0;
+ ssid->owe_transition_bss_select_count = 0;
wpas_clear_temp_disabled(wpa_s, ssid, 1);
wpas_notify_network_enabled_changed(wpa_s, ssid);
@@ -3567,6 +3577,8 @@
wpa_s->disconnected = 0;
wpa_s->reassociate = 1;
wpa_s->last_owe_group = 0;
+ if (ssid)
+ ssid->owe_transition_bss_select_count = 0;
if (wpa_s->connect_without_scan ||
wpa_supplicant_fast_associate(wpa_s) != 1) {
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index d367ba0..abacaa9 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -743,6 +743,8 @@
unsigned int mac_addr_changed:1;
unsigned int added_vif:1;
unsigned int wnmsleep_used:1;
+ unsigned int owe_transition_select:1;
+ unsigned int owe_transition_search:1;
struct os_reltime last_mac_addr_change;
int last_mac_addr_style;
@@ -1059,6 +1061,10 @@
struct neighbor_report *wnm_neighbor_report_elements;
struct os_reltime wnm_cand_valid_until;
u8 wnm_cand_from_bss[ETH_ALEN];
+ struct wpabuf *coloc_intf_elems;
+ u8 coloc_intf_dialog_token;
+ u8 coloc_intf_auto_report;
+ u8 coloc_intf_timeout;
#ifdef CONFIG_MBO
unsigned int wnm_mbo_trans_reason_present:1;
u8 wnm_mbo_transition_reason;