diff --git a/src/common/dpp.c b/src/common/dpp.c index 2ca906283..db8a68401 100644 --- a/src/common/dpp.c +++ b/src/common/dpp.c @@ -17,6 +17,7 @@ #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" #include "common/gas.h" +#include "eap_common/eap_defs.h" #include "crypto/crypto.h" #include "crypto/random.h" #include "crypto/aes.h" @@ -980,6 +981,7 @@ void dpp_configuration_free(struct dpp_configuration *conf) return; str_clear_free(conf->passphrase); os_free(conf->group_id); + os_free(conf->csrattrs); bin_clear_free(conf, sizeof(*conf)); } @@ -990,6 +992,7 @@ static int dpp_configuration_parse_helper(struct dpp_authentication *auth, const char *pos, *end; struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL; struct dpp_configuration *conf = NULL; + size_t len; pos = os_strstr(cmd, " conf=sta-"); if (pos) { @@ -1094,6 +1097,17 @@ static int dpp_configuration_parse_helper(struct dpp_authentication *auth, conf->netaccesskey_expiry = val; } + pos = os_strstr(cmd, " csrattrs="); + if (pos) { + pos += 10; + end = os_strchr(pos, ' '); + len = end ? (size_t) (end - pos) : os_strlen(pos); + conf->csrattrs = os_zalloc(len + 1); + if (!conf->csrattrs) + goto fail; + os_memcpy(conf->csrattrs, pos, len); + } + if (!dpp_configuration_valid(conf)) goto fail; @@ -1482,6 +1496,15 @@ skip_groups: tailroom += os_strlen(signed_conn); if (incl_legacy) tailroom += 1000; + if (akm == DPP_AKM_DOT1X) { + if (auth->certbag) + tailroom += 2 * wpabuf_len(auth->certbag); + if (auth->cacert) + tailroom += 2 * wpabuf_len(auth->cacert); + if (auth->trusted_eap_server_name) + tailroom += os_strlen(auth->trusted_eap_server_name); + tailroom += 1000; + } buf = dpp_build_conf_start(auth, conf, tailroom); if (!buf) goto fail; @@ -1497,6 +1520,30 @@ skip_groups: dpp_build_legacy_cred_params(buf, conf); json_value_sep(buf); } + if (akm == DPP_AKM_DOT1X) { + json_start_object(buf, "entCreds"); + if (!auth->certbag) + goto fail; + json_add_base64(buf, "certBag", wpabuf_head(auth->certbag), + wpabuf_len(auth->certbag)); + if (auth->cacert) { + json_value_sep(buf); + json_add_base64(buf, "caCert", + wpabuf_head(auth->cacert), + wpabuf_len(auth->cacert)); + } + if (auth->trusted_eap_server_name) { + json_value_sep(buf); + json_add_string(buf, "trustedEapServerName", + auth->trusted_eap_server_name); + } + json_value_sep(buf); + json_start_array(buf, "eapMethods"); + wpabuf_printf(buf, "%d", EAP_TYPE_TLS); + json_end_array(buf); + json_end_object(buf); + json_value_sep(buf); + } wpabuf_put_str(buf, "\"signedConnector\":\""); wpabuf_put_str(buf, signed_conn); wpabuf_put_str(buf, "\""); @@ -1556,7 +1603,7 @@ dpp_build_conf_obj_legacy(struct dpp_authentication *auth, static struct wpabuf * dpp_build_conf_obj(struct dpp_authentication *auth, enum dpp_netrole netrole, - int idx) + int idx, bool cert_req) { struct dpp_configuration *conf = NULL; @@ -1589,15 +1636,28 @@ dpp_build_conf_obj(struct dpp_authentication *auth, enum dpp_netrole netrole, return NULL; } + if (conf->akm == DPP_AKM_DOT1X) { + if (!auth->conf) { + wpa_printf(MSG_DEBUG, + "DPP: No Configurator data available"); + return NULL; + } + if (!cert_req && !auth->certbag) { + wpa_printf(MSG_DEBUG, + "DPP: No certificate data available for dot1x configuration"); + return NULL; + } + return dpp_build_conf_obj_dpp(auth, conf); + } if (dpp_akm_dpp(conf->akm) || (auth->peer_version >= 2 && auth->conf)) return dpp_build_conf_obj_dpp(auth, conf); return dpp_build_conf_obj_legacy(auth, conf); } -static struct wpabuf * +struct wpabuf * dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce, - u16 e_nonce_len, enum dpp_netrole netrole) + u16 e_nonce_len, enum dpp_netrole netrole, bool cert_req) { struct wpabuf *conf = NULL, *conf2 = NULL, *env_data = NULL; size_t clear_len, attr_len; @@ -1612,16 +1672,22 @@ dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce, env_data = dpp_build_enveloped_data(auth); #endif /* CONFIG_DPP2 */ } else { - conf = dpp_build_conf_obj(auth, netrole, 0); + conf = dpp_build_conf_obj(auth, netrole, 0, cert_req); if (conf) { wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON", wpabuf_head(conf), wpabuf_len(conf)); - conf2 = dpp_build_conf_obj(auth, netrole, 1); + conf2 = dpp_build_conf_obj(auth, netrole, 1, cert_req); } } - status = (conf || env_data) ? DPP_STATUS_OK : - DPP_STATUS_CONFIGURE_FAILURE; + + if (conf || env_data) + status = DPP_STATUS_OK; + else if (!cert_req && netrole == DPP_NETROLE_STA && auth->conf_sta && + auth->conf_sta->akm == DPP_AKM_DOT1X && !auth->waiting_csr) + status = DPP_STATUS_CSR_NEEDED; + else + status = DPP_STATUS_CONFIGURE_FAILURE; auth->conf_resp_status = status; /* { E-nonce, configurationObject[, sendConnStatus]}ke */ @@ -1635,6 +1701,9 @@ dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce, if (auth->peer_version >= 2 && auth->send_conn_status && netrole == DPP_NETROLE_STA) clear_len += 4; + if (status == DPP_STATUS_CSR_NEEDED && auth->conf_sta && + auth->conf_sta->csrattrs) + clear_len += 4 + os_strlen(auth->conf_sta->csrattrs); clear = wpabuf_alloc(clear_len); attr_len = 4 + 1 + 4 + clear_len + AES_BLOCK_SIZE; #ifdef CONFIG_TESTING_OPTIONS @@ -1697,12 +1766,21 @@ skip_e_nonce: } if (auth->peer_version >= 2 && auth->send_conn_status && - netrole == DPP_NETROLE_STA) { + netrole == DPP_NETROLE_STA && status == DPP_STATUS_OK) { wpa_printf(MSG_DEBUG, "DPP: sendConnStatus"); wpabuf_put_le16(clear, DPP_ATTR_SEND_CONN_STATUS); wpabuf_put_le16(clear, 0); } + if (status == DPP_STATUS_CSR_NEEDED && auth->conf_sta && + auth->conf_sta->csrattrs) { + auth->waiting_csr = true; + wpa_printf(MSG_DEBUG, "DPP: CSR Attributes Request"); + wpabuf_put_le16(clear, DPP_ATTR_CSR_ATTR_REQ); + wpabuf_put_le16(clear, os_strlen(auth->conf_sta->csrattrs)); + wpabuf_put_str(clear, auth->conf_sta->csrattrs); + } + #ifdef CONFIG_TESTING_OPTIONS skip_config_obj: if (dpp_test == DPP_TEST_NO_STATUS_CONF_RESP) { @@ -1773,6 +1851,7 @@ dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start, struct wpabuf *resp = NULL; struct json_token *root = NULL, *token; enum dpp_netrole netrole; + struct wpabuf *cert_req = NULL; #ifdef CONFIG_TESTING_OPTIONS if (dpp_test == DPP_TEST_STOP_AT_CONF_REQ) { @@ -1881,6 +1960,7 @@ dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start, dpp_auth_fail(auth, "Unsupported netRole"); goto fail; } + auth->e_netrole = netrole; token = json_get_member(root, "mudurl"); if (token && token->type == JSON_STRING) { @@ -1927,9 +2007,30 @@ dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start, txt); } - resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, netrole); +#ifdef CONFIG_DPP2 + cert_req = json_get_member_base64(root, "pkcs10"); + if (cert_req) { + char *txt; + + wpa_hexdump_buf(MSG_DEBUG, "DPP: CertificateRequest", cert_req); + txt = base64_encode_no_lf(wpabuf_head(cert_req), + wpabuf_len(cert_req), NULL); + if (!txt) + goto fail; + wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_CSR "peer=%d csr=%s", + auth->peer_bi ? (int) auth->peer_bi->id : -1, txt); + os_free(txt); + auth->waiting_csr = false; + auth->waiting_cert = true; + goto fail; + } +#endif /* CONFIG_DPP2 */ + + resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, netrole, + cert_req); fail: + wpabuf_free(cert_req); json_free(root); os_free(unwrapped); return resp; @@ -3216,7 +3317,7 @@ int dpp_configurator_own_config(struct dpp_authentication *auth, auth->peer_protocol_key = auth->own_protocol_key; dpp_copy_csign(&auth->conf_obj[0], auth->conf->csign); - conf_obj = dpp_build_conf_obj(auth, ap, 0); + conf_obj = dpp_build_conf_obj(auth, ap, 0, NULL); if (!conf_obj) { wpabuf_free(auth->conf_obj[0].c_sign_key); auth->conf_obj[0].c_sign_key = NULL; diff --git a/src/common/dpp.h b/src/common/dpp.h index c430c2856..8ca41abd2 100644 --- a/src/common/dpp.h +++ b/src/common/dpp.h @@ -222,6 +222,8 @@ struct dpp_configuration { char *passphrase; u8 psk[32]; int psk_set; + + char *csrattrs; }; struct dpp_asymmetric_key { @@ -253,6 +255,7 @@ struct dpp_authentication { u8 e_nonce[DPP_MAX_NONCE_LEN]; u8 i_capab; u8 r_capab; + enum dpp_netrole e_netrole; EVP_PKEY *own_protocol_key; EVP_PKEY *peer_protocol_key; EVP_PKEY *reconfig_old_protocol_key; @@ -319,6 +322,12 @@ struct dpp_authentication { int akm_use_selector; int configurator_set; u8 transaction_id; + bool waiting_csr; + bool waiting_cert; + char *trusted_eap_server_name; + struct wpabuf *cacert; + struct wpabuf *certbag; + void *cert_resp_ctx; #ifdef CONFIG_TESTING_OPTIONS char *config_obj_override; char *discovery_override; @@ -517,6 +526,10 @@ void dpp_configuration_free(struct dpp_configuration *conf); int dpp_set_configurator(struct dpp_authentication *auth, const char *cmd); void dpp_auth_deinit(struct dpp_authentication *auth); struct wpabuf * +dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce, + u16 e_nonce_len, enum dpp_netrole netrole, + bool cert_req); +struct wpabuf * dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start, size_t attr_len); int dpp_conf_resp_rx(struct dpp_authentication *auth, diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index 488e4addc..1fa141520 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -192,6 +192,7 @@ extern "C" { #define DPP_EVENT_CHIRP_STOPPED "DPP-CHIRP-STOPPED " #define DPP_EVENT_MUD_URL "DPP-MUD-URL " #define DPP_EVENT_BAND_SUPPORT "DPP-BAND-SUPPORT " +#define DPP_EVENT_CSR "DPP-CSR " /* MESH events */ #define MESH_GROUP_STARTED "MESH-GROUP-STARTED " diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 298f3c442..8d2ccbae5 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -11204,6 +11204,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, ssid = wpa_config_get_network(wpa_s->conf, atoi(buf + 13)); if (!ssid || wpas_dpp_reconfig(wpa_s, ssid) < 0) reply_len = -1; + } else if (os_strncmp(buf, "DPP_CA_SET ", 11) == 0) { + if (wpas_dpp_ca_set(wpa_s, buf + 10) < 0) + reply_len = -1; #endif /* CONFIG_DPP2 */ #endif /* CONFIG_DPP */ } else { diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c index 66aad2916..b0b657f5e 100644 --- a/wpa_supplicant/dpp_supplicant.c +++ b/wpa_supplicant/dpp_supplicant.c @@ -12,6 +12,7 @@ #include "utils/common.h" #include "utils/eloop.h" #include "utils/ip_addr.h" +#include "utils/base64.h" #include "common/dpp.h" #include "common/gas.h" #include "common/gas_server.h" @@ -2679,6 +2680,16 @@ wpas_dpp_gas_req_handler(void *ctx, void *resp_ctx, const u8 *sa, wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_REQ_RX "src=" MACSTR, MAC2STR(sa)); resp = dpp_conf_req_rx(auth, query, query_len); + +#ifdef CONFIG_DPP2 + if (!resp && auth->waiting_cert) { + wpa_printf(MSG_DEBUG, "DPP: Certificate not yet ready"); + auth->cert_resp_ctx = resp_ctx; + *comeback_delay = 500; + return NULL; + } +#endif /* CONFIG_DPP2 */ + if (!resp) wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_FAILED); auth->conf_resp = resp; @@ -2704,6 +2715,14 @@ wpas_dpp_gas_status_handler(void *ctx, struct wpabuf *resp, int ok) return; } +#ifdef CONFIG_DPP2 + if (auth->waiting_csr && ok) { + wpa_printf(MSG_DEBUG, "DPP: Waiting for CSR"); + wpabuf_free(resp); + return; + } +#endif /* CONFIG_DPP2 */ + wpa_printf(MSG_DEBUG, "DPP: Configuration exchange completed (ok=%d)", ok); eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL); @@ -3449,4 +3468,84 @@ int wpas_dpp_reconfig(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) return eloop_register_timeout(0, 0, wpas_dpp_chirp_next, wpa_s, NULL); } + +int wpas_dpp_ca_set(struct wpa_supplicant *wpa_s, const char *cmd) +{ + int peer; + const char *pos, *value; + struct dpp_authentication *auth = wpa_s->dpp_auth; + u8 *bin; + size_t bin_len; + struct wpabuf *buf; + + if (!auth || !auth->waiting_cert) { + wpa_printf(MSG_DEBUG, + "DPP: No authentication exchange waiting for certificate information"); + return -1; + } + + pos = os_strstr(cmd, " peer="); + if (pos) { + peer = atoi(pos + 6); + if (!auth->peer_bi || + (unsigned int) peer != auth->peer_bi->id) { + wpa_printf(MSG_DEBUG, "DPP: Peer mismatch"); + return -1; + } + } + + pos = os_strstr(cmd, " value="); + if (!pos) + return -1; + value = pos + 7; + + pos = os_strstr(cmd, " name="); + if (!pos) + return -1; + pos += 6; + + if (os_strncmp(pos, "trustedEapServerName ", 21) == 0) { + os_free(auth->trusted_eap_server_name); + auth->trusted_eap_server_name = os_strdup(value); + return auth->trusted_eap_server_name ? 0 : -1; + } + + bin = base64_decode(value, os_strlen(value), &bin_len); + if (!bin) + return -1; + buf = wpabuf_alloc_copy(bin, bin_len); + os_free(bin); + + if (os_strncmp(pos, "caCert ", 7) == 0) { + wpabuf_free(auth->cacert); + auth->cacert = buf; + return 0; + } + + if (os_strncmp(pos, "certBag ", 8) == 0) { + struct wpabuf *resp; + + wpabuf_free(auth->certbag); + auth->certbag = buf; + + resp = dpp_build_conf_resp(auth, auth->e_nonce, + auth->curve->nonce_len, + auth->e_netrole, true); + if (!resp) + return -1; + if (gas_server_set_resp(wpa_s->gas_server, auth->cert_resp_ctx, + resp) < 0) { + wpa_printf(MSG_DEBUG, + "DPP: Could not find pending GAS response"); + wpabuf_free(resp); + return -1; + } + auth->conf_resp = resp; + return 0; + } + + wpabuf_free(buf); + return -1; +} + #endif /* CONFIG_DPP2 */ diff --git a/wpa_supplicant/dpp_supplicant.h b/wpa_supplicant/dpp_supplicant.h index 2dc86e09e..081615b95 100644 --- a/wpa_supplicant/dpp_supplicant.h +++ b/wpa_supplicant/dpp_supplicant.h @@ -40,5 +40,6 @@ void wpas_dpp_send_conn_status_result(struct wpa_supplicant *wpa_s, int wpas_dpp_chirp(struct wpa_supplicant *wpa_s, const char *cmd); void wpas_dpp_chirp_stop(struct wpa_supplicant *wpa_s); int wpas_dpp_reconfig(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); +int wpas_dpp_ca_set(struct wpa_supplicant *wpa_s, const char *cmd); #endif /* DPP_SUPPLICANT_H */