]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
DPP2: Enterprise provisioning (Configurator)
authorJouni Malinen <jouni@codeaurora.org>
Mon, 15 Jun 2020 17:20:50 +0000 (20:20 +0300)
committerJouni Malinen <j@w1.fi>
Tue, 16 Jun 2020 15:24:23 +0000 (18:24 +0300)
Add Configurator functionality for provisioning enterprise (EAP-TLS)
configuration object.

Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
src/common/dpp.c
src/common/dpp.h
src/common/wpa_ctrl.h
wpa_supplicant/ctrl_iface.c
wpa_supplicant/dpp_supplicant.c
wpa_supplicant/dpp_supplicant.h

index 2ca9062834e4f109629e2bc2ad3f6b890dfebcb5..db8a68401bff38cc13e19a2c83bb5d06e96ae119 100644 (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;
index c430c28562075eb26c02494dda554f8365656059..8ca41abd2134c0f3b898c95ae988710a9a816bf1 100644 (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,
index 488e4addc6306ee07b56ec265113522ae850b97a..1fa141520f846f3d51437188f0ba2b78179f9eb0 100644 (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 "
index 298f3c44288fb82323e622549c5a1269ede9732d..8d2ccbae536612e9ccc94bf5f78c988918226948 100644 (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 {
index 66aad2916146a53263f6cd996e6349b8a3c93d99..b0b657f5e6474b3d7fa247611cbfdae64bcfe758 100644 (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 */
index 2dc86e09e0ec25d18aef731c779c59c7dc2e6545..081615b950eb60f67b1646e4017af3b0bed8461a 100644 (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 */