DPP: Configurator in hostapd

This integrates DPP configuration request processing into hostapd GAS
server implementation.

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
Jouni Malinen 2017-07-03 12:56:48 +03:00 committed by Jouni Malinen
parent 163f76ded3
commit 2605405aa4
6 changed files with 266 additions and 0 deletions

View file

@ -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_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) { } else if (os_strncmp(buf, "DPP_PKEX_ADD ", 13) == 0) {
res = hostapd_dpp_pkex_add(hapd, buf + 12); res = hostapd_dpp_pkex_add(hapd, buf + 12);
if (res < 0) { if (res < 0) {

View file

@ -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) int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd)
{ {
struct dpp_bootstrap_info *own_bi; 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) int hostapd_dpp_init(struct hostapd_data *hapd)
{ {
dl_list_init(&hapd->dpp_bootstrap); dl_list_init(&hapd->dpp_bootstrap);
dl_list_init(&hapd->dpp_configurator);
hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE; hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE;
hapd->dpp_init_done = 1; hapd->dpp_init_done = 1;
return 0; return 0;
@ -1268,6 +1390,7 @@ void hostapd_dpp_deinit(struct hostapd_data *hapd)
if (!hapd->dpp_init_done) if (!hapd->dpp_init_done)
return; return;
dpp_bootstrap_del(hapd, 0); dpp_bootstrap_del(hapd, 0);
dpp_configurator_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, "*"); hostapd_dpp_pkex_remove(hapd, "*");

View file

@ -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); 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);
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_add(struct hostapd_data *hapd, const char *cmd);
int hostapd_dpp_pkex_remove(struct hostapd_data *hapd, const char *id); 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);

View file

@ -11,14 +11,32 @@
#include "common.h" #include "common.h"
#include "common/ieee802_11_defs.h" #include "common/ieee802_11_defs.h"
#include "common/gas.h" #include "common/gas.h"
#include "common/dpp.h"
#include "common/wpa_ctrl.h"
#include "utils/eloop.h" #include "utils/eloop.h"
#include "hostapd.h" #include "hostapd.h"
#include "ap_config.h" #include "ap_config.h"
#include "ap_drv_ops.h" #include "ap_drv_ops.h"
#include "dpp_hostapd.h"
#include "sta_info.h" #include "sta_info.h"
#include "gas_serv.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) static void convert_to_protected_dual(struct wpabuf *msg)
{ {
u8 *categ = wpabuf_mhead_u8(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, static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
const u8 *sa, const u8 *sa,
const u8 *data, size_t len, int prot, 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; u16 slen;
struct anqp_query_info qi; struct anqp_query_info qi;
const u8 *adv_proto; const u8 *adv_proto;
#ifdef CONFIG_DPP
int dpp = 0;
#endif /* CONFIG_DPP */
if (len < 1 + 2) if (len < 1 + 2)
return; return;
@ -1432,6 +1519,15 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
next = pos + slen; next = pos + slen;
pos++; /* skip QueryRespLenLimit and PAME-BI */ 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) { if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
struct wpabuf *buf; struct wpabuf *buf;
wpa_msg(hapd->msg_ctx, MSG_DEBUG, wpa_msg(hapd->msg_ctx, MSG_DEBUG,
@ -1471,6 +1567,18 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
return; return;
end = pos + slen; 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 */ /* ANQP Query Request */
while (pos < end) { while (pos < end) {
u16 info_id, elen; 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); gas_serv_dialog_clear(dialog);
return; 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, tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
WLAN_STATUS_SUCCESS, WLAN_STATUS_SUCCESS,
dialog->sd_frag_id, dialog->sd_frag_id,
@ -1581,6 +1701,10 @@ static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
} else { } else {
wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of " wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of "
"SD response sent"); "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_dialog_clear(dialog);
gas_serv_free_dialogs(hapd, sa); gas_serv_free_dialogs(hapd, sa);
} }

View file

@ -71,6 +71,7 @@ struct gas_dialog_info {
size_t sd_resp_pos; /* Offset in sd_resp */ size_t sd_resp_pos; /* Offset in sd_resp */
u8 sd_frag_id; u8 sd_frag_id;
int prot; /* whether Protected Dual of Public Action frame is used */ int prot; /* whether Protected Dual of Public Action frame is used */
int dpp; /* whether this is a DPP Config Response */
}; };
struct hostapd_data; struct hostapd_data;

View file

@ -322,6 +322,7 @@ struct hostapd_data {
#ifdef CONFIG_DPP #ifdef CONFIG_DPP
struct dl_list dpp_bootstrap; /* struct dpp_bootstrap_info */ struct dl_list dpp_bootstrap; /* struct dpp_bootstrap_info */
struct dl_list dpp_configurator; /* struct dpp_configurator */
int dpp_init_done; int dpp_init_done;
struct dpp_authentication *dpp_auth; struct dpp_authentication *dpp_auth;
u8 dpp_allowed_roles; u8 dpp_allowed_roles;