diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index 33d4cb784..c11cebc4b 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -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 { diff --git a/src/common/dpp.h b/src/common/dpp.h index 08febd94f..b2129fccf 100644 --- a/src/common/dpp.h +++ b/src/common/dpp.h @@ -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, diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 1c89e7d12..7cccc1dd7 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -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 */ diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c index 64eb5ea63..7901f0115 100644 --- a/wpa_supplicant/dpp_supplicant.c +++ b/wpa_supplicant/dpp_supplicant.c @@ -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 */ diff --git a/wpa_supplicant/dpp_supplicant.h b/wpa_supplicant/dpp_supplicant.h index f9ccc6028..e2bdd9d40 100644 --- a/wpa_supplicant/dpp_supplicant.h +++ b/wpa_supplicant/dpp_supplicant.h @@ -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 */ diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index ff39b5d12..aa0f70589 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -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 */ diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index e98a34b40..5f9bbe32d 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -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;