DPP3: Push button Configurator in wpa_supplicant

Extend DPP push button support in wpa_supplicant to allow the role of
the Configurator to be used. This provides similar functionality to the
way the DPP_PUSH_BUTTON command in hostapd worked when providing the
configuration parameters with that command (instead of building the
config object based on current AP configuration).

Signed-off-by: Jouni Malinen <quic_jouni@quicinc.com>
This commit is contained in:
Jouni Malinen 2022-07-22 12:28:18 +03:00 committed by Jouni Malinen
parent b94e46bc71
commit 1ff9251a83
7 changed files with 388 additions and 16 deletions

View file

@ -43,13 +43,6 @@ struct mesh_conf;
#define CTRL_IFACE_COOKIE_LEN 8
#endif /* CONFIG_CTRL_IFACE_UDP */
#define DPP_PB_INFO_COUNT 2
struct dpp_pb_info {
u8 hash[SHA256_MAC_LEN];
struct os_reltime rx_time;
};
struct hostapd_iface;
struct hapd_interfaces {

View file

@ -447,6 +447,13 @@ struct dpp_controller_config {
bool (*tcp_msg_sent)(void *ctx, struct dpp_authentication *auth);
};
#define DPP_PB_INFO_COUNT 2
struct dpp_pb_info {
u8 hash[SHA256_MAC_LEN];
struct os_reltime rx_time;
};
#ifdef CONFIG_TESTING_OPTIONS
enum dpp_test_behavior {
DPP_TEST_DISABLED = 0,

View file

@ -12455,7 +12455,10 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
#endif /* CONFIG_DPP2 */
#ifdef CONFIG_DPP3
} else if (os_strcmp(buf, "DPP_PUSH_BUTTON") == 0) {
if (wpas_dpp_push_button(wpa_s) < 0)
if (wpas_dpp_push_button(wpa_s, NULL) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "DPP_PUSH_BUTTON ", 16) == 0) {
if (wpas_dpp_push_button(wpa_s, buf + 15) < 0)
reply_len = -1;
#endif /* CONFIG_DPP3 */
#endif /* CONFIG_DPP */

View file

@ -17,6 +17,7 @@
#include "common/dpp.h"
#include "common/gas.h"
#include "common/gas_server.h"
#include "crypto/random.h"
#include "rsn_supp/wpa.h"
#include "rsn_supp/pmksa_cache.h"
#include "wpa_supplicant_i.h"
@ -2032,6 +2033,42 @@ static void wpas_dpp_conn_status_result_wait_timeout(void *eloop_ctx,
}
#ifdef CONFIG_DPP3
static bool wpas_dpp_pb_active(struct wpa_supplicant *wpa_s)
{
return (wpa_s->dpp_pb_time.sec || wpa_s->dpp_pb_time.usec) &&
wpa_s->dpp_pb_configurator;
}
static void wpas_dpp_remove_pb_hash(struct wpa_supplicant *wpa_s)
{
int i;
if (!wpa_s->dpp_pb_bi)
return;
for (i = 0; i < DPP_PB_INFO_COUNT; i++) {
struct dpp_pb_info *info = &wpa_s->dpp_pb[i];
if (info->rx_time.sec == 0 && info->rx_time.usec == 0)
continue;
if (os_memcmp(info->hash, wpa_s->dpp_pb_resp_hash,
SHA256_MAC_LEN) == 0) {
/* Allow a new push button session to be established
* immediately without the successfully completed
* session triggering session overlap. */
info->rx_time.sec = 0;
info->rx_time.usec = 0;
wpa_printf(MSG_DEBUG,
"DPP: Removed PB hash from session overlap detection due to successfully completed provisioning");
}
}
}
#endif /* CONFIG_DPP3 */
static void wpas_dpp_rx_conf_result(struct wpa_supplicant *wpa_s, const u8 *src,
const u8 *hdr, const u8 *buf, size_t len)
{
@ -2098,6 +2135,20 @@ static void wpas_dpp_rx_conf_result(struct wpa_supplicant *wpa_s, const u8 *src,
dpp_auth_deinit(auth);
wpa_s->dpp_auth = NULL;
eloop_cancel_timeout(wpas_dpp_config_result_wait_timeout, wpa_s, NULL);
#ifdef CONFIG_DPP3
if (!wpa_s->dpp_pb_result_indicated && wpas_dpp_pb_active(wpa_s)) {
if (status == DPP_STATUS_OK)
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_RESULT
"success");
else
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_RESULT
"no-configuration-available");
wpa_s->dpp_pb_result_indicated = true;
if (status == DPP_STATUS_OK)
wpas_dpp_remove_pb_hash(wpa_s);
wpas_dpp_push_button_stop(wpa_s);
}
#endif /* CONFIG_DPP3 */
}
@ -3083,7 +3134,7 @@ wpas_dpp_pkex_finish(struct wpa_supplicant *wpa_s, const u8 *peer,
wpa_s->dpp_pkex = NULL;
#ifdef CONFIG_DPP3
if (wpa_s->dpp_pb_bi &&
if (wpa_s->dpp_pb_bi && !wpa_s->dpp_pb_configurator &&
os_memcmp(bi->pubkey_hash_chirp, wpa_s->dpp_pb_init_hash,
SHA256_MAC_LEN) != 0) {
char id[20];
@ -3185,6 +3236,28 @@ wpas_dpp_rx_pkex_commit_reveal_resp(struct wpa_supplicant *wpa_s, const u8 *src,
if (!bi)
return;
#ifdef CONFIG_DPP3
if (wpa_s->dpp_pb_bi && wpa_s->dpp_pb_configurator &&
os_memcmp(bi->pubkey_hash_chirp, wpa_s->dpp_pb_resp_hash,
SHA256_MAC_LEN) != 0) {
char id[20];
wpa_printf(MSG_INFO,
"DPP: Peer bootstrap key from PKEX does not match PB announcement hash");
wpa_hexdump(MSG_DEBUG,
"DPP: Peer provided bootstrap key hash(chirp) from PB PKEX",
bi->pubkey_hash_chirp, SHA256_MAC_LEN);
wpa_hexdump(MSG_DEBUG,
"DPP: Peer provided bootstrap key hash(chirp) from PB announcement",
wpa_s->dpp_pb_resp_hash, SHA256_MAC_LEN);
os_snprintf(id, sizeof(id), "%u", bi->id);
dpp_bootstrap_remove(wpa_s->dpp, id);
wpas_dpp_push_button_stop(wpa_s);
return;
}
#endif /* CONFIG_DPP3 */
os_snprintf(cmd, sizeof(cmd), " peer=%u %s",
bi->id,
wpa_s->dpp_pkex_auth_cmd ? wpa_s->dpp_pkex_auth_cmd : "");
@ -3202,6 +3275,220 @@ wpas_dpp_rx_pkex_commit_reveal_resp(struct wpa_supplicant *wpa_s, const u8 *src,
#ifdef CONFIG_DPP3
static void wpas_dpp_pb_pkex_init(struct wpa_supplicant *wpa_s,
unsigned int freq, const u8 *src,
const u8 *r_hash)
{
struct dpp_pkex *pkex;
struct wpabuf *msg;
unsigned int wait_time;
if (wpa_s->dpp_pkex) {
wpa_printf(MSG_DEBUG,
"DPP: Sending previously generated PKEX Exchange Request to "
MACSTR, MAC2STR(src));
msg = wpa_s->dpp_pkex->exchange_req;
wait_time = wpa_s->max_remain_on_chan;
if (wait_time > 2000)
wait_time = 2000;
offchannel_send_action(wpa_s, freq, src,
wpa_s->own_addr, broadcast,
wpabuf_head(msg), wpabuf_len(msg),
wait_time, wpas_dpp_tx_pkex_status, 0);
return;
}
wpa_printf(MSG_DEBUG, "DPP: Initiate PKEX for push button with "
MACSTR, MAC2STR(src));
if (!wpa_s->dpp_pb_cmd) {
wpa_printf(MSG_INFO,
"DPP: No configuration to provision as push button Configurator");
wpas_dpp_push_button_stop(wpa_s);
return;
}
wpa_s->dpp_pkex_bi = wpa_s->dpp_pb_bi;
os_memcpy(wpa_s->dpp_pb_resp_hash, r_hash, SHA256_MAC_LEN);
pkex = dpp_pkex_init(wpa_s, wpa_s->dpp_pkex_bi, wpa_s->own_addr,
"PBPKEX", (const char *) wpa_s->dpp_pb_c_nonce,
wpa_s->dpp_pb_bi->curve->nonce_len,
true);
if (!pkex) {
wpas_dpp_push_button_stop(wpa_s);
return;
}
pkex->freq = freq;
wpa_s->dpp_pkex = pkex;
msg = wpa_s->dpp_pkex->exchange_req;
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
" freq=%u type=%d", MAC2STR(src), freq,
DPP_PA_PKEX_EXCHANGE_REQ);
wait_time = wpa_s->max_remain_on_chan;
if (wait_time > 2000)
wait_time = 2000;
offchannel_send_action(wpa_s, pkex->freq, src,
wpa_s->own_addr, broadcast,
wpabuf_head(msg), wpabuf_len(msg),
wait_time, wpas_dpp_tx_pkex_status, 0);
pkex->exch_req_wait_time = 2000;
pkex->exch_req_tries = 1;
/* Use the externally provided configuration */
os_free(wpa_s->dpp_pkex_auth_cmd);
wpa_s->dpp_pkex_auth_cmd = os_strdup(wpa_s->dpp_pb_cmd);
if (!wpa_s->dpp_pkex_auth_cmd)
wpas_dpp_push_button_stop(wpa_s);
}
static void
wpas_dpp_rx_pb_presence_announcement(struct wpa_supplicant *wpa_s,
const u8 *src, const u8 *hdr,
const u8 *buf, size_t len,
unsigned int freq)
{
const u8 *r_hash;
u16 r_hash_len;
unsigned int i;
bool found = false;
struct dpp_pb_info *info, *tmp;
struct os_reltime now, age;
struct wpabuf *msg;
os_get_reltime(&now);
wpa_printf(MSG_DEBUG, "DPP: Push Button Presence Announcement from "
MACSTR, MAC2STR(src));
r_hash = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
&r_hash_len);
if (!r_hash || r_hash_len != SHA256_MAC_LEN) {
wpa_printf(MSG_DEBUG,
"DPP: Missing or invalid required Responder Bootstrapping Key Hash attribute");
return;
}
wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
r_hash, r_hash_len);
for (i = 0; i < DPP_PB_INFO_COUNT; i++) {
info = &wpa_s->dpp_pb[i];
if ((info->rx_time.sec == 0 && info->rx_time.usec == 0) ||
os_memcmp(r_hash, info->hash, SHA256_MAC_LEN) != 0)
continue;
wpa_printf(MSG_DEBUG,
"DPP: Active push button Enrollee already known");
found = true;
info->rx_time = now;
}
if (!found) {
for (i = 0; i < DPP_PB_INFO_COUNT; i++) {
tmp = &wpa_s->dpp_pb[i];
if (tmp->rx_time.sec == 0 && tmp->rx_time.usec == 0)
continue;
if (os_reltime_expired(&now, &tmp->rx_time, 120)) {
wpa_hexdump(MSG_DEBUG,
"DPP: Push button Enrollee hash expired",
tmp->hash, SHA256_MAC_LEN);
tmp->rx_time.sec = 0;
tmp->rx_time.usec = 0;
continue;
}
wpa_hexdump(MSG_DEBUG,
"DPP: Push button session overlap with hash",
tmp->hash, SHA256_MAC_LEN);
if (!wpa_s->dpp_pb_result_indicated &&
wpas_dpp_pb_active(wpa_s)) {
wpa_msg(wpa_s, MSG_INFO,
DPP_EVENT_PB_RESULT "session-overlap");
wpa_s->dpp_pb_result_indicated = true;
}
wpas_dpp_push_button_stop(wpa_s);
return;
}
/* Replace the oldest entry */
info = &wpa_s->dpp_pb[0];
for (i = 1; i < DPP_PB_INFO_COUNT; i++) {
tmp = &wpa_s->dpp_pb[i];
if (os_reltime_before(&tmp->rx_time, &info->rx_time))
info = tmp;
}
wpa_printf(MSG_DEBUG, "DPP: New active push button Enrollee");
os_memcpy(info->hash, r_hash, SHA256_MAC_LEN);
info->rx_time = now;
}
if (!wpas_dpp_pb_active(wpa_s)) {
wpa_printf(MSG_DEBUG,
"DPP: Discard message since own push button has not been pressed");
return;
}
if (wpa_s->dpp_pb_announce_time.sec == 0 &&
wpa_s->dpp_pb_announce_time.usec == 0) {
/* Start a wait before allowing PKEX to be initiated */
wpa_s->dpp_pb_announce_time = now;
}
if (!wpa_s->dpp_pb_bi) {
int res;
res = dpp_bootstrap_gen(wpa_s->dpp, "type=pkex");
if (res < 0)
return;
wpa_s->dpp_pb_bi = dpp_bootstrap_get_id(wpa_s->dpp, res);
if (!wpa_s->dpp_pb_bi)
return;
if (random_get_bytes(wpa_s->dpp_pb_c_nonce,
wpa_s->dpp_pb_bi->curve->nonce_len)) {
wpa_printf(MSG_ERROR,
"DPP: Failed to generate C-nonce");
wpas_dpp_push_button_stop(wpa_s);
return;
}
}
/* Skip the response if one was sent within last 50 ms since the
* Enrollee is going to send out at least three announcement messages.
*/
os_reltime_sub(&now, &wpa_s->dpp_pb_last_resp, &age);
if (age.sec == 0 && age.usec < 50000) {
wpa_printf(MSG_DEBUG,
"DPP: Skip Push Button Presence Announcement Response frame immediately after having sent one");
return;
}
msg = dpp_build_pb_announcement_resp(
wpa_s->dpp_pb_bi, r_hash, wpa_s->dpp_pb_c_nonce,
wpa_s->dpp_pb_bi->curve->nonce_len);
if (!msg) {
wpas_dpp_push_button_stop(wpa_s);
return;
}
wpa_printf(MSG_DEBUG,
"DPP: Send Push Button Presence Announcement Response to "
MACSTR, MAC2STR(src));
wpa_s->dpp_pb_last_resp = now;
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
MAC2STR(src), freq, DPP_PA_PB_PRESENCE_ANNOUNCEMENT_RESP);
offchannel_send_action(wpa_s, freq, src, wpa_s->own_addr, broadcast,
wpabuf_head(msg), wpabuf_len(msg),
0, NULL, 0);
wpabuf_free(msg);
if (os_reltime_expired(&now, &wpa_s->dpp_pb_announce_time, 15))
wpas_dpp_pb_pkex_init(wpa_s, freq, src, r_hash);
}
static void
wpas_dpp_rx_pb_presence_announcement_resp(struct wpa_supplicant *wpa_s,
const u8 *src, const u8 *hdr,
@ -3212,9 +3499,10 @@ wpas_dpp_rx_pb_presence_announcement_resp(struct wpa_supplicant *wpa_s,
u16 i_hash_len, r_hash_len, c_nonce_len;
bool overlap = false;
if (!wpa_s->dpp_pb_announcement || !wpa_s->dpp_pb_bi) {
if (!wpa_s->dpp_pb_announcement || !wpa_s->dpp_pb_bi ||
wpa_s->dpp_pb_configurator) {
wpa_printf(MSG_INFO,
"DPP: Not in active push button mode - discard Push Button Presence Announcement Response from "
"DPP: Not in active push button Enrollee mode - discard Push Button Presence Announcement Response from "
MACSTR, MAC2STR(src));
return;
}
@ -3672,6 +3960,10 @@ void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src,
break;
#endif /* CONFIG_DPP2 */
#ifdef CONFIG_DPP3
case DPP_PA_PB_PRESENCE_ANNOUNCEMENT:
wpas_dpp_rx_pb_presence_announcement(wpa_s, src, hdr,
buf, len, freq);
break;
case DPP_PA_PB_PRESENCE_ANNOUNCEMENT_RESP:
wpas_dpp_rx_pb_presence_announcement_resp(wpa_s, src, hdr,
buf, len, freq);
@ -3888,6 +4180,20 @@ wpas_dpp_gas_status_handler(void *ctx, struct wpabuf *resp, int ok)
dpp_auth_deinit(wpa_s->dpp_auth);
wpa_s->dpp_auth = NULL;
wpabuf_free(resp);
#ifdef CONFIG_DPP3
if (!wpa_s->dpp_pb_result_indicated && wpas_dpp_pb_active(wpa_s)) {
if (ok)
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_RESULT
"success");
else
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_RESULT
"could-not-connect");
wpa_s->dpp_pb_result_indicated = true;
if (ok)
wpas_dpp_remove_pb_hash(wpa_s);
wpas_dpp_push_button_stop(wpa_s);
}
#endif /* CONFIG_DPP3 */
}
@ -5170,7 +5476,37 @@ static void wpas_dpp_pb_next(void *eloop_ctx, void *timeout_ctx)
}
int wpas_dpp_push_button(struct wpa_supplicant *wpa_s)
static void wpas_dpp_push_button_expire(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
wpa_printf(MSG_DEBUG,
"DPP: Active push button Configurator mode expired");
wpas_dpp_push_button_stop(wpa_s);
}
static int wpas_dpp_push_button_configurator(struct wpa_supplicant *wpa_s,
const char *cmd)
{
wpa_s->dpp_pb_configurator = true;
wpa_s->dpp_pb_announce_time.sec = 0;
wpa_s->dpp_pb_announce_time.usec = 0;
str_clear_free(wpa_s->dpp_pb_cmd);
wpa_s->dpp_pb_cmd = NULL;
if (cmd) {
wpa_s->dpp_pb_cmd = os_strdup(cmd);
if (!wpa_s->dpp_pb_cmd)
return -1;
}
eloop_register_timeout(100, 0, wpas_dpp_push_button_expire,
wpa_s, NULL);
return 0;
}
int wpas_dpp_push_button(struct wpa_supplicant *wpa_s, const char *cmd)
{
int res;
@ -5182,6 +5518,13 @@ int wpas_dpp_push_button(struct wpa_supplicant *wpa_s)
os_get_reltime(&wpa_s->dpp_pb_time);
if (cmd &&
(os_strstr(cmd, " role=configurator") ||
os_strstr(cmd, " conf=")))
return wpas_dpp_push_button_configurator(wpa_s, cmd);
wpa_s->dpp_pb_configurator = false;
if (wpas_dpp_pb_channels(wpa_s) < 0)
return -1;
wpa_s->dpp_pb_freq_idx = 0;
@ -5219,16 +5562,36 @@ void wpas_dpp_push_button_stop(struct wpa_supplicant *wpa_s)
os_snprintf(id, sizeof(id), "%u", wpa_s->dpp_pb_bi->id);
dpp_bootstrap_remove(wpa_s->dpp, id);
wpa_s->dpp_pb_bi = NULL;
if (!wpa_s->dpp_pb_result_indicated)
if (!wpa_s->dpp_pb_result_indicated) {
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_RESULT "failed");
wpa_s->dpp_pb_result_indicated = true;
}
}
wpa_s->dpp_pb_resp_freq = 0;
wpa_s->dpp_pb_stop_iter = 0;
wpa_s->dpp_pb_discovery_done = false;
wpa_s->dpp_pb_result_indicated = false;
os_free(wpa_s->dpp_pb_cmd);
wpa_s->dpp_pb_cmd = NULL;
eloop_cancel_timeout(wpas_dpp_pb_next, wpa_s, NULL);
eloop_cancel_timeout(wpas_dpp_push_button_expire, wpa_s, NULL);
if (wpas_dpp_pb_active(wpa_s)) {
wpa_printf(MSG_DEBUG, "DPP: Stop active push button mode");
if (!wpa_s->dpp_pb_result_indicated)
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_RESULT "failed");
}
wpa_s->dpp_pb_time.sec = 0;
wpa_s->dpp_pb_time.usec = 0;
dpp_pkex_free(wpa_s->dpp_pkex);
wpa_s->dpp_pkex = NULL;
os_free(wpa_s->dpp_pkex_auth_cmd);
wpa_s->dpp_pkex_auth_cmd = NULL;
wpa_s->dpp_pb_result_indicated = false;
str_clear_free(wpa_s->dpp_pb_cmd);
wpa_s->dpp_pb_cmd = NULL;
}
#endif /* CONFIG_DPP3 */

