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:
parent
4643b2feec
commit
6568e5d203
6 changed files with 228 additions and 10 deletions
121
src/common/dpp.c
121
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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 "
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in a new issue