DPP: Add dev_exec_action support for Program,DPP

This adds initial, but not yet fully complete, support for the new DPP
CAPI operations using the dev_exec_action command.

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
diff --git a/dpp.c b/dpp.c
new file mode 100644
index 0000000..eafacd5
--- /dev/null
+++ b/dpp.c
@@ -0,0 +1,706 @@
+/*
+ * Sigma Control API DUT (station/AP/sniffer)
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * All Rights Reserved.
+ * Licensed under the Clear BSD license. See README for more details.
+ */
+
+#include "sigma_dut.h"
+#include "wpa_ctrl.h"
+#include "wpa_helpers.h"
+
+
+static int sigma_dut_is_ap(struct sigma_dut *dut)
+{
+	return dut->device_type == AP_unknown ||
+		dut->device_type == AP_testbed ||
+		dut->device_type == AP_dut;
+}
+
+
+static int dpp_hostapd_run(struct sigma_dut *dut)
+{
+	if (dut->hostapd_running)
+		return 0;
+
+	sigma_dut_print(dut, DUT_MSG_INFO,
+			"Starting hostapd in unconfigured state for DPP");
+	snprintf(dut->ap_ssid, sizeof(dut->ap_ssid), "unconfigured");
+	dut->ap_channel = 11;
+	dut->ap_is_dual = 0;
+	dut->ap_mode = AP_11ng;
+	dut->ap_key_mgmt = AP_OPEN;
+	dut->ap_cipher = AP_PLAIN;
+	return cmd_ap_config_commit(dut, NULL, NULL) == 1 ? 0 : -1;
+}
+
+
+static const char * dpp_get_curve(struct sigma_cmd *cmd, const char *arg)
+{
+	const char *val = get_param(cmd, arg);
+
+	if (!val)
+		val = "P-256";
+	else if (strcasecmp(val, "BP-256R1") == 0)
+		val = "BP-256";
+	else if (strcasecmp(val, "BP-384R1") == 0)
+		val = "BP-384";
+	else if (strcasecmp(val, "BP-512R1") == 0)
+		val = "BP-512";
+
+	return val;
+}
+
+
+static int dpp_get_local_bootstrap(struct sigma_dut *dut,
+				   struct sigma_conn *conn,
+				   struct sigma_cmd *cmd)
+{
+	const char *curve = dpp_get_curve(cmd, "DPPCryptoIdentifier");
+	const char *bs = get_param(cmd, "DPPBS");
+	char *pos, mac[50], buf[100], resp[1000], hex[2000];
+	const char *ifname = get_station_ifname();
+
+	if (strcasecmp(bs, "QR") != 0) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Unsupported DPPBS");
+		return 0;
+	}
+
+	if (sigma_dut_is_ap(dut)) {
+		u8 bssid[ETH_ALEN];
+
+		if (!dut->hostapd_ifname) {
+			sigma_dut_print(dut, DUT_MSG_ERROR,
+					"hostapd ifname not specified (-j)");
+			return -2;
+		}
+		ifname = dut->hostapd_ifname;
+		if (get_hwaddr(dut->hostapd_ifname, bssid) < 0) {
+			sigma_dut_print(dut, DUT_MSG_ERROR,
+					"Could not get MAC address for %s",
+					dut->hostapd_ifname);
+			return -2;
+		}
+		snprintf(mac, sizeof(mac), "%02x%02x%02x%02x%02x%02x",
+			 bssid[0], bssid[1], bssid[2],
+			 bssid[3], bssid[4], bssid[5]);
+	} else {
+		if (get_wpa_status(ifname, "address", mac, sizeof(mac)) < 0)
+			return -2;
+	}
+
+	pos = mac;
+	while (*pos) {
+		if (*pos == ':')
+			memmove(pos, pos + 1, strlen(pos));
+		else
+			pos++;
+	}
+
+	snprintf(buf, sizeof(buf),
+		 "DPP_BOOTSTRAP_GEN type=qrcode curve=%s chan=81/11 mac=%s",
+		 curve, mac);
+
+	if (sigma_dut_is_ap(dut) && dpp_hostapd_run(dut) < 0) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Failed to start hostapd");
+		return 0;
+	}
+
+	if (wpa_command_resp(ifname, buf, resp, sizeof(resp)) < 0)
+		return -2;
+	if (strncmp(resp, "FAIL", 4) == 0)
+		return -2;
+	dut->dpp_local_bootstrap = atoi(resp);
+	snprintf(buf, sizeof(buf), "DPP_BOOTSTRAP_GET_URI %d",
+		 atoi(resp));
+	if (wpa_command_resp(ifname, buf, resp, sizeof(resp)) < 0)
+		return -2;
+	if (strncmp(resp, "FAIL", 4) == 0)
+		return -2;
+
+	sigma_dut_print(dut, DUT_MSG_DEBUG, "URI: %s", resp);
+	ascii2hexstr(resp, hex);
+	snprintf(resp, sizeof(resp), "BootstrappingData,%s", hex);
+	send_resp(dut, conn, SIGMA_COMPLETE, resp);
+	return 0;
+}
+
+
+static int dpp_set_peer_bootstrap(struct sigma_dut *dut,
+				  struct sigma_conn *conn,
+				  struct sigma_cmd *cmd)
+{
+	const char *val = get_param(cmd, "DPPBootstrappingdata");
+	char uri[1000], buf[1200];
+	int res;
+	const char *ifname = get_station_ifname();
+
+	if (!val) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Missing DPPBootstrappingdata");
+		return 0;
+	}
+
+	res = parse_hexstr(val, (unsigned char *) uri, sizeof(uri));
+	if (res < 0 || (size_t) res >= sizeof(uri))
+		return -2;
+	uri[res] = '\0';
+	sigma_dut_print(dut, DUT_MSG_DEBUG, "URI: %s", uri);
+
+	snprintf(buf, sizeof(buf), "DPP_QR_CODE %s", uri);
+
+	if (sigma_dut_is_ap(dut)) {
+		if (!dut->hostapd_ifname) {
+			sigma_dut_print(dut, DUT_MSG_ERROR,
+					"hostapd ifname not specified (-j)");
+			return -2;
+		}
+		ifname = dut->hostapd_ifname;
+
+		if (dpp_hostapd_run(dut) < 0) {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "errorCode,Failed to start hostapd");
+			return 0;
+		}
+	}
+
+	if (wpa_command_resp(ifname, buf, buf, sizeof(buf)) < 0) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Failed to parse URI");
+		return 0;
+	}
+	dut->dpp_peer_bootstrap = atoi(buf);
+
+	return 1;
+}
+
+
+static int dpp_hostapd_conf_update(struct sigma_dut *dut,
+				   struct sigma_conn *conn, const char *ifname,
+				   struct wpa_ctrl *ctrl)
+{
+	int res;
+	char buf[2000], buf2[2500], *pos, *pos2;
+
+	sigma_dut_print(dut, DUT_MSG_INFO,
+			"Update hostapd configuration based on DPP Config Object");
+
+	if (wpa_command(ifname, "SET wpa 2") < 0 ||
+	    wpa_command(ifname, "SET wpa_key_mgmt DPP") < 0 ||
+	    wpa_command(ifname, "SET rsn_pairwise CCMP") < 0) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Failed to update AP security parameters");
+		goto out;
+	}
+
+	res = get_wpa_cli_event(dut, ctrl, "DPP-CONFOBJ-SSID",
+				buf, sizeof(buf));
+	if (res < 0) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,No DPP-CONFOBJ-SSID");
+		goto out;
+	}
+	pos = strchr(buf, ' ');
+	if (!pos)
+		return -2;
+	pos++;
+	sigma_dut_print(dut, DUT_MSG_INFO,
+			"DPP: Config Object SSID: %s", pos);
+	snprintf(buf2, sizeof(buf2), "SET ssid %s", pos);
+	if (wpa_command(ifname, buf2) < 0) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Failed to update AP SSID");
+		goto out;
+	}
+
+	res = get_wpa_cli_event(dut, ctrl, "DPP-CONNECTOR",
+				buf, sizeof(buf));
+	if (res < 0) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,No DPP-CONNECTOR");
+		goto out;
+	}
+	pos = strchr(buf, ' ');
+	if (!pos)
+		return -2;
+	pos++;
+	sigma_dut_print(dut, DUT_MSG_INFO, "DPP: Connector: %s", pos);
+	snprintf(buf2, sizeof(buf2), "SET dpp_connector %s", pos);
+	if (wpa_command(ifname, buf2) < 0) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Failed to update AP Connector");
+		goto out;
+	}
+
+	res = get_wpa_cli_event(dut, ctrl, "DPP-C-SIGN-KEY",
+				buf, sizeof(buf));
+	if (res < 0) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,No DPP-C-SIGN-KEY");
+		goto out;
+	}
+	pos = strchr(buf, ' ');
+	if (!pos)
+		return -2;
+	pos++;
+	pos2 = strchr(pos, ' ');
+	if (pos2)
+		*pos2++ = '\0';
+	sigma_dut_print(dut, DUT_MSG_INFO, "DPP: C-sign-key: %s", pos);
+	snprintf(buf2, sizeof(buf2), "SET dpp_csign %s", pos);
+	if (wpa_command(ifname, buf2) < 0) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Failed to update AP C-sign-key");
+		goto out;
+	}
+	if (pos2) {
+		sigma_dut_print(dut, DUT_MSG_INFO, "DPP: C-sign-key expiry: %s",
+				pos2);
+		snprintf(buf2, sizeof(buf2), "SET dpp_csign_expiry %s", pos2);
+		if (wpa_command(ifname, buf2) < 0) {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "errorCode,Failed to update AP C-sign-key expiry");
+			goto out;
+		}
+	}
+
+	res = get_wpa_cli_event(dut, ctrl, "DPP-NET-ACCESS-KEY",
+				buf, sizeof(buf));
+	if (res < 0) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,No DPP-NET-ACCESS-KEY");
+		goto out;
+	}
+	pos = strchr(buf, ' ');
+	if (!pos)
+		return -2;
+	pos++;
+	pos2 = strchr(pos, ' ');
+	if (pos2)
+		*pos2++ = '\0';
+	sigma_dut_print(dut, DUT_MSG_INFO, "DPP: netAccessKey: %s", pos);
+	snprintf(buf2, sizeof(buf2), "SET dpp_netaccesskey %s", pos);
+	if (wpa_command(ifname, buf2) < 0) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Failed to update AP netAccessKey");
+		goto out;
+	}
+	if (pos2) {
+		sigma_dut_print(dut, DUT_MSG_INFO,
+				"DPP: netAccessKey expiry: %s", pos2);
+		snprintf(buf2, sizeof(buf2), "SET dpp_netaccesskey_expiry %s",
+			 pos2);
+		if (wpa_command(ifname, buf2) < 0) {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "errorCode,Failed to update AP netAccessKey expiry");
+			goto out;
+		}
+	}
+
+	if (wpa_command(ifname, "DISABLE") < 0 ||
+	    wpa_command(ifname, "ENABLE") < 0) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Failed to update AP configuration");
+		goto out;
+	}
+
+	res = get_wpa_cli_event(dut, ctrl, "AP-ENABLED", buf, sizeof(buf));
+	if (res < 0) {
+		send_resp(dut, conn, SIGMA_ERROR, "errorCode,No AP-ENABLED");
+		goto out;
+	}
+
+	return 1;
+out:
+	return 0;
+}
+
+
+static int dpp_manual_dpp(struct sigma_dut *dut,
+			  struct sigma_conn *conn,
+			  struct sigma_cmd *cmd)
+{
+	/* TODO */
+	return -1;
+}
+
+
+static int dpp_automatic_dpp(struct sigma_dut *dut,
+			     struct sigma_conn *conn,
+			     struct sigma_cmd *cmd)
+{
+	const char *bs = get_param(cmd, "DPPBS");
+	const char *auth_role = get_param(cmd, "DPPAuthRole");
+	const char *prov_role = get_param(cmd, "DPPProvisioningRole");
+	const char *pkex_code = get_param(cmd, "DPPPKEXCode");
+	const char *pkex_code_id = get_param(cmd, "DPPPKEXCodeIdentifier");
+	const char *wait_conn = get_param(cmd, "DPPWaitForConnect");
+	const char *self_conf = get_param(cmd, "DPPSelfConfigure");
+	const char *role;
+	const char *val;
+	const char *conf_role;
+	int mutual;
+	int conf_index = -1;
+	char buf[2000];
+	char conf_ssid[100];
+	char conf_pass[100];
+	char pkex_identifier[200];
+	struct wpa_ctrl *ctrl;
+	int res;
+	unsigned int old_timeout;
+	int own_pkex_id = -1;
+	const char *ifname = get_station_ifname();
+	const char *auth_events[] = {
+		"DPP-AUTH-SUCCESS",
+		"DPP-NOT-COMPATIBLE",
+		"DPP-RESPONSE-PENDING",
+		"DPP-SCAN-PEER-QR-CODE",
+		NULL
+	};
+	const char *conf_events[] = {
+		"DPP-CONF-RECEIVED",
+		"DPP-CONF-SENT",
+		"DPP-CONF-FAILED",
+		NULL
+	};
+	const char *conn_events[] = {
+		"PMKSA-CACHE-ADDED",
+		"CTRL-EVENT-CONNECTED",
+		NULL
+	};
+
+	if (!wait_conn)
+		wait_conn = "no";
+	if (!self_conf)
+		self_conf = "no";
+
+	if (!auth_role) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Missing DPPAuthRole");
+		return 0;
+	}
+
+	if (!prov_role) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Missing DPPProvisioningRole");
+		return 0;
+	}
+
+	val = get_param(cmd, "DPPAuthDirection");
+	mutual = val && strcasecmp(val, "Mutual") == 0;
+
+	if (sigma_dut_is_ap(dut)) {
+		if (!dut->hostapd_ifname) {
+			sigma_dut_print(dut, DUT_MSG_ERROR,
+					"hostapd ifname not specified (-j)");
+			return -2;
+		}
+		ifname = dut->hostapd_ifname;
+
+		if (dpp_hostapd_run(dut) < 0) {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "errorCode,Failed to start hostapd");
+			return 0;
+		}
+	}
+
+	if (strcasecmp(prov_role, "Configurator") == 0) {
+		if (dut->dpp_conf_id < 0) {
+			snprintf(buf, sizeof(buf),
+				 "DPP_CONFIGURATOR_ADD curve=%s",
+				 dpp_get_curve(cmd, "DPPSigningKeyECC"));
+			if (wpa_command_resp(ifname, buf,
+					     buf, sizeof(buf)) < 0) {
+				send_resp(dut, conn, SIGMA_ERROR,
+					  "errorCode,Failed to set up configurator");
+				return 0;
+			}
+			dut->dpp_conf_id = atoi(buf);
+		}
+		role = "configurator";
+	} else if (strcasecmp(prov_role, "Enrollee") == 0) {
+		role = "enrollee";
+	} else {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Unknown DPPProvisioningRole");
+		return 0;
+	}
+
+	pkex_identifier[0] = '\0';
+	if (strcasecmp(bs, "PKEX") == 0) {
+		if (!pkex_code) {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "errorCode,Missing DPPPKEXCode");
+			return 0;
+		}
+
+		if (pkex_code_id)
+			snprintf(pkex_identifier, sizeof(pkex_identifier),
+				 "identifier=%s ", pkex_code_id);
+
+		snprintf(buf, sizeof(buf),
+			 "DPP_BOOTSTRAP_GEN type=pkex curve=%s",
+			 dpp_get_curve(cmd, "DPPCryptoIdentifier"));
+		if (wpa_command_resp(ifname, buf, buf, sizeof(buf)) < 0) {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "errorCode,Failed to set up PKEX");
+			return 0;
+		}
+		own_pkex_id = atoi(buf);
+	}
+
+	ctrl = open_wpa_mon(ifname);
+	if (!ctrl) {
+		sigma_dut_print(dut, DUT_MSG_ERROR,
+				"Failed to open wpa_supplicant monitor connection");
+		return -2;
+	}
+
+	old_timeout = dut->default_timeout;
+	val = get_param(cmd, "DPPTimeout");
+	if (val && atoi(val) > 0) {
+		dut->default_timeout = atoi(val);
+		sigma_dut_print(dut, DUT_MSG_DEBUG, "DPP timeout: %u",
+				dut->default_timeout);
+	}
+
+	conf_ssid[0] = '\0';
+	conf_pass[0] = '\0';
+	val = get_param(cmd, "DPPConfIndex");
+	if (val)
+		conf_index = atoi(val);
+	val = get_param(cmd, "DPPConfEnrolleeRole");
+	switch (conf_index) {
+	case 1:
+		ascii2hexstr("DPPNET01", buf);
+		snprintf(conf_ssid, sizeof(conf_ssid), "ssid=%s", buf);
+		if (val && strcasecmp(val, "AP") == 0)
+			conf_role = "ap-dpp";
+		else
+			conf_role = "sta-dpp";
+		break;
+	case 2:
+		ascii2hexstr("DPPNET01", buf);
+		snprintf(conf_ssid, sizeof(conf_ssid), "ssid=%s", buf);
+		/* TODO: raw PSK */
+		if (val && strcasecmp(val, "AP") == 0)
+			conf_role = "ap-psk";
+		else
+			conf_role = "sta-psk";
+		break;
+	case 3:
+		ascii2hexstr("DPPNET01", buf);
+		snprintf(conf_ssid, sizeof(conf_ssid), "ssid=%s", buf);
+		ascii2hexstr("ThisIsDppPassphrase", buf);
+		snprintf(conf_pass, sizeof(conf_pass), "pass=%s", buf);
+		if (val && strcasecmp(val, "AP") == 0)
+			conf_role = "ap-psk";
+		else
+			conf_role = "sta-psk";
+		break;
+	}
+
+	if (strcasecmp(auth_role, "Initiator") == 0) {
+		char own_txt[20];
+
+		if (mutual)
+			snprintf(own_txt, sizeof(own_txt), " own=%d",
+				 dut->dpp_local_bootstrap);
+		else
+			own_txt[0] = '\0';
+		if (strcasecmp(bs, "QR") == 0 &&
+		    strcasecmp(prov_role, "Configurator") == 0) {
+			snprintf(buf, sizeof(buf),
+				 "DPP_AUTH_INIT peer=%d%s role=%s conf=%s %s %s configurator=%d",
+				 dut->dpp_peer_bootstrap, own_txt, role,
+				 conf_role, conf_ssid, conf_pass,
+				 dut->dpp_conf_id);
+		} else if (strcasecmp(bs, "QR") == 0) {
+			snprintf(buf, sizeof(buf),
+				 "DPP_AUTH_INIT peer=%d%s role=%s",
+				 dut->dpp_peer_bootstrap, own_txt, role);
+		} else if (strcasecmp(bs, "PKEX") == 0 &&
+			   strcasecmp(prov_role, "Configurator") == 0) {
+			snprintf(buf, sizeof(buf),
+				 "DPP_PKEX_ADD own=%d init=1 role=%s conf=%s %s %s configurator=%d %scode=%s",
+				 own_pkex_id, role, conf_role,
+				 conf_ssid, conf_pass, dut->dpp_conf_id,
+				 pkex_identifier, pkex_code);
+		} else if (strcasecmp(bs, "PKEX") == 0) {
+			snprintf(buf, sizeof(buf),
+				 "DPP_PKEX_ADD own=%d init=1 role=%s %scode=%s",
+				 own_pkex_id, role, pkex_identifier, pkex_code);
+		}
+		if (wpa_command(ifname, buf) < 0) {
+			send_resp(dut, conn, SIGMA_ERROR,
+				  "errorCode,Failed to initiate DPP authentication");
+			goto out;
+		}
+	} else if (strcasecmp(auth_role, "Responder") == 0) {
+		int freq = 2462;
+
+		if (strcasecmp(prov_role, "Configurator") == 0) {
+			snprintf(buf, sizeof(buf),
+				 "SET dpp_configurator_params  conf=%s %s %s configurator=%d",
+				 conf_role, conf_ssid, conf_pass,
+				 dut->dpp_conf_id);
+			if (wpa_command(ifname, buf) < 0) {
+				send_resp(dut, conn, SIGMA_ERROR,
+					  "errorCode,Failed to set configurator parameters");
+				goto out;
+			}
+		}
+		if (strcasecmp(bs, "PKEX") == 0) {
+			freq = 2437;
+
+			snprintf(buf, sizeof(buf),
+				 "DPP_PKEX_ADD own=%d role=%s %scode=%s",
+				 own_pkex_id, role, pkex_identifier, pkex_code);
+			if (wpa_command(ifname, buf) < 0) {
+				send_resp(dut, conn, SIGMA_ERROR,
+					  "errorCode,Failed to configure DPP PKEX");
+				goto out;
+			}
+		}
+
+		if (!sigma_dut_is_ap(dut)) {
+			snprintf(buf, sizeof(buf), "DPP_LISTEN %d role=%s",
+				 freq, role);
+			if (wpa_command(ifname, buf) < 0) {
+				send_resp(dut, conn, SIGMA_ERROR,
+					  "errorCode,Failed to start DPP listen");
+				goto out;
+			}
+		}
+	} else {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Unknown DPPAuthRole");
+		goto out;
+	}
+
+	res = get_wpa_cli_events(dut, ctrl, auth_events, buf, sizeof(buf));
+	if (res < 0) {
+		send_resp(dut, conn, SIGMA_COMPLETE,
+			  "BootstrapResult,OK,AuthResult,Timeout");
+		goto out;
+	}
+	sigma_dut_print(dut, DUT_MSG_DEBUG, "DPP auth result: %s", buf);
+
+	if (strstr(buf, "DPP-NOT-COMPATIBLE")) {
+		send_resp(dut, conn, SIGMA_COMPLETE,
+			  "BootstrapResult,OK,AuthResult,ROLES_NOT_COMPATIBLE");
+		goto out;
+	}
+
+	if (!strstr(buf, "DPP-AUTH-SUCCESS")) {
+		send_resp(dut, conn, SIGMA_COMPLETE,
+			  "BootstrapResult,OK,AuthResult,FAILED");
+		goto out;
+	}
+
+	res = get_wpa_cli_events(dut, ctrl, conf_events, buf, sizeof(buf));
+	if (res < 0) {
+		send_resp(dut, conn, SIGMA_COMPLETE,
+			  "BootstrapResult,OK,AuthResult,OK,ConfResult,Timeout");
+		goto out;
+	}
+	sigma_dut_print(dut, DUT_MSG_DEBUG, "DPP conf result: %s", buf);
+
+	if (!strstr(buf, "DPP-CONF-SENT") &&
+	    !strstr(buf, "DPP-CONF-RECEIVED")) {
+		send_resp(dut, conn, SIGMA_COMPLETE,
+			  "BootstrapResult,OK,AuthResult,OK,ConfResult,FAILED");
+		goto out;
+	}
+
+	if (sigma_dut_is_ap(dut) &&
+	    strcasecmp(prov_role, "Enrollee") == 0) {
+		res = dpp_hostapd_conf_update(dut, conn, ifname, ctrl);
+		if (res == 0)
+			goto out;
+		if (res < 0) {
+			send_resp(dut, conn, SIGMA_ERROR, NULL);
+			goto out;
+		}
+	}
+
+	if (strcasecmp(wait_conn, "Yes") == 0 &&
+	    !sigma_dut_is_ap(dut) &&
+	    strcasecmp(prov_role, "Enrollee") == 0) {
+		res = get_wpa_cli_events(dut, ctrl, conn_events,
+					 buf, sizeof(buf));
+		if (res < 0) {
+			send_resp(dut, conn, SIGMA_COMPLETE,
+				  "BootstrapResult,OK,AuthResult,OK,ConfResult,OK,NetworkIntroResult,Timeout,NetworkConnectResult,Timeout");
+			goto out;
+		}
+		sigma_dut_print(dut, DUT_MSG_DEBUG, "DPP connect result: %s",
+				buf);
+
+		if (strstr(buf, "PMKSA-CACHE-ADDED")) {
+			res = get_wpa_cli_events(dut, ctrl, conn_events,
+						 buf, sizeof(buf));
+			if (res < 0) {
+				send_resp(dut, conn, SIGMA_COMPLETE,
+					  "BootstrapResult,OK,AuthResult,OK,ConfResult,OK,NetworkIntroResult,OK,NetworkConnectResult,Timeout");
+				goto out;
+			}
+			sigma_dut_print(dut, DUT_MSG_DEBUG,
+					"DPP connect result: %s", buf);
+			if (strstr(buf, "CTRL-EVENT-CONNECTED"))
+				send_resp(dut, conn, SIGMA_COMPLETE,
+					  "BootstrapResult,OK,AuthResult,OK,ConfResult,OK,NetworkIntroResult,OK,NetworkConnectResult,OK");
+			else
+				send_resp(dut, conn, SIGMA_COMPLETE,
+					  "BootstrapResult,OK,AuthResult,OK,ConfResult,OK,NetworkIntroResult,OK,NetworkConnectResult,Timeout");
+			goto out;
+		}
+
+		send_resp(dut, conn, SIGMA_COMPLETE,
+			  "BootstrapResult,OK,AuthResult,OK,ConfResult,OK,NetworkConnectResult,OK");
+		goto out;
+	}
+
+	send_resp(dut, conn, SIGMA_COMPLETE,
+		  "BootstrapResult,OK,AuthResult,OK,ConfResult,OK");
+out:
+	wpa_ctrl_detach(ctrl);
+	wpa_ctrl_close(ctrl);
+	dut->default_timeout = old_timeout;
+	return 0;
+}
+
+
+int dpp_dev_exec_action(struct sigma_dut *dut, struct sigma_conn *conn,
+			struct sigma_cmd *cmd)
+{
+	const char *type = get_param(cmd, "DPPActionType");
+	const char *bs = get_param(cmd, "DPPBS");
+
+	if (!bs) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Missing DPPBS");
+		return 0;
+	}
+
+	if (!type) {
+		send_resp(dut, conn, SIGMA_ERROR,
+			  "errorCode,Missing DPPActionType");
+		return 0;
+	}
+
+	if (strcasecmp(type, "GetLocalBootstrap") == 0)
+		return dpp_get_local_bootstrap(dut, conn, cmd);
+	if (strcasecmp(type, "SetPeerBootstrap") == 0)
+		return dpp_set_peer_bootstrap(dut, conn, cmd);
+	if (strcasecmp(type, "ManualDPP") == 0)
+		return dpp_manual_dpp(dut, conn, cmd);
+	if (strcasecmp(type, "AutomaticDPP") == 0)
+		return dpp_automatic_dpp(dut, conn, cmd);
+
+	send_resp(dut, conn, SIGMA_ERROR,
+		  "errorCode,Unsupported DPPActionType");
+	return 0;
+}