/*
* IEEE 802.1X-2004 Authenticator - EAPOL state machine
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
#define STATE_MACHINE_DEBUG_PREFIX "IEEE 802.1X"
#define STATE_MACHINE_ADDR sm->addr
-static struct eapol_callbacks eapol_cb;
+static const struct eapol_callbacks eapol_cb;
/* EAPOL state machines are described in IEEE Std 802.1X-2004, Chap. 8.2 */
static void eapol_sm_step_run(struct eapol_state_machine *sm);
static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx);
static void eapol_auth_initialize(struct eapol_state_machine *sm);
+static void eapol_auth_conf_free(struct eapol_auth_config *conf);
static void eapol_auth_logger(struct eapol_authenticator *eapol,
{
SM_ENTRY_MA(AUTH_PAE, INITIALIZE, auth_pae);
sm->portMode = Auto;
+
+ /*
+ * Clearing keyRun here is not specified in IEEE Std 802.1X-2004, but
+ * it looks like this would be logical thing to do here since the
+ * EAPOL-Key exchange is not possible in this state. It is possible to
+ * get here on disconnection event without advancing to the
+ * AUTHENTICATING state to clear keyRun before the IEEE 802.11 RSN
+ * authenticator state machine runs and that may advance from
+ * AUTHENTICATION2 to INITPMK if keyRun = TRUE has been left from the
+ * last association. This can be avoided by clearing keyRun here.
+ */
+ sm->keyRun = FALSE;
}
sm->eapolLogoff = FALSE;
if (!from_initialize) {
sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0,
- sm->flags & EAPOL_SM_PREAUTH);
+ sm->flags & EAPOL_SM_PREAUTH,
+ sm->remediation);
}
}
eap_server_get_name(0, sm->eap_type_supp));
}
sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0,
- sm->flags & EAPOL_SM_PREAUTH);
+ sm->flags & EAPOL_SM_PREAUTH, sm->remediation);
}
eap_server_get_name(0, sm->eap_type_authsrv),
extra);
sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 1,
- sm->flags & EAPOL_SM_PREAUTH);
+ sm->flags & EAPOL_SM_PREAUTH, sm->remediation);
}
struct eapol_state_machine *
eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr,
int flags, const struct wpabuf *assoc_wps_ie,
- const struct wpabuf *assoc_p2p_ie, void *sta_ctx)
+ const struct wpabuf *assoc_p2p_ie, void *sta_ctx,
+ const char *identity, const char *radius_cui)
{
struct eapol_state_machine *sm;
struct eap_config eap_conf;
eap_conf.peer_addr = addr;
eap_conf.fragment_size = eapol->conf.fragment_size;
eap_conf.pwd_group = eapol->conf.pwd_group;
+ eap_conf.pbc_in_m1 = eapol->conf.pbc_in_m1;
+ eap_conf.server_id = eapol->conf.server_id;
+ eap_conf.server_id_len = eapol->conf.server_id_len;
+ eap_conf.erp = eapol->conf.erp;
+ eap_conf.tls_session_lifetime = eapol->conf.tls_session_lifetime;
sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf);
if (sm->eap == NULL) {
eapol_auth_free(sm);
eapol_auth_initialize(sm);
+ if (identity) {
+ sm->identity = (u8 *) os_strdup(identity);
+ if (sm->identity)
+ sm->identity_len = os_strlen(identity);
+ }
+ if (radius_cui)
+ sm->radius_cui = wpabuf_alloc_copy(radius_cui,
+ os_strlen(radius_cui));
+
+ sm->acct_multi_session_id_lo = eapol->acct_multi_session_id_lo++;
+ if (eapol->acct_multi_session_id_lo == 0)
+ eapol->acct_multi_session_id_hi++;
+ sm->acct_multi_session_id_hi = eapol->acct_multi_session_id_hi;
+
return sm;
}
struct eap_user *user)
{
struct eapol_state_machine *sm = ctx;
- return sm->eapol->cb.get_eap_user(sm->eapol->conf.ctx, identity,
- identity_len, phase2, user);
+ int ret;
+
+ ret = sm->eapol->cb.get_eap_user(sm->eapol->conf.ctx, identity,
+ identity_len, phase2, user);
+ if (user->remediation)
+ sm->remediation = 1;
+ return ret;
}
}
-static struct eapol_callbacks eapol_cb =
+static int eapol_sm_get_erp_send_reauth_start(void *ctx)
+{
+ struct eapol_state_machine *sm = ctx;
+ return sm->eapol->conf.erp_send_reauth_start;
+}
+
+
+static const char * eapol_sm_get_erp_domain(void *ctx)
+{
+ struct eapol_state_machine *sm = ctx;
+ return sm->eapol->conf.erp_domain;
+}
+
+
+static struct eap_server_erp_key * eapol_sm_erp_get_key(void *ctx,
+ const char *keyname)
+{
+ struct eapol_state_machine *sm = ctx;
+ return sm->eapol->cb.erp_get_key(sm->eapol->conf.ctx, keyname);
+}
+
+
+static int eapol_sm_erp_add_key(void *ctx, struct eap_server_erp_key *erp)
+{
+ struct eapol_state_machine *sm = ctx;
+ return sm->eapol->cb.erp_add_key(sm->eapol->conf.ctx, erp);
+}
+
+
+static const struct eapol_callbacks eapol_cb =
{
eapol_sm_get_eap_user,
- eapol_sm_get_eap_req_id_text
+ eapol_sm_get_eap_req_id_text,
+ NULL,
+ eapol_sm_get_erp_send_reauth_start,
+ eapol_sm_get_erp_domain,
+ eapol_sm_erp_get_key,
+ eapol_sm_erp_add_key,
};
int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx)
{
- if (sm == NULL || ctx != sm->eap)
+ if (sm == NULL || ctx == NULL || ctx != sm->eap)
return -1;
eap_sm_pending_cb(sm->eap);
}
+void eapol_auth_reauthenticate(struct eapol_state_machine *sm)
+{
+ wpa_printf(MSG_DEBUG, "EAPOL: External reauthentication trigger for "
+ MACSTR, MAC2STR(sm->addr));
+ sm->reAuthenticate = TRUE;
+ eapol_auth_step(sm);
+}
+
+
+int eapol_auth_set_conf(struct eapol_state_machine *sm, const char *param,
+ const char *value)
+{
+ wpa_printf(MSG_DEBUG, "EAPOL: External configuration operation for "
+ MACSTR " - param=%s value=%s",
+ MAC2STR(sm->addr), param, value);
+
+ if (os_strcasecmp(param, "AdminControlledDirections") == 0) {
+ if (os_strcmp(value, "Both") == 0)
+ sm->adminControlledDirections = Both;
+ else if (os_strcmp(value, "In") == 0)
+ sm->adminControlledDirections = In;
+ else
+ return -1;
+ eapol_auth_step(sm);
+ return 0;
+ }
+
+ if (os_strcasecmp(param, "AdminControlledPortControl") == 0) {
+ if (os_strcmp(value, "ForceAuthorized") == 0)
+ sm->portControl = ForceAuthorized;
+ else if (os_strcmp(value, "ForceUnauthorized") == 0)
+ sm->portControl = ForceUnauthorized;
+ else if (os_strcmp(value, "Auto") == 0)
+ sm->portControl = Auto;
+ else
+ return -1;
+ eapol_auth_step(sm);
+ return 0;
+ }
+
+ if (os_strcasecmp(param, "quietPeriod") == 0) {
+ sm->quietPeriod = atoi(value);
+ return 0;
+ }
+
+ if (os_strcasecmp(param, "serverTimeout") == 0) {
+ sm->serverTimeout = atoi(value);
+ return 0;
+ }
+
+ if (os_strcasecmp(param, "reAuthPeriod") == 0) {
+ sm->reAuthPeriod = atoi(value);
+ return 0;
+ }
+
+ if (os_strcasecmp(param, "reAuthEnabled") == 0) {
+ if (os_strcmp(value, "TRUE") == 0)
+ sm->reAuthEnabled = TRUE;
+ else if (os_strcmp(value, "FALSE") == 0)
+ sm->reAuthEnabled = FALSE;
+ else
+ return -1;
+ eapol_auth_step(sm);
+ return 0;
+ }
+
+ if (os_strcasecmp(param, "KeyTransmissionEnabled") == 0) {
+ if (os_strcmp(value, "TRUE") == 0)
+ sm->keyTxEnabled = TRUE;
+ else if (os_strcmp(value, "FALSE") == 0)
+ sm->keyTxEnabled = FALSE;
+ else
+ return -1;
+ eapol_auth_step(sm);
+ return 0;
+ }
+
+ return -1;
+}
+
+
static int eapol_auth_conf_clone(struct eapol_auth_config *dst,
struct eapol_auth_config *src)
{
dst->eap_sim_db_priv = src->eap_sim_db_priv;
os_free(dst->eap_req_id_text);
dst->pwd_group = src->pwd_group;
+ dst->pbc_in_m1 = src->pbc_in_m1;
+ dst->server_id = src->server_id;
+ dst->server_id_len = src->server_id_len;
if (src->eap_req_id_text) {
dst->eap_req_id_text = os_malloc(src->eap_req_id_text_len);
if (dst->eap_req_id_text == NULL)
}
if (src->pac_opaque_encr_key) {
dst->pac_opaque_encr_key = os_malloc(16);
+ if (dst->pac_opaque_encr_key == NULL)
+ goto fail;
os_memcpy(dst->pac_opaque_encr_key, src->pac_opaque_encr_key,
16);
} else
dst->pac_opaque_encr_key = NULL;
if (src->eap_fast_a_id) {
dst->eap_fast_a_id = os_malloc(src->eap_fast_a_id_len);
- if (dst->eap_fast_a_id == NULL) {
- os_free(dst->eap_req_id_text);
- return -1;
- }
+ if (dst->eap_fast_a_id == NULL)
+ goto fail;
os_memcpy(dst->eap_fast_a_id, src->eap_fast_a_id,
src->eap_fast_a_id_len);
dst->eap_fast_a_id_len = src->eap_fast_a_id_len;
dst->eap_fast_a_id = NULL;
if (src->eap_fast_a_id_info) {
dst->eap_fast_a_id_info = os_strdup(src->eap_fast_a_id_info);
- if (dst->eap_fast_a_id_info == NULL) {
- os_free(dst->eap_req_id_text);
- os_free(dst->eap_fast_a_id);
- return -1;
- }
+ if (dst->eap_fast_a_id_info == NULL)
+ goto fail;
} else
dst->eap_fast_a_id_info = NULL;
dst->eap_fast_prov = src->eap_fast_prov;
dst->tnc = src->tnc;
dst->wps = src->wps;
dst->fragment_size = src->fragment_size;
+
+ os_free(dst->erp_domain);
+ if (src->erp_domain) {
+ dst->erp_domain = os_strdup(src->erp_domain);
+ if (dst->erp_domain == NULL)
+ goto fail;
+ } else {
+ dst->erp_domain = NULL;
+ }
+ dst->erp_send_reauth_start = src->erp_send_reauth_start;
+ dst->erp = src->erp;
+ dst->tls_session_lifetime = src->tls_session_lifetime;
+
return 0;
+
+fail:
+ eapol_auth_conf_free(dst);
+ return -1;
}
conf->eap_fast_a_id = NULL;
os_free(conf->eap_fast_a_id_info);
conf->eap_fast_a_id_info = NULL;
+ os_free(conf->erp_domain);
+ conf->erp_domain = NULL;
}
struct eapol_auth_cb *cb)
{
struct eapol_authenticator *eapol;
+ struct os_time now;
eapol = os_zalloc(sizeof(*eapol));
if (eapol == NULL)
eapol->cb.abort_auth = cb->abort_auth;
eapol->cb.tx_key = cb->tx_key;
eapol->cb.eapol_event = cb->eapol_event;
+ eapol->cb.erp_get_key = cb->erp_get_key;
+ eapol->cb.erp_add_key = cb->erp_add_key;
+
+ /* Acct-Multi-Session-Id should be unique over reboots. If reliable
+ * clock is not available, this could be replaced with reboot counter,
+ * etc. */
+ os_get_time(&now);
+ eapol->acct_multi_session_id_hi = now.sec;
return eapol;
}