DPP2: Enterprise provisioning (Configurator)

Add Configurator functionality for provisioning enterprise (EAP-TLS)
configuration object.

Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
This commit is contained in:
Jouni Malinen 2020-06-15 20:20:50 +03:00 committed by Jouni Malinen
parent 4643b2feec
commit 6568e5d203
6 changed files with 228 additions and 10 deletions

View File

@ -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;

View File

@ -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,

View File

@ -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 "

View File

@ -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 {

View File

@ -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 */

View File

@ -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 */