User override of server certificate
Store server certificate hash and TOD policy from WPA3-Enterprise
connection attempts with sta_associate. Add support for dev_exec_action
ServerCertTrust,Accept argument. If TOD policy is in place for the
current network profile, reject that command; if TOD policy is not in
place, update network profile to use the last received server
certificate (from the last failed connection attempt) and try to
reconnect.
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
diff --git a/dev.c b/dev.c
index ada3f90..e70b7fa 100644
--- a/dev.c
+++ b/dev.c
@@ -67,11 +67,129 @@
}
+static enum sigma_cmd_result sta_server_cert_trust(struct sigma_dut *dut,
+ struct sigma_conn *conn,
+ const char *val)
+{
+ char buf[100];
+ struct wpa_ctrl *ctrl = NULL;
+ int e;
+ char resp[200];
+ int num_disconnected = 0;
+
+ strlcpy(resp, "ServerCertTrustResult,Accepted", sizeof(resp));
+
+ if (strcasecmp(val, "Accept") != 0) {
+ strlcpy(resp,
+ "ServerCertTrustResult,NotAccepted,Reason,Unsupported ServerCertTrust value",
+ sizeof(resp));
+ goto done;
+ }
+
+ if (!dut->server_cert_hash[0]) {
+ strlcpy(resp,
+ "ServerCertTrustResult,NotAccepted,Reason,No server certificate stored",
+ sizeof(resp));
+ goto done;
+ }
+
+ if (dut->sta_tod_policy) {
+ strlcpy(resp,
+ "ServerCertTrustResult,NotAccepted,Reason,TOD policy",
+ sizeof(resp));
+ goto done;
+ }
+
+ snprintf(buf, sizeof(buf), "hash://server/sha256/%s",
+ dut->server_cert_hash);
+ if (set_network_quoted(get_station_ifname(), dut->infra_network_id,
+ "ca_cert", buf) < 0) {
+ strlcpy(resp,
+ "ServerCertTrustResult,NotAccepted,Reason,Could not configure server certificate hash for the network profile",
+ sizeof(resp));
+ goto done;
+ }
+
+ wpa_command(get_station_ifname(), "DISCONNECT");
+ snprintf(buf, sizeof(buf), "SELECT_NETWORK %d", dut->infra_network_id);
+ if (wpa_command(get_station_ifname(), buf) < 0) {
+ sigma_dut_print(dut, DUT_MSG_INFO, "Failed to select "
+ "network id %d on %s",
+ dut->infra_network_id,
+ get_station_ifname());
+ strlcpy(resp,
+ "ServerCertTrustResult,Accepted,Result,Could not request reconnection",
+ sizeof(resp));
+ goto done;
+ }
+
+ ctrl = open_wpa_mon(get_station_ifname());
+ if (!ctrl)
+ goto done;
+
+ for (e = 0; e < 20; e++) {
+ const char *events[] = {
+ "CTRL-EVENT-EAP-TLS-CERT-ERROR",
+ "CTRL-EVENT-DISCONNECTED",
+ "CTRL-EVENT-CONNECTED",
+ NULL
+ };
+ char buf[1024];
+ int res;
+
+ res = get_wpa_cli_events(dut, ctrl, events, buf, sizeof(buf));
+ if (res < 0) {
+ strlcpy(resp,
+ "ServerCertTrustResult,Accepted,Result,Association did not complete",
+ sizeof(resp));
+ goto done;
+ }
+ sigma_dut_print(dut, DUT_MSG_DEBUG, "Connection event: %s",
+ buf);
+
+ if (strstr(buf, "CTRL-EVENT-EAP-TLS-CERT-ERROR")) {
+ strlcpy(resp,
+ "ServerCertTrustResult,Accepted,Result,TLS server certitficate validation failed with updated profile",
+ sizeof(resp));
+ goto done;
+ }
+
+ if (strstr(buf, "CTRL-EVENT-DISCONNECTED")) {
+ num_disconnected++;
+
+ if (num_disconnected > 2) {
+ strlcpy(resp,
+ "ServerCertTrustResult,Accepted,Result,Connection failed",
+ sizeof(resp));
+ goto done;
+ }
+ }
+
+ if (strstr(buf, "CTRL-EVENT-CONNECTED")) {
+ strlcpy(resp,
+ "ServerCertTrustResult,Accepted,Result,Connected",
+ sizeof(resp));
+ break;
+ }
+ }
+
+done:
+ if (ctrl) {
+ wpa_ctrl_detach(ctrl);
+ wpa_ctrl_close(ctrl);
+ }
+
+ send_resp(dut, conn, SIGMA_COMPLETE, resp);
+ return STATUS_SENT;
+}
+
+
static enum sigma_cmd_result cmd_dev_exec_action(struct sigma_dut *dut,
struct sigma_conn *conn,
struct sigma_cmd *cmd)
{
const char *program = get_param(cmd, "Program");
+ const char *val;
#ifdef MIRACAST
if (program && (strcasecmp(program, "WFD") == 0 ||
@@ -85,6 +203,10 @@
if (program && strcasecmp(program, "DPP") == 0)
return dpp_dev_exec_action(dut, conn, cmd);
+ val = get_param(cmd, "ServerCertTrust");
+ if (val)
+ return sta_server_cert_trust(dut, conn, val);
+
return ERROR_SEND_STATUS;
}
diff --git a/sigma_dut.h b/sigma_dut.h
index 0a172cb..7ef07b9 100644
--- a/sigma_dut.h
+++ b/sigma_dut.h
@@ -834,6 +834,9 @@
char *sae_commit_override;
char *rsne_override;
+ int sta_associate_wait_connect;
+ char server_cert_hash[65];
+ int sta_tod_policy;
const char *hostapd_bin;
int use_hostapd_pid_file;
const char *hostapd_ifname;
diff --git a/sta.c b/sta.c
index 39371db..04dc9cb 100644
--- a/sta.c
+++ b/sta.c
@@ -2334,6 +2334,8 @@
if (erp && set_network(ifname, id, "erp", "1") < 0)
return ERROR_SEND_STATUS;
+ dut->sta_associate_wait_connect = 1;
+
return id;
}
@@ -3221,6 +3223,12 @@
const char *network_mode = get_param(cmd, "network_mode");
int wps = 0;
char buf[1000], extra[50];
+ int e;
+ enum sigma_cmd_result ret = SUCCESS_SEND_STATUS;
+ struct wpa_ctrl *ctrl = NULL;
+ int num_network_not_found = 0;
+ int num_disconnected = 0;
+ int tod = -1;
if (ssid == NULL)
return -1;
@@ -3300,6 +3308,13 @@
return 0;
}
+ if (dut->program == PROGRAM_WPA3 &&
+ dut->sta_associate_wait_connect) {
+ ctrl = open_wpa_mon(get_station_ifname());
+ if (!ctrl)
+ return ERROR_SEND_STATUS;
+ }
+
extra[0] = '\0';
if (chan)
snprintf(extra, sizeof(extra), " freq=%u",
@@ -3311,11 +3326,109 @@
"network id %d on %s",
dut->infra_network_id,
get_station_ifname());
- return -2;
+ ret = ERROR_SEND_STATUS;
+ goto done;
}
}
- return 1;
+ if (!ctrl)
+ return SUCCESS_SEND_STATUS;
+
+ /* Wait for connection result to be able to store server certificate
+ * hash for trust root override testing
+ * (dev_exec_action,ServerCertTrust). */
+
+ for (e = 0; e < 20; e++) {
+ const char *events[] = {
+ "CTRL-EVENT-EAP-PEER-CERT",
+ "CTRL-EVENT-EAP-TLS-CERT-ERROR",
+ "CTRL-EVENT-DISCONNECTED",
+ "CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-NETWORK-NOT-FOUND",
+ NULL
+ };
+ char buf[1024];
+ int res;
+
+ res = get_wpa_cli_events(dut, ctrl, events, buf, sizeof(buf));
+ if (res < 0) {
+ send_resp(dut, conn, SIGMA_ERROR,
+ "ErrorCode,Association did not complete");
+ ret = STATUS_SENT_ERROR;
+ break;
+ }
+ sigma_dut_print(dut, DUT_MSG_DEBUG, "Connection event: %s",
+ buf);
+
+ if (strstr(buf, "CTRL-EVENT-EAP-PEER-CERT") &&
+ strstr(buf, " depth=0")) {
+ char *pos = strstr(buf, " hash=");
+
+ if (pos) {
+ char *end;
+
+ tod = strstr(buf, " tod=1") != NULL;
+ sigma_dut_print(dut, DUT_MSG_DEBUG,
+ "Server certificate TOD policy: %d",
+ tod);
+
+ pos += 6;
+ end = strchr(pos, ' ');
+ if (end)
+ *end = '\0';
+ strlcpy(dut->server_cert_hash, pos,
+ sizeof(dut->server_cert_hash));
+ sigma_dut_print(dut, DUT_MSG_DEBUG,
+ "Server certificate hash: %s",
+ dut->server_cert_hash);
+ }
+ }
+
+ if (strstr(buf, "CTRL-EVENT-EAP-TLS-CERT-ERROR")) {
+ send_resp(dut, conn, SIGMA_COMPLETE,
+ "Result,TLS server certificate validation failed");
+ ret = STATUS_SENT_ERROR;
+ break;
+ }
+
+ if (strstr(buf, "CTRL-EVENT-NETWORK-NOT-FOUND")) {
+ num_network_not_found++;
+
+ if (num_network_not_found > 2) {
+ send_resp(dut, conn, SIGMA_COMPLETE,
+ "Result,Network not found");
+ ret = STATUS_SENT_ERROR;
+ break;
+ }
+ }
+
+ if (strstr(buf, "CTRL-EVENT-DISCONNECTED")) {
+ num_disconnected++;
+
+ if (num_disconnected > 2) {
+ send_resp(dut, conn, SIGMA_COMPLETE,
+ "Result,Connection failed");
+ ret = STATUS_SENT_ERROR;
+ break;
+ }
+ }
+
+ if (strstr(buf, "CTRL-EVENT-CONNECTED")) {
+ if (tod >= 0) {
+ sigma_dut_print(dut, DUT_MSG_DEBUG,
+ "Network profile TOD policy update: %d -> %d",
+ dut->sta_tod_policy, tod);
+ dut->sta_tod_policy = tod;
+ }
+ break;
+ }
+ }
+done:
+ if (ctrl) {
+ wpa_ctrl_detach(ctrl);
+ wpa_ctrl_close(ctrl);
+ }
+ return ret;
}
@@ -7516,6 +7629,10 @@
free(dut->sae_commit_override);
dut->sae_commit_override = NULL;
+ dut->sta_associate_wait_connect = 0;
+ dut->server_cert_hash[0] = '\0';
+ dut->sta_tod_policy = 0;
+
dut->dpp_conf_id = -1;
free(dut->dpp_peer_uri);
dut->dpp_peer_uri = NULL;