diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index 1ac009212..e5c7e9962 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -2688,6 +2688,18 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, } else if (os_strncmp(buf, "DPP_AUTH_INIT ", 14) == 0) { if (hostapd_dpp_auth_init(hapd, buf + 13) < 0) reply_len = -1; + } else if (os_strncmp(buf, "DPP_CONFIGURATOR_ADD", 20) == 0) { + res = hostapd_dpp_configurator_add(hapd, buf + 20); + if (res < 0) { + reply_len = -1; + } else { + reply_len = os_snprintf(reply, reply_size, "%d", res); + if (os_snprintf_error(reply_size, reply_len)) + reply_len = -1; + } + } else if (os_strncmp(buf, "DPP_CONFIGURATOR_REMOVE ", 24) == 0) { + if (hostapd_dpp_configurator_remove(hapd, buf + 24) < 0) + reply_len = -1; } else if (os_strncmp(buf, "DPP_PKEX_ADD ", 13) == 0) { res = hostapd_dpp_pkex_add(hapd, buf + 12); if (res < 0) { diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c index 21f5aa89c..648ae6503 100644 --- a/src/ap/dpp_hostapd.c +++ b/src/ap/dpp_hostapd.c @@ -1142,6 +1142,127 @@ void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src, } +struct wpabuf * +hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa, + const u8 *query, size_t query_len) +{ + struct dpp_authentication *auth = hapd->dpp_auth; + struct wpabuf *resp; + + wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR, MAC2STR(sa)); + if (!auth || !auth->auth_success || + os_memcmp(sa, auth->peer_mac_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress"); + return NULL; + } + wpa_hexdump(MSG_DEBUG, + "DPP: Received Configuration Request (GAS Query Request)", + query, query_len); + resp = dpp_conf_req_rx(auth, query, query_len); + if (!resp) + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED); + return resp; +} + + +static unsigned int hostapd_dpp_next_configurator_id(struct hostapd_data *hapd) +{ + struct dpp_configurator *conf; + unsigned int max_id = 0; + + dl_list_for_each(conf, &hapd->dpp_configurator, + struct dpp_configurator, list) { + if (conf->id > max_id) + max_id = conf->id; + } + return max_id + 1; +} + + +int hostapd_dpp_configurator_add(struct hostapd_data *hapd, const char *cmd) +{ + char *expiry = NULL, *curve = NULL; + char *key = NULL; + u8 *privkey = NULL; + size_t privkey_len = 0; + int ret = -1; + struct dpp_configurator *conf = NULL; + + expiry = get_param(cmd, " expiry="); + curve = get_param(cmd, " curve="); + key = get_param(cmd, " key="); + + if (key) { + privkey_len = os_strlen(key) / 2; + privkey = os_malloc(privkey_len); + if (!privkey || + hexstr2bin(key, privkey, privkey_len) < 0) + goto fail; + } + + conf = dpp_keygen_configurator(curve, privkey, privkey_len); + if (!conf) + goto fail; + + if (expiry) { + long int val; + + val = strtol(expiry, NULL, 0); + if (val <= 0) + goto fail; + conf->csign_expiry = val; + } + + conf->id = hostapd_dpp_next_configurator_id(hapd); + dl_list_add(&hapd->dpp_configurator, &conf->list); + ret = conf->id; + conf = NULL; +fail: + os_free(curve); + os_free(expiry); + str_clear_free(key); + bin_clear_free(privkey, privkey_len); + dpp_configurator_free(conf); + return ret; +} + + +static int dpp_configurator_del(struct hostapd_data *hapd, unsigned int id) +{ + struct dpp_configurator *conf, *tmp; + int found = 0; + + dl_list_for_each_safe(conf, tmp, &hapd->dpp_configurator, + struct dpp_configurator, list) { + if (id && conf->id != id) + continue; + found = 1; + dl_list_del(&conf->list); + dpp_configurator_free(conf); + } + + if (id == 0) + return 0; /* flush succeeds regardless of entries found */ + return found ? 0 : -1; +} + + +int hostapd_dpp_configurator_remove(struct hostapd_data *hapd, const char *id) +{ + unsigned int id_val; + + if (os_strcmp(id, "*") == 0) { + id_val = 0; + } else { + id_val = atoi(id); + if (id_val == 0) + return -1; + } + + return dpp_configurator_del(hapd, id_val); +} + + int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd) { struct dpp_bootstrap_info *own_bi; @@ -1246,6 +1367,7 @@ int hostapd_dpp_pkex_remove(struct hostapd_data *hapd, const char *id) int hostapd_dpp_init(struct hostapd_data *hapd) { dl_list_init(&hapd->dpp_bootstrap); + dl_list_init(&hapd->dpp_configurator); hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE; hapd->dpp_init_done = 1; return 0; @@ -1268,6 +1390,7 @@ void hostapd_dpp_deinit(struct hostapd_data *hapd) if (!hapd->dpp_init_done) return; dpp_bootstrap_del(hapd, 0); + dpp_configurator_del(hapd, 0); dpp_auth_deinit(hapd->dpp_auth); hapd->dpp_auth = NULL; hostapd_dpp_pkex_remove(hapd, "*"); diff --git a/src/ap/dpp_hostapd.h b/src/ap/dpp_hostapd.h index 4a585a1cd..9aa08305d 100644 --- a/src/ap/dpp_hostapd.h +++ b/src/ap/dpp_hostapd.h @@ -19,6 +19,11 @@ void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src, const u8 *buf, size_t len, unsigned int freq); void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst, const u8 *data, size_t data_len, int ok); +struct wpabuf * +hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa, + const u8 *query, size_t query_len); +int hostapd_dpp_configurator_add(struct hostapd_data *hapd, const char *cmd); +int hostapd_dpp_configurator_remove(struct hostapd_data *hapd, const char *id); int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd); int hostapd_dpp_pkex_remove(struct hostapd_data *hapd, const char *id); int hostapd_dpp_init(struct hostapd_data *hapd); diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c index 155728d4e..e3068efb4 100644 --- a/src/ap/gas_serv.c +++ b/src/ap/gas_serv.c @@ -11,14 +11,32 @@ #include "common.h" #include "common/ieee802_11_defs.h" #include "common/gas.h" +#include "common/dpp.h" +#include "common/wpa_ctrl.h" #include "utils/eloop.h" #include "hostapd.h" #include "ap_config.h" #include "ap_drv_ops.h" +#include "dpp_hostapd.h" #include "sta_info.h" #include "gas_serv.h" +#ifdef CONFIG_DPP +static void gas_serv_write_dpp_adv_proto(struct wpabuf *buf) +{ + wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); + wpabuf_put_u8(buf, 8); /* Length */ + wpabuf_put_u8(buf, 0x7f); + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(buf, 5); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, DPP_OUI_TYPE); + wpabuf_put_u8(buf, 0x01); +} +#endif /* CONFIG_DPP */ + + static void convert_to_protected_dual(struct wpabuf *msg) { u8 *categ = wpabuf_mhead_u8(msg); @@ -1393,6 +1411,72 @@ static void gas_serv_req_local_processing(struct hostapd_data *hapd, } +#ifdef CONFIG_DPP +static void gas_serv_req_dpp_processing(struct hostapd_data *hapd, + const u8 *sa, u8 dialog_token, + int prot, struct wpabuf *buf) +{ + struct wpabuf *tx_buf; + + if (wpabuf_len(buf) > hapd->conf->gas_frag_limit || + hapd->conf->gas_comeback_delay) { + struct gas_dialog_info *di; + u16 comeback_delay = 1; + + if (hapd->conf->gas_comeback_delay) { + /* Testing - allow overriding of the delay value */ + comeback_delay = hapd->conf->gas_comeback_delay; + } + + wpa_printf(MSG_DEBUG, + "DPP: Too long response to fit in initial response - use GAS comeback"); + di = gas_dialog_create(hapd, sa, dialog_token); + if (!di) { + wpa_printf(MSG_INFO, "DPP: Could not create dialog for " + MACSTR " (dialog token %u)", + MAC2STR(sa), dialog_token); + wpabuf_free(buf); + tx_buf = gas_build_initial_resp( + dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE, + 0, 10); + if (tx_buf) + gas_serv_write_dpp_adv_proto(tx_buf); + } else { + di->prot = prot; + di->sd_resp = buf; + di->sd_resp_pos = 0; + tx_buf = gas_build_initial_resp( + dialog_token, WLAN_STATUS_SUCCESS, + comeback_delay, 10); + if (tx_buf) + gas_serv_write_dpp_adv_proto(tx_buf); + } + } else { + wpa_printf(MSG_DEBUG, + "DPP: GAS Initial response (no comeback)"); + tx_buf = gas_build_initial_resp( + dialog_token, WLAN_STATUS_SUCCESS, 0, + 10 + 2 + wpabuf_len(buf)); + if (tx_buf) { + gas_serv_write_dpp_adv_proto(tx_buf); + wpabuf_put_le16(tx_buf, wpabuf_len(buf)); + wpabuf_put_buf(tx_buf, buf); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT); + } + wpabuf_free(buf); + } + if (!tx_buf) + return; + if (prot) + convert_to_protected_dual(tx_buf); + hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, + wpabuf_head(tx_buf), + wpabuf_len(tx_buf)); + wpabuf_free(tx_buf); +} +#endif /* CONFIG_DPP */ + + static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, const u8 *sa, const u8 *data, size_t len, int prot, @@ -1405,6 +1489,9 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, u16 slen; struct anqp_query_info qi; const u8 *adv_proto; +#ifdef CONFIG_DPP + int dpp = 0; +#endif /* CONFIG_DPP */ if (len < 1 + 2) return; @@ -1432,6 +1519,15 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, next = pos + slen; pos++; /* skip QueryRespLenLimit and PAME-BI */ +#ifdef CONFIG_DPP + if (slen == 8 && *pos == WLAN_EID_VENDOR_SPECIFIC && + pos[1] == 5 && WPA_GET_BE24(&pos[2]) == OUI_WFA && + pos[5] == DPP_OUI_TYPE && pos[6] == 0x01) { + wpa_printf(MSG_DEBUG, "DPP: Configuration Request"); + dpp = 1; + } else +#endif /* CONFIG_DPP */ + if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { struct wpabuf *buf; wpa_msg(hapd->msg_ctx, MSG_DEBUG, @@ -1471,6 +1567,18 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, return; end = pos + slen; +#ifdef CONFIG_DPP + if (dpp) { + struct wpabuf *msg; + + msg = hostapd_dpp_gas_req_handler(hapd, sa, pos, slen); + if (!msg) + return; + gas_serv_req_dpp_processing(hapd, sa, dialog_token, prot, msg); + return; + } +#endif /* CONFIG_DPP */ + /* ANQP Query Request */ while (pos < end) { u16 info_id, elen; @@ -1558,6 +1666,18 @@ static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd, gas_serv_dialog_clear(dialog); return; } +#ifdef CONFIG_DPP + if (dialog->dpp) { + tx_buf = gas_build_comeback_resp(dialog_token, + WLAN_STATUS_SUCCESS, + dialog->sd_frag_id, more, 0, + 10 + frag_len); + if (tx_buf) { + gas_serv_write_dpp_adv_proto(tx_buf); + wpabuf_put_buf(tx_buf, buf); + } + } else +#endif /* CONFIG_DPP */ tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token, WLAN_STATUS_SUCCESS, dialog->sd_frag_id, @@ -1581,6 +1701,10 @@ static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd, } else { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of " "SD response sent"); +#ifdef CONFIG_DPP + if (dialog->dpp) + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT); +#endif /* CONFIG_DPP */ gas_serv_dialog_clear(dialog); gas_serv_free_dialogs(hapd, sa); } diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h index 4af6ddafb..3a3029813 100644 --- a/src/ap/gas_serv.h +++ b/src/ap/gas_serv.h @@ -71,6 +71,7 @@ struct gas_dialog_info { size_t sd_resp_pos; /* Offset in sd_resp */ u8 sd_frag_id; int prot; /* whether Protected Dual of Public Action frame is used */ + int dpp; /* whether this is a DPP Config Response */ }; struct hostapd_data; diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index 9222628cc..794635f66 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -322,6 +322,7 @@ struct hostapd_data { #ifdef CONFIG_DPP struct dl_list dpp_bootstrap; /* struct dpp_bootstrap_info */ + struct dl_list dpp_configurator; /* struct dpp_configurator */ int dpp_init_done; struct dpp_authentication *dpp_auth; u8 dpp_allowed_roles;