DPP: PKEX in hostapd
Allow hostapd to initiate and respond with PKEX bootstrapping similarly to how this was implemented in wpa_supplicant. Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
parent
359dc93de5
commit
efeada91a4
5 changed files with 326 additions and 1 deletions
|
@ -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) {
|
} else if (os_strncmp(buf, "DPP_AUTH_INIT ", 14) == 0) {
|
||||||
if (hostapd_dpp_auth_init(hapd, buf + 13) < 0)
|
if (hostapd_dpp_auth_init(hapd, buf + 13) < 0)
|
||||||
reply_len = -1;
|
reply_len = -1;
|
||||||
|
} else if (os_strncmp(buf, "DPP_PKEX_ADD ", 13) == 0) {
|
||||||
|
res = hostapd_dpp_pkex_add(hapd, buf + 12);
|
||||||
|
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_PKEX_REMOVE ", 16) == 0) {
|
||||||
|
if (hostapd_dpp_pkex_remove(hapd, buf + 16) < 0)
|
||||||
|
reply_len = -1;
|
||||||
#endif /* CONFIG_DPP */
|
#endif /* CONFIG_DPP */
|
||||||
} else {
|
} else {
|
||||||
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
|
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
|
||||||
|
|
|
@ -121,6 +121,8 @@ int hostapd_dpp_bootstrap_gen(struct hostapd_data *hapd, const char *cmd)
|
||||||
|
|
||||||
if (os_strstr(cmd, "type=qrcode"))
|
if (os_strstr(cmd, "type=qrcode"))
|
||||||
bi->type = DPP_BOOTSTRAP_QR_CODE;
|
bi->type = DPP_BOOTSTRAP_QR_CODE;
|
||||||
|
else if (os_strstr(cmd, "type=pkex"))
|
||||||
|
bi->type = DPP_BOOTSTRAP_PKEX;
|
||||||
else
|
else
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
|
@ -901,6 +903,192 @@ static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
hostapd_dpp_rx_pkex_exchange_req(struct hostapd_data *hapd, const u8 *src,
|
||||||
|
const u8 *buf, size_t len, unsigned int freq)
|
||||||
|
{
|
||||||
|
struct wpabuf *msg;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request from " MACSTR,
|
||||||
|
MAC2STR(src));
|
||||||
|
|
||||||
|
/* TODO: Support multiple PKEX codes by iterating over all the enabled
|
||||||
|
* values here */
|
||||||
|
|
||||||
|
if (!hapd->dpp_pkex_code || !hapd->dpp_pkex_bi) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"DPP: No PKEX code configured - ignore request");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hapd->dpp_pkex) {
|
||||||
|
/* TODO: Support parallel operations */
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"DPP: Already in PKEX session - ignore new request");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hapd->dpp_pkex = dpp_pkex_rx_exchange_req(hapd->dpp_pkex_bi,
|
||||||
|
hapd->own_addr, src,
|
||||||
|
hapd->dpp_pkex_identifier,
|
||||||
|
hapd->dpp_pkex_code,
|
||||||
|
buf, len);
|
||||||
|
if (!hapd->dpp_pkex) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"DPP: Failed to process the request - ignore it");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg = hapd->dpp_pkex->exchange_resp;
|
||||||
|
hostapd_drv_send_action(hapd, freq, 0, src,
|
||||||
|
wpabuf_head(msg), wpabuf_len(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
hostapd_dpp_rx_pkex_exchange_resp(struct hostapd_data *hapd, const u8 *src,
|
||||||
|
const u8 *buf, size_t len, unsigned int freq)
|
||||||
|
{
|
||||||
|
struct wpabuf *msg;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response from " MACSTR,
|
||||||
|
MAC2STR(src));
|
||||||
|
|
||||||
|
/* TODO: Support multiple PKEX codes by iterating over all the enabled
|
||||||
|
* values here */
|
||||||
|
|
||||||
|
if (!hapd->dpp_pkex || !hapd->dpp_pkex->initiator ||
|
||||||
|
hapd->dpp_pkex->exchange_done) {
|
||||||
|
wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
os_memcpy(hapd->dpp_pkex->peer_mac, src, ETH_ALEN);
|
||||||
|
msg = dpp_pkex_rx_exchange_resp(hapd->dpp_pkex, buf, len);
|
||||||
|
if (!msg) {
|
||||||
|
wpa_printf(MSG_DEBUG, "DPP: Failed to process the response");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Request to " MACSTR,
|
||||||
|
MAC2STR(src));
|
||||||
|
|
||||||
|
hostapd_drv_send_action(hapd, freq, 0, src,
|
||||||
|
wpabuf_head(msg), wpabuf_len(msg));
|
||||||
|
wpabuf_free(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
hostapd_dpp_rx_pkex_commit_reveal_req(struct hostapd_data *hapd, const u8 *src,
|
||||||
|
const u8 *buf, size_t len,
|
||||||
|
unsigned int freq)
|
||||||
|
{
|
||||||
|
struct wpabuf *msg;
|
||||||
|
struct dpp_pkex *pkex = hapd->dpp_pkex;
|
||||||
|
struct dpp_bootstrap_info *bi;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Request from " MACSTR,
|
||||||
|
MAC2STR(src));
|
||||||
|
|
||||||
|
if (!pkex || pkex->initiator || !pkex->exchange_done) {
|
||||||
|
wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg = dpp_pkex_rx_commit_reveal_req(pkex, buf, len);
|
||||||
|
if (!msg) {
|
||||||
|
wpa_printf(MSG_DEBUG, "DPP: Failed to process the request");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Response to "
|
||||||
|
MACSTR, MAC2STR(src));
|
||||||
|
|
||||||
|
hostapd_drv_send_action(hapd, freq, 0, src,
|
||||||
|
wpabuf_head(msg), wpabuf_len(msg));
|
||||||
|
wpabuf_free(msg);
|
||||||
|
|
||||||
|
bi = os_zalloc(sizeof(*bi));
|
||||||
|
if (!bi)
|
||||||
|
return;
|
||||||
|
bi->id = hapd_dpp_next_id(hapd);
|
||||||
|
bi->type = DPP_BOOTSTRAP_PKEX;
|
||||||
|
os_memcpy(bi->mac_addr, src, ETH_ALEN);
|
||||||
|
bi->num_freq = 1;
|
||||||
|
bi->freq[0] = freq;
|
||||||
|
bi->curve = pkex->own_bi->curve;
|
||||||
|
bi->pubkey = pkex->peer_bootstrap_key;
|
||||||
|
pkex->peer_bootstrap_key = NULL;
|
||||||
|
dpp_pkex_free(pkex);
|
||||||
|
hapd->dpp_pkex = NULL;
|
||||||
|
if (dpp_bootstrap_key_hash(bi) < 0) {
|
||||||
|
dpp_bootstrap_info_free(bi);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dl_list_add(&hapd->dpp_bootstrap, &bi->list);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
hostapd_dpp_rx_pkex_commit_reveal_resp(struct hostapd_data *hapd, const u8 *src,
|
||||||
|
const u8 *buf, size_t len,
|
||||||
|
unsigned int freq)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
struct dpp_bootstrap_info *bi, *own_bi;
|
||||||
|
struct dpp_pkex *pkex = hapd->dpp_pkex;
|
||||||
|
char cmd[500];
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Response from " MACSTR,
|
||||||
|
MAC2STR(src));
|
||||||
|
|
||||||
|
if (!pkex || !pkex->initiator || !pkex->exchange_done) {
|
||||||
|
wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = dpp_pkex_rx_commit_reveal_resp(pkex, buf, len);
|
||||||
|
if (res < 0) {
|
||||||
|
wpa_printf(MSG_DEBUG, "DPP: Failed to process the response");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
own_bi = pkex->own_bi;
|
||||||
|
|
||||||
|
bi = os_zalloc(sizeof(*bi));
|
||||||
|
if (!bi)
|
||||||
|
return;
|
||||||
|
bi->id = hapd_dpp_next_id(hapd);
|
||||||
|
bi->type = DPP_BOOTSTRAP_PKEX;
|
||||||
|
os_memcpy(bi->mac_addr, src, ETH_ALEN);
|
||||||
|
bi->num_freq = 1;
|
||||||
|
bi->freq[0] = freq;
|
||||||
|
bi->curve = own_bi->curve;
|
||||||
|
bi->pubkey = pkex->peer_bootstrap_key;
|
||||||
|
pkex->peer_bootstrap_key = NULL;
|
||||||
|
dpp_pkex_free(pkex);
|
||||||
|
hapd->dpp_pkex = NULL;
|
||||||
|
if (dpp_bootstrap_key_hash(bi) < 0) {
|
||||||
|
dpp_bootstrap_info_free(bi);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dl_list_add(&hapd->dpp_bootstrap, &bi->list);
|
||||||
|
|
||||||
|
os_snprintf(cmd, sizeof(cmd), " peer=%u %s",
|
||||||
|
bi->id,
|
||||||
|
hapd->dpp_pkex_auth_cmd ? hapd->dpp_pkex_auth_cmd : "");
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"DPP: Start authentication after PKEX with parameters: %s",
|
||||||
|
cmd);
|
||||||
|
if (hostapd_dpp_auth_init(hapd, cmd) < 0) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"DPP: Authentication initialization failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
|
void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
|
||||||
const u8 *buf, size_t len, unsigned int freq)
|
const u8 *buf, size_t len, unsigned int freq)
|
||||||
{
|
{
|
||||||
|
@ -933,6 +1121,19 @@ void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
|
||||||
case DPP_PA_PEER_DISCOVERY_REQ:
|
case DPP_PA_PEER_DISCOVERY_REQ:
|
||||||
hostapd_dpp_rx_peer_disc_req(hapd, src, buf, len, freq);
|
hostapd_dpp_rx_peer_disc_req(hapd, src, buf, len, freq);
|
||||||
break;
|
break;
|
||||||
|
case DPP_PA_PKEX_EXCHANGE_REQ:
|
||||||
|
hostapd_dpp_rx_pkex_exchange_req(hapd, src, buf, len, freq);
|
||||||
|
break;
|
||||||
|
case DPP_PA_PKEX_EXCHANGE_RESP:
|
||||||
|
hostapd_dpp_rx_pkex_exchange_resp(hapd, src, buf, len, freq);
|
||||||
|
break;
|
||||||
|
case DPP_PA_PKEX_COMMIT_REVEAL_REQ:
|
||||||
|
hostapd_dpp_rx_pkex_commit_reveal_req(hapd, src, buf, len, freq);
|
||||||
|
break;
|
||||||
|
case DPP_PA_PKEX_COMMIT_REVEAL_RESP:
|
||||||
|
hostapd_dpp_rx_pkex_commit_reveal_resp(hapd, src, buf, len,
|
||||||
|
freq);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
wpa_printf(MSG_DEBUG,
|
wpa_printf(MSG_DEBUG,
|
||||||
"DPP: Ignored unsupported frame subtype %d", type);
|
"DPP: Ignored unsupported frame subtype %d", type);
|
||||||
|
@ -941,6 +1142,107 @@ void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd)
|
||||||
|
{
|
||||||
|
struct dpp_bootstrap_info *own_bi;
|
||||||
|
const char *pos, *end;
|
||||||
|
|
||||||
|
pos = os_strstr(cmd, " own=");
|
||||||
|
if (!pos)
|
||||||
|
return -1;
|
||||||
|
pos += 5;
|
||||||
|
own_bi = dpp_bootstrap_get_id(hapd, atoi(pos));
|
||||||
|
if (!own_bi) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"DPP: Identified bootstrap info not found");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (own_bi->type != DPP_BOOTSTRAP_PKEX) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"DPP: Identified bootstrap info not for PKEX");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
hapd->dpp_pkex_bi = own_bi;
|
||||||
|
|
||||||
|
os_free(hapd->dpp_pkex_identifier);
|
||||||
|
hapd->dpp_pkex_identifier = NULL;
|
||||||
|
pos = os_strstr(cmd, " identifier=");
|
||||||
|
if (pos) {
|
||||||
|
pos += 12;
|
||||||
|
end = os_strchr(pos, ' ');
|
||||||
|
if (!end)
|
||||||
|
return -1;
|
||||||
|
hapd->dpp_pkex_identifier = os_malloc(end - pos + 1);
|
||||||
|
if (!hapd->dpp_pkex_identifier)
|
||||||
|
return -1;
|
||||||
|
os_memcpy(hapd->dpp_pkex_identifier, pos, end - pos);
|
||||||
|
hapd->dpp_pkex_identifier[end - pos] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = os_strstr(cmd, " code=");
|
||||||
|
if (!pos)
|
||||||
|
return -1;
|
||||||
|
os_free(hapd->dpp_pkex_code);
|
||||||
|
hapd->dpp_pkex_code = os_strdup(pos + 6);
|
||||||
|
if (!hapd->dpp_pkex_code)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (os_strstr(cmd, " init=1")) {
|
||||||
|
struct wpabuf *msg;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "DPP: Initiating PKEX");
|
||||||
|
dpp_pkex_free(hapd->dpp_pkex);
|
||||||
|
hapd->dpp_pkex = dpp_pkex_init(own_bi, hapd->own_addr,
|
||||||
|
hapd->dpp_pkex_identifier,
|
||||||
|
hapd->dpp_pkex_code);
|
||||||
|
if (!hapd->dpp_pkex)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
msg = hapd->dpp_pkex->exchange_req;
|
||||||
|
/* TODO: Which channel to use? */
|
||||||
|
hostapd_drv_send_action(hapd, 2437, 0, broadcast,
|
||||||
|
wpabuf_head(msg), wpabuf_len(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Support multiple PKEX info entries */
|
||||||
|
|
||||||
|
os_free(hapd->dpp_pkex_auth_cmd);
|
||||||
|
hapd->dpp_pkex_auth_cmd = os_strdup(cmd);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hostapd_dpp_pkex_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((id_val != 0 && id_val != 1) || !hapd->dpp_pkex_code)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* TODO: Support multiple PKEX entries */
|
||||||
|
os_free(hapd->dpp_pkex_code);
|
||||||
|
hapd->dpp_pkex_code = NULL;
|
||||||
|
os_free(hapd->dpp_pkex_identifier);
|
||||||
|
hapd->dpp_pkex_identifier = NULL;
|
||||||
|
os_free(hapd->dpp_pkex_auth_cmd);
|
||||||
|
hapd->dpp_pkex_auth_cmd = NULL;
|
||||||
|
hapd->dpp_pkex_bi = NULL;
|
||||||
|
/* TODO: Remove dpp_pkex only if it is for the identified PKEX code */
|
||||||
|
dpp_pkex_free(hapd->dpp_pkex);
|
||||||
|
hapd->dpp_pkex = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int hostapd_dpp_init(struct hostapd_data *hapd)
|
int hostapd_dpp_init(struct hostapd_data *hapd)
|
||||||
{
|
{
|
||||||
dl_list_init(&hapd->dpp_bootstrap);
|
dl_list_init(&hapd->dpp_bootstrap);
|
||||||
|
@ -968,4 +1270,6 @@ void hostapd_dpp_deinit(struct hostapd_data *hapd)
|
||||||
dpp_bootstrap_del(hapd, 0);
|
dpp_bootstrap_del(hapd, 0);
|
||||||
dpp_auth_deinit(hapd->dpp_auth);
|
dpp_auth_deinit(hapd->dpp_auth);
|
||||||
hapd->dpp_auth = NULL;
|
hapd->dpp_auth = NULL;
|
||||||
|
hostapd_dpp_pkex_remove(hapd, "*");
|
||||||
|
hapd->dpp_pkex = NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,8 @@ void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
|
||||||
const u8 *buf, size_t len, unsigned int freq);
|
const u8 *buf, size_t len, unsigned int freq);
|
||||||
void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst,
|
void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst,
|
||||||
const u8 *data, size_t data_len, int ok);
|
const u8 *data, size_t data_len, int ok);
|
||||||
|
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);
|
int hostapd_dpp_init(struct hostapd_data *hapd);
|
||||||
void hostapd_dpp_deinit(struct hostapd_data *hapd);
|
void hostapd_dpp_deinit(struct hostapd_data *hapd);
|
||||||
|
|
||||||
|
|
|
@ -328,6 +328,11 @@ struct hostapd_data {
|
||||||
int dpp_qr_mutual;
|
int dpp_qr_mutual;
|
||||||
int dpp_auth_ok_on_ack;
|
int dpp_auth_ok_on_ack;
|
||||||
struct gas_query_ap *gas;
|
struct gas_query_ap *gas;
|
||||||
|
struct dpp_pkex *dpp_pkex;
|
||||||
|
struct dpp_bootstrap_info *dpp_pkex_bi;
|
||||||
|
char *dpp_pkex_code;
|
||||||
|
char *dpp_pkex_identifier;
|
||||||
|
char *dpp_pkex_auth_cmd;
|
||||||
#ifdef CONFIG_TESTING_OPTIONS
|
#ifdef CONFIG_TESTING_OPTIONS
|
||||||
char *dpp_config_obj_override;
|
char *dpp_config_obj_override;
|
||||||
char *dpp_discovery_override;
|
char *dpp_discovery_override;
|
||||||
|
|
|
@ -3602,7 +3602,9 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) {
|
if ((!is_broadcast_ether_addr(mgmt->da) ||
|
||||||
|
stype != WLAN_FC_STYPE_ACTION) &&
|
||||||
|
os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) {
|
||||||
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
|
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
|
||||||
HOSTAPD_LEVEL_DEBUG,
|
HOSTAPD_LEVEL_DEBUG,
|
||||||
"MGMT: DA=" MACSTR " not our address",
|
"MGMT: DA=" MACSTR " not our address",
|
||||||
|
|
Loading…
Reference in a new issue