id = auth->get(auth, AUTH_RULE_EAP_IDENTITY);
if (id)
{
- if (id->get_type(id) == ID_ANY)
+ this->method = load_method(this, EAP_IDENTITY, 0, EAP_SERVER);
+ if (this->method)
{
- this->method = load_method(this, EAP_IDENTITY, 0, EAP_SERVER);
- if (this->method)
+ if (this->method->initiate(this->method, &out) == NEED_MORE)
{
- if (this->method->initiate(this->method, &out) == NEED_MORE)
- {
- DBG1(DBG_IKE, "initiating %N method (id 0x%02X)",
- eap_type_names, EAP_IDENTITY,
- this->method->get_identifier(this->method));
- return out;
- }
- this->method->destroy(this->method);
+ DBG1(DBG_IKE, "initiating %N method (id 0x%02X)",
+ eap_type_names, EAP_IDENTITY,
+ this->method->get_identifier(this->method));
+ return out;
}
- DBG1(DBG_IKE, "EAP-Identity request configured, "
- "but not supported");
- }
- else
- {
- DBG1(DBG_IKE, "using configured EAP-Identity %Y", id);
- this->eap_identity = id->clone(id);
+ this->method->destroy(this->method);
}
+ DBG1(DBG_IKE, "EAP-Identity request configured, "
+ "but not supported");
+ return eap_payload_create_code(EAP_FAILURE, 0);
}
}
/* invoke real EAP method */
}
/**
- * Replace the existing EAP-Identity in other auth config
+ * Replaces the existing EAP-Identity in other auth config and checks if it
+ * matches the configured identity.
*/
-static void replace_eap_identity(private_eap_authenticator_t *this)
+static bool apply_eap_identity(private_eap_authenticator_t *this,
+ identification_t *eap_identity)
{
- identification_t *eap_identity;
+ identification_t *configured;
auth_cfg_t *cfg;
+ bool match;
+
+ DBG1(DBG_IKE, "received EAP identity '%Y'", eap_identity);
+ this->eap_identity = eap_identity;
- eap_identity = this->eap_identity->clone(this->eap_identity);
cfg = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE);
- cfg->add(cfg, AUTH_RULE_EAP_IDENTITY, eap_identity);
+ configured = cfg->get(cfg, AUTH_RULE_EAP_IDENTITY);
+ match = eap_identity->matches(eap_identity, configured) != ID_MATCH_NONE;
+ cfg->add(cfg, AUTH_RULE_EAP_IDENTITY, eap_identity->clone(eap_identity));
+ return match;
}
/**
- * Handle EAP exchange as server
+ * Handle EAP exchange as server. Returns an EAP payload, or NULL if the EAP
+ * Identity doesn't match.
*/
static eap_payload_t* server_process_eap(private_eap_authenticator_t *this,
eap_payload_t *in)
{
chunk_t data;
- if (this->method->get_msk(this->method, &data) == SUCCESS)
+ if (this->method->get_msk(this->method, &data) != SUCCESS)
+ {
+ DBG1(DBG_IKE, "client did not send an EAP-Identity, "
+ "sending %N", eap_code_names, EAP_FAILURE);
+ return eap_payload_create_code(EAP_FAILURE,
+ in->get_identifier(in));
+ }
+ /* apply the received EAP identity and match it against config,
+ * return NULL if it doesn't match to possibly switch to a
+ * different config */
+ if (!apply_eap_identity(this,
+ identification_create_from_data(data)))
{
- this->eap_identity = identification_create_from_data(data);
- DBG1(DBG_IKE, "received EAP identity '%Y'",
- this->eap_identity);
- replace_eap_identity(this);
+ this->method->destroy(this->method);
+ this->method = NULL;
+ return NULL;
}
- /* restart EAP exchange, but with real method */
+ /* ID matched, restart EAP exchange, but with real method */
this->method->destroy(this->method);
return server_initiate_eap(this, FALSE);
}
return FAILED;
}
this->eap_payload = server_process_eap(this, eap_payload);
+ if (!this->eap_payload)
+ {
+ /* try to switch to a different config in case the EAP identity
+ * doesn't match */
+ return INVALID_ARG;
+ }
}
return NEED_MORE;
}
return this->peer_cfg != NULL;
}
+/**
+ * Check if the given auth config is for EAP.
+ */
+static bool is_eap_cfg(auth_cfg_t *cfg)
+{
+ return cfg &&
+ ((uintptr_t)cfg->get(cfg, AUTH_RULE_EAP_TYPE) != EAP_NAK ||
+ (uintptr_t)cfg->get(cfg, AUTH_RULE_EAP_VENDOR) != 0);
+}
+
+/**
+ * Switch configurations until we find an alternative with EAP authentication.
+ */
+static bool find_eap_cfg(private_ike_auth_t *this, auth_cfg_t **cand)
+{
+ do
+ {
+ if (!is_eap_cfg(*cand))
+ {
+ DBG1(DBG_IKE, "peer requested EAP, config unacceptable");
+ }
+ this->peer_cfg->destroy(this->peer_cfg);
+ this->peer_cfg = NULL;
+ if (!update_cfg_candidates(this, FALSE))
+ {
+ return FALSE;
+ }
+ *cand = get_auth_cfg(this, FALSE);
+ }
+ while (!is_eap_cfg(*cand));
+
+ return TRUE;
+}
+
+/**
+ * Copy EAP-specific rules to another auth config. Copying the EAP-Identity is
+ * optional.
+ */
+static void copy_eap_cfg(auth_cfg_t *src, auth_cfg_t *dst, bool copy_eap_id)
+{
+ identification_t *id;
+
+ /* copy over the EAP specific rules for authentication */
+ dst->add(dst, AUTH_RULE_EAP_TYPE,
+ src->get(src, AUTH_RULE_EAP_TYPE));
+ dst->add(dst, AUTH_RULE_EAP_VENDOR,
+ src->get(src, AUTH_RULE_EAP_VENDOR));
+ id = (identification_t*)src->get(src, AUTH_RULE_AAA_IDENTITY);
+ if (id)
+ {
+ dst->add(dst, AUTH_RULE_AAA_IDENTITY, id->clone(id));
+ }
+ if (copy_eap_id)
+ {
+ id = (identification_t*)src->get(src, AUTH_RULE_EAP_IDENTITY);
+ if (id)
+ {
+ dst->add(dst, AUTH_RULE_EAP_IDENTITY, id->clone(id));
+ }
+ }
+}
+
+/**
+ * Find an alternative EAP config that matches the EAP-Identity we received.
+ */
+static bool find_alternative_eap_cfg(private_ike_auth_t *this)
+{
+ auth_cfg_t *cfg, *cand;
+ identification_t *eap_id, *id;
+
+ /* clear current auth round, but copy IKE and EAP identities we received
+ * from the peer */
+ cfg = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE);
+ eap_id = cfg->get(cfg, AUTH_RULE_EAP_IDENTITY);
+ eap_id = eap_id->clone(eap_id);
+ id = cfg->get(cfg, AUTH_RULE_IDENTITY);
+ id = id->clone(id);
+
+ cfg->purge(cfg, FALSE);
+ cfg->add(cfg, AUTH_RULE_EAP_IDENTITY, eap_id);
+ cfg->add(cfg, AUTH_RULE_IDENTITY, id);
+
+ cand = get_auth_cfg(this, FALSE);
+ while (TRUE)
+ {
+ /* the log messages here are similar to those in auth_cfg_t */
+ id = cand->get(cand, AUTH_RULE_EAP_IDENTITY);
+ if (!id)
+ {
+ DBG1(DBG_CFG, "constraint check failed: no EAP identity required, "
+ "but '%Y' (%N) received",
+ eap_id, id_type_names, eap_id->get_type(eap_id));
+ }
+ else if (!eap_id->matches(eap_id, id))
+ {
+ DBG1(DBG_CFG, "constraint check failed: EAP identity '%Y'"
+ " (%N) required, not matched by '%Y' (%N)",
+ id, id_type_names, id->get_type(id),
+ eap_id, id_type_names, eap_id->get_type(eap_id));
+ }
+ else
+ {
+ break;
+ }
+ if (!find_eap_cfg(this, &cand))
+ {
+ return FALSE;
+ }
+ }
+ /* copy over the EAP-specific rules for the alternative config, except
+ * the identity we already received from the peer */
+ copy_eap_cfg(cand, cfg, FALSE);
+ return TRUE;
+}
+
/**
* Currently defined PPK_ID types
*/
}
}
if (!message->get_payload(message, PLV2_AUTH))
- { /* before authenticating with EAP, we need a EAP config */
+ {
+ /* before authenticating with EAP, we need an EAP config */
cand = get_auth_cfg(this, FALSE);
- while (!cand || (
- (uintptr_t)cand->get(cand, AUTH_RULE_EAP_TYPE) == EAP_NAK &&
- (uintptr_t)cand->get(cand, AUTH_RULE_EAP_VENDOR) == 0))
- { /* peer requested EAP, but current config does not match */
- DBG1(DBG_IKE, "peer requested EAP, config unacceptable");
- this->peer_cfg->destroy(this->peer_cfg);
- this->peer_cfg = NULL;
- if (!update_cfg_candidates(this, FALSE))
- {
- this->authentication_failed = TRUE;
- return NEED_MORE;
- }
- cand = get_auth_cfg(this, FALSE);
- }
- /* copy over the EAP specific rules for authentication */
- cfg->add(cfg, AUTH_RULE_EAP_TYPE,
- cand->get(cand, AUTH_RULE_EAP_TYPE));
- cfg->add(cfg, AUTH_RULE_EAP_VENDOR,
- cand->get(cand, AUTH_RULE_EAP_VENDOR));
- id = (identification_t*)cand->get(cand, AUTH_RULE_EAP_IDENTITY);
- if (id)
- {
- cfg->add(cfg, AUTH_RULE_EAP_IDENTITY, id->clone(id));
- }
- id = (identification_t*)cand->get(cand, AUTH_RULE_AAA_IDENTITY);
- if (id)
+ if (!is_eap_cfg(cand) && !find_eap_cfg(this, &cand))
{
- cfg->add(cfg, AUTH_RULE_AAA_IDENTITY, id->clone(id));
+ this->authentication_failed = TRUE;
+ return NEED_MORE;
}
+ copy_eap_cfg(cand, cfg, TRUE);
}
/* verify authentication data */
this->other_auth->use_ppk(this->other_auth, this->ppk, FALSE);
}
}
+
+retry_eap_auth:
switch (this->other_auth->process(this->other_auth, message))
{
case SUCCESS:
break;
}
return NEED_MORE;
+ case INVALID_ARG:
+ /* EAP-Identity didn't match, try to find an alternative config */
+ if (find_alternative_eap_cfg(this))
+ {
+ goto retry_eap_auth;
+ }
+ /* fall-through */
default:
this->authentication_failed = TRUE;
return NEED_MORE;