static void hostapd_dpp_handle_config_obj(struct hostapd_data *hapd,
- struct dpp_authentication *auth)
+ struct dpp_authentication *auth,
+ struct dpp_config_obj *conf)
{
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_RECEIVED);
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_AKM "%s",
- dpp_akm_str(auth->akm));
- if (auth->ssid_len)
+ dpp_akm_str(conf->akm));
+ if (conf->ssid_len)
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_SSID "%s",
- wpa_ssid_txt(auth->ssid, auth->ssid_len));
- if (auth->connector) {
+ wpa_ssid_txt(conf->ssid, conf->ssid_len));
+ if (conf->connector) {
/* TODO: Save the Connector and consider using a command
* to fetch the value instead of sending an event with
* it. The Connector could end up being larger than what
* most clients are ready to receive as an event
* message. */
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONNECTOR "%s",
- auth->connector);
- } else if (auth->passphrase[0]) {
+ conf->connector);
+ } else if (conf->passphrase[0]) {
char hex[64 * 2 + 1];
wpa_snprintf_hex(hex, sizeof(hex),
- (const u8 *) auth->passphrase,
- os_strlen(auth->passphrase));
+ (const u8 *) conf->passphrase,
+ os_strlen(conf->passphrase));
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_PASS "%s",
hex);
- } else if (auth->psk_set) {
+ } else if (conf->psk_set) {
char hex[PMK_LEN * 2 + 1];
- wpa_snprintf_hex(hex, sizeof(hex), auth->psk, PMK_LEN);
+ wpa_snprintf_hex(hex, sizeof(hex), conf->psk, PMK_LEN);
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_PSK "%s",
hex);
}
- if (auth->c_sign_key) {
+ if (conf->c_sign_key) {
char *hex;
size_t hexlen;
- hexlen = 2 * wpabuf_len(auth->c_sign_key) + 1;
+ hexlen = 2 * wpabuf_len(conf->c_sign_key) + 1;
hex = os_malloc(hexlen);
if (hex) {
wpa_snprintf_hex(hex, hexlen,
- wpabuf_head(auth->c_sign_key),
- wpabuf_len(auth->c_sign_key));
+ wpabuf_head(conf->c_sign_key),
+ wpabuf_len(conf->c_sign_key));
wpa_msg(hapd->msg_ctx, MSG_INFO,
DPP_EVENT_C_SIGN_KEY "%s", hex);
os_free(hex);
goto fail;
}
- hostapd_dpp_handle_config_obj(hapd, auth);
+ hostapd_dpp_handle_config_obj(hapd, auth, &auth->conf_obj[0]);
status = DPP_STATUS_OK;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_REJECT_CONFIG) {
if (dpp_set_configurator(hapd->iface->interfaces->dpp, hapd->msg_ctx,
auth, cmd) == 0 &&
dpp_configurator_own_config(auth, curve, 1) == 0) {
- hostapd_dpp_handle_config_obj(hapd, auth);
+ hostapd_dpp_handle_config_obj(hapd, auth, &auth->conf_obj[0]);
ret = 0;
}
}
+static const u8 * dpp_get_attr_next(const u8 *prev, const u8 *buf, size_t len,
+ u16 req_id, u16 *ret_len)
+{
+ u16 id, alen;
+ const u8 *pos, *end = buf + len;
+
+ if (!prev)
+ pos = buf;
+ else
+ pos = prev + WPA_GET_LE16(prev - 2);
+ while (end - pos >= 4) {
+ id = WPA_GET_LE16(pos);
+ pos += 2;
+ alen = WPA_GET_LE16(pos);
+ pos += 2;
+ if (alen > end - pos)
+ return NULL;
+ if (id == req_id) {
+ *ret_len = alen;
+ return pos;
+ }
+ pos += alen;
+ }
+
+ return NULL;
+}
+
+
int dpp_check_attrs(const u8 *buf, size_t len)
{
const u8 *pos, *end;
void dpp_auth_deinit(struct dpp_authentication *auth)
{
+ unsigned int i;
+
if (!auth)
return;
dpp_configuration_free(auth->conf_ap);
wpabuf_free(auth->req_msg);
wpabuf_free(auth->resp_msg);
wpabuf_free(auth->conf_req);
- os_free(auth->connector);
+ for (i = 0; i < auth->num_conf_obj; i++) {
+ struct dpp_config_obj *conf = &auth->conf_obj[i];
+
+ os_free(conf->connector);
+ wpabuf_free(conf->c_sign_key);
+ }
wpabuf_free(auth->net_access_key);
- wpabuf_free(auth->c_sign_key);
dpp_bootstrap_info_free(auth->tmp_own_bi);
#ifdef CONFIG_TESTING_OPTIONS
os_free(auth->config_obj_override);
}
-static int dpp_parse_cred_legacy(struct dpp_authentication *auth,
+static int dpp_parse_cred_legacy(struct dpp_config_obj *conf,
struct json_token *cred)
{
struct json_token *pass, *psk_hex;
pass->string, len);
if (len < 8 || len > 63)
return -1;
- os_strlcpy(auth->passphrase, pass->string,
- sizeof(auth->passphrase));
+ os_strlcpy(conf->passphrase, pass->string,
+ sizeof(conf->passphrase));
} else if (psk_hex && psk_hex->type == JSON_STRING) {
- if (dpp_akm_sae(auth->akm) && !dpp_akm_psk(auth->akm)) {
+ if (dpp_akm_sae(conf->akm) && !dpp_akm_psk(conf->akm)) {
wpa_printf(MSG_DEBUG,
"DPP: Unexpected psk_hex with akm=sae");
return -1;
}
if (os_strlen(psk_hex->string) != PMK_LEN * 2 ||
- hexstr2bin(psk_hex->string, auth->psk, PMK_LEN) < 0) {
+ hexstr2bin(psk_hex->string, conf->psk, PMK_LEN) < 0) {
wpa_printf(MSG_DEBUG, "DPP: Invalid psk_hex encoding");
return -1;
}
wpa_hexdump_key(MSG_DEBUG, "DPP: Legacy PSK",
- auth->psk, PMK_LEN);
- auth->psk_set = 1;
+ conf->psk, PMK_LEN);
+ conf->psk_set = 1;
} else {
wpa_printf(MSG_DEBUG, "DPP: No pass or psk_hex strings found");
return -1;
}
- if (dpp_akm_sae(auth->akm) && !auth->passphrase[0]) {
+ if (dpp_akm_sae(conf->akm) && !conf->passphrase[0]) {
wpa_printf(MSG_DEBUG, "DPP: No pass for sae found");
return -1;
}
static int dpp_parse_connector(struct dpp_authentication *auth,
+ struct dpp_config_obj *conf,
const unsigned char *payload,
u16 payload_len)
{
}
-static void dpp_copy_csign(struct dpp_authentication *auth, EVP_PKEY *csign)
+static void dpp_copy_csign(struct dpp_config_obj *conf, EVP_PKEY *csign)
{
unsigned char *der = NULL;
int der_len;
der_len = i2d_PUBKEY(csign, &der);
if (der_len <= 0)
return;
- wpabuf_free(auth->c_sign_key);
- auth->c_sign_key = wpabuf_alloc_copy(der, der_len);
+ wpabuf_free(conf->c_sign_key);
+ conf->c_sign_key = wpabuf_alloc_copy(der, der_len);
OPENSSL_free(der);
}
-static void dpp_copy_netaccesskey(struct dpp_authentication *auth)
+static void dpp_copy_netaccesskey(struct dpp_authentication *auth,
+ struct dpp_config_obj *conf)
{
unsigned char *der = NULL;
int der_len;
static int dpp_parse_cred_dpp(struct dpp_authentication *auth,
+ struct dpp_config_obj *conf,
struct json_token *cred)
{
struct dpp_signed_connector_info info;
os_memset(&info, 0, sizeof(info));
- if (dpp_akm_psk(auth->akm) || dpp_akm_sae(auth->akm)) {
+ if (dpp_akm_psk(conf->akm) || dpp_akm_sae(conf->akm)) {
wpa_printf(MSG_DEBUG,
"DPP: Legacy credential included in Connector credential");
- if (dpp_parse_cred_legacy(auth, cred) < 0)
+ if (dpp_parse_cred_legacy(conf, cred) < 0)
return -1;
}
signed_connector) != DPP_STATUS_OK)
goto fail;
- if (dpp_parse_connector(auth, info.payload, info.payload_len) < 0) {
+ if (dpp_parse_connector(auth, conf,
+ info.payload, info.payload_len) < 0) {
wpa_printf(MSG_DEBUG, "DPP: Failed to parse connector");
goto fail;
}
- os_free(auth->connector);
- auth->connector = os_strdup(signed_connector);
+ os_free(conf->connector);
+ conf->connector = os_strdup(signed_connector);
- dpp_copy_csign(auth, csign_pub);
- dpp_copy_netaccesskey(auth);
+ dpp_copy_csign(conf, csign_pub);
+ dpp_copy_netaccesskey(auth, conf);
ret = 0;
fail:
{
int ret = -1;
struct json_token *root, *token, *discovery, *cred;
+ struct dpp_config_obj *conf;
root = json_parse((const char *) conf_obj, conf_obj_len);
if (!root)
dpp_auth_fail(auth, "Too long discovery::ssid string value");
goto fail;
}
- auth->ssid_len = os_strlen(token->string);
- os_memcpy(auth->ssid, token->string, auth->ssid_len);
+
+ if (auth->num_conf_obj == DPP_MAX_CONF_OBJ) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No room for this many Config Objects - ignore this one");
+ json_free(root);
+ return 0;
+ }
+ conf = &auth->conf_obj[auth->num_conf_obj++];
+
+ conf->ssid_len = os_strlen(token->string);
+ os_memcpy(conf->ssid, token->string, conf->ssid_len);
cred = json_get_member(root, "cred");
if (!cred || cred->type != JSON_OBJECT) {
dpp_auth_fail(auth, "No cred::akm string value found");
goto fail;
}
- auth->akm = dpp_akm_from_str(token->string);
+ conf->akm = dpp_akm_from_str(token->string);
- if (dpp_akm_legacy(auth->akm)) {
- if (dpp_parse_cred_legacy(auth, cred) < 0)
+ if (dpp_akm_legacy(conf->akm)) {
+ if (dpp_parse_cred_legacy(conf, cred) < 0)
goto fail;
- } else if (dpp_akm_dpp(auth->akm)) {
- if (dpp_parse_cred_dpp(auth, cred) < 0)
+ } else if (dpp_akm_dpp(conf->akm)) {
+ if (dpp_parse_cred_dpp(auth, conf, cred) < 0)
goto fail;
} else {
wpa_printf(MSG_DEBUG, "DPP: Unsupported akm: %s",
goto fail;
}
- conf_obj = dpp_get_attr(unwrapped, unwrapped_len,
- DPP_ATTR_CONFIG_OBJ, &conf_obj_len);
+ conf_obj = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_CONFIG_OBJ,
+ &conf_obj_len);
if (!conf_obj) {
dpp_auth_fail(auth,
"Missing required Configuration Object attribute");
goto fail;
}
- wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON",
- conf_obj, conf_obj_len);
- if (dpp_parse_conf_obj(auth, conf_obj, conf_obj_len) < 0)
- goto fail;
+ while (conf_obj) {
+ wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON",
+ conf_obj, conf_obj_len);
+ if (dpp_parse_conf_obj(auth, conf_obj, conf_obj_len) < 0)
+ goto fail;
+ conf_obj = dpp_get_attr_next(conf_obj, unwrapped, unwrapped_len,
+ DPP_ATTR_CONFIG_OBJ,
+ &conf_obj_len);
+ }
#ifdef CONFIG_DPP2
status = dpp_get_attr(unwrapped, unwrapped_len,
auth->own_protocol_key = dpp_gen_keypair(auth->curve);
if (!auth->own_protocol_key)
return -1;
- dpp_copy_netaccesskey(auth);
+ dpp_copy_netaccesskey(auth, &auth->conf_obj[0]);
auth->peer_protocol_key = auth->own_protocol_key;
- dpp_copy_csign(auth, auth->conf->csign);
+ dpp_copy_csign(&auth->conf_obj[0], auth->conf->csign);
conf_obj = dpp_build_conf_obj(auth, ap, 0);
if (!conf_obj)
int psk_set;
};
+#define DPP_MAX_CONF_OBJ 10
+
struct dpp_authentication {
void *msg_ctx;
u8 peer_version;
struct dpp_configuration *conf_sta;
struct dpp_configuration *conf2_sta;
struct dpp_configurator *conf;
- char *connector; /* received signedConnector */
- u8 ssid[SSID_MAX_LEN];
- u8 ssid_len;
- char passphrase[64];
- u8 psk[PMK_LEN];
- int psk_set;
- enum dpp_akm akm;
+ struct dpp_config_obj {
+ char *connector; /* received signedConnector */
+ u8 ssid[SSID_MAX_LEN];
+ u8 ssid_len;
+ char passphrase[64];
+ u8 psk[PMK_LEN];
+ int psk_set;
+ enum dpp_akm akm;
+ struct wpabuf *c_sign_key;
+ } conf_obj[DPP_MAX_CONF_OBJ];
+ unsigned int num_conf_obj;
struct wpabuf *net_access_key;
os_time_t net_access_key_expiry;
- struct wpabuf *c_sign_key;
int send_conn_status;
int conn_status_requested;
#ifdef CONFIG_TESTING_OPTIONS
static struct wpa_ssid * wpas_dpp_add_network(struct wpa_supplicant *wpa_s,
- struct dpp_authentication *auth)
+ struct dpp_authentication *auth,
+ struct dpp_config_obj *conf)
{
struct wpa_ssid *ssid;
#ifdef CONFIG_DPP2
- if (auth->akm == DPP_AKM_SAE) {
+ if (conf->akm == DPP_AKM_SAE) {
#ifdef CONFIG_SAE
struct wpa_driver_capa capa;
int res;
wpa_config_set_network_defaults(ssid);
ssid->disabled = 1;
- ssid->ssid = os_malloc(auth->ssid_len);
+ ssid->ssid = os_malloc(conf->ssid_len);
if (!ssid->ssid)
goto fail;
- os_memcpy(ssid->ssid, auth->ssid, auth->ssid_len);
- ssid->ssid_len = auth->ssid_len;
+ os_memcpy(ssid->ssid, conf->ssid, conf->ssid_len);
+ ssid->ssid_len = conf->ssid_len;
- if (auth->connector) {
+ if (conf->connector) {
ssid->key_mgmt = WPA_KEY_MGMT_DPP;
ssid->ieee80211w = MGMT_FRAME_PROTECTION_REQUIRED;
- ssid->dpp_connector = os_strdup(auth->connector);
+ ssid->dpp_connector = os_strdup(conf->connector);
if (!ssid->dpp_connector)
goto fail;
}
- if (auth->c_sign_key) {
- ssid->dpp_csign = os_malloc(wpabuf_len(auth->c_sign_key));
+ if (conf->c_sign_key) {
+ ssid->dpp_csign = os_malloc(wpabuf_len(conf->c_sign_key));
if (!ssid->dpp_csign)
goto fail;
- os_memcpy(ssid->dpp_csign, wpabuf_head(auth->c_sign_key),
- wpabuf_len(auth->c_sign_key));
- ssid->dpp_csign_len = wpabuf_len(auth->c_sign_key);
+ os_memcpy(ssid->dpp_csign, wpabuf_head(conf->c_sign_key),
+ wpabuf_len(conf->c_sign_key));
+ ssid->dpp_csign_len = wpabuf_len(conf->c_sign_key);
}
if (auth->net_access_key) {
ssid->dpp_netaccesskey_expiry = auth->net_access_key_expiry;
}
- if (!auth->connector || dpp_akm_psk(auth->akm) ||
- dpp_akm_sae(auth->akm)) {
- if (!auth->connector)
+ if (!conf->connector || dpp_akm_psk(conf->akm) ||
+ dpp_akm_sae(conf->akm)) {
+ if (!conf->connector)
ssid->key_mgmt = 0;
- if (dpp_akm_psk(auth->akm))
+ if (dpp_akm_psk(conf->akm))
ssid->key_mgmt |= WPA_KEY_MGMT_PSK |
WPA_KEY_MGMT_PSK_SHA256 | WPA_KEY_MGMT_FT_PSK;
- if (dpp_akm_sae(auth->akm))
+ if (dpp_akm_sae(conf->akm))
ssid->key_mgmt |= WPA_KEY_MGMT_SAE |
WPA_KEY_MGMT_FT_SAE;
ssid->ieee80211w = MGMT_FRAME_PROTECTION_OPTIONAL;
- if (auth->passphrase[0]) {
+ if (conf->passphrase[0]) {
if (wpa_config_set_quoted(ssid, "psk",
- auth->passphrase) < 0)
+ conf->passphrase) < 0)
goto fail;
wpa_config_update_psk(ssid);
ssid->export_keys = 1;
} else {
- ssid->psk_set = auth->psk_set;
- os_memcpy(ssid->psk, auth->psk, PMK_LEN);
+ ssid->psk_set = conf->psk_set;
+ os_memcpy(ssid->psk, conf->psk, PMK_LEN);
}
}
- os_memcpy(wpa_s->dpp_last_ssid, auth->ssid, auth->ssid_len);
- wpa_s->dpp_last_ssid_len = auth->ssid_len;
+ os_memcpy(wpa_s->dpp_last_ssid, conf->ssid, conf->ssid_len);
+ wpa_s->dpp_last_ssid_len = conf->ssid_len;
return ssid;
fail:
static int wpas_dpp_process_config(struct wpa_supplicant *wpa_s,
- struct dpp_authentication *auth)
+ struct dpp_authentication *auth,
+ struct dpp_config_obj *conf)
{
struct wpa_ssid *ssid;
if (wpa_s->conf->dpp_config_processing < 1)
return 0;
- ssid = wpas_dpp_add_network(wpa_s, auth);
+ ssid = wpas_dpp_add_network(wpa_s, auth, conf);
if (!ssid)
return -1;
wpa_printf(MSG_DEBUG, "DPP: Failed to update configuration");
#endif /* CONFIG_NO_CONFIG_WRITE */
+ return 0;
+}
+
+
+static void wpas_dpp_post_process_config(struct wpa_supplicant *wpa_s,
+ struct dpp_authentication *auth)
+{
if (wpa_s->conf->dpp_config_processing < 2)
- return 0;
+ return;
#ifdef CONFIG_DPP2
if (auth->peer_version >= 2) {
wpa_printf(MSG_DEBUG,
"DPP: Postpone connection attempt to wait for completion of DPP Configuration Result");
auth->connect_on_tx_status = 1;
- return 0;
+ return;
}
#endif /* CONFIG_DPP2 */
wpas_dpp_try_to_connect(wpa_s);
- return 0;
}
static int wpas_dpp_handle_config_obj(struct wpa_supplicant *wpa_s,
- struct dpp_authentication *auth)
+ struct dpp_authentication *auth,
+ struct dpp_config_obj *conf)
{
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_RECEIVED);
- if (auth->ssid_len)
+ if (conf->ssid_len)
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONFOBJ_SSID "%s",
- wpa_ssid_txt(auth->ssid, auth->ssid_len));
- if (auth->connector) {
+ wpa_ssid_txt(conf->ssid, conf->ssid_len));
+ if (conf->connector) {
/* TODO: Save the Connector and consider using a command
* to fetch the value instead of sending an event with
* it. The Connector could end up being larger than what
* most clients are ready to receive as an event
* message. */
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONNECTOR "%s",
- auth->connector);
+ conf->connector);
}
- if (auth->c_sign_key) {
+ if (conf->c_sign_key) {
char *hex;
size_t hexlen;
- hexlen = 2 * wpabuf_len(auth->c_sign_key) + 1;
+ hexlen = 2 * wpabuf_len(conf->c_sign_key) + 1;
hex = os_malloc(hexlen);
if (hex) {
wpa_snprintf_hex(hex, hexlen,
- wpabuf_head(auth->c_sign_key),
- wpabuf_len(auth->c_sign_key));
+ wpabuf_head(conf->c_sign_key),
+ wpabuf_len(conf->c_sign_key));
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_C_SIGN_KEY "%s",
hex);
os_free(hex);
}
}
- return wpas_dpp_process_config(wpa_s, auth);
+ return wpas_dpp_process_config(wpa_s, auth, conf);
}
struct dpp_authentication *auth = wpa_s->dpp_auth;
int res;
enum dpp_status_error status = DPP_STATUS_CONFIG_REJECTED;
+ unsigned int i;
wpa_s->dpp_gas_dialog_token = -1;
goto fail;
}
- res = wpas_dpp_handle_config_obj(wpa_s, auth);
- if (res < 0)
- goto fail;
+ for (i = 0; i < auth->num_conf_obj; i++) {
+ res = wpas_dpp_handle_config_obj(wpa_s, auth,
+ &auth->conf_obj[i]);
+ if (res < 0)
+ goto fail;
+ }
+ if (auth->num_conf_obj)
+ wpas_dpp_post_process_config(wpa_s, auth);
status = DPP_STATUS_OK;
#ifdef CONFIG_TESTING_OPTIONS
struct dpp_authentication *auth)
{
struct wpa_supplicant *wpa_s = ctx;
+ unsigned int i;
+ int res = -1;
+
+ for (i = 0; i < auth->num_conf_obj; i++) {
+ res = wpas_dpp_handle_config_obj(wpa_s, auth,
+ &auth->conf_obj[i]);
+ if (res)
+ break;
+ }
+ if (!res)
+ wpas_dpp_post_process_config(wpa_s, auth);
- return wpas_dpp_handle_config_obj(wpa_s, auth);
+ return res;
}
#endif /* CONFIG_DPP2 */
wpas_dpp_set_testing_options(wpa_s, auth);
if (dpp_set_configurator(wpa_s->dpp, wpa_s, auth, cmd) == 0 &&
dpp_configurator_own_config(auth, curve, 0) == 0)
- ret = wpas_dpp_handle_config_obj(wpa_s, auth);
+ ret = wpas_dpp_handle_config_obj(wpa_s, auth,
+ &auth->conf_obj[0]);
+ if (!ret)
+ wpas_dpp_post_process_config(wpa_s, auth);
dpp_auth_deinit(auth);
os_free(curve);