View file

@ -44,7 +44,7 @@ void wpas_dpp_chirp_stop(struct wpa_supplicant *wpa_s);
int wpas_dpp_reconfig(struct wpa_supplicant *wpa_s, const char *cmd);
int wpas_dpp_ca_set(struct wpa_supplicant *wpa_s, const char *cmd);
int wpas_dpp_conf_set(struct wpa_supplicant *wpa_s, const char *cmd);
int wpas_dpp_push_button(struct wpa_supplicant *wpa_s);
int wpas_dpp_push_button(struct wpa_supplicant *wpa_s, const char *cmd);
void wpas_dpp_push_button_stop(struct wpa_supplicant *wpa_s);
#endif /* DPP_SUPPLICANT_H */

View file

@ -3165,7 +3165,7 @@ static int wpa_cli_cmd_dpp_stop_chirp(struct wpa_ctrl *ctrl, int argc,
static int wpa_cli_cmd_dpp_push_button(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "DPP_PUSH_BUTTON");
return wpa_cli_cmd(ctrl, "DPP_PUSH_BUTTON", 0, argc, argv);
}
#endif /* CONFIG_DPP3 */
#endif /* CONFIG_DPP */

View file

@ -1496,6 +1496,7 @@ struct wpa_supplicant {
#endif /* CONFIG_DPP2 */
#ifdef CONFIG_DPP3
struct os_reltime dpp_pb_time;
bool dpp_pb_configurator;
int *dpp_pb_freqs;
unsigned int dpp_pb_freq_idx;
unsigned int dpp_pb_announce_count;
@ -1508,6 +1509,11 @@ struct wpa_supplicant {
u8 dpp_pb_c_nonce[DPP_MAX_NONCE_LEN];
size_t dpp_pb_c_nonce_len;
bool dpp_pb_result_indicated;
struct os_reltime dpp_pb_announce_time;
struct dpp_pb_info dpp_pb[DPP_PB_INFO_COUNT];
u8 dpp_pb_resp_hash[SHA256_MAC_LEN];
struct os_reltime dpp_pb_last_resp;
char *dpp_pb_cmd;
#endif /* CONFIG_DPP3 */
#ifdef CONFIG_TESTING_OPTIONS
char *dpp_config_obj_override;