aboutsummaryrefslogtreecommitdiffstats
path: root/src/eap_peer
diff options
context:
space:
mode:
authorJouni Malinen <j@w1.fi>2008-02-28 01:34:43 (GMT)
committerJouni Malinen <jm@jm.kir.nu>2008-02-28 01:34:43 (GMT)
commit6fc6879bd55a394f807cbbe927df736c190cb8ab (patch)
treecdf50da0c58f21510a808d53502a060d911ff243 /src/eap_peer
downloadhostap-6fc6879bd55a394f807cbbe927df736c190cb8ab.zip
hostap-6fc6879bd55a394f807cbbe927df736c190cb8ab.tar.gz
hostap-6fc6879bd55a394f807cbbe927df736c190cb8ab.tar.bz2
Re-initialize hostapd/wpa_supplicant git repository based on 0.6.3 release
Diffstat (limited to 'src/eap_peer')
-rw-r--r--src/eap_peer/.gitignore1
-rw-r--r--src/eap_peer/Makefile6
-rw-r--r--src/eap_peer/eap.c2030
-rw-r--r--src/eap_peer/eap.h288
-rw-r--r--src/eap_peer/eap_aka.c1097
-rw-r--r--src/eap_peer/eap_config.h572
-rw-r--r--src/eap_peer/eap_fast.c1859
-rw-r--r--src/eap_peer/eap_fast_pac.c916
-rw-r--r--src/eap_peer/eap_fast_pac.h56
-rw-r--r--src/eap_peer/eap_gpsk.c732
-rw-r--r--src/eap_peer/eap_gtc.c151
-rw-r--r--src/eap_peer/eap_i.h353
-rw-r--r--src/eap_peer/eap_ikev2.c506
-rw-r--r--src/eap_peer/eap_leap.c403
-rw-r--r--src/eap_peer/eap_md5.c120
-rw-r--r--src/eap_peer/eap_methods.c514
-rw-r--r--src/eap_peer/eap_methods.h87
-rw-r--r--src/eap_peer/eap_mschapv2.c891
-rw-r--r--src/eap_peer/eap_otp.c107
-rw-r--r--src/eap_peer/eap_pax.c532
-rw-r--r--src/eap_peer/eap_peap.c810
-rw-r--r--src/eap_peer/eap_psk.c482
-rw-r--r--src/eap_peer/eap_sake.c499
-rw-r--r--src/eap_peer/eap_sim.c1038
-rw-r--r--src/eap_peer/eap_tls.c288
-rw-r--r--src/eap_peer/eap_tls_common.c1007
-rw-r--r--src/eap_peer/eap_tls_common.h139
-rw-r--r--src/eap_peer/eap_tlv.c189
-rw-r--r--src/eap_peer/eap_tlv.h26
-rw-r--r--src/eap_peer/eap_tnc.c220
-rw-r--r--src/eap_peer/eap_ttls.c1976
-rw-r--r--src/eap_peer/eap_vendor_test.c195
-rw-r--r--src/eap_peer/ikev2.c1303
-rw-r--r--src/eap_peer/ikev2.h65
-rw-r--r--src/eap_peer/mschapv2.c119
-rw-r--r--src/eap_peer/mschapv2.h34
-rw-r--r--src/eap_peer/tncc.c1204
-rw-r--r--src/eap_peer/tncc.h40
38 files changed, 20855 insertions, 0 deletions
diff --git a/src/eap_peer/.gitignore b/src/eap_peer/.gitignore
new file mode 100644
index 0000000..a438335
--- /dev/null
+++ b/src/eap_peer/.gitignore
@@ -0,0 +1 @@
+*.d
diff --git a/src/eap_peer/Makefile b/src/eap_peer/Makefile
new file mode 100644
index 0000000..37d649c
--- /dev/null
+++ b/src/eap_peer/Makefile
@@ -0,0 +1,6 @@
+all:
+ @echo Nothing to be made.
+
+clean:
+ for d in $(SUBDIRS); do make -C $$d clean; done
+ rm -f *~ *.o *.d
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
new file mode 100644
index 0000000..71bb07f
--- /dev/null
+++ b/src/eap_peer/eap.c
@@ -0,0 +1,2030 @@
+/*
+ * EAP peer state machines (RFC 4137)
+ * Copyright (c) 2004-2008, 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 file implements the Peer State Machine as defined in RFC 4137. The used
+ * states and state transitions match mostly with the RFC. However, there are
+ * couple of additional transitions for working around small issues noticed
+ * during testing. These exceptions are explained in comments within the
+ * functions in this file. The method functions, m.func(), are similar to the
+ * ones used in RFC 4137, but some small changes have used here to optimize
+ * operations and to add functionality needed for fast re-authentication
+ * (session resumption).
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_config.h"
+#include "tls.h"
+#include "crypto.h"
+#include "pcsc_funcs.h"
+#include "wpa_ctrl.h"
+#include "state_machine.h"
+
+#define STATE_MACHINE_DATA struct eap_sm
+#define STATE_MACHINE_DEBUG_PREFIX "EAP"
+
+#define EAP_MAX_AUTH_ROUNDS 50
+
+
+static Boolean eap_sm_allowMethod(struct eap_sm *sm, int vendor,
+ EapType method);
+static struct wpabuf * eap_sm_buildNak(struct eap_sm *sm, int id);
+static void eap_sm_processIdentity(struct eap_sm *sm,
+ const struct wpabuf *req);
+static void eap_sm_processNotify(struct eap_sm *sm, const struct wpabuf *req);
+static struct wpabuf * eap_sm_buildNotify(int id);
+static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req);
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
+static const char * eap_sm_method_state_txt(EapMethodState state);
+static const char * eap_sm_decision_txt(EapDecision decision);
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+
+
+
+static Boolean eapol_get_bool(struct eap_sm *sm, enum eapol_bool_var var)
+{
+ return sm->eapol_cb->get_bool(sm->eapol_ctx, var);
+}
+
+
+static void eapol_set_bool(struct eap_sm *sm, enum eapol_bool_var var,
+ Boolean value)
+{
+ sm->eapol_cb->set_bool(sm->eapol_ctx, var, value);
+}
+
+
+static unsigned int eapol_get_int(struct eap_sm *sm, enum eapol_int_var var)
+{
+ return sm->eapol_cb->get_int(sm->eapol_ctx, var);
+}
+
+
+static void eapol_set_int(struct eap_sm *sm, enum eapol_int_var var,
+ unsigned int value)
+{
+ sm->eapol_cb->set_int(sm->eapol_ctx, var, value);
+}
+
+
+static struct wpabuf * eapol_get_eapReqData(struct eap_sm *sm)
+{
+ return sm->eapol_cb->get_eapReqData(sm->eapol_ctx);
+}
+
+
+static void eap_deinit_prev_method(struct eap_sm *sm, const char *txt)
+{
+ if (sm->m == NULL || sm->eap_method_priv == NULL)
+ return;
+
+ wpa_printf(MSG_DEBUG, "EAP: deinitialize previously used EAP method "
+ "(%d, %s) at %s", sm->selectedMethod, sm->m->name, txt);
+ sm->m->deinit(sm, sm->eap_method_priv);
+ sm->eap_method_priv = NULL;
+ sm->m = NULL;
+}
+
+
+/**
+ * eap_allowed_method - Check whether EAP method is allowed
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @vendor: Vendor-Id for expanded types or 0 = IETF for legacy types
+ * @method: EAP type
+ * Returns: 1 = allowed EAP method, 0 = not allowed
+ */
+static int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ int i;
+ struct eap_method_type *m;
+
+ if (config == NULL || config->eap_methods == NULL)
+ return 1;
+
+ m = config->eap_methods;
+ for (i = 0; m[i].vendor != EAP_VENDOR_IETF ||
+ m[i].method != EAP_TYPE_NONE; i++) {
+ if (m[i].vendor == vendor && m[i].method == method)
+ return 1;
+ }
+ return 0;
+}
+
+
+/*
+ * This state initializes state machine variables when the machine is
+ * activated (portEnabled = TRUE). This is also used when re-starting
+ * authentication (eapRestart == TRUE).
+ */
+SM_STATE(EAP, INITIALIZE)
+{
+ SM_ENTRY(EAP, INITIALIZE);
+ if (sm->fast_reauth && sm->m && sm->m->has_reauth_data &&
+ sm->m->has_reauth_data(sm, sm->eap_method_priv)) {
+ wpa_printf(MSG_DEBUG, "EAP: maintaining EAP method data for "
+ "fast reauthentication");
+ sm->m->deinit_for_reauth(sm, sm->eap_method_priv);
+ } else {
+ eap_deinit_prev_method(sm, "INITIALIZE");
+ }
+ sm->selectedMethod = EAP_TYPE_NONE;
+ sm->methodState = METHOD_NONE;
+ sm->allowNotifications = TRUE;
+ sm->decision = DECISION_FAIL;
+ eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout);
+ eapol_set_bool(sm, EAPOL_eapSuccess, FALSE);
+ eapol_set_bool(sm, EAPOL_eapFail, FALSE);
+ os_free(sm->eapKeyData);
+ sm->eapKeyData = NULL;
+ sm->eapKeyAvailable = FALSE;
+ eapol_set_bool(sm, EAPOL_eapRestart, FALSE);
+ sm->lastId = -1; /* new session - make sure this does not match with
+ * the first EAP-Packet */
+ /*
+ * RFC 4137 does not reset eapResp and eapNoResp here. However, this
+ * seemed to be able to trigger cases where both were set and if EAPOL
+ * state machine uses eapNoResp first, it may end up not sending a real
+ * reply correctly. This occurred when the workaround in FAIL state set
+ * eapNoResp = TRUE.. Maybe that workaround needs to be fixed to do
+ * something else(?)
+ */
+ eapol_set_bool(sm, EAPOL_eapResp, FALSE);
+ eapol_set_bool(sm, EAPOL_eapNoResp, FALSE);
+ sm->num_rounds = 0;
+}
+
+
+/*
+ * This state is reached whenever service from the lower layer is interrupted
+ * or unavailable (portEnabled == FALSE). Immediate transition to INITIALIZE
+ * occurs when the port becomes enabled.
+ */
+SM_STATE(EAP, DISABLED)
+{
+ SM_ENTRY(EAP, DISABLED);
+ sm->num_rounds = 0;
+}
+
+
+/*
+ * The state machine spends most of its time here, waiting for something to
+ * happen. This state is entered unconditionally from INITIALIZE, DISCARD, and
+ * SEND_RESPONSE states.
+ */
+SM_STATE(EAP, IDLE)
+{
+ SM_ENTRY(EAP, IDLE);
+}
+
+
+/*
+ * This state is entered when an EAP packet is received (eapReq == TRUE) to
+ * parse the packet header.
+ */
+SM_STATE(EAP, RECEIVED)
+{
+ const struct wpabuf *eapReqData;
+
+ SM_ENTRY(EAP, RECEIVED);
+ eapReqData = eapol_get_eapReqData(sm);
+ /* parse rxReq, rxSuccess, rxFailure, reqId, reqMethod */
+ eap_sm_parseEapReq(sm, eapReqData);
+ sm->num_rounds++;
+}
+
+
+/*
+ * This state is entered when a request for a new type comes in. Either the
+ * correct method is started, or a Nak response is built.
+ */
+SM_STATE(EAP, GET_METHOD)
+{
+ int reinit;
+ EapType method;
+
+ SM_ENTRY(EAP, GET_METHOD);
+
+ if (sm->reqMethod == EAP_TYPE_EXPANDED)
+ method = sm->reqVendorMethod;
+ else
+ method = sm->reqMethod;
+
+ if (!eap_sm_allowMethod(sm, sm->reqVendor, method)) {
+ wpa_printf(MSG_DEBUG, "EAP: vendor %u method %u not allowed",
+ sm->reqVendor, method);
+ goto nak;
+ }
+
+ /*
+ * RFC 4137 does not define specific operation for fast
+ * re-authentication (session resumption). The design here is to allow
+ * the previously used method data to be maintained for
+ * re-authentication if the method support session resumption.
+ * Otherwise, the previously used method data is freed and a new method
+ * is allocated here.
+ */
+ if (sm->fast_reauth &&
+ sm->m && sm->m->vendor == sm->reqVendor &&
+ sm->m->method == method &&
+ sm->m->has_reauth_data &&
+ sm->m->has_reauth_data(sm, sm->eap_method_priv)) {
+ wpa_printf(MSG_DEBUG, "EAP: Using previous method data"
+ " for fast re-authentication");
+ reinit = 1;
+ } else {
+ eap_deinit_prev_method(sm, "GET_METHOD");
+ reinit = 0;
+ }
+
+ sm->selectedMethod = sm->reqMethod;
+ if (sm->m == NULL)
+ sm->m = eap_peer_get_eap_method(sm->reqVendor, method);
+ if (!sm->m) {
+ wpa_printf(MSG_DEBUG, "EAP: Could not find selected method: "
+ "vendor %d method %d",
+ sm->reqVendor, method);
+ goto nak;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP: Initialize selected EAP method: "
+ "vendor %u method %u (%s)",
+ sm->reqVendor, method, sm->m->name);
+ if (reinit)
+ sm->eap_method_priv = sm->m->init_for_reauth(
+ sm, sm->eap_method_priv);
+ else
+ sm->eap_method_priv = sm->m->init(sm);
+
+ if (sm->eap_method_priv == NULL) {
+ struct eap_peer_config *config = eap_get_config(sm);
+ wpa_msg(sm->msg_ctx, MSG_INFO,
+ "EAP: Failed to initialize EAP method: vendor %u "
+ "method %u (%s)",
+ sm->reqVendor, method, sm->m->name);
+ sm->m = NULL;
+ sm->methodState = METHOD_NONE;
+ sm->selectedMethod = EAP_TYPE_NONE;
+ if (sm->reqMethod == EAP_TYPE_TLS && config &&
+ (config->pending_req_pin ||
+ config->pending_req_passphrase)) {
+ /*
+ * Return without generating Nak in order to allow
+ * entering of PIN code or passphrase to retry the
+ * current EAP packet.
+ */
+ wpa_printf(MSG_DEBUG, "EAP: Pending PIN/passphrase "
+ "request - skip Nak");
+ return;
+ }
+
+ goto nak;
+ }
+
+ sm->methodState = METHOD_INIT;
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_METHOD
+ "EAP vendor %u method %u (%s) selected",
+ sm->reqVendor, method, sm->m->name);
+ return;
+
+nak:
+ wpabuf_free(sm->eapRespData);
+ sm->eapRespData = NULL;
+ sm->eapRespData = eap_sm_buildNak(sm, sm->reqId);
+}
+
+
+/*
+ * The method processing happens here. The request from the authenticator is
+ * processed, and an appropriate response packet is built.
+ */
+SM_STATE(EAP, METHOD)
+{
+ struct wpabuf *eapReqData;
+ struct eap_method_ret ret;
+
+ SM_ENTRY(EAP, METHOD);
+ if (sm->m == NULL) {
+ wpa_printf(MSG_WARNING, "EAP::METHOD - method not selected");
+ return;
+ }
+
+ eapReqData = eapol_get_eapReqData(sm);
+
+ /*
+ * Get ignore, methodState, decision, allowNotifications, and
+ * eapRespData. RFC 4137 uses three separate method procedure (check,
+ * process, and buildResp) in this state. These have been combined into
+ * a single function call to m->process() in order to optimize EAP
+ * method implementation interface a bit. These procedures are only
+ * used from within this METHOD state, so there is no need to keep
+ * these as separate C functions.
+ *
+ * The RFC 4137 procedures return values as follows:
+ * ignore = m.check(eapReqData)
+ * (methodState, decision, allowNotifications) = m.process(eapReqData)
+ * eapRespData = m.buildResp(reqId)
+ */
+ os_memset(&ret, 0, sizeof(ret));
+ ret.ignore = sm->ignore;
+ ret.methodState = sm->methodState;
+ ret.decision = sm->decision;
+ ret.allowNotifications = sm->allowNotifications;
+ wpabuf_free(sm->eapRespData);
+ sm->eapRespData = NULL;
+ sm->eapRespData = sm->m->process(sm, sm->eap_method_priv, &ret,
+ eapReqData);
+ wpa_printf(MSG_DEBUG, "EAP: method process -> ignore=%s "
+ "methodState=%s decision=%s",
+ ret.ignore ? "TRUE" : "FALSE",
+ eap_sm_method_state_txt(ret.methodState),
+ eap_sm_decision_txt(ret.decision));
+
+ sm->ignore = ret.ignore;
+ if (sm->ignore)
+ return;
+ sm->methodState = ret.methodState;
+ sm->decision = ret.decision;
+ sm->allowNotifications = ret.allowNotifications;
+
+ if (sm->m->isKeyAvailable && sm->m->getKey &&
+ sm->m->isKeyAvailable(sm, sm->eap_method_priv)) {
+ os_free(sm->eapKeyData);
+ sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv,
+ &sm->eapKeyDataLen);
+ }
+}
+
+
+/*
+ * This state signals the lower layer that a response packet is ready to be
+ * sent.
+ */
+SM_STATE(EAP, SEND_RESPONSE)
+{
+ SM_ENTRY(EAP, SEND_RESPONSE);
+ wpabuf_free(sm->lastRespData);
+ if (sm->eapRespData) {
+ if (sm->workaround)
+ os_memcpy(sm->last_md5, sm->req_md5, 16);
+ sm->lastId = sm->reqId;
+ sm->lastRespData = wpabuf_dup(sm->eapRespData);
+ eapol_set_bool(sm, EAPOL_eapResp, TRUE);
+ } else
+ sm->lastRespData = NULL;
+ eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+ eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout);
+}
+
+
+/*
+ * This state signals the lower layer that the request was discarded, and no
+ * response packet will be sent at this time.
+ */
+SM_STATE(EAP, DISCARD)
+{
+ SM_ENTRY(EAP, DISCARD);
+ eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+ eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
+}
+
+
+/*
+ * Handles requests for Identity method and builds a response.
+ */
+SM_STATE(EAP, IDENTITY)
+{
+ const struct wpabuf *eapReqData;
+
+ SM_ENTRY(EAP, IDENTITY);
+ eapReqData = eapol_get_eapReqData(sm);
+ eap_sm_processIdentity(sm, eapReqData);
+ wpabuf_free(sm->eapRespData);
+ sm->eapRespData = NULL;
+ sm->eapRespData = eap_sm_buildIdentity(sm, sm->reqId, 0);
+}
+
+
+/*
+ * Handles requests for Notification method and builds a response.
+ */
+SM_STATE(EAP, NOTIFICATION)
+{
+ const struct wpabuf *eapReqData;
+
+ SM_ENTRY(EAP, NOTIFICATION);
+ eapReqData = eapol_get_eapReqData(sm);
+ eap_sm_processNotify(sm, eapReqData);
+ wpabuf_free(sm->eapRespData);
+ sm->eapRespData = NULL;
+ sm->eapRespData = eap_sm_buildNotify(sm->reqId);
+}
+
+
+/*
+ * This state retransmits the previous response packet.
+ */
+SM_STATE(EAP, RETRANSMIT)
+{
+ SM_ENTRY(EAP, RETRANSMIT);
+ wpabuf_free(sm->eapRespData);
+ if (sm->lastRespData)
+ sm->eapRespData = wpabuf_dup(sm->lastRespData);
+ else
+ sm->eapRespData = NULL;
+}
+
+
+/*
+ * This state is entered in case of a successful completion of authentication
+ * and state machine waits here until port is disabled or EAP authentication is
+ * restarted.
+ */
+SM_STATE(EAP, SUCCESS)
+{
+ SM_ENTRY(EAP, SUCCESS);
+ if (sm->eapKeyData != NULL)
+ sm->eapKeyAvailable = TRUE;
+ eapol_set_bool(sm, EAPOL_eapSuccess, TRUE);
+
+ /*
+ * RFC 4137 does not clear eapReq here, but this seems to be required
+ * to avoid processing the same request twice when state machine is
+ * initialized.
+ */
+ eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+
+ /*
+ * RFC 4137 does not set eapNoResp here, but this seems to be required
+ * to get EAPOL Supplicant backend state machine into SUCCESS state. In
+ * addition, either eapResp or eapNoResp is required to be set after
+ * processing the received EAP frame.
+ */
+ eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
+
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
+ "EAP authentication completed successfully");
+}
+
+
+/*
+ * This state is entered in case of a failure and state machine waits here
+ * until port is disabled or EAP authentication is restarted.
+ */
+SM_STATE(EAP, FAILURE)
+{
+ SM_ENTRY(EAP, FAILURE);
+ eapol_set_bool(sm, EAPOL_eapFail, TRUE);
+
+ /*
+ * RFC 4137 does not clear eapReq here, but this seems to be required
+ * to avoid processing the same request twice when state machine is
+ * initialized.
+ */
+ eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+
+ /*
+ * RFC 4137 does not set eapNoResp here. However, either eapResp or
+ * eapNoResp is required to be set after processing the received EAP
+ * frame.
+ */
+ eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
+
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE
+ "EAP authentication failed");
+}
+
+
+static int eap_success_workaround(struct eap_sm *sm, int reqId, int lastId)
+{
+ /*
+ * At least Microsoft IAS and Meetinghouse Aegis seem to be sending
+ * EAP-Success/Failure with lastId + 1 even though RFC 3748 and
+ * RFC 4137 require that reqId == lastId. In addition, it looks like
+ * Ringmaster v2.1.2.0 would be using lastId + 2 in EAP-Success.
+ *
+ * Accept this kind of Id if EAP workarounds are enabled. These are
+ * unauthenticated plaintext messages, so this should have minimal
+ * security implications (bit easier to fake EAP-Success/Failure).
+ */
+ if (sm->workaround && (reqId == ((lastId + 1) & 0xff) ||
+ reqId == ((lastId + 2) & 0xff))) {
+ wpa_printf(MSG_DEBUG, "EAP: Workaround for unexpected "
+ "identifier field in EAP Success: "
+ "reqId=%d lastId=%d (these are supposed to be "
+ "same)", reqId, lastId);
+ return 1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP: EAP-Success Id mismatch - reqId=%d "
+ "lastId=%d", reqId, lastId);
+ return 0;
+}
+
+
+/*
+ * RFC 4137 - Appendix A.1: EAP Peer State Machine - State transitions
+ */
+
+static void eap_peer_sm_step_idle(struct eap_sm *sm)
+{
+ /*
+ * The first three transitions are from RFC 4137. The last two are
+ * local additions to handle special cases with LEAP and PEAP server
+ * not sending EAP-Success in some cases.
+ */
+ if (eapol_get_bool(sm, EAPOL_eapReq))
+ SM_ENTER(EAP, RECEIVED);
+ else if ((eapol_get_bool(sm, EAPOL_altAccept) &&
+ sm->decision != DECISION_FAIL) ||
+ (eapol_get_int(sm, EAPOL_idleWhile) == 0 &&
+ sm->decision == DECISION_UNCOND_SUCC))
+ SM_ENTER(EAP, SUCCESS);
+ else if (eapol_get_bool(sm, EAPOL_altReject) ||
+ (eapol_get_int(sm, EAPOL_idleWhile) == 0 &&
+ sm->decision != DECISION_UNCOND_SUCC) ||
+ (eapol_get_bool(sm, EAPOL_altAccept) &&
+ sm->methodState != METHOD_CONT &&
+ sm->decision == DECISION_FAIL))
+ SM_ENTER(EAP, FAILURE);
+ else if (sm->selectedMethod == EAP_TYPE_LEAP &&
+ sm->leap_done && sm->decision != DECISION_FAIL &&
+ sm->methodState == METHOD_DONE)
+ SM_ENTER(EAP, SUCCESS);
+ else if (sm->selectedMethod == EAP_TYPE_PEAP &&
+ sm->peap_done && sm->decision != DECISION_FAIL &&
+ sm->methodState == METHOD_DONE)
+ SM_ENTER(EAP, SUCCESS);
+}
+
+
+static int eap_peer_req_is_duplicate(struct eap_sm *sm)
+{
+ int duplicate;
+
+ duplicate = (sm->reqId == sm->lastId) && sm->rxReq;
+ if (sm->workaround && duplicate &&
+ os_memcmp(sm->req_md5, sm->last_md5, 16) != 0) {
+ /*
+ * RFC 4137 uses (reqId == lastId) as the only verification for
+ * duplicate EAP requests. However, this misses cases where the
+ * AS is incorrectly using the same id again; and
+ * unfortunately, such implementations exist. Use MD5 hash as
+ * an extra verification for the packets being duplicate to
+ * workaround these issues.
+ */
+ wpa_printf(MSG_DEBUG, "EAP: AS used the same Id again, but "
+ "EAP packets were not identical");
+ wpa_printf(MSG_DEBUG, "EAP: workaround - assume this is not a "
+ "duplicate packet");
+ duplicate = 0;
+ }
+
+ return duplicate;
+}
+
+
+static void eap_peer_sm_step_received(struct eap_sm *sm)
+{
+ int duplicate = eap_peer_req_is_duplicate(sm);
+
+ /*
+ * Two special cases below for LEAP are local additions to work around
+ * odd LEAP behavior (EAP-Success in the middle of authentication and
+ * then swapped roles). Other transitions are based on RFC 4137.
+ */
+ if (sm->rxSuccess && sm->decision != DECISION_FAIL &&
+ (sm->reqId == sm->lastId ||
+ eap_success_workaround(sm, sm->reqId, sm->lastId)))
+ SM_ENTER(EAP, SUCCESS);
+ else if (sm->methodState != METHOD_CONT &&
+ ((sm->rxFailure &&
+ sm->decision != DECISION_UNCOND_SUCC) ||
+ (sm->rxSuccess && sm->decision == DECISION_FAIL &&
+ (sm->selectedMethod != EAP_TYPE_LEAP ||
+ sm->methodState != METHOD_MAY_CONT))) &&
+ (sm->reqId == sm->lastId ||
+ eap_success_workaround(sm, sm->reqId, sm->lastId)))
+ SM_ENTER(EAP, FAILURE);
+ else if (sm->rxReq && duplicate)
+ SM_ENTER(EAP, RETRANSMIT);
+ else if (sm->rxReq && !duplicate &&
+ sm->reqMethod == EAP_TYPE_NOTIFICATION &&
+ sm->allowNotifications)
+ SM_ENTER(EAP, NOTIFICATION);
+ else if (sm->rxReq && !duplicate &&
+ sm->selectedMethod == EAP_TYPE_NONE &&
+ sm->reqMethod == EAP_TYPE_IDENTITY)
+ SM_ENTER(EAP, IDENTITY);
+ else if (sm->rxReq && !duplicate &&
+ sm->selectedMethod == EAP_TYPE_NONE &&
+ sm->reqMethod != EAP_TYPE_IDENTITY &&
+ sm->reqMethod != EAP_TYPE_NOTIFICATION)
+ SM_ENTER(EAP, GET_METHOD);
+ else if (sm->rxReq && !duplicate &&
+ sm->reqMethod == sm->selectedMethod &&
+ sm->methodState != METHOD_DONE)
+ SM_ENTER(EAP, METHOD);
+ else if (sm->selectedMethod == EAP_TYPE_LEAP &&
+ (sm->rxSuccess || sm->rxResp))
+ SM_ENTER(EAP, METHOD);
+ else
+ SM_ENTER(EAP, DISCARD);
+}
+
+
+static void eap_peer_sm_step_local(struct eap_sm *sm)
+{
+ switch (sm->EAP_state) {
+ case EAP_INITIALIZE:
+ SM_ENTER(EAP, IDLE);
+ break;
+ case EAP_DISABLED:
+ if (eapol_get_bool(sm, EAPOL_portEnabled) &&
+ !sm->force_disabled)
+ SM_ENTER(EAP, INITIALIZE);
+ break;
+ case EAP_IDLE:
+ eap_peer_sm_step_idle(sm);
+ break;
+ case EAP_RECEIVED:
+ eap_peer_sm_step_received(sm);
+ break;
+ case EAP_GET_METHOD:
+ if (sm->selectedMethod == sm->reqMethod)
+ SM_ENTER(EAP, METHOD);
+ else
+ SM_ENTER(EAP, SEND_RESPONSE);
+ break;
+ case EAP_METHOD:
+ if (sm->ignore)
+ SM_ENTER(EAP, DISCARD);
+ else
+ SM_ENTER(EAP, SEND_RESPONSE);
+ break;
+ case EAP_SEND_RESPONSE:
+ SM_ENTER(EAP, IDLE);
+ break;
+ case EAP_DISCARD:
+ SM_ENTER(EAP, IDLE);
+ break;
+ case EAP_IDENTITY:
+ SM_ENTER(EAP, SEND_RESPONSE);
+ break;
+ case EAP_NOTIFICATION:
+ SM_ENTER(EAP, SEND_RESPONSE);
+ break;
+ case EAP_RETRANSMIT:
+ SM_ENTER(EAP, SEND_RESPONSE);
+ break;
+ case EAP_SUCCESS:
+ break;
+ case EAP_FAILURE:
+ break;
+ }
+}
+
+
+SM_STEP(EAP)
+{
+ /* Global transitions */
+ if (eapol_get_bool(sm, EAPOL_eapRestart) &&
+ eapol_get_bool(sm, EAPOL_portEnabled))
+ SM_ENTER_GLOBAL(EAP, INITIALIZE);
+ else if (!eapol_get_bool(sm, EAPOL_portEnabled) || sm->force_disabled)
+ SM_ENTER_GLOBAL(EAP, DISABLED);
+ else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) {
+ /* RFC 4137 does not place any limit on number of EAP messages
+ * in an authentication session. However, some error cases have
+ * ended up in a state were EAP messages were sent between the
+ * peer and server in a loop (e.g., TLS ACK frame in both
+ * direction). Since this is quite undesired outcome, limit the
+ * total number of EAP round-trips and abort authentication if
+ * this limit is exceeded.
+ */
+ if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) {
+ wpa_msg(sm->msg_ctx, MSG_INFO, "EAP: more than %d "
+ "authentication rounds - abort",
+ EAP_MAX_AUTH_ROUNDS);
+ sm->num_rounds++;
+ SM_ENTER_GLOBAL(EAP, FAILURE);
+ }
+ } else {
+ /* Local transitions */
+ eap_peer_sm_step_local(sm);
+ }
+}
+
+
+static Boolean eap_sm_allowMethod(struct eap_sm *sm, int vendor,
+ EapType method)
+{
+ if (!eap_allowed_method(sm, vendor, method)) {
+ wpa_printf(MSG_DEBUG, "EAP: configuration does not allow: "
+ "vendor %u method %u", vendor, method);
+ return FALSE;
+ }
+ if (eap_peer_get_eap_method(vendor, method))
+ return TRUE;
+ wpa_printf(MSG_DEBUG, "EAP: not included in build: "
+ "vendor %u method %u", vendor, method);
+ return FALSE;
+}
+
+
+static struct wpabuf * eap_sm_build_expanded_nak(
+ struct eap_sm *sm, int id, const struct eap_method *methods,
+ size_t count)
+{
+ struct wpabuf *resp;
+ int found = 0;
+ const struct eap_method *m;
+
+ wpa_printf(MSG_DEBUG, "EAP: Building expanded EAP-Nak");
+
+ /* RFC 3748 - 5.3.2: Expanded Nak */
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EXPANDED,
+ 8 + 8 * (count + 1), EAP_CODE_RESPONSE, id);
+ if (resp == NULL)
+ return NULL;
+
+ wpabuf_put_be24(resp, EAP_VENDOR_IETF);
+ wpabuf_put_be32(resp, EAP_TYPE_NAK);
+
+ for (m = methods; m; m = m->next) {
+ if (sm->reqVendor == m->vendor &&
+ sm->reqVendorMethod == m->method)
+ continue; /* do not allow the current method again */
+ if (eap_allowed_method(sm, m->vendor, m->method)) {
+ wpa_printf(MSG_DEBUG, "EAP: allowed type: "
+ "vendor=%u method=%u",
+ m->vendor, m->method);
+ wpabuf_put_u8(resp, EAP_TYPE_EXPANDED);
+ wpabuf_put_be24(resp, m->vendor);
+ wpabuf_put_be32(resp, m->method);
+
+ found++;
+ }
+ }
+ if (!found) {
+ wpa_printf(MSG_DEBUG, "EAP: no more allowed methods");
+ wpabuf_put_u8(resp, EAP_TYPE_EXPANDED);
+ wpabuf_put_be24(resp, EAP_VENDOR_IETF);
+ wpabuf_put_be32(resp, EAP_TYPE_NONE);
+ }
+
+ eap_update_len(resp);
+
+ return resp;
+}
+
+
+static struct wpabuf * eap_sm_buildNak(struct eap_sm *sm, int id)
+{
+ struct wpabuf *resp;
+ u8 *start;
+ int found = 0, expanded_found = 0;
+ size_t count;
+ const struct eap_method *methods, *m;
+
+ wpa_printf(MSG_DEBUG, "EAP: Building EAP-Nak (requested type %u "
+ "vendor=%u method=%u not allowed)", sm->reqMethod,
+ sm->reqVendor, sm->reqVendorMethod);
+ methods = eap_peer_get_methods(&count);
+ if (methods == NULL)
+ return NULL;
+ if (sm->reqMethod == EAP_TYPE_EXPANDED)
+ return eap_sm_build_expanded_nak(sm, id, methods, count);
+
+ /* RFC 3748 - 5.3.1: Legacy Nak */
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK,
+ sizeof(struct eap_hdr) + 1 + count + 1,
+ EAP_CODE_RESPONSE, id);
+ if (resp == NULL)
+ return NULL;
+
+ start = wpabuf_put(resp, 0);
+ for (m = methods; m; m = m->next) {
+ if (m->vendor == EAP_VENDOR_IETF && m->method == sm->reqMethod)
+ continue; /* do not allow the current method again */
+ if (eap_allowed_method(sm, m->vendor, m->method)) {
+ if (m->vendor != EAP_VENDOR_IETF) {
+ if (expanded_found)
+ continue;
+ expanded_found = 1;
+ wpabuf_put_u8(resp, EAP_TYPE_EXPANDED);
+ } else
+ wpabuf_put_u8(resp, m->method);
+ found++;
+ }
+ }
+ if (!found)
+ wpabuf_put_u8(resp, EAP_TYPE_NONE);
+ wpa_hexdump(MSG_DEBUG, "EAP: allowed methods", start, found);
+
+ eap_update_len(resp);
+
+ return resp;
+}
+
+
+static void eap_sm_processIdentity(struct eap_sm *sm, const struct wpabuf *req)
+{
+ const struct eap_hdr *hdr = wpabuf_head(req);
+ const u8 *pos = (const u8 *) (hdr + 1);
+ pos++;
+
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED
+ "EAP authentication started");
+
+ /*
+ * RFC 3748 - 5.1: Identity
+ * Data field may contain a displayable message in UTF-8. If this
+ * includes NUL-character, only the data before that should be
+ * displayed. Some EAP implementasitons may piggy-back additional
+ * options after the NUL.
+ */
+ /* TODO: could save displayable message so that it can be shown to the
+ * user in case of interaction is required */
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Identity data",
+ pos, be_to_host16(hdr->length) - 5);
+}
+
+
+#ifdef PCSC_FUNCS
+static int eap_sm_imsi_identity(struct eap_sm *sm,
+ struct eap_peer_config *conf)
+{
+ int aka = 0;
+ char imsi[100];
+ size_t imsi_len;
+ struct eap_method_type *m = conf->eap_methods;
+ int i;
+
+ imsi_len = sizeof(imsi);
+ if (scard_get_imsi(sm->scard_ctx, imsi, &imsi_len)) {
+ wpa_printf(MSG_WARNING, "Failed to get IMSI from SIM");
+ return -1;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "IMSI", (u8 *) imsi, imsi_len);
+
+ for (i = 0; m && (m[i].vendor != EAP_VENDOR_IETF ||
+ m[i].method != EAP_TYPE_NONE); i++) {
+ if (m[i].vendor == EAP_VENDOR_IETF &&
+ m[i].method == EAP_TYPE_AKA) {
+ aka = 1;
+ break;
+ }
+ }
+
+ os_free(conf->identity);
+ conf->identity = os_malloc(1 + imsi_len);
+ if (conf->identity == NULL) {
+ wpa_printf(MSG_WARNING, "Failed to allocate buffer for "
+ "IMSI-based identity");
+ return -1;
+ }
+
+ conf->identity[0] = aka ? '0' : '1';
+ os_memcpy(conf->identity + 1, imsi, imsi_len);
+ conf->identity_len = 1 + imsi_len;
+
+ return 0;
+}
+#endif /* PCSC_FUNCS */
+
+
+static int eap_sm_get_scard_identity(struct eap_sm *sm,
+ struct eap_peer_config *conf)
+{
+#ifdef PCSC_FUNCS
+ if (scard_set_pin(sm->scard_ctx, conf->pin)) {
+ /*
+ * Make sure the same PIN is not tried again in order to avoid
+ * blocking SIM.
+ */
+ os_free(conf->pin);
+ conf->pin = NULL;
+
+ wpa_printf(MSG_WARNING, "PIN validation failed");
+ eap_sm_request_pin(sm);
+ return -1;
+ }
+
+ return eap_sm_imsi_identity(sm, conf);
+#else /* PCSC_FUNCS */
+ return -1;
+#endif /* PCSC_FUNCS */
+}
+
+
+/**
+ * eap_sm_buildIdentity - Build EAP-Identity/Response for the current network
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @id: EAP identifier for the packet
+ * @encrypted: Whether the packet is for encrypted tunnel (EAP phase 2)
+ * Returns: Pointer to the allocated EAP-Identity/Response packet or %NULL on
+ * failure
+ *
+ * This function allocates and builds an EAP-Identity/Response packet for the
+ * current network. The caller is responsible for freeing the returned data.
+ */
+struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ struct wpabuf *resp;
+ const u8 *identity;
+ size_t identity_len;
+
+ if (config == NULL) {
+ wpa_printf(MSG_WARNING, "EAP: buildIdentity: configuration "
+ "was not available");
+ return NULL;
+ }
+
+ if (sm->m && sm->m->get_identity &&
+ (identity = sm->m->get_identity(sm, sm->eap_method_priv,
+ &identity_len)) != NULL) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP: using method re-auth "
+ "identity", identity, identity_len);
+ } else if (!encrypted && config->anonymous_identity) {
+ identity = config->anonymous_identity;
+ identity_len = config->anonymous_identity_len;
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP: using anonymous identity",
+ identity, identity_len);
+ } else {
+ identity = config->identity;
+ identity_len = config->identity_len;
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP: using real identity",
+ identity, identity_len);
+ }
+
+ if (identity == NULL) {
+ wpa_printf(MSG_WARNING, "EAP: buildIdentity: identity "
+ "configuration was not available");
+ if (config->pcsc) {
+ if (eap_sm_get_scard_identity(sm, config) < 0)
+ return NULL;
+ identity = config->identity;
+ identity_len = config->identity_len;
+ wpa_hexdump_ascii(MSG_DEBUG, "permanent identity from "
+ "IMSI", identity, identity_len);
+ } else {
+ eap_sm_request_identity(sm);
+ return NULL;
+ }
+ }
+
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, identity_len,
+ EAP_CODE_RESPONSE, id);
+ if (resp == NULL)
+ return NULL;
+
+ wpabuf_put_data(resp, identity, identity_len);
+
+ return resp;
+}
+
+
+static void eap_sm_processNotify(struct eap_sm *sm, const struct wpabuf *req)
+{
+ const u8 *pos;
+ char *msg;
+ size_t i, msg_len;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, req,
+ &msg_len);
+ if (pos == NULL)
+ return;
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Notification data",
+ pos, msg_len);
+
+ msg = os_malloc(msg_len + 1);
+ if (msg == NULL)
+ return;
+ for (i = 0; i < msg_len; i++)
+ msg[i] = isprint(pos[i]) ? (char) pos[i] : '_';
+ msg[msg_len] = '\0';
+ wpa_msg(sm->msg_ctx, MSG_INFO, "%s%s",
+ WPA_EVENT_EAP_NOTIFICATION, msg);
+ os_free(msg);
+}
+
+
+static struct wpabuf * eap_sm_buildNotify(int id)
+{
+ struct wpabuf *resp;
+
+ wpa_printf(MSG_DEBUG, "EAP: Generating EAP-Response Notification");
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, 0,
+ EAP_CODE_RESPONSE, id);
+ if (resp == NULL)
+ return NULL;
+
+ return resp;
+}
+
+
+static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req)
+{
+ const struct eap_hdr *hdr;
+ size_t plen;
+ const u8 *pos;
+
+ sm->rxReq = sm->rxResp = sm->rxSuccess = sm->rxFailure = FALSE;
+ sm->reqId = 0;
+ sm->reqMethod = EAP_TYPE_NONE;
+ sm->reqVendor = EAP_VENDOR_IETF;
+ sm->reqVendorMethod = EAP_TYPE_NONE;
+
+ if (req == NULL || wpabuf_len(req) < sizeof(*hdr))
+ return;
+
+ hdr = wpabuf_head(req);
+ plen = be_to_host16(hdr->length);
+ if (plen > wpabuf_len(req)) {
+ wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet "
+ "(len=%lu plen=%lu)",
+ (unsigned long) wpabuf_len(req),
+ (unsigned long) plen);
+ return;
+ }
+
+ sm->reqId = hdr->identifier;
+
+ if (sm->workaround) {
+ const u8 *addr[1];
+ addr[0] = wpabuf_head(req);
+ md5_vector(1, addr, &plen, sm->req_md5);
+ }
+
+ switch (hdr->code) {
+ case EAP_CODE_REQUEST:
+ if (plen < sizeof(*hdr) + 1) {
+ wpa_printf(MSG_DEBUG, "EAP: Too short EAP-Request - "
+ "no Type field");
+ return;
+ }
+ sm->rxReq = TRUE;
+ pos = (const u8 *) (hdr + 1);
+ sm->reqMethod = *pos++;
+ if (sm->reqMethod == EAP_TYPE_EXPANDED) {
+ if (plen < sizeof(*hdr) + 8) {
+ wpa_printf(MSG_DEBUG, "EAP: Ignored truncated "
+ "expanded EAP-Packet (plen=%lu)",
+ (unsigned long) plen);
+ return;
+ }
+ sm->reqVendor = WPA_GET_BE24(pos);
+ pos += 3;
+ sm->reqVendorMethod = WPA_GET_BE32(pos);
+ }
+ wpa_printf(MSG_DEBUG, "EAP: Received EAP-Request id=%d "
+ "method=%u vendor=%u vendorMethod=%u",
+ sm->reqId, sm->reqMethod, sm->reqVendor,
+ sm->reqVendorMethod);
+ break;
+ case EAP_CODE_RESPONSE:
+ if (sm->selectedMethod == EAP_TYPE_LEAP) {
+ /*
+ * LEAP differs from RFC 4137 by using reversed roles
+ * for mutual authentication and because of this, we
+ * need to accept EAP-Response frames if LEAP is used.
+ */
+ if (plen < sizeof(*hdr) + 1) {
+ wpa_printf(MSG_DEBUG, "EAP: Too short "
+ "EAP-Response - no Type field");
+ return;
+ }
+ sm->rxResp = TRUE;
+ pos = (const u8 *) (hdr + 1);
+ sm->reqMethod = *pos;
+ wpa_printf(MSG_DEBUG, "EAP: Received EAP-Response for "
+ "LEAP method=%d id=%d",
+ sm->reqMethod, sm->reqId);
+ break;
+ }
+ wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Response");
+ break;
+ case EAP_CODE_SUCCESS:
+ wpa_printf(MSG_DEBUG, "EAP: Received EAP-Success");
+ sm->rxSuccess = TRUE;
+ break;
+ case EAP_CODE_FAILURE:
+ wpa_printf(MSG_DEBUG, "EAP: Received EAP-Failure");
+ sm->rxFailure = TRUE;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Packet with unknown "
+ "code %d", hdr->code);
+ break;
+ }
+}
+
+
+/**
+ * eap_peer_sm_init - Allocate and initialize EAP peer state machine
+ * @eapol_ctx: Context data to be used with eapol_cb calls
+ * @eapol_cb: Pointer to EAPOL callback functions
+ * @msg_ctx: Context data for wpa_msg() calls
+ * @conf: EAP configuration
+ * Returns: Pointer to the allocated EAP state machine or %NULL on failure
+ *
+ * This function allocates and initializes an EAP state machine. In addition,
+ * this initializes TLS library for the new EAP state machine. eapol_cb pointer
+ * will be in use until eap_peer_sm_deinit() is used to deinitialize this EAP
+ * state machine. Consequently, the caller must make sure that this data
+ * structure remains alive while the EAP state machine is active.
+ */
+struct eap_sm * eap_peer_sm_init(void *eapol_ctx,
+ struct eapol_callbacks *eapol_cb,
+ void *msg_ctx, struct eap_config *conf)
+{
+ struct eap_sm *sm;
+ struct tls_config tlsconf;
+
+ sm = os_zalloc(sizeof(*sm));
+ if (sm == NULL)
+ return NULL;
+ sm->eapol_ctx = eapol_ctx;
+ sm->eapol_cb = eapol_cb;
+ sm->msg_ctx = msg_ctx;
+ sm->ClientTimeout = 60;
+ if (conf->mac_addr)
+ os_memcpy(sm->mac_addr, conf->mac_addr, ETH_ALEN);
+
+ os_memset(&tlsconf, 0, sizeof(tlsconf));
+ tlsconf.opensc_engine_path = conf->opensc_engine_path;
+ tlsconf.pkcs11_engine_path = conf->pkcs11_engine_path;
+ tlsconf.pkcs11_module_path = conf->pkcs11_module_path;
+ sm->ssl_ctx = tls_init(&tlsconf);
+ if (sm->ssl_ctx == NULL) {
+ wpa_printf(MSG_WARNING, "SSL: Failed to initialize TLS "
+ "context.");
+ os_free(sm);
+ return NULL;
+ }
+
+ return sm;
+}
+
+
+/**
+ * eap_peer_sm_deinit - Deinitialize and free an EAP peer state machine
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * This function deinitializes EAP state machine and frees all allocated
+ * resources.
+ */
+void eap_peer_sm_deinit(struct eap_sm *sm)
+{
+ if (sm == NULL)
+ return;
+ eap_deinit_prev_method(sm, "EAP deinit");
+ eap_sm_abort(sm);
+ tls_deinit(sm->ssl_ctx);
+ os_free(sm);
+}
+
+
+/**
+ * eap_peer_sm_step - Step EAP peer state machine
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: 1 if EAP state was changed or 0 if not
+ *
+ * This function advances EAP state machine to a new state to match with the
+ * current variables. This should be called whenever variables used by the EAP
+ * state machine have changed.
+ */
+int eap_peer_sm_step(struct eap_sm *sm)
+{
+ int res = 0;
+ do {
+ sm->changed = FALSE;
+ SM_STEP_RUN(EAP);
+ if (sm->changed)
+ res = 1;
+ } while (sm->changed);
+ return res;
+}
+
+
+/**
+ * eap_sm_abort - Abort EAP authentication
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * Release system resources that have been allocated for the authentication
+ * session without fully deinitializing the EAP state machine.
+ */
+void eap_sm_abort(struct eap_sm *sm)
+{
+ wpabuf_free(sm->lastRespData);
+ sm->lastRespData = NULL;
+ wpabuf_free(sm->eapRespData);
+ sm->eapRespData = NULL;
+ os_free(sm->eapKeyData);
+ sm->eapKeyData = NULL;
+
+ /* This is not clearly specified in the EAP statemachines draft, but
+ * it seems necessary to make sure that some of the EAPOL variables get
+ * cleared for the next authentication. */
+ eapol_set_bool(sm, EAPOL_eapSuccess, FALSE);
+}
+
+
+#ifdef CONFIG_CTRL_IFACE
+static const char * eap_sm_state_txt(int state)
+{
+ switch (state) {
+ case EAP_INITIALIZE:
+ return "INITIALIZE";
+ case EAP_DISABLED:
+ return "DISABLED";
+ case EAP_IDLE:
+ return "IDLE";
+ case EAP_RECEIVED:
+ return "RECEIVED";
+ case EAP_GET_METHOD:
+ return "GET_METHOD";
+ case EAP_METHOD:
+ return "METHOD";
+ case EAP_SEND_RESPONSE:
+ return "SEND_RESPONSE";
+ case EAP_DISCARD:
+ return "DISCARD";
+ case EAP_IDENTITY:
+ return "IDENTITY";
+ case EAP_NOTIFICATION:
+ return "NOTIFICATION";
+ case EAP_RETRANSMIT:
+ return "RETRANSMIT";
+ case EAP_SUCCESS:
+ return "SUCCESS";
+ case EAP_FAILURE:
+ return "FAILURE";
+ default:
+ return "UNKNOWN";
+ }
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
+static const char * eap_sm_method_state_txt(EapMethodState state)
+{
+ switch (state) {
+ case METHOD_NONE:
+ return "NONE";
+ case METHOD_INIT:
+ return "INIT";
+ case METHOD_CONT:
+ return "CONT";
+ case METHOD_MAY_CONT:
+ return "MAY_CONT";
+ case METHOD_DONE:
+ return "DONE";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+
+static const char * eap_sm_decision_txt(EapDecision decision)
+{
+ switch (decision) {
+ case DECISION_FAIL:
+ return "FAIL";
+ case DECISION_COND_SUCC:
+ return "COND_SUCC";
+ case DECISION_UNCOND_SUCC:
+ return "UNCOND_SUCC";
+ default:
+ return "UNKNOWN";
+ }
+}
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+
+
+#ifdef CONFIG_CTRL_IFACE
+
+/**
+ * eap_sm_get_status - Get EAP state machine status
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @buf: Buffer for status information
+ * @buflen: Maximum buffer length
+ * @verbose: Whether to include verbose status information
+ * Returns: Number of bytes written to buf.
+ *
+ * Query EAP state machine for status information. This function fills in a
+ * text area with current status information from the EAPOL state machine. If
+ * the buffer (buf) is not large enough, status information will be truncated
+ * to fit the buffer.
+ */
+int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose)
+{
+ int len, ret;
+
+ if (sm == NULL)
+ return 0;
+
+ len = os_snprintf(buf, buflen,
+ "EAP state=%s\n",
+ eap_sm_state_txt(sm->EAP_state));
+ if (len < 0 || (size_t) len >= buflen)
+ return 0;
+
+ if (sm->selectedMethod != EAP_TYPE_NONE) {
+ const char *name;
+ if (sm->m) {
+ name = sm->m->name;
+ } else {
+ const struct eap_method *m =
+ eap_peer_get_eap_method(EAP_VENDOR_IETF,
+ sm->selectedMethod);
+ if (m)
+ name = m->name;
+ else
+ name = "?";
+ }
+ ret = os_snprintf(buf + len, buflen - len,
+ "selectedMethod=%d (EAP-%s)\n",
+ sm->selectedMethod, name);
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ len += ret;
+
+ if (sm->m && sm->m->get_status) {
+ len += sm->m->get_status(sm, sm->eap_method_priv,
+ buf + len, buflen - len,
+ verbose);
+ }
+ }
+
+ if (verbose) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "reqMethod=%d\n"
+ "methodState=%s\n"
+ "decision=%s\n"
+ "ClientTimeout=%d\n",
+ sm->reqMethod,
+ eap_sm_method_state_txt(sm->methodState),
+ eap_sm_decision_txt(sm->decision),
+ sm->ClientTimeout);
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ len += ret;
+ }
+
+ return len;
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
+typedef enum {
+ TYPE_IDENTITY, TYPE_PASSWORD, TYPE_OTP, TYPE_PIN, TYPE_NEW_PASSWORD,
+ TYPE_PASSPHRASE
+} eap_ctrl_req_type;
+
+static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type,
+ const char *msg, size_t msglen)
+{
+ struct eap_peer_config *config;
+ char *field, *txt, *tmp;
+
+ if (sm == NULL)
+ return;
+ config = eap_get_config(sm);
+ if (config == NULL)
+ return;
+
+ switch (type) {
+ case TYPE_IDENTITY:
+ field = "IDENTITY";
+ txt = "Identity";
+ config->pending_req_identity++;
+ break;
+ case TYPE_PASSWORD:
+ field = "PASSWORD";
+ txt = "Password";
+ config->pending_req_password++;
+ break;
+ case TYPE_NEW_PASSWORD:
+ field = "NEW_PASSWORD";
+ txt = "New Password";
+ config->pending_req_new_password++;
+ break;
+ case TYPE_PIN:
+ field = "PIN";
+ txt = "PIN";
+ config->pending_req_pin++;
+ break;
+ case TYPE_OTP:
+ field = "OTP";
+ if (msg) {
+ tmp = os_malloc(msglen + 3);
+ if (tmp == NULL)
+ return;
+ tmp[0] = '[';
+ os_memcpy(tmp + 1, msg, msglen);
+ tmp[msglen + 1] = ']';
+ tmp[msglen + 2] = '\0';
+ txt = tmp;
+ os_free(config->pending_req_otp);
+ config->pending_req_otp = tmp;
+ config->pending_req_otp_len = msglen + 3;
+ } else {
+ if (config->pending_req_otp == NULL)
+ return;
+ txt = config->pending_req_otp;
+ }
+ break;
+ case TYPE_PASSPHRASE:
+ field = "PASSPHRASE";
+ txt = "Private key passphrase";
+ config->pending_req_passphrase++;
+ break;
+ default:
+ return;
+ }
+
+ if (sm->eapol_cb->eap_param_needed)
+ sm->eapol_cb->eap_param_needed(sm->eapol_ctx, field, txt);
+}
+#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+#define eap_sm_request(sm, type, msg, msglen) do { } while (0)
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+
+
+/**
+ * eap_sm_request_identity - Request identity from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * EAP methods can call this function to request identity information for the
+ * current network. This is normally called when the identity is not included
+ * in the network configuration. The request will be sent to monitor programs
+ * through the control interface.
+ */
+void eap_sm_request_identity(struct eap_sm *sm)
+{
+ eap_sm_request(sm, TYPE_IDENTITY, NULL, 0);
+}
+
+
+/**
+ * eap_sm_request_password - Request password from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * EAP methods can call this function to request password information for the
+ * current network. This is normally called when the password is not included
+ * in the network configuration. The request will be sent to monitor programs
+ * through the control interface.
+ */
+void eap_sm_request_password(struct eap_sm *sm)
+{
+ eap_sm_request(sm, TYPE_PASSWORD, NULL, 0);
+}
+
+
+/**
+ * eap_sm_request_new_password - Request new password from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * EAP methods can call this function to request new password information for
+ * the current network. This is normally called when the EAP method indicates
+ * that the current password has expired and password change is required. The
+ * request will be sent to monitor programs through the control interface.
+ */
+void eap_sm_request_new_password(struct eap_sm *sm)
+{
+ eap_sm_request(sm, TYPE_NEW_PASSWORD, NULL, 0);
+}
+
+
+/**
+ * eap_sm_request_pin - Request SIM or smart card PIN from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * EAP methods can call this function to request SIM or smart card PIN
+ * information for the current network. This is normally called when the PIN is
+ * not included in the network configuration. The request will be sent to
+ * monitor programs through the control interface.
+ */
+void eap_sm_request_pin(struct eap_sm *sm)
+{
+ eap_sm_request(sm, TYPE_PIN, NULL, 0);
+}
+
+
+/**
+ * eap_sm_request_otp - Request one time password from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @msg: Message to be displayed to the user when asking for OTP
+ * @msg_len: Length of the user displayable message
+ *
+ * EAP methods can call this function to request open time password (OTP) for
+ * the current network. The request will be sent to monitor programs through
+ * the control interface.
+ */
+void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len)
+{
+ eap_sm_request(sm, TYPE_OTP, msg, msg_len);
+}
+
+
+/**
+ * eap_sm_request_passphrase - Request passphrase from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * EAP methods can call this function to request passphrase for a private key
+ * for the current network. This is normally called when the passphrase is not
+ * included in the network configuration. The request will be sent to monitor
+ * programs through the control interface.
+ */
+void eap_sm_request_passphrase(struct eap_sm *sm)
+{
+ eap_sm_request(sm, TYPE_PASSPHRASE, NULL, 0);
+}
+
+
+/**
+ * eap_sm_notify_ctrl_attached - Notification of attached monitor
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * Notify EAP state machines that a monitor was attached to the control
+ * interface to trigger re-sending of pending requests for user input.
+ */
+void eap_sm_notify_ctrl_attached(struct eap_sm *sm)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ if (config == NULL)
+ return;
+
+ /* Re-send any pending requests for user data since a new control
+ * interface was added. This handles cases where the EAP authentication
+ * starts immediately after system startup when the user interface is
+ * not yet running. */
+ if (config->pending_req_identity)
+ eap_sm_request_identity(sm);
+ if (config->pending_req_password)
+ eap_sm_request_password(sm);
+ if (config->pending_req_new_password)
+ eap_sm_request_new_password(sm);
+ if (config->pending_req_otp)
+ eap_sm_request_otp(sm, NULL, 0);
+ if (config->pending_req_pin)
+ eap_sm_request_pin(sm);
+ if (config->pending_req_passphrase)
+ eap_sm_request_passphrase(sm);
+}
+
+
+static int eap_allowed_phase2_type(int vendor, int type)
+{
+ if (vendor != EAP_VENDOR_IETF)
+ return 0;
+ return type != EAP_TYPE_PEAP && type != EAP_TYPE_TTLS &&
+ type != EAP_TYPE_FAST;
+}
+
+
+/**
+ * eap_get_phase2_type - Get EAP type for the given EAP phase 2 method name
+ * @name: EAP method name, e.g., MD5
+ * @vendor: Buffer for returning EAP Vendor-Id
+ * Returns: EAP method type or %EAP_TYPE_NONE if not found
+ *
+ * This function maps EAP type names into EAP type numbers that are allowed for
+ * Phase 2, i.e., for tunneled authentication. Phase 2 is used, e.g., with
+ * EAP-PEAP, EAP-TTLS, and EAP-FAST.
+ */
+u32 eap_get_phase2_type(const char *name, int *vendor)
+{
+ int v;
+ u8 type = eap_peer_get_type(name, &v);
+ if (eap_allowed_phase2_type(v, type)) {
+ *vendor = v;
+ return type;
+ }
+ *vendor = EAP_VENDOR_IETF;
+ return EAP_TYPE_NONE;
+}
+
+
+/**
+ * eap_get_phase2_types - Get list of allowed EAP phase 2 types
+ * @config: Pointer to a network configuration
+ * @count: Pointer to a variable to be filled with number of returned EAP types
+ * Returns: Pointer to allocated type list or %NULL on failure
+ *
+ * This function generates an array of allowed EAP phase 2 (tunneled) types for
+ * the given network configuration.
+ */
+struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config,
+ size_t *count)
+{
+ struct eap_method_type *buf;
+ u32 method;
+ int vendor;
+ size_t mcount;
+ const struct eap_method *methods, *m;
+
+ methods = eap_peer_get_methods(&mcount);
+ if (methods == NULL)
+ return NULL;
+ *count = 0;
+ buf = os_malloc(mcount * sizeof(struct eap_method_type));
+ if (buf == NULL)
+ return NULL;
+
+ for (m = methods; m; m = m->next) {
+ vendor = m->vendor;
+ method = m->method;
+ if (eap_allowed_phase2_type(vendor, method)) {
+ if (vendor == EAP_VENDOR_IETF &&
+ method == EAP_TYPE_TLS && config &&
+ config->private_key2 == NULL)
+ continue;
+ buf[*count].vendor = vendor;
+ buf[*count].method = method;
+ (*count)++;
+ }
+ }
+
+ return buf;
+}
+
+
+/**
+ * eap_set_fast_reauth - Update fast_reauth setting
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @enabled: 1 = Fast reauthentication is enabled, 0 = Disabled
+ */
+void eap_set_fast_reauth(struct eap_sm *sm, int enabled)
+{
+ sm->fast_reauth = enabled;
+}
+
+
+/**
+ * eap_set_workaround - Update EAP workarounds setting
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @workaround: 1 = Enable EAP workarounds, 0 = Disable EAP workarounds
+ */
+void eap_set_workaround(struct eap_sm *sm, unsigned int workaround)
+{
+ sm->workaround = workaround;
+}
+
+
+/**
+ * eap_get_config - Get current network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: Pointer to the current network configuration or %NULL if not found
+ *
+ * EAP peer methods should avoid using this function if they can use other
+ * access functions, like eap_get_config_identity() and
+ * eap_get_config_password(), that do not require direct access to
+ * struct eap_peer_config.
+ */
+struct eap_peer_config * eap_get_config(struct eap_sm *sm)
+{
+ return sm->eapol_cb->get_config(sm->eapol_ctx);
+}
+
+
+/**
+ * eap_get_config_identity - Get identity from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Buffer for the length of the identity
+ * Returns: Pointer to the identity or %NULL if not found
+ */
+const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config == NULL)
+ return NULL;
+ *len = config->identity_len;
+ return config->identity;
+}
+
+
+/**
+ * eap_get_config_password - Get password from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Buffer for the length of the password
+ * Returns: Pointer to the password or %NULL if not found
+ */
+const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config == NULL)
+ return NULL;
+ *len = config->password_len;
+ return config->password;
+}
+
+
+/**
+ * eap_get_config_password2 - Get password from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Buffer for the length of the password
+ * @hash: Buffer for returning whether the password is stored as a
+ * NtPasswordHash instead of plaintext password; can be %NULL if this
+ * information is not needed
+ * Returns: Pointer to the password or %NULL if not found
+ */
+const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config == NULL)
+ return NULL;
+ *len = config->password_len;
+ if (hash)
+ *hash = !!(config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH);
+ return config->password;
+}
+
+
+/**
+ * eap_get_config_new_password - Get new password from network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Buffer for the length of the new password
+ * Returns: Pointer to the new password or %NULL if not found
+ */
+const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config == NULL)
+ return NULL;
+ *len = config->new_password_len;
+ return config->new_password;
+}
+
+
+/**
+ * eap_get_config_otp - Get one-time password from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Buffer for the length of the one-time password
+ * Returns: Pointer to the one-time password or %NULL if not found
+ */
+const u8 * eap_get_config_otp(struct eap_sm *sm, size_t *len)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config == NULL)
+ return NULL;
+ *len = config->otp_len;
+ return config->otp;
+}
+
+
+/**
+ * eap_clear_config_otp - Clear used one-time password
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * This function clears a used one-time password (OTP) from the current network
+ * configuration. This should be called when the OTP has been used and is not
+ * needed anymore.
+ */
+void eap_clear_config_otp(struct eap_sm *sm)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config == NULL)
+ return;
+ os_memset(config->otp, 0, config->otp_len);
+ os_free(config->otp);
+ config->otp = NULL;
+ config->otp_len = 0;
+}
+
+
+/**
+ * eap_get_config_phase1 - Get phase1 data from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: Pointer to the phase1 data or %NULL if not found
+ */
+const char * eap_get_config_phase1(struct eap_sm *sm)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config == NULL)
+ return NULL;
+ return config->phase1;
+}
+
+
+/**
+ * eap_get_config_phase2 - Get phase2 data from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: Pointer to the phase1 data or %NULL if not found
+ */
+const char * eap_get_config_phase2(struct eap_sm *sm)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config == NULL)
+ return NULL;
+ return config->phase2;
+}
+
+
+/**
+ * eap_key_available - Get key availability (eapKeyAvailable variable)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: 1 if EAP keying material is available, 0 if not
+ */
+int eap_key_available(struct eap_sm *sm)
+{
+ return sm ? sm->eapKeyAvailable : 0;
+}
+
+
+/**
+ * eap_notify_success - Notify EAP state machine about external success trigger
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * This function is called when external event, e.g., successful completion of
+ * WPA-PSK key handshake, is indicating that EAP state machine should move to
+ * success state. This is mainly used with security modes that do not use EAP
+ * state machine (e.g., WPA-PSK).
+ */
+void eap_notify_success(struct eap_sm *sm)
+{
+ if (sm) {
+ sm->decision = DECISION_COND_SUCC;
+ sm->EAP_state = EAP_SUCCESS;
+ }
+}
+
+
+/**
+ * eap_notify_lower_layer_success - Notification of lower layer success
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * Notify EAP state machines that a lower layer has detected a successful
+ * authentication. This is used to recover from dropped EAP-Success messages.
+ */
+void eap_notify_lower_layer_success(struct eap_sm *sm)
+{
+ if (sm == NULL)
+ return;
+
+ if (eapol_get_bool(sm, EAPOL_eapSuccess) ||
+ sm->decision == DECISION_FAIL ||
+ (sm->methodState != METHOD_MAY_CONT &&
+ sm->methodState != METHOD_DONE))
+ return;
+
+ if (sm->eapKeyData != NULL)
+ sm->eapKeyAvailable = TRUE;
+ eapol_set_bool(sm, EAPOL_eapSuccess, TRUE);
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
+ "EAP authentication completed successfully (based on lower "
+ "layer success)");
+}
+
+
+/**
+ * eap_get_eapKeyData - Get master session key (MSK) from EAP state machine
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Pointer to variable that will be set to number of bytes in the key
+ * Returns: Pointer to the EAP keying data or %NULL on failure
+ *
+ * Fetch EAP keying material (MSK, eapKeyData) from the EAP state machine. The
+ * key is available only after a successful authentication. EAP state machine
+ * continues to manage the key data and the caller must not change or free the
+ * returned data.
+ */
+const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len)
+{
+ if (sm == NULL || sm->eapKeyData == NULL) {
+ *len = 0;
+ return NULL;
+ }
+
+ *len = sm->eapKeyDataLen;
+ return sm->eapKeyData;
+}
+
+
+/**
+ * eap_get_eapKeyData - Get EAP response data
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: Pointer to the EAP response (eapRespData) or %NULL on failure
+ *
+ * Fetch EAP response (eapRespData) from the EAP state machine. This data is
+ * available when EAP state machine has processed an incoming EAP request. The
+ * EAP state machine does not maintain a reference to the response after this
+ * function is called and the caller is responsible for freeing the data.
+ */
+struct wpabuf * eap_get_eapRespData(struct eap_sm *sm)
+{
+ struct wpabuf *resp;
+
+ if (sm == NULL || sm->eapRespData == NULL)
+ return NULL;
+
+ resp = sm->eapRespData;
+ sm->eapRespData = NULL;
+
+ return resp;
+}
+
+
+/**
+ * eap_sm_register_scard_ctx - Notification of smart card context
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @ctx: Context data for smart card operations
+ *
+ * Notify EAP state machines of context data for smart card operations. This
+ * context data will be used as a parameter for scard_*() functions.
+ */
+void eap_register_scard_ctx(struct eap_sm *sm, void *ctx)
+{
+ if (sm)
+ sm->scard_ctx = ctx;
+}
+
+
+/**
+ * eap_set_config_blob - Set or add a named configuration blob
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @blob: New value for the blob
+ *
+ * Adds a new configuration blob or replaces the current value of an existing
+ * blob.
+ */
+void eap_set_config_blob(struct eap_sm *sm, struct wpa_config_blob *blob)
+{
+#ifndef CONFIG_NO_CONFIG_BLOBS
+ sm->eapol_cb->set_config_blob(sm->eapol_ctx, blob);
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+}
+
+
+/**
+ * eap_get_config_blob - Get a named configuration blob
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @name: Name of the blob
+ * Returns: Pointer to blob data or %NULL if not found
+ */
+const struct wpa_config_blob * eap_get_config_blob(struct eap_sm *sm,
+ const char *name)
+{
+#ifndef CONFIG_NO_CONFIG_BLOBS
+ return sm->eapol_cb->get_config_blob(sm->eapol_ctx, name);
+#else /* CONFIG_NO_CONFIG_BLOBS */
+ return NULL;
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+}
+
+
+/**
+ * eap_set_force_disabled - Set force_disabled flag
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @disabled: 1 = EAP disabled, 0 = EAP enabled
+ *
+ * This function is used to force EAP state machine to be disabled when it is
+ * not in use (e.g., with WPA-PSK or plaintext connections).
+ */
+void eap_set_force_disabled(struct eap_sm *sm, int disabled)
+{
+ sm->force_disabled = disabled;
+}
+
+
+ /**
+ * eap_notify_pending - Notify that EAP method is ready to re-process a request
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * An EAP method can perform a pending operation (e.g., to get a response from
+ * an external process). Once the response is available, this function can be
+ * used to request EAPOL state machine to retry delivering the previously
+ * received (and still unanswered) EAP request to EAP state machine.
+ */
+void eap_notify_pending(struct eap_sm *sm)
+{
+ sm->eapol_cb->notify_pending(sm->eapol_ctx);
+}
+
+
+/**
+ * eap_invalidate_cached_session - Mark cached session data invalid
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ */
+void eap_invalidate_cached_session(struct eap_sm *sm)
+{
+ if (sm)
+ eap_deinit_prev_method(sm, "invalidate");
+}
diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h
new file mode 100644
index 0000000..d3db7d6
--- /dev/null
+++ b/src/eap_peer/eap.h
@@ -0,0 +1,288 @@
+/*
+ * EAP peer state machine functions (RFC 4137)
+ * Copyright (c) 2004-2007, 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.
+ */
+
+#ifndef EAP_H
+#define EAP_H
+
+#include "defs.h"
+#include "eap_common/eap_defs.h"
+#include "eap_peer/eap_methods.h"
+
+struct eap_sm;
+struct wpa_config_blob;
+struct wpabuf;
+
+struct eap_method_type {
+ int vendor;
+ u32 method;
+};
+
+#ifdef IEEE8021X_EAPOL
+
+/**
+ * enum eapol_bool_var - EAPOL boolean state variables for EAP state machine
+ *
+ * These variables are used in the interface between EAP peer state machine and
+ * lower layer. These are defined in RFC 4137, Sect. 4.1. Lower layer code is
+ * expected to maintain these variables and register a callback functions for
+ * EAP state machine to get and set the variables.
+ */
+enum eapol_bool_var {
+ /**
+ * EAPOL_eapSuccess - EAP SUCCESS state reached
+ *
+ * EAP state machine reads and writes this value.
+ */
+ EAPOL_eapSuccess,
+
+ /**
+ * EAPOL_eapRestart - Lower layer request to restart authentication
+ *
+ * Set to TRUE in lower layer, FALSE in EAP state machine.
+ */
+ EAPOL_eapRestart,
+
+ /**
+ * EAPOL_eapFail - EAP FAILURE state reached
+ *
+ * EAP state machine writes this value.
+ */
+ EAPOL_eapFail,
+
+ /**
+ * EAPOL_eapResp - Response to send
+ *
+ * Set to TRUE in EAP state machine, FALSE in lower layer.
+ */
+ EAPOL_eapResp,
+
+ /**
+ * EAPOL_eapNoResp - Request has been process; no response to send
+ *
+ * Set to TRUE in EAP state machine, FALSE in lower layer.
+ */
+ EAPOL_eapNoResp,
+
+ /**
+ * EAPOL_eapReq - EAP request available from lower layer
+ *
+ * Set to TRUE in lower layer, FALSE in EAP state machine.
+ */
+ EAPOL_eapReq,
+
+ /**
+ * EAPOL_portEnabled - Lower layer is ready for communication
+ *
+ * EAP state machines reads this value.
+ */
+ EAPOL_portEnabled,
+
+ /**
+ * EAPOL_altAccept - Alternate indication of success (RFC3748)
+ *
+ * EAP state machines reads this value.
+ */
+ EAPOL_altAccept,
+
+ /**
+ * EAPOL_altReject - Alternate indication of failure (RFC3748)
+ *
+ * EAP state machines reads this value.
+ */
+ EAPOL_altReject
+};
+
+/**
+ * enum eapol_int_var - EAPOL integer state variables for EAP state machine
+ *
+ * These variables are used in the interface between EAP peer state machine and
+ * lower layer. These are defined in RFC 4137, Sect. 4.1. Lower layer code is
+ * expected to maintain these variables and register a callback functions for
+ * EAP state machine to get and set the variables.
+ */
+enum eapol_int_var {
+ /**
+ * EAPOL_idleWhile - Outside time for EAP peer timeout
+ *
+ * This integer variable is used to provide an outside timer that the
+ * external (to EAP state machine) code must decrement by one every
+ * second until the value reaches zero. This is used in the same way as
+ * EAPOL state machine timers. EAP state machine reads and writes this
+ * value.
+ */
+ EAPOL_idleWhile
+};
+
+/**
+ * struct eapol_callbacks - Callback functions from EAP to lower layer
+ *
+ * This structure defines the callback functions that EAP state machine
+ * requires from the lower layer (usually EAPOL state machine) for updating
+ * state variables and requesting information. eapol_ctx from
+ * eap_peer_sm_init() call will be used as the ctx parameter for these
+ * callback functions.
+ */
+struct eapol_callbacks {
+ /**
+ * get_config - Get pointer to the current network configuration
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ */
+ struct eap_peer_config * (*get_config)(void *ctx);
+
+ /**
+ * get_bool - Get a boolean EAPOL state variable
+ * @variable: EAPOL boolean variable to get
+ * Returns: Value of the EAPOL variable
+ */
+ Boolean (*get_bool)(void *ctx, enum eapol_bool_var variable);
+
+ /**
+ * set_bool - Set a boolean EAPOL state variable
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @variable: EAPOL boolean variable to set
+ * @value: Value for the EAPOL variable
+ */
+ void (*set_bool)(void *ctx, enum eapol_bool_var variable,
+ Boolean value);
+
+ /**
+ * get_int - Get an integer EAPOL state variable
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @variable: EAPOL integer variable to get
+ * Returns: Value of the EAPOL variable
+ */
+ unsigned int (*get_int)(void *ctx, enum eapol_int_var variable);
+
+ /**
+ * set_int - Set an integer EAPOL state variable
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @variable: EAPOL integer variable to set
+ * @value: Value for the EAPOL variable
+ */
+ void (*set_int)(void *ctx, enum eapol_int_var variable,
+ unsigned int value);
+
+ /**
+ * get_eapReqData - Get EAP-Request data
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @len: Pointer to variable that will be set to eapReqDataLen
+ * Returns: Reference to eapReqData (EAP state machine will not free
+ * this) or %NULL if eapReqData not available.
+ */
+ struct wpabuf * (*get_eapReqData)(void *ctx);
+
+ /**
+ * set_config_blob - Set named configuration blob
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @blob: New value for the blob
+ *
+ * Adds a new configuration blob or replaces the current value of an
+ * existing blob.
+ */
+ void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob);
+
+ /**
+ * get_config_blob - Get a named configuration blob
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @name: Name of the blob
+ * Returns: Pointer to blob data or %NULL if not found
+ */
+ const struct wpa_config_blob * (*get_config_blob)(void *ctx,
+ const char *name);
+
+ /**
+ * notify_pending - Notify that a pending request can be retried
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ *
+ * An EAP method can perform a pending operation (e.g., to get a
+ * response from an external process). Once the response is available,
+ * this callback function can be used to request EAPOL state machine to
+ * retry delivering the previously received (and still unanswered) EAP
+ * request to EAP state machine.
+ */
+ void (*notify_pending)(void *ctx);
+
+ /**
+ * eap_param_needed - Notify that EAP parameter is needed
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @field: Field name (e.g., "IDENTITY")
+ * @txt: User readable text describing the required parameter
+ */
+ void (*eap_param_needed)(void *ctx, const char *field,
+ const char *txt);
+};
+
+/**
+ * struct eap_config - Configuration for EAP state machine
+ */
+struct eap_config {
+ /**
+ * opensc_engine_path - OpenSC engine for OpenSSL engine support
+ *
+ * Usually, path to engine_opensc.so.
+ */
+ const char *opensc_engine_path;
+ /**
+ * pkcs11_engine_path - PKCS#11 engine for OpenSSL engine support
+ *
+ * Usually, path to engine_pkcs11.so.
+ */
+ const char *pkcs11_engine_path;
+ /**
+ * pkcs11_module_path - OpenSC PKCS#11 module for OpenSSL engine
+ *
+ * Usually, path to opensc-pkcs11.so.
+ */
+ const char *pkcs11_module_path;
+ /**
+ * mac_addr - MAC address of the peer
+ *
+ * This can be left %NULL if not available.
+ */
+ const u8 *mac_addr;
+};
+
+struct eap_sm * eap_peer_sm_init(void *eapol_ctx,
+ struct eapol_callbacks *eapol_cb,
+ void *msg_ctx, struct eap_config *conf);
+void eap_peer_sm_deinit(struct eap_sm *sm);
+int eap_peer_sm_step(struct eap_sm *sm);
+void eap_sm_abort(struct eap_sm *sm);
+int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen,
+ int verbose);
+struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted);
+void eap_sm_request_identity(struct eap_sm *sm);
+void eap_sm_request_password(struct eap_sm *sm);
+void eap_sm_request_new_password(struct eap_sm *sm);
+void eap_sm_request_pin(struct eap_sm *sm);
+void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len);
+void eap_sm_request_passphrase(struct eap_sm *sm);
+void eap_sm_notify_ctrl_attached(struct eap_sm *sm);
+u32 eap_get_phase2_type(const char *name, int *vendor);
+struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config,
+ size_t *count);
+void eap_set_fast_reauth(struct eap_sm *sm, int enabled);
+void eap_set_workaround(struct eap_sm *sm, unsigned int workaround);
+void eap_set_force_disabled(struct eap_sm *sm, int disabled);
+int eap_key_available(struct eap_sm *sm);
+void eap_notify_success(struct eap_sm *sm);
+void eap_notify_lower_layer_success(struct eap_sm *sm);
+const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len);
+struct wpabuf * eap_get_eapRespData(struct eap_sm *sm);
+void eap_register_scard_ctx(struct eap_sm *sm, void *ctx);
+void eap_invalidate_cached_session(struct eap_sm *sm);
+
+#endif /* IEEE8021X_EAPOL */
+
+#endif /* EAP_H */
diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c
new file mode 100644
index 0000000..304e20a
--- /dev/null
+++ b/src/eap_peer/eap_aka.c
@@ -0,0 +1,1097 @@
+/*
+ * EAP peer method: EAP-AKA (RFC 4187)
+ * Copyright (c) 2004-2007, 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_peer/eap_i.h"
+#include "pcsc_funcs.h"
+#include "eap_common/eap_sim_common.h"
+#include "sha1.h"
+#include "crypto.h"
+
+
+struct eap_aka_data {
+ u8 ik[EAP_AKA_IK_LEN], ck[EAP_AKA_CK_LEN], res[EAP_AKA_RES_MAX_LEN];
+ size_t res_len;
+ u8 nonce_s[EAP_SIM_NONCE_S_LEN];
+ u8 mk[EAP_SIM_MK_LEN];
+ u8 k_aut[EAP_SIM_K_AUT_LEN];
+ u8 k_encr[EAP_SIM_K_ENCR_LEN];
+ u8 msk[EAP_SIM_KEYING_DATA_LEN];
+ u8 emsk[EAP_EMSK_LEN];
+ u8 rand[EAP_AKA_RAND_LEN], autn[EAP_AKA_AUTN_LEN];
+ u8 auts[EAP_AKA_AUTS_LEN];
+
+ int num_id_req, num_notification;
+ u8 *pseudonym;
+ size_t pseudonym_len;
+ u8 *reauth_id;
+ size_t reauth_id_len;
+ int reauth;
+ unsigned int counter, counter_too_small;
+ u8 *last_eap_identity;
+ size_t last_eap_identity_len;
+ enum {
+ CONTINUE, RESULT_SUCCESS, RESULT_FAILURE, SUCCESS, FAILURE
+ } state;
+
+ struct wpabuf *id_msgs;
+ int prev_id;
+ int result_ind, use_result_ind;
+};
+
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static const char * eap_aka_state_txt(int state)
+{
+ switch (state) {
+ case CONTINUE:
+ return "CONTINUE";
+ case RESULT_SUCCESS:
+ return "RESULT_SUCCESS";
+ case RESULT_FAILURE:
+ return "RESULT_FAILURE";
+ case SUCCESS:
+ return "SUCCESS";
+ case FAILURE:
+ return "FAILURE";
+ default:
+ return "?";
+ }
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+static void eap_aka_state(struct eap_aka_data *data, int state)
+{
+ wpa_printf(MSG_DEBUG, "EAP-AKA: %s -> %s",
+ eap_aka_state_txt(data->state),
+ eap_aka_state_txt(state));
+ data->state = state;
+}
+
+
+static void * eap_aka_init(struct eap_sm *sm)
+{
+ struct eap_aka_data *data;
+ const char *phase1 = eap_get_config_phase1(sm);
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+
+ eap_aka_state(data, CONTINUE);
+ data->prev_id = -1;
+
+ data->result_ind = phase1 && os_strstr(phase1, "result_ind=1") != NULL;
+
+ return data;
+}
+
+
+static void eap_aka_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_aka_data *data = priv;
+ if (data) {
+ os_free(data->pseudonym);
+ os_free(data->reauth_id);
+ os_free(data->last_eap_identity);
+ wpabuf_free(data->id_msgs);
+ os_free(data);
+ }
+}
+
+
+static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data)
+{
+ wpa_printf(MSG_DEBUG, "EAP-AKA: UMTS authentication algorithm");
+#ifdef PCSC_FUNCS
+ return scard_umts_auth(sm->scard_ctx, data->rand,
+ data->autn, data->res, &data->res_len,
+ data->ik, data->ck, data->auts);
+#else /* PCSC_FUNCS */
+ /* These hardcoded Kc and SRES values are used for testing.
+ * Could consider making them configurable. */
+ os_memset(data->res, '2', EAP_AKA_RES_MAX_LEN);
+ data->res_len = EAP_AKA_RES_MAX_LEN;
+ os_memset(data->ik, '3', EAP_AKA_IK_LEN);
+ os_memset(data->ck, '4', EAP_AKA_CK_LEN);
+ {
+ u8 autn[EAP_AKA_AUTN_LEN];
+ os_memset(autn, '1', EAP_AKA_AUTN_LEN);
+ if (os_memcmp(autn, data->autn, EAP_AKA_AUTN_LEN) != 0) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: AUTN did not match "
+ "with expected value");
+ return -1;
+ }
+ }
+#if 0
+ {
+ static int test_resync = 1;
+ if (test_resync) {
+ /* Test Resynchronization */
+ test_resync = 0;
+ return -2;
+ }
+ }
+#endif
+ return 0;
+#endif /* PCSC_FUNCS */
+}
+
+
+#define CLEAR_PSEUDONYM 0x01
+#define CLEAR_REAUTH_ID 0x02
+#define CLEAR_EAP_ID 0x04
+
+static void eap_aka_clear_identities(struct eap_aka_data *data, int id)
+{
+ wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old%s%s%s",
+ id & CLEAR_PSEUDONYM ? " pseudonym" : "",
+ id & CLEAR_REAUTH_ID ? " reauth_id" : "",
+ id & CLEAR_EAP_ID ? " eap_id" : "");
+ if (id & CLEAR_PSEUDONYM) {
+ os_free(data->pseudonym);
+ data->pseudonym = NULL;
+ data->pseudonym_len = 0;
+ }
+ if (id & CLEAR_REAUTH_ID) {
+ os_free(data->reauth_id);
+ data->reauth_id = NULL;
+ data->reauth_id_len = 0;
+ }
+ if (id & CLEAR_EAP_ID) {
+ os_free(data->last_eap_identity);
+ data->last_eap_identity = NULL;
+ data->last_eap_identity_len = 0;
+ }
+}
+
+
+static int eap_aka_learn_ids(struct eap_aka_data *data,
+ struct eap_sim_attrs *attr)
+{
+ if (attr->next_pseudonym) {
+ os_free(data->pseudonym);
+ data->pseudonym = os_malloc(attr->next_pseudonym_len);
+ if (data->pseudonym == NULL) {
+ wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for "
+ "next pseudonym");
+ return -1;
+ }
+ os_memcpy(data->pseudonym, attr->next_pseudonym,
+ attr->next_pseudonym_len);
+ data->pseudonym_len = attr->next_pseudonym_len;
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "EAP-AKA: (encr) AT_NEXT_PSEUDONYM",
+ data->pseudonym,
+ data->pseudonym_len);
+ }
+
+ if (attr->next_reauth_id) {
+ os_free(data->reauth_id);
+ data->reauth_id = os_malloc(attr->next_reauth_id_len);
+ if (data->reauth_id == NULL) {
+ wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for "
+ "next reauth_id");
+ return -1;
+ }
+ os_memcpy(data->reauth_id, attr->next_reauth_id,
+ attr->next_reauth_id_len);
+ data->reauth_id_len = attr->next_reauth_id_len;
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "EAP-AKA: (encr) AT_NEXT_REAUTH_ID",
+ data->reauth_id,
+ data->reauth_id_len);
+ }
+
+ return 0;
+}
+
+
+static int eap_aka_add_id_msg(struct eap_aka_data *data,
+ const struct wpabuf *msg)
+{
+ if (msg == NULL)
+ return -1;
+
+ if (data->id_msgs == NULL) {
+ data->id_msgs = wpabuf_dup(msg);
+ return data->id_msgs == NULL ? -1 : 0;
+ }
+
+ if (wpabuf_resize(&data->id_msgs, wpabuf_len(msg)) < 0)
+ return -1;
+ wpabuf_put_buf(data->id_msgs, msg);
+
+ return 0;
+}
+
+
+static void eap_aka_add_checkcode(struct eap_aka_data *data,
+ struct eap_sim_msg *msg)
+{
+ const u8 *addr;
+ size_t len;
+ u8 hash[SHA1_MAC_LEN];
+
+ wpa_printf(MSG_DEBUG, " AT_CHECKCODE");
+
+ if (data->id_msgs == NULL) {
+ /*
+ * No EAP-AKA/Identity packets were exchanged - send empty
+ * checkcode.
+ */
+ eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, NULL, 0);
+ return;
+ }
+
+ /* Checkcode is SHA1 hash over all EAP-AKA/Identity packets. */
+ addr = wpabuf_head(data->id_msgs);
+ len = wpabuf_len(data->id_msgs);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-AKA: AT_CHECKCODE data", addr, len);
+ sha1_vector(1, &addr, &len, hash);
+
+ eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, hash,
+ EAP_AKA_CHECKCODE_LEN);
+}
+
+
+static int eap_aka_verify_checkcode(struct eap_aka_data *data,
+ const u8 *checkcode, size_t checkcode_len)
+{
+ const u8 *addr;
+ size_t len;
+ u8 hash[SHA1_MAC_LEN];
+
+ if (checkcode == NULL)
+ return -1;
+
+ if (data->id_msgs == NULL) {
+ if (checkcode_len != 0) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from server "
+ "indicates that AKA/Identity messages were "
+ "used, but they were not");
+ return -1;
+ }
+ return 0;
+ }
+
+ if (checkcode_len != EAP_AKA_CHECKCODE_LEN) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from server "
+ "indicates that AKA/Identity message were not "
+ "used, but they were");
+ return -1;
+ }
+
+ /* Checkcode is SHA1 hash over all EAP-AKA/Identity packets. */
+ addr = wpabuf_head(data->id_msgs);
+ len = wpabuf_len(data->id_msgs);
+ sha1_vector(1, &addr, &len, hash);
+
+ if (os_memcmp(hash, checkcode, EAP_AKA_CHECKCODE_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_aka_client_error(struct eap_aka_data *data, u8 id,
+ int err)
+{
+ struct eap_sim_msg *msg;
+
+ eap_aka_state(data, FAILURE);
+ data->num_id_req = 0;
+ data->num_notification = 0;
+
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_AKA,
+ EAP_AKA_SUBTYPE_CLIENT_ERROR);
+ eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0);
+ return eap_sim_msg_finish(msg, NULL, NULL, 0);
+}
+
+
+static struct wpabuf * eap_aka_authentication_reject(struct eap_aka_data *data,
+ u8 id)
+{
+ struct eap_sim_msg *msg;
+
+ eap_aka_state(data, FAILURE);
+ data->num_id_req = 0;
+ data->num_notification = 0;
+
+ wpa_printf(MSG_DEBUG, "Generating EAP-AKA Authentication-Reject "
+ "(id=%d)", id);
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_AKA,
+ EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT);
+ return eap_sim_msg_finish(msg, NULL, NULL, 0);
+}
+
+
+static struct wpabuf * eap_aka_synchronization_failure(
+ struct eap_aka_data *data, u8 id)
+{
+ struct eap_sim_msg *msg;
+
+ data->num_id_req = 0;
+ data->num_notification = 0;
+
+ wpa_printf(MSG_DEBUG, "Generating EAP-AKA Synchronization-Failure "
+ "(id=%d)", id);
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_AKA,
+ EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE);
+ wpa_printf(MSG_DEBUG, " AT_AUTS");
+ eap_sim_msg_add_full(msg, EAP_SIM_AT_AUTS, data->auts,
+ EAP_AKA_AUTS_LEN);
+ return eap_sim_msg_finish(msg, NULL, NULL, 0);
+}
+
+
+static struct wpabuf * eap_aka_response_identity(struct eap_sm *sm,
+ struct eap_aka_data *data,
+ u8 id,
+ enum eap_sim_id_req id_req)
+{
+ const u8 *identity = NULL;
+ size_t identity_len = 0;
+ struct eap_sim_msg *msg;
+
+ data->reauth = 0;
+ if (id_req == ANY_ID && data->reauth_id) {
+ identity = data->reauth_id;
+ identity_len = data->reauth_id_len;
+ data->reauth = 1;
+ } else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) &&
+ data->pseudonym) {
+ identity = data->pseudonym;
+ identity_len = data->pseudonym_len;
+ eap_aka_clear_identities(data, CLEAR_REAUTH_ID);
+ } else if (id_req != NO_ID_REQ) {
+ identity = eap_get_config_identity(sm, &identity_len);
+ if (identity) {
+ eap_aka_clear_identities(data, CLEAR_PSEUDONYM |
+ CLEAR_REAUTH_ID);
+ }
+ }
+ if (id_req != NO_ID_REQ)
+ eap_aka_clear_identities(data, CLEAR_EAP_ID);
+
+ wpa_printf(MSG_DEBUG, "Generating EAP-AKA Identity (id=%d)", id);
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_AKA,
+ EAP_AKA_SUBTYPE_IDENTITY);
+
+ if (identity) {
+ wpa_hexdump_ascii(MSG_DEBUG, " AT_IDENTITY",
+ identity, identity_len);
+ eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len,
+ identity, identity_len);
+ }
+
+ return eap_sim_msg_finish(msg, NULL, NULL, 0);
+}
+
+
+static struct wpabuf * eap_aka_response_challenge(struct eap_aka_data *data,
+ u8 id)
+{
+ struct eap_sim_msg *msg;
+
+ wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d)", id);
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_AKA,
+ EAP_AKA_SUBTYPE_CHALLENGE);
+ wpa_printf(MSG_DEBUG, " AT_RES");
+ eap_sim_msg_add(msg, EAP_SIM_AT_RES, data->res_len,
+ data->res, data->res_len);
+ eap_aka_add_checkcode(data, msg);
+ if (data->use_result_ind) {
+ wpa_printf(MSG_DEBUG, " AT_RESULT_IND");
+ eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
+ }
+ wpa_printf(MSG_DEBUG, " AT_MAC");
+ eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+ return eap_sim_msg_finish(msg, data->k_aut, (u8 *) "", 0);
+}
+
+
+static struct wpabuf * eap_aka_response_reauth(struct eap_aka_data *data,
+ u8 id, int counter_too_small,
+ const u8 *nonce_s)
+{
+ struct eap_sim_msg *msg;
+ unsigned int counter;
+
+ wpa_printf(MSG_DEBUG, "Generating EAP-AKA Reauthentication (id=%d)",
+ id);
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_AKA,
+ EAP_AKA_SUBTYPE_REAUTHENTICATION);
+ wpa_printf(MSG_DEBUG, " AT_IV");
+ wpa_printf(MSG_DEBUG, " AT_ENCR_DATA");
+ eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA);
+
+ if (counter_too_small) {
+ wpa_printf(MSG_DEBUG, " *AT_COUNTER_TOO_SMALL");
+ eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER_TOO_SMALL, 0, NULL, 0);
+ counter = data->counter_too_small;
+ } else
+ counter = data->counter;
+
+ wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", counter);
+ eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0);
+
+ if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt "
+ "AT_ENCR_DATA");
+ eap_sim_msg_free(msg);
+ return NULL;
+ }
+ eap_aka_add_checkcode(data, msg);
+ if (data->use_result_ind) {
+ wpa_printf(MSG_DEBUG, " AT_RESULT_IND");
+ eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
+ }
+ wpa_printf(MSG_DEBUG, " AT_MAC");
+ eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+ return eap_sim_msg_finish(msg, data->k_aut, nonce_s,
+ EAP_SIM_NONCE_S_LEN);
+}
+
+
+static struct wpabuf * eap_aka_response_notification(struct eap_aka_data *data,
+ u8 id, u16 notification)
+{
+ struct eap_sim_msg *msg;
+ u8 *k_aut = (notification & 0x4000) == 0 ? data->k_aut : NULL;
+
+ wpa_printf(MSG_DEBUG, "Generating EAP-AKA Notification (id=%d)", id);
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_AKA,
+ EAP_AKA_SUBTYPE_NOTIFICATION);
+ if (k_aut && data->reauth) {
+ wpa_printf(MSG_DEBUG, " AT_IV");
+ wpa_printf(MSG_DEBUG, " AT_ENCR_DATA");
+ eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV,
+ EAP_SIM_AT_ENCR_DATA);
+ wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", data->counter);
+ eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter,
+ NULL, 0);
+ if (eap_sim_msg_add_encr_end(msg, data->k_encr,
+ EAP_SIM_AT_PADDING)) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt "
+ "AT_ENCR_DATA");
+ eap_sim_msg_free(msg);
+ return NULL;
+ }
+ }
+ if (k_aut) {
+ wpa_printf(MSG_DEBUG, " AT_MAC");
+ eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+ }
+ return eap_sim_msg_finish(msg, k_aut, (u8 *) "", 0);
+}
+
+
+static struct wpabuf * eap_aka_process_identity(struct eap_sm *sm,
+ struct eap_aka_data *data,
+ u8 id,
+ const struct wpabuf *reqData,
+ struct eap_sim_attrs *attr)
+{
+ int id_error;
+ struct wpabuf *buf;
+
+ wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Identity");
+
+ id_error = 0;
+ switch (attr->id_req) {
+ case NO_ID_REQ:
+ break;
+ case ANY_ID:
+ if (data->num_id_req > 0)
+ id_error++;
+ data->num_id_req++;
+ break;
+ case FULLAUTH_ID:
+ if (data->num_id_req > 1)
+ id_error++;
+ data->num_id_req++;
+ break;
+ case PERMANENT_ID:
+ if (data->num_id_req > 2)
+ id_error++;
+ data->num_id_req++;
+ break;
+ }
+ if (id_error) {
+ wpa_printf(MSG_INFO, "EAP-AKA: Too many ID requests "
+ "used within one authentication");
+ return eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ buf = eap_aka_response_identity(sm, data, id, attr->id_req);
+
+ if (data->prev_id != id) {
+ eap_aka_add_id_msg(data, reqData);
+ eap_aka_add_id_msg(data, buf);
+ data->prev_id = id;
+ }
+
+ return buf;
+}
+
+
+static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm,
+ struct eap_aka_data *data,
+ u8 id,
+ const struct wpabuf *reqData,
+ struct eap_sim_attrs *attr)
+{
+ const u8 *identity;
+ size_t identity_len;
+ int res;
+ struct eap_sim_attrs eattr;
+
+ wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Challenge");
+
+ if (attr->checkcode &&
+ eap_aka_verify_checkcode(data, attr->checkcode,
+ attr->checkcode_len)) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the "
+ "message");
+ return eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ data->reauth = 0;
+ if (!attr->mac || !attr->rand || !attr->autn) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message "
+ "did not include%s%s%s",
+ !attr->mac ? " AT_MAC" : "",
+ !attr->rand ? " AT_RAND" : "",
+ !attr->autn ? " AT_AUTN" : "");
+ return eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+ os_memcpy(data->rand, attr->rand, EAP_AKA_RAND_LEN);
+ os_memcpy(data->autn, attr->autn, EAP_AKA_AUTN_LEN);
+
+ res = eap_aka_umts_auth(sm, data);
+ if (res == -1) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication "
+ "failed (AUTN)");
+ return eap_aka_authentication_reject(data, id);
+ } else if (res == -2) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication "
+ "failed (AUTN seq# -> AUTS)");
+ return eap_aka_synchronization_failure(data, id);
+ } else if (res) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication failed");
+ return eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+ if (data->last_eap_identity) {
+ identity = data->last_eap_identity;
+ identity_len = data->last_eap_identity_len;
+ } else if (data->pseudonym) {
+ identity = data->pseudonym;
+ identity_len = data->pseudonym_len;
+ } else
+ identity = eap_get_config_identity(sm, &identity_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Selected identity for MK "
+ "derivation", identity, identity_len);
+ eap_aka_derive_mk(identity, identity_len, data->ik, data->ck,
+ data->mk);
+ eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk,
+ data->emsk);
+ if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, (u8 *) "", 0))
+ {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message "
+ "used invalid AT_MAC");
+ return eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ /* Old reauthentication and pseudonym identities must not be used
+ * anymore. In other words, if no new identities are received, full
+ * authentication will be used on next reauthentication. */
+ eap_aka_clear_identities(data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID |
+ CLEAR_EAP_ID);
+
+ if (attr->encr_data) {
+ u8 *decrypted;
+ decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
+ attr->encr_data_len, attr->iv,
+ &eattr, 0);
+ if (decrypted == NULL) {
+ return eap_aka_client_error(
+ data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+ eap_aka_learn_ids(data, &eattr);
+ os_free(decrypted);
+ }
+
+ if (data->result_ind && attr->result_ind)
+ data->use_result_ind = 1;
+
+ if (data->state != FAILURE && data->state != RESULT_FAILURE) {
+ eap_aka_state(data, data->use_result_ind ?
+ RESULT_SUCCESS : SUCCESS);
+ }
+
+ data->num_id_req = 0;
+ data->num_notification = 0;
+ /* RFC 4187 specifies that counter is initialized to one after
+ * fullauth, but initializing it to zero makes it easier to implement
+ * reauth verification. */
+ data->counter = 0;
+ return eap_aka_response_challenge(data, id);
+}
+
+
+static int eap_aka_process_notification_reauth(struct eap_aka_data *data,
+ struct eap_sim_attrs *attr)
+{
+ struct eap_sim_attrs eattr;
+ u8 *decrypted;
+
+ if (attr->encr_data == NULL || attr->iv == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Notification message after "
+ "reauth did not include encrypted data");
+ return -1;
+ }
+
+ decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
+ attr->encr_data_len, attr->iv, &eattr,
+ 0);
+ if (decrypted == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted "
+ "data from notification message");
+ return -1;
+ }
+
+ if (eattr.counter < 0 || (size_t) eattr.counter != data->counter) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Counter in notification "
+ "message does not match with counter in reauth "
+ "message");
+ os_free(decrypted);
+ return -1;
+ }
+
+ os_free(decrypted);
+ return 0;
+}
+
+
+static int eap_aka_process_notification_auth(struct eap_aka_data *data,
+ const struct wpabuf *reqData,
+ struct eap_sim_attrs *attr)
+{
+ if (attr->mac == NULL) {
+ wpa_printf(MSG_INFO, "EAP-AKA: no AT_MAC in after_auth "
+ "Notification message");
+ return -1;
+ }
+
+ if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, (u8 *) "", 0))
+ {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Notification message "
+ "used invalid AT_MAC");
+ return -1;
+ }
+
+ if (data->reauth &&
+ eap_aka_process_notification_reauth(data, attr)) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Invalid notification "
+ "message after reauth");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_aka_process_notification(
+ struct eap_sm *sm, struct eap_aka_data *data, u8 id,
+ const struct wpabuf *reqData, struct eap_sim_attrs *attr)
+{
+ wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Notification");
+ if (data->num_notification > 0) {
+ wpa_printf(MSG_INFO, "EAP-AKA: too many notification "
+ "rounds (only one allowed)");
+ return eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+ data->num_notification++;
+ if (attr->notification == -1) {
+ wpa_printf(MSG_INFO, "EAP-AKA: no AT_NOTIFICATION in "
+ "Notification message");
+ return eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ if ((attr->notification & 0x4000) == 0 &&
+ eap_aka_process_notification_auth(data, reqData, attr)) {
+ return eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ eap_sim_report_notification(sm->msg_ctx, attr->notification, 1);
+ if (attr->notification >= 0 && attr->notification < 32768) {
+ eap_aka_state(data, FAILURE);
+ } else if (attr->notification == EAP_SIM_SUCCESS &&
+ data->state == RESULT_SUCCESS)
+ eap_aka_state(data, SUCCESS);
+ return eap_aka_response_notification(data, id, attr->notification);
+}
+
+
+static struct wpabuf * eap_aka_process_reauthentication(
+ struct eap_sm *sm, struct eap_aka_data *data, u8 id,
+ const struct wpabuf *reqData, struct eap_sim_attrs *attr)
+{
+ struct eap_sim_attrs eattr;
+ u8 *decrypted;
+
+ wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Reauthentication");
+
+ if (attr->checkcode &&
+ eap_aka_verify_checkcode(data, attr->checkcode,
+ attr->checkcode_len)) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the "
+ "message");
+ return eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ if (data->reauth_id == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Server is trying "
+ "reauthentication, but no reauth_id available");
+ return eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ data->reauth = 1;
+ if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, (u8 *) "", 0))
+ {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication "
+ "did not have valid AT_MAC");
+ return eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ if (attr->encr_data == NULL || attr->iv == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication "
+ "message did not include encrypted data");
+ return eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
+ attr->encr_data_len, attr->iv, &eattr,
+ 0);
+ if (decrypted == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted "
+ "data from reauthentication message");
+ return eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ if (eattr.nonce_s == NULL || eattr.counter < 0) {
+ wpa_printf(MSG_INFO, "EAP-AKA: (encr) No%s%s in reauth packet",
+ !eattr.nonce_s ? " AT_NONCE_S" : "",
+ eattr.counter < 0 ? " AT_COUNTER" : "");
+ os_free(decrypted);
+ return eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ if (eattr.counter < 0 || (size_t) eattr.counter <= data->counter) {
+ struct wpabuf *res;
+ wpa_printf(MSG_INFO, "EAP-AKA: (encr) Invalid counter "
+ "(%d <= %d)", eattr.counter, data->counter);
+ data->counter_too_small = eattr.counter;
+
+ eap_sim_derive_keys_reauth(eattr.counter, data->reauth_id,
+ data->reauth_id_len, eattr.nonce_s,
+ data->mk, NULL, NULL);
+
+ /* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current
+ * reauth_id must not be used to start a new reauthentication.
+ * However, since it was used in the last EAP-Response-Identity
+ * packet, it has to saved for the following fullauth to be
+ * used in MK derivation. */
+ os_free(data->last_eap_identity);
+ data->last_eap_identity = data->reauth_id;
+ data->last_eap_identity_len = data->reauth_id_len;
+ data->reauth_id = NULL;
+ data->reauth_id_len = 0;
+
+ res = eap_aka_response_reauth(data, id, 1, eattr.nonce_s);
+ os_free(decrypted);
+
+ return res;
+ }
+ data->counter = eattr.counter;
+
+ os_memcpy(data->nonce_s, eattr.nonce_s, EAP_SIM_NONCE_S_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-AKA: (encr) AT_NONCE_S",
+ data->nonce_s, EAP_SIM_NONCE_S_LEN);
+
+ eap_sim_derive_keys_reauth(data->counter,
+ data->reauth_id, data->reauth_id_len,
+ data->nonce_s, data->mk, data->msk,
+ data->emsk);
+ eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+ eap_aka_learn_ids(data, &eattr);
+
+ if (data->result_ind && attr->result_ind)
+ data->use_result_ind = 1;
+
+ if (data->state != FAILURE && data->state != RESULT_FAILURE) {
+ eap_aka_state(data, data->use_result_ind ?
+ RESULT_SUCCESS : SUCCESS);
+ }
+
+ data->num_id_req = 0;
+ data->num_notification = 0;
+ if (data->counter > EAP_AKA_MAX_FAST_REAUTHS) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Maximum number of "
+ "fast reauths performed - force fullauth");
+ eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+ }
+ os_free(decrypted);
+ return eap_aka_response_reauth(data, id, 0, data->nonce_s);
+}
+
+
+static struct wpabuf * eap_aka_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct eap_aka_data *data = priv;
+ const struct eap_hdr *req;
+ u8 subtype, id;
+ struct wpabuf *res;
+ const u8 *pos;
+ struct eap_sim_attrs attr;
+ size_t len;
+
+ wpa_hexdump_buf(MSG_DEBUG, "EAP-AKA: EAP data", reqData);
+ if (eap_get_config_identity(sm, &len) == NULL) {
+ wpa_printf(MSG_INFO, "EAP-AKA: Identity not configured");
+ eap_sm_request_identity(sm);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_AKA, reqData, &len);
+ if (pos == NULL || len < 1) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ req = wpabuf_head(reqData);
+ id = req->identifier;
+ len = be_to_host16(req->length);
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = TRUE;
+
+ subtype = *pos++;
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Subtype=%d", subtype);
+ pos += 2; /* Reserved */
+
+ if (eap_sim_parse_attr(pos, wpabuf_head_u8(reqData) + len, &attr, 1,
+ 0)) {
+ res = eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ goto done;
+ }
+
+ switch (subtype) {
+ case EAP_AKA_SUBTYPE_IDENTITY:
+ res = eap_aka_process_identity(sm, data, id, reqData, &attr);
+ break;
+ case EAP_AKA_SUBTYPE_CHALLENGE:
+ res = eap_aka_process_challenge(sm, data, id, reqData, &attr);
+ break;
+ case EAP_AKA_SUBTYPE_NOTIFICATION:
+ res = eap_aka_process_notification(sm, data, id, reqData,
+ &attr);
+ break;
+ case EAP_AKA_SUBTYPE_REAUTHENTICATION:
+ res = eap_aka_process_reauthentication(sm, data, id, reqData,
+ &attr);
+ break;
+ case EAP_AKA_SUBTYPE_CLIENT_ERROR:
+ wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Client-Error");
+ res = eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown subtype=%d", subtype);
+ res = eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ break;
+ }
+
+done:
+ if (data->state == FAILURE) {
+ ret->decision = DECISION_FAIL;
+ ret->methodState = METHOD_DONE;
+ } else if (data->state == SUCCESS) {
+ ret->decision = data->use_result_ind ?
+ DECISION_UNCOND_SUCC : DECISION_COND_SUCC;
+ /*
+ * It is possible for the server to reply with AKA
+ * Notification, so we must allow the method to continue and
+ * not only accept EAP-Success at this point.
+ */
+ ret->methodState = data->use_result_ind ?
+ METHOD_DONE : METHOD_MAY_CONT;
+ } else if (data->state == RESULT_FAILURE)
+ ret->methodState = METHOD_CONT;
+ else if (data->state == RESULT_SUCCESS)
+ ret->methodState = METHOD_CONT;
+
+ if (ret->methodState == METHOD_DONE) {
+ ret->allowNotifications = FALSE;
+ }
+
+ return res;
+}
+
+
+static Boolean eap_aka_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+ struct eap_aka_data *data = priv;
+ return data->pseudonym || data->reauth_id;
+}
+
+
+static void eap_aka_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_aka_data *data = priv;
+ eap_aka_clear_identities(data, CLEAR_EAP_ID);
+ data->prev_id = -1;
+ wpabuf_free(data->id_msgs);
+ data->id_msgs = NULL;
+ data->use_result_ind = 0;
+}
+
+
+static void * eap_aka_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_aka_data *data = priv;
+ data->num_id_req = 0;
+ data->num_notification = 0;
+ eap_aka_state(data, CONTINUE);
+ return priv;
+}
+
+
+static const u8 * eap_aka_get_identity(struct eap_sm *sm, void *priv,
+ size_t *len)
+{
+ struct eap_aka_data *data = priv;
+
+ if (data->reauth_id) {
+ *len = data->reauth_id_len;
+ return data->reauth_id;
+ }
+
+ if (data->pseudonym) {
+ *len = data->pseudonym_len;
+ return data->pseudonym;
+ }
+
+ return NULL;
+}
+
+
+static Boolean eap_aka_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_aka_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_aka_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(EAP_SIM_KEYING_DATA_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_SIM_KEYING_DATA_LEN;
+ os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
+
+ return key;
+}
+
+
+static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_aka_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(EAP_EMSK_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_EMSK_LEN;
+ os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+
+ return key;
+}
+
+
+int eap_peer_aka_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_AKA, "AKA");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_aka_init;
+ eap->deinit = eap_aka_deinit;
+ eap->process = eap_aka_process;
+ eap->isKeyAvailable = eap_aka_isKeyAvailable;
+ eap->getKey = eap_aka_getKey;
+ eap->has_reauth_data = eap_aka_has_reauth_data;
+ eap->deinit_for_reauth = eap_aka_deinit_for_reauth;
+ eap->init_for_reauth = eap_aka_init_for_reauth;
+ eap->get_identity = eap_aka_get_identity;
+ eap->get_emsk = eap_aka_get_emsk;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h
new file mode 100644
index 0000000..c08f6fe
--- /dev/null
+++ b/src/eap_peer/eap_config.h
@@ -0,0 +1,572 @@
+/*
+ * EAP peer configuration data
+ * Copyright (c) 2003-2008, 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.
+ */
+
+#ifndef EAP_CONFIG_H
+#define EAP_CONFIG_H
+
+/**
+ * struct eap_peer_config - EAP peer configuration/credentials
+ */
+struct eap_peer_config {
+ /**
+ * identity - EAP Identity
+ *
+ * This field is used to set the real user identity or NAI (for
+ * EAP-PSK/PAX/SAKE/GPSK).
+ */
+ u8 *identity;
+
+ /**
+ * identity_len - EAP Identity length
+ */
+ size_t identity_len;
+
+ /**
+ * anonymous_identity - Anonymous EAP Identity
+ *
+ * This field is used for unencrypted use with EAP types that support
+ * different tunnelled identity, e.g., EAP-TTLS, in order to reveal the
+ * real identity (identity field) only to the authentication server.
+ *
+ * If not set, the identity field will be used for both unencrypted and
+ * protected fields.
+ */
+ u8 *anonymous_identity;
+
+ /**
+ * anonymous_identity_len - Length of anonymous_identity
+ */
+ size_t anonymous_identity_len;
+
+ /**
+ * password - Password string for EAP
+ *
+ * This field can include either the plaintext password (default
+ * option) or a NtPasswordHash (16-byte MD4 hash of the unicode
+ * presentation of the password) if flags field has
+ * EAP_CONFIG_FLAGS_PASSWORD_NTHASH bit set to 1. NtPasswordHash can
+ * only be used with authentication mechanism that use this hash as the
+ * starting point for operation: MSCHAP and MSCHAPv2 (EAP-MSCHAPv2,
+ * EAP-TTLS/MSCHAPv2, EAP-TTLS/MSCHAP, LEAP).
+ *
+ * In addition, this field is used to configure a pre-shared key for
+ * EAP-PSK/PAX/SAKE/GPSK. The length of the PSK must be 16 for EAP-PSK
+ * and EAP-PAX and 32 for EAP-SAKE. EAP-GPSK can use a variable length
+ * PSK.
+ */
+ u8 *password;
+
+ /**
+ * password_len - Length of password field
+ */
+ size_t password_len;
+
+ /**
+ * ca_cert - File path to CA certificate file (PEM/DER)
+ *
+ * This file can have one or more trusted CA certificates. If ca_cert
+ * and ca_path are not included, server certificate will not be
+ * verified. This is insecure and a trusted CA certificate should
+ * always be configured when using EAP-TLS/TTLS/PEAP. Full path to the
+ * file should be used since working directory may change when
+ * wpa_supplicant is run in the background.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://<blob name>.
+ *
+ * On Windows, trusted CA certificates can be loaded from the system
+ * certificate store by setting this to cert_store://<name>, e.g.,
+ * ca_cert="cert_store://CA" or ca_cert="cert_store://ROOT".
+ * Note that when running wpa_supplicant as an application, the user
+ * certificate store (My user account) is used, whereas computer store
+ * (Computer account) is used when running wpasvc as a service.
+ */
+ u8 *ca_cert;
+
+ /**
+ * ca_path - Directory path for CA certificate files (PEM)
+ *
+ * This path may contain multiple CA certificates in OpenSSL format.
+ * Common use for this is to point to system trusted CA list which is
+ * often installed into directory like /etc/ssl/certs. If configured,
+ * these certificates are added to the list of trusted CAs. ca_cert
+ * may also be included in that case, but it is not required.
+ */
+ u8 *ca_path;
+
+ /**
+ * client_cert - File path to client certificate file (PEM/DER)
+ *
+ * This field is used with EAP method that use TLS authentication.
+ * Usually, this is only configured for EAP-TLS, even though this could
+ * in theory be used with EAP-TTLS and EAP-PEAP, too. Full path to the
+ * file should be used since working directory may change when
+ * wpa_supplicant is run in the background.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://<blob name>.
+ */
+ u8 *client_cert;
+
+ /**
+ * private_key - File path to client private key file (PEM/DER/PFX)
+ *
+ * When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be
+ * commented out. Both the private key and certificate will be read
+ * from the PKCS#12 file in this case. Full path to the file should be
+ * used since working directory may change when wpa_supplicant is run
+ * in the background.
+ *
+ * Windows certificate store can be used by leaving client_cert out and
+ * configuring private_key in one of the following formats:
+ *
+ * cert://substring_to_match
+ *
+ * hash://certificate_thumbprint_in_hex
+ *
+ * For example: private_key="hash://63093aa9c47f56ae88334c7b65a4"
+ *
+ * Note that when running wpa_supplicant as an application, the user
+ * certificate store (My user account) is used, whereas computer store
+ * (Computer account) is used when running wpasvc as a service.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://<blob name>.
+ */
+ u8 *private_key;
+
+ /**
+ * private_key_passwd - Password for private key file
+ *
+ * If left out, this will be asked through control interface.
+ */
+ u8 *private_key_passwd;
+
+ /**
+ * dh_file - File path to DH/DSA parameters file (in PEM format)
+ *
+ * This is an optional configuration file for setting parameters for an
+ * ephemeral DH key exchange. In most cases, the default RSA
+ * authentication does not use this configuration. However, it is
+ * possible setup RSA to use ephemeral DH key exchange. In addition,
+ * ciphers with DSA keys always use ephemeral DH keys. This can be used
+ * to achieve forward secrecy. If the file is in DSA parameters format,
+ * it will be automatically converted into DH params. Full path to the
+ * file should be used since working directory may change when
+ * wpa_supplicant is run in the background.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://<blob name>.
+ */
+ u8 *dh_file;
+
+ /**
+ * subject_match - Constraint for server certificate subject
+ *
+ * This substring is matched against the subject of the authentication
+ * server certificate. If this string is set, the server sertificate is
+ * only accepted if it contains this string in the subject. The subject
+ * string is in following format:
+ *
+ * /C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@n.example.com
+ */
+ u8 *subject_match;
+
+ /**
+ * altsubject_match - Constraint for server certificate alt. subject
+ *
+ * Semicolon separated string of entries to be matched against the
+ * alternative subject name of the authentication server certificate.
+ * If this string is set, the server sertificate is only accepted if it
+ * contains one of the entries in an alternative subject name
+ * extension.
+ *
+ * altSubjectName string is in following format: TYPE:VALUE
+ *
+ * Example: EMAIL:server@example.com
+ * Example: DNS:server.example.com;DNS:server2.example.com
+ *
+ * Following types are supported: EMAIL, DNS, URI
+ */
+ u8 *altsubject_match;
+
+ /**
+ * ca_cert2 - File path to CA certificate file (PEM/DER) (Phase 2)
+ *
+ * This file can have one or more trusted CA certificates. If ca_cert2
+ * and ca_path2 are not included, server certificate will not be
+ * verified. This is insecure and a trusted CA certificate should
+ * always be configured. Full path to the file should be used since
+ * working directory may change when wpa_supplicant is run in the
+ * background.
+ *
+ * This field is like ca_cert, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://<blob name>.
+ */
+ u8 *ca_cert2;
+
+ /**
+ * ca_path2 - Directory path for CA certificate files (PEM) (Phase 2)
+ *
+ * This path may contain multiple CA certificates in OpenSSL format.
+ * Common use for this is to point to system trusted CA list which is
+ * often installed into directory like /etc/ssl/certs. If configured,
+ * these certificates are added to the list of trusted CAs. ca_cert
+ * may also be included in that case, but it is not required.
+ *
+ * This field is like ca_path, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ */
+ u8 *ca_path2;
+
+ /**
+ * client_cert2 - File path to client certificate file
+ *
+ * This field is like client_cert, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the
+ * file should be used since working directory may change when
+ * wpa_supplicant is run in the background.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://<blob name>.
+ */
+ u8 *client_cert2;
+
+ /**
+ * private_key2 - File path to client private key file
+ *
+ * This field is like private_key, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the
+ * file should be used since working directory may change when
+ * wpa_supplicant is run in the background.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://<blob name>.
+ */
+ u8 *private_key2;
+
+ /**
+ * private_key2_passwd - Password for private key file
+ *
+ * This field is like private_key_passwd, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ */
+ u8 *private_key2_passwd;
+
+ /**
+ * dh_file2 - File path to DH/DSA parameters file (in PEM format)
+ *
+ * This field is like dh_file, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the
+ * file should be used since working directory may change when
+ * wpa_supplicant is run in the background.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://<blob name>.
+ */
+ u8 *dh_file2;
+
+ /**
+ * subject_match2 - Constraint for server certificate subject
+ *
+ * This field is like subject_match, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ */
+ u8 *subject_match2;
+
+ /**
+ * altsubject_match2 - Constraint for server certificate alt. subject
+ *
+ * This field is like altsubject_match, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ */
+ u8 *altsubject_match2;
+
+ /**
+ * eap_methods - Allowed EAP methods
+ *
+ * (vendor=EAP_VENDOR_IETF,method=EAP_TYPE_NONE) terminated list of
+ * allowed EAP methods or %NULL if all methods are accepted.
+ */
+ struct eap_method_type *eap_methods;
+
+ /**
+ * phase1 - Phase 1 (outer authentication) parameters
+ *
+ * String with field-value pairs, e.g., "peapver=0" or
+ * "peapver=1 peaplabel=1".
+ *
+ * 'peapver' can be used to force which PEAP version (0 or 1) is used.
+ *
+ * 'peaplabel=1' can be used to force new label, "client PEAP
+ * encryption", to be used during key derivation when PEAPv1 or newer.
+ *
+ * Most existing PEAPv1 implementation seem to be using the old label,
+ * "client EAP encryption", and wpa_supplicant is now using that as the
+ * default value.
+ *
+ * Some servers, e.g., Radiator, may require peaplabel=1 configuration
+ * to interoperate with PEAPv1; see eap_testing.txt for more details.
+ *
+ * 'peap_outer_success=0' can be used to terminate PEAP authentication
+ * on tunneled EAP-Success. This is required with some RADIUS servers
+ * that implement draft-josefsson-pppext-eap-tls-eap-05.txt (e.g.,
+ * Lucent NavisRadius v4.4.0 with PEAP in "IETF Draft 5" mode).
+ *
+ * include_tls_length=1 can be used to force wpa_supplicant to include
+ * TLS Message Length field in all TLS messages even if they are not
+ * fragmented.
+ *
+ * sim_min_num_chal=3 can be used to configure EAP-SIM to require three
+ * challenges (by default, it accepts 2 or 3).
+ *
+ * result_ind=1 can be used to enable EAP-SIM and EAP-AKA to use
+ * protected result indication.
+ *
+ * fast_provisioning option can be used to enable in-line provisioning
+ * of EAP-FAST credentials (PAC):
+ * 0 = disabled,
+ * 1 = allow unauthenticated provisioning,
+ * 2 = allow authenticated provisioning,
+ * 3 = allow both unauthenticated and authenticated provisioning
+ *
+ * fast_max_pac_list_len=<num> option can be used to set the maximum
+ * number of PAC entries to store in a PAC list (default: 10).
+ *
+ * fast_pac_format=binary option can be used to select binary format
+ * for storing PAC entires in order to save some space (the default
+ * text format uses about 2.5 times the size of minimal binary format).
+ */
+ char *phase1;
+
+ /**
+ * phase2 - Phase2 (inner authentication with TLS tunnel) parameters
+ *
+ * String with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or
+ * "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS.
+ */
+ char *phase2;
+
+ /**
+ * pcsc - Parameters for PC/SC smartcard interface for USIM and GSM SIM
+ *
+ * This field is used to configure PC/SC smartcard interface.
+ * Currently, the only configuration is whether this field is %NULL (do
+ * not use PC/SC) or non-NULL (e.g., "") to enable PC/SC.
+ *
+ * This field is used for EAP-SIM and EAP-AKA.
+ */
+ char *pcsc;
+
+ /**
+ * pin - PIN for USIM, GSM SIM, and smartcards
+ *
+ * This field is used to configure PIN for SIM and smartcards for
+ * EAP-SIM and EAP-AKA. In addition, this is used with EAP-TLS if a
+ * smartcard is used for private key operations.
+ *
+ * If left out, this will be asked through control interface.
+ */
+ char *pin;
+
+ /**
+ * engine - Enable OpenSSL engine (e.g., for smartcard access)
+ *
+ * This is used if private key operations for EAP-TLS are performed
+ * using a smartcard.
+ */
+ int engine;
+
+ /**
+ * engine_id - Engine ID for OpenSSL engine
+ *
+ * "opensc" to select OpenSC engine or "pkcs11" to select PKCS#11
+ * engine.
+ *
+ * This is used if private key operations for EAP-TLS are performed
+ * using a smartcard.
+ */
+ char *engine_id;
+
+ /**
+ * key_id - Key ID for OpenSSL engine
+ *
+ * This is used if private key operations for EAP-TLS are performed
+ * using a smartcard.
+ */
+ char *key_id;
+
+ /**
+ * otp - One-time-password
+ *
+ * This field should not be set in configuration step. It is only used
+ * internally when OTP is entered through the control interface.
+ */
+ u8 *otp;
+
+ /**
+ * otp_len - Length of the otp field
+ */
+ size_t otp_len;
+
+ /**
+ * pending_req_identity - Whether there is a pending identity request
+ *
+ * This field should not be set in configuration step. It is only used
+ * internally when control interface is used to request needed
+ * information.
+ */
+ int pending_req_identity;
+
+ /**
+ * pending_req_password - Whether there is a pending password request
+ *
+ * This field should not be set in configuration step. It is only used
+ * internally when control interface is used to request needed
+ * information.
+ */
+ int pending_req_password;
+
+ /**
+ * pending_req_pin - Whether there is a pending PIN request
+ *
+ * This field should not be set in configuration step. It is only used
+ * internally when control interface is used to request needed
+ * information.
+ */
+ int pending_req_pin;
+
+ /**
+ * pending_req_new_password - Pending password update request
+ *
+ * This field should not be set in configuration step. It is only used
+ * internally when control interface is used to request needed
+ * information.
+ */
+ int pending_req_new_password;
+
+ /**
+ * pending_req_passphrase - Pending passphrase request
+ *
+ * This field should not be set in configuration step. It is only used
+ * internally when control interface is used to request needed
+ * information.
+ */
+ int pending_req_passphrase;
+
+ /**
+ * pending_req_otp - Whether there is a pending OTP request
+ *
+ * This field should not be set in configuration step. It is only used
+ * internally when control interface is used to request needed
+ * information.
+ */
+ char *pending_req_otp;
+
+ /**
+ * pending_req_otp_len - Length of the pending OTP request
+ */
+ size_t pending_req_otp_len;
+
+ /**
+ * pac_file - File path or blob name for the PAC entries (EAP-FAST)
+ *
+ * wpa_supplicant will need to be able to create this file and write
+ * updates to it when PAC is being provisioned or refreshed. Full path
+ * to the file should be used since working directory may change when
+ * wpa_supplicant is run in the background.
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://<blob name>.
+ */
+ char *pac_file;
+
+ /**
+ * mschapv2_retry - MSCHAPv2 retry in progress
+ *
+ * This field is used internally by EAP-MSCHAPv2 and should not be set
+ * as part of configuration.
+ */
+ int mschapv2_retry;
+
+ /**
+ * new_password - New password for password update
+ *
+ * This field is used during MSCHAPv2 password update. This is normally
+ * requested from the user through the control interface and not set
+ * from configuration.
+ */
+ u8 *new_password;
+
+ /**
+ * new_password_len - Length of new_password field
+ */
+ size_t new_password_len;
+
+ /**
+ * fragment_size - Maximum EAP fragment size in bytes (default 1398)
+ *
+ * This value limits the fragment size for EAP methods that support
+ * fragmentation (e.g., EAP-TLS and EAP-PEAP). This value should be set
+ * small enough to make the EAP messages fit in MTU of the network
+ * interface used for EAPOL. The default value is suitable for most
+ * cases.
+ */
+ int fragment_size;
+
+#define EAP_CONFIG_FLAGS_PASSWORD_NTHASH BIT(0)
+ /**
+ * flags - Network configuration flags (bitfield)
+ *
+ * This variable is used for internal flags to describe further details
+ * for the network parameters.
+ * bit 0 = password is represented as a 16-byte NtPasswordHash value
+ * instead of plaintext password
+ */
+ u32 flags;
+};
+
+
+/**
+ * struct wpa_config_blob - Named configuration blob
+ *
+ * This data structure is used to provide storage for binary objects to store
+ * abstract information like certificates and private keys inlined with the
+ * configuration data.
+ */
+struct wpa_config_blob {
+ /**
+ * name - Blob name
+ */
+ char *name;
+
+ /**
+ * data - Pointer to binary data
+ */
+ u8 *data;
+
+ /**
+ * len - Length of binary data
+ */
+ size_t len;
+
+ /**
+ * next - Pointer to next blob in the configuration
+ */
+ struct wpa_config_blob *next;
+};
+
+#endif /* EAP_CONFIG_H */
diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c
new file mode 100644
index 0000000..caca89e
--- /dev/null
+++ b/src/eap_peer/eap_fast.c
@@ -0,0 +1,1859 @@
+/*
+ * EAP peer method: EAP-FAST (RFC 4851)
+ * Copyright (c) 2004-2008, 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "eap_config.h"
+#include "tls.h"
+#include "eap_tlv.h"
+#include "sha1.h"
+#include "eap_fast_pac.h"
+
+#ifdef EAP_FAST_DYNAMIC
+#include "eap_fast_pac.c"
+#endif /* EAP_FAST_DYNAMIC */
+
+/* TODO:
+ * - test session resumption and enable it if it interoperates
+ * - password change (pending mschapv2 packet; replay decrypted packet)
+ */
+
+
+static void eap_fast_deinit(struct eap_sm *sm, void *priv);
+
+
+struct eap_fast_data {
+ struct eap_ssl_data ssl;
+
+ int fast_version;
+
+ const struct eap_method *phase2_method;
+ void *phase2_priv;
+ int phase2_success;
+
+ struct eap_method_type phase2_type;
+ struct eap_method_type *phase2_types;
+ size_t num_phase2_types;
+ int resuming; /* starting a resumed session */
+ struct eap_fast_key_block_provisioning *key_block_p;
+#define EAP_FAST_PROV_UNAUTH 1
+#define EAP_FAST_PROV_AUTH 2
+ int provisioning_allowed; /* Allowed PAC provisioning modes */
+ int provisioning; /* doing PAC provisioning (not the normal auth) */
+ int anon_provisioning; /* doing anonymous (unauthenticated)
+ * provisioning */
+ int session_ticket_used;
+
+ u8 key_data[EAP_FAST_KEY_LEN];
+ u8 emsk[EAP_EMSK_LEN];
+ int success;
+
+ struct eap_fast_pac *pac;
+ struct eap_fast_pac *current_pac;
+ size_t max_pac_list_len;
+ int use_pac_binary_format;
+
+ u8 simck[EAP_FAST_SIMCK_LEN];
+ int simck_idx;
+
+ struct wpabuf *pending_phase2_req;
+};
+
+
+static int eap_fast_session_ticket_cb(void *ctx, const u8 *ticket, size_t len,
+ const u8 *client_random,
+ const u8 *server_random,
+ u8 *master_secret)
+{
+ struct eap_fast_data *data = ctx;
+#define TLS_RANDOM_LEN 32
+#define TLS_MASTER_SECRET_LEN 48
+ u8 seed[2 * TLS_RANDOM_LEN];
+
+ wpa_printf(MSG_DEBUG, "EAP-FAST: SessionTicket callback");
+
+ if (client_random == NULL || server_random == NULL ||
+ master_secret == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: SessionTicket failed - fall "
+ "back to full TLS handshake");
+ data->session_ticket_used = 0;
+ if (data->provisioning_allowed) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Try to provision a "
+ "new PAC-Key");
+ data->provisioning = 1;
+ data->current_pac = NULL;
+ }
+ return 0;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "EAP-FAST: SessionTicket", ticket, len);
+ wpa_hexdump(MSG_DEBUG, "EAP-FAST: client_random",
+ client_random, TLS_RANDOM_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-FAST: server_random",
+ server_random, TLS_RANDOM_LEN);
+
+ if (data->current_pac == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC-Key available for "
+ "using SessionTicket");
+ data->session_ticket_used = 0;
+ return 0;
+ }
+
+ /*
+ * RFC 4851, Section 5.1:
+ * master_secret = T-PRF(PAC-Key, "PAC to master secret label hash",
+ * server_random + client_random, 48)
+ */
+ os_memcpy(seed, server_random, TLS_RANDOM_LEN);
+ os_memcpy(seed + TLS_RANDOM_LEN, client_random, TLS_RANDOM_LEN);
+ sha1_t_prf(data->current_pac->pac_key, EAP_FAST_PAC_KEY_LEN,
+ "PAC to master secret label hash",
+ seed, sizeof(seed), master_secret, TLS_MASTER_SECRET_LEN);
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: master_secret",
+ master_secret, TLS_MASTER_SECRET_LEN);
+
+ data->session_ticket_used = 1;
+
+ return 1;
+}
+
+
+static int eap_fast_parse_phase1(struct eap_fast_data *data,
+ const char *phase1)
+{
+ const char *pos;
+
+ pos = os_strstr(phase1, "fast_provisioning=");
+ if (pos) {
+ data->provisioning_allowed = atoi(pos + 18);
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Automatic PAC provisioning "
+ "mode: %d", data->provisioning_allowed);
+ }
+
+ pos = os_strstr(phase1, "fast_max_pac_list_len=");
+ if (pos) {
+ data->max_pac_list_len = atoi(pos + 22);
+ if (data->max_pac_list_len == 0)
+ data->max_pac_list_len = 1;
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Maximum PAC list length: %lu",
+ (unsigned long) data->max_pac_list_len);
+ }
+
+ pos = os_strstr(phase1, "fast_pac_format=binary");
+ if (pos) {
+ data->use_pac_binary_format = 1;
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Using binary format for PAC "
+ "list");
+ }
+
+ return 0;
+}
+
+
+static void * eap_fast_init(struct eap_sm *sm)
+{
+ struct eap_fast_data *data;
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->fast_version = EAP_FAST_VERSION;
+ data->max_pac_list_len = 10;
+
+ if (config && config->phase1 &&
+ eap_fast_parse_phase1(data, config->phase1) < 0) {
+ eap_fast_deinit(sm, data);
+ return NULL;
+ }
+
+ if (eap_peer_select_phase2_methods(config, "auth=",
+ &data->phase2_types,
+ &data->num_phase2_types) < 0) {
+ eap_fast_deinit(sm, data);
+ return NULL;
+ }
+
+ data->phase2_type.vendor = EAP_VENDOR_IETF;
+ data->phase2_type.method = EAP_TYPE_NONE;
+
+ if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize SSL.");
+ eap_fast_deinit(sm, data);
+ return NULL;
+ }
+
+ if (tls_connection_set_session_ticket_cb(sm->ssl_ctx, data->ssl.conn,
+ eap_fast_session_ticket_cb,
+ data) < 0) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Failed to set SessionTicket "
+ "callback");
+ eap_fast_deinit(sm, data);
+ return NULL;
+ }
+
+ /*
+ * The local RADIUS server in a Cisco AP does not seem to like empty
+ * fragments before data, so disable that workaround for CBC.
+ * TODO: consider making this configurable
+ */
+ if (tls_connection_enable_workaround(sm->ssl_ctx, data->ssl.conn)) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to enable TLS "
+ "workarounds");
+ }
+
+ if (data->use_pac_binary_format &&
+ eap_fast_load_pac_bin(sm, &data->pac, config->pac_file) < 0) {
+ eap_fast_deinit(sm, data);
+ return NULL;
+ }
+
+ if (!data->use_pac_binary_format &&
+ eap_fast_load_pac(sm, &data->pac, config->pac_file) < 0) {
+ eap_fast_deinit(sm, data);
+ return NULL;
+ }
+ eap_fast_pac_list_truncate(data->pac, data->max_pac_list_len);
+
+ if (data->pac == NULL && !data->provisioning_allowed) {
+ wpa_printf(MSG_INFO, "EAP-FAST: No PAC configured and "
+ "provisioning disabled");
+ eap_fast_deinit(sm, data);
+ return NULL;
+ }
+
+ return data;
+}
+
+
+static void eap_fast_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_fast_data *data = priv;
+ struct eap_fast_pac *pac, *prev;
+
+ if (data == NULL)
+ return;
+ if (data->phase2_priv && data->phase2_method)
+ data->phase2_method->deinit(sm, data->phase2_priv);
+ os_free(data->phase2_types);
+ os_free(data->key_block_p);
+ eap_peer_tls_ssl_deinit(sm, &data->ssl);
+
+ pac = data->pac;
+ prev = NULL;
+ while (pac) {
+ prev = pac;
+ pac = pac->next;
+ eap_fast_free_pac(prev);
+ }
+ wpabuf_free(data->pending_phase2_req);
+ os_free(data);
+}
+
+
+static int eap_fast_derive_msk(struct eap_fast_data *data)
+{
+ /* Derive EAP Master Session Keys (section 5.4) */
+ sha1_t_prf(data->simck, EAP_FAST_SIMCK_LEN,
+ "Session Key Generating Function", (u8 *) "", 0,
+ data->key_data, EAP_FAST_KEY_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (MSK)",
+ data->key_data, EAP_FAST_KEY_LEN);
+
+ sha1_t_prf(data->simck, EAP_FAST_SIMCK_LEN,
+ "Extended Session Key Generating Function",
+ (u8 *) "", 0, data->emsk, EAP_EMSK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (EMSK)",
+ data->emsk, EAP_EMSK_LEN);
+
+ data->success = 1;
+
+ return 0;
+}
+
+
+static u8 * eap_fast_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
+ char *label, size_t len)
+{
+ struct tls_keys keys;
+ u8 *rnd = NULL, *out;
+ int block_size;
+
+ block_size = tls_connection_get_keyblock_size(sm->ssl_ctx, data->conn);
+ if (block_size < 0)
+ return NULL;
+
+ out = os_malloc(block_size + len);
+ if (out == NULL)
+ return NULL;
+
+ if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 1, out,
+ block_size + len) == 0) {
+ os_memmove(out, out + block_size, len);
+ return out;
+ }
+
+ if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
+ goto fail;
+
+ rnd = os_malloc(keys.client_random_len + keys.server_random_len);
+ if (rnd == NULL)
+ goto fail;
+
+ os_memcpy(rnd, keys.server_random, keys.server_random_len);
+ os_memcpy(rnd + keys.server_random_len, keys.client_random,
+ keys.client_random_len);
+
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: master_secret for key "
+ "expansion", keys.master_key, keys.master_key_len);
+ if (tls_prf(keys.master_key, keys.master_key_len,
+ label, rnd, keys.client_random_len +
+ keys.server_random_len, out, block_size + len))
+ goto fail;
+ os_free(rnd);
+ os_memmove(out, out + block_size, len);
+ return out;
+
+fail:
+ os_free(rnd);
+ os_free(out);
+ return NULL;
+}
+
+
+static void eap_fast_derive_key_auth(struct eap_sm *sm,
+ struct eap_fast_data *data)
+{
+ u8 *sks;
+
+ /* RFC 4851, Section 5.1:
+ * Extra key material after TLS key_block: session_key_seed[40]
+ */
+
+ sks = eap_fast_derive_key(sm, &data->ssl, "key expansion",
+ EAP_FAST_SKS_LEN);
+ if (sks == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive "
+ "session_key_seed");
+ return;
+ }
+
+ /*
+ * RFC 4851, Section 5.2:
+ * S-IMCK[0] = session_key_seed
+ */
+ wpa_hexdump_key(MSG_DEBUG,
+ "EAP-FAST: session_key_seed (SKS = S-IMCK[0])",
+ sks, EAP_FAST_SKS_LEN);
+ data->simck_idx = 0;
+ os_memcpy(data->simck, sks, EAP_FAST_SIMCK_LEN);
+ os_free(sks);
+}
+
+
+static void eap_fast_derive_key_provisioning(struct eap_sm *sm,
+ struct eap_fast_data *data)
+{
+ os_free(data->key_block_p);
+ data->key_block_p = (struct eap_fast_key_block_provisioning *)
+ eap_fast_derive_key(sm, &data->ssl, "key expansion",
+ sizeof(*data->key_block_p));
+ if (data->key_block_p == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive key block");
+ return;
+ }
+ /*
+ * RFC 4851, Section 5.2:
+ * S-IMCK[0] = session_key_seed
+ */
+ wpa_hexdump_key(MSG_DEBUG,
+ "EAP-FAST: session_key_seed (SKS = S-IMCK[0])",
+ data->key_block_p->session_key_seed,
+ sizeof(data->key_block_p->session_key_seed));
+ data->simck_idx = 0;
+ os_memcpy(data->simck, data->key_block_p->session_key_seed,
+ EAP_FAST_SIMCK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: server_challenge",
+ data->key_block_p->server_challenge,
+ sizeof(data->key_block_p->server_challenge));
+ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: client_challenge",
+ data->key_block_p->client_challenge,
+ sizeof(data->key_block_p->client_challenge));
+}
+
+
+static void eap_fast_derive_keys(struct eap_sm *sm, struct eap_fast_data *data)
+{
+ if (data->anon_provisioning)
+ eap_fast_derive_key_provisioning(sm, data);
+ else
+ eap_fast_derive_key_auth(sm, data);
+}
+
+
+static int eap_fast_init_phase2_method(struct eap_sm *sm,
+ struct eap_fast_data *data)
+{
+ data->phase2_method =
+ eap_peer_get_eap_method(data->phase2_type.vendor,
+ data->phase2_type.method);
+ if (data->phase2_method == NULL)
+ return -1;
+
+ if (data->key_block_p) {
+ sm->auth_challenge = data->key_block_p->server_challenge;
+ sm->peer_challenge = data->key_block_p->client_challenge;
+ }
+ sm->init_phase2 = 1;
+ sm->mschapv2_full_key = 1;
+ data->phase2_priv = data->phase2_method->init(sm);
+ sm->init_phase2 = 0;
+ sm->mschapv2_full_key = 0;
+ sm->auth_challenge = NULL;
+ sm->peer_challenge = NULL;
+
+ return data->phase2_priv == NULL ? -1 : 0;
+}
+
+
+static int eap_fast_select_phase2_method(struct eap_fast_data *data, u8 type)
+{
+ size_t i;
+
+ if (data->anon_provisioning && type != EAP_TYPE_MSCHAPV2) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Only EAP-MSCHAPv2 is allowed "
+ "during unauthenticated provisioning; reject phase2"
+ " type %d", type);
+ return -1;
+ }
+
+ for (i = 0; i < data->num_phase2_types; i++) {
+ if (data->phase2_types[i].vendor != EAP_VENDOR_IETF ||
+ data->phase2_types[i].method != type)
+ continue;
+
+ data->phase2_type.vendor = data->phase2_types[i].vendor;
+ data->phase2_type.method = data->phase2_types[i].method;
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Selected Phase 2 EAP "
+ "vendor %d method %d",
+ data->phase2_type.vendor,
+ data->phase2_type.method);
+ break;
+ }
+
+ if (type != data->phase2_type.method || type == EAP_TYPE_NONE)
+ return -1;
+
+ return 0;
+}
+
+
+static int eap_fast_phase2_request(struct eap_sm *sm,
+ struct eap_fast_data *data,
+ struct eap_method_ret *ret,
+ struct eap_hdr *hdr,
+ struct wpabuf **resp)
+{
+ size_t len = be_to_host16(hdr->length);
+ u8 *pos;
+ struct eap_method_ret iret;
+ struct eap_peer_config *config = eap_get_config(sm);
+ struct wpabuf msg;
+
+ if (len <= sizeof(struct eap_hdr)) {
+ wpa_printf(MSG_INFO, "EAP-FAST: too short "
+ "Phase 2 request (len=%lu)", (unsigned long) len);
+ return -1;
+ }
+ pos = (u8 *) (hdr + 1);
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 Request: type=%d", *pos);
+ if (*pos == EAP_TYPE_IDENTITY) {
+ *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1);
+ return 0;
+ }
+
+ if (data->phase2_type.vendor == EAP_VENDOR_IETF &&
+ data->phase2_type.method == EAP_TYPE_NONE &&
+ eap_fast_select_phase2_method(data, *pos) < 0) {
+ if (eap_peer_tls_phase2_nak(data->phase2_types,
+ data->num_phase2_types,
+ hdr, resp))
+ return -1;
+ return 0;
+ }
+
+ if (data->phase2_priv == NULL &&
+ eap_fast_init_phase2_method(sm, data) < 0) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize "
+ "Phase 2 EAP method %d", *pos);
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return -1;
+ }
+
+ os_memset(&iret, 0, sizeof(iret));
+ wpabuf_set(&msg, hdr, len);
+ *resp = data->phase2_method->process(sm, data->phase2_priv, &iret,
+ &msg);
+ if (*resp == NULL ||
+ (iret.methodState == METHOD_DONE &&
+ iret.decision == DECISION_FAIL)) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ } else if ((iret.methodState == METHOD_DONE ||
+ iret.methodState == METHOD_MAY_CONT) &&
+ (iret.decision == DECISION_UNCOND_SUCC ||
+ iret.decision == DECISION_COND_SUCC)) {
+ data->phase2_success = 1;
+ }
+
+ if (*resp == NULL && config &&
+ (config->pending_req_identity || config->pending_req_password ||
+ config->pending_req_otp || config->pending_req_new_password)) {
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = wpabuf_alloc_copy(hdr, len);
+ } else if (*resp == NULL)
+ return -1;
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_fast_tlv_nak(int vendor_id, int tlv_type)
+{
+ struct wpabuf *buf;
+ struct eap_tlv_nak_tlv *nak;
+ buf = wpabuf_alloc(sizeof(*nak));
+ if (buf == NULL)
+ return NULL;
+ nak = wpabuf_put(buf, sizeof(*nak));
+ nak->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | EAP_TLV_NAK_TLV);
+ nak->length = host_to_be16(6);
+ nak->vendor_id = host_to_be32(vendor_id);
+ nak->nak_type = host_to_be16(tlv_type);
+ return buf;
+}
+
+
+static struct wpabuf * eap_fast_tlv_result(int status, int intermediate)
+{
+ struct wpabuf *buf;
+ struct eap_tlv_intermediate_result_tlv *result;
+ buf = wpabuf_alloc(sizeof(*result));
+ if (buf == NULL)
+ return NULL;
+ result = wpabuf_put(buf, sizeof(*result));
+ result->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+ (intermediate ?
+ EAP_TLV_INTERMEDIATE_RESULT_TLV :
+ EAP_TLV_RESULT_TLV));
+ result->length = host_to_be16(2);
+ result->status = host_to_be16(status);
+ return buf;
+}
+
+
+static struct wpabuf * eap_fast_tlv_pac_ack(void)
+{
+ struct wpabuf *buf;
+ struct eap_tlv_result_tlv *res;
+ struct eap_tlv_pac_ack_tlv *ack;
+
+ buf = wpabuf_alloc(sizeof(*res) + sizeof(*ack));
+ if (buf == NULL)
+ return NULL;
+
+ res = wpabuf_put(buf, sizeof(*res));
+ res->tlv_type = host_to_be16(EAP_TLV_RESULT_TLV |
+ EAP_TLV_TYPE_MANDATORY);
+ res->length = host_to_be16(sizeof(*res) - sizeof(struct eap_tlv_hdr));
+ res->status = host_to_be16(EAP_TLV_RESULT_SUCCESS);
+
+ ack = wpabuf_put(buf, sizeof(*ack));
+ ack->tlv_type = host_to_be16(EAP_TLV_PAC_TLV |
+ EAP_TLV_TYPE_MANDATORY);
+ ack->length = host_to_be16(sizeof(*ack) - sizeof(struct eap_tlv_hdr));
+ ack->pac_type = host_to_be16(PAC_TYPE_PAC_ACKNOWLEDGEMENT);
+ ack->pac_len = host_to_be16(2);
+ ack->result = host_to_be16(EAP_TLV_RESULT_SUCCESS);
+
+ return buf;
+}
+
+
+static struct wpabuf * eap_fast_tlv_eap_payload(struct wpabuf *buf)
+{
+ struct wpabuf *msg;
+ struct eap_tlv_hdr *tlv;
+
+ if (buf == NULL)
+ return NULL;
+
+ /* Encapsulate EAP packet in EAP Payload TLV */
+ msg = wpabuf_alloc(sizeof(*tlv) + wpabuf_len(buf));
+ if (msg == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to allocate memory "
+ "for TLV encapsulation");
+ wpabuf_free(buf);
+ return NULL;
+ }
+ tlv = wpabuf_put(msg, sizeof(*tlv));
+ tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+ EAP_TLV_EAP_PAYLOAD_TLV);
+ tlv->length = host_to_be16(wpabuf_len(buf));
+ wpabuf_put_buf(msg, buf);
+ wpabuf_free(buf);
+ return msg;
+}
+
+
+static struct wpabuf * eap_fast_process_eap_payload_tlv(
+ struct eap_sm *sm, struct eap_fast_data *data,
+ struct eap_method_ret *ret, const struct eap_hdr *req,
+ u8 *eap_payload_tlv, size_t eap_payload_tlv_len)
+{
+ struct eap_hdr *hdr;
+ struct wpabuf *resp = NULL;
+
+ if (eap_payload_tlv_len < sizeof(*hdr)) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: too short EAP "
+ "Payload TLV (len=%lu)",
+ (unsigned long) eap_payload_tlv_len);
+ return NULL;
+ }
+
+ hdr = (struct eap_hdr *) eap_payload_tlv;
+ if (be_to_host16(hdr->length) > eap_payload_tlv_len) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: EAP packet overflow in "
+ "EAP Payload TLV");
+ return NULL;
+ }
+
+ if (hdr->code != EAP_CODE_REQUEST) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Unexpected code=%d in "
+ "Phase 2 EAP header", hdr->code);
+ return NULL;
+ }
+
+ if (eap_fast_phase2_request(sm, data, ret, hdr, &resp)) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Phase2 Request processing "
+ "failed");
+ return NULL;
+ }
+
+ return eap_fast_tlv_eap_payload(resp);
+}
+
+
+static int eap_fast_validate_crypto_binding(
+ struct eap_tlv_crypto_binding__tlv *_bind)
+{
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Crypto-Binding TLV: Version %d "
+ "Received Version %d SubType %d",
+ _bind->version, _bind->received_version, _bind->subtype);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE",
+ _bind->nonce, sizeof(_bind->nonce));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC",
+ _bind->compound_mac, sizeof(_bind->compound_mac));
+
+ if (_bind->version != EAP_FAST_VERSION ||
+ _bind->received_version != EAP_FAST_VERSION ||
+ _bind->subtype != EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Invalid version/subtype in "
+ "Crypto-Binding TLV: Version %d "
+ "Received Version %d SubType %d",
+ _bind->version, _bind->received_version,
+ _bind->subtype);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void eap_fast_write_crypto_binding(
+ struct eap_tlv_crypto_binding__tlv *rbind,
+ struct eap_tlv_crypto_binding__tlv *_bind, const u8 *cmk)
+{
+ rbind->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+ EAP_TLV_CRYPTO_BINDING_TLV);
+ rbind->length = host_to_be16(sizeof(*rbind) -
+ sizeof(struct eap_tlv_hdr));
+ rbind->version = EAP_FAST_VERSION;
+ rbind->received_version = _bind->version;
+ rbind->subtype = EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE;
+ os_memcpy(rbind->nonce, _bind->nonce, sizeof(_bind->nonce));
+ inc_byte_array(rbind->nonce, sizeof(rbind->nonce));
+ hmac_sha1(cmk, 20, (u8 *) rbind, sizeof(*rbind), rbind->compound_mac);
+
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Reply Crypto-Binding TLV: Version %d "
+ "Received Version %d SubType %d",
+ rbind->version, rbind->received_version, rbind->subtype);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE",
+ rbind->nonce, sizeof(rbind->nonce));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC",
+ rbind->compound_mac, sizeof(rbind->compound_mac));
+}
+
+
+static int eap_fast_get_phase2_key(struct eap_sm *sm,
+ struct eap_fast_data *data,
+ u8 *isk, size_t isk_len)
+{
+ u8 *key;
+ size_t key_len;
+
+ os_memset(isk, 0, isk_len);
+
+ if (data->phase2_method == NULL || data->phase2_priv == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 method not "
+ "available");
+ return -1;
+ }
+
+ if (data->phase2_method->isKeyAvailable == NULL ||
+ data->phase2_method->getKey == NULL)
+ return 0;
+
+ if (!data->phase2_method->isKeyAvailable(sm, data->phase2_priv) ||
+ (key = data->phase2_method->getKey(sm, data->phase2_priv,
+ &key_len)) == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Could not get key material "
+ "from Phase 2");
+ return -1;
+ }
+
+ if (key_len > isk_len)
+ key_len = isk_len;
+ os_memcpy(isk, key, key_len);
+ os_free(key);
+
+ return 0;
+}
+
+
+static int eap_fast_get_cmk(struct eap_sm *sm, struct eap_fast_data *data,
+ u8 *cmk)
+{
+ u8 isk[32], imck[60];
+
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Determining CMK[%d] for Compound MIC "
+ "calculation", data->simck_idx + 1);
+
+ /*
+ * RFC 4851, Section 5.2:
+ * IMCK[j] = T-PRF(S-IMCK[j-1], "Inner Methods Compound Keys",
+ * MSK[j], 60)
+ * S-IMCK[j] = first 40 octets of IMCK[j]
+ * CMK[j] = last 20 octets of IMCK[j]
+ */
+
+ if (eap_fast_get_phase2_key(sm, data, isk, sizeof(isk)) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: ISK[j]", isk, sizeof(isk));
+ sha1_t_prf(data->simck, EAP_FAST_SIMCK_LEN,
+ "Inner Methods Compound Keys",
+ isk, sizeof(isk), imck, sizeof(imck));
+ data->simck_idx++;
+ os_memcpy(data->simck, imck, EAP_FAST_SIMCK_LEN);
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: S-IMCK[j]",
+ data->simck, EAP_FAST_SIMCK_LEN);
+ os_memcpy(cmk, imck + EAP_FAST_SIMCK_LEN, 20);
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: CMK[j]", cmk, 20);
+
+ return 0;
+}
+
+
+static u8 * eap_fast_write_pac_request(u8 *pos, u16 pac_type)
+{
+ struct eap_tlv_hdr *pac;
+ struct eap_tlv_request_action_tlv *act;
+ struct eap_tlv_pac_type_tlv *type;
+
+ act = (struct eap_tlv_request_action_tlv *) pos;
+ act->tlv_type = host_to_be16(EAP_TLV_REQUEST_ACTION_TLV);
+ act->length = host_to_be16(2);
+ act->action = host_to_be16(EAP_TLV_ACTION_PROCESS_TLV);
+
+ pac = (struct eap_tlv_hdr *) (act + 1);
+ pac->tlv_type = host_to_be16(EAP_TLV_PAC_TLV);
+ pac->length = host_to_be16(sizeof(*type));
+
+ type = (struct eap_tlv_pac_type_tlv *) (pac + 1);
+ type->tlv_type = host_to_be16(PAC_TYPE_PAC_TYPE);
+ type->length = host_to_be16(2);
+ type->pac_type = host_to_be16(pac_type);
+
+ return (u8 *) (type + 1);
+}
+
+
+static struct wpabuf * eap_fast_process_crypto_binding(
+ struct eap_sm *sm, struct eap_fast_data *data,
+ struct eap_method_ret *ret,
+ struct eap_tlv_crypto_binding__tlv *_bind, size_t bind_len, int final)
+{
+ struct wpabuf *resp;
+ u8 *pos;
+ struct eap_tlv_intermediate_result_tlv *rresult;
+ u8 cmk[20], cmac[20];
+ int res, req_tunnel_pac = 0;
+ size_t len;
+
+ if (eap_fast_validate_crypto_binding(_bind) < 0)
+ return eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 1);
+
+ if (eap_fast_get_cmk(sm, data, cmk) < 0)
+ return NULL;
+
+ /* Validate received Compound MAC */
+ os_memcpy(cmac, _bind->compound_mac, sizeof(cmac));
+ os_memset(_bind->compound_mac, 0, sizeof(cmac));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV for Compound "
+ "MAC calculation", (u8 *) _bind, bind_len);
+ hmac_sha1(cmk, 20, (u8 *) _bind, bind_len, _bind->compound_mac);
+ res = os_memcmp(cmac, _bind->compound_mac, sizeof(cmac));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Received Compound MAC",
+ cmac, sizeof(cmac));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Calculated Compound MAC",
+ _bind->compound_mac, sizeof(cmac));
+ if (res != 0) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Compound MAC did not match");
+ resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 1);
+ os_memcpy(_bind->compound_mac, cmac, sizeof(cmac));
+ return resp;
+ }
+
+ /*
+ * Compound MAC was valid, so authentication succeeded. Reply with
+ * crypto binding to allow server to complete authentication.
+ */
+
+ if (data->current_pac == NULL && data->provisioning &&
+ !data->anon_provisioning) {
+ /*
+ * Need to request Tunnel PAC when using authenticated
+ * provisioning.
+ */
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Request Tunnel PAC");
+ req_tunnel_pac = 1;
+ }
+
+ len = sizeof(*rresult) + sizeof(struct eap_tlv_crypto_binding__tlv);
+ if (req_tunnel_pac)
+ len += sizeof(struct eap_tlv_hdr) +
+ sizeof(struct eap_tlv_request_action_tlv) +
+ sizeof(struct eap_tlv_pac_type_tlv);
+ resp = wpabuf_alloc(len);
+ if (resp == NULL)
+ return NULL;
+
+ /*
+ * Both intermediate and final Result TLVs are identical, so ok to use
+ * the same structure definition for them.
+ */
+ rresult = wpabuf_put(resp, sizeof(*rresult));
+ rresult->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+ (final ? EAP_TLV_RESULT_TLV :
+ EAP_TLV_INTERMEDIATE_RESULT_TLV));
+ rresult->length = host_to_be16(2);
+ rresult->status = host_to_be16(EAP_TLV_RESULT_SUCCESS);
+
+ if (!data->anon_provisioning && data->phase2_success &&
+ eap_fast_derive_msk(data) < 0) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Failed to generate MSK");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ rresult->status = host_to_be16(EAP_TLV_RESULT_FAILURE);
+ data->phase2_success = 0;
+ }
+
+ pos = wpabuf_put(resp, sizeof(struct eap_tlv_crypto_binding__tlv));
+ eap_fast_write_crypto_binding((struct eap_tlv_crypto_binding__tlv *)
+ pos, _bind, cmk);
+
+ if (req_tunnel_pac) {
+ u8 *pos2;
+ pos = wpabuf_put(resp, 0);
+ pos2 = eap_fast_write_pac_request(pos, PAC_TYPE_TUNNEL_PAC);
+ wpabuf_put(resp, pos2 - pos);
+ }
+
+ if (final && data->phase2_success) {
+ if (data->anon_provisioning) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Unauthenticated "
+ "provisioning completed successfully.");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Authentication "
+ "completed successfully.");
+ if (data->provisioning)
+ ret->methodState = METHOD_MAY_CONT;
+ else
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ }
+ }
+
+ return resp;
+}
+
+
+static void eap_fast_parse_pac_tlv(struct eap_fast_pac *entry, int type,
+ u8 *pos, size_t len, int *pac_key_found)
+{
+ switch (type & 0x7fff) {
+ case PAC_TYPE_PAC_KEY:
+ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: PAC-Key", pos, len);
+ if (len != EAP_FAST_PAC_KEY_LEN) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid PAC-Key "
+ "length %lu", (unsigned long) len);
+ break;
+ }
+ *pac_key_found = 1;
+ os_memcpy(entry->pac_key, pos, len);
+ break;
+ case PAC_TYPE_PAC_OPAQUE:
+ wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Opaque", pos, len);
+ entry->pac_opaque = pos;
+ entry->pac_opaque_len = len;
+ break;
+ case PAC_TYPE_PAC_INFO:
+ wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Info", pos, len);
+ entry->pac_info = pos;
+ entry->pac_info_len = len;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored unknown PAC type %d",
+ type);
+ break;
+ }
+}
+
+
+static int eap_fast_process_pac_tlv(struct eap_fast_pac *entry,
+ u8 *pac, size_t pac_len)
+{
+ struct pac_tlv_hdr *hdr;
+ u8 *pos;
+ size_t left, len;
+ int type, pac_key_found = 0;
+
+ pos = pac;
+ left = pac_len;
+
+ while (left > sizeof(*hdr)) {
+ hdr = (struct pac_tlv_hdr *) pos;
+ type = be_to_host16(hdr->type);
+ len = be_to_host16(hdr->len);
+ pos += sizeof(*hdr);
+ left -= sizeof(*hdr);
+ if (len > left) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV overrun "
+ "(type=%d len=%lu left=%lu)",
+ type, (unsigned long) len,
+ (unsigned long) left);
+ return -1;
+ }
+
+ eap_fast_parse_pac_tlv(entry, type, pos, len, &pac_key_found);
+
+ pos += len;
+ left -= len;
+ }
+
+ if (!pac_key_found || !entry->pac_opaque || !entry->pac_info) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV does not include "
+ "all the required fields");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int eap_fast_parse_pac_info(struct eap_fast_pac *entry, int type,
+ u8 *pos, size_t len)
+{
+ u16 pac_type;
+ u32 lifetime;
+ struct os_time now;
+
+ switch (type & 0x7fff) {
+ case PAC_TYPE_CRED_LIFETIME:
+ if (len != 4) {
+ wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Info - "
+ "Invalid CRED_LIFETIME length - ignored",
+ pos, len);
+ return 0;
+ }
+
+ /*
+ * This is not currently saved separately in PAC files since
+ * the server can automatically initiate PAC update when
+ * needed. Anyway, the information is available from PAC-Info
+ * dump if it is needed for something in the future.
+ */
+ lifetime = WPA_GET_BE32(pos);
+ os_get_time(&now);
+ wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info - CRED_LIFETIME %d "
+ "(%d days)",
+ lifetime, (lifetime - (u32) now.sec) / 86400);
+ break;
+ case PAC_TYPE_A_ID:
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - A-ID",
+ pos, len);
+ entry->a_id = pos;
+ entry->a_id_len = len;
+ break;
+ case PAC_TYPE_I_ID:
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - I-ID",
+ pos, len);
+ entry->i_id = pos;
+ entry->i_id_len = len;
+ break;
+ case PAC_TYPE_A_ID_INFO:
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - A-ID-Info",
+ pos, len);
+ entry->a_id_info = pos;
+ entry->a_id_info_len = len;
+ break;
+ case PAC_TYPE_PAC_TYPE:
+ /*
+ * draft-cam-winget-eap-fast-provisioning-04.txt,
+ * Section 4.2.6 - PAC-Type TLV
+ */
+ if (len != 2) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Invalid PAC-Type "
+ "length %lu (expected 2)",
+ (unsigned long) len);
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "EAP-FAST: PAC-Info - PAC-Type",
+ pos, len);
+ return -1;
+ }
+ pac_type = WPA_GET_BE16(pos);
+ if (pac_type != PAC_TYPE_TUNNEL_PAC &&
+ pac_type != PAC_TYPE_USER_AUTHORIZATION &&
+ pac_type != PAC_TYPE_MACHINE_AUTHENTICATION) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Unsupported PAC Type "
+ "%d", pac_type);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info - PAC-Type %d",
+ pac_type);
+ entry->pac_type = pac_type;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored unknown PAC-Info "
+ "type %d", type);
+ break;
+ }
+
+ return 0;
+}
+
+
+static int eap_fast_process_pac_info(struct eap_fast_pac *entry)
+{
+ struct pac_tlv_hdr *hdr;
+ u8 *pos;
+ size_t left, len;
+ int type;
+
+ /* draft-cam-winget-eap-fast-provisioning-04.txt, Section 4.2.4 */
+
+ /* PAC-Type defaults to Tunnel PAC (Type 1) */
+ entry->pac_type = PAC_TYPE_TUNNEL_PAC;
+
+ pos = entry->pac_info;
+ left = entry->pac_info_len;
+ while (left > sizeof(*hdr)) {
+ hdr = (struct pac_tlv_hdr *) pos;
+ type = be_to_host16(hdr->type);
+ len = be_to_host16(hdr->len);
+ pos += sizeof(*hdr);
+ left -= sizeof(*hdr);
+ if (len > left) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info overrun "
+ "(type=%d len=%lu left=%lu)",
+ type, (unsigned long) len,
+ (unsigned long) left);
+ return -1;
+ }
+
+ if (eap_fast_parse_pac_info(entry, type, pos, len) < 0)
+ return -1;
+
+ pos += len;
+ left -= len;
+ }
+
+ if (entry->a_id == NULL || entry->a_id_info == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info does not include "
+ "all the required fields");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_fast_process_pac(struct eap_sm *sm,
+ struct eap_fast_data *data,
+ struct eap_method_ret *ret,
+ u8 *pac, size_t pac_len)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ struct eap_fast_pac entry;
+
+ os_memset(&entry, 0, sizeof(entry));
+ if (eap_fast_process_pac_tlv(&entry, pac, pac_len) ||
+ eap_fast_process_pac_info(&entry))
+ return eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0);
+
+ eap_fast_add_pac(&data->pac, &data->current_pac, &entry);
+ eap_fast_pac_list_truncate(data->pac, data->max_pac_list_len);
+ if (data->use_pac_binary_format)
+ eap_fast_save_pac_bin(sm, data->pac, config->pac_file);
+ else
+ eap_fast_save_pac(sm, data->pac, config->pac_file);
+
+ if (data->provisioning) {
+ if (data->anon_provisioning) {
+ /*
+ * Unauthenticated provisioning does not provide keying
+ * material and must end with an EAP-Failure.
+ * Authentication will be done separately after this.
+ */
+ data->success = 0;
+ ret->decision = DECISION_FAIL;
+ } else {
+ /*
+ * Server may or may not allow authenticated
+ * provisioning also for key generation.
+ */
+ ret->decision = DECISION_COND_SUCC;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV "
+ "- Provisioning completed successfully");
+ } else {
+ /*
+ * This is PAC refreshing, i.e., normal authentication that is
+ * expected to be completed with an EAP-Success.
+ */
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV "
+ "- PAC refreshing completed successfully");
+ ret->decision = DECISION_UNCOND_SUCC;
+ }
+ ret->methodState = METHOD_DONE;
+ return eap_fast_tlv_pac_ack();
+}
+
+
+struct eap_fast_tlv_parse {
+ u8 *eap_payload_tlv;
+ size_t eap_payload_tlv_len;
+ u8 *pac;
+ size_t pac_len;
+ struct eap_tlv_crypto_binding__tlv *crypto_binding;
+ size_t crypto_binding_len;
+ int iresult;
+ int result;
+};
+
+
+static int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv,
+ int tlv_type, u8 *pos, int len)
+{
+ switch (tlv_type) {
+ case EAP_TLV_EAP_PAYLOAD_TLV:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: EAP Payload TLV",
+ pos, len);
+ tlv->eap_payload_tlv = pos;
+ tlv->eap_payload_tlv_len = len;
+ break;
+ case EAP_TLV_RESULT_TLV:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Result TLV", pos, len);
+ if (len < 2) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
+ "Result TLV");
+ tlv->result = EAP_TLV_RESULT_FAILURE;
+ break;
+ }
+ tlv->result = WPA_GET_BE16(pos);
+ if (tlv->result != EAP_TLV_RESULT_SUCCESS &&
+ tlv->result != EAP_TLV_RESULT_FAILURE) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown Result %d",
+ tlv->result);
+ tlv->result = EAP_TLV_RESULT_FAILURE;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Result: %s",
+ tlv->result == EAP_TLV_RESULT_SUCCESS ?
+ "Success" : "Failure");
+ break;
+ case EAP_TLV_INTERMEDIATE_RESULT_TLV:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Intermediate Result TLV",
+ pos, len);
+ if (len < 2) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
+ "Intermediate Result TLV");
+ tlv->iresult = EAP_TLV_RESULT_FAILURE;
+ break;
+ }
+ tlv->iresult = WPA_GET_BE16(pos);
+ if (tlv->iresult != EAP_TLV_RESULT_SUCCESS &&
+ tlv->iresult != EAP_TLV_RESULT_FAILURE) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown Intermediate "
+ "Result %d", tlv->iresult);
+ tlv->iresult = EAP_TLV_RESULT_FAILURE;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Intermediate Result: %s",
+ tlv->iresult == EAP_TLV_RESULT_SUCCESS ?
+ "Success" : "Failure");
+ break;
+ case EAP_TLV_CRYPTO_BINDING_TLV:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV",
+ pos, len);
+ tlv->crypto_binding_len = sizeof(struct eap_tlv_hdr) + len;
+ if (tlv->crypto_binding_len < sizeof(*tlv->crypto_binding)) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
+ "Crypto-Binding TLV");
+ tlv->iresult = EAP_TLV_RESULT_FAILURE;
+ return -2;
+ }
+ tlv->crypto_binding = (struct eap_tlv_crypto_binding__tlv *)
+ (pos - sizeof(struct eap_tlv_hdr));
+ break;
+ case EAP_TLV_PAC_TLV:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: PAC TLV", pos, len);
+ tlv->pac = pos;
+ tlv->pac_len = len;
+ break;
+ default:
+ /* Unknown TLV */
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int eap_fast_parse_decrypted(struct wpabuf *decrypted,
+ struct eap_fast_tlv_parse *tlv,
+ struct wpabuf **resp)
+{
+ int mandatory, tlv_type, len, res;
+ u8 *pos, *end;
+
+ os_memset(tlv, 0, sizeof(*tlv));
+
+ /* Parse TLVs from the decrypted Phase 2 data */
+ pos = wpabuf_mhead(decrypted);
+ end = pos + wpabuf_len(decrypted);
+ while (pos + 4 < end) {
+ mandatory = pos[0] & 0x80;
+ tlv_type = WPA_GET_BE16(pos) & 0x3fff;
+ pos += 2;
+ len = WPA_GET_BE16(pos);
+ pos += 2;
+ if (pos + len > end) {
+ wpa_printf(MSG_INFO, "EAP-FAST: TLV overflow");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Received Phase 2: "
+ "TLV type %d length %d%s",
+ tlv_type, len, mandatory ? " (mandatory)" : "");
+
+ res = eap_fast_parse_tlv(tlv, tlv_type, pos, len);
+ if (res == -2)
+ break;
+ if (res < 0) {
+ if (mandatory) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Nak unknown "
+ "mandatory TLV type %d", tlv_type);
+ *resp = eap_fast_tlv_nak(0, tlv_type);
+ break;
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: ignored "
+ "unknown optional TLV type %d",
+ tlv_type);
+ }
+ }
+
+ pos += len;
+ }
+
+ return 0;
+}
+
+
+static int eap_fast_encrypt_response(struct eap_sm *sm,
+ struct eap_fast_data *data,
+ struct wpabuf *resp,
+ u8 identifier, struct wpabuf **out_data)
+{
+ if (resp == NULL)
+ return 0;
+
+ wpa_hexdump_buf(MSG_DEBUG, "EAP-FAST: Encrypting Phase 2 data",
+ resp);
+ if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_FAST,
+ data->fast_version, identifier,
+ resp, out_data)) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Failed to encrypt a Phase 2 "
+ "frame");
+ }
+ wpabuf_free(resp);
+
+ return 0;
+}
+
+
+static int eap_fast_process_decrypted(struct eap_sm *sm,
+ struct eap_fast_data *data,
+ struct eap_method_ret *ret,
+ const struct eap_hdr *req,
+ struct wpabuf *decrypted,
+ struct wpabuf **out_data)
+{
+ struct wpabuf *resp = NULL;
+ struct eap_fast_tlv_parse tlv;
+
+ if (eap_fast_parse_decrypted(decrypted, &tlv, &resp) < 0)
+ return 0;
+ if (resp)
+ return eap_fast_encrypt_response(sm, data, resp,
+ req->identifier, out_data);
+
+ if (tlv.result == EAP_TLV_RESULT_FAILURE) {
+ resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0);
+ return eap_fast_encrypt_response(sm, data, resp,
+ req->identifier, out_data);
+ }
+
+ if (tlv.iresult == EAP_TLV_RESULT_FAILURE) {
+ resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 1);
+ return eap_fast_encrypt_response(sm, data, resp,
+ req->identifier, out_data);
+ }
+
+ if (tlv.eap_payload_tlv) {
+ resp = eap_fast_process_eap_payload_tlv(
+ sm, data, ret, req, tlv.eap_payload_tlv,
+ tlv.eap_payload_tlv_len);
+ return eap_fast_encrypt_response(sm, data, resp,
+ req->identifier, out_data);
+ }
+
+ if (tlv.crypto_binding) {
+ int final = tlv.result == EAP_TLV_RESULT_SUCCESS;
+ resp = eap_fast_process_crypto_binding(sm, data, ret,
+ tlv.crypto_binding,
+ tlv.crypto_binding_len,
+ final);
+ return eap_fast_encrypt_response(sm, data, resp,
+ req->identifier, out_data);
+ }
+
+ if (tlv.pac && tlv.result != EAP_TLV_RESULT_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV without Result TLV "
+ "acknowledging success");
+ resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0);
+ return eap_fast_encrypt_response(sm, data, resp,
+ req->identifier, out_data);
+ }
+
+ if (tlv.pac && tlv.result == EAP_TLV_RESULT_SUCCESS) {
+ resp = eap_fast_process_pac(sm, data, ret, tlv.pac,
+ tlv.pac_len);
+ return eap_fast_encrypt_response(sm, data, resp,
+ req->identifier, out_data);
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-FAST: No recognized TLVs - send "
+ "empty response packet");
+ return eap_fast_encrypt_response(sm, data, wpabuf_alloc(1),
+ req->identifier, out_data);
+}
+
+
+static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data,
+ struct eap_method_ret *ret,
+ const struct eap_hdr *req,
+ const struct wpabuf *in_data,
+ struct wpabuf **out_data)
+{
+ struct wpabuf *in_decrypted;
+ int res;
+
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Received %lu bytes encrypted data for"
+ " Phase 2", (unsigned long) wpabuf_len(in_data));
+
+ if (data->pending_phase2_req) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Pending Phase 2 request - "
+ "skip decryption and use old data");
+ /* Clear TLS reassembly state. */
+ eap_peer_tls_reset_input(&data->ssl);
+
+ in_decrypted = data->pending_phase2_req;
+ data->pending_phase2_req = NULL;
+ goto continue_req;
+ }
+
+ if (wpabuf_len(in_data) == 0) {
+ /* Received TLS ACK - requesting more fragments */
+ return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_FAST,
+ data->fast_version,
+ req->identifier, NULL, out_data);
+ }
+
+ res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted);
+ if (res)
+ return res;
+
+continue_req:
+ wpa_hexdump_buf(MSG_MSGDUMP, "EAP-FAST: Decrypted Phase 2 TLV(s)",
+ in_decrypted);
+
+ if (wpabuf_len(in_decrypted) < 4) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Too short Phase 2 "
+ "TLV frame (len=%lu)",
+ (unsigned long) wpabuf_len(in_decrypted));
+ wpabuf_free(in_decrypted);
+ return -1;
+ }
+
+ res = eap_fast_process_decrypted(sm, data, ret, req,
+ in_decrypted, out_data);
+
+ wpabuf_free(in_decrypted);
+
+ return res;
+}
+
+
+static const u8 * eap_fast_get_a_id(const u8 *buf, size_t len, size_t *id_len)
+{
+ const u8 *a_id;
+ struct pac_tlv_hdr *hdr;
+
+ /*
+ * Parse authority identity (A-ID) from the EAP-FAST/Start. This
+ * supports both raw A-ID and one inside an A-ID TLV.
+ */
+ a_id = buf;
+ *id_len = len;
+ if (len > sizeof(*hdr)) {
+ int tlen;
+ hdr = (struct pac_tlv_hdr *) buf;
+ tlen = be_to_host16(hdr->len);
+ if (be_to_host16(hdr->type) == PAC_TYPE_A_ID &&
+ sizeof(*hdr) + tlen <= len) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: A-ID was in TLV "
+ "(Start)");
+ a_id = (u8 *) (hdr + 1);
+ *id_len = tlen;
+ }
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: A-ID", a_id, *id_len);
+
+ return a_id;
+}
+
+
+static void eap_fast_select_pac(struct eap_fast_data *data,
+ const u8 *a_id, size_t a_id_len)
+{
+ data->current_pac = eap_fast_get_pac(data->pac, a_id, a_id_len,
+ PAC_TYPE_TUNNEL_PAC);
+ if (data->current_pac == NULL) {
+ /*
+ * Tunnel PAC was not available for this A-ID. Try to use
+ * Machine Authentication PAC, if one is available.
+ */
+ data->current_pac = eap_fast_get_pac(
+ data->pac, a_id, a_id_len,
+ PAC_TYPE_MACHINE_AUTHENTICATION);
+ }
+
+ if (data->current_pac) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: PAC found for this A-ID "
+ "(PAC-Type %d)", data->current_pac->pac_type);
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-FAST: A-ID-Info",
+ data->current_pac->a_id_info,
+ data->current_pac->a_id_info_len);
+ }
+}
+
+
+static int eap_fast_use_pac_opaque(struct eap_sm *sm,
+ struct eap_fast_data *data,
+ struct eap_fast_pac *pac)
+{
+ u8 *tlv;
+ size_t tlv_len, olen;
+ struct eap_tlv_hdr *ehdr;
+
+ olen = pac->pac_opaque_len;
+ tlv_len = sizeof(*ehdr) + olen;
+ tlv = os_malloc(tlv_len);
+ if (tlv) {
+ ehdr = (struct eap_tlv_hdr *) tlv;
+ ehdr->tlv_type = host_to_be16(PAC_TYPE_PAC_OPAQUE);
+ ehdr->length = host_to_be16(olen);
+ os_memcpy(ehdr + 1, pac->pac_opaque, olen);
+ }
+ if (tlv == NULL ||
+ tls_connection_client_hello_ext(sm->ssl_ctx, data->ssl.conn,
+ TLS_EXT_PAC_OPAQUE,
+ tlv, tlv_len) < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to add PAC-Opaque TLS "
+ "extension");
+ os_free(tlv);
+ return -1;
+ }
+ os_free(tlv);
+
+ return 0;
+}
+
+
+static int eap_fast_clear_pac_opaque_ext(struct eap_sm *sm,
+ struct eap_fast_data *data)
+{
+ if (tls_connection_client_hello_ext(sm->ssl_ctx, data->ssl.conn,
+ TLS_EXT_PAC_OPAQUE, NULL, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to remove PAC-Opaque "
+ "TLS extension");
+ return -1;
+ }
+ return 0;
+}
+
+
+static int eap_fast_set_provisioning_ciphers(struct eap_sm *sm,
+ struct eap_fast_data *data)
+{
+ u8 ciphers[5];
+ int count = 0;
+
+ if (data->provisioning_allowed & EAP_FAST_PROV_UNAUTH) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Enabling unauthenticated "
+ "provisioning TLS cipher suites");
+ ciphers[count++] = TLS_CIPHER_ANON_DH_AES128_SHA;
+ }
+
+ if (data->provisioning_allowed & EAP_FAST_PROV_AUTH) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Enabling authenticated "
+ "provisioning TLS cipher suites");
+ ciphers[count++] = TLS_CIPHER_RSA_DHE_AES128_SHA;
+ ciphers[count++] = TLS_CIPHER_AES128_SHA;
+ ciphers[count++] = TLS_CIPHER_RC4_SHA;
+ }
+
+ ciphers[count++] = TLS_CIPHER_NONE;
+
+ if (tls_connection_set_cipher_list(sm->ssl_ctx, data->ssl.conn,
+ ciphers)) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Could not configure TLS "
+ "cipher suites for provisioning");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int eap_fast_process_start(struct eap_sm *sm,
+ struct eap_fast_data *data, u8 flags,
+ const u8 *pos, size_t left)
+{
+ const u8 *a_id;
+ size_t a_id_len;
+
+ /* EAP-FAST Version negotiation (section 3.1) */
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Start (server ver=%d, own ver=%d)",
+ flags & EAP_PEAP_VERSION_MASK, data->fast_version);
+ if ((flags & EAP_PEAP_VERSION_MASK) < data->fast_version)
+ data->fast_version = flags & EAP_PEAP_VERSION_MASK;
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Using FAST version %d",
+ data->fast_version);
+
+ a_id = eap_fast_get_a_id(pos, left, &a_id_len);
+ eap_fast_select_pac(data, a_id, a_id_len);
+
+ if (data->resuming && data->current_pac) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Trying to resume session - "
+ "do not add PAC-Opaque to TLS ClientHello");
+ if (eap_fast_clear_pac_opaque_ext(sm, data) < 0)
+ return -1;
+ } else if (data->current_pac) {
+ /*
+ * PAC found for the A-ID and we are not resuming an old
+ * session, so add PAC-Opaque extension to ClientHello.
+ */
+ if (eap_fast_use_pac_opaque(sm, data, data->current_pac) < 0)
+ return -1;
+ } else {
+ /* No PAC found, so we must provision one. */
+ if (!data->provisioning_allowed) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC found and "
+ "provisioning disabled");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC found - "
+ "starting provisioning");
+ if (eap_fast_set_provisioning_ciphers(sm, data) < 0 ||
+ eap_fast_clear_pac_opaque_ext(sm, data) < 0)
+ return -1;
+ data->provisioning = 1;
+ }
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ const struct eap_hdr *req;
+ size_t left;
+ int res;
+ u8 flags, id;
+ struct wpabuf *resp;
+ const u8 *pos;
+ struct eap_fast_data *data = priv;
+
+ pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_FAST, ret,
+ reqData, &left, &flags);
+ if (pos == NULL)
+ return NULL;
+
+ req = wpabuf_head(reqData);
+ id = req->identifier;
+
+ if (flags & EAP_TLS_FLAGS_START) {
+ if (eap_fast_process_start(sm, data, flags, pos, left) < 0)
+ return NULL;
+
+ left = 0; /* A-ID is not used in further packet processing */
+ }
+
+ resp = NULL;
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+ !data->resuming) {
+ /* Process tunneled (encrypted) phase 2 data. */
+ struct wpabuf msg;
+ wpabuf_set(&msg, pos, left);
+ res = eap_fast_decrypt(sm, data, ret, req, &msg, &resp);
+ if (res < 0) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ /*
+ * Ack possible Alert that may have caused failure in
+ * decryption.
+ */
+ res = 1;
+ }
+ } else {
+ /* Continue processing TLS handshake (phase 1). */
+ res = eap_peer_tls_process_helper(sm, &data->ssl,
+ EAP_TYPE_FAST,
+ data->fast_version, id, pos,
+ left, &resp);
+
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+ char cipher[80];
+ wpa_printf(MSG_DEBUG,
+ "EAP-FAST: TLS done, proceed to Phase 2");
+ if (data->provisioning &&
+ (!(data->provisioning_allowed &
+ EAP_FAST_PROV_AUTH) ||
+ tls_get_cipher(sm->ssl_ctx, data->ssl.conn,
+ cipher, sizeof(cipher)) < 0 ||
+ os_strstr(cipher, "ADH-") ||
+ os_strstr(cipher, "anon"))) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Using "
+ "anonymous (unauthenticated) "
+ "provisioning");
+ data->anon_provisioning = 1;
+ } else
+ data->anon_provisioning = 0;
+ data->resuming = 0;
+ eap_fast_derive_keys(sm, data);
+ }
+
+ if (res == 2) {
+ struct wpabuf msg;
+ /*
+ * Application data included in the handshake message.
+ */
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = resp;
+ resp = NULL;
+ wpabuf_set(&msg, pos, left);
+ res = eap_fast_decrypt(sm, data, ret, req, &msg,
+ &resp);
+ }
+ }
+
+ if (res == 1) {
+ wpabuf_free(resp);
+ return eap_peer_tls_build_ack(id, EAP_TYPE_FAST,
+ data->fast_version);
+ }
+
+ return resp;
+}
+
+
+#if 0 /* FIX */
+static Boolean eap_fast_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+ struct eap_fast_data *data = priv;
+ return tls_connection_established(sm->ssl_ctx, data->ssl.conn);
+}
+
+
+static void eap_fast_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_fast_data *data = priv;
+ os_free(data->key_block_p);
+ data->key_block_p = NULL;
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = NULL;
+}
+
+
+static void * eap_fast_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_fast_data *data = priv;
+ if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
+ os_free(data);
+ return NULL;
+ }
+ if (data->phase2_priv && data->phase2_method &&
+ data->phase2_method->init_for_reauth)
+ data->phase2_method->init_for_reauth(sm, data->phase2_priv);
+ data->phase2_success = 0;
+ data->resuming = 1;
+ data->provisioning = 0;
+ data->anon_provisioning = 0;
+ data->simck_idx = 0;
+ return priv;
+}
+#endif
+
+
+static int eap_fast_get_status(struct eap_sm *sm, void *priv, char *buf,
+ size_t buflen, int verbose)
+{
+ struct eap_fast_data *data = priv;
+ int len, ret;
+
+ len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);
+ if (data->phase2_method) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "EAP-FAST Phase2 method=%s\n",
+ data->phase2_method->name);
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ len += ret;
+ }
+ return len;
+}
+
+
+static Boolean eap_fast_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_fast_data *data = priv;
+ return data->success;
+}
+
+
+static u8 * eap_fast_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_fast_data *data = priv;
+ u8 *key;
+
+ if (!data->success)
+ return NULL;
+
+ key = os_malloc(EAP_FAST_KEY_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_FAST_KEY_LEN;
+ os_memcpy(key, data->key_data, EAP_FAST_KEY_LEN);
+
+ return key;
+}
+
+
+static u8 * eap_fast_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_fast_data *data = priv;
+ u8 *key;
+
+ if (!data->success)
+ return NULL;
+
+ key = os_malloc(EAP_EMSK_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_EMSK_LEN;
+ os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+
+ return key;
+}
+
+
+int eap_peer_fast_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_FAST, "FAST");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_fast_init;
+ eap->deinit = eap_fast_deinit;
+ eap->process = eap_fast_process;
+ eap->isKeyAvailable = eap_fast_isKeyAvailable;
+ eap->getKey = eap_fast_getKey;
+ eap->get_status = eap_fast_get_status;
+#if 0
+ eap->has_reauth_data = eap_fast_has_reauth_data;
+ eap->deinit_for_reauth = eap_fast_deinit_for_reauth;
+ eap->init_for_reauth = eap_fast_init_for_reauth;
+#endif
+ eap->get_emsk = eap_fast_get_emsk;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_fast_pac.c b/src/eap_peer/eap_fast_pac.c
new file mode 100644
index 0000000..1583f49
--- /dev/null
+++ b/src/eap_peer/eap_fast_pac.c
@@ -0,0 +1,916 @@
+/*
+ * EAP peer method: EAP-FAST PAC file processing
+ * Copyright (c) 2004-2006, 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_config.h"
+#include "eap_i.h"
+#include "eap_fast_pac.h"
+
+/* TODO: encrypt PAC-Key in the PAC file */
+
+
+/* Text data format */
+static const char *pac_file_hdr =
+ "wpa_supplicant EAP-FAST PAC file - version 1";
+
+/*
+ * Binary data format
+ * 4-octet magic value: 6A E4 92 0C
+ * 2-octet version (big endian)
+ * <version specific data>
+ *
+ * version=0:
+ * Sequence of PAC entries:
+ * 2-octet PAC-Type (big endian)
+ * 32-octet PAC-Key
+ * 2-octet PAC-Opaque length (big endian)
+ * <variable len> PAC-Opaque data (length bytes)
+ * 2-octet PAC-Info length (big endian)
+ * <variable len> PAC-Info data (length bytes)
+ */
+
+#define EAP_FAST_PAC_BINARY_MAGIC 0x6ae4920c
+#define EAP_FAST_PAC_BINARY_FORMAT_VERSION 0
+
+
+/**
+ * eap_fast_free_pac - Free PAC data
+ * @pac: Pointer to the PAC entry
+ *
+ * Note that the PAC entry must not be in a list since this function does not
+ * remove the list links.
+ */
+void eap_fast_free_pac(struct eap_fast_pac *pac)
+{
+ os_free(pac->pac_opaque);
+ os_free(pac->pac_info);
+ os_free(pac->a_id);
+ os_free(pac->i_id);
+ os_free(pac->a_id_info);
+ os_free(pac);
+}
+
+
+/**
+ * eap_fast_get_pac - Get a PAC entry based on A-ID
+ * @pac_root: Pointer to root of the PAC list
+ * @a_id: A-ID to search for
+ * @a_id_len: Length of A-ID
+ * @pac_type: PAC-Type to search for
+ * Returns: Pointer to the PAC entry, or %NULL if A-ID not found
+ */
+struct eap_fast_pac * eap_fast_get_pac(struct eap_fast_pac *pac_root,
+ const u8 *a_id, size_t a_id_len,
+ u16 pac_type)
+{
+ struct eap_fast_pac *pac = pac_root;
+
+ while (pac) {
+ if (pac->pac_type == pac_type && pac->a_id_len == a_id_len &&
+ os_memcmp(pac->a_id, a_id, a_id_len) == 0) {
+ return pac;
+ }
+ pac = pac->next;
+ }
+ return NULL;
+}
+
+
+static void eap_fast_remove_pac(struct eap_fast_pac **pac_root,
+ struct eap_fast_pac **pac_current,
+ const u8 *a_id, size_t a_id_len, u16 pac_type)
+{
+ struct eap_fast_pac *pac, *prev;
+
+ pac = *pac_root;
+ prev = NULL;
+
+ while (pac) {
+ if (pac->pac_type == pac_type && pac->a_id_len == a_id_len &&
+ os_memcmp(pac->a_id, a_id, a_id_len) == 0) {
+ if (prev == NULL)
+ *pac_root = pac->next;
+ else
+ prev->next = pac->next;
+ if (*pac_current == pac)
+ *pac_current = NULL;
+ eap_fast_free_pac(pac);
+ break;
+ }
+ prev = pac;
+ pac = pac->next;
+ }
+}
+
+
+static int eap_fast_copy_buf(u8 **dst, size_t *dst_len,
+ const u8 *src, size_t src_len)
+{
+ if (src) {
+ *dst = os_malloc(src_len);
+ if (*dst == NULL)
+ return -1;
+ os_memcpy(*dst, src, src_len);
+ *dst_len = src_len;
+ }
+ return 0;
+}
+
+
+/**
+ * eap_fast_add_pac - Add a copy of a PAC entry to a list
+ * @pac_root: Pointer to PAC list root pointer
+ * @pac_current: Pointer to the current PAC pointer
+ * @entry: New entry to clone and add to the list
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function makes a clone of the given PAC entry and adds this copied
+ * entry to the list (pac_root). If an old entry for the same A-ID is found,
+ * it will be removed from the PAC list and in this case, pac_current entry
+ * is set to %NULL if it was the removed entry.
+ */
+int eap_fast_add_pac(struct eap_fast_pac **pac_root,
+ struct eap_fast_pac **pac_current,
+ struct eap_fast_pac *entry)
+{
+ struct eap_fast_pac *pac;
+
+ if (entry == NULL || entry->a_id == NULL)
+ return -1;
+
+ /* Remove a possible old entry for the matching A-ID. */
+ eap_fast_remove_pac(pac_root, pac_current,
+ entry->a_id, entry->a_id_len, entry->pac_type);
+
+ /* Allocate a new entry and add it to the list of PACs. */
+ pac = os_zalloc(sizeof(*pac));
+ if (pac == NULL)
+ return -1;
+
+ pac->pac_type = entry->pac_type;
+ os_memcpy(pac->pac_key, entry->pac_key, EAP_FAST_PAC_KEY_LEN);
+ if (eap_fast_copy_buf(&pac->pac_opaque, &pac->pac_opaque_len,
+ entry->pac_opaque, entry->pac_opaque_len) < 0 ||
+ eap_fast_copy_buf(&pac->pac_info, &pac->pac_info_len,
+ entry->pac_info, entry->pac_info_len) < 0 ||
+ eap_fast_copy_buf(&pac->a_id, &pac->a_id_len,
+ entry->a_id, entry->a_id_len) < 0 ||
+ eap_fast_copy_buf(&pac->i_id, &pac->i_id_len,
+ entry->i_id, entry->i_id_len) < 0 ||
+ eap_fast_copy_buf(&pac->a_id_info, &pac->a_id_info_len,
+ entry->a_id_info, entry->a_id_info_len) < 0) {
+ eap_fast_free_pac(pac);
+ return -1;
+ }
+
+ pac->next = *pac_root;
+ *pac_root = pac;
+
+ return 0;
+}
+
+
+struct eap_fast_read_ctx {
+ FILE *f;
+ const char *pos;
+ const char *end;
+ int line;
+ char *buf;
+ size_t buf_len;
+};
+
+static int eap_fast_read_line(struct eap_fast_read_ctx *rc, char **value)
+{
+ char *pos;
+
+ rc->line++;
+ if (rc->f) {
+ if (fgets(rc->buf, rc->buf_len, rc->f) == NULL)
+ return -1;
+ } else {
+ const char *l_end;
+ size_t len;
+ if (rc->pos >= rc->end)
+ return -1;
+ l_end = rc->pos;
+ while (l_end < rc->end && *l_end != '\n')
+ l_end++;
+ len = l_end - rc->pos;
+ if (len >= rc->buf_len)
+ len = rc->buf_len - 1;
+ os_memcpy(rc->buf, rc->pos, len);
+ rc->buf[len] = '\0';
+ rc->pos = l_end + 1;
+ }
+
+ rc->buf[rc->buf_len - 1] = '\0';
+ pos = rc->buf;
+ while (*pos != '\0') {
+ if (*pos == '\n' || *pos == '\r') {
+ *pos = '\0';
+ break;
+ }
+ pos++;
+ }
+
+ pos = os_strchr(rc->buf, '=');
+ if (pos)
+ *pos++ = '\0';
+ *value = pos;
+
+ return 0;
+}
+
+
+static u8 * eap_fast_parse_hex(const char *value, size_t *len)
+{
+ int hlen;
+ u8 *buf;
+
+ if (value == NULL)
+ return NULL;
+ hlen = os_strlen(value);
+ if (hlen & 1)
+ return NULL;
+ *len = hlen / 2;
+ buf = os_malloc(*len);
+ if (buf == NULL)
+ return NULL;
+ if (hexstr2bin(value, buf, *len)) {
+ os_free(buf);
+ return NULL;
+ }
+ return buf;
+}
+
+
+static int eap_fast_init_pac_data(struct eap_sm *sm, const char *pac_file,
+ struct eap_fast_read_ctx *rc)
+{
+ os_memset(rc, 0, sizeof(*rc));
+
+ rc->buf_len = 2048;
+ rc->buf = os_malloc(rc->buf_len);
+ if (rc->buf == NULL)
+ return -1;
+
+ if (os_strncmp(pac_file, "blob://", 7) == 0) {
+ const struct wpa_config_blob *blob;
+ blob = eap_get_config_blob(sm, pac_file + 7);
+ if (blob == NULL) {
+ wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - "
+ "assume no PAC entries have been "
+ "provisioned", pac_file + 7);
+ os_free(rc->buf);
+ return -1;
+ }
+ rc->pos = (char *) blob->data;
+ rc->end = (char *) blob->data + blob->len;
+ } else {
+ rc->f = fopen(pac_file, "rb");
+ if (rc->f == NULL) {
+ wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - "
+ "assume no PAC entries have been "
+ "provisioned", pac_file);
+ os_free(rc->buf);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+static void eap_fast_deinit_pac_data(struct eap_fast_read_ctx *rc)
+{
+ os_free(rc->buf);
+ if (rc->f)
+ fclose(rc->f);
+}
+
+
+static const char * eap_fast_parse_start(struct eap_fast_pac **pac)
+{
+ if (*pac)
+ return "START line without END";
+
+ *pac = os_zalloc(sizeof(struct eap_fast_pac));
+ if (*pac == NULL)
+ return "No memory for PAC entry";
+ (*pac)->pac_type = PAC_TYPE_TUNNEL_PAC;
+ return NULL;
+}
+
+
+static const char * eap_fast_parse_end(struct eap_fast_pac **pac_root,
+ struct eap_fast_pac **pac)
+{
+ if (*pac == NULL)
+ return "END line without START";
+ if (*pac_root) {
+ struct eap_fast_pac *end = *pac_root;
+ while (end->next)
+ end = end->next;
+ end->next = *pac;
+ } else
+ *pac_root = *pac;
+
+ *pac = NULL;
+ return NULL;
+}
+
+
+static const char * eap_fast_parse_pac_type(struct eap_fast_pac *pac,
+ char *pos)
+{
+ pac->pac_type = atoi(pos);
+ if (pac->pac_type != PAC_TYPE_TUNNEL_PAC &&
+ pac->pac_type != PAC_TYPE_USER_AUTHORIZATION &&
+ pac->pac_type != PAC_TYPE_MACHINE_AUTHENTICATION)
+ return "Unrecognized PAC-Type";
+
+ return NULL;
+}
+
+
+static const char * eap_fast_parse_pac_key(struct eap_fast_pac *pac, char *pos)
+{
+ u8 *key;
+ size_t key_len;
+
+ key = eap_fast_parse_hex(pos, &key_len);
+ if (key == NULL || key_len != EAP_FAST_PAC_KEY_LEN) {
+ os_free(key);
+ return "Invalid PAC-Key";
+ }
+
+ os_memcpy(pac->pac_key, key, EAP_FAST_PAC_KEY_LEN);
+ os_free(key);
+
+ return NULL;
+}
+
+
+static const char * eap_fast_parse_pac_opaque(struct eap_fast_pac *pac,
+ char *pos)
+{
+ os_free(pac->pac_opaque);
+ pac->pac_opaque = eap_fast_parse_hex(pos, &pac->pac_opaque_len);
+ if (pac->pac_opaque == NULL)
+ return "Invalid PAC-Opaque";
+ return NULL;
+}
+
+
+static const char * eap_fast_parse_a_id(struct eap_fast_pac *pac, char *pos)
+{
+ os_free(pac->a_id);
+ pac->a_id = eap_fast_parse_hex(pos, &pac->a_id_len);
+ if (pac->a_id == NULL)
+ return "Invalid A-ID";
+ return NULL;
+}
+
+
+static const char * eap_fast_parse_i_id(struct eap_fast_pac *pac, char *pos)
+{
+ os_free(pac->i_id);
+ pac->i_id = eap_fast_parse_hex(pos, &pac->i_id_len);
+ if (pac->i_id == NULL)
+ return "Invalid I-ID";
+ return NULL;
+}
+
+
+static const char * eap_fast_parse_a_id_info(struct eap_fast_pac *pac,
+ char *pos)
+{
+ os_free(pac->a_id_info);
+ pac->a_id_info = eap_fast_parse_hex(pos, &pac->a_id_info_len);
+ if (pac->a_id_info == NULL)
+ return "Invalid A-ID-Info";
+ return NULL;
+}
+
+
+/**
+ * eap_fast_load_pac - Load PAC entries (text format)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @pac_root: Pointer to root of the PAC list (to be filled)
+ * @pac_file: Name of the PAC file/blob to load
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_pac **pac_root,
+ const char *pac_file)
+{
+ struct eap_fast_read_ctx rc;
+ struct eap_fast_pac *pac = NULL;
+ int count = 0;
+ char *pos;
+ const char *err = NULL;
+
+ if (pac_file == NULL)
+ return -1;
+
+ if (eap_fast_init_pac_data(sm, pac_file, &rc) < 0)
+ return 0;
+
+ if (eap_fast_read_line(&rc, &pos) < 0 ||
+ os_strcmp(pac_file_hdr, rc.buf) != 0)
+ err = "Unrecognized header line";
+
+ while (!err && eap_fast_read_line(&rc, &pos) == 0) {
+ if (os_strcmp(rc.buf, "START") == 0)
+ err = eap_fast_parse_start(&pac);
+ else if (os_strcmp(rc.buf, "END") == 0) {
+ err = eap_fast_parse_end(pac_root, &pac);
+ count++;
+ } else if (!pac)
+ err = "Unexpected line outside START/END block";
+ else if (os_strcmp(rc.buf, "PAC-Type") == 0)
+ err = eap_fast_parse_pac_type(pac, pos);
+ else if (os_strcmp(rc.buf, "PAC-Key") == 0)
+ err = eap_fast_parse_pac_key(pac, pos);
+ else if (os_strcmp(rc.buf, "PAC-Opaque") == 0)
+ err = eap_fast_parse_pac_opaque(pac, pos);
+ else if (os_strcmp(rc.buf, "A-ID") == 0)
+ err = eap_fast_parse_a_id(pac, pos);
+ else if (os_strcmp(rc.buf, "I-ID") == 0)
+ err = eap_fast_parse_i_id(pac, pos);
+ else if (os_strcmp(rc.buf, "A-ID-Info") == 0)
+ err = eap_fast_parse_a_id_info(pac, pos);
+ }
+
+ if (pac) {
+ err = "PAC block not terminated with END";
+ eap_fast_free_pac(pac);
+ }
+
+ eap_fast_deinit_pac_data(&rc);
+
+ if (err) {
+ wpa_printf(MSG_INFO, "EAP-FAST: %s in '%s:%d'",
+ err, pac_file, rc.line);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Read %d PAC entries from '%s'",
+ count, pac_file);
+
+ return 0;
+}
+
+
+static void eap_fast_write(char **buf, char **pos, size_t *buf_len,
+ const char *field, const u8 *data,
+ size_t len, int txt)
+{
+ size_t i, need;
+ int ret;
+
+ if (data == NULL || *buf == NULL)
+ return;
+
+ need = os_strlen(field) + len * 2 + 30;
+ if (txt)
+ need += os_strlen(field) + len + 20;
+
+ if (*pos - *buf + need > *buf_len) {
+ char *nbuf = os_realloc(*buf, *buf_len + need);
+ if (nbuf == NULL) {
+ os_free(*buf);
+ *buf = NULL;
+ return;
+ }
+ *buf = nbuf;
+ *buf_len += need;
+ }
+
+ ret = os_snprintf(*pos, *buf + *buf_len - *pos, "%s=", field);
+ if (ret < 0 || ret >= *buf + *buf_len - *pos)
+ return;
+ *pos += ret;
+ *pos += wpa_snprintf_hex(*pos, *buf + *buf_len - *pos, data, len);
+ ret = os_snprintf(*pos, *buf + *buf_len - *pos, "\n");
+ if (ret < 0 || ret >= *buf + *buf_len - *pos)
+ return;
+ *pos += ret;
+
+ if (txt) {
+ ret = os_snprintf(*pos, *buf + *buf_len - *pos,
+ "%s-txt=", field);
+ if (ret < 0 || ret >= *buf + *buf_len - *pos)
+ return;
+ *pos += ret;
+ for (i = 0; i < len; i++) {
+ ret = os_snprintf(*pos, *buf + *buf_len - *pos,
+ "%c", data[i]);
+ if (ret < 0 || ret >= *buf + *buf_len - *pos)
+ return;
+ *pos += ret;
+ }
+ ret = os_snprintf(*pos, *buf + *buf_len - *pos, "\n");
+ if (ret < 0 || ret >= *buf + *buf_len - *pos)
+ return;
+ *pos += ret;
+ }
+}
+
+
+static int eap_fast_write_pac(struct eap_sm *sm, const char *pac_file,
+ char *buf, size_t len)
+{
+ if (os_strncmp(pac_file, "blob://", 7) == 0) {
+ struct wpa_config_blob *blob;
+ blob = os_zalloc(sizeof(*blob));
+ if (blob == NULL)
+ return -1;
+ blob->data = (u8 *) buf;
+ blob->len = len;
+ buf = NULL;
+ blob->name = os_strdup(pac_file + 7);
+ if (blob->name == NULL) {
+ os_free(blob);
+ return -1;
+ }
+ eap_set_config_blob(sm, blob);
+ } else {
+ FILE *f;
+ f = fopen(pac_file, "wb");
+ if (f == NULL) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Failed to open PAC "
+ "file '%s' for writing", pac_file);
+ return -1;
+ }
+ fwrite(buf, 1, len, f);
+ os_free(buf);
+ fclose(f);
+ }
+
+ return 0;
+}
+
+
+static int eap_fast_add_pac_data(struct eap_fast_pac *pac, char **buf,
+ char **pos, size_t *buf_len)
+{
+ int ret;
+
+ ret = os_snprintf(*pos, *buf + *buf_len - *pos,
+ "START\nPAC-Type=%d\n", pac->pac_type);
+ if (ret < 0 || ret >= *buf + *buf_len - *pos)
+ return -1;
+
+ *pos += ret;
+ eap_fast_write(buf, pos, buf_len, "PAC-Key",
+ pac->pac_key, EAP_FAST_PAC_KEY_LEN, 0);
+ eap_fast_write(buf, pos, buf_len, "PAC-Opaque",
+ pac->pac_opaque, pac->pac_opaque_len, 0);
+ eap_fast_write(buf, pos, buf_len, "PAC-Info",
+ pac->pac_info, pac->pac_info_len, 0);
+ eap_fast_write(buf, pos, buf_len, "A-ID",
+ pac->a_id, pac->a_id_len, 0);
+ eap_fast_write(buf, pos, buf_len, "I-ID",
+ pac->i_id, pac->i_id_len, 1);
+ eap_fast_write(buf, pos, buf_len, "A-ID-Info",
+ pac->a_id_info, pac->a_id_info_len, 1);
+ if (*buf == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: No memory for PAC "
+ "data");
+ return -1;
+ }
+ ret = os_snprintf(*pos, *buf + *buf_len - *pos, "END\n");
+ if (ret < 0 || ret >= *buf + *buf_len - *pos)
+ return -1;
+ *pos += ret;
+
+ return 0;
+}
+
+
+/**
+ * eap_fast_save_pac - Save PAC entries (text format)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @pac_root: Root of the PAC list
+ * @pac_file: Name of the PAC file/blob
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_fast_save_pac(struct eap_sm *sm, struct eap_fast_pac *pac_root,
+ const char *pac_file)
+{
+ struct eap_fast_pac *pac;
+ int ret, count = 0;
+ char *buf, *pos;
+ size_t buf_len;
+
+ if (pac_file == NULL)
+ return -1;
+
+ buf_len = 1024;
+ pos = buf = os_malloc(buf_len);
+ if (buf == NULL)
+ return -1;
+
+ ret = os_snprintf(pos, buf + buf_len - pos, "%s\n", pac_file_hdr);
+ if (ret < 0 || ret >= buf + buf_len - pos) {
+ os_free(buf);
+ return -1;
+ }
+ pos += ret;
+
+ pac = pac_root;
+ while (pac) {
+ if (eap_fast_add_pac_data(pac, &buf, &pos, &buf_len)) {
+ os_free(buf);
+ return -1;
+ }
+ count++;
+ pac = pac->next;
+ }
+
+ if (eap_fast_write_pac(sm, pac_file, buf, pos - buf)) {
+ os_free(buf);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %d PAC entries into '%s'",
+ count, pac_file);
+
+ return 0;
+}
+
+
+/**
+ * eap_fast_pac_list_truncate - Truncate a PAC list to the given length
+ * @pac_root: Root of the PAC list
+ * @max_len: Maximum length of the list (>= 1)
+ * Returns: Number of PAC entries removed
+ */
+size_t eap_fast_pac_list_truncate(struct eap_fast_pac *pac_root,
+ size_t max_len)
+{
+ struct eap_fast_pac *pac, *prev;
+ size_t count;
+
+ pac = pac_root;
+ prev = NULL;
+ count = 0;
+
+ while (pac) {
+ count++;
+ if (count > max_len)
+ break;
+ prev = pac;
+ pac = pac->next;
+ }
+
+ if (count <= max_len || prev == NULL)
+ return 0;
+
+ count = 0;
+ prev->next = NULL;
+
+ while (pac) {
+ prev = pac;
+ pac = pac->next;
+ eap_fast_free_pac(prev);
+ count++;
+ }
+
+ return count;
+}
+
+
+static void eap_fast_pac_get_a_id(struct eap_fast_pac *pac)
+{
+ u8 *pos, *end;
+ u16 type, len;
+
+ pos = pac->pac_info;
+ end = pos + pac->pac_info_len;
+
+ while (pos + 4 < end) {
+ type = WPA_GET_BE16(pos);
+ pos += 2;
+ len = WPA_GET_BE16(pos);
+ pos += 2;
+ if (pos + len > end)
+ break;
+
+ if (type == PAC_TYPE_A_ID) {
+ os_free(pac->a_id);
+ pac->a_id = os_malloc(len);
+ if (pac->a_id == NULL)
+ break;
+ os_memcpy(pac->a_id, pos, len);
+ pac->a_id_len = len;
+ }
+
+ if (type == PAC_TYPE_A_ID_INFO) {
+ os_free(pac->a_id_info);
+ pac->a_id_info = os_malloc(len);
+ if (pac->a_id_info == NULL)
+ break;
+ os_memcpy(pac->a_id_info, pos, len);
+ pac->a_id_info_len = len;
+ }
+
+ pos += len;
+ }
+}
+
+
+/**
+ * eap_fast_load_pac_bin - Load PAC entries (binary format)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @pac_root: Pointer to root of the PAC list (to be filled)
+ * @pac_file: Name of the PAC file/blob to load
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root,
+ const char *pac_file)
+{
+ const struct wpa_config_blob *blob = NULL;
+ u8 *buf, *end, *pos;
+ size_t len, count = 0;
+ struct eap_fast_pac *pac, *prev;
+
+ *pac_root = NULL;
+
+ if (pac_file == NULL)
+ return -1;
+
+ if (os_strncmp(pac_file, "blob://", 7) == 0) {
+ blob = eap_get_config_blob(sm, pac_file + 7);
+ if (blob == NULL) {
+ wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - "
+ "assume no PAC entries have been "
+ "provisioned", pac_file + 7);
+ return 0;
+ }
+ buf = blob->data;
+ len = blob->len;
+ } else {
+ buf = (u8 *) os_readfile(pac_file, &len);
+ if (buf == NULL) {
+ wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - "
+ "assume no PAC entries have been "
+ "provisioned", pac_file);
+ return 0;
+ }
+ }
+
+ if (len == 0) {
+ if (blob == NULL)
+ os_free(buf);
+ return 0;
+ }
+
+ if (len < 6 || WPA_GET_BE32(buf) != EAP_FAST_PAC_BINARY_MAGIC ||
+ WPA_GET_BE16(buf + 4) != EAP_FAST_PAC_BINARY_FORMAT_VERSION) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Invalid PAC file '%s' (bin)",
+ pac_file);
+ if (blob == NULL)
+ os_free(buf);
+ return -1;
+ }
+
+ pac = prev = NULL;
+ pos = buf + 6;
+ end = buf + len;
+ while (pos < end) {
+ if (end - pos < 2 + 32 + 2 + 2)
+ goto parse_fail;
+
+ pac = os_zalloc(sizeof(*pac));
+ if (pac == NULL)
+ goto parse_fail;
+
+ pac->pac_type = WPA_GET_BE16(pos);
+ pos += 2;
+ os_memcpy(pac->pac_key, pos, EAP_FAST_PAC_KEY_LEN);
+ pos += EAP_FAST_PAC_KEY_LEN;
+ pac->pac_opaque_len = WPA_GET_BE16(pos);
+ pos += 2;
+ if (pos + pac->pac_opaque_len + 2 > end)
+ goto parse_fail;
+ pac->pac_opaque = os_malloc(pac->pac_opaque_len);
+ if (pac->pac_opaque == NULL)
+ goto parse_fail;
+ os_memcpy(pac->pac_opaque, pos, pac->pac_opaque_len);
+ pos += pac->pac_opaque_len;
+ pac->pac_info_len = WPA_GET_BE16(pos);
+ pos += 2;
+ if (pos + pac->pac_info_len > end)
+ goto parse_fail;
+ pac->pac_info = os_malloc(pac->pac_info_len);
+ if (pac->pac_info == NULL)
+ goto parse_fail;
+ os_memcpy(pac->pac_info, pos, pac->pac_info_len);
+ pos += pac->pac_info_len;
+ eap_fast_pac_get_a_id(pac);
+
+ count++;
+ if (prev)
+ prev->next = pac;
+ else
+ *pac_root = pac;
+ prev = pac;
+ }
+
+ if (blob == NULL)
+ os_free(buf);
+
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Read %d PAC entries from '%s' (bin)",
+ count, pac_file);
+
+ return 0;
+
+parse_fail:
+ wpa_printf(MSG_INFO, "EAP-FAST: Failed to parse PAC file '%s' (bin)",
+ pac_file);
+ if (blob == NULL)
+ os_free(buf);
+ if (pac)
+ eap_fast_free_pac(pac);
+ return -1;
+}
+
+
+/**
+ * eap_fast_save_pac_bin - Save PAC entries (binary format)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @pac_root: Root of the PAC list
+ * @pac_file: Name of the PAC file/blob
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_fast_save_pac_bin(struct eap_sm *sm, struct eap_fast_pac *pac_root,
+ const char *pac_file)
+{
+ size_t len, count = 0;
+ struct eap_fast_pac *pac;
+ u8 *buf, *pos;
+
+ len = 6;
+ pac = pac_root;
+ while (pac) {
+ if (pac->pac_opaque_len > 65535 ||
+ pac->pac_info_len > 65535)
+ return -1;
+ len += 2 + EAP_FAST_PAC_KEY_LEN + 2 + pac->pac_opaque_len +
+ 2 + pac->pac_info_len;
+ pac = pac->next;
+ }
+
+ buf = os_malloc(len);
+ if (buf == NULL)
+ return -1;
+
+ pos = buf;
+ WPA_PUT_BE32(pos, EAP_FAST_PAC_BINARY_MAGIC);
+ pos += 4;
+ WPA_PUT_BE16(pos, EAP_FAST_PAC_BINARY_FORMAT_VERSION);
+ pos += 2;
+
+ pac = pac_root;
+ while (pac) {
+ WPA_PUT_BE16(pos, pac->pac_type);
+ pos += 2;
+ os_memcpy(pos, pac->pac_key, EAP_FAST_PAC_KEY_LEN);
+ pos += EAP_FAST_PAC_KEY_LEN;
+ WPA_PUT_BE16(pos, pac->pac_opaque_len);
+ pos += 2;
+ os_memcpy(pos, pac->pac_opaque, pac->pac_opaque_len);
+ pos += pac->pac_opaque_len;
+ WPA_PUT_BE16(pos, pac->pac_info_len);
+ pos += 2;
+ os_memcpy(pos, pac->pac_info, pac->pac_info_len);
+ pos += pac->pac_info_len;
+
+ pac = pac->next;
+ count++;
+ }
+
+ if (eap_fast_write_pac(sm, pac_file, (char *) buf, len)) {
+ os_free(buf);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %d PAC entries into '%s' (bin)",
+ count, pac_file);
+
+ return 0;
+}
diff --git a/src/eap_peer/eap_fast_pac.h b/src/eap_peer/eap_fast_pac.h
new file mode 100644
index 0000000..9483f96
--- /dev/null
+++ b/src/eap_peer/eap_fast_pac.h
@@ -0,0 +1,56 @@
+/*
+ * EAP peer method: EAP-FAST PAC file processing
+ * Copyright (c) 2004-2007, 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.
+ */
+
+#ifndef EAP_FAST_PAC_H
+#define EAP_FAST_PAC_H
+
+#include "eap_common/eap_fast_common.h"
+
+struct eap_fast_pac {
+ struct eap_fast_pac *next;
+
+ u8 pac_key[EAP_FAST_PAC_KEY_LEN];
+ u8 *pac_opaque;
+ size_t pac_opaque_len;
+ u8 *pac_info;
+ size_t pac_info_len;
+ u8 *a_id;
+ size_t a_id_len;
+ u8 *i_id;
+ size_t i_id_len;
+ u8 *a_id_info;
+ size_t a_id_info_len;
+ u16 pac_type;
+};
+
+
+void eap_fast_free_pac(struct eap_fast_pac *pac);
+struct eap_fast_pac * eap_fast_get_pac(struct eap_fast_pac *pac_root,
+ const u8 *a_id, size_t a_id_len,
+ u16 pac_type);
+int eap_fast_add_pac(struct eap_fast_pac **pac_root,
+ struct eap_fast_pac **pac_current,
+ struct eap_fast_pac *entry);
+int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_pac **pac_root,
+ const char *pac_file);
+int eap_fast_save_pac(struct eap_sm *sm, struct eap_fast_pac *pac_root,
+ const char *pac_file);
+size_t eap_fast_pac_list_truncate(struct eap_fast_pac *pac_root,
+ size_t max_len);
+int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root,
+ const char *pac_file);
+int eap_fast_save_pac_bin(struct eap_sm *sm, struct eap_fast_pac *pac_root,
+ const char *pac_file);
+
+#endif /* EAP_FAST_PAC_H */
diff --git a/src/eap_peer/eap_gpsk.c b/src/eap_peer/eap_gpsk.c
new file mode 100644
index 0000000..963f41d
--- /dev/null
+++ b/src/eap_peer/eap_gpsk.c
@@ -0,0 +1,732 @@
+/*
+ * EAP peer method: EAP-GPSK (draft-ietf-emu-eap-gpsk-08.txt)
+ * Copyright (c) 2006-2008, 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_peer/eap_i.h"
+#include "eap_common/eap_gpsk_common.h"
+
+struct eap_gpsk_data {
+ enum { GPSK_1, GPSK_3, SUCCESS, FAILURE } state;
+ u8 rand_server[EAP_GPSK_RAND_LEN];
+ u8 rand_peer[EAP_GPSK_RAND_LEN];
+ u8 msk[EAP_MSK_LEN];
+ u8 emsk[EAP_EMSK_LEN];
+ u8 sk[EAP_GPSK_MAX_SK_LEN];
+ size_t sk_len;
+ u8 pk[EAP_GPSK_MAX_PK_LEN];
+ size_t pk_len;
+ u8 session_id;
+ int session_id_set;
+ u8 *id_peer;
+ size_t id_peer_len;
+ u8 *id_server;
+ size_t id_server_len;
+ int vendor; /* CSuite/Specifier */
+ int specifier; /* CSuite/Specifier */
+ u8 *psk;
+ size_t psk_len;
+};
+
+
+static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data,
+ u8 identifier,
+ const u8 *csuite_list,
+ size_t csuite_list_len);
+static struct wpabuf * eap_gpsk_send_gpsk_4(struct eap_gpsk_data *data,
+ u8 identifier);
+
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static const char * eap_gpsk_state_txt(int state)
+{
+ switch (state) {
+ case GPSK_1:
+ return "GPSK-1";
+ case GPSK_3:
+ return "GPSK-3";
+ case SUCCESS:
+ return "SUCCESS";
+ case FAILURE:
+ return "FAILURE";
+ default:
+ return "?";
+ }
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+static void eap_gpsk_state(struct eap_gpsk_data *data, int state)
+{
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: %s -> %s",
+ eap_gpsk_state_txt(data->state),
+ eap_gpsk_state_txt(state));
+ data->state = state;
+}
+
+
+static void eap_gpsk_deinit(struct eap_sm *sm, void *priv);
+
+
+static void * eap_gpsk_init(struct eap_sm *sm)
+{
+ struct eap_gpsk_data *data;
+ const u8 *identity, *password;
+ size_t identity_len, password_len;
+
+ password = eap_get_config_password(sm, &password_len);
+ if (password == NULL) {
+ wpa_printf(MSG_INFO, "EAP-GPSK: No key (password) configured");
+ return NULL;
+ }
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->state = GPSK_1;
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ if (identity) {
+ data->id_peer = os_malloc(identity_len);
+ if (data->id_peer == NULL) {
+ eap_gpsk_deinit(sm, data);
+ return NULL;
+ }
+ os_memcpy(data->id_peer, identity, identity_len);
+ data->id_peer_len = identity_len;
+ }
+
+ data->psk = os_malloc(password_len);
+ if (data->psk == NULL) {
+ eap_gpsk_deinit(sm, data);
+ return NULL;
+ }
+ os_memcpy(data->psk, password, password_len);
+ data->psk_len = password_len;
+
+ return data;
+}
+
+
+static void eap_gpsk_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_gpsk_data *data = priv;
+ os_free(data->id_server);
+ os_free(data->id_peer);
+ os_free(data->psk);
+ os_free(data);
+}
+
+
+const u8 * eap_gpsk_process_id_server(struct eap_gpsk_data *data,
+ const u8 *pos, const u8 *end)
+{
+ u16 alen;
+
+ if (end - pos < 2) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet");
+ return NULL;
+ }
+ alen = WPA_GET_BE16(pos);
+ pos += 2;
+ if (end - pos < alen) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: ID_Server overflow");
+ return NULL;
+ }
+ os_free(data->id_server);
+ data->id_server = os_malloc(alen);
+ if (data->id_server == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: No memory for ID_Server");
+ return NULL;
+ }
+ os_memcpy(data->id_server, pos, alen);
+ data->id_server_len = alen;
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server",
+ data->id_server, data->id_server_len);
+ pos += alen;
+
+ return pos;
+}
+
+
+const u8 * eap_gpsk_process_rand_server(struct eap_gpsk_data *data,
+ const u8 *pos, const u8 *end)
+{
+ if (pos == NULL)
+ return NULL;
+
+ if (end - pos < EAP_GPSK_RAND_LEN) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server overflow");
+ return NULL;
+ }
+ os_memcpy(data->rand_server, pos, EAP_GPSK_RAND_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server",
+ data->rand_server, EAP_GPSK_RAND_LEN);
+ pos += EAP_GPSK_RAND_LEN;
+
+ return pos;
+}
+
+
+static int eap_gpsk_select_csuite(struct eap_sm *sm,
+ struct eap_gpsk_data *data,
+ const u8 *csuite_list,
+ size_t csuite_list_len)
+{
+ struct eap_gpsk_csuite *csuite;
+ int i, count;
+
+ count = csuite_list_len / sizeof(struct eap_gpsk_csuite);
+ data->vendor = EAP_GPSK_VENDOR_IETF;
+ data->specifier = EAP_GPSK_CIPHER_RESERVED;
+ csuite = (struct eap_gpsk_csuite *) csuite_list;
+ for (i = 0; i < count; i++) {
+ int vendor, specifier;
+ vendor = WPA_GET_BE32(csuite->vendor);
+ specifier = WPA_GET_BE16(csuite->specifier);
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite[%d]: %d:%d",
+ i, vendor, specifier);
+ if (data->vendor == EAP_GPSK_VENDOR_IETF &&
+ data->specifier == EAP_GPSK_CIPHER_RESERVED &&
+ eap_gpsk_supported_ciphersuite(vendor, specifier)) {
+ data->vendor = vendor;
+ data->specifier = specifier;
+ }
+ csuite++;
+ }
+ if (data->vendor == EAP_GPSK_VENDOR_IETF &&
+ data->specifier == EAP_GPSK_CIPHER_RESERVED) {
+ wpa_msg(sm->msg_ctx, MSG_INFO, "EAP-GPSK: No supported "
+ "ciphersuite found");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Selected ciphersuite %d:%d",
+ data->vendor, data->specifier);
+
+ return 0;
+}
+
+
+const u8 * eap_gpsk_process_csuite_list(struct eap_sm *sm,
+ struct eap_gpsk_data *data,
+ const u8 **list, size_t *list_len,
+ const u8 *pos, const u8 *end)
+{
+ if (pos == NULL)
+ return NULL;
+
+ if (end - pos < 2) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet");
+ return NULL;
+ }
+ *list_len = WPA_GET_BE16(pos);
+ pos += 2;
+ if (end - pos < (int) *list_len) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_List overflow");
+ return NULL;
+ }
+ if (*list_len == 0 || (*list_len % sizeof(struct eap_gpsk_csuite))) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid CSuite_List len %d",
+ *list_len);
+ return NULL;
+ }
+ *list = pos;
+ pos += *list_len;
+
+ if (eap_gpsk_select_csuite(sm, data, *list, *list_len) < 0)
+ return NULL;
+
+ return pos;
+}
+
+
+static struct wpabuf * eap_gpsk_process_gpsk_1(struct eap_sm *sm,
+ struct eap_gpsk_data *data,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData,
+ const u8 *payload,
+ size_t payload_len)
+{
+ size_t csuite_list_len;
+ const u8 *csuite_list, *pos, *end;
+ struct wpabuf *resp;
+
+ if (data->state != GPSK_1) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Request/GPSK-1");
+
+ end = payload + payload_len;
+
+ pos = eap_gpsk_process_id_server(data, payload, end);
+ pos = eap_gpsk_process_rand_server(data, pos, end);
+ pos = eap_gpsk_process_csuite_list(sm, data, &csuite_list,
+ &csuite_list_len, pos, end);
+ if (pos == NULL) {
+ eap_gpsk_state(data, FAILURE);
+ return NULL;
+ }
+
+ resp = eap_gpsk_send_gpsk_2(data, eap_get_id(reqData),
+ csuite_list, csuite_list_len);
+ if (resp == NULL)
+ return NULL;
+
+ eap_gpsk_state(data, GPSK_3);
+
+ return resp;
+}
+
+
+static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data,
+ u8 identifier,
+ const u8 *csuite_list,
+ size_t csuite_list_len)
+{
+ struct wpabuf *resp;
+ size_t len, miclen;
+ u8 *rpos, *start;
+ struct eap_gpsk_csuite *csuite;
+
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Sending Response/GPSK-2");
+
+ miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
+ len = 1 + 2 + data->id_peer_len + 2 + data->id_server_len +
+ 2 * EAP_GPSK_RAND_LEN + 2 + csuite_list_len +
+ sizeof(struct eap_gpsk_csuite) + 2 + miclen;
+
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len,
+ EAP_CODE_RESPONSE, identifier);
+ if (resp == NULL)
+ return NULL;
+
+ wpabuf_put_u8(resp, EAP_GPSK_OPCODE_GPSK_2);
+ start = wpabuf_put(resp, 0);
+
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Peer",
+ data->id_peer, data->id_peer_len);
+ wpabuf_put_be16(resp, data->id_peer_len);
+ wpabuf_put_data(resp, data->id_peer, data->id_peer_len);
+
+ wpabuf_put_be16(resp, data->id_server_len);
+ wpabuf_put_data(resp, data->id_server, data->id_server_len);
+
+ if (os_get_random(data->rand_peer, EAP_GPSK_RAND_LEN)) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to get random data "
+ "for RAND_Peer");
+ eap_gpsk_state(data, FAILURE);
+ wpabuf_free(resp);
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer",
+ data->rand_peer, EAP_GPSK_RAND_LEN);
+ wpabuf_put_data(resp, data->rand_peer, EAP_GPSK_RAND_LEN);
+ wpabuf_put_data(resp, data->rand_server, EAP_GPSK_RAND_LEN);
+
+ wpabuf_put_be16(resp, csuite_list_len);
+ wpabuf_put_data(resp, csuite_list, csuite_list_len);
+
+ csuite = wpabuf_put(resp, sizeof(*csuite));
+ WPA_PUT_BE32(csuite->vendor, data->vendor);
+ WPA_PUT_BE16(csuite->specifier, data->specifier);
+
+ if (eap_gpsk_derive_keys(data->psk, data->psk_len,
+ data->vendor, data->specifier,
+ data->rand_peer, data->rand_server,
+ data->id_peer, data->id_peer_len,
+ data->id_server, data->id_server_len,
+ data->msk, data->emsk,
+ data->sk, &data->sk_len,
+ data->pk, &data->pk_len) < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive keys");
+ eap_gpsk_state(data, FAILURE);
+ wpabuf_free(resp);
+ return NULL;
+ }
+
+ /* No PD_Payload_1 */
+ wpabuf_put_be16(resp, 0);
+
+ rpos = wpabuf_put(resp, miclen);
+ if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
+ data->specifier, start, rpos - start, rpos) <
+ 0) {
+ eap_gpsk_state(data, FAILURE);
+ wpabuf_free(resp);
+ return NULL;
+ }
+
+ return resp;
+}
+
+
+const u8 * eap_gpsk_validate_rand(struct eap_gpsk_data *data, const u8 *pos,
+ const u8 *end)
+{
+ if (end - pos < EAP_GPSK_RAND_LEN) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
+ "RAND_Peer");
+ return NULL;
+ }
+ if (os_memcmp(pos, data->rand_peer, EAP_GPSK_RAND_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-2 and "
+ "GPSK-3 did not match");
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-2",
+ data->rand_peer, EAP_GPSK_RAND_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-3",
+ pos, EAP_GPSK_RAND_LEN);
+ return NULL;
+ }
+ pos += EAP_GPSK_RAND_LEN;
+
+ if (end - pos < EAP_GPSK_RAND_LEN) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
+ "RAND_Server");
+ return NULL;
+ }
+ if (os_memcmp(pos, data->rand_server, EAP_GPSK_RAND_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1 and "
+ "GPSK-3 did not match");
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1",
+ data->rand_server, EAP_GPSK_RAND_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-3",
+ pos, EAP_GPSK_RAND_LEN);
+ return NULL;
+ }
+ pos += EAP_GPSK_RAND_LEN;
+
+ return pos;
+}
+
+
+const u8 * eap_gpsk_validate_id_server(struct eap_gpsk_data *data,
+ const u8 *pos, const u8 *end)
+{
+ size_t len;
+
+ if (pos == NULL)
+ return NULL;
+
+ if (end - pos < (int) 2) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
+ "length(ID_Server)");
+ return NULL;
+ }
+
+ len = WPA_GET_BE16(pos);
+ pos += 2;
+
+ if (end - pos < (int) len) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
+ "ID_Server");
+ return NULL;
+ }
+
+ if (len != data->id_server_len ||
+ os_memcmp(pos, data->id_server, len) != 0) {
+ wpa_printf(MSG_INFO, "EAP-GPSK: ID_Server did not match with "
+ "the one used in GPSK-1");
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-1",
+ data->id_server, data->id_server_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-3",
+ pos, len);
+ }
+
+ pos += len;
+
+ return pos;
+}
+
+
+const u8 * eap_gpsk_validate_csuite(struct eap_gpsk_data *data, const u8 *pos,
+ const u8 *end)
+{
+ int vendor, specifier;
+ const struct eap_gpsk_csuite *csuite;
+
+ if (pos == NULL)
+ return NULL;
+
+ if (end - pos < (int) sizeof(*csuite)) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
+ "CSuite_Sel");
+ return NULL;
+ }
+ csuite = (const struct eap_gpsk_csuite *) pos;
+ vendor = WPA_GET_BE32(csuite->vendor);
+ specifier = WPA_GET_BE16(csuite->specifier);
+ pos += sizeof(*csuite);
+ if (vendor != data->vendor || specifier != data->specifier) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_Sel (%d:%d) does not "
+ "match with the one sent in GPSK-2 (%d:%d)",
+ vendor, specifier, data->vendor, data->specifier);
+ return NULL;
+ }
+
+ return pos;
+}
+
+
+const u8 * eap_gpsk_validate_pd_payload_2(struct eap_gpsk_data *data,
+ const u8 *pos, const u8 *end)
+{
+ u16 alen;
+
+ if (pos == NULL)
+ return NULL;
+
+ if (end - pos < 2) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
+ "PD_Payload_2 length");
+ return NULL;
+ }
+ alen = WPA_GET_BE16(pos);
+ pos += 2;
+ if (end - pos < alen) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
+ "%d-octet PD_Payload_2", alen);
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_2", pos, alen);
+ pos += alen;
+
+ return pos;
+}
+
+
+const u8 * eap_gpsk_validate_gpsk_3_mic(struct eap_gpsk_data *data,
+ const u8 *payload,
+ const u8 *pos, const u8 *end)
+{
+ size_t miclen;
+ u8 mic[EAP_GPSK_MAX_MIC_LEN];
+
+ if (pos == NULL)
+ return NULL;
+
+ miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
+ if (end - pos < (int) miclen) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC "
+ "(left=%d miclen=%d)", end - pos, miclen);
+ return NULL;
+ }
+ if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
+ data->specifier, payload, pos - payload, mic)
+ < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC");
+ return NULL;
+ }
+ if (os_memcmp(mic, pos, miclen) != 0) {
+ wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-3");
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen);
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen);
+ return NULL;
+ }
+ pos += miclen;
+
+ return pos;
+}
+
+
+static struct wpabuf * eap_gpsk_process_gpsk_3(struct eap_sm *sm,
+ struct eap_gpsk_data *data,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData,
+ const u8 *payload,
+ size_t payload_len)
+{
+ struct wpabuf *resp;
+ const u8 *pos, *end;
+
+ if (data->state != GPSK_3) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Request/GPSK-3");
+
+ end = payload + payload_len;
+
+ pos = eap_gpsk_validate_rand(data, payload, end);
+ pos = eap_gpsk_validate_id_server(data, pos, end);
+ pos = eap_gpsk_validate_csuite(data, pos, end);
+ pos = eap_gpsk_validate_pd_payload_2(data, pos, end);
+ pos = eap_gpsk_validate_gpsk_3_mic(data, payload, pos, end);
+
+ if (pos == NULL) {
+ eap_gpsk_state(data, FAILURE);
+ return NULL;
+ }
+ if (pos != end) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %d bytes of extra "
+ "data in the end of GPSK-2", end - pos);
+ }
+
+ resp = eap_gpsk_send_gpsk_4(data, eap_get_id(reqData));
+ if (resp == NULL)
+ return NULL;
+
+ eap_gpsk_state(data, SUCCESS);
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+
+ return resp;
+}
+
+
+static struct wpabuf * eap_gpsk_send_gpsk_4(struct eap_gpsk_data *data,
+ u8 identifier)
+{
+ struct wpabuf *resp;
+ u8 *rpos, *start;
+ size_t mlen;
+
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Sending Response/GPSK-4");
+
+ mlen = eap_gpsk_mic_len(data->vendor, data->specifier);
+
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, 1 + 2 + mlen,
+ EAP_CODE_RESPONSE, identifier);
+ if (resp == NULL)
+ return NULL;
+
+ wpabuf_put_u8(resp, EAP_GPSK_OPCODE_GPSK_4);
+ start = wpabuf_put(resp, 0);
+
+ /* No PD_Payload_3 */
+ wpabuf_put_be16(resp, 0);
+
+ rpos = wpabuf_put(resp, mlen);
+ if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
+ data->specifier, start, rpos - start, rpos) <
+ 0) {
+ eap_gpsk_state(data, FAILURE);
+ wpabuf_free(resp);
+ return NULL;
+ }
+
+ return resp;
+}
+
+
+static struct wpabuf * eap_gpsk_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct eap_gpsk_data *data = priv;
+ struct wpabuf *resp;
+ const u8 *pos;
+ size_t len;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, reqData, &len);
+ if (pos == NULL || len < 1) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode %d", *pos);
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = FALSE;
+
+ switch (*pos) {
+ case EAP_GPSK_OPCODE_GPSK_1:
+ resp = eap_gpsk_process_gpsk_1(sm, data, ret, reqData,
+ pos + 1, len - 1);
+ break;
+ case EAP_GPSK_OPCODE_GPSK_3:
+ resp = eap_gpsk_process_gpsk_3(sm, data, ret, reqData,
+ pos + 1, len - 1);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignoring message with "
+ "unknown opcode %d", *pos);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ return resp;
+}
+
+
+static Boolean eap_gpsk_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_gpsk_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+static u8 * eap_gpsk_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_gpsk_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(EAP_MSK_LEN);
+ if (key == NULL)
+ return NULL;
+ os_memcpy(key, data->msk, EAP_MSK_LEN);
+ *len = EAP_MSK_LEN;
+
+ return key;
+}
+
+
+static u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_gpsk_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(EAP_EMSK_LEN);
+ if (key == NULL)
+ return NULL;
+ os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+ *len = EAP_EMSK_LEN;
+
+ return key;
+}
+
+
+int eap_peer_gpsk_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_GPSK, "GPSK");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_gpsk_init;
+ eap->deinit = eap_gpsk_deinit;
+ eap->process = eap_gpsk_process;
+ eap->isKeyAvailable = eap_gpsk_isKeyAvailable;
+ eap->getKey = eap_gpsk_getKey;
+ eap->get_emsk = eap_gpsk_get_emsk;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_gtc.c b/src/eap_peer/eap_gtc.c
new file mode 100644
index 0000000..b2b554b
--- /dev/null
+++ b/src/eap_peer/eap_gtc.c
@@ -0,0 +1,151 @@
+/*
+ * EAP peer method: EAP-GTC (RFC 3748)
+ * Copyright (c) 2004-2006, 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+
+
+struct eap_gtc_data {
+ int prefix;
+};
+
+
+static void * eap_gtc_init(struct eap_sm *sm)
+{
+ struct eap_gtc_data *data;
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+
+ if (sm->m && sm->m->vendor == EAP_VENDOR_IETF &&
+ sm->m->method == EAP_TYPE_FAST) {
+ wpa_printf(MSG_DEBUG, "EAP-GTC: EAP-FAST tunnel - use prefix "
+ "with challenge/response");
+ data->prefix = 1;
+ }
+ return data;
+}
+
+
+static void eap_gtc_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_gtc_data *data = priv;
+ os_free(data);
+}
+
+
+static struct wpabuf * eap_gtc_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct eap_gtc_data *data = priv;
+ struct wpabuf *resp;
+ const u8 *pos, *password, *identity;
+ size_t password_len, identity_len, len, plen;
+ int otp;
+ u8 id;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GTC, reqData, &len);
+ if (pos == NULL) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ id = eap_get_id(reqData);
+
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-GTC: Request message", pos, len);
+ if (data->prefix &&
+ (len < 10 || os_memcmp(pos, "CHALLENGE=", 10) != 0)) {
+ wpa_printf(MSG_DEBUG, "EAP-GTC: Challenge did not start with "
+ "expected prefix");
+
+ /* Send an empty response in order to allow tunneled
+ * acknowledgement of the failure. This will also cover the
+ * error case which seems to use EAP-MSCHAPv2 like error
+ * reporting with EAP-GTC inside EAP-FAST tunnel. */
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GTC,
+ 0, EAP_CODE_RESPONSE, id);
+ return resp;
+ }
+
+ password = eap_get_config_otp(sm, &password_len);
+ if (password)
+ otp = 1;
+ else {
+ password = eap_get_config_password(sm, &password_len);
+ otp = 0;
+ }
+
+ if (password == NULL) {
+ wpa_printf(MSG_INFO, "EAP-GTC: Password not configured");
+ eap_sm_request_otp(sm, (const char *) pos, len);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ ret->ignore = FALSE;
+
+ ret->methodState = data->prefix ? METHOD_MAY_CONT : METHOD_DONE;
+ ret->decision = DECISION_COND_SUCC;
+ ret->allowNotifications = FALSE;
+
+ plen = password_len;
+ identity = eap_get_config_identity(sm, &identity_len);
+ if (identity == NULL)
+ return NULL;
+ if (data->prefix)
+ plen += 9 + identity_len + 1;
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GTC, plen,
+ EAP_CODE_RESPONSE, id);
+ if (resp == NULL)
+ return NULL;
+ if (data->prefix) {
+ wpabuf_put_data(resp, "RESPONSE=", 9);
+ wpabuf_put_data(resp, identity, identity_len);
+ wpabuf_put_u8(resp, '\0');
+ }
+ wpabuf_put_data(resp, password, password_len);
+ wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-GTC: Response",
+ wpabuf_head_u8(resp) + sizeof(struct eap_hdr) +
+ 1, plen);
+
+ if (otp) {
+ wpa_printf(MSG_DEBUG, "EAP-GTC: Forgetting used password");
+ eap_clear_config_otp(sm);
+ }
+
+ return resp;
+}
+
+
+int eap_peer_gtc_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_GTC, "GTC");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_gtc_init;
+ eap->deinit = eap_gtc_deinit;
+ eap->process = eap_gtc_process;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h
new file mode 100644
index 0000000..623701c
--- /dev/null
+++ b/src/eap_peer/eap_i.h
@@ -0,0 +1,353 @@
+/*
+ * EAP peer state machines internal structures (RFC 4137)
+ * Copyright (c) 2004-2007, 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.
+ */
+
+#ifndef EAP_I_H
+#define EAP_I_H
+
+#include "wpabuf.h"
+#include "eap_peer/eap.h"
+#include "eap_common/eap_common.h"
+
+/* RFC 4137 - EAP Peer state machine */
+
+typedef enum {
+ DECISION_FAIL, DECISION_COND_SUCC, DECISION_UNCOND_SUCC
+} EapDecision;
+
+typedef enum {
+ METHOD_NONE, METHOD_INIT, METHOD_CONT, METHOD_MAY_CONT, METHOD_DONE
+} EapMethodState;
+
+/**
+ * struct eap_method_ret - EAP return values from struct eap_method::process()
+ *
+ * These structure contains OUT variables for the interface between peer state
+ * machine and methods (RFC 4137, Sect. 4.2). eapRespData will be returned as
+ * the return value of struct eap_method::process() so it is not included in
+ * this structure.
+ */
+struct eap_method_ret {
+ /**
+ * ignore - Whether method decided to drop the current packed (OUT)
+ */
+ Boolean ignore;
+
+ /**
+ * methodState - Method-specific state (IN/OUT)
+ */
+ EapMethodState methodState;
+
+ /**
+ * decision - Authentication decision (OUT)
+ */
+ EapDecision decision;
+
+ /**
+ * allowNotifications - Whether method allows notifications (OUT)
+ */
+ Boolean allowNotifications;
+};
+
+
+/**
+ * struct eap_method - EAP method interface
+ * This structure defines the EAP method interface. Each method will need to
+ * register its own EAP type, EAP name, and set of function pointers for method
+ * specific operations. This interface is based on section 4.4 of RFC 4137.
+ */
+struct eap_method {
+ /**
+ * vendor - EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF)
+ */
+ int vendor;
+
+ /**
+ * method - EAP type number (EAP_TYPE_*)
+ */
+ EapType method;
+
+ /**
+ * name - Name of the method (e.g., "TLS")
+ */
+ const char *name;
+
+ /**
+ * init - Initialize an EAP method
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: Pointer to allocated private data, or %NULL on failure
+ *
+ * This function is used to initialize the EAP method explicitly
+ * instead of using METHOD_INIT state as specific in RFC 4137. The
+ * method is expected to initialize it method-specific state and return
+ * a pointer that will be used as the priv argument to other calls.
+ */
+ void * (*init)(struct eap_sm *sm);
+
+ /**
+ * deinit - Deinitialize an EAP method
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ *
+ * Deinitialize the EAP method and free any allocated private data.
+ */
+ void (*deinit)(struct eap_sm *sm, void *priv);
+
+ /**
+ * process - Process an EAP request
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * @ret: Return values from EAP request validation and processing
+ * @reqData: EAP request to be processed (eapReqData)
+ * Returns: Pointer to allocated EAP response packet (eapRespData)
+ *
+ * This function is a combination of m.check(), m.process(), and
+ * m.buildResp() procedures defined in section 4.4 of RFC 4137 In other
+ * words, this function validates the incoming request, processes it,
+ * and build a response packet. m.check() and m.process() return values
+ * are returned through struct eap_method_ret *ret variable. Caller is
+ * responsible for freeing the returned EAP response packet.
+ */
+ struct wpabuf * (*process)(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData);
+
+ /**
+ * isKeyAvailable - Find out whether EAP method has keying material
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * Returns: %TRUE if key material (eapKeyData) is available
+ */
+ Boolean (*isKeyAvailable)(struct eap_sm *sm, void *priv);
+
+ /**
+ * getKey - Get EAP method specific keying material (eapKeyData)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * @len: Pointer to variable to store key length (eapKeyDataLen)
+ * Returns: Keying material (eapKeyData) or %NULL if not available
+ *
+ * This function can be used to get the keying material from the EAP
+ * method. The key may already be stored in the method-specific private
+ * data or this function may derive the key.
+ */
+ u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len);
+
+ /**
+ * get_status - Get EAP method status
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * @buf: Buffer for status information
+ * @buflen: Maximum buffer length
+ * @verbose: Whether to include verbose status information
+ * Returns: Number of bytes written to buf
+ *
+ * Query EAP method for status information. This function fills in a
+ * text area with current status information from the EAP method. If
+ * the buffer (buf) is not large enough, status information will be
+ * truncated to fit the buffer.
+ */
+ int (*get_status)(struct eap_sm *sm, void *priv, char *buf,
+ size_t buflen, int verbose);
+
+ /**
+ * has_reauth_data - Whether method is ready for fast reauthentication
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * Returns: %TRUE or %FALSE based on whether fast reauthentication is
+ * possible
+ *
+ * This function is an optional handler that only EAP methods
+ * supporting fast re-authentication need to implement.
+ */
+ Boolean (*has_reauth_data)(struct eap_sm *sm, void *priv);
+
+ /**
+ * deinit_for_reauth - Release data that is not needed for fast re-auth
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ *
+ * This function is an optional handler that only EAP methods
+ * supporting fast re-authentication need to implement. This is called
+ * when authentication has been completed and EAP state machine is
+ * requesting that enough state information is maintained for fast
+ * re-authentication
+ */
+ void (*deinit_for_reauth)(struct eap_sm *sm, void *priv);
+
+ /**
+ * init_for_reauth - Prepare for start of fast re-authentication
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ *
+ * This function is an optional handler that only EAP methods
+ * supporting fast re-authentication need to implement. This is called
+ * when EAP authentication is started and EAP state machine is
+ * requesting fast re-authentication to be used.
+ */
+ void * (*init_for_reauth)(struct eap_sm *sm, void *priv);
+
+ /**
+ * get_identity - Get method specific identity for re-authentication
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * @len: Length of the returned identity
+ * Returns: Pointer to the method specific identity or %NULL if default
+ * identity is to be used
+ *
+ * This function is an optional handler that only EAP methods
+ * that use method specific identity need to implement.
+ */
+ const u8 * (*get_identity)(struct eap_sm *sm, void *priv, size_t *len);
+
+ /**
+ * free - Free EAP method data
+ * @method: Pointer to the method data registered with
+ * eap_peer_method_register().
+ *
+ * This function will be called when the EAP method is being
+ * unregistered. If the EAP method allocated resources during
+ * registration (e.g., allocated struct eap_method), they should be
+ * freed in this function. No other method functions will be called
+ * after this call. If this function is not defined (i.e., function
+ * pointer is %NULL), a default handler is used to release the method
+ * data with free(method). This is suitable for most cases.
+ */
+ void (*free)(struct eap_method *method);
+
+#define EAP_PEER_METHOD_INTERFACE_VERSION 1
+ /**
+ * version - Version of the EAP peer method interface
+ *
+ * The EAP peer method implementation should set this variable to
+ * EAP_PEER_METHOD_INTERFACE_VERSION. This is used to verify that the
+ * EAP method is using supported API version when using dynamically
+ * loadable EAP methods.
+ */
+ int version;
+
+ /**
+ * next - Pointer to the next EAP method
+ *
+ * This variable is used internally in the EAP method registration code
+ * to create a linked list of registered EAP methods.
+ */
+ struct eap_method *next;
+
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+ /**
+ * dl_handle - Handle for the dynamic library
+ *
+ * This variable is used internally in the EAP method registration code
+ * to store a handle for the dynamic library. If the method is linked
+ * in statically, this is %NULL.
+ */
+ void *dl_handle;
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+ /**
+ * get_emsk - Get EAP method specific keying extended material (EMSK)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * @len: Pointer to a variable to store EMSK length
+ * Returns: EMSK or %NULL if not available
+ *
+ * This function can be used to get the extended keying material from
+ * the EAP method. The key may already be stored in the method-specific
+ * private data or this function may derive the key.
+ */
+ u8 * (*get_emsk)(struct eap_sm *sm, void *priv, size_t *len);
+};
+
+
+/**
+ * struct eap_sm - EAP state machine data
+ */
+struct eap_sm {
+ enum {
+ EAP_INITIALIZE, EAP_DISABLED, EAP_IDLE, EAP_RECEIVED,
+ EAP_GET_METHOD, EAP_METHOD, EAP_SEND_RESPONSE, EAP_DISCARD,
+ EAP_IDENTITY, EAP_NOTIFICATION, EAP_RETRANSMIT, EAP_SUCCESS,
+ EAP_FAILURE
+ } EAP_state;
+ /* Long-term local variables */
+ EapType selectedMethod;
+ EapMethodState methodState;
+ int lastId;
+ struct wpabuf *lastRespData;
+ EapDecision decision;
+ /* Short-term local variables */
+ Boolean rxReq;
+ Boolean rxSuccess;
+ Boolean rxFailure;
+ int reqId;
+ EapType reqMethod;
+ int reqVendor;
+ u32 reqVendorMethod;
+ Boolean ignore;
+ /* Constants */
+ int ClientTimeout;
+
+ /* Miscellaneous variables */
+ Boolean allowNotifications; /* peer state machine <-> methods */
+ struct wpabuf *eapRespData; /* peer to lower layer */
+ Boolean eapKeyAvailable; /* peer to lower layer */
+ u8 *eapKeyData; /* peer to lower layer */
+ size_t eapKeyDataLen; /* peer to lower layer */
+ const struct eap_method *m; /* selected EAP method */
+ /* not defined in RFC 4137 */
+ Boolean changed;
+ void *eapol_ctx;
+ struct eapol_callbacks *eapol_cb;
+ void *eap_method_priv;
+ int init_phase2;
+ int fast_reauth;
+
+ Boolean rxResp /* LEAP only */;
+ Boolean leap_done;
+ Boolean peap_done;
+ u8 req_md5[16]; /* MD5() of the current EAP packet */
+ u8 last_md5[16]; /* MD5() of the previously received EAP packet; used
+ * in duplicate request detection. */
+
+ void *msg_ctx;
+ void *scard_ctx;
+ void *ssl_ctx;
+
+ unsigned int workaround;
+
+ /* Optional challenges generated in Phase 1 (EAP-FAST) */
+ u8 *peer_challenge, *auth_challenge;
+ int mschapv2_full_key; /* Request full MSCHAPv2 key */
+
+ int num_rounds;
+ int force_disabled;
+
+ u8 mac_addr[ETH_ALEN];
+};
+
+const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len);
+const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len);
+const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash);
+const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len);
+const u8 * eap_get_config_otp(struct eap_sm *sm, size_t *len);
+void eap_clear_config_otp(struct eap_sm *sm);
+const char * eap_get_config_phase1(struct eap_sm *sm);
+const char * eap_get_config_phase2(struct eap_sm *sm);
+struct eap_peer_config * eap_get_config(struct eap_sm *sm);
+void eap_set_config_blob(struct eap_sm *sm, struct wpa_config_blob *blob);
+const struct wpa_config_blob *
+eap_get_config_blob(struct eap_sm *sm, const char *name);
+void eap_notify_pending(struct eap_sm *sm);
+
+#endif /* EAP_I_H */
diff --git a/src/eap_peer/eap_ikev2.c b/src/eap_peer/eap_ikev2.c
new file mode 100644
index 0000000..d8c7b1f
--- /dev/null
+++ b/src/eap_peer/eap_ikev2.c
@@ -0,0 +1,506 @@
+/*
+ * EAP-IKEv2 peer (RFC 5106)
+ * Copyright (c) 2007, 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_common/eap_ikev2_common.h"
+#include "ikev2.h"
+
+
+struct eap_ikev2_data {
+ struct ikev2_responder_data ikev2;
+ enum { WAIT_START, MSG, WAIT_FRAG_ACK, DONE, FAIL } state;
+ struct wpabuf *in_buf;
+ struct wpabuf *out_buf;
+ size_t out_used;
+ size_t fragment_size;
+ int keys_ready;
+ u8 keymat[EAP_MSK_LEN + EAP_EMSK_LEN];
+ int keymat_ok;
+};
+
+
+static const char * eap_ikev2_state_txt(int state)
+{
+ switch (state) {
+ case WAIT_START:
+ return "WAIT_START";
+ case MSG:
+ return "MSG";
+ case WAIT_FRAG_ACK:
+ return "WAIT_FRAG_ACK";
+ case DONE:
+ return "DONE";
+ case FAIL:
+ return "FAIL";
+ default:
+ return "?";
+ }
+}
+
+
+static void eap_ikev2_state(struct eap_ikev2_data *data, int state)
+{
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: %s -> %s",
+ eap_ikev2_state_txt(data->state),
+ eap_ikev2_state_txt(state));
+ data->state = state;
+}
+
+
+static void * eap_ikev2_init(struct eap_sm *sm)
+{
+ struct eap_ikev2_data *data;
+ const u8 *identity, *password;
+ size_t identity_len, password_len;
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ if (identity == NULL) {
+ wpa_printf(MSG_INFO, "EAP-IKEV2: No identity available");
+ return NULL;
+ }
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->state = WAIT_START;
+ data->fragment_size = IKEV2_FRAGMENT_SIZE;
+ data->ikev2.state = SA_INIT;
+ data->ikev2.peer_auth = PEER_AUTH_SECRET;
+ data->ikev2.key_pad = (u8 *) os_strdup("Key Pad for EAP-IKEv2");
+ if (data->ikev2.key_pad == NULL)
+ goto failed;
+ data->ikev2.key_pad_len = 21;
+ data->ikev2.IDr = os_malloc(identity_len);
+ if (data->ikev2.IDr == NULL)
+ goto failed;
+ os_memcpy(data->ikev2.IDr, identity, identity_len);
+ data->ikev2.IDr_len = identity_len;
+
+ password = eap_get_config_password(sm, &password_len);
+ if (password) {
+ data->ikev2.shared_secret = os_malloc(password_len);
+ if (data->ikev2.shared_secret == NULL)
+ goto failed;
+ os_memcpy(data->ikev2.shared_secret, password, password_len);
+ data->ikev2.shared_secret_len = password_len;
+ }
+
+ return data;
+
+failed:
+ ikev2_responder_deinit(&data->ikev2);
+ os_free(data);
+ return NULL;
+}
+
+
+static void eap_ikev2_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_ikev2_data *data = priv;
+ wpabuf_free(data->in_buf);
+ wpabuf_free(data->out_buf);
+ ikev2_responder_deinit(&data->ikev2);
+ os_free(data);
+}
+
+
+static int eap_ikev2_peer_keymat(struct eap_ikev2_data *data)
+{
+ if (eap_ikev2_derive_keymat(
+ data->ikev2.proposal.prf, &data->ikev2.keys,
+ data->ikev2.i_nonce, data->ikev2.i_nonce_len,
+ data->ikev2.r_nonce, data->ikev2.r_nonce_len,
+ data->keymat) < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to "
+ "derive key material");
+ return -1;
+ }
+ data->keymat_ok = 1;
+ return 0;
+}
+
+
+static struct wpabuf * eap_ikev2_build_msg(struct eap_ikev2_data *data,
+ struct eap_method_ret *ret, u8 id)
+{
+ struct wpabuf *resp;
+ u8 flags;
+ size_t send_len, plen, icv_len = 0;
+
+ ret->ignore = FALSE;
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Generating Response");
+ ret->allowNotifications = TRUE;
+
+ flags = 0;
+ send_len = wpabuf_len(data->out_buf) - data->out_used;
+ if (1 + send_len > data->fragment_size) {
+ send_len = data->fragment_size - 1;
+ flags |= IKEV2_FLAGS_MORE_FRAGMENTS;
+ if (data->out_used == 0) {
+ flags |= IKEV2_FLAGS_LENGTH_INCLUDED;
+ send_len -= 4;
+ }
+ }
+#ifdef CCNS_PL
+ /* Some issues figuring out the length of the message if Message Length
+ * field not included?! */
+ if (!(flags & IKEV2_FLAGS_LENGTH_INCLUDED))
+ flags |= IKEV2_FLAGS_LENGTH_INCLUDED;
+#endif /* CCNS_PL */
+
+ plen = 1 + send_len;
+ if (flags & IKEV2_FLAGS_LENGTH_INCLUDED)
+ plen += 4;
+ if (data->keys_ready) {
+ const struct ikev2_integ_alg *integ;
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Add Integrity Checksum "
+ "Data");
+ flags |= IKEV2_FLAGS_ICV_INCLUDED;
+ integ = ikev2_get_integ(data->ikev2.proposal.integ);
+ if (integ == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unknown INTEG "
+ "transform / cannot generate ICV");
+ return NULL;
+ }
+ icv_len = integ->hash_len;
+
+ plen += icv_len;
+ }
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, plen,
+ EAP_CODE_RESPONSE, id);
+ if (resp == NULL)
+ return NULL;
+
+ wpabuf_put_u8(resp, flags); /* Flags */
+ if (flags & IKEV2_FLAGS_LENGTH_INCLUDED)
+ wpabuf_put_be32(resp, wpabuf_len(data->out_buf));
+
+ wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used,
+ send_len);
+ data->out_used += send_len;
+
+ if (flags & IKEV2_FLAGS_ICV_INCLUDED) {
+ const u8 *msg = wpabuf_head(resp);
+ size_t len = wpabuf_len(resp);
+ ikev2_integ_hash(data->ikev2.proposal.integ,
+ data->ikev2.keys.SK_ar,
+ data->ikev2.keys.SK_integ_len,
+ msg, len, wpabuf_put(resp, icv_len));
+ }
+
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_FAIL;
+
+ if (data->out_used == wpabuf_len(data->out_buf)) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes "
+ "(message sent completely)",
+ (unsigned long) send_len);
+ wpabuf_free(data->out_buf);
+ data->out_buf = NULL;
+ data->out_used = 0;
+ switch (data->ikev2.state) {
+ case SA_AUTH:
+ /* SA_INIT was sent out, so message have to be
+ * integrity protected from now on. */
+ data->keys_ready = 1;
+ break;
+ case IKEV2_DONE:
+ ret->methodState = METHOD_DONE;
+ if (data->state == FAIL)
+ break;
+ ret->decision = DECISION_COND_SUCC;
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Authentication "
+ "completed successfully");
+ if (eap_ikev2_peer_keymat(data))
+ break;
+ eap_ikev2_state(data, DONE);
+ break;
+ case IKEV2_FAILED:
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Authentication "
+ "failed");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ break;
+ default:
+ break;
+ }
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes "
+ "(%lu more to send)", (unsigned long) send_len,
+ (unsigned long) wpabuf_len(data->out_buf) -
+ data->out_used);
+ eap_ikev2_state(data, WAIT_FRAG_ACK);
+ }
+
+ return resp;
+}
+
+
+static int eap_ikev2_process_icv(struct eap_ikev2_data *data,
+ const struct wpabuf *reqData,
+ u8 flags, const u8 *pos, const u8 **end)
+{
+ if (flags & IKEV2_FLAGS_ICV_INCLUDED) {
+ int icv_len = eap_ikev2_validate_icv(
+ data->ikev2.proposal.integ, &data->ikev2.keys, 1,
+ reqData, pos, *end);
+ if (icv_len < 0)
+ return -1;
+ /* Hide Integrity Checksum Data from further processing */
+ *end -= icv_len;
+ } else if (data->keys_ready) {
+ wpa_printf(MSG_INFO, "EAP-IKEV2: The message should have "
+ "included integrity checksum");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int eap_ikev2_process_cont(struct eap_ikev2_data *data,
+ const u8 *buf, size_t len)
+{
+ /* Process continuation of a pending message */
+ if (len > wpabuf_tailroom(data->in_buf)) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment overflow");
+ eap_ikev2_state(data, FAIL);
+ return -1;
+ }
+
+ wpabuf_put_data(data->in_buf, buf, len);
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes, waiting "
+ "for %lu bytes more", (unsigned long) len,
+ (unsigned long) wpabuf_tailroom(data->in_buf));
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_ikev2_process_fragment(struct eap_ikev2_data *data,
+ struct eap_method_ret *ret,
+ u8 id, u8 flags,
+ u32 message_length,
+ const u8 *buf, size_t len)
+{
+ /* Process a fragment that is not the last one of the message */
+ if (data->in_buf == NULL && !(flags & IKEV2_FLAGS_LENGTH_INCLUDED)) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: No Message Length field in "
+ "a fragmented packet");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (data->in_buf == NULL) {
+ /* First fragment of the message */
+ data->in_buf = wpabuf_alloc(message_length);
+ if (data->in_buf == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: No memory for "
+ "message");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ wpabuf_put_data(data->in_buf, buf, len);
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes in first "
+ "fragment, waiting for %lu bytes more",
+ (unsigned long) len,
+ (unsigned long) wpabuf_tailroom(data->in_buf));
+ }
+
+ return eap_ikev2_build_frag_ack(id, EAP_CODE_RESPONSE);
+}
+
+
+static struct wpabuf * eap_ikev2_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct eap_ikev2_data *data = priv;
+ const u8 *start, *pos, *end;
+ size_t len;
+ u8 flags, id;
+ u32 message_length = 0;
+ struct wpabuf tmpbuf;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, reqData, &len);
+ if (pos == NULL) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ id = eap_get_id(reqData);
+
+ start = pos;
+ end = start + len;
+
+ if (len == 0)
+ flags = 0; /* fragment ack */
+ else
+ flags = *pos++;
+
+ if (eap_ikev2_process_icv(data, reqData, flags, pos, &end) < 0) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) {
+ if (end - pos < 4) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Message underflow");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ message_length = WPA_GET_BE32(pos);
+ pos += 4;
+
+ if (message_length < (u32) (end - pos)) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Invalid Message "
+ "Length (%d; %ld remaining in this msg)",
+ message_length, (long) (end - pos));
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received packet: Flags 0x%x "
+ "Message Length %u", flags, message_length);
+
+ if (data->state == WAIT_FRAG_ACK) {
+#ifdef CCNS_PL
+ if (len > 1) /* Empty Flags field included in ACK */
+#else /* CCNS_PL */
+ if (len != 0)
+#endif /* CCNS_PL */
+ {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected payload "
+ "in WAIT_FRAG_ACK state");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment acknowledged");
+ eap_ikev2_state(data, MSG);
+ return eap_ikev2_build_msg(data, ret, id);
+ }
+
+ if (data->in_buf && eap_ikev2_process_cont(data, pos, end - pos) < 0) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (flags & IKEV2_FLAGS_MORE_FRAGMENTS) {
+ return eap_ikev2_process_fragment(data, ret, id, flags,
+ message_length, pos,
+ end - pos);
+ }
+
+ if (data->in_buf == NULL) {
+ /* Wrap unfragmented messages as wpabuf without extra copy */
+ wpabuf_set(&tmpbuf, pos, end - pos);
+ data->in_buf = &tmpbuf;
+ }
+
+ if (ikev2_responder_process(&data->ikev2, data->in_buf) < 0) {
+ if (data->in_buf == &tmpbuf)
+ data->in_buf = NULL;
+ eap_ikev2_state(data, FAIL);
+ return NULL;
+ }
+
+ if (data->in_buf != &tmpbuf)
+ wpabuf_free(data->in_buf);
+ data->in_buf = NULL;
+
+ if (data->out_buf == NULL) {
+ data->out_buf = ikev2_responder_build(&data->ikev2);
+ if (data->out_buf == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to generate "
+ "IKEv2 message");
+ return NULL;
+ }
+ data->out_used = 0;
+ }
+
+ eap_ikev2_state(data, MSG);
+ return eap_ikev2_build_msg(data, ret, id);
+}
+
+
+static Boolean eap_ikev2_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_ikev2_data *data = priv;
+ return data->state == DONE && data->keymat_ok;
+}
+
+
+static u8 * eap_ikev2_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_ikev2_data *data = priv;
+ u8 *key;
+
+ if (data->state != DONE || !data->keymat_ok)
+ return NULL;
+
+ key = os_malloc(EAP_MSK_LEN);
+ if (key) {
+ os_memcpy(key, data->keymat, EAP_MSK_LEN);
+ *len = EAP_MSK_LEN;
+ }
+
+ return key;
+}
+
+
+static u8 * eap_ikev2_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_ikev2_data *data = priv;
+ u8 *key;
+
+ if (data->state != DONE || !data->keymat_ok)
+ return NULL;
+
+ key = os_malloc(EAP_EMSK_LEN);
+ if (key) {
+ os_memcpy(key, data->keymat + EAP_MSK_LEN, EAP_EMSK_LEN);
+ *len = EAP_EMSK_LEN;
+ }
+
+ return key;
+}
+
+
+int eap_peer_ikev2_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_IKEV2,
+ "IKEV2");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_ikev2_init;
+ eap->deinit = eap_ikev2_deinit;
+ eap->process = eap_ikev2_process;
+ eap->isKeyAvailable = eap_ikev2_isKeyAvailable;
+ eap->getKey = eap_ikev2_getKey;
+ eap->get_emsk = eap_ikev2_get_emsk;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_leap.c b/src/eap_peer/eap_leap.c
new file mode 100644
index 0000000..01c1f16
--- /dev/null
+++ b/src/eap_peer/eap_leap.c
@@ -0,0 +1,403 @@
+/*
+ * EAP peer method: LEAP
+ * Copyright (c) 2004-2007, 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#include "ms_funcs.h"
+#include "crypto.h"
+
+#define LEAP_VERSION 1
+#define LEAP_CHALLENGE_LEN 8
+#define LEAP_RESPONSE_LEN 24
+#define LEAP_KEY_LEN 16
+
+
+struct eap_leap_data {
+ enum {
+ LEAP_WAIT_CHALLENGE,
+ LEAP_WAIT_SUCCESS,
+ LEAP_WAIT_RESPONSE,
+ LEAP_DONE
+ } state;
+
+ u8 peer_challenge[LEAP_CHALLENGE_LEN];
+ u8 peer_response[LEAP_RESPONSE_LEN];
+
+ u8 ap_challenge[LEAP_CHALLENGE_LEN];
+ u8 ap_response[LEAP_RESPONSE_LEN];
+};
+
+
+static void * eap_leap_init(struct eap_sm *sm)
+{
+ struct eap_leap_data *data;
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->state = LEAP_WAIT_CHALLENGE;
+
+ sm->leap_done = FALSE;
+ return data;
+}
+
+
+static void eap_leap_deinit(struct eap_sm *sm, void *priv)
+{
+ os_free(priv);
+}
+
+
+static struct wpabuf * eap_leap_process_request(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct eap_leap_data *data = priv;
+ struct wpabuf *resp;
+ const u8 *pos, *challenge, *identity, *password;
+ u8 challenge_len, *rpos;
+ size_t identity_len, password_len, len;
+ int pwhash;
+
+ wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Request");
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ password = eap_get_config_password2(sm, &password_len, &pwhash);
+ if (identity == NULL || password == NULL)
+ return NULL;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_LEAP, reqData, &len);
+ if (pos == NULL || len < 3) {
+ wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Request frame");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (*pos != LEAP_VERSION) {
+ wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version "
+ "%d", *pos);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ pos++;
+
+ pos++; /* skip unused byte */
+
+ challenge_len = *pos++;
+ if (challenge_len != LEAP_CHALLENGE_LEN || challenge_len > len - 3) {
+ wpa_printf(MSG_INFO, "EAP-LEAP: Invalid challenge "
+ "(challenge_len=%d reqDataLen=%lu)",
+ challenge_len, (unsigned long) wpabuf_len(reqData));
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ challenge = pos;
+ os_memcpy(data->peer_challenge, challenge, LEAP_CHALLENGE_LEN);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge from AP",
+ challenge, LEAP_CHALLENGE_LEN);
+
+ wpa_printf(MSG_DEBUG, "EAP-LEAP: Generating Challenge Response");
+
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_LEAP,
+ 3 + LEAP_RESPONSE_LEN + identity_len,
+ EAP_CODE_RESPONSE, eap_get_id(reqData));
+ if (resp == NULL)
+ return NULL;
+ wpabuf_put_u8(resp, LEAP_VERSION);
+ wpabuf_put_u8(resp, 0); /* unused */
+ wpabuf_put_u8(resp, LEAP_RESPONSE_LEN);
+ rpos = wpabuf_put(resp, LEAP_RESPONSE_LEN);
+ if (pwhash)
+ challenge_response(challenge, password, rpos);
+ else
+ nt_challenge_response(challenge, password, password_len, rpos);
+ os_memcpy(data->peer_response, rpos, LEAP_RESPONSE_LEN);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Response",
+ rpos, LEAP_RESPONSE_LEN);
+ wpabuf_put_data(resp, identity, identity_len);
+
+ data->state = LEAP_WAIT_SUCCESS;
+
+ return resp;
+}
+
+
+static struct wpabuf * eap_leap_process_success(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct eap_leap_data *data = priv;
+ struct wpabuf *resp;
+ u8 *pos;
+ const u8 *identity;
+ size_t identity_len;
+
+ wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Success");
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ if (identity == NULL)
+ return NULL;
+
+ if (data->state != LEAP_WAIT_SUCCESS) {
+ wpa_printf(MSG_INFO, "EAP-LEAP: EAP-Success received in "
+ "unexpected state (%d) - ignored", data->state);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_LEAP,
+ 3 + LEAP_CHALLENGE_LEN + identity_len,
+ EAP_CODE_REQUEST, eap_get_id(reqData));
+ if (resp == NULL)
+ return NULL;
+ wpabuf_put_u8(resp, LEAP_VERSION);
+ wpabuf_put_u8(resp, 0); /* unused */
+ wpabuf_put_u8(resp, LEAP_CHALLENGE_LEN);
+ pos = wpabuf_put(resp, LEAP_CHALLENGE_LEN);
+ if (os_get_random(pos, LEAP_CHALLENGE_LEN)) {
+ wpa_printf(MSG_WARNING, "EAP-LEAP: Failed to read random data "
+ "for challenge");
+ wpabuf_free(resp);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ os_memcpy(data->ap_challenge, pos, LEAP_CHALLENGE_LEN);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge to AP/AS", pos,
+ LEAP_CHALLENGE_LEN);
+ wpabuf_put_data(resp, identity, identity_len);
+
+ data->state = LEAP_WAIT_RESPONSE;
+
+ return resp;
+}
+
+
+static struct wpabuf * eap_leap_process_response(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct eap_leap_data *data = priv;
+ const u8 *pos, *password;
+ u8 response_len, pw_hash[16], pw_hash_hash[16],
+ expected[LEAP_RESPONSE_LEN];
+ size_t password_len, len;
+ int pwhash;
+
+ wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Response");
+
+ password = eap_get_config_password2(sm, &password_len, &pwhash);
+ if (password == NULL)
+ return NULL;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_LEAP, reqData, &len);
+ if (pos == NULL || len < 3) {
+ wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Response frame");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (*pos != LEAP_VERSION) {
+ wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version "
+ "%d", *pos);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ pos++;
+
+ pos++; /* skip unused byte */
+
+ response_len = *pos++;
+ if (response_len != LEAP_RESPONSE_LEN || response_len > len - 3) {
+ wpa_printf(MSG_INFO, "EAP-LEAP: Invalid response "
+ "(response_len=%d reqDataLen=%lu)",
+ response_len, (unsigned long) wpabuf_len(reqData));
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Response from AP",
+ pos, LEAP_RESPONSE_LEN);
+ os_memcpy(data->ap_response, pos, LEAP_RESPONSE_LEN);
+
+ if (pwhash) {
+ hash_nt_password_hash(password, pw_hash_hash);
+ } else {
+ nt_password_hash(password, password_len, pw_hash);
+ hash_nt_password_hash(pw_hash, pw_hash_hash);
+ }
+ challenge_response(data->ap_challenge, pw_hash_hash, expected);
+
+ ret->methodState = METHOD_DONE;
+ ret->allowNotifications = FALSE;
+
+ if (os_memcmp(pos, expected, LEAP_RESPONSE_LEN) != 0) {
+ wpa_printf(MSG_WARNING, "EAP-LEAP: AP sent an invalid "
+ "response - authentication failed");
+ wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Expected response from AP",
+ expected, LEAP_RESPONSE_LEN);
+ ret->decision = DECISION_FAIL;
+ return NULL;
+ }
+
+ ret->decision = DECISION_UNCOND_SUCC;
+
+ /* LEAP is somewhat odd method since it sends EAP-Success in the middle
+ * of the authentication. Use special variable to transit EAP state
+ * machine to SUCCESS state. */
+ sm->leap_done = TRUE;
+ data->state = LEAP_DONE;
+
+ /* No more authentication messages expected; AP will send EAPOL-Key
+ * frames if encryption is enabled. */
+ return NULL;
+}
+
+
+static struct wpabuf * eap_leap_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ const struct eap_hdr *eap;
+ size_t password_len;
+ const u8 *password;
+
+ password = eap_get_config_password(sm, &password_len);
+ if (password == NULL) {
+ wpa_printf(MSG_INFO, "EAP-LEAP: Password not configured");
+ eap_sm_request_password(sm);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ /*
+ * LEAP needs to be able to handle EAP-Success frame which does not
+ * include Type field. Consequently, eap_hdr_validate() cannot be used
+ * here. This validation will be done separately for EAP-Request and
+ * EAP-Response frames.
+ */
+ eap = wpabuf_head(reqData);
+ if (wpabuf_len(reqData) < sizeof(*eap) ||
+ be_to_host16(eap->length) > wpabuf_len(reqData)) {
+ wpa_printf(MSG_INFO, "EAP-LEAP: Invalid frame");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ ret->ignore = FALSE;
+ ret->allowNotifications = TRUE;
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_FAIL;
+
+ sm->leap_done = FALSE;
+
+ switch (eap->code) {
+ case EAP_CODE_REQUEST:
+ return eap_leap_process_request(sm, priv, ret, reqData);
+ case EAP_CODE_SUCCESS:
+ return eap_leap_process_success(sm, priv, ret, reqData);
+ case EAP_CODE_RESPONSE:
+ return eap_leap_process_response(sm, priv, ret, reqData);
+ default:
+ wpa_printf(MSG_INFO, "EAP-LEAP: Unexpected EAP code (%d) - "
+ "ignored", eap->code);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+}
+
+
+static Boolean eap_leap_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_leap_data *data = priv;
+ return data->state == LEAP_DONE;
+}
+
+
+static u8 * eap_leap_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_leap_data *data = priv;
+ u8 *key, pw_hash_hash[16], pw_hash[16];
+ const u8 *addr[5], *password;
+ size_t elen[5], password_len;
+ int pwhash;
+
+ if (data->state != LEAP_DONE)
+ return NULL;
+
+ password = eap_get_config_password2(sm, &password_len, &pwhash);
+ if (password == NULL)
+ return NULL;
+
+ key = os_malloc(LEAP_KEY_LEN);
+ if (key == NULL)
+ return NULL;
+
+ if (pwhash)
+ hash_nt_password_hash(password, pw_hash_hash);
+ else {
+ nt_password_hash(password, password_len, pw_hash);
+ hash_nt_password_hash(pw_hash, pw_hash_hash);
+ }
+ wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: pw_hash_hash",
+ pw_hash_hash, 16);
+ wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_challenge",
+ data->peer_challenge, LEAP_CHALLENGE_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_response",
+ data->peer_response, LEAP_RESPONSE_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_challenge",
+ data->ap_challenge, LEAP_CHALLENGE_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_response",
+ data->ap_response, LEAP_RESPONSE_LEN);
+
+ addr[0] = pw_hash_hash;
+ elen[0] = 16;
+ addr[1] = data->ap_challenge;
+ elen[1] = LEAP_CHALLENGE_LEN;
+ addr[2] = data->ap_response;
+ elen[2] = LEAP_RESPONSE_LEN;
+ addr[3] = data->peer_challenge;
+ elen[3] = LEAP_CHALLENGE_LEN;
+ addr[4] = data->peer_response;
+ elen[4] = LEAP_RESPONSE_LEN;
+ md5_vector(5, addr, elen, key);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: master key", key, LEAP_KEY_LEN);
+ *len = LEAP_KEY_LEN;
+
+ return key;
+}
+
+
+int eap_peer_leap_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_LEAP, "LEAP");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_leap_init;
+ eap->deinit = eap_leap_deinit;
+ eap->process = eap_leap_process;
+ eap->isKeyAvailable = eap_leap_isKeyAvailable;
+ eap->getKey = eap_leap_getKey;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_md5.c b/src/eap_peer/eap_md5.c
new file mode 100644
index 0000000..7961143
--- /dev/null
+++ b/src/eap_peer/eap_md5.c
@@ -0,0 +1,120 @@
+/*
+ * EAP peer method: EAP-MD5 (RFC 3748 and RFC 1994)
+ * Copyright (c) 2004-2006, 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_common/chap.h"
+
+
+static void * eap_md5_init(struct eap_sm *sm)
+{
+ /* No need for private data. However, must return non-NULL to indicate
+ * success. */
+ return (void *) 1;
+}
+
+
+static void eap_md5_deinit(struct eap_sm *sm, void *priv)
+{
+}
+
+
+static struct wpabuf * eap_md5_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct wpabuf *resp;
+ const u8 *pos, *challenge, *password;
+ u8 *rpos, id;
+ size_t len, challenge_len, password_len;
+
+ password = eap_get_config_password(sm, &password_len);
+ if (password == NULL) {
+ wpa_printf(MSG_INFO, "EAP-MD5: Password not configured");
+ eap_sm_request_password(sm);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MD5, reqData, &len);
+ if (pos == NULL || len == 0) {
+ wpa_printf(MSG_INFO, "EAP-MD5: Invalid frame (pos=%p len=%lu)",
+ pos, (unsigned long) len);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ /*
+ * CHAP Challenge:
+ * Value-Size (1 octet) | Value(Challenge) | Name(optional)
+ */
+ challenge_len = *pos++;
+ if (challenge_len == 0 || challenge_len > len - 1) {
+ wpa_printf(MSG_INFO, "EAP-MD5: Invalid challenge "
+ "(challenge_len=%lu len=%lu)",
+ (unsigned long) challenge_len, (unsigned long) len);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ ret->ignore = FALSE;
+ challenge = pos;
+ wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Challenge",
+ challenge, challenge_len);
+
+ wpa_printf(MSG_DEBUG, "EAP-MD5: Generating Challenge Response");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ ret->allowNotifications = TRUE;
+
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MD5, 1 + CHAP_MD5_LEN,
+ EAP_CODE_RESPONSE, eap_get_id(reqData));
+ if (resp == NULL)
+ return NULL;
+
+ /*
+ * CHAP Response:
+ * Value-Size (1 octet) | Value(Response) | Name(optional)
+ */
+ wpabuf_put_u8(resp, CHAP_MD5_LEN);
+
+ id = eap_get_id(resp);
+ rpos = wpabuf_put(resp, CHAP_MD5_LEN);
+ chap_md5(id, password, password_len, challenge, challenge_len, rpos);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", rpos, CHAP_MD5_LEN);
+
+ return resp;
+}
+
+
+int eap_peer_md5_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_MD5, "MD5");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_md5_init;
+ eap->deinit = eap_md5_deinit;
+ eap->process = eap_md5_process;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_methods.c b/src/eap_peer/eap_methods.c
new file mode 100644
index 0000000..0973b2f
--- /dev/null
+++ b/src/eap_peer/eap_methods.c
@@ -0,0 +1,514 @@
+/*
+ * EAP peer: Method registration
+ * Copyright (c) 2004-2007, 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.
+ */
+
+#include "includes.h"
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+#include <dlfcn.h>
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_methods.h"
+
+
+static struct eap_method *eap_methods = NULL;
+
+
+/**
+ * eap_peer_get_eap_method - Get EAP method based on type number
+ * @vendor: EAP Vendor-Id (0 = IETF)
+ * @method: EAP type number
+ * Returns: Pointer to EAP method or %NULL if not found
+ */
+const struct eap_method * eap_peer_get_eap_method(int vendor, EapType method)
+{
+ struct eap_method *m;
+ for (m = eap_methods; m; m = m->next) {
+ if (m->vendor == vendor && m->method == method)
+ return m;
+ }
+ return NULL;
+}
+
+
+/**
+ * eap_peer_get_type - Get EAP type for the given EAP method name
+ * @name: EAP method name, e.g., TLS
+ * @vendor: Buffer for returning EAP Vendor-Id
+ * Returns: EAP method type or %EAP_TYPE_NONE if not found
+ *
+ * This function maps EAP type names into EAP type numbers based on the list of
+ * EAP methods included in the build.
+ */
+EapType eap_peer_get_type(const char *name, int *vendor)
+{
+ struct eap_method *m;
+ for (m = eap_methods; m; m = m->next) {
+ if (os_strcmp(m->name, name) == 0) {
+ *vendor = m->vendor;
+ return m->method;
+ }
+ }
+ *vendor = EAP_VENDOR_IETF;
+ return EAP_TYPE_NONE;
+}
+
+
+/**
+ * eap_get_name - Get EAP method name for the given EAP type
+ * @vendor: EAP Vendor-Id (0 = IETF)
+ * @type: EAP method type
+ * Returns: EAP method name, e.g., TLS, or %NULL if not found
+ *
+ * This function maps EAP type numbers into EAP type names based on the list of
+ * EAP methods included in the build.
+ */
+const char * eap_get_name(int vendor, EapType type)
+{
+ struct eap_method *m;
+ for (m = eap_methods; m; m = m->next) {
+ if (m->vendor == vendor && m->method == type)
+ return m->name;
+ }
+ return NULL;
+}
+
+
+/**
+ * eap_get_names - Get space separated list of names for supported EAP methods
+ * @buf: Buffer for names
+ * @buflen: Buffer length
+ * Returns: Number of characters written into buf (not including nul
+ * termination)
+ */
+size_t eap_get_names(char *buf, size_t buflen)
+{
+ char *pos, *end;
+ struct eap_method *m;
+ int ret;
+
+ if (buflen == 0)
+ return 0;
+
+ pos = buf;
+ end = pos + buflen;
+
+ for (m = eap_methods; m; m = m->next) {
+ ret = os_snprintf(pos, end - pos, "%s%s",
+ m == eap_methods ? "" : " ", m->name);
+ if (ret < 0 || ret >= end - pos)
+ break;
+ pos += ret;
+ }
+ buf[buflen - 1] = '\0';
+
+ return pos - buf;
+}
+
+
+/**
+ * eap_get_names_as_string_array - Get supported EAP methods as string array
+ * @num: Buffer for returning the number of items in array, not including %NULL
+ * terminator. This parameter can be %NULL if the length is not needed.
+ * Returns: A %NULL-terminated array of strings, or %NULL on error.
+ *
+ * This function returns the list of names for all supported EAP methods as an
+ * array of strings. The caller must free the returned array items and the
+ * array.
+ */
+char ** eap_get_names_as_string_array(size_t *num)
+{
+ struct eap_method *m;
+ size_t array_len = 0;
+ char **array;
+ int i = 0, j;
+
+ for (m = eap_methods; m; m = m->next)
+ array_len++;
+
+ array = os_zalloc(sizeof(char *) * (array_len + 1));
+ if (array == NULL)
+ return NULL;
+
+ for (m = eap_methods; m; m = m->next) {
+ array[i++] = os_strdup(m->name);
+ if (array[i - 1] == NULL) {
+ for (j = 0; j < i; j++)
+ os_free(array[j]);
+ os_free(array);
+ return NULL;
+ }
+ }
+ array[i] = NULL;
+
+ if (num)
+ *num = array_len;
+
+ return array;
+}
+
+
+/**
+ * eap_peer_get_methods - Get a list of enabled EAP peer methods
+ * @count: Set to number of available methods
+ * Returns: List of enabled EAP peer methods
+ */
+const struct eap_method * eap_peer_get_methods(size_t *count)
+{
+ int c = 0;
+ struct eap_method *m;
+
+ for (m = eap_methods; m; m = m->next)
+ c++;
+
+ *count = c;
+ return eap_methods;
+}
+
+
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+/**
+ * eap_peer_method_load - Load a dynamic EAP method library (shared object)
+ * @so: File path for the shared object file to load
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_peer_method_load(const char *so)
+{
+ void *handle;
+ int (*dyn_init)(void);
+ int ret;
+
+ handle = dlopen(so, RTLD_LAZY);
+ if (handle == NULL) {
+ wpa_printf(MSG_ERROR, "EAP: Failed to open dynamic EAP method "
+ "'%s': %s", so, dlerror());
+ return -1;
+ }
+
+ dyn_init = dlsym(handle, "eap_peer_method_dynamic_init");
+ if (dyn_init == NULL) {
+ dlclose(handle);
+ wpa_printf(MSG_ERROR, "EAP: Invalid EAP method '%s' - no "
+ "eap_peer_method_dynamic_init()", so);
+ return -1;
+ }
+
+ ret = dyn_init();
+ if (ret) {
+ dlclose(handle);
+ wpa_printf(MSG_ERROR, "EAP: Failed to add EAP method '%s' - "
+ "ret %d", so, ret);
+ return ret;
+ }
+
+ /* Store the handle for this shared object. It will be freed with
+ * dlclose() when the EAP method is unregistered. */
+ eap_methods->dl_handle = handle;
+
+ wpa_printf(MSG_DEBUG, "EAP: Loaded dynamic EAP method: '%s'", so);
+
+ return 0;
+}
+
+
+/**
+ * eap_peer_method_unload - Unload a dynamic EAP method library (shared object)
+ * @method: Pointer to the dynamically loaded EAP method
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to unload EAP methods that have been previously
+ * loaded with eap_peer_method_load(). Before unloading the method, all
+ * references to the method must be removed to make sure that no dereferences
+ * of freed memory will occur after unloading.
+ */
+int eap_peer_method_unload(struct eap_method *method)
+{
+ struct eap_method *m, *prev;
+ void *handle;
+
+ m = eap_methods;
+ prev = NULL;
+ while (m) {
+ if (m == method)
+ break;
+ prev = m;
+ m = m->next;
+ }
+
+ if (m == NULL || m->dl_handle == NULL)
+ return -1;
+
+ if (prev)
+ prev->next = m->next;
+ else
+ eap_methods = m->next;
+
+ handle = m->dl_handle;
+
+ if (m->free)
+ m->free(m);
+ else
+ eap_peer_method_free(m);
+
+ dlclose(handle);
+
+ return 0;
+}
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+
+/**
+ * eap_peer_method_alloc - Allocate EAP peer method structure
+ * @version: Version of the EAP peer method interface (set to
+ * EAP_PEER_METHOD_INTERFACE_VERSION)
+ * @vendor: EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF)
+ * @method: EAP type number (EAP_TYPE_*)
+ * @name: Name of the method (e.g., "TLS")
+ * Returns: Allocated EAP method structure or %NULL on failure
+ *
+ * The returned structure should be freed with eap_peer_method_free() when it
+ * is not needed anymore.
+ */
+struct eap_method * eap_peer_method_alloc(int version, int vendor,
+ EapType method, const char *name)
+{
+ struct eap_method *eap;
+ eap = os_zalloc(sizeof(*eap));
+ if (eap == NULL)
+ return NULL;
+ eap->version = version;
+ eap->vendor = vendor;
+ eap->method = method;
+ eap->name = name;
+ return eap;
+}
+
+
+/**
+ * eap_peer_method_free - Free EAP peer method structure
+ * @method: Method structure allocated with eap_peer_method_alloc()
+ */
+void eap_peer_method_free(struct eap_method *method)
+{
+ os_free(method);
+}
+
+
+/**
+ * eap_peer_method_register - Register an EAP peer method
+ * @method: EAP method to register
+ * Returns: 0 on success, -1 on invalid method, or -2 if a matching EAP method
+ * has already been registered
+ *
+ * Each EAP peer method needs to call this function to register itself as a
+ * supported EAP method.
+ */
+int eap_peer_method_register(struct eap_method *method)
+{
+ struct eap_method *m, *last = NULL;
+
+ if (method == NULL || method->name == NULL ||
+ method->version != EAP_PEER_METHOD_INTERFACE_VERSION)
+ return -1;
+
+ for (m = eap_methods; m; m = m->next) {
+ if ((m->vendor == method->vendor &&
+ m->method == method->method) ||
+ os_strcmp(m->name, method->name) == 0)
+ return -2;
+ last = m;
+ }
+
+ if (last)
+ last->next = method;
+ else
+ eap_methods = method;
+
+ return 0;
+}
+
+
+/**
+ * eap_peer_register_methods - Register statically linked EAP peer methods
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called at program initialization to register all EAP peer
+ * methods that were linked in statically.
+ */
+int eap_peer_register_methods(void)
+{
+ int ret = 0;
+
+#ifdef EAP_MD5
+ if (ret == 0) {
+ int eap_peer_md5_register(void);
+ ret = eap_peer_md5_register();
+ }
+#endif /* EAP_MD5 */
+
+#ifdef EAP_TLS
+ if (ret == 0) {
+ int eap_peer_tls_register(void);
+ ret = eap_peer_tls_register();
+ }
+#endif /* EAP_TLS */
+
+#ifdef EAP_MSCHAPv2
+ if (ret == 0) {
+ int eap_peer_mschapv2_register(void);
+ ret = eap_peer_mschapv2_register();
+ }
+#endif /* EAP_MSCHAPv2 */
+
+#ifdef EAP_PEAP
+ if (ret == 0) {
+ int eap_peer_peap_register(void);
+ ret = eap_peer_peap_register();
+ }
+#endif /* EAP_PEAP */
+
+#ifdef EAP_TTLS
+ if (ret == 0) {
+ int eap_peer_ttls_register(void);
+ ret = eap_peer_ttls_register();
+ }
+#endif /* EAP_TTLS */
+
+#ifdef EAP_GTC
+ if (ret == 0) {
+ int eap_peer_gtc_register(void);
+ ret = eap_peer_gtc_register();
+ }
+#endif /* EAP_GTC */
+
+#ifdef EAP_OTP
+ if (ret == 0) {
+ int eap_peer_otp_register(void);
+ ret = eap_peer_otp_register();
+ }
+#endif /* EAP_OTP */
+
+#ifdef EAP_SIM
+ if (ret == 0) {
+ int eap_peer_sim_register(void);
+ ret = eap_peer_sim_register();
+ }
+#endif /* EAP_SIM */
+
+#ifdef EAP_LEAP
+ if (ret == 0) {
+ int eap_peer_leap_register(void);
+ ret = eap_peer_leap_register();
+ }
+#endif /* EAP_LEAP */
+
+#ifdef EAP_PSK
+ if (ret == 0) {
+ int eap_peer_psk_register(void);
+ ret = eap_peer_psk_register();
+ }
+#endif /* EAP_PSK */
+
+#ifdef EAP_AKA
+ if (ret == 0) {
+ int eap_peer_aka_register(void);
+ ret = eap_peer_aka_register();
+ }
+#endif /* EAP_AKA */
+
+#ifdef EAP_FAST
+ if (ret == 0) {
+ int eap_peer_fast_register(void);
+ ret = eap_peer_fast_register();
+ }
+#endif /* EAP_FAST */
+
+#ifdef EAP_PAX
+ if (ret == 0) {
+ int eap_peer_pax_register(void);
+ ret = eap_peer_pax_register();
+ }
+#endif /* EAP_PAX */
+
+#ifdef EAP_SAKE
+ if (ret == 0) {
+ int eap_peer_sake_register(void);
+ ret = eap_peer_sake_register();
+ }
+#endif /* EAP_SAKE */
+
+#ifdef EAP_GPSK
+ if (ret == 0) {
+ int eap_peer_gpsk_register(void);
+ ret = eap_peer_gpsk_register();
+ }
+#endif /* EAP_GPSK */
+
+#ifdef EAP_IKEV2
+ if (ret == 0) {
+ int eap_peer_ikev2_register(void);
+ ret = eap_peer_ikev2_register();
+ }
+#endif /* EAP_IKEV2 */
+
+#ifdef EAP_VENDOR_TEST
+ if (ret == 0) {
+ int eap_peer_vendor_test_register(void);
+ ret = eap_peer_vendor_test_register();
+ }
+#endif /* EAP_VENDOR_TEST */
+
+#ifdef EAP_TNC
+ if (ret == 0) {
+ int eap_peer_tnc_register(void);
+ ret = eap_peer_tnc_register();
+ }
+#endif /* EAP_TNC */
+
+ return ret;
+}
+
+
+/**
+ * eap_peer_unregister_methods - Unregister EAP peer methods
+ *
+ * This function is called at program termination to unregister all EAP peer
+ * methods.
+ */
+void eap_peer_unregister_methods(void)
+{
+ struct eap_method *m;
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+ void *handle;
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+ while (eap_methods) {
+ m = eap_methods;
+ eap_methods = eap_methods->next;
+
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+ handle = m->dl_handle;
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+ if (m->free)
+ m->free(m);
+ else
+ eap_peer_method_free(m);
+
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+ if (handle)
+ dlclose(handle);
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+ }
+}
diff --git a/src/eap_peer/eap_methods.h b/src/eap_peer/eap_methods.h
new file mode 100644
index 0000000..c11bd8c
--- /dev/null
+++ b/src/eap_peer/eap_methods.h
@@ -0,0 +1,87 @@
+/*
+ * EAP peer: Method registration
+ * Copyright (c) 2004-2007, 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.
+ */
+
+#ifndef EAP_METHODS_H
+#define EAP_METHODS_H
+
+#include "eap_common/eap_defs.h"
+
+const struct eap_method * eap_peer_get_eap_method(int vendor, EapType method);
+const struct eap_method * eap_peer_get_methods(size_t *count);
+
+struct eap_method * eap_peer_method_alloc(int version, int vendor,
+ EapType method, const char *name);
+void eap_peer_method_free(struct eap_method *method);
+int eap_peer_method_register(struct eap_method *method);
+
+
+#ifdef IEEE8021X_EAPOL
+
+EapType eap_peer_get_type(const char *name, int *vendor);
+const char * eap_get_name(int vendor, EapType type);
+size_t eap_get_names(char *buf, size_t buflen);
+char ** eap_get_names_as_string_array(size_t *num);
+int eap_peer_register_methods(void);
+void eap_peer_unregister_methods(void);
+
+#else /* IEEE8021X_EAPOL */
+
+static inline EapType eap_peer_get_type(const char *name, int *vendor)
+{
+ *vendor = EAP_VENDOR_IETF;
+ return EAP_TYPE_NONE;
+}
+
+static inline const char * eap_get_name(int vendor, EapType type)
+{
+ return NULL;
+}
+
+static inline size_t eap_get_names(char *buf, size_t buflen)
+{
+ return 0;
+}
+
+static inline int eap_peer_register_methods(void)
+{
+ return 0;
+}
+
+static inline void eap_peer_unregister_methods(void)
+{
+}
+
+#endif /* IEEE8021X_EAPOL */
+
+
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+
+int eap_peer_method_load(const char *so);
+int eap_peer_method_unload(struct eap_method *method);
+
+#else /* CONFIG_DYNAMIC_EAP_METHODS */
+
+static inline int eap_peer_method_load(const char *so)
+{
+ return 0;
+}
+
+static inline int eap_peer_method_unload(struct eap_method *method)
+{
+ return 0;
+}
+
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+#endif /* EAP_METHODS_H */
diff --git a/src/eap_peer/eap_mschapv2.c b/src/eap_peer/eap_mschapv2.c
new file mode 100644
index 0000000..e025442
--- /dev/null
+++ b/src/eap_peer/eap_mschapv2.c
@@ -0,0 +1,891 @@
+/*
+ * EAP peer method: EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt)
+ * Copyright (c) 2004-2008, 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 file implements EAP peer part of EAP-MSCHAPV2 method (EAP type 26).
+ * draft-kamath-pppext-eap-mschapv2-00.txt defines the Microsoft EAP CHAP
+ * Extensions Protocol, Version 2, for mutual authentication and key
+ * derivation. This encapsulates MS-CHAP-v2 protocol which is defined in
+ * RFC 2759. Use of EAP-MSCHAPV2 derived keys with MPPE cipher is described in
+ * RFC 3079.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_config.h"
+#include "ms_funcs.h"
+#include "wpa_ctrl.h"
+#include "mschapv2.h"
+
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct eap_mschapv2_hdr {
+ u8 op_code; /* MSCHAPV2_OP_* */
+ u8 mschapv2_id; /* usually same as EAP identifier; must be changed
+ * for challenges, but not for success/failure */
+ u8 ms_length[2]; /* Note: misaligned; length - 5 */
+ /* followed by data */
+} STRUCT_PACKED;
+
+/* Response Data field */
+struct ms_response {
+ u8 peer_challenge[MSCHAPV2_CHAL_LEN];
+ u8 reserved[8];
+ u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN];
+ u8 flags;
+} STRUCT_PACKED;
+
+/* Change-Password Data field */
+struct ms_change_password {
+ u8 encr_password[516];
+ u8 encr_hash[16];
+ u8 peer_challenge[MSCHAPV2_CHAL_LEN];
+ u8 reserved[8];
+ u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN];
+ u8 flags[2];
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+#define MSCHAPV2_OP_CHALLENGE 1
+#define MSCHAPV2_OP_RESPONSE 2
+#define MSCHAPV2_OP_SUCCESS 3
+#define MSCHAPV2_OP_FAILURE 4
+#define MSCHAPV2_OP_CHANGE_PASSWORD 7
+
+#define ERROR_RESTRICTED_LOGON_HOURS 646
+#define ERROR_ACCT_DISABLED 647
+#define ERROR_PASSWD_EXPIRED 648
+#define ERROR_NO_DIALIN_PERMISSION 649
+#define ERROR_AUTHENTICATION_FAILURE 691
+#define ERROR_CHANGING_PASSWORD 709
+
+#define PASSWD_CHANGE_CHAL_LEN 16
+#define MSCHAPV2_KEY_LEN 16
+
+
+struct eap_mschapv2_data {
+ u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN];
+ int auth_response_valid;
+
+ int prev_error;
+ u8 passwd_change_challenge[PASSWD_CHANGE_CHAL_LEN];
+ int passwd_change_challenge_valid;
+ int passwd_change_version;
+
+ /* Optional challenge values generated in EAP-FAST Phase 1 negotiation
+ */
+ u8 *peer_challenge;
+ u8 *auth_challenge;
+ int full_key;
+
+ int phase2;
+ u8 master_key[MSCHAPV2_MASTER_KEY_LEN];
+ int master_key_valid;
+ int success;
+
+ struct wpabuf *prev_challenge;
+};
+
+
+static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv);
+
+
+static void * eap_mschapv2_init(struct eap_sm *sm)
+{
+ struct eap_mschapv2_data *data;
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+
+ data->full_key = sm->mschapv2_full_key;
+
+ if (sm->peer_challenge) {
+ data->full_key = 1;
+ data->peer_challenge = os_malloc(MSCHAPV2_CHAL_LEN);
+ if (data->peer_challenge == NULL) {
+ eap_mschapv2_deinit(sm, data);
+ return NULL;
+ }
+ os_memcpy(data->peer_challenge, sm->peer_challenge,
+ MSCHAPV2_CHAL_LEN);
+ }
+
+ if (sm->auth_challenge) {
+ data->auth_challenge = os_malloc(MSCHAPV2_CHAL_LEN);
+ if (data->auth_challenge == NULL) {
+ eap_mschapv2_deinit(sm, data);
+ return NULL;
+ }
+ os_memcpy(data->auth_challenge, sm->auth_challenge,
+ MSCHAPV2_CHAL_LEN);
+ }
+
+ data->phase2 = sm->init_phase2;
+
+ return data;
+}
+
+
+static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_mschapv2_data *data = priv;
+ os_free(data->peer_challenge);
+ os_free(data->auth_challenge);
+ wpabuf_free(data->prev_challenge);
+ os_free(data);
+}
+
+
+static struct wpabuf * eap_mschapv2_challenge_reply(
+ struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id,
+ u8 mschapv2_id, const u8 *auth_challenge)
+{
+ struct wpabuf *resp;
+ struct eap_mschapv2_hdr *ms;
+ u8 *peer_challenge;
+ int ms_len;
+ struct ms_response *r;
+ size_t identity_len, password_len;
+ const u8 *identity, *password;
+ int pwhash;
+
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Generating Challenge Response");
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ password = eap_get_config_password2(sm, &password_len, &pwhash);
+ if (identity == NULL || password == NULL)
+ return NULL;
+
+ ms_len = sizeof(*ms) + 1 + sizeof(*r) + identity_len;
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
+ EAP_CODE_RESPONSE, id);
+ if (resp == NULL)
+ return NULL;
+
+ ms = wpabuf_put(resp, sizeof(*ms));
+ ms->op_code = MSCHAPV2_OP_RESPONSE;
+ ms->mschapv2_id = mschapv2_id;
+ if (data->prev_error) {
+ /*
+ * TODO: this does not seem to be enough when processing two
+ * or more failure messages. IAS did not increment mschapv2_id
+ * in its own packets, but it seemed to expect the peer to
+ * increment this for all packets(?).
+ */
+ ms->mschapv2_id++;
+ }
+ WPA_PUT_BE16(ms->ms_length, ms_len);
+
+ wpabuf_put_u8(resp, sizeof(*r)); /* Value-Size */
+
+ /* Response */
+ r = wpabuf_put(resp, sizeof(*r));
+ peer_challenge = r->peer_challenge;
+ if (data->peer_challenge) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge generated "
+ "in Phase 1");
+ peer_challenge = data->peer_challenge;
+ os_memset(r->peer_challenge, 0, MSCHAPV2_CHAL_LEN);
+ } else if (os_get_random(peer_challenge, MSCHAPV2_CHAL_LEN)) {
+ wpabuf_free(resp);
+ return NULL;
+ }
+ os_memset(r->reserved, 0, 8);
+ if (data->auth_challenge) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge generated "
+ "in Phase 1");
+ auth_challenge = data->auth_challenge;
+ }
+ mschapv2_derive_response(identity, identity_len, password,
+ password_len, pwhash, auth_challenge,
+ peer_challenge, r->nt_response,
+ data->auth_response, data->master_key);
+ data->auth_response_valid = 1;
+ data->master_key_valid = 1;
+
+ r->flags = 0; /* reserved, must be zero */
+
+ wpabuf_put_data(resp, identity, identity_len);
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
+ "(response)", id, ms->mschapv2_id);
+ return resp;
+}
+
+
+/**
+ * eap_mschapv2_process - Process an EAP-MSCHAPv2 challenge message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Pointer to private EAP method data from eap_mschapv2_init()
+ * @ret: Return values from EAP request validation and processing
+ * @req: Pointer to EAP-MSCHAPv2 header from the request
+ * @req_len: Length of the EAP-MSCHAPv2 data
+ * @id: EAP identifier used in the request
+ * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
+ * no reply available
+ */
+static struct wpabuf * eap_mschapv2_challenge(
+ struct eap_sm *sm, struct eap_mschapv2_data *data,
+ struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req,
+ size_t req_len, u8 id)
+{
+ size_t len, challenge_len;
+ const u8 *pos, *challenge;
+
+ if (eap_get_config_identity(sm, &len) == NULL ||
+ eap_get_config_password(sm, &len) == NULL)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received challenge");
+ if (req_len < sizeof(*req) + 1) {
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge data "
+ "(len %lu)", (unsigned long) req_len);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ pos = (const u8 *) (req + 1);
+ challenge_len = *pos++;
+ len = req_len - sizeof(*req) - 1;
+ if (challenge_len != MSCHAPV2_CHAL_LEN) {
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid challenge length "
+ "%lu", (unsigned long) challenge_len);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (len < challenge_len) {
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge"
+ " packet: len=%lu challenge_len=%lu",
+ (unsigned long) len, (unsigned long) challenge_len);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (data->passwd_change_challenge_valid) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using challenge from the "
+ "failure message");
+ challenge = data->passwd_change_challenge;
+ } else
+ challenge = pos;
+ pos += challenge_len;
+ len -= challenge_len;
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Authentication Servername",
+ pos, len);
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = TRUE;
+
+ return eap_mschapv2_challenge_reply(sm, data, id, req->mschapv2_id,
+ challenge);
+}
+
+
+static void eap_mschapv2_password_changed(struct eap_sm *sm,
+ struct eap_mschapv2_data *data)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config && config->new_password) {
+ wpa_msg(sm->msg_ctx, MSG_INFO,
+ WPA_EVENT_PASSWORD_CHANGED
+ "EAP-MSCHAPV2: Password changed successfully");
+ data->prev_error = 0;
+ os_free(config->password);
+ if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) {
+ config->password = os_malloc(16);
+ config->password_len = 16;
+ if (config->password) {
+ nt_password_hash(config->new_password,
+ config->new_password_len,
+ config->password);
+ }
+ os_free(config->new_password);
+ } else {
+ config->password = config->new_password;
+ config->password_len = config->new_password_len;
+ }
+ config->new_password = NULL;
+ config->new_password_len = 0;
+ }
+}
+
+
+/**
+ * eap_mschapv2_process - Process an EAP-MSCHAPv2 success message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Pointer to private EAP method data from eap_mschapv2_init()
+ * @ret: Return values from EAP request validation and processing
+ * @req: Pointer to EAP-MSCHAPv2 header from the request
+ * @req_len: Length of the EAP-MSCHAPv2 data
+ * @id: EAP identifier used in th erequest
+ * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
+ * no reply available
+ */
+static struct wpabuf * eap_mschapv2_success(struct eap_sm *sm,
+ struct eap_mschapv2_data *data,
+ struct eap_method_ret *ret,
+ const struct eap_mschapv2_hdr *req,
+ size_t req_len, u8 id)
+{
+ struct wpabuf *resp;
+ const u8 *pos;
+ size_t len;
+
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received success");
+ len = req_len - sizeof(*req);
+ pos = (const u8 *) (req + 1);
+ if (!data->auth_response_valid ||
+ mschapv2_verify_auth_response(data->auth_response, pos, len)) {
+ wpa_printf(MSG_WARNING, "EAP-MSCHAPV2: Invalid authenticator "
+ "response in success request");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return NULL;
+ }
+ pos += 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
+ len -= 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
+ while (len > 0 && *pos == ' ') {
+ pos++;
+ len--;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Success message",
+ pos, len);
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Authentication succeeded");
+
+ /* Note: Only op_code of the EAP-MSCHAPV2 header is included in success
+ * message. */
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
+ EAP_CODE_RESPONSE, id);
+ if (resp == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Failed to allocate "
+ "buffer for success response");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ wpabuf_put_u8(resp, MSCHAPV2_OP_SUCCESS); /* op_code */
+
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ ret->allowNotifications = FALSE;
+ data->success = 1;
+
+ if (data->prev_error == ERROR_PASSWD_EXPIRED)
+ eap_mschapv2_password_changed(sm, data);
+
+ return resp;
+}
+
+
+static int eap_mschapv2_failure_txt(struct eap_sm *sm,
+ struct eap_mschapv2_data *data, char *txt)
+{
+ char *pos, *msg = "";
+ int retry = 1;
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ /* For example:
+ * E=691 R=1 C=<32 octets hex challenge> V=3 M=Authentication Failure
+ */
+
+ pos = txt;
+
+ if (pos && os_strncmp(pos, "E=", 2) == 0) {
+ pos += 2;
+ data->prev_error = atoi(pos);
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: error %d",
+ data->prev_error);
+ pos = os_strchr(pos, ' ');
+ if (pos)
+ pos++;
+ }
+
+ if (pos && os_strncmp(pos, "R=", 2) == 0) {
+ pos += 2;
+ retry = atoi(pos);
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: retry is %sallowed",
+ retry == 1 ? "" : "not ");
+ pos = os_strchr(pos, ' ');
+ if (pos)
+ pos++;
+ }
+
+ if (pos && os_strncmp(pos, "C=", 2) == 0) {
+ int hex_len;
+ pos += 2;
+ hex_len = os_strchr(pos, ' ') - (char *) pos;
+ if (hex_len == PASSWD_CHANGE_CHAL_LEN * 2) {
+ if (hexstr2bin(pos, data->passwd_change_challenge,
+ PASSWD_CHANGE_CHAL_LEN)) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid "
+ "failure challenge");
+ } else {
+ wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: failure "
+ "challenge",
+ data->passwd_change_challenge,
+ PASSWD_CHANGE_CHAL_LEN);
+ data->passwd_change_challenge_valid = 1;
+ }
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid failure "
+ "challenge len %d", hex_len);
+ }
+ pos = os_strchr(pos, ' ');
+ if (pos)
+ pos++;
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: required challenge field "
+ "was not present in failure message");
+ }
+
+ if (pos && os_strncmp(pos, "V=", 2) == 0) {
+ pos += 2;
+ data->passwd_change_version = atoi(pos);
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: password changing "
+ "protocol version %d", data->passwd_change_version);
+ pos = os_strchr(pos, ' ');
+ if (pos)
+ pos++;
+ }
+
+ if (pos && os_strncmp(pos, "M=", 2) == 0) {
+ pos += 2;
+ msg = pos;
+ }
+ wpa_msg(sm->msg_ctx, MSG_WARNING,
+ "EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error "
+ "%d)",
+ msg, retry == 1 ? "" : "not ", data->prev_error);
+ if (data->prev_error == ERROR_PASSWD_EXPIRED &&
+ data->passwd_change_version == 3 && config) {
+ if (config->new_password == NULL) {
+ wpa_msg(sm->msg_ctx, MSG_INFO,
+ "EAP-MSCHAPV2: Password expired - password "
+ "change required");
+ eap_sm_request_new_password(sm);
+ }
+ } else if (retry == 1 && config) {
+ /* TODO: could prevent the current password from being used
+ * again at least for some period of time */
+ if (!config->mschapv2_retry)
+ eap_sm_request_identity(sm);
+ eap_sm_request_password(sm);
+ config->mschapv2_retry = 1;
+ } else if (config) {
+ /* TODO: prevent retries using same username/password */
+ config->mschapv2_retry = 0;
+ }
+
+ return retry == 1;
+}
+
+
+static struct wpabuf * eap_mschapv2_change_password(
+ struct eap_sm *sm, struct eap_mschapv2_data *data,
+ struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, u8 id)
+{
+ struct wpabuf *resp;
+ int ms_len;
+ const u8 *username, *password, *new_password;
+ size_t username_len, password_len, new_password_len;
+ struct eap_mschapv2_hdr *ms;
+ struct ms_change_password *cp;
+ u8 password_hash[16], password_hash_hash[16];
+ int pwhash;
+
+ username = eap_get_config_identity(sm, &username_len);
+ password = eap_get_config_password2(sm, &password_len, &pwhash);
+ new_password = eap_get_config_new_password(sm, &new_password_len);
+ if (username == NULL || password == NULL || new_password == NULL)
+ return NULL;
+
+ username = mschapv2_remove_domain(username, &username_len);
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_COND_SUCC;
+ ret->allowNotifications = TRUE;
+
+ ms_len = sizeof(*ms) + sizeof(*cp);
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
+ EAP_CODE_RESPONSE, id);
+ if (resp == NULL)
+ return NULL;
+
+ ms = wpabuf_put(resp, sizeof(*ms));
+ ms->op_code = MSCHAPV2_OP_CHANGE_PASSWORD;
+ ms->mschapv2_id = req->mschapv2_id + 1;
+ WPA_PUT_BE16(ms->ms_length, ms_len);
+ cp = wpabuf_put(resp, sizeof(*cp));
+
+ /* Encrypted-Password */
+ if (pwhash) {
+ if (encrypt_pw_block_with_password_hash(
+ new_password, new_password_len,
+ password, cp->encr_password))
+ goto fail;
+ } else {
+ if (new_password_encrypted_with_old_nt_password_hash(
+ new_password, new_password_len,
+ password, password_len, cp->encr_password))
+ goto fail;
+ }
+
+ /* Encrypted-Hash */
+ if (pwhash) {
+ u8 new_password_hash[16];
+ nt_password_hash(new_password, new_password_len,
+ new_password_hash);
+ nt_password_hash_encrypted_with_block(password,
+ new_password_hash,
+ cp->encr_hash);
+ } else {
+ old_nt_password_hash_encrypted_with_new_nt_password_hash(
+ new_password, new_password_len,
+ password, password_len, cp->encr_hash);
+ }
+
+ /* Peer-Challenge */
+ if (os_get_random(cp->peer_challenge, MSCHAPV2_CHAL_LEN))
+ goto fail;
+
+ /* Reserved, must be zero */
+ os_memset(cp->reserved, 0, 8);
+
+ /* NT-Response */
+ wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge",
+ data->passwd_change_challenge, PASSWD_CHANGE_CHAL_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge",
+ cp->peer_challenge, MSCHAPV2_CHAL_LEN);
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: username",
+ username, username_len);
+ wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-MSCHAPV2: new password",
+ new_password, new_password_len);
+ generate_nt_response(data->passwd_change_challenge, cp->peer_challenge,
+ username, username_len,
+ new_password, new_password_len,
+ cp->nt_response);
+ wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: NT-Response",
+ cp->nt_response, MSCHAPV2_NT_RESPONSE_LEN);
+
+ /* Authenticator response is not really needed yet, but calculate it
+ * here so that challenges need not be saved. */
+ generate_authenticator_response(new_password, new_password_len,
+ cp->peer_challenge,
+ data->passwd_change_challenge,
+ username, username_len,
+ cp->nt_response, data->auth_response);
+ data->auth_response_valid = 1;
+
+ /* Likewise, generate master_key here since we have the needed data
+ * available. */
+ nt_password_hash(new_password, new_password_len, password_hash);
+ hash_nt_password_hash(password_hash, password_hash_hash);
+ get_master_key(password_hash_hash, cp->nt_response, data->master_key);
+ data->master_key_valid = 1;
+
+ /* Flags */
+ os_memset(cp->flags, 0, 2);
+
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
+ "(change pw)", id, ms->mschapv2_id);
+
+ return resp;
+
+fail:
+ wpabuf_free(resp);
+ return NULL;
+}
+
+
+/**
+ * eap_mschapv2_process - Process an EAP-MSCHAPv2 failure message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Pointer to private EAP method data from eap_mschapv2_init()
+ * @ret: Return values from EAP request validation and processing
+ * @req: Pointer to EAP-MSCHAPv2 header from the request
+ * @req_len: Length of the EAP-MSCHAPv2 data
+ * @id: EAP identifier used in th erequest
+ * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
+ * no reply available
+ */
+static struct wpabuf * eap_mschapv2_failure(struct eap_sm *sm,
+ struct eap_mschapv2_data *data,
+ struct eap_method_ret *ret,
+ const struct eap_mschapv2_hdr *req,
+ size_t req_len, u8 id)
+{
+ struct wpabuf *resp;
+ const u8 *msdata = (const u8 *) (req + 1);
+ char *buf;
+ size_t len = req_len - sizeof(*req);
+ int retry = 0;
+
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received failure");
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Failure data",
+ msdata, len);
+ /*
+ * eap_mschapv2_failure_txt() expects a nul terminated string, so we
+ * must allocate a large enough temporary buffer to create that since
+ * the received message does not include nul termination.
+ */
+ buf = os_malloc(len + 1);
+ if (buf) {
+ os_memcpy(buf, msdata, len);
+ buf[len] = '\0';
+ retry = eap_mschapv2_failure_txt(sm, data, buf);
+ os_free(buf);
+ }
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = FALSE;
+
+ if (data->prev_error == ERROR_PASSWD_EXPIRED &&
+ data->passwd_change_version == 3) {
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config && config->new_password)
+ return eap_mschapv2_change_password(sm, data, ret, req,
+ id);
+ if (config && config->pending_req_new_password)
+ return NULL;
+ } else if (retry && data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
+ /* TODO: could try to retry authentication, e.g, after having
+ * changed the username/password. In this case, EAP MS-CHAP-v2
+ * Failure Response would not be sent here. */
+ return NULL;
+ }
+
+ /* Note: Only op_code of the EAP-MSCHAPV2 header is included in failure
+ * message. */
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
+ EAP_CODE_RESPONSE, id);
+ if (resp == NULL)
+ return NULL;
+
+ wpabuf_put_u8(resp, MSCHAPV2_OP_FAILURE); /* op_code */
+
+ return resp;
+}
+
+
+static int eap_mschapv2_check_config(struct eap_sm *sm)
+{
+ size_t len;
+
+ if (eap_get_config_identity(sm, &len) == NULL) {
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Identity not configured");
+ eap_sm_request_identity(sm);
+ return -1;
+ }
+
+ if (eap_get_config_password(sm, &len) == NULL) {
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
+ eap_sm_request_password(sm);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int eap_mschapv2_check_mslen(struct eap_sm *sm, size_t len,
+ const struct eap_mschapv2_hdr *ms)
+{
+ size_t ms_len = WPA_GET_BE16(ms->ms_length);
+
+ if (ms_len == len)
+ return 0;
+
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid header: len=%lu "
+ "ms_len=%lu", (unsigned long) len, (unsigned long) ms_len);
+ if (sm->workaround) {
+ /* Some authentication servers use invalid ms_len,
+ * ignore it for interoperability. */
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: workaround, ignore"
+ " invalid ms_len %lu (len %lu)",
+ (unsigned long) ms_len,
+ (unsigned long) len);
+ return 0;
+ }
+
+ return -1;
+}
+
+
+static void eap_mschapv2_copy_challenge(struct eap_mschapv2_data *data,
+ const struct wpabuf *reqData)
+{
+ /*
+ * Store a copy of the challenge message, so that it can be processed
+ * again in case retry is allowed after a possible failure.
+ */
+ wpabuf_free(data->prev_challenge);
+ data->prev_challenge = wpabuf_dup(reqData);
+}
+
+
+/**
+ * eap_mschapv2_process - Process an EAP-MSCHAPv2 request
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_mschapv2_init()
+ * @ret: Return values from EAP request validation and processing
+ * @reqData: EAP request to be processed (eapReqData)
+ * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
+ * no reply available
+ */
+static struct wpabuf * eap_mschapv2_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct eap_mschapv2_data *data = priv;
+ struct eap_peer_config *config = eap_get_config(sm);
+ const struct eap_mschapv2_hdr *ms;
+ int using_prev_challenge = 0;
+ const u8 *pos;
+ size_t len;
+ u8 id;
+
+ if (eap_mschapv2_check_config(sm)) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (config->mschapv2_retry && data->prev_challenge &&
+ data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Replacing pending packet "
+ "with the previous challenge");
+
+ reqData = data->prev_challenge;
+ using_prev_challenge = 1;
+ config->mschapv2_retry = 0;
+ }
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, reqData,
+ &len);
+ if (pos == NULL || len < sizeof(*ms) + 1) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ ms = (const struct eap_mschapv2_hdr *) pos;
+ if (eap_mschapv2_check_mslen(sm, len, ms)) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ id = eap_get_id(reqData);
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: RX identifier %d mschapv2_id %d",
+ id, ms->mschapv2_id);
+
+ switch (ms->op_code) {
+ case MSCHAPV2_OP_CHALLENGE:
+ if (!using_prev_challenge)
+ eap_mschapv2_copy_challenge(data, reqData);
+ return eap_mschapv2_challenge(sm, data, ret, ms, len, id);
+ case MSCHAPV2_OP_SUCCESS:
+ return eap_mschapv2_success(sm, data, ret, ms, len, id);
+ case MSCHAPV2_OP_FAILURE:
+ return eap_mschapv2_failure(sm, data, ret, ms, len, id);
+ default:
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Unknown op %d - ignored",
+ ms->op_code);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+}
+
+
+static Boolean eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_mschapv2_data *data = priv;
+ return data->success && data->master_key_valid;
+}
+
+
+static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_mschapv2_data *data = priv;
+ u8 *key;
+ int key_len;
+
+ if (!data->master_key_valid || !data->success)
+ return NULL;
+
+ if (data->full_key) {
+ /* EAP-FAST needs both send and receive keys */
+ key_len = 2 * MSCHAPV2_KEY_LEN;
+ } else {
+ key_len = MSCHAPV2_KEY_LEN;
+ }
+
+ key = os_malloc(key_len);
+ if (key == NULL)
+ return NULL;
+
+ if (data->full_key) {
+ get_asymetric_start_key(data->master_key, key,
+ MSCHAPV2_KEY_LEN, 0, 0);
+ get_asymetric_start_key(data->master_key,
+ key + MSCHAPV2_KEY_LEN,
+ MSCHAPV2_KEY_LEN, 1, 0);
+ } else {
+ get_asymetric_start_key(data->master_key, key,
+ MSCHAPV2_KEY_LEN, 1, 0);
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key",
+ key, key_len);
+
+ *len = key_len;
+ return key;
+}
+
+
+/**
+ * eap_peer_mschapv2_register - Register EAP-MSCHAPv2 peer method
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to register EAP-MSCHAPv2 peer method into the EAP
+ * method list.
+ */
+int eap_peer_mschapv2_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
+ "MSCHAPV2");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_mschapv2_init;
+ eap->deinit = eap_mschapv2_deinit;
+ eap->process = eap_mschapv2_process;
+ eap->isKeyAvailable = eap_mschapv2_isKeyAvailable;
+ eap->getKey = eap_mschapv2_getKey;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_otp.c b/src/eap_peer/eap_otp.c
new file mode 100644
index 0000000..556c22f
--- /dev/null
+++ b/src/eap_peer/eap_otp.c
@@ -0,0 +1,107 @@
+/*
+ * EAP peer method: EAP-OTP (RFC 3748)
+ * Copyright (c) 2004-2006, 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+
+
+static void * eap_otp_init(struct eap_sm *sm)
+{
+ /* No need for private data. However, must return non-NULL to indicate
+ * success. */
+ return (void *) 1;
+}
+
+
+static void eap_otp_deinit(struct eap_sm *sm, void *priv)
+{
+}
+
+
+static struct wpabuf * eap_otp_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct wpabuf *resp;
+ const u8 *pos, *password;
+ size_t password_len, len;
+ int otp;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_OTP, reqData, &len);
+ if (pos == NULL) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-OTP: Request message",
+ pos, len);
+
+ password = eap_get_config_otp(sm, &password_len);
+ if (password)
+ otp = 1;
+ else {
+ password = eap_get_config_password(sm, &password_len);
+ otp = 0;
+ }
+
+ if (password == NULL) {
+ wpa_printf(MSG_INFO, "EAP-OTP: Password not configured");
+ eap_sm_request_otp(sm, (const char *) pos, len);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ ret->ignore = FALSE;
+
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_COND_SUCC;
+ ret->allowNotifications = FALSE;
+
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_OTP, password_len,
+ EAP_CODE_RESPONSE, eap_get_id(reqData));
+ if (resp == NULL)
+ return NULL;
+ wpabuf_put_data(resp, password, password_len);
+ wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-OTP: Response",
+ password, password_len);
+
+ if (otp) {
+ wpa_printf(MSG_DEBUG, "EAP-OTP: Forgetting used password");
+ eap_clear_config_otp(sm);
+ }
+
+ return resp;
+}
+
+
+int eap_peer_otp_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_OTP, "OTP");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_otp_init;
+ eap->deinit = eap_otp_deinit;
+ eap->process = eap_otp_process;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_pax.c b/src/eap_peer/eap_pax.c
new file mode 100644
index 0000000..afd56dd
--- /dev/null
+++ b/src/eap_peer/eap_pax.c
@@ -0,0 +1,532 @@
+/*
+ * EAP peer method: EAP-PAX (RFC 4746)
+ * Copyright (c) 2005-2008, 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_peer/eap_i.h"
+#include "eap_common/eap_pax_common.h"
+#include "sha1.h"
+#include "crypto.h"
+
+/*
+ * Note: only PAX_STD subprotocol is currently supported
+ *
+ * TODO: Add support with PAX_SEC with the mandatory to implement ciphersuite
+ * (HMAC_SHA1_128, IANA DH Group 14 (2048 bits), RSA-PKCS1-V1_5) and
+ * recommended ciphersuite (HMAC_SHA256_128, IANA DH Group 15 (3072 bits),
+ * RSAES-OAEP).
+ */
+
+struct eap_pax_data {
+ enum { PAX_INIT, PAX_STD_2_SENT, PAX_DONE } state;
+ u8 mac_id, dh_group_id, public_key_id;
+ union {
+ u8 e[2 * EAP_PAX_RAND_LEN];
+ struct {
+ u8 x[EAP_PAX_RAND_LEN]; /* server rand */
+ u8 y[EAP_PAX_RAND_LEN]; /* client rand */
+ } r;
+ } rand;
+ char *cid;
+ size_t cid_len;
+ u8 ak[EAP_PAX_AK_LEN];
+ u8 mk[EAP_PAX_MK_LEN];
+ u8 ck[EAP_PAX_CK_LEN];
+ u8 ick[EAP_PAX_ICK_LEN];
+};
+
+
+static void eap_pax_deinit(struct eap_sm *sm, void *priv);
+
+
+static void * eap_pax_init(struct eap_sm *sm)
+{
+ struct eap_pax_data *data;
+ const u8 *identity, *password;
+ size_t identity_len, password_len;
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ password = eap_get_config_password(sm, &password_len);
+ if (!identity || !password) {
+ wpa_printf(MSG_INFO, "EAP-PAX: CID (nai) or key (password) "
+ "not configured");
+ return NULL;
+ }
+
+ if (password_len != EAP_PAX_AK_LEN) {
+ wpa_printf(MSG_INFO, "EAP-PAX: Invalid PSK length");
+ return NULL;
+ }
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->state = PAX_INIT;
+
+ data->cid = os_malloc(identity_len);
+ if (data->cid == NULL) {
+ eap_pax_deinit(sm, data);
+ return NULL;
+ }
+ os_memcpy(data->cid, identity, identity_len);
+ data->cid_len = identity_len;
+
+ os_memcpy(data->ak, password, EAP_PAX_AK_LEN);
+
+ return data;
+}
+
+
+static void eap_pax_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_pax_data *data = priv;
+ os_free(data->cid);
+ os_free(data);
+}
+
+
+static struct wpabuf * eap_pax_alloc_resp(const struct eap_pax_hdr *req,
+ u8 id, u8 op_code, size_t plen)
+{
+ struct wpabuf *resp;
+ struct eap_pax_hdr *pax;
+
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX,
+ sizeof(*pax) + plen, EAP_CODE_RESPONSE, id);
+ if (resp == NULL)
+ return NULL;
+
+ pax = wpabuf_put(resp, sizeof(*pax));
+ pax->op_code = op_code;
+ pax->flags = 0;
+ pax->mac_id = req->mac_id;
+ pax->dh_group_id = req->dh_group_id;
+ pax->public_key_id = req->public_key_id;
+
+ return resp;
+}
+
+
+static struct wpabuf * eap_pax_process_std_1(struct eap_pax_data *data,
+ struct eap_method_ret *ret, u8 id,
+ const struct eap_pax_hdr *req,
+ size_t req_plen)
+{
+ struct wpabuf *resp;
+ const u8 *pos;
+ u8 *rpos;
+ size_t left, plen;
+
+ wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (received)");
+
+ if (data->state != PAX_INIT) {
+ wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 received in "
+ "unexpected state (%d) - ignored", data->state);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (req->flags & EAP_PAX_FLAGS_CE) {
+ wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with CE flag set - "
+ "ignored");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ left = req_plen - sizeof(*req);
+
+ if (left < 2 + EAP_PAX_RAND_LEN) {
+ wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with too short "
+ "payload");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ pos = (const u8 *) (req + 1);
+ if (WPA_GET_BE16(pos) != EAP_PAX_RAND_LEN) {
+ wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with incorrect A "
+ "length %d (expected %d)",
+ WPA_GET_BE16(pos), EAP_PAX_RAND_LEN);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ pos += 2;
+ left -= 2;
+ os_memcpy(data->rand.r.x, pos, EAP_PAX_RAND_LEN);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: X (server rand)",
+ data->rand.r.x, EAP_PAX_RAND_LEN);
+ pos += EAP_PAX_RAND_LEN;
+ left -= EAP_PAX_RAND_LEN;
+
+ if (left > 0) {
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload",
+ pos, left);
+ }
+
+ if (os_get_random(data->rand.r.y, EAP_PAX_RAND_LEN)) {
+ wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)",
+ data->rand.r.y, EAP_PAX_RAND_LEN);
+
+ if (eap_pax_initial_key_derivation(req->mac_id, data->ak, data->rand.e,
+ data->mk, data->ck, data->ick) < 0)
+ {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-2 (sending)");
+
+ plen = 2 + EAP_PAX_RAND_LEN + 2 + data->cid_len + 2 + EAP_PAX_MAC_LEN +
+ EAP_PAX_ICV_LEN;
+ resp = eap_pax_alloc_resp(req, id, EAP_PAX_OP_STD_2, plen);
+ if (resp == NULL)
+ return NULL;
+
+ wpabuf_put_be16(resp, EAP_PAX_RAND_LEN);
+ wpabuf_put_data(resp, data->rand.r.y, EAP_PAX_RAND_LEN);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: B = Y (client rand)",
+ data->rand.r.y, EAP_PAX_RAND_LEN);
+
+ wpabuf_put_be16(resp, data->cid_len);
+ wpabuf_put_data(resp, data->cid, data->cid_len);
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID",
+ (u8 *) data->cid, data->cid_len);
+
+ wpabuf_put_be16(resp, EAP_PAX_MAC_LEN);
+ rpos = wpabuf_put(resp, EAP_PAX_MAC_LEN);
+ eap_pax_mac(req->mac_id, data->ck, EAP_PAX_CK_LEN,
+ data->rand.r.x, EAP_PAX_RAND_LEN,
+ data->rand.r.y, EAP_PAX_RAND_LEN,
+ (u8 *) data->cid, data->cid_len, rpos);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)",
+ rpos, EAP_PAX_MAC_LEN);
+
+ /* Optional ADE could be added here, if needed */
+
+ rpos = wpabuf_put(resp, EAP_PAX_ICV_LEN);
+ eap_pax_mac(req->mac_id, data->ick, EAP_PAX_ICK_LEN,
+ wpabuf_head(resp), wpabuf_len(resp) - EAP_PAX_ICV_LEN,
+ NULL, 0, NULL, 0, rpos);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", rpos, EAP_PAX_ICV_LEN);
+
+ data->state = PAX_STD_2_SENT;
+ data->mac_id = req->mac_id;
+ data->dh_group_id = req->dh_group_id;
+ data->public_key_id = req->public_key_id;
+
+ return resp;
+}
+
+
+static struct wpabuf * eap_pax_process_std_3(struct eap_pax_data *data,
+ struct eap_method_ret *ret, u8 id,
+ const struct eap_pax_hdr *req,
+ size_t req_plen)
+{
+ struct wpabuf *resp;
+ u8 *rpos, mac[EAP_PAX_MAC_LEN];
+ const u8 *pos;
+ size_t left;
+
+ wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (received)");
+
+ if (data->state != PAX_STD_2_SENT) {
+ wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 received in "
+ "unexpected state (%d) - ignored", data->state);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (req->flags & EAP_PAX_FLAGS_CE) {
+ wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with CE flag set - "
+ "ignored");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ left = req_plen - sizeof(*req);
+
+ if (left < 2 + EAP_PAX_MAC_LEN) {
+ wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with too short "
+ "payload");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ pos = (const u8 *) (req + 1);
+ if (WPA_GET_BE16(pos) != EAP_PAX_MAC_LEN) {
+ wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with incorrect "
+ "MAC_CK length %d (expected %d)",
+ WPA_GET_BE16(pos), EAP_PAX_MAC_LEN);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ pos += 2;
+ left -= 2;
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)",
+ pos, EAP_PAX_MAC_LEN);
+ eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
+ data->rand.r.y, EAP_PAX_RAND_LEN,
+ (u8 *) data->cid, data->cid_len, NULL, 0, mac);
+ if (os_memcmp(pos, mac, EAP_PAX_MAC_LEN) != 0) {
+ wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(B, CID) "
+ "received");
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: expected MAC_CK(B, CID)",
+ mac, EAP_PAX_MAC_LEN);
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return NULL;
+ }
+
+ pos += EAP_PAX_MAC_LEN;
+ left -= EAP_PAX_MAC_LEN;
+
+ if (left > 0) {
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload",
+ pos, left);
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-PAX: PAX-ACK (sending)");
+
+ resp = eap_pax_alloc_resp(req, id, EAP_PAX_OP_ACK, EAP_PAX_ICV_LEN);
+ if (resp == NULL)
+ return NULL;
+
+ /* Optional ADE could be added here, if needed */
+
+ rpos = wpabuf_put(resp, EAP_PAX_ICV_LEN);
+ eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
+ wpabuf_head(resp), wpabuf_len(resp) - EAP_PAX_ICV_LEN,
+ NULL, 0, NULL, 0, rpos);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", rpos, EAP_PAX_ICV_LEN);
+
+ data->state = PAX_DONE;
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ ret->allowNotifications = FALSE;
+
+ return resp;
+}
+
+
+static struct wpabuf * eap_pax_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct eap_pax_data *data = priv;
+ const struct eap_pax_hdr *req;
+ struct wpabuf *resp;
+ u8 icvbuf[EAP_PAX_ICV_LEN], id;
+ const u8 *icv, *pos;
+ size_t len;
+ u16 flen, mlen;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, reqData, &len);
+ if (pos == NULL || len < EAP_PAX_ICV_LEN) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ id = eap_get_id(reqData);
+ req = (const struct eap_pax_hdr *) pos;
+ flen = len - EAP_PAX_ICV_LEN;
+ mlen = wpabuf_len(reqData) - EAP_PAX_ICV_LEN;
+
+ wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x "
+ "flags 0x%x mac_id 0x%x dh_group_id 0x%x "
+ "public_key_id 0x%x",
+ req->op_code, req->flags, req->mac_id, req->dh_group_id,
+ req->public_key_id);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload",
+ pos, len - EAP_PAX_ICV_LEN);
+
+ if (data->state != PAX_INIT && data->mac_id != req->mac_id) {
+ wpa_printf(MSG_INFO, "EAP-PAX: MAC ID changed during "
+ "authentication (was 0x%d, is 0x%d)",
+ data->mac_id, req->mac_id);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (data->state != PAX_INIT && data->dh_group_id != req->dh_group_id) {
+ wpa_printf(MSG_INFO, "EAP-PAX: DH Group ID changed during "
+ "authentication (was 0x%d, is 0x%d)",
+ data->dh_group_id, req->dh_group_id);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (data->state != PAX_INIT &&
+ data->public_key_id != req->public_key_id) {
+ wpa_printf(MSG_INFO, "EAP-PAX: Public Key ID changed during "
+ "authentication (was 0x%d, is 0x%d)",
+ data->public_key_id, req->public_key_id);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ /* TODO: add support EAP_PAX_HMAC_SHA256_128 */
+ if (req->mac_id != EAP_PAX_MAC_HMAC_SHA1_128) {
+ wpa_printf(MSG_INFO, "EAP-PAX: Unsupported MAC ID 0x%x",
+ req->mac_id);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (req->dh_group_id != EAP_PAX_DH_GROUP_NONE) {
+ wpa_printf(MSG_INFO, "EAP-PAX: Unsupported DH Group ID 0x%x",
+ req->dh_group_id);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (req->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) {
+ wpa_printf(MSG_INFO, "EAP-PAX: Unsupported Public Key ID 0x%x",
+ req->public_key_id);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (req->flags & EAP_PAX_FLAGS_MF) {
+ /* TODO: add support for reassembling fragments */
+ wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported - "
+ "ignored packet");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ icv = pos + len - EAP_PAX_ICV_LEN;
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN);
+ if (req->op_code == EAP_PAX_OP_STD_1) {
+ eap_pax_mac(req->mac_id, (u8 *) "", 0,
+ wpabuf_head(reqData), mlen, NULL, 0, NULL, 0,
+ icvbuf);
+ } else {
+ eap_pax_mac(req->mac_id, data->ick, EAP_PAX_ICK_LEN,
+ wpabuf_head(reqData), mlen, NULL, 0, NULL, 0,
+ icvbuf);
+ }
+ if (os_memcmp(icv, icvbuf, EAP_PAX_ICV_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "EAP-PAX: invalid ICV - ignoring the "
+ "message");
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: expected ICV",
+ icvbuf, EAP_PAX_ICV_LEN);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = TRUE;
+
+ switch (req->op_code) {
+ case EAP_PAX_OP_STD_1:
+ resp = eap_pax_process_std_1(data, ret, id, req, flen);
+ break;
+ case EAP_PAX_OP_STD_3:
+ resp = eap_pax_process_std_3(data, ret, id, req, flen);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-PAX: ignoring message with unknown "
+ "op_code %d", req->op_code);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (ret->methodState == METHOD_DONE) {
+ ret->allowNotifications = FALSE;
+ }
+
+ return resp;
+}
+
+
+static Boolean eap_pax_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_pax_data *data = priv;
+ return data->state == PAX_DONE;
+}
+
+
+static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_pax_data *data = priv;
+ u8 *key;
+
+ if (data->state != PAX_DONE)
+ return NULL;
+
+ key = os_malloc(EAP_MSK_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_MSK_LEN;
+ eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
+ "Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN,
+ EAP_MSK_LEN, key);
+
+ return key;
+}
+
+
+static u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_pax_data *data = priv;
+ u8 *key;
+
+ if (data->state != PAX_DONE)
+ return NULL;
+
+ key = os_malloc(EAP_EMSK_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_EMSK_LEN;
+ eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
+ "Extended Master Session Key",
+ data->rand.e, 2 * EAP_PAX_RAND_LEN,
+ EAP_EMSK_LEN, key);
+
+ return key;
+}
+
+
+int eap_peer_pax_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_pax_init;
+ eap->deinit = eap_pax_deinit;
+ eap->process = eap_pax_process;
+ eap->isKeyAvailable = eap_pax_isKeyAvailable;
+ eap->getKey = eap_pax_getKey;
+ eap->get_emsk = eap_pax_get_emsk;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c
new file mode 100644
index 0000000..1f77aa7
--- /dev/null
+++ b/src/eap_peer/eap_peap.c
@@ -0,0 +1,810 @@
+/*
+ * EAP peer method: EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt)
+ * Copyright (c) 2004-2008, 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "eap_config.h"
+#include "tls.h"
+#include "eap_tlv.h"
+
+
+/* Maximum supported PEAP version
+ * 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt
+ * 1 = draft-josefsson-ppext-eap-tls-eap-05.txt
+ * 2 = draft-josefsson-ppext-eap-tls-eap-10.txt
+ */
+#define EAP_PEAP_VERSION 1
+
+
+static void eap_peap_deinit(struct eap_sm *sm, void *priv);
+
+
+struct eap_peap_data {
+ struct eap_ssl_data ssl;
+
+ int peap_version, force_peap_version, force_new_label;
+
+ const struct eap_method *phase2_method;
+ void *phase2_priv;
+ int phase2_success;
+ int phase2_eap_success;
+ int phase2_eap_started;
+
+ struct eap_method_type phase2_type;
+ struct eap_method_type *phase2_types;
+ size_t num_phase2_types;
+
+ int peap_outer_success; /* 0 = PEAP terminated on Phase 2 inner
+ * EAP-Success
+ * 1 = reply with tunneled EAP-Success to inner
+ * EAP-Success and expect AS to send outer
+ * (unencrypted) EAP-Success after this
+ * 2 = reply with PEAP/TLS ACK to inner
+ * EAP-Success and expect AS to send outer
+ * (unencrypted) EAP-Success after this */
+ int resuming; /* starting a resumed session */
+ u8 *key_data;
+
+ struct wpabuf *pending_phase2_req;
+};
+
+
+static int eap_peap_parse_phase1(struct eap_peap_data *data,
+ const char *phase1)
+{
+ const char *pos;
+
+ pos = os_strstr(phase1, "peapver=");
+ if (pos) {
+ data->force_peap_version = atoi(pos + 8);
+ data->peap_version = data->force_peap_version;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Forced PEAP version %d",
+ data->force_peap_version);
+ }
+
+ if (os_strstr(phase1, "peaplabel=1")) {
+ data->force_new_label = 1;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Force new label for key "
+ "derivation");
+ }
+
+ if (os_strstr(phase1, "peap_outer_success=0")) {
+ data->peap_outer_success = 0;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: terminate authentication on "
+ "tunneled EAP-Success");
+ } else if (os_strstr(phase1, "peap_outer_success=1")) {
+ data->peap_outer_success = 1;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: send tunneled EAP-Success "
+ "after receiving tunneled EAP-Success");
+ } else if (os_strstr(phase1, "peap_outer_success=2")) {
+ data->peap_outer_success = 2;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: send PEAP/TLS ACK after "
+ "receiving tunneled EAP-Success");
+ }
+
+ return 0;
+}
+
+
+static void * eap_peap_init(struct eap_sm *sm)
+{
+ struct eap_peap_data *data;
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ sm->peap_done = FALSE;
+ data->peap_version = EAP_PEAP_VERSION;
+ data->force_peap_version = -1;
+ data->peap_outer_success = 2;
+
+ if (config && config->phase1 &&
+ eap_peap_parse_phase1(data, config->phase1) < 0) {
+ eap_peap_deinit(sm, data);
+ return NULL;
+ }
+
+ if (eap_peer_select_phase2_methods(config, "auth=",
+ &data->phase2_types,
+ &data->num_phase2_types) < 0) {
+ eap_peap_deinit(sm, data);
+ return NULL;
+ }
+
+ data->phase2_type.vendor = EAP_VENDOR_IETF;
+ data->phase2_type.method = EAP_TYPE_NONE;
+
+ if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL.");
+ eap_peap_deinit(sm, data);
+ return NULL;
+ }
+
+ return data;
+}
+
+
+static void eap_peap_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_peap_data *data = priv;
+ if (data == NULL)
+ return;
+ if (data->phase2_priv && data->phase2_method)
+ data->phase2_method->deinit(sm, data->phase2_priv);
+ os_free(data->phase2_types);
+ eap_peer_tls_ssl_deinit(sm, &data->ssl);
+ os_free(data->key_data);
+ wpabuf_free(data->pending_phase2_req);
+ os_free(data);
+}
+
+
+static struct wpabuf * eap_peapv2_tlv_eap_payload(struct wpabuf *buf)
+{
+ struct wpabuf *e;
+ struct eap_tlv_hdr *tlv;
+
+ if (buf == NULL)
+ return NULL;
+
+ /* Encapsulate EAP packet in EAP-Payload TLV */
+ wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Add EAP-Payload TLV");
+ e = wpabuf_alloc(sizeof(*tlv) + wpabuf_len(buf));
+ if (e == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Failed to allocate memory "
+ "for TLV encapsulation");
+ wpabuf_free(buf);
+ return NULL;
+ }
+ tlv = wpabuf_put(e, sizeof(*tlv));
+ tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+ EAP_TLV_EAP_PAYLOAD_TLV);
+ tlv->length = host_to_be16(wpabuf_len(buf));
+ wpabuf_put_buf(e, buf);
+ wpabuf_free(buf);
+ return e;
+}
+
+
+static int eap_peap_phase2_request(struct eap_sm *sm,
+ struct eap_peap_data *data,
+ struct eap_method_ret *ret,
+ struct wpabuf *req,
+ struct wpabuf **resp)
+{
+ struct eap_hdr *hdr = wpabuf_mhead(req);
+ size_t len = be_to_host16(hdr->length);
+ u8 *pos;
+ struct eap_method_ret iret;
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ if (len <= sizeof(struct eap_hdr)) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: too short "
+ "Phase 2 request (len=%lu)", (unsigned long) len);
+ return -1;
+ }
+ pos = (u8 *) (hdr + 1);
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Request: type=%d", *pos);
+ switch (*pos) {
+ case EAP_TYPE_IDENTITY:
+ *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1);
+ break;
+ case EAP_TYPE_TLV:
+ os_memset(&iret, 0, sizeof(iret));
+ if (eap_tlv_process(sm, &iret, req, resp,
+ data->phase2_eap_started &&
+ !data->phase2_eap_success)) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return -1;
+ }
+ if (iret.methodState == METHOD_DONE ||
+ iret.methodState == METHOD_MAY_CONT) {
+ ret->methodState = iret.methodState;
+ ret->decision = iret.decision;
+ data->phase2_success = 1;
+ }
+ break;
+ default:
+ if (data->phase2_type.vendor == EAP_VENDOR_IETF &&
+ data->phase2_type.method == EAP_TYPE_NONE) {
+ size_t i;
+ for (i = 0; i < data->num_phase2_types; i++) {
+ if (data->phase2_types[i].vendor !=
+ EAP_VENDOR_IETF ||
+ data->phase2_types[i].method != *pos)
+ continue;
+
+ data->phase2_type.vendor =
+ data->phase2_types[i].vendor;
+ data->phase2_type.method =
+ data->phase2_types[i].method;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Selected "
+ "Phase 2 EAP vendor %d method %d",
+ data->phase2_type.vendor,
+ data->phase2_type.method);
+ break;
+ }
+ }
+ if (*pos != data->phase2_type.method ||
+ *pos == EAP_TYPE_NONE) {
+ if (eap_peer_tls_phase2_nak(data->phase2_types,
+ data->num_phase2_types,
+ hdr, resp))
+ return -1;
+ return 0;
+ }
+
+ if (data->phase2_priv == NULL) {
+ data->phase2_method = eap_peer_get_eap_method(
+ data->phase2_type.vendor,
+ data->phase2_type.method);
+ if (data->phase2_method) {
+ sm->init_phase2 = 1;
+ data->phase2_priv =
+ data->phase2_method->init(sm);
+ sm->init_phase2 = 0;
+ }
+ }
+ if (data->phase2_priv == NULL || data->phase2_method == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: failed to initialize "
+ "Phase 2 EAP method %d", *pos);
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return -1;
+ }
+ data->phase2_eap_started = 1;
+ os_memset(&iret, 0, sizeof(iret));
+ *resp = data->phase2_method->process(sm, data->phase2_priv,
+ &iret, req);
+ if ((iret.methodState == METHOD_DONE ||
+ iret.methodState == METHOD_MAY_CONT) &&
+ (iret.decision == DECISION_UNCOND_SUCC ||
+ iret.decision == DECISION_COND_SUCC)) {
+ data->phase2_eap_success = 1;
+ data->phase2_success = 1;
+ }
+ break;
+ }
+
+ if (*resp == NULL &&
+ (config->pending_req_identity || config->pending_req_password ||
+ config->pending_req_otp || config->pending_req_new_password)) {
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = wpabuf_alloc_copy(hdr, len);
+ }
+
+ return 0;
+}
+
+
+static int eap_peap_decrypt(struct eap_sm *sm, struct eap_peap_data *data,
+ struct eap_method_ret *ret,
+ const struct eap_hdr *req,
+ const struct wpabuf *in_data,
+ struct wpabuf **out_data)
+{
+ struct wpabuf *in_decrypted = NULL;
+ int res, skip_change = 0;
+ struct eap_hdr *hdr, *rhdr;
+ struct wpabuf *resp = NULL;
+ size_t len;
+
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for"
+ " Phase 2", (unsigned long) wpabuf_len(in_data));
+
+ if (data->pending_phase2_req) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 request - "
+ "skip decryption and use old data");
+ /* Clear TLS reassembly state. */
+ eap_peer_tls_reset_input(&data->ssl);
+ in_decrypted = data->pending_phase2_req;
+ data->pending_phase2_req = NULL;
+ skip_change = 1;
+ goto continue_req;
+ }
+
+ if (wpabuf_len(in_data) == 0 && sm->workaround &&
+ data->phase2_success) {
+ /*
+ * Cisco ACS seems to be using TLS ACK to terminate
+ * EAP-PEAPv0/GTC. Try to reply with TLS ACK.
+ */
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Received TLS ACK, but "
+ "expected data - acknowledge with TLS ACK since "
+ "Phase 2 has been completed");
+ ret->decision = DECISION_COND_SUCC;
+ ret->methodState = METHOD_DONE;
+ return 1;
+ } else if (wpabuf_len(in_data) == 0) {
+ /* Received TLS ACK - requesting more fragments */
+ return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP,
+ data->peap_version,
+ req->identifier, NULL, out_data);
+ }
+
+ res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted);
+ if (res)
+ return res;
+
+continue_req:
+ wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP",
+ in_decrypted);
+
+ hdr = wpabuf_mhead(in_decrypted);
+ if (wpabuf_len(in_decrypted) == 5 && hdr->code == EAP_CODE_REQUEST &&
+ be_to_host16(hdr->length) == 5 &&
+ eap_get_type(in_decrypted) == EAP_TYPE_IDENTITY) {
+ /* At least FreeRADIUS seems to send full EAP header with
+ * EAP Request Identity */
+ skip_change = 1;
+ }
+ if (wpabuf_len(in_decrypted) >= 5 && hdr->code == EAP_CODE_REQUEST &&
+ eap_get_type(in_decrypted) == EAP_TYPE_TLV) {
+ skip_change = 1;
+ }
+
+ if (data->peap_version == 0 && !skip_change) {
+ struct eap_hdr *nhdr;
+ struct wpabuf *nmsg = wpabuf_alloc(sizeof(struct eap_hdr) +
+ wpabuf_len(in_decrypted));
+ if (nmsg == NULL) {
+ wpabuf_free(in_decrypted);
+ return 0;
+ }
+ nhdr = wpabuf_put(nmsg, sizeof(*nhdr));
+ wpabuf_put_buf(nmsg, in_decrypted);
+ nhdr->code = req->code;
+ nhdr->identifier = req->identifier;
+ nhdr->length = host_to_be16(sizeof(struct eap_hdr) +
+ wpabuf_len(in_decrypted));
+
+ wpabuf_free(in_decrypted);
+ in_decrypted = nmsg;
+ }
+
+ if (data->peap_version >= 2) {
+ struct eap_tlv_hdr *tlv;
+ struct wpabuf *nmsg;
+
+ if (wpabuf_len(in_decrypted) < sizeof(*tlv) + sizeof(*hdr)) {
+ wpa_printf(MSG_INFO, "EAP-PEAPv2: Too short Phase 2 "
+ "EAP TLV");
+ wpabuf_free(in_decrypted);
+ return 0;
+ }
+ tlv = wpabuf_mhead(in_decrypted);
+ if ((be_to_host16(tlv->tlv_type) & 0x3fff) !=
+ EAP_TLV_EAP_PAYLOAD_TLV) {
+ wpa_printf(MSG_INFO, "EAP-PEAPv2: Not an EAP TLV");
+ wpabuf_free(in_decrypted);
+ return 0;
+ }
+ if (sizeof(*tlv) + be_to_host16(tlv->length) >
+ wpabuf_len(in_decrypted)) {
+ wpa_printf(MSG_INFO, "EAP-PEAPv2: Invalid EAP TLV "
+ "length");
+ wpabuf_free(in_decrypted);
+ return 0;
+ }
+ hdr = (struct eap_hdr *) (tlv + 1);
+ if (be_to_host16(hdr->length) > be_to_host16(tlv->length)) {
+ wpa_printf(MSG_INFO, "EAP-PEAPv2: No room for full "
+ "EAP packet in EAP TLV");
+ wpabuf_free(in_decrypted);
+ return 0;
+ }
+
+ nmsg = wpabuf_alloc(be_to_host16(hdr->length));
+ if (nmsg == NULL) {
+ wpabuf_free(in_decrypted);
+ return 0;
+ }
+
+ wpabuf_put_data(nmsg, hdr, be_to_host16(hdr->length));
+ wpabuf_free(in_decrypted);
+ in_decrypted = nmsg;
+ }
+
+ hdr = wpabuf_mhead(in_decrypted);
+ if (wpabuf_len(in_decrypted) < sizeof(*hdr)) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 "
+ "EAP frame (len=%lu)",
+ (unsigned long) wpabuf_len(in_decrypted));
+ wpabuf_free(in_decrypted);
+ return 0;
+ }
+ len = be_to_host16(hdr->length);
+ if (len > wpabuf_len(in_decrypted)) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in "
+ "Phase 2 EAP frame (len=%lu hdr->length=%lu)",
+ (unsigned long) wpabuf_len(in_decrypted),
+ (unsigned long) len);
+ wpabuf_free(in_decrypted);
+ return 0;
+ }
+ if (len < wpabuf_len(in_decrypted)) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: Odd.. Phase 2 EAP header has "
+ "shorter length than full decrypted data "
+ "(%lu < %lu)",
+ (unsigned long) len,
+ (unsigned long) wpabuf_len(in_decrypted));
+ }
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d "
+ "identifier=%d length=%lu", hdr->code, hdr->identifier,
+ (unsigned long) len);
+ switch (hdr->code) {
+ case EAP_CODE_REQUEST:
+ if (eap_peap_phase2_request(sm, data, ret, in_decrypted,
+ &resp)) {
+ wpabuf_free(in_decrypted);
+ wpa_printf(MSG_INFO, "EAP-PEAP: Phase2 Request "
+ "processing failed");
+ return 0;
+ }
+ break;
+ case EAP_CODE_SUCCESS:
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success");
+ if (data->peap_version == 1) {
+ /* EAP-Success within TLS tunnel is used to indicate
+ * shutdown of the TLS channel. The authentication has
+ * been completed. */
+ if (data->phase2_eap_started &&
+ !data->phase2_eap_success) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 "
+ "Success used to indicate success, "
+ "but Phase 2 EAP was not yet "
+ "completed successfully");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ wpabuf_free(in_decrypted);
+ return 0;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Version 1 - "
+ "EAP-Success within TLS tunnel - "
+ "authentication completed");
+ ret->decision = DECISION_UNCOND_SUCC;
+ ret->methodState = METHOD_DONE;
+ data->phase2_success = 1;
+ if (data->peap_outer_success == 2) {
+ wpabuf_free(in_decrypted);
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Use TLS ACK "
+ "to finish authentication");
+ return 1;
+ } else if (data->peap_outer_success == 1) {
+ /* Reply with EAP-Success within the TLS
+ * channel to complete the authentication. */
+ resp = wpabuf_alloc(sizeof(struct eap_hdr));
+ if (resp) {
+ rhdr = wpabuf_put(resp, sizeof(*rhdr));
+ rhdr->code = EAP_CODE_SUCCESS;
+ rhdr->identifier = hdr->identifier;
+ rhdr->length =
+ host_to_be16(sizeof(*rhdr));
+ }
+ } else {
+ /* No EAP-Success expected for Phase 1 (outer,
+ * unencrypted auth), so force EAP state
+ * machine to SUCCESS state. */
+ sm->peap_done = TRUE;
+ }
+ } else {
+ /* FIX: ? */
+ }
+ break;
+ case EAP_CODE_FAILURE:
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure");
+ ret->decision = DECISION_FAIL;
+ ret->methodState = METHOD_MAY_CONT;
+ ret->allowNotifications = FALSE;
+ /* Reply with EAP-Failure within the TLS channel to complete
+ * failure reporting. */
+ resp = wpabuf_alloc(sizeof(struct eap_hdr));
+ if (resp) {
+ rhdr = wpabuf_put(resp, sizeof(*rhdr));
+ rhdr->code = EAP_CODE_FAILURE;
+ rhdr->identifier = hdr->identifier;
+ rhdr->length = host_to_be16(sizeof(*rhdr));
+ }
+ break;
+ default:
+ wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in "
+ "Phase 2 EAP header", hdr->code);
+ break;
+ }
+
+ wpabuf_free(in_decrypted);
+
+ if (resp) {
+ int skip_change2 = 0;
+ struct wpabuf *rmsg, buf;
+
+ wpa_hexdump_buf_key(MSG_DEBUG,
+ "EAP-PEAP: Encrypting Phase 2 data", resp);
+ /* PEAP version changes */
+ if (data->peap_version >= 2) {
+ resp = eap_peapv2_tlv_eap_payload(resp);
+ if (resp == NULL)
+ return -1;
+ }
+ if (wpabuf_len(resp) >= 5 &&
+ wpabuf_head_u8(resp)[0] == EAP_CODE_RESPONSE &&
+ eap_get_type(resp) == EAP_TYPE_TLV)
+ skip_change2 = 1;
+ rmsg = resp;
+ if (data->peap_version == 0 && !skip_change2) {
+ wpabuf_set(&buf, wpabuf_head_u8(resp) +
+ sizeof(struct eap_hdr),
+ wpabuf_len(resp) - sizeof(struct eap_hdr));
+ rmsg = &buf;
+ }
+
+ if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP,
+ data->peap_version, req->identifier,
+ rmsg, out_data)) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt "
+ "a Phase 2 frame");
+ }
+ wpabuf_free(resp);
+ }
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ const struct eap_hdr *req;
+ size_t left;
+ int res;
+ u8 flags, id;
+ struct wpabuf *resp;
+ const u8 *pos;
+ struct eap_peap_data *data = priv;
+
+ pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_PEAP, ret,
+ reqData, &left, &flags);
+ if (pos == NULL)
+ return NULL;
+ req = wpabuf_head(reqData);
+ id = req->identifier;
+
+ if (flags & EAP_TLS_FLAGS_START) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Start (server ver=%d, own "
+ "ver=%d)", flags & EAP_PEAP_VERSION_MASK,
+ data->peap_version);
+ if ((flags & EAP_PEAP_VERSION_MASK) < data->peap_version)
+ data->peap_version = flags & EAP_PEAP_VERSION_MASK;
+ if (data->force_peap_version >= 0 &&
+ data->force_peap_version != data->peap_version) {
+ wpa_printf(MSG_WARNING, "EAP-PEAP: Failed to select "
+ "forced PEAP version %d",
+ data->force_peap_version);
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = FALSE;
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Using PEAP version %d",
+ data->peap_version);
+ left = 0; /* make sure that this frame is empty, even though it
+ * should always be, anyway */
+ }
+
+ resp = NULL;
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+ !data->resuming) {
+ struct wpabuf msg;
+ wpabuf_set(&msg, pos, left);
+ res = eap_peap_decrypt(sm, data, ret, req, &msg, &resp);
+ } else {
+ res = eap_peer_tls_process_helper(sm, &data->ssl,
+ EAP_TYPE_PEAP,
+ data->peap_version, id, pos,
+ left, &resp);
+
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+ char *label;
+ wpa_printf(MSG_DEBUG,
+ "EAP-PEAP: TLS done, proceed to Phase 2");
+ os_free(data->key_data);
+ /* draft-josefsson-ppext-eap-tls-eap-05.txt
+ * specifies that PEAPv1 would use "client PEAP
+ * encryption" as the label. However, most existing
+ * PEAPv1 implementations seem to be using the old
+ * label, "client EAP encryption", instead. Use the old
+ * label by default, but allow it to be configured with
+ * phase1 parameter peaplabel=1. */
+ if (data->peap_version > 1 || data->force_new_label)
+ label = "client PEAP encryption";
+ else
+ label = "client EAP encryption";
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: using label '%s' in "
+ "key derivation", label);
+ data->key_data =
+ eap_peer_tls_derive_key(sm, &data->ssl, label,
+ EAP_TLS_KEY_LEN);
+ if (data->key_data) {
+ wpa_hexdump_key(MSG_DEBUG,
+ "EAP-PEAP: Derived key",
+ data->key_data,
+ EAP_TLS_KEY_LEN);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to "
+ "derive key");
+ }
+
+ if (sm->workaround && data->resuming) {
+ /*
+ * At least few RADIUS servers (Aegis v1.1.6;
+ * but not v1.1.4; and Cisco ACS) seem to be
+ * terminating PEAPv1 (Aegis) or PEAPv0 (Cisco
+ * ACS) session resumption with outer
+ * EAP-Success. This does not seem to follow
+ * draft-josefsson-pppext-eap-tls-eap-05.txt
+ * section 4.2, so only allow this if EAP
+ * workarounds are enabled.
+ */
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Workaround - "
+ "allow outer EAP-Success to "
+ "terminate PEAP resumption");
+ ret->decision = DECISION_COND_SUCC;
+ data->phase2_success = 1;
+ }
+
+ data->resuming = 0;
+ }
+
+ if (res == 2) {
+ struct wpabuf msg;
+ /*
+ * Application data included in the handshake message.
+ */
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = resp;
+ resp = NULL;
+ wpabuf_set(&msg, pos, left);
+ res = eap_peap_decrypt(sm, data, ret, req, &msg,
+ &resp);
+ }
+ }
+
+ if (ret->methodState == METHOD_DONE) {
+ ret->allowNotifications = FALSE;
+ }
+
+ if (res == 1) {
+ wpabuf_free(resp);
+ return eap_peer_tls_build_ack(id, EAP_TYPE_PEAP,
+ data->peap_version);
+ }
+
+ return resp;
+}
+
+
+static Boolean eap_peap_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+ struct eap_peap_data *data = priv;
+ return tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+ data->phase2_success;
+}
+
+
+static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_peap_data *data = priv;
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = NULL;
+}
+
+
+static void * eap_peap_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_peap_data *data = priv;
+ os_free(data->key_data);
+ data->key_data = NULL;
+ if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
+ os_free(data);
+ return NULL;
+ }
+ if (data->phase2_priv && data->phase2_method &&
+ data->phase2_method->init_for_reauth)
+ data->phase2_method->init_for_reauth(sm, data->phase2_priv);
+ data->phase2_success = 0;
+ data->phase2_eap_success = 0;
+ data->phase2_eap_started = 0;
+ data->resuming = 1;
+ sm->peap_done = FALSE;
+ return priv;
+}
+
+
+static int eap_peap_get_status(struct eap_sm *sm, void *priv, char *buf,
+ size_t buflen, int verbose)
+{
+ struct eap_peap_data *data = priv;
+ int len, ret;
+
+ len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);
+ if (data->phase2_method) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "EAP-PEAPv%d Phase2 method=%s\n",
+ data->peap_version,
+ data->phase2_method->name);
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ len += ret;
+ }
+ return len;
+}
+
+
+static Boolean eap_peap_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_peap_data *data = priv;
+ return data->key_data != NULL && data->phase2_success;
+}
+
+
+static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_peap_data *data = priv;
+ u8 *key;
+
+ if (data->key_data == NULL || !data->phase2_success)
+ return NULL;
+
+ key = os_malloc(EAP_TLS_KEY_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_TLS_KEY_LEN;
+ os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
+
+ return key;
+}
+
+
+int eap_peer_peap_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_PEAP, "PEAP");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_peap_init;
+ eap->deinit = eap_peap_deinit;
+ eap->process = eap_peap_process;
+ eap->isKeyAvailable = eap_peap_isKeyAvailable;
+ eap->getKey = eap_peap_getKey;
+ eap->get_status = eap_peap_get_status;
+ eap->has_reauth_data = eap_peap_has_reauth_data;
+ eap->deinit_for_reauth = eap_peap_deinit_for_reauth;
+ eap->init_for_reauth = eap_peap_init_for_reauth;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_psk.c b/src/eap_peer/eap_psk.c
new file mode 100644
index 0000000..1ce6356
--- /dev/null
+++ b/src/eap_peer/eap_psk.c
@@ -0,0 +1,482 @@
+/*
+ * EAP peer method: EAP-PSK (RFC 4764)
+ * Copyright (c) 2004-2008, 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.
+ *
+ * Note: EAP-PSK is an EAP authentication method and as such, completely
+ * different from WPA-PSK. This file is not needed for WPA-PSK functionality.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_peer/eap_i.h"
+#include "aes_wrap.h"
+#include "eap_common/eap_psk_common.h"
+
+
+struct eap_psk_data {
+ enum { PSK_INIT, PSK_MAC_SENT, PSK_DONE } state;
+ u8 rand_p[EAP_PSK_RAND_LEN];
+ u8 ak[EAP_PSK_AK_LEN], kdk[EAP_PSK_KDK_LEN], tek[EAP_PSK_TEK_LEN];
+ u8 *id_s, *id_p;
+ size_t id_s_len, id_p_len;
+ u8 msk[EAP_MSK_LEN];
+ u8 emsk[EAP_EMSK_LEN];
+};
+
+
+static void * eap_psk_init(struct eap_sm *sm)
+{
+ struct eap_psk_data *data;
+ const u8 *identity, *password;
+ size_t identity_len, password_len;
+
+ password = eap_get_config_password(sm, &password_len);
+ if (!password || password_len != 16) {
+ wpa_printf(MSG_INFO, "EAP-PSK: 16-octet pre-shared key not "
+ "configured");
+ return NULL;
+ }
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ if (eap_psk_key_setup(password, data->ak, data->kdk)) {
+ os_free(data);
+ return NULL;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: AK", data->ak, EAP_PSK_AK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: KDK", data->kdk, EAP_PSK_KDK_LEN);
+ data->state = PSK_INIT;
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ if (identity) {
+ data->id_p = os_malloc(identity_len);
+ if (data->id_p)
+ os_memcpy(data->id_p, identity, identity_len);
+ data->id_p_len = identity_len;
+ }
+ if (data->id_p == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PSK: could not get own identity");
+ os_free(data);
+ return NULL;
+ }
+
+ return data;
+}
+
+
+static void eap_psk_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_psk_data *data = priv;
+ os_free(data->id_s);
+ os_free(data->id_p);
+ os_free(data);
+}
+
+
+static struct wpabuf * eap_psk_process_1(struct eap_psk_data *data,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ const struct eap_psk_hdr_1 *hdr1;
+ struct eap_psk_hdr_2 *hdr2;
+ struct wpabuf *resp;
+ u8 *buf, *pos;
+ size_t buflen, len;
+ const u8 *cpos;
+
+ wpa_printf(MSG_DEBUG, "EAP-PSK: in INIT state");
+
+ cpos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, reqData, &len);
+ hdr1 = (const struct eap_psk_hdr_1 *) cpos;
+ if (cpos == NULL || len < sizeof(*hdr1)) {
+ wpa_printf(MSG_INFO, "EAP-PSK: Invalid first message "
+ "length (%lu; expected %lu or more)",
+ (unsigned long) len,
+ (unsigned long) sizeof(*hdr1));
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr1->flags);
+ if (EAP_PSK_FLAGS_GET_T(hdr1->flags) != 0) {
+ wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 0)",
+ EAP_PSK_FLAGS_GET_T(hdr1->flags));
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr1->rand_s,
+ EAP_PSK_RAND_LEN);
+ os_free(data->id_s);
+ data->id_s_len = len - sizeof(*hdr1);
+ data->id_s = os_malloc(data->id_s_len);
+ if (data->id_s == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory for "
+ "ID_S (len=%lu)", (unsigned long) data->id_s_len);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ os_memcpy(data->id_s, (u8 *) (hdr1 + 1), data->id_s_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_S",
+ data->id_s, data->id_s_len);
+
+ if (os_get_random(data->rand_p, EAP_PSK_RAND_LEN)) {
+ wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK,
+ sizeof(*hdr2) + data->id_p_len, EAP_CODE_RESPONSE,
+ eap_get_id(reqData));
+ if (resp == NULL)
+ return NULL;
+ hdr2 = wpabuf_put(resp, sizeof(*hdr2));
+ hdr2->flags = EAP_PSK_FLAGS_SET_T(1); /* T=1 */
+ os_memcpy(hdr2->rand_s, hdr1->rand_s, EAP_PSK_RAND_LEN);
+ os_memcpy(hdr2->rand_p, data->rand_p, EAP_PSK_RAND_LEN);
+ wpabuf_put_data(resp, data->id_p, data->id_p_len);
+ /* MAC_P = OMAC1-AES-128(AK, ID_P||ID_S||RAND_S||RAND_P) */
+ buflen = data->id_p_len + data->id_s_len + 2 * EAP_PSK_RAND_LEN;
+ buf = os_malloc(buflen);
+ if (buf == NULL) {
+ wpabuf_free(resp);
+ return NULL;
+ }
+ os_memcpy(buf, data->id_p, data->id_p_len);
+ pos = buf + data->id_p_len;
+ os_memcpy(pos, data->id_s, data->id_s_len);
+ pos += data->id_s_len;
+ os_memcpy(pos, hdr1->rand_s, EAP_PSK_RAND_LEN);
+ pos += EAP_PSK_RAND_LEN;
+ os_memcpy(pos, data->rand_p, EAP_PSK_RAND_LEN);
+ if (omac1_aes_128(data->ak, buf, buflen, hdr2->mac_p)) {
+ os_free(buf);
+ wpabuf_free(resp);
+ return NULL;
+ }
+ os_free(buf);
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_P", hdr2->rand_p,
+ EAP_PSK_RAND_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_P", hdr2->mac_p, EAP_PSK_MAC_LEN);
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_P",
+ data->id_p, data->id_p_len);
+
+ data->state = PSK_MAC_SENT;
+
+ return resp;
+}
+
+
+static struct wpabuf * eap_psk_process_3(struct eap_psk_data *data,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ const struct eap_psk_hdr_3 *hdr3;
+ struct eap_psk_hdr_4 *hdr4;
+ struct wpabuf *resp;
+ u8 *buf, *rpchannel, nonce[16], *decrypted;
+ const u8 *pchannel, *tag, *msg;
+ u8 mac[EAP_PSK_MAC_LEN];
+ size_t buflen, left, data_len, len, plen;
+ int failed = 0;
+ const u8 *pos;
+
+ wpa_printf(MSG_DEBUG, "EAP-PSK: in MAC_SENT state");
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK,
+ reqData, &len);
+ hdr3 = (const struct eap_psk_hdr_3 *) pos;
+ if (pos == NULL || len < sizeof(*hdr3)) {
+ wpa_printf(MSG_INFO, "EAP-PSK: Invalid third message "
+ "length (%lu; expected %lu or more)",
+ (unsigned long) len,
+ (unsigned long) sizeof(*hdr3));
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ left = len - sizeof(*hdr3);
+ pchannel = (const u8 *) (hdr3 + 1);
+ wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr3->flags);
+ if (EAP_PSK_FLAGS_GET_T(hdr3->flags) != 2) {
+ wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 2)",
+ EAP_PSK_FLAGS_GET_T(hdr3->flags));
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr3->rand_s,
+ EAP_PSK_RAND_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_S", hdr3->mac_s, EAP_PSK_MAC_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL", pchannel, left);
+
+ if (left < 4 + 16 + 1) {
+ wpa_printf(MSG_INFO, "EAP-PSK: Too short PCHANNEL data in "
+ "third message (len=%lu, expected 21)",
+ (unsigned long) left);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ /* MAC_S = OMAC1-AES-128(AK, ID_S||RAND_P) */
+ buflen = data->id_s_len + EAP_PSK_RAND_LEN;
+ buf = os_malloc(buflen);
+ if (buf == NULL)
+ return NULL;
+ os_memcpy(buf, data->id_s, data->id_s_len);
+ os_memcpy(buf + data->id_s_len, data->rand_p, EAP_PSK_RAND_LEN);
+ if (omac1_aes_128(data->ak, buf, buflen, mac)) {
+ os_free(buf);
+ return NULL;
+ }
+ os_free(buf);
+ if (os_memcmp(mac, hdr3->mac_s, EAP_PSK_MAC_LEN) != 0) {
+ wpa_printf(MSG_WARNING, "EAP-PSK: Invalid MAC_S in third "
+ "message");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-PSK: MAC_S verified successfully");
+
+ if (eap_psk_derive_keys(data->kdk, data->rand_p, data->tek,
+ data->msk, data->emsk)) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return NULL;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: TEK", data->tek, EAP_PSK_TEK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: MSK", data->msk, EAP_MSK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: EMSK", data->emsk, EAP_EMSK_LEN);
+
+ os_memset(nonce, 0, 12);
+ os_memcpy(nonce + 12, pchannel, 4);
+ pchannel += 4;
+ left -= 4;
+
+ tag = pchannel;
+ pchannel += 16;
+ left -= 16;
+
+ msg = pchannel;
+
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - nonce",
+ nonce, sizeof(nonce));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - hdr",
+ wpabuf_head(reqData), 5);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - cipher msg", msg, left);
+
+ decrypted = os_malloc(left);
+ if (decrypted == NULL) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return NULL;
+ }
+ os_memcpy(decrypted, msg, left);
+
+ if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce),
+ wpabuf_head(reqData),
+ sizeof(struct eap_hdr) + 1 +
+ sizeof(*hdr3) - EAP_PSK_MAC_LEN, decrypted,
+ left, tag)) {
+ wpa_printf(MSG_WARNING, "EAP-PSK: PCHANNEL decryption failed");
+ os_free(decrypted);
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: Decrypted PCHANNEL message",
+ decrypted, left);
+
+ /* Verify R flag */
+ switch (decrypted[0] >> 6) {
+ case EAP_PSK_R_FLAG_CONT:
+ wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - CONT - unsupported");
+ failed = 1;
+ break;
+ case EAP_PSK_R_FLAG_DONE_SUCCESS:
+ wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_SUCCESS");
+ break;
+ case EAP_PSK_R_FLAG_DONE_FAILURE:
+ wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_FAILURE");
+ wpa_printf(MSG_INFO, "EAP-PSK: Authentication server rejected "
+ "authentication");
+ failed = 1;
+ break;
+ }
+
+ data_len = 1;
+ if ((decrypted[0] & EAP_PSK_E_FLAG) && left > 1)
+ data_len++;
+ plen = sizeof(*hdr4) + 4 + 16 + data_len;
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK, plen,
+ EAP_CODE_RESPONSE, eap_get_id(reqData));
+ if (resp == NULL) {
+ os_free(decrypted);
+ return NULL;
+ }
+ hdr4 = wpabuf_put(resp, sizeof(*hdr4));
+ hdr4->flags = EAP_PSK_FLAGS_SET_T(3); /* T=3 */
+ os_memcpy(hdr4->rand_s, hdr3->rand_s, EAP_PSK_RAND_LEN);
+ rpchannel = wpabuf_put(resp, 4 + 16 + data_len);
+
+ /* nonce++ */
+ inc_byte_array(nonce, sizeof(nonce));
+ os_memcpy(rpchannel, nonce + 12, 4);
+
+ if (decrypted[0] & EAP_PSK_E_FLAG) {
+ wpa_printf(MSG_DEBUG, "EAP-PSK: Unsupported E (Ext) flag");
+ failed = 1;
+ rpchannel[4 + 16] = (EAP_PSK_R_FLAG_DONE_FAILURE << 6) |
+ EAP_PSK_E_FLAG;
+ if (left > 1) {
+ /* Add empty EXT_Payload with same EXT_Type */
+ rpchannel[4 + 16 + 1] = decrypted[1];
+ }
+ } else if (failed)
+ rpchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_FAILURE << 6;
+ else
+ rpchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_SUCCESS << 6;
+
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (plaintext)",
+ rpchannel + 4 + 16, data_len);
+ if (aes_128_eax_encrypt(data->tek, nonce, sizeof(nonce),
+ wpabuf_head(resp),
+ sizeof(struct eap_hdr) + 1 + sizeof(*hdr4),
+ rpchannel + 4 + 16, data_len, rpchannel + 4)) {
+ os_free(decrypted);
+ wpabuf_free(resp);
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (PCHANNEL)",
+ rpchannel, 4 + 16 + data_len);
+
+ wpa_printf(MSG_DEBUG, "EAP-PSK: Completed %ssuccessfully",
+ failed ? "un" : "");
+ data->state = PSK_DONE;
+ ret->methodState = METHOD_DONE;
+ ret->decision = failed ? DECISION_FAIL : DECISION_UNCOND_SUCC;
+
+ os_free(decrypted);
+
+ return resp;
+}
+
+
+static struct wpabuf * eap_psk_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct eap_psk_data *data = priv;
+ const u8 *pos;
+ struct wpabuf *resp = NULL;
+ size_t len;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, reqData, &len);
+ if (pos == NULL) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = TRUE;
+
+ switch (data->state) {
+ case PSK_INIT:
+ resp = eap_psk_process_1(data, ret, reqData);
+ break;
+ case PSK_MAC_SENT:
+ resp = eap_psk_process_3(data, ret, reqData);
+ break;
+ case PSK_DONE:
+ wpa_printf(MSG_DEBUG, "EAP-PSK: in DONE state - ignore "
+ "unexpected message");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (ret->methodState == METHOD_DONE) {
+ ret->allowNotifications = FALSE;
+ }
+
+ return resp;
+}
+
+
+static Boolean eap_psk_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_psk_data *data = priv;
+ return data->state == PSK_DONE;
+}
+
+
+static u8 * eap_psk_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_psk_data *data = priv;
+ u8 *key;
+
+ if (data->state != PSK_DONE)
+ return NULL;
+
+ key = os_malloc(EAP_MSK_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_MSK_LEN;
+ os_memcpy(key, data->msk, EAP_MSK_LEN);
+
+ return key;
+}
+
+
+static u8 * eap_psk_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_psk_data *data = priv;
+ u8 *key;
+
+ if (data->state != PSK_DONE)
+ return NULL;
+
+ key = os_malloc(EAP_EMSK_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_EMSK_LEN;
+ os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+
+ return key;
+}
+
+
+int eap_peer_psk_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_PSK, "PSK");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_psk_init;
+ eap->deinit = eap_psk_deinit;
+ eap->process = eap_psk_process;
+ eap->isKeyAvailable = eap_psk_isKeyAvailable;
+ eap->getKey = eap_psk_getKey;
+ eap->get_emsk = eap_psk_get_emsk;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_sake.c b/src/eap_peer/eap_sake.c
new file mode 100644
index 0000000..bb06bb2
--- /dev/null
+++ b/src/eap_peer/eap_sake.c
@@ -0,0 +1,499 @@
+/*
+ * EAP peer method: EAP-SAKE (RFC 4763)
+ * Copyright (c) 2006-2008, 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_peer/eap_i.h"
+#include "eap_common/eap_sake_common.h"
+
+struct eap_sake_data {
+ enum { IDENTITY, CHALLENGE, CONFIRM, SUCCESS, FAILURE } state;
+ u8 root_secret_a[EAP_SAKE_ROOT_SECRET_LEN];
+ u8 root_secret_b[EAP_SAKE_ROOT_SECRET_LEN];
+ u8 rand_s[EAP_SAKE_RAND_LEN];
+ u8 rand_p[EAP_SAKE_RAND_LEN];
+ struct {
+ u8 auth[EAP_SAKE_TEK_AUTH_LEN];
+ u8 cipher[EAP_SAKE_TEK_CIPHER_LEN];
+ } tek;
+ u8 msk[EAP_MSK_LEN];
+ u8 emsk[EAP_EMSK_LEN];
+ u8 session_id;
+ int session_id_set;
+ u8 *peerid;
+ size_t peerid_len;
+ u8 *serverid;
+ size_t serverid_len;
+};
+
+
+static const char * eap_sake_state_txt(int state)
+{
+ switch (state) {
+ case IDENTITY:
+ return "IDENTITY";
+ case CHALLENGE:
+ return "CHALLENGE";
+ case CONFIRM:
+ return "CONFIRM";
+ case SUCCESS:
+ return "SUCCESS";
+ case FAILURE:
+ return "FAILURE";
+ default:
+ return "?";
+ }
+}
+
+
+static void eap_sake_state(struct eap_sake_data *data, int state)
+{
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: %s -> %s",
+ eap_sake_state_txt(data->state),
+ eap_sake_state_txt(state));
+ data->state = state;
+}
+
+
+static void eap_sake_deinit(struct eap_sm *sm, void *priv);
+
+
+static void * eap_sake_init(struct eap_sm *sm)
+{
+ struct eap_sake_data *data;
+ const u8 *identity, *password;
+ size_t identity_len, password_len;
+
+ password = eap_get_config_password(sm, &password_len);
+ if (!password || password_len != 2 * EAP_SAKE_ROOT_SECRET_LEN) {
+ wpa_printf(MSG_INFO, "EAP-SAKE: No key of correct length "
+ "configured");
+ return NULL;
+ }
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->state = IDENTITY;
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ if (identity) {
+ data->peerid = os_malloc(identity_len);
+ if (data->peerid == NULL) {
+ eap_sake_deinit(sm, data);
+ return NULL;
+ }
+ os_memcpy(data->peerid, identity, identity_len);
+ data->peerid_len = identity_len;
+ }
+
+ os_memcpy(data->root_secret_a, password, EAP_SAKE_ROOT_SECRET_LEN);
+ os_memcpy(data->root_secret_b,
+ password + EAP_SAKE_ROOT_SECRET_LEN,
+ EAP_SAKE_ROOT_SECRET_LEN);
+
+ return data;
+}
+
+
+static void eap_sake_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_sake_data *data = priv;
+ os_free(data->serverid);
+ os_free(data->peerid);
+ os_free(data);
+}
+
+
+static struct wpabuf * eap_sake_build_msg(struct eap_sake_data *data,
+ int id, size_t length, u8 subtype)
+{
+ struct eap_sake_hdr *sake;
+ struct wpabuf *msg;
+ size_t plen;
+
+ plen = length + sizeof(struct eap_sake_hdr);
+
+ msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_SAKE, plen,
+ EAP_CODE_RESPONSE, id);
+ if (msg == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to allocate memory "
+ "request");
+ return NULL;
+ }
+
+ sake = wpabuf_put(msg, sizeof(*sake));
+ sake->version = EAP_SAKE_VERSION;
+ sake->session_id = data->session_id;
+ sake->subtype = subtype;
+
+ return msg;
+}
+
+
+static struct wpabuf * eap_sake_process_identity(struct eap_sm *sm,
+ struct eap_sake_data *data,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData,
+ const u8 *payload,
+ size_t payload_len)
+{
+ struct eap_sake_parse_attr attr;
+ struct wpabuf *resp;
+
+ if (data->state != IDENTITY) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Identity");
+
+ if (eap_sake_parse_attributes(payload, payload_len, &attr))
+ return NULL;
+
+ if (!attr.perm_id_req && !attr.any_id_req) {
+ wpa_printf(MSG_INFO, "EAP-SAKE: No AT_PERM_ID_REQ or "
+ "AT_ANY_ID_REQ in Request/Identity");
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Identity");
+
+ resp = eap_sake_build_msg(data, eap_get_id(reqData),
+ 2 + data->peerid_len,
+ EAP_SAKE_SUBTYPE_IDENTITY);
+ if (resp == NULL)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PEERID");
+ eap_sake_add_attr(resp, EAP_SAKE_AT_PEERID,
+ data->peerid, data->peerid_len);
+
+ eap_sake_state(data, CHALLENGE);
+
+ return resp;
+}
+
+
+static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm,
+ struct eap_sake_data *data,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData,
+ const u8 *payload,
+ size_t payload_len)
+{
+ struct eap_sake_parse_attr attr;
+ struct wpabuf *resp;
+ u8 *rpos;
+ size_t rlen;
+
+ if (data->state != IDENTITY && data->state != CHALLENGE) {
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge received "
+ "in unexpected state (%d)", data->state);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ if (data->state == IDENTITY)
+ eap_sake_state(data, CHALLENGE);
+
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Challenge");
+
+ if (eap_sake_parse_attributes(payload, payload_len, &attr))
+ return NULL;
+
+ if (!attr.rand_s) {
+ wpa_printf(MSG_INFO, "EAP-SAKE: Request/Challenge did not "
+ "include AT_RAND_S");
+ return NULL;
+ }
+
+ os_memcpy(data->rand_s, attr.rand_s, EAP_SAKE_RAND_LEN);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)",
+ data->rand_s, EAP_SAKE_RAND_LEN);
+
+ if (os_get_random(data->rand_p, EAP_SAKE_RAND_LEN)) {
+ wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data");
+ return NULL;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_P (peer rand)",
+ data->rand_p, EAP_SAKE_RAND_LEN);
+
+ os_free(data->serverid);
+ data->serverid = NULL;
+ data->serverid_len = 0;
+ if (attr.serverid) {
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-SAKE: SERVERID",
+ attr.serverid, attr.serverid_len);
+ data->serverid = os_malloc(attr.serverid_len);
+ if (data->serverid == NULL)
+ return NULL;
+ os_memcpy(data->serverid, attr.serverid, attr.serverid_len);
+ data->serverid_len = attr.serverid_len;
+ }
+
+ eap_sake_derive_keys(data->root_secret_a, data->root_secret_b,
+ data->rand_s, data->rand_p,
+ (u8 *) &data->tek, data->msk, data->emsk);
+
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Challenge");
+
+ rlen = 2 + EAP_SAKE_RAND_LEN + 2 + EAP_SAKE_MIC_LEN;
+ if (data->peerid)
+ rlen += 2 + data->peerid_len;
+ resp = eap_sake_build_msg(data, eap_get_id(reqData), rlen,
+ EAP_SAKE_SUBTYPE_CHALLENGE);
+ if (resp == NULL)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_RAND_P");
+ eap_sake_add_attr(resp, EAP_SAKE_AT_RAND_P,
+ data->rand_p, EAP_SAKE_RAND_LEN);
+
+ if (data->peerid) {
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PEERID");
+ eap_sake_add_attr(resp, EAP_SAKE_AT_PEERID,
+ data->peerid, data->peerid_len);
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_P");
+ wpabuf_put_u8(resp, EAP_SAKE_AT_MIC_P);
+ wpabuf_put_u8(resp, 2 + EAP_SAKE_MIC_LEN);
+ rpos = wpabuf_put(resp, EAP_SAKE_MIC_LEN);
+ if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
+ data->serverid, data->serverid_len,
+ data->peerid, data->peerid_len, 1,
+ wpabuf_head(resp), wpabuf_len(resp), rpos,
+ rpos)) {
+ wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
+ wpabuf_free(resp);
+ return NULL;
+ }
+
+ eap_sake_state(data, CONFIRM);
+
+ return resp;
+}
+
+
+static struct wpabuf * eap_sake_process_confirm(struct eap_sm *sm,
+ struct eap_sake_data *data,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData,
+ const u8 *payload,
+ size_t payload_len)
+{
+ struct eap_sake_parse_attr attr;
+ u8 mic_s[EAP_SAKE_MIC_LEN];
+ struct wpabuf *resp;
+ u8 *rpos;
+
+ if (data->state != CONFIRM) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Confirm");
+
+ if (eap_sake_parse_attributes(payload, payload_len, &attr))
+ return NULL;
+
+ if (!attr.mic_s) {
+ wpa_printf(MSG_INFO, "EAP-SAKE: Request/Confirm did not "
+ "include AT_MIC_S");
+ return NULL;
+ }
+
+ eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
+ data->serverid, data->serverid_len,
+ data->peerid, data->peerid_len, 0,
+ wpabuf_head(reqData), wpabuf_len(reqData),
+ attr.mic_s, mic_s);
+ if (os_memcmp(attr.mic_s, mic_s, EAP_SAKE_MIC_LEN) != 0) {
+ wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_S");
+ eap_sake_state(data, FAILURE);
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = FALSE;
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending "
+ "Response/Auth-Reject");
+ return eap_sake_build_msg(data, eap_get_id(reqData), 0,
+ EAP_SAKE_SUBTYPE_AUTH_REJECT);
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Confirm");
+
+ resp = eap_sake_build_msg(data, eap_get_id(reqData),
+ 2 + EAP_SAKE_MIC_LEN,
+ EAP_SAKE_SUBTYPE_CONFIRM);
+ if (resp == NULL)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_P");
+ wpabuf_put_u8(resp, EAP_SAKE_AT_MIC_P);
+ wpabuf_put_u8(resp, 2 + EAP_SAKE_MIC_LEN);
+ rpos = wpabuf_put(resp, EAP_SAKE_MIC_LEN);
+ if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
+ data->serverid, data->serverid_len,
+ data->peerid, data->peerid_len, 1,
+ wpabuf_head(resp), wpabuf_len(resp), rpos,
+ rpos)) {
+ wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
+ wpabuf_free(resp);
+ return NULL;
+ }
+
+ eap_sake_state(data, SUCCESS);
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ ret->allowNotifications = FALSE;
+
+ return resp;
+}
+
+
+static struct wpabuf * eap_sake_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct eap_sake_data *data = priv;
+ const struct eap_sake_hdr *req;
+ struct wpabuf *resp;
+ const u8 *pos, *end;
+ size_t len;
+ u8 subtype, session_id;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, reqData, &len);
+ if (pos == NULL || len < sizeof(struct eap_sake_hdr)) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ req = (const struct eap_sake_hdr *) pos;
+ end = pos + len;
+ subtype = req->subtype;
+ session_id = req->session_id;
+ pos = (const u8 *) (req + 1);
+
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Received frame: subtype %d "
+ "session_id %d", subtype, session_id);
+ wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Received attributes",
+ pos, end - pos);
+
+ if (data->session_id_set && data->session_id != session_id) {
+ wpa_printf(MSG_INFO, "EAP-SAKE: Session ID mismatch (%d,%d)",
+ session_id, data->session_id);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ data->session_id = session_id;
+ data->session_id_set = 1;
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = TRUE;
+
+ switch (subtype) {
+ case EAP_SAKE_SUBTYPE_IDENTITY:
+ resp = eap_sake_process_identity(sm, data, ret, reqData,
+ pos, end - pos);
+ break;
+ case EAP_SAKE_SUBTYPE_CHALLENGE:
+ resp = eap_sake_process_challenge(sm, data, ret, reqData,
+ pos, end - pos);
+ break;
+ case EAP_SAKE_SUBTYPE_CONFIRM:
+ resp = eap_sake_process_confirm(sm, data, ret, reqData,
+ pos, end - pos);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Ignoring message with "
+ "unknown subtype %d", subtype);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (ret->methodState == METHOD_DONE)
+ ret->allowNotifications = FALSE;
+
+ return resp;
+}
+
+
+static Boolean eap_sake_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_sake_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_sake_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(EAP_MSK_LEN);
+ if (key == NULL)
+ return NULL;
+ os_memcpy(key, data->msk, EAP_MSK_LEN);
+ *len = EAP_MSK_LEN;
+
+ return key;
+}
+
+
+static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_sake_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(EAP_EMSK_LEN);
+ if (key == NULL)
+ return NULL;
+ os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+ *len = EAP_EMSK_LEN;
+
+ return key;
+}
+
+
+int eap_peer_sake_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_sake_init;
+ eap->deinit = eap_sake_deinit;
+ eap->process = eap_sake_process;
+ eap->isKeyAvailable = eap_sake_isKeyAvailable;
+ eap->getKey = eap_sake_getKey;
+ eap->get_emsk = eap_sake_get_emsk;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c
new file mode 100644
index 0000000..c89eddd
--- /dev/null
+++ b/src/eap_peer/eap_sim.c
@@ -0,0 +1,1038 @@
+/*
+ * EAP peer method: EAP-SIM (RFC 4186)
+ * Copyright (c) 2004-2008, 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_peer/eap_i.h"
+#include "eap_config.h"
+#include "pcsc_funcs.h"
+#include "eap_common/eap_sim_common.h"
+
+
+struct eap_sim_data {
+ u8 *ver_list;
+ size_t ver_list_len;
+ int selected_version;
+ size_t min_num_chal, num_chal;
+
+ u8 kc[3][EAP_SIM_KC_LEN];
+ u8 sres[3][EAP_SIM_SRES_LEN];
+ u8 nonce_mt[EAP_SIM_NONCE_MT_LEN], nonce_s[EAP_SIM_NONCE_S_LEN];
+ u8 mk[EAP_SIM_MK_LEN];
+ u8 k_aut[EAP_SIM_K_AUT_LEN];
+ u8 k_encr[EAP_SIM_K_ENCR_LEN];
+ u8 msk[EAP_SIM_KEYING_DATA_LEN];
+ u8 emsk[EAP_EMSK_LEN];
+ u8 rand[3][GSM_RAND_LEN];
+
+ int num_id_req, num_notification;
+ u8 *pseudonym;
+ size_t pseudonym_len;
+ u8 *reauth_id;
+ size_t reauth_id_len;
+ int reauth;
+ unsigned int counter, counter_too_small;
+ u8 *last_eap_identity;
+ size_t last_eap_identity_len;
+ enum {
+ CONTINUE, RESULT_SUCCESS, RESULT_FAILURE, SUCCESS, FAILURE
+ } state;
+ int result_ind, use_result_ind;
+};
+
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static const char * eap_sim_state_txt(int state)
+{
+ switch (state) {
+ case CONTINUE:
+ return "CONTINUE";
+ case RESULT_SUCCESS:
+ return "RESULT_SUCCESS";
+ case RESULT_FAILURE:
+ return "RESULT_FAILURE";
+ case SUCCESS:
+ return "SUCCESS";
+ case FAILURE:
+ return "FAILURE";
+ default:
+ return "?";
+ }
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+static void eap_sim_state(struct eap_sim_data *data, int state)
+{
+ wpa_printf(MSG_DEBUG, "EAP-SIM: %s -> %s",
+ eap_sim_state_txt(data->state),
+ eap_sim_state_txt(state));
+ data->state = state;
+}
+
+
+static void * eap_sim_init(struct eap_sm *sm)
+{
+ struct eap_sim_data *data;
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+
+ if (os_get_random(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data "
+ "for NONCE_MT");
+ os_free(data);
+ return NULL;
+ }
+
+ data->min_num_chal = 2;
+ if (config && config->phase1) {
+ char *pos = os_strstr(config->phase1, "sim_min_num_chal=");
+ if (pos) {
+ data->min_num_chal = atoi(pos + 17);
+ if (data->min_num_chal < 2 || data->min_num_chal > 3) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Invalid "
+ "sim_min_num_chal configuration "
+ "(%lu, expected 2 or 3)",
+ (unsigned long) data->min_num_chal);
+ os_free(data);
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Set minimum number of "
+ "challenges to %lu",
+ (unsigned long) data->min_num_chal);
+ }
+
+ data->result_ind = os_strstr(config->phase1, "result_ind=1") !=
+ NULL;
+ }
+
+ eap_sim_state(data, CONTINUE);
+
+ return data;
+}
+
+
+static void eap_sim_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_sim_data *data = priv;
+ if (data) {
+ os_free(data->ver_list);
+ os_free(data->pseudonym);
+ os_free(data->reauth_id);
+ os_free(data->last_eap_identity);
+ os_free(data);
+ }
+}
+
+
+static int eap_sim_gsm_auth(struct eap_sm *sm, struct eap_sim_data *data)
+{
+ wpa_printf(MSG_DEBUG, "EAP-SIM: GSM authentication algorithm");
+#ifdef PCSC_FUNCS
+ if (scard_gsm_auth(sm->scard_ctx, data->rand[0],
+ data->sres[0], data->kc[0]) ||
+ scard_gsm_auth(sm->scard_ctx, data->rand[1],
+ data->sres[1], data->kc[1]) ||
+ (data->num_chal > 2 &&
+ scard_gsm_auth(sm->scard_ctx, data->rand[2],
+ data->sres[2], data->kc[2]))) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: GSM SIM authentication could "
+ "not be completed");
+ return -1;
+ }
+#else /* PCSC_FUNCS */
+ /* These hardcoded Kc and SRES values are used for testing. RAND to
+ * KC/SREC mapping is very bogus as far as real authentication is
+ * concerned, but it is quite useful for cases where the AS is rotating
+ * the order of pre-configured values. */
+ {
+ size_t i;
+ for (i = 0; i < data->num_chal; i++) {
+ if (data->rand[i][0] == 0xaa) {
+ os_memcpy(data->kc[i],
+ "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7",
+ EAP_SIM_KC_LEN);
+ os_memcpy(data->sres[i], "\xd1\xd2\xd3\xd4",
+ EAP_SIM_SRES_LEN);
+ } else if (data->rand[i][0] == 0xbb) {
+ os_memcpy(data->kc[i],
+ "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7",
+ EAP_SIM_KC_LEN);
+ os_memcpy(data->sres[i], "\xe1\xe2\xe3\xe4",
+ EAP_SIM_SRES_LEN);
+ } else {
+ os_memcpy(data->kc[i],
+ "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7",
+ EAP_SIM_KC_LEN);
+ os_memcpy(data->sres[i], "\xf1\xf2\xf3\xf4",
+ EAP_SIM_SRES_LEN);
+ }
+ }
+ }
+#endif /* PCSC_FUNCS */
+ return 0;
+}
+
+
+static int eap_sim_supported_ver(int version)
+{
+ return version == EAP_SIM_VERSION;
+}
+
+
+#define CLEAR_PSEUDONYM 0x01
+#define CLEAR_REAUTH_ID 0x02
+#define CLEAR_EAP_ID 0x04
+
+static void eap_sim_clear_identities(struct eap_sim_data *data, int id)
+{
+ wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old%s%s%s",
+ id & CLEAR_PSEUDONYM ? " pseudonym" : "",
+ id & CLEAR_REAUTH_ID ? " reauth_id" : "",
+ id & CLEAR_EAP_ID ? " eap_id" : "");
+ if (id & CLEAR_PSEUDONYM) {
+ os_free(data->pseudonym);
+ data->pseudonym = NULL;
+ data->pseudonym_len = 0;
+ }
+ if (id & CLEAR_REAUTH_ID) {
+ os_free(data->reauth_id);
+ data->reauth_id = NULL;
+ data->reauth_id_len = 0;
+ }
+ if (id & CLEAR_EAP_ID) {
+ os_free(data->last_eap_identity);
+ data->last_eap_identity = NULL;
+ data->last_eap_identity_len = 0;
+ }
+}
+
+
+static int eap_sim_learn_ids(struct eap_sim_data *data,
+ struct eap_sim_attrs *attr)
+{
+ if (attr->next_pseudonym) {
+ os_free(data->pseudonym);
+ data->pseudonym = os_malloc(attr->next_pseudonym_len);
+ if (data->pseudonym == NULL) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for "
+ "next pseudonym");
+ return -1;
+ }
+ os_memcpy(data->pseudonym, attr->next_pseudonym,
+ attr->next_pseudonym_len);
+ data->pseudonym_len = attr->next_pseudonym_len;
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "EAP-SIM: (encr) AT_NEXT_PSEUDONYM",
+ data->pseudonym,
+ data->pseudonym_len);
+ }
+
+ if (attr->next_reauth_id) {
+ os_free(data->reauth_id);
+ data->reauth_id = os_malloc(attr->next_reauth_id_len);
+ if (data->reauth_id == NULL) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for "
+ "next reauth_id");
+ return -1;
+ }
+ os_memcpy(data->reauth_id, attr->next_reauth_id,
+ attr->next_reauth_id_len);
+ data->reauth_id_len = attr->next_reauth_id_len;
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "EAP-SIM: (encr) AT_NEXT_REAUTH_ID",
+ data->reauth_id,
+ data->reauth_id_len);
+ }
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_sim_client_error(struct eap_sim_data *data, u8 id,
+ int err)
+{
+ struct eap_sim_msg *msg;
+
+ eap_sim_state(data, FAILURE);
+ data->num_id_req = 0;
+ data->num_notification = 0;
+
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM,
+ EAP_SIM_SUBTYPE_CLIENT_ERROR);
+ eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0);
+ return eap_sim_msg_finish(msg, NULL, NULL, 0);
+}
+
+
+static struct wpabuf * eap_sim_response_start(struct eap_sm *sm,
+ struct eap_sim_data *data, u8 id,
+ enum eap_sim_id_req id_req)
+{
+ const u8 *identity = NULL;
+ size_t identity_len = 0;
+ struct eap_sim_msg *msg;
+
+ data->reauth = 0;
+ if (id_req == ANY_ID && data->reauth_id) {
+ identity = data->reauth_id;
+ identity_len = data->reauth_id_len;
+ data->reauth = 1;
+ } else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) &&
+ data->pseudonym) {
+ identity = data->pseudonym;
+ identity_len = data->pseudonym_len;
+ eap_sim_clear_identities(data, CLEAR_REAUTH_ID);
+ } else if (id_req != NO_ID_REQ) {
+ identity = eap_get_config_identity(sm, &identity_len);
+ if (identity) {
+ eap_sim_clear_identities(data, CLEAR_PSEUDONYM |
+ CLEAR_REAUTH_ID);
+ }
+ }
+ if (id_req != NO_ID_REQ)
+ eap_sim_clear_identities(data, CLEAR_EAP_ID);
+
+ wpa_printf(MSG_DEBUG, "Generating EAP-SIM Start (id=%d)", id);
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START);
+ if (!data->reauth) {
+ wpa_hexdump(MSG_DEBUG, " AT_NONCE_MT",
+ data->nonce_mt, EAP_SIM_NONCE_MT_LEN);
+ eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_MT, 0,
+ data->nonce_mt, EAP_SIM_NONCE_MT_LEN);
+ wpa_printf(MSG_DEBUG, " AT_SELECTED_VERSION %d",
+ data->selected_version);
+ eap_sim_msg_add(msg, EAP_SIM_AT_SELECTED_VERSION,
+ data->selected_version, NULL, 0);
+ }
+
+ if (identity) {
+ wpa_hexdump_ascii(MSG_DEBUG, " AT_IDENTITY",
+ identity, identity_len);
+ eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len,
+ identity, identity_len);
+ }
+
+ return eap_sim_msg_finish(msg, NULL, NULL, 0);
+}
+
+
+static struct wpabuf * eap_sim_response_challenge(struct eap_sim_data *data,
+ u8 id)
+{
+ struct eap_sim_msg *msg;
+
+ wpa_printf(MSG_DEBUG, "Generating EAP-SIM Challenge (id=%d)", id);
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM,
+ EAP_SIM_SUBTYPE_CHALLENGE);
+ if (data->use_result_ind) {
+ wpa_printf(MSG_DEBUG, " AT_RESULT_IND");
+ eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
+ }
+ wpa_printf(MSG_DEBUG, " AT_MAC");
+ eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+ return eap_sim_msg_finish(msg, data->k_aut, (u8 *) data->sres,
+ data->num_chal * EAP_SIM_SRES_LEN);
+}
+
+
+static struct wpabuf * eap_sim_response_reauth(struct eap_sim_data *data,
+ u8 id, int counter_too_small)
+{
+ struct eap_sim_msg *msg;
+ unsigned int counter;
+
+ wpa_printf(MSG_DEBUG, "Generating EAP-SIM Reauthentication (id=%d)",
+ id);
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM,
+ EAP_SIM_SUBTYPE_REAUTHENTICATION);
+ wpa_printf(MSG_DEBUG, " AT_IV");
+ wpa_printf(MSG_DEBUG, " AT_ENCR_DATA");
+ eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA);
+
+ if (counter_too_small) {
+ wpa_printf(MSG_DEBUG, " *AT_COUNTER_TOO_SMALL");
+ eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER_TOO_SMALL, 0, NULL, 0);
+ counter = data->counter_too_small;
+ } else
+ counter = data->counter;
+
+ wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", counter);
+ eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0);
+
+ if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt "
+ "AT_ENCR_DATA");
+ eap_sim_msg_free(msg);
+ return NULL;
+ }
+ if (data->use_result_ind) {
+ wpa_printf(MSG_DEBUG, " AT_RESULT_IND");
+ eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
+ }
+ wpa_printf(MSG_DEBUG, " AT_MAC");
+ eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+ return eap_sim_msg_finish(msg, data->k_aut, data->nonce_s,
+ EAP_SIM_NONCE_S_LEN);
+}
+
+
+static struct wpabuf * eap_sim_response_notification(struct eap_sim_data *data,
+ u8 id, u16 notification)
+{
+ struct eap_sim_msg *msg;
+ u8 *k_aut = (notification & 0x4000) == 0 ? data->k_aut : NULL;
+
+ wpa_printf(MSG_DEBUG, "Generating EAP-SIM Notification (id=%d)", id);
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION);
+ wpa_printf(MSG_DEBUG, " AT_NOTIFICATION");
+ eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, notification, NULL, 0);
+ if (k_aut && data->reauth) {
+ wpa_printf(MSG_DEBUG, " AT_IV");
+ wpa_printf(MSG_DEBUG, " AT_ENCR_DATA");
+ eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV,
+ EAP_SIM_AT_ENCR_DATA);
+ wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", data->counter);
+ eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter,
+ NULL, 0);
+ if (eap_sim_msg_add_encr_end(msg, data->k_encr,
+ EAP_SIM_AT_PADDING)) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt "
+ "AT_ENCR_DATA");
+ eap_sim_msg_free(msg);
+ return NULL;
+ }
+ }
+ if (k_aut) {
+ wpa_printf(MSG_DEBUG, " AT_MAC");
+ eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+ }
+ return eap_sim_msg_finish(msg, k_aut, (u8 *) "", 0);
+}
+
+
+static struct wpabuf * eap_sim_process_start(struct eap_sm *sm,
+ struct eap_sim_data *data, u8 id,
+ struct eap_sim_attrs *attr)
+{
+ int selected_version = -1, id_error;
+ size_t i;
+ u8 *pos;
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Start");
+ if (attr->version_list == NULL) {
+ wpa_printf(MSG_INFO, "EAP-SIM: No AT_VERSION_LIST in "
+ "SIM/Start");
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNSUPPORTED_VERSION);
+ }
+
+ os_free(data->ver_list);
+ data->ver_list = os_malloc(attr->version_list_len);
+ if (data->ver_list == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to allocate "
+ "memory for version list");
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+ os_memcpy(data->ver_list, attr->version_list, attr->version_list_len);
+ data->ver_list_len = attr->version_list_len;
+ pos = data->ver_list;
+ for (i = 0; i < data->ver_list_len / 2; i++) {
+ int ver = pos[0] * 256 + pos[1];
+ pos += 2;
+ if (eap_sim_supported_ver(ver)) {
+ selected_version = ver;
+ break;
+ }
+ }
+ if (selected_version < 0) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Could not find a supported "
+ "version");
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNSUPPORTED_VERSION);
+ }
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Selected Version %d",
+ selected_version);
+ data->selected_version = selected_version;
+
+ id_error = 0;
+ switch (attr->id_req) {
+ case NO_ID_REQ:
+ break;
+ case ANY_ID:
+ if (data->num_id_req > 0)
+ id_error++;
+ data->num_id_req++;
+ break;
+ case FULLAUTH_ID:
+ if (data->num_id_req > 1)
+ id_error++;
+ data->num_id_req++;
+ break;
+ case PERMANENT_ID:
+ if (data->num_id_req > 2)
+ id_error++;
+ data->num_id_req++;
+ break;
+ }
+ if (id_error) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Too many ID requests "
+ "used within one authentication");
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ return eap_sim_response_start(sm, data, id, attr->id_req);
+}
+
+
+static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm,
+ struct eap_sim_data *data,
+ u8 id,
+ const struct wpabuf *reqData,
+ struct eap_sim_attrs *attr)
+{
+ const u8 *identity;
+ size_t identity_len;
+ struct eap_sim_attrs eattr;
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Challenge");
+ data->reauth = 0;
+ if (!attr->mac || !attr->rand) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message "
+ "did not include%s%s",
+ !attr->mac ? " AT_MAC" : "",
+ !attr->rand ? " AT_RAND" : "");
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM: %lu challenges",
+ (unsigned long) attr->num_chal);
+ if (attr->num_chal < data->min_num_chal) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Insufficient number of "
+ "challenges (%lu)", (unsigned long) attr->num_chal);
+ return eap_sim_client_error(data, id,
+ EAP_SIM_INSUFFICIENT_NUM_OF_CHAL);
+ }
+ if (attr->num_chal > 3) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Too many challenges "
+ "(%lu)", (unsigned long) attr->num_chal);
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ /* Verify that RANDs are different */
+ if (os_memcmp(attr->rand, attr->rand + GSM_RAND_LEN,
+ GSM_RAND_LEN) == 0 ||
+ (attr->num_chal > 2 &&
+ (os_memcmp(attr->rand, attr->rand + 2 * GSM_RAND_LEN,
+ GSM_RAND_LEN) == 0 ||
+ os_memcmp(attr->rand + GSM_RAND_LEN,
+ attr->rand + 2 * GSM_RAND_LEN,
+ GSM_RAND_LEN) == 0))) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Same RAND used multiple times");
+ return eap_sim_client_error(data, id,
+ EAP_SIM_RAND_NOT_FRESH);
+ }
+
+ os_memcpy(data->rand, attr->rand, attr->num_chal * GSM_RAND_LEN);
+ data->num_chal = attr->num_chal;
+
+ if (eap_sim_gsm_auth(sm, data)) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: GSM authentication failed");
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+ if (data->last_eap_identity) {
+ identity = data->last_eap_identity;
+ identity_len = data->last_eap_identity_len;
+ } else if (data->pseudonym) {
+ identity = data->pseudonym;
+ identity_len = data->pseudonym_len;
+ } else
+ identity = eap_get_config_identity(sm, &identity_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Selected identity for MK "
+ "derivation", identity, identity_len);
+ eap_sim_derive_mk(identity, identity_len, data->nonce_mt,
+ data->selected_version, data->ver_list,
+ data->ver_list_len, data->num_chal,
+ (const u8 *) data->kc, data->mk);
+ eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk,
+ data->emsk);
+ if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, data->nonce_mt,
+ EAP_SIM_NONCE_MT_LEN)) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message "
+ "used invalid AT_MAC");
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ /* Old reauthentication and pseudonym identities must not be used
+ * anymore. In other words, if no new identities are received, full
+ * authentication will be used on next reauthentication. */
+ eap_sim_clear_identities(data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID |
+ CLEAR_EAP_ID);
+
+ if (attr->encr_data) {
+ u8 *decrypted;
+ decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
+ attr->encr_data_len, attr->iv,
+ &eattr, 0);
+ if (decrypted == NULL) {
+ return eap_sim_client_error(
+ data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+ eap_sim_learn_ids(data, &eattr);
+ os_free(decrypted);
+ }
+
+ if (data->result_ind && attr->result_ind)
+ data->use_result_ind = 1;
+
+ if (data->state != FAILURE && data->state != RESULT_FAILURE) {
+ eap_sim_state(data, data->use_result_ind ?
+ RESULT_SUCCESS : SUCCESS);
+ }
+
+ data->num_id_req = 0;
+ data->num_notification = 0;
+ /* RFC 4186 specifies that counter is initialized to one after
+ * fullauth, but initializing it to zero makes it easier to implement
+ * reauth verification. */
+ data->counter = 0;
+ return eap_sim_response_challenge(data, id);
+}
+
+
+static int eap_sim_process_notification_reauth(struct eap_sim_data *data,
+ struct eap_sim_attrs *attr)
+{
+ struct eap_sim_attrs eattr;
+ u8 *decrypted;
+
+ if (attr->encr_data == NULL || attr->iv == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Notification message after "
+ "reauth did not include encrypted data");
+ return -1;
+ }
+
+ decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
+ attr->encr_data_len, attr->iv, &eattr,
+ 0);
+ if (decrypted == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted "
+ "data from notification message");
+ return -1;
+ }
+
+ if (eattr.counter < 0 || (size_t) eattr.counter != data->counter) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Counter in notification "
+ "message does not match with counter in reauth "
+ "message");
+ os_free(decrypted);
+ return -1;
+ }
+
+ os_free(decrypted);
+ return 0;
+}
+
+
+static int eap_sim_process_notification_auth(struct eap_sim_data *data,
+ const struct wpabuf *reqData,
+ struct eap_sim_attrs *attr)
+{
+ if (attr->mac == NULL) {
+ wpa_printf(MSG_INFO, "EAP-SIM: no AT_MAC in after_auth "
+ "Notification message");
+ return -1;
+ }
+
+ if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, (u8 *) "", 0))
+ {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Notification message "
+ "used invalid AT_MAC");
+ return -1;
+ }
+
+ if (data->reauth &&
+ eap_sim_process_notification_reauth(data, attr)) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Invalid notification "
+ "message after reauth");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_sim_process_notification(
+ struct eap_sm *sm, struct eap_sim_data *data, u8 id,
+ const struct wpabuf *reqData, struct eap_sim_attrs *attr)
+{
+ wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Notification");
+ if (data->num_notification > 0) {
+ wpa_printf(MSG_INFO, "EAP-SIM: too many notification "
+ "rounds (only one allowed)");
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+ data->num_notification++;
+ if (attr->notification == -1) {
+ wpa_printf(MSG_INFO, "EAP-SIM: no AT_NOTIFICATION in "
+ "Notification message");
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ if ((attr->notification & 0x4000) == 0 &&
+ eap_sim_process_notification_auth(data, reqData, attr)) {
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ eap_sim_report_notification(sm->msg_ctx, attr->notification, 0);
+ if (attr->notification >= 0 && attr->notification < 32768) {
+ eap_sim_state(data, FAILURE);
+ } else if (attr->notification == EAP_SIM_SUCCESS &&
+ data->state == RESULT_SUCCESS)
+ eap_sim_state(data, SUCCESS);
+ return eap_sim_response_notification(data, id, attr->notification);
+}
+
+
+static struct wpabuf * eap_sim_process_reauthentication(
+ struct eap_sm *sm, struct eap_sim_data *data, u8 id,
+ const struct wpabuf *reqData, struct eap_sim_attrs *attr)
+{
+ struct eap_sim_attrs eattr;
+ u8 *decrypted;
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Reauthentication");
+
+ if (data->reauth_id == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Server is trying "
+ "reauthentication, but no reauth_id available");
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ data->reauth = 1;
+ if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, (u8 *) "", 0))
+ {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication "
+ "did not have valid AT_MAC");
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ if (attr->encr_data == NULL || attr->iv == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication "
+ "message did not include encrypted data");
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
+ attr->encr_data_len, attr->iv, &eattr,
+ 0);
+ if (decrypted == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted "
+ "data from reauthentication message");
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ if (eattr.nonce_s == NULL || eattr.counter < 0) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) No%s%s in reauth packet",
+ !eattr.nonce_s ? " AT_NONCE_S" : "",
+ eattr.counter < 0 ? " AT_COUNTER" : "");
+ os_free(decrypted);
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ if (eattr.counter < 0 || (size_t) eattr.counter <= data->counter) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid counter "
+ "(%d <= %d)", eattr.counter, data->counter);
+ data->counter_too_small = eattr.counter;
+ /* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current
+ * reauth_id must not be used to start a new reauthentication.
+ * However, since it was used in the last EAP-Response-Identity
+ * packet, it has to saved for the following fullauth to be
+ * used in MK derivation. */
+ os_free(data->last_eap_identity);
+ data->last_eap_identity = data->reauth_id;
+ data->last_eap_identity_len = data->reauth_id_len;
+ data->reauth_id = NULL;
+ data->reauth_id_len = 0;
+ os_free(decrypted);
+ return eap_sim_response_reauth(data, id, 1);
+ }
+ data->counter = eattr.counter;
+
+ os_memcpy(data->nonce_s, eattr.nonce_s, EAP_SIM_NONCE_S_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-SIM: (encr) AT_NONCE_S",
+ data->nonce_s, EAP_SIM_NONCE_S_LEN);
+
+ eap_sim_derive_keys_reauth(data->counter,
+ data->reauth_id, data->reauth_id_len,
+ data->nonce_s, data->mk, data->msk,
+ data->emsk);
+ eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+ eap_sim_learn_ids(data, &eattr);
+
+ if (data->result_ind && attr->result_ind)
+ data->use_result_ind = 1;
+
+ if (data->state != FAILURE && data->state != RESULT_FAILURE) {
+ eap_sim_state(data, data->use_result_ind ?
+ RESULT_SUCCESS : SUCCESS);
+ }
+
+ data->num_id_req = 0;
+ data->num_notification = 0;
+ if (data->counter > EAP_SIM_MAX_FAST_REAUTHS) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Maximum number of "
+ "fast reauths performed - force fullauth");
+ eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+ }
+ os_free(decrypted);
+ return eap_sim_response_reauth(data, id, 0);
+}
+
+
+static struct wpabuf * eap_sim_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct eap_sim_data *data = priv;
+ const struct eap_hdr *req;
+ u8 subtype, id;
+ struct wpabuf *res;
+ const u8 *pos;
+ struct eap_sim_attrs attr;
+ size_t len;
+
+ wpa_hexdump_buf(MSG_DEBUG, "EAP-SIM: EAP data", reqData);
+ if (eap_get_config_identity(sm, &len) == NULL) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Identity not configured");
+ eap_sm_request_identity(sm);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, reqData, &len);
+ if (pos == NULL || len < 1) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ req = wpabuf_head(reqData);
+ id = req->identifier;
+ len = be_to_host16(req->length);
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = TRUE;
+
+ subtype = *pos++;
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Subtype=%d", subtype);
+ pos += 2; /* Reserved */
+
+ if (eap_sim_parse_attr(pos, wpabuf_head_u8(reqData) + len, &attr, 0,
+ 0)) {
+ res = eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ goto done;
+ }
+
+ switch (subtype) {
+ case EAP_SIM_SUBTYPE_START:
+ res = eap_sim_process_start(sm, data, id, &attr);
+ break;
+ case EAP_SIM_SUBTYPE_CHALLENGE:
+ res = eap_sim_process_challenge(sm, data, id, reqData, &attr);
+ break;
+ case EAP_SIM_SUBTYPE_NOTIFICATION:
+ res = eap_sim_process_notification(sm, data, id, reqData,
+ &attr);
+ break;
+ case EAP_SIM_SUBTYPE_REAUTHENTICATION:
+ res = eap_sim_process_reauthentication(sm, data, id, reqData,
+ &attr);
+ break;
+ case EAP_SIM_SUBTYPE_CLIENT_ERROR:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Client-Error");
+ res = eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown subtype=%d", subtype);
+ res = eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ break;
+ }
+
+done:
+ if (data->state == FAILURE) {
+ ret->decision = DECISION_FAIL;
+ ret->methodState = METHOD_DONE;
+ } else if (data->state == SUCCESS) {
+ ret->decision = data->use_result_ind ?
+ DECISION_UNCOND_SUCC : DECISION_COND_SUCC;
+ ret->methodState = data->use_result_ind ?
+ METHOD_DONE : METHOD_MAY_CONT;
+ } else if (data->state == RESULT_FAILURE)
+ ret->methodState = METHOD_CONT;
+ else if (data->state == RESULT_SUCCESS)
+ ret->methodState = METHOD_CONT;
+
+ if (ret->methodState == METHOD_DONE) {
+ ret->allowNotifications = FALSE;
+ }
+
+ return res;
+}
+
+
+static Boolean eap_sim_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+ struct eap_sim_data *data = priv;
+ return data->pseudonym || data->reauth_id;
+}
+
+
+static void eap_sim_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_sim_data *data = priv;
+ eap_sim_clear_identities(data, CLEAR_EAP_ID);
+ data->use_result_ind = 0;
+}
+
+
+static void * eap_sim_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_sim_data *data = priv;
+ if (os_get_random(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data "
+ "for NONCE_MT");
+ os_free(data);
+ return NULL;
+ }
+ data->num_id_req = 0;
+ data->num_notification = 0;
+ eap_sim_state(data, CONTINUE);
+ return priv;
+}
+
+
+static const u8 * eap_sim_get_identity(struct eap_sm *sm, void *priv,
+ size_t *len)
+{
+ struct eap_sim_data *data = priv;
+
+ if (data->reauth_id) {
+ *len = data->reauth_id_len;
+ return data->reauth_id;
+ }
+
+ if (data->pseudonym) {
+ *len = data->pseudonym_len;
+ return data->pseudonym;
+ }
+
+ return NULL;
+}
+
+
+static Boolean eap_sim_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_sim_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_sim_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(EAP_SIM_KEYING_DATA_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_SIM_KEYING_DATA_LEN;
+ os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
+
+ return key;
+}
+
+
+static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_sim_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(EAP_EMSK_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_EMSK_LEN;
+ os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+
+ return key;
+}
+
+
+int eap_peer_sim_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_SIM, "SIM");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_sim_init;
+ eap->deinit = eap_sim_deinit;
+ eap->process = eap_sim_process;
+ eap->isKeyAvailable = eap_sim_isKeyAvailable;
+ eap->getKey = eap_sim_getKey;
+ eap->has_reauth_data = eap_sim_has_reauth_data;
+ eap->deinit_for_reauth = eap_sim_deinit_for_reauth;
+ eap->init_for_reauth = eap_sim_init_for_reauth;
+ eap->get_identity = eap_sim_get_identity;
+ eap->get_emsk = eap_sim_get_emsk;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c
new file mode 100644
index 0000000..6929468
--- /dev/null
+++ b/src/eap_peer/eap_tls.c
@@ -0,0 +1,288 @@
+/*
+ * EAP peer method: EAP-TLS (RFC 2716)
+ * Copyright (c) 2004-2008, 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "eap_config.h"
+#include "tls.h"
+
+
+static void eap_tls_deinit(struct eap_sm *sm, void *priv);
+
+
+struct eap_tls_data {
+ struct eap_ssl_data ssl;
+ u8 *key_data;
+};
+
+
+static void * eap_tls_init(struct eap_sm *sm)
+{
+ struct eap_tls_data *data;
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config == NULL ||
+ ((sm->init_phase2 ? config->private_key2 : config->private_key)
+ == NULL && config->engine == 0)) {
+ wpa_printf(MSG_INFO, "EAP-TLS: Private key not configured");
+ return NULL;
+ }
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+
+ if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) {
+ wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
+ eap_tls_deinit(sm, data);
+ if (config->engine) {
+ wpa_printf(MSG_DEBUG, "EAP-TLS: Requesting Smartcard "
+ "PIN");
+ eap_sm_request_pin(sm);
+ sm->ignore = TRUE;
+ } else if (config->private_key && !config->private_key_passwd)
+ {
+ wpa_printf(MSG_DEBUG, "EAP-TLS: Requesting private "
+ "key passphrase");
+ eap_sm_request_passphrase(sm);
+ sm->ignore = TRUE;
+ }
+ return NULL;
+ }
+
+ return data;
+}
+
+
+static void eap_tls_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_tls_data *data = priv;
+ if (data == NULL)
+ return;
+ eap_peer_tls_ssl_deinit(sm, &data->ssl);
+ os_free(data->key_data);
+ os_free(data);
+}
+
+
+static struct wpabuf * eap_tls_failure(struct eap_sm *sm,
+ struct eap_tls_data *data,
+ struct eap_method_ret *ret, int res,
+ struct wpabuf *resp, u8 id)
+{
+ wpa_printf(MSG_DEBUG, "EAP-TLS: TLS processing failed");
+
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+
+ if (res == -1) {
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config) {
+ /*
+ * The TLS handshake failed. So better forget the old
+ * PIN. It may be wrong, we cannot be sure but trying
+ * the wrong one again might block it on the card--so
+ * better ask the user again.
+ */
+ os_free(config->pin);
+ config->pin = NULL;
+ }
+ }
+
+ if (resp) {
+ /*
+ * This is likely an alert message, so send it instead of just
+ * ACKing the error.
+ */
+ return resp;
+ }
+
+ return eap_peer_tls_build_ack(id, EAP_TYPE_TLS, 0);
+}
+
+
+static void eap_tls_success(struct eap_sm *sm, struct eap_tls_data *data,
+ struct eap_method_ret *ret)
+{
+ wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
+
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+
+ os_free(data->key_data);
+ data->key_data = eap_peer_tls_derive_key(sm, &data->ssl,
+ "client EAP encryption",
+ EAP_TLS_KEY_LEN +
+ EAP_EMSK_LEN);
+ if (data->key_data) {
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TLS: Derived key",
+ data->key_data, EAP_TLS_KEY_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TLS: Derived EMSK",
+ data->key_data + EAP_TLS_KEY_LEN,
+ EAP_EMSK_LEN);
+ } else {
+ wpa_printf(MSG_INFO, "EAP-TLS: Failed to derive key");
+ }
+}
+
+
+static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ size_t left;
+ int res;
+ struct wpabuf *resp;
+ u8 flags, id;
+ const u8 *pos;
+ struct eap_tls_data *data = priv;
+
+ pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TLS, ret,
+ reqData, &left, &flags);
+ if (pos == NULL)
+ return NULL;
+ id = eap_get_id(reqData);
+
+ if (flags & EAP_TLS_FLAGS_START) {
+ wpa_printf(MSG_DEBUG, "EAP-TLS: Start");
+ left = 0; /* make sure that this frame is empty, even though it
+ * should always be, anyway */
+ }
+
+ resp = NULL;
+ res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TLS, 0, id,
+ pos, left, &resp);
+
+ if (res < 0) {
+ return eap_tls_failure(sm, data, ret, res, resp, id);
+ }
+
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn))
+ eap_tls_success(sm, data, ret);
+
+ if (res == 1) {
+ wpabuf_free(resp);
+ return eap_peer_tls_build_ack(id, EAP_TYPE_TLS, 0);
+ }
+
+ return resp;
+}
+
+
+static Boolean eap_tls_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+ struct eap_tls_data *data = priv;
+ return tls_connection_established(sm->ssl_ctx, data->ssl.conn);
+}
+
+
+static void eap_tls_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+}
+
+
+static void * eap_tls_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_tls_data *data = priv;
+ os_free(data->key_data);
+ data->key_data = NULL;
+ if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
+ os_free(data);
+ return NULL;
+ }
+ return priv;
+}
+
+
+static int eap_tls_get_status(struct eap_sm *sm, void *priv, char *buf,
+ size_t buflen, int verbose)
+{
+ struct eap_tls_data *data = priv;
+ return eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);
+}
+
+
+static Boolean eap_tls_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_tls_data *data = priv;
+ return data->key_data != NULL;
+}
+
+
+static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_tls_data *data = priv;
+ u8 *key;
+
+ if (data->key_data == NULL)
+ return NULL;
+
+ key = os_malloc(EAP_TLS_KEY_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_TLS_KEY_LEN;
+ os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
+
+ return key;
+}
+
+
+static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_tls_data *data = priv;
+ u8 *key;
+
+ if (data->key_data == NULL)
+ return NULL;
+
+ key = os_malloc(EAP_EMSK_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_EMSK_LEN;
+ os_memcpy(key, data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN);
+
+ return key;
+}
+
+
+int eap_peer_tls_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_tls_init;
+ eap->deinit = eap_tls_deinit;
+ eap->process = eap_tls_process;
+ eap->isKeyAvailable = eap_tls_isKeyAvailable;
+ eap->getKey = eap_tls_getKey;
+ eap->get_status = eap_tls_get_status;
+ eap->has_reauth_data = eap_tls_has_reauth_data;
+ eap->deinit_for_reauth = eap_tls_deinit_for_reauth;
+ eap->init_for_reauth = eap_tls_init_for_reauth;
+ eap->get_emsk = eap_tls_get_emsk;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c
new file mode 100644
index 0000000..0cff3e8
--- /dev/null
+++ b/src/eap_peer/eap_tls_common.c
@@ -0,0 +1,1007 @@
+/*
+ * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions
+ * Copyright (c) 2004-2008, 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "eap_config.h"
+#include "sha1.h"
+#include "tls.h"
+
+
+static int eap_tls_check_blob(struct eap_sm *sm, const char **name,
+ const u8 **data, size_t *data_len)
+{
+ const struct wpa_config_blob *blob;
+
+ if (*name == NULL || os_strncmp(*name, "blob://", 7) != 0)
+ return 0;
+
+ blob = eap_get_config_blob(sm, *name + 7);
+ if (blob == NULL) {
+ wpa_printf(MSG_ERROR, "%s: Named configuration blob '%s' not "
+ "found", __func__, *name + 7);
+ return -1;
+ }
+
+ *name = NULL;
+ *data = blob->data;
+ *data_len = blob->len;
+
+ return 0;
+}
+
+
+static void eap_tls_params_from_conf1(struct tls_connection_params *params,
+ struct eap_peer_config *config)
+{
+ params->ca_cert = (char *) config->ca_cert;
+ params->ca_path = (char *) config->ca_path;
+ params->client_cert = (char *) config->client_cert;
+ params->private_key = (char *) config->private_key;
+ params->private_key_passwd = (char *) config->private_key_passwd;
+ params->dh_file = (char *) config->dh_file;
+ params->subject_match = (char *) config->subject_match;
+ params->altsubject_match = (char *) config->altsubject_match;
+ params->engine_id = config->engine_id;
+ params->pin = config->pin;
+ params->key_id = config->key_id;
+}
+
+
+static void eap_tls_params_from_conf2(struct tls_connection_params *params,
+ struct eap_peer_config *config)
+{
+ params->ca_cert = (char *) config->ca_cert2;
+ params->ca_path = (char *) config->ca_path2;
+ params->client_cert = (char *) config->client_cert2;
+ params->private_key = (char *) config->private_key2;
+ params->private_key_passwd = (char *) config->private_key2_passwd;
+ params->dh_file = (char *) config->dh_file2;
+ params->subject_match = (char *) config->subject_match2;
+ params->altsubject_match = (char *) config->altsubject_match2;
+}
+
+
+static int eap_tls_params_from_conf(struct eap_sm *sm,
+ struct eap_ssl_data *data,
+ struct tls_connection_params *params,
+ struct eap_peer_config *config, int phase2)
+{
+ os_memset(params, 0, sizeof(*params));
+ params->engine = config->engine;
+ if (phase2)
+ eap_tls_params_from_conf2(params, config);
+ else
+ eap_tls_params_from_conf1(params, config);
+ params->tls_ia = data->tls_ia;
+
+ /*
+ * Use blob data, if available. Otherwise, leave reference to external
+ * file as-is.
+ */
+ if (eap_tls_check_blob(sm, &params->ca_cert, &params->ca_cert_blob,
+ &params->ca_cert_blob_len) ||
+ eap_tls_check_blob(sm, &params->client_cert,
+ &params->client_cert_blob,
+ &params->client_cert_blob_len) ||
+ eap_tls_check_blob(sm, &params->private_key,
+ &params->private_key_blob,
+ &params->private_key_blob_len) ||
+ eap_tls_check_blob(sm, &params->dh_file, &params->dh_blob,
+ &params->dh_blob_len)) {
+ wpa_printf(MSG_INFO, "SSL: Failed to get configuration blobs");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int eap_tls_init_connection(struct eap_sm *sm,
+ struct eap_ssl_data *data,
+ struct eap_peer_config *config,
+ struct tls_connection_params *params)
+{
+ int res;
+
+ data->conn = tls_connection_init(sm->ssl_ctx);
+ if (data->conn == NULL) {
+ wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS "
+ "connection");
+ return -1;
+ }
+
+ res = tls_connection_set_params(sm->ssl_ctx, data->conn, params);
+ if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) {
+ /*
+ * At this point with the pkcs11 engine the PIN might be wrong.
+ * We reset the PIN in the configuration to be sure to not use
+ * it again and the calling function must request a new one.
+ */
+ os_free(config->pin);
+ config->pin = NULL;
+ } else if (res == TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED) {
+ wpa_printf(MSG_INFO, "TLS: Failed to load private key");
+ /*
+ * We do not know exactly but maybe the PIN was wrong,
+ * so ask for a new one.
+ */
+ os_free(config->pin);
+ config->pin = NULL;
+ eap_sm_request_pin(sm);
+ sm->ignore = TRUE;
+ return -1;
+ } else if (res) {
+ wpa_printf(MSG_INFO, "TLS: Failed to set TLS connection "
+ "parameters");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * eap_peer_tls_ssl_init - Initialize shared TLS functionality
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @config: Pointer to the network configuration
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to initialize shared TLS functionality for EAP-TLS,
+ * EAP-PEAP, EAP-TTLS, and EAP-FAST.
+ */
+int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
+ struct eap_peer_config *config)
+{
+ struct tls_connection_params params;
+
+ if (config == NULL)
+ return -1;
+
+ data->eap = sm;
+ data->phase2 = sm->init_phase2;
+ if (eap_tls_params_from_conf(sm, data, &params, config, data->phase2) <
+ 0)
+ return -1;
+
+ if (eap_tls_init_connection(sm, data, config, &params) < 0)
+ return -1;
+
+ data->tls_out_limit = config->fragment_size;
+ if (data->phase2) {
+ /* Limit the fragment size in the inner TLS authentication
+ * since the outer authentication with EAP-PEAP does not yet
+ * support fragmentation */
+ if (data->tls_out_limit > 100)
+ data->tls_out_limit -= 100;
+ }
+
+ if (config->phase1 &&
+ os_strstr(config->phase1, "include_tls_length=1")) {
+ wpa_printf(MSG_DEBUG, "TLS: Include TLS Message Length in "
+ "unfragmented packets");
+ data->include_tls_length = 1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * eap_peer_tls_ssl_deinit - Deinitialize shared TLS functionality
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ *
+ * This function deinitializes shared TLS functionality that was initialized
+ * with eap_peer_tls_ssl_init().
+ */
+void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data)
+{
+ tls_connection_deinit(sm->ssl_ctx, data->conn);
+ eap_peer_tls_reset_input(data);
+ eap_peer_tls_reset_output(data);
+}
+
+
+/**
+ * eap_peer_tls_derive_key - Derive a key based on TLS session data
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @label: Label string for deriving the keys, e.g., "client EAP encryption"
+ * @len: Length of the key material to generate (usually 64 for MSK)
+ * Returns: Pointer to allocated key on success or %NULL on failure
+ *
+ * This function uses TLS-PRF to generate pseudo-random data based on the TLS
+ * session data (client/server random and master key). Each key type may use a
+ * different label to bind the key usage into the generated material.
+ *
+ * The caller is responsible for freeing the returned buffer.
+ */
+u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
+ const char *label, size_t len)
+{
+ struct tls_keys keys;
+ u8 *rnd = NULL, *out;
+
+ out = os_malloc(len);
+ if (out == NULL)
+ return NULL;
+
+ /* First, try to use TLS library function for PRF, if available. */
+ if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, out, len) ==
+ 0)
+ return out;
+
+ /*
+ * TLS library did not support key generation, so get the needed TLS
+ * session parameters and use an internal implementation of TLS PRF to
+ * derive the key.
+ */
+ if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
+ goto fail;
+
+ if (keys.client_random == NULL || keys.server_random == NULL ||
+ keys.master_key == NULL)
+ goto fail;
+
+ rnd = os_malloc(keys.client_random_len + keys.server_random_len);
+ if (rnd == NULL)
+ goto fail;
+ os_memcpy(rnd, keys.client_random, keys.client_random_len);
+ os_memcpy(rnd + keys.client_random_len, keys.server_random,
+ keys.server_random_len);
+
+ if (tls_prf(keys.master_key, keys.master_key_len,
+ label, rnd, keys.client_random_len +
+ keys.server_random_len, out, len))
+ goto fail;
+
+ os_free(rnd);
+ return out;
+
+fail:
+ os_free(out);
+ os_free(rnd);
+ return NULL;
+}
+
+
+/**
+ * eap_peer_tls_reassemble_fragment - Reassemble a received fragment
+ * @data: Data for TLS processing
+ * @in_data: Next incoming TLS segment
+ * @in_len: Length of in_data
+ * Returns: 0 on success, 1 if more data is needed for the full message, or
+ * -1 on error
+ */
+static int eap_peer_tls_reassemble_fragment(struct eap_ssl_data *data,
+ const u8 *in_data, size_t in_len)
+{
+ u8 *buf;
+
+ if (data->tls_in_len + in_len == 0) {
+ /* No message data received?! */
+ wpa_printf(MSG_WARNING, "SSL: Invalid reassembly state: "
+ "tls_in_left=%lu tls_in_len=%lu in_len=%lu",
+ (unsigned long) data->tls_in_left,
+ (unsigned long) data->tls_in_len,
+ (unsigned long) in_len);
+ eap_peer_tls_reset_input(data);
+ return -1;
+ }
+
+ if (data->tls_in_len + in_len > 65536) {
+ /*
+ * Limit length to avoid rogue servers from causing large
+ * memory allocations.
+ */
+ wpa_printf(MSG_INFO, "SSL: Too long TLS fragment (size over "
+ "64 kB)");
+ eap_peer_tls_reset_input(data);
+ return -1;
+ }
+
+ if (in_len > data->tls_in_left) {
+ /* Sender is doing something odd - reject message */
+ wpa_printf(MSG_INFO, "SSL: more data than TLS message length "
+ "indicated");
+ eap_peer_tls_reset_input(data);
+ return -1;
+ }
+
+ buf = os_realloc(data->tls_in, data->tls_in_len + in_len);
+ if (buf == NULL) {
+ wpa_printf(MSG_INFO, "SSL: Could not allocate memory for TLS "
+ "data");
+ eap_peer_tls_reset_input(data);
+ return -1;
+ }
+ os_memcpy(buf + data->tls_in_len, in_data, in_len);
+ data->tls_in = buf;
+ data->tls_in_len += in_len;
+ data->tls_in_left -= in_len;
+
+ if (data->tls_in_left > 0) {
+ wpa_printf(MSG_DEBUG, "SSL: Need %lu bytes more input "
+ "data", (unsigned long) data->tls_in_left);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * eap_peer_tls_data_reassemble - Reassemble TLS data
+ * @data: Data for TLS processing
+ * @in_data: Next incoming TLS segment
+ * @in_len: Length of in_data
+ * @out_len: Variable for returning length of the reassembled message
+ * @need_more_input: Variable for returning whether more input data is needed
+ * to reassemble this TLS packet
+ * Returns: Pointer to output data, %NULL on error or when more data is needed
+ * for the full message (in which case, *need_more_input is also set to 1).
+ *
+ * This function reassembles TLS fragments. Caller must not free the returned
+ * data buffer since an internal pointer to it is maintained.
+ */
+const u8 * eap_peer_tls_data_reassemble(
+ struct eap_ssl_data *data, const u8 *in_data, size_t in_len,
+ size_t *out_len, int *need_more_input)
+{
+ *need_more_input = 0;
+
+ if (data->tls_in_left > in_len || data->tls_in) {
+ /* Message has fragments */
+ int res = eap_peer_tls_reassemble_fragment(data, in_data,
+ in_len);
+ if (res) {
+ if (res == 1)
+ *need_more_input = 1;
+ return NULL;
+ }
+
+ /* Message is now fully reassembled. */
+ } else {
+ /* No fragments in this message, so just make a copy of it. */
+ data->tls_in_left = 0;
+ data->tls_in = os_malloc(in_len ? in_len : 1);
+ if (data->tls_in == NULL)
+ return NULL;
+ os_memcpy(data->tls_in, in_data, in_len);
+ data->tls_in_len = in_len;
+ }
+
+ *out_len = data->tls_in_len;
+ return data->tls_in;
+}
+
+
+/**
+ * eap_tls_process_input - Process incoming TLS message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @in_data: Message received from the server
+ * @in_len: Length of in_data
+ * @out_data: Buffer for returning a pointer to application data (if available)
+ * Returns: 0 on success, 1 if more input data is needed, 2 if application data
+ * is available, -1 on failure
+ */
+static int eap_tls_process_input(struct eap_sm *sm, struct eap_ssl_data *data,
+ const u8 *in_data, size_t in_len,
+ struct wpabuf **out_data)
+{
+ const u8 *msg;
+ size_t msg_len;
+ int need_more_input;
+ u8 *appl_data;
+ size_t appl_data_len;
+
+ msg = eap_peer_tls_data_reassemble(data, in_data, in_len,
+ &msg_len, &need_more_input);
+ if (msg == NULL)
+ return need_more_input ? 1 : -1;
+
+ /* Full TLS message reassembled - continue handshake processing */
+ if (data->tls_out) {
+ /* This should not happen.. */
+ wpa_printf(MSG_INFO, "SSL: eap_tls_process_input - pending "
+ "tls_out data even though tls_out_len = 0");
+ os_free(data->tls_out);
+ WPA_ASSERT(data->tls_out == NULL);
+ }
+ appl_data = NULL;
+ data->tls_out = tls_connection_handshake(sm->ssl_ctx, data->conn,
+ msg, msg_len,
+ &data->tls_out_len,
+ &appl_data, &appl_data_len);
+
+ eap_peer_tls_reset_input(data);
+
+ if (appl_data &&
+ tls_connection_established(sm->ssl_ctx, data->conn) &&
+ !tls_connection_get_failed(sm->ssl_ctx, data->conn)) {
+ wpa_hexdump_key(MSG_MSGDUMP, "SSL: Application data",
+ appl_data, appl_data_len);
+ *out_data = wpabuf_alloc_ext_data(appl_data, appl_data_len);
+ if (*out_data == NULL) {
+ os_free(appl_data);
+ return -1;
+ }
+ return 2;
+ }
+
+ os_free(appl_data);
+
+ return 0;
+}
+
+
+/**
+ * eap_tls_process_output - Process outgoing TLS message
+ * @data: Data for TLS processing
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @peap_version: Version number for EAP-PEAP/TTLS
+ * @id: EAP identifier for the response
+ * @ret: Return value to use on success
+ * @out_data: Buffer for returning the allocated output buffer
+ * Returns: ret (0 or 1) on success, -1 on failure
+ */
+static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type,
+ int peap_version, u8 id, int ret,
+ struct wpabuf **out_data)
+{
+ size_t len;
+ u8 *flags;
+ int more_fragments, length_included;
+
+ len = data->tls_out_len - data->tls_out_pos;
+ wpa_printf(MSG_DEBUG, "SSL: %lu bytes left to be sent out (of total "
+ "%lu bytes)",
+ (unsigned long) len, (unsigned long) data->tls_out_len);
+
+ /*
+ * Limit outgoing message to the configured maximum size. Fragment
+ * message if needed.
+ */
+ if (len > data->tls_out_limit) {
+ more_fragments = 1;
+ len = data->tls_out_limit;
+ wpa_printf(MSG_DEBUG, "SSL: sending %lu bytes, more fragments "
+ "will follow", (unsigned long) len);
+ } else
+ more_fragments = 0;
+
+ length_included = data->tls_out_pos == 0 &&
+ (data->tls_out_len > data->tls_out_limit ||
+ data->include_tls_length);
+
+ *out_data = eap_msg_alloc(EAP_VENDOR_IETF, eap_type,
+ 1 + length_included * 4 + len,
+ EAP_CODE_RESPONSE, id);
+ if (*out_data == NULL)
+ return -1;
+
+ flags = wpabuf_put(*out_data, 1);
+ *flags = peap_version;
+ if (more_fragments)
+ *flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS;
+ if (length_included) {
+ *flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED;
+ wpabuf_put_be32(*out_data, data->tls_out_len);
+ }
+
+ wpabuf_put_data(*out_data, &data->tls_out[data->tls_out_pos], len);
+ data->tls_out_pos += len;
+
+ if (!more_fragments)
+ eap_peer_tls_reset_output(data);
+
+ return ret;
+}
+
+
+/**
+ * eap_peer_tls_process_helper - Process TLS handshake message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @peap_version: Version number for EAP-PEAP/TTLS
+ * @id: EAP identifier for the response
+ * @in_data: Message received from the server
+ * @in_len: Length of in_data
+ * @out_data: Buffer for returning a pointer to the response message
+ * Returns: 0 on success, 1 if more input data is needed, 2 if application data
+ * is available, or -1 on failure
+ *
+ * This function can be used to process TLS handshake messages. It reassembles
+ * the received fragments and uses a TLS library to process the messages. The
+ * response data from the TLS library is fragmented to suitable output messages
+ * that the caller can send out.
+ *
+ * out_data is used to return the response message if the return value of this
+ * function is 0, 2, or -1. In case of failure, the message is likely a TLS
+ * alarm message. The caller is responsible for freeing the allocated buffer if
+ * *out_data is not %NULL.
+ *
+ * This function is called for each received TLS message during the TLS
+ * handshake after eap_peer_tls_process_init() call and possible processing of
+ * TLS Flags field. Once the handshake has been completed, i.e., when
+ * tls_connection_established() returns 1, EAP method specific decrypting of
+ * the tunneled data is used.
+ */
+int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
+ EapType eap_type, int peap_version,
+ u8 id, const u8 *in_data, size_t in_len,
+ struct wpabuf **out_data)
+{
+ int ret = 0;
+
+ *out_data = NULL;
+
+ if (data->tls_out_len > 0 && in_len > 0) {
+ wpa_printf(MSG_DEBUG, "SSL: Received non-ACK when output "
+ "fragments are waiting to be sent out");
+ return -1;
+ }
+
+ if (data->tls_out_len == 0) {
+ /*
+ * No more data to send out - expect to receive more data from
+ * the AS.
+ */
+ int res = eap_tls_process_input(sm, data, in_data, in_len,
+ out_data);
+ if (res) {
+ /*
+ * Input processing failed (res = -1) or more data is
+ * needed (res = 1).
+ */
+ return res;
+ }
+
+ /*
+ * The incoming message has been reassembled and processed. The
+ * response was allocated into data->tls_out buffer.
+ */
+ }
+
+ if (data->tls_out == NULL) {
+ /*
+ * No outgoing fragments remaining from the previous message
+ * and no new message generated. This indicates an error in TLS
+ * processing.
+ */
+ eap_peer_tls_reset_output(data);
+ return -1;
+ }
+
+ if (tls_connection_get_failed(sm->ssl_ctx, data->conn)) {
+ /* TLS processing has failed - return error */
+ wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to "
+ "report error");
+ ret = -1;
+ /* TODO: clean pin if engine used? */
+ }
+
+ if (data->tls_out_len == 0) {
+ /*
+ * TLS negotiation should now be complete since all other cases
+ * needing more data should have been caught above based on
+ * the TLS Message Length field.
+ */
+ wpa_printf(MSG_DEBUG, "SSL: No data to be sent out");
+ os_free(data->tls_out);
+ data->tls_out = NULL;
+ return 1;
+ }
+
+ /* Send the pending message (in fragments, if needed). */
+ return eap_tls_process_output(data, eap_type, peap_version, id, ret,
+ out_data);
+}
+
+
+/**
+ * eap_peer_tls_build_ack - Build a TLS ACK frame
+ * @id: EAP identifier for the response
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @peap_version: Version number for EAP-PEAP/TTLS
+ * Returns: Pointer to the allocated ACK frame or %NULL on failure
+ */
+struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type,
+ int peap_version)
+{
+ struct wpabuf *resp;
+
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, 1, EAP_CODE_RESPONSE,
+ id);
+ if (resp == NULL)
+ return NULL;
+ wpa_printf(MSG_DEBUG, "SSL: Building ACK (type=%d id=%d ver=%d)",
+ (int) eap_type, id, peap_version);
+ wpabuf_put_u8(resp, peap_version); /* Flags */
+ return resp;
+}
+
+
+/**
+ * eap_peer_tls_reauth_init - Re-initialize shared TLS for session resumption
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data)
+{
+ eap_peer_tls_reset_input(data);
+ eap_peer_tls_reset_output(data);
+ return tls_connection_shutdown(sm->ssl_ctx, data->conn);
+}
+
+
+/**
+ * eap_peer_tls_status - Get TLS status
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @buf: Buffer for status information
+ * @buflen: Maximum buffer length
+ * @verbose: Whether to include verbose status information
+ * Returns: Number of bytes written to buf.
+ */
+int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data,
+ char *buf, size_t buflen, int verbose)
+{
+ char name[128];
+ int len = 0, ret;
+
+ if (tls_get_cipher(sm->ssl_ctx, data->conn, name, sizeof(name)) == 0) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "EAP TLS cipher=%s\n", name);
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ len += ret;
+ }
+
+ return len;
+}
+
+
+/**
+ * eap_peer_tls_process_init - Initial validation/processing of EAP requests
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @ret: Return values from EAP request validation and processing
+ * @reqData: EAP request to be processed (eapReqData)
+ * @len: Buffer for returning length of the remaining payload
+ * @flags: Buffer for returning TLS flags
+ * Returns: Pointer to payload after TLS flags and length or %NULL on failure
+ *
+ * This function validates the EAP header and processes the optional TLS
+ * Message Length field. If this is the first fragment of a TLS message, the
+ * TLS reassembly code is initialized to receive the indicated number of bytes.
+ *
+ * EAP-TLS, EAP-PEAP, EAP-TTLS, and EAP-FAST methods are expected to use this
+ * function as the first step in processing received messages. They will need
+ * to process the flags (apart from Message Length Included) that are returned
+ * through the flags pointer and the message payload that will be returned (and
+ * the length is returned through the len pointer). Return values (ret) are set
+ * for continuation of EAP method processing. The caller is responsible for
+ * setting these to indicate completion (either success or failure) based on
+ * the authentication result.
+ */
+const u8 * eap_peer_tls_process_init(struct eap_sm *sm,
+ struct eap_ssl_data *data,
+ EapType eap_type,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData,
+ size_t *len, u8 *flags)
+{
+ const u8 *pos;
+ size_t left;
+ unsigned int tls_msg_len;
+
+ if (tls_get_errors(sm->ssl_ctx)) {
+ wpa_printf(MSG_INFO, "SSL: TLS errors detected");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, reqData, &left);
+ if (pos == NULL) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ *flags = *pos++;
+ left--;
+ wpa_printf(MSG_DEBUG, "SSL: Received packet(len=%lu) - "
+ "Flags 0x%02x", (unsigned long) wpabuf_len(reqData),
+ *flags);
+ if (*flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) {
+ if (left < 4) {
+ wpa_printf(MSG_INFO, "SSL: Short frame with TLS "
+ "length");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ tls_msg_len = WPA_GET_BE32(pos);
+ wpa_printf(MSG_DEBUG, "SSL: TLS Message Length: %d",
+ tls_msg_len);
+ if (data->tls_in_left == 0) {
+ data->tls_in_total = tls_msg_len;
+ data->tls_in_left = tls_msg_len;
+ os_free(data->tls_in);
+ data->tls_in = NULL;
+ data->tls_in_len = 0;
+ }
+ pos += 4;
+ left -= 4;
+ }
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = TRUE;
+
+ *len = left;
+ return pos;
+}
+
+
+/**
+ * eap_peer_tls_reset_input - Reset input buffers
+ * @data: Data for TLS processing
+ *
+ * This function frees any allocated memory for input buffers and resets input
+ * state.
+ */
+void eap_peer_tls_reset_input(struct eap_ssl_data *data)
+{
+ data->tls_in_left = data->tls_in_total = data->tls_in_len = 0;
+ os_free(data->tls_in);
+ data->tls_in = NULL;
+}
+
+
+/**
+ * eap_peer_tls_reset_output - Reset output buffers
+ * @data: Data for TLS processing
+ *
+ * This function frees any allocated memory for output buffers and resets
+ * output state.
+ */
+void eap_peer_tls_reset_output(struct eap_ssl_data *data)
+{
+ data->tls_out_len = 0;
+ data->tls_out_pos = 0;
+ os_free(data->tls_out);
+ data->tls_out = NULL;
+}
+
+
+/**
+ * eap_peer_tls_decrypt - Decrypt received phase 2 TLS message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @in_data: Message received from the server
+ * @in_decrypted: Buffer for returning a pointer to the decrypted message
+ * Returns: 0 on success, 1 if more input data is needed, or -1 on failure
+ */
+int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data,
+ const struct wpabuf *in_data,
+ struct wpabuf **in_decrypted)
+{
+ int res;
+ const u8 *msg;
+ size_t msg_len, buf_len;
+ int need_more_input;
+
+ msg = eap_peer_tls_data_reassemble(data, wpabuf_head(in_data),
+ wpabuf_len(in_data), &msg_len,
+ &need_more_input);
+ if (msg == NULL)
+ return need_more_input ? 1 : -1;
+
+ buf_len = wpabuf_len(in_data);
+ if (data->tls_in_total > buf_len)
+ buf_len = data->tls_in_total;
+ *in_decrypted = wpabuf_alloc(buf_len ? buf_len : 1);
+ if (*in_decrypted == NULL) {
+ eap_peer_tls_reset_input(data);
+ wpa_printf(MSG_WARNING, "SSL: Failed to allocate memory for "
+ "decryption");
+ return -1;
+ }
+
+ res = tls_connection_decrypt(sm->ssl_ctx, data->conn, msg, msg_len,
+ wpabuf_mhead(*in_decrypted), buf_len);
+ eap_peer_tls_reset_input(data);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "SSL: Failed to decrypt Phase 2 data");
+ return -1;
+ }
+ wpabuf_put(*in_decrypted, res);
+ return 0;
+}
+
+
+/**
+ * eap_peer_tls_encrypt - Encrypt phase 2 TLS message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @peap_version: Version number for EAP-PEAP/TTLS
+ * @id: EAP identifier for the response
+ * @in_data: Plaintext phase 2 data to encrypt or %NULL to continue fragments
+ * @out_data: Buffer for returning a pointer to the encrypted response message
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data,
+ EapType eap_type, int peap_version, u8 id,
+ const struct wpabuf *in_data,
+ struct wpabuf **out_data)
+{
+ int res;
+ size_t len;
+
+ if (in_data) {
+ eap_peer_tls_reset_output(data);
+ len = wpabuf_len(in_data) + 100;
+ data->tls_out = os_malloc(len);
+ if (data->tls_out == NULL)
+ return -1;
+
+ res = tls_connection_encrypt(sm->ssl_ctx, data->conn,
+ wpabuf_head(in_data),
+ wpabuf_len(in_data),
+ data->tls_out, len);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 "
+ "data (in_len=%lu)",
+ (unsigned long) wpabuf_len(in_data));
+ eap_peer_tls_reset_output(data);
+ return -1;
+ }
+
+ data->tls_out_len = res;
+ }
+
+ return eap_tls_process_output(data, eap_type, peap_version, id, 0,
+ out_data);
+}
+
+
+/**
+ * eap_peer_select_phase2_methods - Select phase 2 EAP method
+ * @config: Pointer to the network configuration
+ * @prefix: 'phase2' configuration prefix, e.g., "auth="
+ * @types: Buffer for returning allocated list of allowed EAP methods
+ * @num_types: Buffer for returning number of allocated EAP methods
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to parse EAP method list and select allowed methods
+ * for Phase2 authentication.
+ */
+int eap_peer_select_phase2_methods(struct eap_peer_config *config,
+ const char *prefix,
+ struct eap_method_type **types,
+ size_t *num_types)
+{
+ char *start, *pos, *buf;
+ struct eap_method_type *methods = NULL, *_methods;
+ u8 method;
+ size_t num_methods = 0, prefix_len;
+
+ if (config == NULL || config->phase2 == NULL)
+ goto get_defaults;
+
+ start = buf = os_strdup(config->phase2);
+ if (buf == NULL)
+ return -1;
+
+ prefix_len = os_strlen(prefix);
+
+ while (start && *start != '\0') {
+ int vendor;
+ pos = os_strstr(start, prefix);
+ if (pos == NULL)
+ break;
+ if (start != pos && *(pos - 1) != ' ') {
+ start = pos + prefix_len;
+ continue;
+ }
+
+ start = pos + prefix_len;
+ pos = os_strchr(start, ' ');
+ if (pos)
+ *pos++ = '\0';
+ method = eap_get_phase2_type(start, &vendor);
+ if (vendor == EAP_VENDOR_IETF && method == EAP_TYPE_NONE) {
+ wpa_printf(MSG_ERROR, "TLS: Unsupported Phase2 EAP "
+ "method '%s'", start);
+ } else {
+ num_methods++;
+ _methods = os_realloc(methods,
+ num_methods * sizeof(*methods));
+ if (_methods == NULL) {
+ os_free(methods);
+ os_free(buf);
+ return -1;
+ }
+ methods = _methods;
+ methods[num_methods - 1].vendor = vendor;
+ methods[num_methods - 1].method = method;
+ }
+
+ start = pos;
+ }
+
+ os_free(buf);
+
+get_defaults:
+ if (methods == NULL)
+ methods = eap_get_phase2_types(config, &num_methods);
+
+ if (methods == NULL) {
+ wpa_printf(MSG_ERROR, "TLS: No Phase2 EAP methods available");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "TLS: Phase2 EAP types",
+ (u8 *) methods,
+ num_methods * sizeof(struct eap_method_type));
+
+ *types = methods;
+ *num_types = num_methods;
+
+ return 0;
+}
+
+
+/**
+ * eap_peer_tls_phase2_nak - Generate EAP-Nak for Phase 2
+ * @types: Buffer for returning allocated list of allowed EAP methods
+ * @num_types: Buffer for returning number of allocated EAP methods
+ * @hdr: EAP-Request header (and the following EAP type octet)
+ * @resp: Buffer for returning the EAP-Nak message
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_peer_tls_phase2_nak(struct eap_method_type *types, size_t num_types,
+ struct eap_hdr *hdr, struct wpabuf **resp)
+{
+ u8 *pos = (u8 *) (hdr + 1);
+ size_t i;
+
+ /* TODO: add support for expanded Nak */
+ wpa_printf(MSG_DEBUG, "TLS: Phase 2 Request: Nak type=%d", *pos);
+ wpa_hexdump(MSG_DEBUG, "TLS: Allowed Phase2 EAP types",
+ (u8 *) types, num_types * sizeof(struct eap_method_type));
+ *resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK, num_types,
+ EAP_CODE_RESPONSE, hdr->identifier);
+ if (*resp == NULL)
+ return -1;
+
+ for (i = 0; i < num_types; i++) {
+ if (types[i].vendor == EAP_VENDOR_IETF &&
+ types[i].method < 256)
+ wpabuf_put_u8(*resp, types[i].method);
+ }
+
+ eap_update_len(*resp);
+
+ return 0;
+}
diff --git a/src/eap_peer/eap_tls_common.h b/src/eap_peer/eap_tls_common.h
new file mode 100644
index 0000000..2c87427
--- /dev/null
+++ b/src/eap_peer/eap_tls_common.h
@@ -0,0 +1,139 @@
+/*
+ * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions
+ * Copyright (c) 2004-2006, 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.
+ */
+
+#ifndef EAP_TLS_COMMON_H
+#define EAP_TLS_COMMON_H
+
+/**
+ * struct eap_ssl_data - TLS data for EAP methods
+ */
+struct eap_ssl_data {
+ /**
+ * conn - TLS connection context data from tls_connection_init()
+ */
+ struct tls_connection *conn;
+
+ /**
+ * tls_out - TLS message to be sent out in fragments
+ */
+ u8 *tls_out;
+
+ /**
+ * tls_out_len - Total length of the outgoing TLS message
+ */
+ size_t tls_out_len;
+
+ /**
+ * tls_out_pos - The current position in the outgoing TLS message
+ */
+ size_t tls_out_pos;
+
+ /**
+ * tls_out_limit - Maximum fragment size for outgoing TLS messages
+ */
+ size_t tls_out_limit;
+
+ /**
+ * tls_in - Received TLS message buffer for re-assembly
+ */
+ u8 *tls_in;
+
+ /**
+ * tls_in_len - Number of bytes of the received TLS message in tls_in
+ */
+ size_t tls_in_len;
+
+ /**
+ * tls_in_left - Number of remaining bytes in the incoming TLS message
+ */
+ size_t tls_in_left;
+
+ /**
+ * tls_in_total - Total number of bytes in the incoming TLS message
+ */
+ size_t tls_in_total;
+
+ /**
+ * phase2 - Whether this TLS connection is used in EAP phase 2 (tunnel)
+ */
+ int phase2;
+
+ /**
+ * include_tls_length - Whether the TLS length field is included even
+ * if the TLS data is not fragmented
+ */
+ int include_tls_length;
+
+ /**
+ * tls_ia - Whether TLS/IA is enabled for this TLS connection
+ */
+ int tls_ia;
+
+ /**
+ * eap - Pointer to EAP state machine allocated with eap_peer_sm_init()
+ */
+ struct eap_sm *eap;
+};
+
+
+/* EAP TLS Flags */
+#define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80
+#define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40
+#define EAP_TLS_FLAGS_START 0x20
+#define EAP_PEAP_VERSION_MASK 0x07
+
+ /* could be up to 128 bytes, but only the first 64 bytes are used */
+#define EAP_TLS_KEY_LEN 64
+
+
+int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
+ struct eap_peer_config *config);
+void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data);
+u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
+ const char *label, size_t len);
+const u8 * eap_peer_tls_data_reassemble(
+ struct eap_ssl_data *data, const u8 *in_data, size_t in_len,
+ size_t *out_len, int *need_more_input);
+int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
+ EapType eap_type, int peap_version,
+ u8 id, const u8 *in_data, size_t in_len,
+ struct wpabuf **out_data);
+struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type,
+ int peap_version);
+int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data);
+int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data,
+ char *buf, size_t buflen, int verbose);
+const u8 * eap_peer_tls_process_init(struct eap_sm *sm,
+ struct eap_ssl_data *data,
+ EapType eap_type,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData,
+ size_t *len, u8 *flags);
+void eap_peer_tls_reset_input(struct eap_ssl_data *data);
+void eap_peer_tls_reset_output(struct eap_ssl_data *data);
+int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data,
+ const struct wpabuf *in_data,
+ struct wpabuf **in_decrypted);
+int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data,
+ EapType eap_type, int peap_version, u8 id,
+ const struct wpabuf *in_data,
+ struct wpabuf **out_data);
+int eap_peer_select_phase2_methods(struct eap_peer_config *config,
+ const char *prefix,
+ struct eap_method_type **types,
+ size_t *num_types);
+int eap_peer_tls_phase2_nak(struct eap_method_type *types, size_t num_types,
+ struct eap_hdr *hdr, struct wpabuf **resp);
+
+#endif /* EAP_TLS_COMMON_H */
diff --git a/src/eap_peer/eap_tlv.c b/src/eap_peer/eap_tlv.c
new file mode 100644
index 0000000..e2b9483
--- /dev/null
+++ b/src/eap_peer/eap_tlv.c
@@ -0,0 +1,189 @@
+/*
+ * EAP peer method: EAP-TLV (draft-josefsson-pppext-eap-tls-eap-07.txt)
+ * Copyright (c) 2004-2008, 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_tlv.h"
+
+
+/**
+ * eap_tlv_build_nak - Build EAP-TLV NAK message
+ * @id: EAP identifier for the header
+ * @nak_type: TLV type (EAP_TLV_*)
+ * Returns: Buffer to the allocated EAP-TLV NAK message or %NULL on failure
+ *
+ * This funtion builds an EAP-TLV NAK message. The caller is responsible for
+ * freeing the returned buffer.
+ */
+struct wpabuf * eap_tlv_build_nak(int id, u16 nak_type)
+{
+ struct wpabuf *msg;
+
+ msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, 10,
+ EAP_CODE_RESPONSE, id);
+ if (msg == NULL)
+ return NULL;
+
+ wpabuf_put_u8(msg, 0x80); /* Mandatory */
+ wpabuf_put_u8(msg, EAP_TLV_NAK_TLV);
+ wpabuf_put_be16(msg, 6); /* Length */
+ wpabuf_put_be32(msg, 0); /* Vendor-Id */
+ wpabuf_put_be16(msg, nak_type); /* NAK-Type */
+
+ return msg;
+}
+
+
+/**
+ * eap_tlv_build_result - Build EAP-TLV Result message
+ * @id: EAP identifier for the header
+ * @status: Status (EAP_TLV_RESULT_SUCCESS or EAP_TLV_RESULT_FAILURE)
+ * Returns: Buffer to the allocated EAP-TLV Result message or %NULL on failure
+ *
+ * This funtion builds an EAP-TLV Result message. The caller is responsible for
+ * freeing the returned buffer.
+ */
+struct wpabuf * eap_tlv_build_result(int id, u16 status)
+{
+ struct wpabuf *msg;
+
+ msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, 6,
+ EAP_CODE_RESPONSE, id);
+ if (msg == NULL)
+ return NULL;
+
+ wpabuf_put_u8(msg, 0x80); /* Mandatory */
+ wpabuf_put_u8(msg, EAP_TLV_RESULT_TLV);
+ wpabuf_put_be16(msg, 2); /* Length */
+ wpabuf_put_be16(msg, status); /* Status */
+
+ return msg;
+}
+
+
+/**
+ * eap_tlv_process - Process a received EAP-TLV message and generate a response
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @ret: Return values from EAP request validation and processing
+ * @req: EAP-TLV request to be processed. The caller must have validated that
+ * the buffer is large enough to contain full request (hdr->length bytes) and
+ * that the EAP type is EAP_TYPE_TLV.
+ * @resp: Buffer to return a pointer to the allocated response message. This
+ * field should be initialized to %NULL before the call. The value will be
+ * updated if a response message is generated. The caller is responsible for
+ * freeing the allocated message.
+ * @force_failure: Force negotiation to fail
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_tlv_process(struct eap_sm *sm, struct eap_method_ret *ret,
+ const struct wpabuf *req, struct wpabuf **resp,
+ int force_failure)
+{
+ size_t left, tlv_len;
+ const u8 *pos;
+ const u8 *result_tlv = NULL;
+ size_t result_tlv_len = 0;
+ int tlv_type, mandatory;
+
+ /* Parse TLVs */
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLV, req, &left);
+ if (pos == NULL)
+ return -1;
+ wpa_hexdump(MSG_DEBUG, "EAP-TLV: Received TLVs", pos, left);
+ while (left >= 4) {
+ mandatory = !!(pos[0] & 0x80);
+ tlv_type = WPA_GET_BE16(pos) & 0x3fff;
+ pos += 2;
+ tlv_len = WPA_GET_BE16(pos);
+ pos += 2;
+ left -= 4;
+ if (tlv_len > left) {
+ wpa_printf(MSG_DEBUG, "EAP-TLV: TLV underrun "
+ "(tlv_len=%lu left=%lu)",
+ (unsigned long) tlv_len,
+ (unsigned long) left);
+ return -1;
+ }
+ switch (tlv_type) {
+ case EAP_TLV_RESULT_TLV:
+ result_tlv = pos;
+ result_tlv_len = tlv_len;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-TLV: Unsupported TLV Type "
+ "%d%s", tlv_type,
+ mandatory ? " (mandatory)" : "");
+ if (mandatory) {
+ /* NAK TLV and ignore all TLVs in this packet.
+ */
+ *resp = eap_tlv_build_nak(eap_get_id(req),
+ tlv_type);
+ return *resp == NULL ? -1 : 0;
+ }
+ /* Ignore this TLV, but process other TLVs */
+ break;
+ }
+
+ pos += tlv_len;
+ left -= tlv_len;
+ }
+ if (left) {
+ wpa_printf(MSG_DEBUG, "EAP-TLV: Last TLV too short in "
+ "Request (left=%lu)", (unsigned long) left);
+ return -1;
+ }
+
+ /* Process supported TLVs */
+ if (result_tlv) {
+ int status, resp_status;
+ wpa_hexdump(MSG_DEBUG, "EAP-TLV: Result TLV",
+ result_tlv, result_tlv_len);
+ if (result_tlv_len < 2) {
+ wpa_printf(MSG_INFO, "EAP-TLV: Too short Result TLV "
+ "(len=%lu)",
+ (unsigned long) result_tlv_len);
+ return -1;
+ }
+ status = WPA_GET_BE16(result_tlv);
+ if (status == EAP_TLV_RESULT_SUCCESS) {
+ wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Success "
+ "- EAP-TLV/Phase2 Completed");
+ if (force_failure) {
+ wpa_printf(MSG_INFO, "EAP-TLV: Earlier failure"
+ " - force failed Phase 2");
+ resp_status = EAP_TLV_RESULT_FAILURE;
+ ret->decision = DECISION_FAIL;
+ } else {
+ resp_status = EAP_TLV_RESULT_SUCCESS;
+ ret->decision = DECISION_UNCOND_SUCC;
+ }
+ } else if (status == EAP_TLV_RESULT_FAILURE) {
+ wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Failure");
+ resp_status = EAP_TLV_RESULT_FAILURE;
+ ret->decision = DECISION_FAIL;
+ } else {
+ wpa_printf(MSG_INFO, "EAP-TLV: Unknown TLV Result "
+ "Status %d", status);
+ resp_status = EAP_TLV_RESULT_FAILURE;
+ ret->decision = DECISION_FAIL;
+ }
+ ret->methodState = METHOD_DONE;
+
+ *resp = eap_tlv_build_result(eap_get_id(req), resp_status);
+ }
+
+ return 0;
+}
diff --git a/src/eap_peer/eap_tlv.h b/src/eap_peer/eap_tlv.h
new file mode 100644
index 0000000..ce70aba
--- /dev/null
+++ b/src/eap_peer/eap_tlv.h
@@ -0,0 +1,26 @@
+/*
+ * EAP peer method: EAP-TLV (draft-josefsson-pppext-eap-tls-eap-07.txt)
+ * Copyright (c) 2004-2008, 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.
+ */
+
+#ifndef EAP_TLV_H
+#define EAP_TLV_H
+
+#include "eap_common/eap_tlv_common.h"
+
+struct wpabuf * eap_tlv_build_nak(int id, u16 nak_type);
+struct wpabuf * eap_tlv_build_result(int id, u16 status);
+int eap_tlv_process(struct eap_sm *sm, struct eap_method_ret *ret,
+ const struct wpabuf *req, struct wpabuf **resp,
+ int force_failure);
+
+#endif /* EAP_TLV_H */
diff --git a/src/eap_peer/eap_tnc.c b/src/eap_peer/eap_tnc.c
new file mode 100644
index 0000000..e808be0
--- /dev/null
+++ b/src/eap_peer/eap_tnc.c
@@ -0,0 +1,220 @@
+/*
+ * EAP peer method: EAP-TNC (Trusted Network Connect)
+ * Copyright (c) 2007, 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "base64.h"
+#include "eap_i.h"
+#include "tncc.h"
+
+
+struct eap_tnc_data {
+ EapMethodState state;
+ struct tncc_data *tncc;
+};
+
+
+/* EAP-TNC Flags */
+#define EAP_TNC_FLAGS_LENGTH_INCLUDED 0x80
+#define EAP_TNC_FLAGS_MORE_FRAGMENTS 0x40
+#define EAP_TNC_FLAGS_START 0x20
+#define EAP_TNC_VERSION_MASK 0x07
+
+#define EAP_TNC_VERSION 1
+
+
+static void * eap_tnc_init(struct eap_sm *sm)
+{
+ struct eap_tnc_data *data;
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->state = METHOD_INIT;
+ data->tncc = tncc_init();
+ if (data->tncc == NULL) {
+ os_free(data);
+ return NULL;
+ }
+
+ return data;
+}
+
+
+static void eap_tnc_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_tnc_data *data = priv;
+
+ tncc_deinit(data->tncc);
+ os_free(data);
+}
+
+
+static struct wpabuf * eap_tnc_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct eap_tnc_data *data = priv;
+ struct wpabuf *resp;
+ const u8 *pos;
+ u8 *rpos, *rpos1, *start;
+ size_t len, rlen;
+ size_t imc_len;
+ char *start_buf, *end_buf;
+ size_t start_len, end_len;
+ int tncs_done = 0;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, reqData, &len);
+ if (pos == NULL || len == 0) {
+ wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame (pos=%p len=%lu)",
+ pos, (unsigned long) len);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Received payload", pos, len);
+
+ if ((*pos & EAP_TNC_VERSION_MASK) != EAP_TNC_VERSION) {
+ wpa_printf(MSG_DEBUG, "EAP-TNC: Unsupported version %d",
+ *pos & EAP_TNC_VERSION_MASK);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (data->state == METHOD_INIT) {
+ if (!(*pos & EAP_TNC_FLAGS_START)) {
+ wpa_printf(MSG_DEBUG, "EAP-TNC: Server did not use "
+ "start flag in the first message");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ tncc_init_connection(data->tncc);
+
+ data->state = METHOD_MAY_CONT;
+ } else {
+ enum tncc_process_res res;
+
+ if (*pos & EAP_TNC_FLAGS_START) {
+ wpa_printf(MSG_DEBUG, "EAP-TNC: Server used start "
+ "flag again");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ res = tncc_process_if_tnccs(data->tncc, pos + 1, len - 1);
+ switch (res) {
+ case TNCCS_PROCESS_ERROR:
+ ret->ignore = TRUE;
+ return NULL;
+ case TNCCS_PROCESS_OK_NO_RECOMMENDATION:
+ case TNCCS_RECOMMENDATION_ERROR:
+ wpa_printf(MSG_DEBUG, "EAP-TNC: No "
+ "TNCCS-Recommendation received");
+ break;
+ case TNCCS_RECOMMENDATION_ALLOW:
+ wpa_msg(sm->msg_ctx, MSG_INFO,
+ "TNC: Recommendation = allow");
+ tncs_done = 1;
+ break;
+ case TNCCS_RECOMMENDATION_NONE:
+ wpa_msg(sm->msg_ctx, MSG_INFO,
+ "TNC: Recommendation = none");
+ tncs_done = 1;
+ break;
+ case TNCCS_RECOMMENDATION_ISOLATE:
+ wpa_msg(sm->msg_ctx, MSG_INFO,
+ "TNC: Recommendation = isolate");
+ tncs_done = 1;
+ break;
+ }
+ }
+
+ ret->ignore = FALSE;
+ ret->methodState = data->state;
+ ret->decision = DECISION_UNCOND_SUCC;
+ ret->allowNotifications = TRUE;
+
+ if (tncs_done) {
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1,
+ EAP_CODE_RESPONSE, eap_get_id(reqData));
+ if (resp == NULL)
+ return NULL;
+
+ wpabuf_put_u8(resp, EAP_TNC_VERSION);
+ wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS done - reply with an "
+ "empty ACK message");
+ return resp;
+ }
+
+ imc_len = tncc_total_send_len(data->tncc);
+
+ start_buf = tncc_if_tnccs_start(data->tncc);
+ if (start_buf == NULL)
+ return NULL;
+ start_len = os_strlen(start_buf);
+ end_buf = tncc_if_tnccs_end();
+ if (end_buf == NULL) {
+ os_free(start_buf);
+ return NULL;
+ }
+ end_len = os_strlen(end_buf);
+
+ rlen = 1 + start_len + imc_len + end_len;
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, rlen,
+ EAP_CODE_RESPONSE, eap_get_id(reqData));
+ if (resp == NULL) {
+ os_free(start_buf);
+ os_free(end_buf);
+ return NULL;
+ }
+
+ start = wpabuf_put(resp, 0);
+ wpabuf_put_u8(resp, EAP_TNC_VERSION);
+ wpabuf_put_data(resp, start_buf, start_len);
+ os_free(start_buf);
+
+ rpos1 = wpabuf_put(resp, 0);
+ rpos = tncc_copy_send_buf(data->tncc, rpos1);
+ wpabuf_put(resp, rpos - rpos1);
+
+ wpabuf_put_data(resp, end_buf, end_len);
+ os_free(end_buf);
+
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Response", start, rlen);
+
+ return resp;
+}
+
+
+int eap_peer_tnc_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_TNC, "TNC");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_tnc_init;
+ eap->deinit = eap_tnc_deinit;
+ eap->process = eap_tnc_process;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c
new file mode 100644
index 0000000..a3ded89
--- /dev/null
+++ b/src/eap_peer/eap_ttls.c
@@ -0,0 +1,1976 @@
+/*
+ * EAP peer method: EAP-TTLS (draft-ietf-pppext-eap-ttls-03.txt)
+ * Copyright (c) 2004-2008, 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_peer/eap_i.h"
+#include "eap_peer/eap_tls_common.h"
+#include "eap_peer/eap_config.h"
+#include "ms_funcs.h"
+#include "sha1.h"
+#include "eap_common/chap.h"
+#include "tls.h"
+#include "mschapv2.h"
+#include "eap_common/eap_ttls.h"
+
+
+/* Maximum supported TTLS version
+ * 0 = draft-ietf-pppext-eap-ttls-03.txt / draft-funk-eap-ttls-v0-00.txt
+ * 1 = draft-funk-eap-ttls-v1-00.txt
+ */
+#ifndef EAP_TTLS_VERSION
+#define EAP_TTLS_VERSION 0 /* TTLSv1 implementation is not yet complete */
+#endif /* EAP_TTLS_VERSION */
+
+
+#define MSCHAPV2_KEY_LEN 16
+#define MSCHAPV2_NT_RESPONSE_LEN 24
+
+
+static void eap_ttls_deinit(struct eap_sm *sm, void *priv);
+
+
+struct eap_ttls_data {
+ struct eap_ssl_data ssl;
+ int ssl_initialized;
+
+ int ttls_version, force_ttls_version;
+
+ const struct eap_method *phase2_method;
+ void *phase2_priv;
+ int phase2_success;
+ int phase2_start;
+
+ enum phase2_types {
+ EAP_TTLS_PHASE2_EAP,
+ EAP_TTLS_PHASE2_MSCHAPV2,
+ EAP_TTLS_PHASE2_MSCHAP,
+ EAP_TTLS_PHASE2_PAP,
+ EAP_TTLS_PHASE2_CHAP
+ } phase2_type;
+ struct eap_method_type phase2_eap_type;
+ struct eap_method_type *phase2_eap_types;
+ size_t num_phase2_eap_types;
+
+ u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN];
+ int auth_response_valid;
+ u8 master_key[MSCHAPV2_MASTER_KEY_LEN]; /* MSCHAPv2 master key */
+ u8 ident;
+ int resuming; /* starting a resumed session */
+ int reauth; /* reauthentication */
+ u8 *key_data;
+
+ struct wpabuf *pending_phase2_req;
+
+#ifdef EAP_TNC
+ int ready_for_tnc;
+ int tnc_started;
+#endif /* EAP_TNC */
+};
+
+
+static void * eap_ttls_init(struct eap_sm *sm)
+{
+ struct eap_ttls_data *data;
+ struct eap_peer_config *config = eap_get_config(sm);
+ char *selected;
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->ttls_version = EAP_TTLS_VERSION;
+ data->force_ttls_version = -1;
+ selected = "EAP";
+ data->phase2_type = EAP_TTLS_PHASE2_EAP;
+
+#if EAP_TTLS_VERSION > 0
+ if (config && config->phase1) {
+ const char *pos = os_strstr(config->phase1, "ttlsver=");
+ if (pos) {
+ data->force_ttls_version = atoi(pos + 8);
+ data->ttls_version = data->force_ttls_version;
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Forced TTLS version "
+ "%d", data->force_ttls_version);
+ }
+ }
+#endif /* EAP_TTLS_VERSION */
+
+ if (config && config->phase2) {
+ if (os_strstr(config->phase2, "autheap=")) {
+ selected = "EAP";
+ data->phase2_type = EAP_TTLS_PHASE2_EAP;
+ } else if (os_strstr(config->phase2, "auth=MSCHAPV2")) {
+ selected = "MSCHAPV2";
+ data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2;
+ } else if (os_strstr(config->phase2, "auth=MSCHAP")) {
+ selected = "MSCHAP";
+ data->phase2_type = EAP_TTLS_PHASE2_MSCHAP;
+ } else if (os_strstr(config->phase2, "auth=PAP")) {
+ selected = "PAP";
+ data->phase2_type = EAP_TTLS_PHASE2_PAP;
+ } else if (os_strstr(config->phase2, "auth=CHAP")) {
+ selected = "CHAP";
+ data->phase2_type = EAP_TTLS_PHASE2_CHAP;
+ }
+ }
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 type: %s", selected);
+
+ if (data->phase2_type == EAP_TTLS_PHASE2_EAP) {
+ if (eap_peer_select_phase2_methods(config, "autheap=",
+ &data->phase2_eap_types,
+ &data->num_phase2_eap_types)
+ < 0) {
+ eap_ttls_deinit(sm, data);
+ return NULL;
+ }
+
+ data->phase2_eap_type.vendor = EAP_VENDOR_IETF;
+ data->phase2_eap_type.method = EAP_TYPE_NONE;
+ }
+
+#if EAP_TTLS_VERSION > 0
+ if (!(tls_capabilities(sm->ssl_ctx) & TLS_CAPABILITY_IA) &&
+ data->ttls_version > 0) {
+ if (data->force_ttls_version > 0) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Forced TTLSv%d and "
+ "TLS library does not support TLS/IA.",
+ data->force_ttls_version);
+ eap_ttls_deinit(sm, data);
+ return NULL;
+ }
+ data->ttls_version = 0;
+ }
+#endif /* EAP_TTLS_VERSION */
+
+ return data;
+}
+
+
+static void eap_ttls_phase2_eap_deinit(struct eap_sm *sm,
+ struct eap_ttls_data *data)
+{
+ if (data->phase2_priv && data->phase2_method) {
+ data->phase2_method->deinit(sm, data->phase2_priv);
+ data->phase2_method = NULL;
+ data->phase2_priv = NULL;
+ }
+}
+
+
+static void eap_ttls_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_ttls_data *data = priv;
+ if (data == NULL)
+ return;
+ eap_ttls_phase2_eap_deinit(sm, data);
+ os_free(data->phase2_eap_types);
+ if (data->ssl_initialized)
+ eap_peer_tls_ssl_deinit(sm, &data->ssl);
+ os_free(data->key_data);
+ wpabuf_free(data->pending_phase2_req);
+ os_free(data);
+}
+
+
+static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id,
+ int mandatory, size_t len)
+{
+ struct ttls_avp_vendor *avp;
+ u8 flags;
+ size_t hdrlen;
+
+ avp = (struct ttls_avp_vendor *) avphdr;
+ flags = mandatory ? AVP_FLAGS_MANDATORY : 0;
+ if (vendor_id) {
+ flags |= AVP_FLAGS_VENDOR;
+ hdrlen = sizeof(*avp);
+ avp->vendor_id = host_to_be32(vendor_id);
+ } else {
+ hdrlen = sizeof(struct ttls_avp);
+ }
+
+ avp->avp_code = host_to_be32(avp_code);
+ avp->avp_length = host_to_be32((flags << 24) | (hdrlen + len));
+
+ return avphdr + hdrlen;
+}
+
+
+static u8 * eap_ttls_avp_add(u8 *start, u8 *avphdr, u32 avp_code,
+ u32 vendor_id, int mandatory,
+ const u8 *data, size_t len)
+{
+ u8 *pos;
+ pos = eap_ttls_avp_hdr(avphdr, avp_code, vendor_id, mandatory, len);
+ os_memcpy(pos, data, len);
+ pos += len;
+ AVP_PAD(start, pos);
+ return pos;
+}
+
+
+static int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code,
+ int mandatory)
+{
+ struct wpabuf *msg;
+ u8 *avp, *pos;
+
+ msg = wpabuf_alloc(sizeof(struct ttls_avp) + wpabuf_len(*resp) + 4);
+ if (msg == NULL) {
+ wpabuf_free(*resp);
+ *resp = NULL;
+ return -1;
+ }
+
+ avp = wpabuf_mhead(msg);
+ pos = eap_ttls_avp_hdr(avp, avp_code, 0, mandatory, wpabuf_len(*resp));
+ os_memcpy(pos, wpabuf_head(*resp), wpabuf_len(*resp));
+ pos += wpabuf_len(*resp);
+ AVP_PAD(avp, pos);
+ wpabuf_free(*resp);
+ wpabuf_put(msg, pos - avp);
+ *resp = msg;
+ return 0;
+}
+
+
+#if EAP_TTLS_VERSION > 0
+static int eap_ttls_ia_permute_inner_secret(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ const u8 *key, size_t key_len)
+{
+ u8 *buf;
+ size_t buf_len;
+ int ret;
+
+ if (key) {
+ buf_len = 2 + key_len;
+ buf = os_malloc(buf_len);
+ if (buf == NULL)
+ return -1;
+ WPA_PUT_BE16(buf, key_len);
+ os_memcpy(buf + 2, key, key_len);
+ } else {
+ buf = NULL;
+ buf_len = 0;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Session keys for TLS/IA inner "
+ "secret permutation", buf, buf_len);
+ ret = tls_connection_ia_permute_inner_secret(sm->ssl_ctx,
+ data->ssl.conn,
+ buf, buf_len);
+ os_free(buf);
+
+ return ret;
+}
+#endif /* EAP_TTLS_VERSION */
+
+
+static int eap_ttls_v0_derive_key(struct eap_sm *sm,
+ struct eap_ttls_data *data)
+{
+ os_free(data->key_data);
+ data->key_data = eap_peer_tls_derive_key(sm, &data->ssl,
+ "ttls keying material",
+ EAP_TLS_KEY_LEN);
+ if (!data->key_data) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Failed to derive key");
+ return -1;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key",
+ data->key_data, EAP_TLS_KEY_LEN);
+
+ return 0;
+}
+
+
+#if EAP_TTLS_VERSION > 0
+static int eap_ttls_v1_derive_key(struct eap_sm *sm,
+ struct eap_ttls_data *data)
+{
+ struct tls_keys keys;
+ u8 *rnd;
+
+ os_free(data->key_data);
+ data->key_data = NULL;
+
+ os_memset(&keys, 0, sizeof(keys));
+ if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) ||
+ keys.client_random == NULL || keys.server_random == NULL ||
+ keys.inner_secret == NULL) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, "
+ "client random, or server random to derive keying "
+ "material");
+ return -1;
+ }
+
+ rnd = os_malloc(keys.client_random_len + keys.server_random_len);
+ data->key_data = os_malloc(EAP_TLS_KEY_LEN);
+ if (rnd == NULL || data->key_data == NULL) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: No memory for key derivation");
+ os_free(rnd);
+ os_free(data->key_data);
+ data->key_data = NULL;
+ return -1;
+ }
+ os_memcpy(rnd, keys.client_random, keys.client_random_len);
+ os_memcpy(rnd + keys.client_random_len, keys.server_random,
+ keys.server_random_len);
+
+ if (tls_prf(keys.inner_secret, keys.inner_secret_len,
+ "ttls v1 keying material", rnd, keys.client_random_len +
+ keys.server_random_len, data->key_data, EAP_TLS_KEY_LEN)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key");
+ os_free(rnd);
+ os_free(data->key_data);
+ data->key_data = NULL;
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: client/server random",
+ rnd, keys.client_random_len + keys.server_random_len);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: TLS/IA inner secret",
+ keys.inner_secret, keys.inner_secret_len);
+
+ os_free(rnd);
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key",
+ data->key_data, EAP_TLS_KEY_LEN);
+
+ return 0;
+}
+#endif /* EAP_TTLS_VERSION */
+
+
+static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm,
+ struct eap_ttls_data *data, size_t len)
+{
+#if EAP_TTLS_VERSION > 0
+ struct tls_keys keys;
+ u8 *challenge, *rnd;
+#endif /* EAP_TTLS_VERSION */
+
+ if (data->ttls_version == 0) {
+ return eap_peer_tls_derive_key(sm, &data->ssl,
+ "ttls challenge", len);
+ }
+
+#if EAP_TTLS_VERSION > 0
+
+ os_memset(&keys, 0, sizeof(keys));
+ if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) ||
+ keys.client_random == NULL || keys.server_random == NULL ||
+ keys.inner_secret == NULL) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, "
+ "client random, or server random to derive "
+ "implicit challenge");
+ return NULL;
+ }
+
+ rnd = os_malloc(keys.client_random_len + keys.server_random_len);
+ challenge = os_malloc(len);
+ if (rnd == NULL || challenge == NULL) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: No memory for implicit "
+ "challenge derivation");
+ os_free(rnd);
+ os_free(challenge);
+ return NULL;
+ }
+ os_memcpy(rnd, keys.server_random, keys.server_random_len);
+ os_memcpy(rnd + keys.server_random_len, keys.client_random,
+ keys.client_random_len);
+
+ if (tls_prf(keys.inner_secret, keys.inner_secret_len,
+ "inner application challenge", rnd,
+ keys.client_random_len + keys.server_random_len,
+ challenge, len)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive implicit "
+ "challenge");
+ os_free(rnd);
+ os_free(challenge);
+ return NULL;
+ }
+
+ os_free(rnd);
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived implicit challenge",
+ challenge, len);
+
+ return challenge;
+
+#else /* EAP_TTLS_VERSION */
+
+ return NULL;
+
+#endif /* EAP_TTLS_VERSION */
+}
+
+
+static void eap_ttlsv1_phase2_eap_finish(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret)
+{
+#if EAP_TTLS_VERSION > 0
+ if (data->ttls_version > 0) {
+ const struct eap_method *m = data->phase2_method;
+ void *priv = data->phase2_priv;
+
+ /* TTLSv1 requires TLS/IA FinalPhaseFinished */
+ if (ret->decision == DECISION_UNCOND_SUCC)
+ ret->decision = DECISION_COND_SUCC;
+ ret->methodState = METHOD_CONT;
+
+ if (ret->decision == DECISION_COND_SUCC &&
+ m->isKeyAvailable && m->getKey &&
+ m->isKeyAvailable(sm, priv)) {
+ u8 *key;
+ size_t key_len;
+ key = m->getKey(sm, priv, &key_len);
+ if (key) {
+ eap_ttls_ia_permute_inner_secret(
+ sm, data, key, key_len);
+ os_free(key);
+ }
+ }
+ }
+#endif /* EAP_TTLS_VERSION */
+}
+
+
+static void eap_ttls_phase2_select_eap_method(struct eap_ttls_data *data,
+ u8 method)
+{
+ size_t i;
+ for (i = 0; i < data->num_phase2_eap_types; i++) {
+ if (data->phase2_eap_types[i].vendor != EAP_VENDOR_IETF ||
+ data->phase2_eap_types[i].method != method)
+ continue;
+
+ data->phase2_eap_type.vendor =
+ data->phase2_eap_types[i].vendor;
+ data->phase2_eap_type.method =
+ data->phase2_eap_types[i].method;
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected "
+ "Phase 2 EAP vendor %d method %d",
+ data->phase2_eap_type.vendor,
+ data->phase2_eap_type.method);
+ break;
+ }
+}
+
+
+static int eap_ttls_phase2_eap_process(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct eap_hdr *hdr, size_t len,
+ struct wpabuf **resp)
+{
+ struct wpabuf msg;
+ struct eap_method_ret iret;
+
+ os_memset(&iret, 0, sizeof(iret));
+ wpabuf_set(&msg, hdr, len);
+ *resp = data->phase2_method->process(sm, data->phase2_priv, &iret,
+ &msg);
+ if ((iret.methodState == METHOD_DONE ||
+ iret.methodState == METHOD_MAY_CONT) &&
+ (iret.decision == DECISION_UNCOND_SUCC ||
+ iret.decision == DECISION_COND_SUCC ||
+ iret.decision == DECISION_FAIL)) {
+ ret->methodState = iret.methodState;
+ ret->decision = iret.decision;
+ }
+ eap_ttlsv1_phase2_eap_finish(sm, data, ret);
+
+ return 0;
+}
+
+
+static int eap_ttls_phase2_request_eap_method(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct eap_hdr *hdr, size_t len,
+ u8 method, struct wpabuf **resp)
+{
+#ifdef EAP_TNC
+ if (data->tnc_started && data->phase2_method &&
+ data->phase2_priv && method == EAP_TYPE_TNC &&
+ data->phase2_eap_type.method == EAP_TYPE_TNC)
+ return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len,
+ resp);
+
+ if (data->ready_for_tnc && !data->tnc_started &&
+ method == EAP_TYPE_TNC) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed "
+ "EAP method");
+ data->tnc_started = 1;
+ }
+
+ if (data->tnc_started) {
+ if (data->phase2_eap_type.vendor != EAP_VENDOR_IETF ||
+ data->phase2_eap_type.method == EAP_TYPE_TNC) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Unexpected EAP "
+ "type %d for TNC", method);
+ return -1;
+ }
+
+ data->phase2_eap_type.vendor = EAP_VENDOR_IETF;
+ data->phase2_eap_type.method = method;
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected "
+ "Phase 2 EAP vendor %d method %d (TNC)",
+ data->phase2_eap_type.vendor,
+ data->phase2_eap_type.method);
+
+ if (data->phase2_type == EAP_TTLS_PHASE2_EAP)
+ eap_ttls_phase2_eap_deinit(sm, data);
+ }
+#endif /* EAP_TNC */
+
+ if (data->phase2_eap_type.vendor == EAP_VENDOR_IETF &&
+ data->phase2_eap_type.method == EAP_TYPE_NONE)
+ eap_ttls_phase2_select_eap_method(data, method);
+
+ if (method != data->phase2_eap_type.method || method == EAP_TYPE_NONE)
+ {
+ if (eap_peer_tls_phase2_nak(data->phase2_eap_types,
+ data->num_phase2_eap_types,
+ hdr, resp))
+ return -1;
+ return 0;
+ }
+
+ if (data->phase2_priv == NULL) {
+ data->phase2_method = eap_peer_get_eap_method(
+ EAP_VENDOR_IETF, method);
+ if (data->phase2_method) {
+ sm->init_phase2 = 1;
+ sm->mschapv2_full_key = 1;
+ data->phase2_priv = data->phase2_method->init(sm);
+ sm->init_phase2 = 0;
+ sm->mschapv2_full_key = 0;
+ }
+ }
+ if (data->phase2_priv == NULL || data->phase2_method == NULL) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: failed to initialize "
+ "Phase 2 EAP method %d", method);
+ return -1;
+ }
+
+ return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len, resp);
+}
+
+
+static int eap_ttls_phase2_request_eap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct eap_hdr *hdr,
+ struct wpabuf **resp)
+{
+ size_t len = be_to_host16(hdr->length);
+ u8 *pos;
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ if (len <= sizeof(struct eap_hdr)) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: too short "
+ "Phase 2 request (len=%lu)", (unsigned long) len);
+ return -1;
+ }
+ pos = (u8 *) (hdr + 1);
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP Request: type=%d", *pos);
+ switch (*pos) {
+ case EAP_TYPE_IDENTITY:
+ *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1);
+ break;
+ default:
+ if (eap_ttls_phase2_request_eap_method(sm, data, ret, hdr, len,
+ *pos, resp) < 0)
+ return -1;
+ break;
+ }
+
+ if (*resp == NULL &&
+ (config->pending_req_identity || config->pending_req_password ||
+ config->pending_req_otp)) {
+ return 0;
+ }
+
+ if (*resp == NULL)
+ return -1;
+
+ wpa_hexdump_buf(MSG_DEBUG, "EAP-TTLS: AVP encapsulate EAP Response",
+ *resp);
+ return eap_ttls_avp_encapsulate(resp, RADIUS_ATTR_EAP_MESSAGE, 1);
+}
+
+
+static void eap_ttlsv1_permute_inner(struct eap_sm *sm,
+ struct eap_ttls_data *data)
+{
+#if EAP_TTLS_VERSION > 0
+ u8 session_key[2 * MSCHAPV2_KEY_LEN];
+
+ if (data->ttls_version == 0)
+ return;
+
+ get_asymetric_start_key(data->master_key, session_key,
+ MSCHAPV2_KEY_LEN, 0, 0);
+ get_asymetric_start_key(data->master_key,
+ session_key + MSCHAPV2_KEY_LEN,
+ MSCHAPV2_KEY_LEN, 1, 0);
+ eap_ttls_ia_permute_inner_secret(sm, data, session_key,
+ sizeof(session_key));
+#endif /* EAP_TTLS_VERSION */
+}
+
+
+static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct wpabuf **resp)
+{
+ struct wpabuf *msg;
+ u8 *buf, *pos, *challenge, *peer_challenge;
+ const u8 *identity, *password;
+ size_t identity_len, password_len;
+ int pwhash;
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAPV2 Request");
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ password = eap_get_config_password2(sm, &password_len, &pwhash);
+ if (identity == NULL || password == NULL)
+ return -1;
+
+ msg = wpabuf_alloc(identity_len + 1000);
+ if (msg == NULL) {
+ wpa_printf(MSG_ERROR,
+ "EAP-TTLS/MSCHAPV2: Failed to allocate memory");
+ return -1;
+ }
+ pos = buf = wpabuf_mhead(msg);
+
+ /* User-Name */
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
+ identity, identity_len);
+
+ /* MS-CHAP-Challenge */
+ challenge = eap_ttls_implicit_challenge(
+ sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN * 2 + 1);
+ if (challenge == NULL) {
+ wpabuf_free(msg);
+ wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive "
+ "implicit challenge");
+ return -1;
+ }
+ peer_challenge = challenge + 1 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN;
+
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE,
+ RADIUS_VENDOR_ID_MICROSOFT, 1,
+ challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);
+
+ /* MS-CHAP2-Response */
+ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_RESPONSE,
+ RADIUS_VENDOR_ID_MICROSOFT, 1,
+ EAP_TTLS_MSCHAPV2_RESPONSE_LEN);
+ data->ident = challenge[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN];
+ *pos++ = data->ident;
+ *pos++ = 0; /* Flags */
+ os_memcpy(pos, peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);
+ pos += EAP_TTLS_MSCHAPV2_CHALLENGE_LEN;
+ os_memset(pos, 0, 8); /* Reserved, must be zero */
+ pos += 8;
+ mschapv2_derive_response(identity, identity_len, password,
+ password_len, pwhash, challenge,
+ peer_challenge, pos, data->auth_response,
+ data->master_key);
+ data->auth_response_valid = 1;
+
+ eap_ttlsv1_permute_inner(sm, data);
+
+ pos += 24;
+ os_free(challenge);
+ AVP_PAD(buf, pos);
+
+ wpabuf_put(msg, pos - buf);
+ *resp = msg;
+
+ if (sm->workaround && data->ttls_version == 0) {
+ /* At least FreeRADIUS seems to be terminating
+ * EAP-TTLS/MSHCAPV2 without the expected MS-CHAP-v2 Success
+ * packet. */
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: EAP workaround - "
+ "allow success without tunneled response");
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_COND_SUCC;
+ }
+
+ return 0;
+}
+
+
+static int eap_ttls_phase2_request_mschap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct wpabuf **resp)
+{
+ struct wpabuf *msg;
+ u8 *buf, *pos, *challenge;
+ const u8 *identity, *password;
+ size_t identity_len, password_len;
+ int pwhash;
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAP Request");
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ password = eap_get_config_password2(sm, &password_len, &pwhash);
+ if (identity == NULL || password == NULL)
+ return -1;
+
+ msg = wpabuf_alloc(identity_len + 1000);
+ if (msg == NULL) {
+ wpa_printf(MSG_ERROR,
+ "EAP-TTLS/MSCHAP: Failed to allocate memory");
+ return -1;
+ }
+ pos = buf = wpabuf_mhead(msg);
+
+ /* User-Name */
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
+ identity, identity_len);
+
+ /* MS-CHAP-Challenge */
+ challenge = eap_ttls_implicit_challenge(sm, data, EAP_TLS_KEY_LEN);
+ if (challenge == NULL) {
+ wpabuf_free(msg);
+ wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed to derive "
+ "implicit challenge");
+ return -1;
+ }
+
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE,
+ RADIUS_VENDOR_ID_MICROSOFT, 1,
+ challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN);
+
+ /* MS-CHAP-Response */
+ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_RESPONSE,
+ RADIUS_VENDOR_ID_MICROSOFT, 1,
+ EAP_TTLS_MSCHAP_RESPONSE_LEN);
+ data->ident = challenge[EAP_TTLS_MSCHAP_CHALLENGE_LEN];
+ *pos++ = data->ident;
+ *pos++ = 1; /* Flags: Use NT style passwords */
+ os_memset(pos, 0, 24); /* LM-Response */
+ pos += 24;
+ if (pwhash) {
+ challenge_response(challenge, password, pos); /* NT-Response */
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password hash",
+ password, 16);
+ } else {
+ nt_challenge_response(challenge, password, password_len,
+ pos); /* NT-Response */
+ wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password",
+ password, password_len);
+ }
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP implicit challenge",
+ challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP response", pos, 24);
+ pos += 24;
+ os_free(challenge);
+ AVP_PAD(buf, pos);
+
+ wpabuf_put(msg, pos - buf);
+ *resp = msg;
+
+ if (data->ttls_version > 0) {
+ /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success,
+ * so do not allow connection to be terminated yet. */
+ ret->methodState = METHOD_CONT;
+ ret->decision = DECISION_COND_SUCC;
+ } else {
+ /* EAP-TTLS/MSCHAP does not provide tunneled success
+ * notification, so assume that Phase2 succeeds. */
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_COND_SUCC;
+ }
+
+ return 0;
+}
+
+
+static int eap_ttls_phase2_request_pap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct wpabuf **resp)
+{
+ struct wpabuf *msg;
+ u8 *buf, *pos;
+ size_t pad;
+ const u8 *identity, *password;
+ size_t identity_len, password_len;
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 PAP Request");
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ password = eap_get_config_password(sm, &password_len);
+ if (identity == NULL || password == NULL)
+ return -1;
+
+ msg = wpabuf_alloc(identity_len + password_len + 100);
+ if (msg == NULL) {
+ wpa_printf(MSG_ERROR,
+ "EAP-TTLS/PAP: Failed to allocate memory");
+ return -1;
+ }
+ pos = buf = wpabuf_mhead(msg);
+
+ /* User-Name */
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
+ identity, identity_len);
+
+ /* User-Password; in RADIUS, this is encrypted, but EAP-TTLS encrypts
+ * the data, so no separate encryption is used in the AVP itself.
+ * However, the password is padded to obfuscate its length. */
+ pad = (16 - (password_len & 15)) & 15;
+ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_USER_PASSWORD, 0, 1,
+ password_len + pad);
+ os_memcpy(pos, password, password_len);
+ pos += password_len;
+ os_memset(pos, 0, pad);
+ pos += pad;
+ AVP_PAD(buf, pos);
+
+ wpabuf_put(msg, pos - buf);
+ *resp = msg;
+
+ if (data->ttls_version > 0) {
+ /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success,
+ * so do not allow connection to be terminated yet. */
+ ret->methodState = METHOD_CONT;
+ ret->decision = DECISION_COND_SUCC;
+ } else {
+ /* EAP-TTLS/PAP does not provide tunneled success notification,
+ * so assume that Phase2 succeeds. */
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_COND_SUCC;
+ }
+
+ return 0;
+}
+
+
+static int eap_ttls_phase2_request_chap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct wpabuf **resp)
+{
+ struct wpabuf *msg;
+ u8 *buf, *pos, *challenge;
+ const u8 *identity, *password;
+ size_t identity_len, password_len;
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 CHAP Request");
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ password = eap_get_config_password(sm, &password_len);
+ if (identity == NULL || password == NULL)
+ return -1;
+
+ msg = wpabuf_alloc(identity_len + 1000);
+ if (msg == NULL) {
+ wpa_printf(MSG_ERROR,
+ "EAP-TTLS/CHAP: Failed to allocate memory");
+ return -1;
+ }
+ pos = buf = wpabuf_mhead(msg);
+
+ /* User-Name */
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
+ identity, identity_len);
+
+ /* CHAP-Challenge */
+ challenge = eap_ttls_implicit_challenge(sm, data, EAP_TLS_KEY_LEN);
+ if (challenge == NULL) {
+ wpabuf_free(msg);
+ wpa_printf(MSG_ERROR, "EAP-TTLS/CHAP: Failed to derive "
+ "implicit challenge");
+ return -1;
+ }
+
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_CHAP_CHALLENGE, 0, 1,
+ challenge, EAP_TTLS_CHAP_CHALLENGE_LEN);
+
+ /* CHAP-Password */
+ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_CHAP_PASSWORD, 0, 1,
+ 1 + EAP_TTLS_CHAP_PASSWORD_LEN);
+ data->ident = challenge[EAP_TTLS_CHAP_CHALLENGE_LEN];
+ *pos++ = data->ident;
+
+ /* MD5(Ident + Password + Challenge) */
+ chap_md5(data->ident, password, password_len, challenge,
+ EAP_TTLS_CHAP_CHALLENGE_LEN, pos);
+
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: CHAP username",
+ identity, identity_len);
+ wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: CHAP password",
+ password, password_len);
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP implicit challenge",
+ challenge, EAP_TTLS_CHAP_CHALLENGE_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP password",
+ pos, EAP_TTLS_CHAP_PASSWORD_LEN);
+ pos += EAP_TTLS_CHAP_PASSWORD_LEN;
+ os_free(challenge);
+ AVP_PAD(buf, pos);
+
+ wpabuf_put(msg, pos - buf);
+ *resp = msg;
+
+ if (data->ttls_version > 0) {
+ /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success,
+ * so do not allow connection to be terminated yet. */
+ ret->methodState = METHOD_CONT;
+ ret->decision = DECISION_COND_SUCC;
+ } else {
+ /* EAP-TTLS/CHAP does not provide tunneled success
+ * notification, so assume that Phase2 succeeds. */
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_COND_SUCC;
+ }
+
+ return 0;
+}
+
+
+static int eap_ttls_phase2_request(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct eap_hdr *hdr,
+ struct wpabuf **resp)
+{
+ int res = 0;
+ size_t len;
+ enum phase2_types phase2_type = data->phase2_type;
+
+#ifdef EAP_TNC
+ if (data->tnc_started) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Processing TNC");
+ phase2_type = EAP_TTLS_PHASE2_EAP;
+ }
+#endif /* EAP_TNC */
+
+ if (phase2_type == EAP_TTLS_PHASE2_MSCHAPV2 ||
+ phase2_type == EAP_TTLS_PHASE2_MSCHAP ||
+ phase2_type == EAP_TTLS_PHASE2_PAP ||
+ phase2_type == EAP_TTLS_PHASE2_CHAP) {
+ if (eap_get_config_identity(sm, &len) == NULL) {
+ wpa_printf(MSG_INFO,
+ "EAP-TTLS: Identity not configured");
+ eap_sm_request_identity(sm);
+ if (eap_get_config_password(sm, &len) == NULL)
+ eap_sm_request_password(sm);
+ return 0;
+ }
+
+ if (eap_get_config_password(sm, &len) == NULL) {
+ wpa_printf(MSG_INFO,
+ "EAP-TTLS: Password not configured");
+ eap_sm_request_password(sm);
+ return 0;
+ }
+ }
+
+ switch (data->phase2_type) {
+ case EAP_TTLS_PHASE2_EAP:
+ res = eap_ttls_phase2_request_eap(sm, data, ret, hdr, resp);
+ break;
+ case EAP_TTLS_PHASE2_MSCHAPV2:
+ res = eap_ttls_phase2_request_mschapv2(sm, data, ret, resp);
+ break;
+ case EAP_TTLS_PHASE2_MSCHAP:
+ res = eap_ttls_phase2_request_mschap(sm, data, ret, resp);
+ break;
+ case EAP_TTLS_PHASE2_PAP:
+ res = eap_ttls_phase2_request_pap(sm, data, ret, resp);
+ break;
+ case EAP_TTLS_PHASE2_CHAP:
+ res = eap_ttls_phase2_request_chap(sm, data, ret, resp);
+ break;
+ default:
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 - Unknown");
+ res = -1;
+ break;
+ }
+
+ if (res < 0) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ }
+
+ return res;
+}
+
+
+#if EAP_TTLS_VERSION > 0
+static struct wpabuf * eap_ttls_build_phase_finished(
+ struct eap_sm *sm, struct eap_ttls_data *data, int id, int final)
+{
+ int len;
+ struct wpabuf *req;
+ u8 *pos;
+ const int max_len = 300;
+
+ req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS, 1 + max_len,
+ EAP_CODE_RESPONSE, id);
+ if (req == NULL)
+ return NULL;
+
+ wpabuf_put_u8(req, data->ttls_version);
+
+ pos = wpabuf_put(req, 0);
+ len = tls_connection_ia_send_phase_finished(sm->ssl_ctx,
+ data->ssl.conn,
+ final, pos, max_len);
+ if (len < 0) {
+ wpabuf_free(req);
+ return NULL;
+ }
+ wpabuf_put(req, len);
+ eap_update_len(req);
+
+ return req;
+}
+#endif /* EAP_TTLS_VERSION */
+
+
+struct ttls_parse_avp {
+ u8 *mschapv2;
+ u8 *eapdata;
+ size_t eap_len;
+ int mschapv2_error;
+};
+
+
+static int eap_ttls_parse_attr_eap(const u8 *dpos, size_t dlen,
+ struct ttls_parse_avp *parse)
+{
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message");
+ if (parse->eapdata == NULL) {
+ parse->eapdata = os_malloc(dlen);
+ if (parse->eapdata == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate "
+ "memory for Phase 2 EAP data");
+ return -1;
+ }
+ os_memcpy(parse->eapdata, dpos, dlen);
+ parse->eap_len = dlen;
+ } else {
+ u8 *neweap = os_realloc(parse->eapdata, parse->eap_len + dlen);
+ if (neweap == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate "
+ "memory for Phase 2 EAP data");
+ return -1;
+ }
+ os_memcpy(neweap + parse->eap_len, dpos, dlen);
+ parse->eapdata = neweap;
+ parse->eap_len += dlen;
+ }
+
+ return 0;
+}
+
+
+static int eap_ttls_parse_avp(u8 *pos, size_t left,
+ struct ttls_parse_avp *parse)
+{
+ struct ttls_avp *avp;
+ u32 avp_code, avp_length, vendor_id = 0;
+ u8 avp_flags, *dpos;
+ size_t dlen;
+
+ avp = (struct ttls_avp *) pos;
+ avp_code = be_to_host32(avp->avp_code);
+ avp_length = be_to_host32(avp->avp_length);
+ avp_flags = (avp_length >> 24) & 0xff;
+ avp_length &= 0xffffff;
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP: code=%d flags=0x%02x "
+ "length=%d", (int) avp_code, avp_flags,
+ (int) avp_length);
+
+ if (avp_length > left) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: AVP overflow "
+ "(len=%d, left=%lu) - dropped",
+ (int) avp_length, (unsigned long) left);
+ return -1;
+ }
+
+ if (avp_length < sizeof(*avp)) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid AVP length %d",
+ avp_length);
+ return -1;
+ }
+
+ dpos = (u8 *) (avp + 1);
+ dlen = avp_length - sizeof(*avp);
+ if (avp_flags & AVP_FLAGS_VENDOR) {
+ if (dlen < 4) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Vendor AVP "
+ "underflow");
+ return -1;
+ }
+ vendor_id = WPA_GET_BE32(dpos);
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d",
+ (int) vendor_id);
+ dpos += 4;
+ dlen -= 4;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: AVP data", dpos, dlen);
+
+ if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) {
+ if (eap_ttls_parse_attr_eap(dpos, dlen, parse) < 0)
+ return -1;
+ } else if (vendor_id == 0 && avp_code == RADIUS_ATTR_REPLY_MESSAGE) {
+ /* This is an optional message that can be displayed to
+ * the user. */
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: AVP - Reply-Message",
+ dpos, dlen);
+ } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
+ avp_code == RADIUS_ATTR_MS_CHAP2_SUCCESS) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MS-CHAP2-Success",
+ dpos, dlen);
+ if (dlen != 43) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Unexpected "
+ "MS-CHAP2-Success length "
+ "(len=%lu, expected 43)",
+ (unsigned long) dlen);
+ return -1;
+ }
+ parse->mschapv2 = dpos;
+ } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
+ avp_code == RADIUS_ATTR_MS_CHAP_ERROR) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MS-CHAP-Error",
+ dpos, dlen);
+ parse->mschapv2_error = 1;
+ } else if (avp_flags & AVP_FLAGS_MANDATORY) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Unsupported mandatory AVP "
+ "code %d vendor_id %d - dropped",
+ (int) avp_code, (int) vendor_id);
+ return -1;
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Ignoring unsupported AVP "
+ "code %d vendor_id %d",
+ (int) avp_code, (int) vendor_id);
+ }
+
+ return avp_length;
+}
+
+
+static int eap_ttls_parse_avps(struct wpabuf *in_decrypted,
+ struct ttls_parse_avp *parse)
+{
+ u8 *pos;
+ size_t left, pad;
+ int avp_length;
+
+ pos = wpabuf_mhead(in_decrypted);
+ left = wpabuf_len(in_decrypted);
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 AVPs", pos, left);
+ if (left < sizeof(struct ttls_avp)) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 AVP frame"
+ " len=%lu expected %lu or more - dropped",
+ (unsigned long) left,
+ (unsigned long) sizeof(struct ttls_avp));
+ return -1;
+ }
+
+ /* Parse AVPs */
+ os_memset(parse, 0, sizeof(*parse));
+
+ while (left > 0) {
+ avp_length = eap_ttls_parse_avp(pos, left, parse);
+ if (avp_length < 0)
+ return -1;
+
+ pad = (4 - (avp_length & 3)) & 3;
+ pos += avp_length + pad;
+ if (left < avp_length + pad)
+ left = 0;
+ else
+ left -= avp_length + pad;
+ }
+
+ return 0;
+}
+
+
+static u8 * eap_ttls_fake_identity_request(void)
+{
+ struct eap_hdr *hdr;
+ u8 *buf;
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: empty data in beginning of "
+ "Phase 2 - use fake EAP-Request Identity");
+ buf = os_malloc(sizeof(*hdr) + 1);
+ if (buf == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: failed to allocate "
+ "memory for fake EAP-Identity Request");
+ return NULL;
+ }
+
+ hdr = (struct eap_hdr *) buf;
+ hdr->code = EAP_CODE_REQUEST;
+ hdr->identifier = 0;
+ hdr->length = host_to_be16(sizeof(*hdr) + 1);
+ buf[sizeof(*hdr)] = EAP_TYPE_IDENTITY;
+
+ return buf;
+}
+
+
+static int eap_ttls_encrypt_response(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct wpabuf *resp, u8 identifier,
+ struct wpabuf **out_data)
+{
+ if (resp == NULL)
+ return 0;
+
+ wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS: Encrypting Phase 2 data",
+ resp);
+ if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS,
+ data->ttls_version, identifier,
+ resp, out_data)) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt a Phase 2 "
+ "frame");
+ return -1;
+ }
+ wpabuf_free(resp);
+
+ return 0;
+}
+
+
+static int eap_ttls_process_phase2_eap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct ttls_parse_avp *parse,
+ struct wpabuf **resp)
+{
+ struct eap_hdr *hdr;
+ size_t len;
+
+ if (parse->eapdata == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: No EAP Message in the "
+ "packet - dropped");
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP",
+ parse->eapdata, parse->eap_len);
+ hdr = (struct eap_hdr *) parse->eapdata;
+
+ if (parse->eap_len < sizeof(*hdr)) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 EAP "
+ "frame (len=%lu, expected %lu or more) - dropped",
+ (unsigned long) parse->eap_len,
+ (unsigned long) sizeof(*hdr));
+ return -1;
+ }
+ len = be_to_host16(hdr->length);
+ if (len > parse->eap_len) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Length mismatch in Phase 2 "
+ "EAP frame (EAP hdr len=%lu, EAP data len in "
+ "AVP=%lu)",
+ (unsigned long) len,
+ (unsigned long) parse->eap_len);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: received Phase 2: code=%d "
+ "identifier=%d length=%lu",
+ hdr->code, hdr->identifier, (unsigned long) len);
+ switch (hdr->code) {
+ case EAP_CODE_REQUEST:
+ if (eap_ttls_phase2_request(sm, data, ret, hdr, resp)) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 Request "
+ "processing failed");
+ return -1;
+ }
+ break;
+ default:
+ wpa_printf(MSG_INFO, "EAP-TTLS: Unexpected code=%d in "
+ "Phase 2 EAP header", hdr->code);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int eap_ttls_process_phase2_mschapv2(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct ttls_parse_avp *parse)
+{
+ if (parse->mschapv2_error) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Received "
+ "MS-CHAP-Error - failed");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ /* Reply with empty data to ACK error */
+ return 1;
+ }
+
+ if (parse->mschapv2 == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: no MS-CHAP2-Success AVP "
+ "received for Phase2 MSCHAPV2");
+ return -1;
+ }
+ if (parse->mschapv2[0] != data->ident) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Ident mismatch for Phase 2 "
+ "MSCHAPV2 (received Ident 0x%02x, expected 0x%02x)",
+ parse->mschapv2[0], data->ident);
+ return -1;
+ }
+ if (!data->auth_response_valid ||
+ mschapv2_verify_auth_response(data->auth_response,
+ parse->mschapv2 + 1, 42)) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid authenticator "
+ "response in Phase 2 MSCHAPV2 success request");
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 MSCHAPV2 "
+ "authentication succeeded");
+ if (data->ttls_version > 0) {
+ /*
+ * EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report
+ * success, so do not allow connection to be terminated
+ * yet.
+ */
+ ret->methodState = METHOD_CONT;
+ ret->decision = DECISION_COND_SUCC;
+ } else {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ data->phase2_success = 1;
+ }
+
+ /*
+ * Reply with empty data; authentication server will reply
+ * with EAP-Success after this.
+ */
+ return 1;
+}
+
+
+#ifdef EAP_TNC
+static int eap_ttls_process_tnc_start(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct ttls_parse_avp *parse,
+ struct wpabuf **resp)
+{
+ /* TNC uses inner EAP method after non-EAP TTLS phase 2. */
+ if (parse->eapdata == NULL) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received "
+ "unexpected tunneled data (no EAP)");
+ return -1;
+ }
+
+ if (!data->ready_for_tnc) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received "
+ "EAP after non-EAP, but not ready for TNC");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed "
+ "non-EAP method");
+ data->tnc_started = 1;
+
+ if (eap_ttls_process_phase2_eap(sm, data, ret, parse, resp) < 0)
+ return -1;
+
+ return 0;
+}
+#endif /* EAP_TNC */
+
+
+static int eap_ttls_process_decrypted(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ u8 identifier,
+ struct ttls_parse_avp *parse,
+ struct wpabuf *in_decrypted,
+ struct wpabuf **out_data)
+{
+ struct wpabuf *resp = NULL;
+ struct eap_peer_config *config = eap_get_config(sm);
+ int res;
+ enum phase2_types phase2_type = data->phase2_type;
+
+#ifdef EAP_TNC
+ if (data->tnc_started)
+ phase2_type = EAP_TTLS_PHASE2_EAP;
+#endif /* EAP_TNC */
+
+ switch (phase2_type) {
+ case EAP_TTLS_PHASE2_EAP:
+ if (eap_ttls_process_phase2_eap(sm, data, ret, parse, &resp) <
+ 0)
+ return -1;
+ break;
+ case EAP_TTLS_PHASE2_MSCHAPV2:
+ res = eap_ttls_process_phase2_mschapv2(sm, data, ret, parse);
+#ifdef EAP_TNC
+ if (res == 1 && parse->eapdata &&
+ ret->methodState == METHOD_DONE &&
+ ret->decision == DECISION_UNCOND_SUCC) {
+ /*
+ * TNC may be required as the next
+ * authentication method within the tunnel.
+ */
+ ret->methodState = METHOD_MAY_CONT;
+ data->ready_for_tnc = 1;
+ if (eap_ttls_process_tnc_start(sm, data, ret, parse,
+ &resp) == 0)
+ break;
+ }
+#endif /* EAP_TNC */
+ return res;
+ case EAP_TTLS_PHASE2_MSCHAP:
+ case EAP_TTLS_PHASE2_PAP:
+ case EAP_TTLS_PHASE2_CHAP:
+#ifdef EAP_TNC
+ if (eap_ttls_process_tnc_start(sm, data, ret, parse, &resp) <
+ 0)
+ return -1;
+ break;
+#else /* EAP_TNC */
+ /* EAP-TTLS/{MSCHAP,PAP,CHAP} should not send any TLS tunneled
+ * requests to the supplicant */
+ wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received unexpected "
+ "tunneled data");
+ return -1;
+#endif /* EAP_TNC */
+ }
+
+ if (resp) {
+ if (eap_ttls_encrypt_response(sm, data, resp, identifier,
+ out_data) < 0)
+ return -1;
+ } else if (config->pending_req_identity ||
+ config->pending_req_password ||
+ config->pending_req_otp ||
+ config->pending_req_new_password) {
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = wpabuf_dup(in_decrypted);
+ }
+
+ return 0;
+}
+
+
+#if EAP_TTLS_VERSION > 0
+static void eap_ttls_final_phase_finished(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ u8 identifier,
+ struct wpabuf **out_data)
+{
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: FinalPhaseFinished received");
+ wpa_printf(MSG_INFO, "EAP-TTLS: TLS/IA authentication succeeded");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ data->phase2_success = 1;
+ *out_data = eap_ttls_build_phase_finished(sm, data, identifier, 1);
+ eap_ttls_v1_derive_key(sm, data);
+}
+#endif /* EAP_TTLS_VERSION */
+
+
+static int eap_ttls_implicit_identity_request(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ u8 identifier,
+ struct wpabuf **out_data)
+{
+ int retval = 0;
+ struct eap_hdr *hdr;
+ struct wpabuf *resp;
+
+ hdr = (struct eap_hdr *) eap_ttls_fake_identity_request();
+ if (hdr == NULL) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return -1;
+ }
+
+ resp = NULL;
+ if (eap_ttls_phase2_request(sm, data, ret, hdr, &resp)) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 Request "
+ "processing failed");
+ retval = -1;
+ } else {
+ retval = eap_ttls_encrypt_response(sm, data, resp, identifier,
+ out_data);
+ }
+
+ os_free(hdr);
+
+ if (retval < 0) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ }
+
+ return retval;
+}
+
+
+static int eap_ttls_phase2_start(struct eap_sm *sm, struct eap_ttls_data *data,
+ struct eap_method_ret *ret, u8 identifier,
+ struct wpabuf **out_data)
+{
+ data->phase2_start = 0;
+
+ /*
+ * EAP-TTLS does not use Phase2 on fast re-auth; this must be done only
+ * if TLS part was indeed resuming a previous session. Most
+ * Authentication Servers terminate EAP-TTLS before reaching this
+ * point, but some do not. Make wpa_supplicant stop phase 2 here, if
+ * needed.
+ */
+ if (data->reauth &&
+ tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Session resumption - "
+ "skip phase 2");
+ *out_data = eap_peer_tls_build_ack(identifier, EAP_TYPE_TTLS,
+ data->ttls_version);
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ data->phase2_success = 1;
+ return 0;
+ }
+
+ return eap_ttls_implicit_identity_request(sm, data, ret, identifier,
+ out_data);
+}
+
+
+static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data,
+ struct eap_method_ret *ret, u8 identifier,
+ const struct wpabuf *in_data,
+ struct wpabuf **out_data)
+{
+ struct wpabuf *in_decrypted = NULL;
+ int retval = 0;
+ struct ttls_parse_avp parse;
+
+ os_memset(&parse, 0, sizeof(parse));
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for"
+ " Phase 2",
+ in_data ? (unsigned long) wpabuf_len(in_data) : 0);
+
+ if (data->pending_phase2_req) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Pending Phase 2 request - "
+ "skip decryption and use old data");
+ /* Clear TLS reassembly state. */
+ eap_peer_tls_reset_input(&data->ssl);
+
+ in_decrypted = data->pending_phase2_req;
+ data->pending_phase2_req = NULL;
+ if (wpabuf_len(in_decrypted) == 0) {
+ wpabuf_free(in_decrypted);
+ return eap_ttls_implicit_identity_request(
+ sm, data, ret, identifier, out_data);
+ }
+ goto continue_req;
+ }
+
+ if ((in_data == NULL || wpabuf_len(in_data) == 0) &&
+ data->phase2_start) {
+ return eap_ttls_phase2_start(sm, data, ret, identifier,
+ out_data);
+ }
+
+ if (in_data == NULL || wpabuf_len(in_data) == 0) {
+ /* Received TLS ACK - requesting more fragments */
+ return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS,
+ data->ttls_version,
+ identifier, NULL, out_data);
+ }
+
+ retval = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted);
+ if (retval)
+ goto done;
+
+#if EAP_TTLS_VERSION > 0
+ if (data->ttls_version > 0 &&
+ (in_decrypted == NULL || wpabuf_len(in_decrypted) == 0) &&
+ tls_connection_ia_final_phase_finished(sm->ssl_ctx,
+ data->ssl.conn)) {
+ eap_ttls_final_phase_finished(sm, data, ret, identifier,
+ out_data);
+ goto done;
+ }
+#endif /* EAP_TTLS_VERSION */
+
+continue_req:
+ data->phase2_start = 0;
+
+ if (eap_ttls_parse_avps(in_decrypted, &parse) < 0) {
+ retval = -1;
+ goto done;
+ }
+
+ retval = eap_ttls_process_decrypted(sm, data, ret, identifier,
+ &parse, in_decrypted, out_data);
+
+done:
+ wpabuf_free(in_decrypted);
+ os_free(parse.eapdata);
+
+ if (retval < 0) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ }
+
+ return retval;
+}
+
+
+static int eap_ttls_process_start(struct eap_sm *sm,
+ struct eap_ttls_data *data, u8 flags,
+ struct eap_method_ret *ret)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Start (server ver=%d, own ver=%d)",
+ flags & EAP_PEAP_VERSION_MASK, data->ttls_version);
+#if EAP_TTLS_VERSION > 0
+ if ((flags & EAP_PEAP_VERSION_MASK) < data->ttls_version)
+ data->ttls_version = flags & EAP_PEAP_VERSION_MASK;
+ if (data->force_ttls_version >= 0 &&
+ data->force_ttls_version != data->ttls_version) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to select "
+ "forced TTLS version %d",
+ data->force_ttls_version);
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = FALSE;
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Using TTLS version %d",
+ data->ttls_version);
+
+ if (data->ttls_version > 0)
+ data->ssl.tls_ia = 1;
+#endif /* EAP_TTLS_VERSION */
+ if (!data->ssl_initialized &&
+ eap_peer_tls_ssl_init(sm, &data->ssl, config)) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL.");
+ return -1;
+ }
+ data->ssl_initialized = 1;
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Start");
+
+ return 0;
+}
+
+
+static int eap_ttls_process_handshake(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ u8 identifier,
+ const u8 *in_data, size_t in_len,
+ struct wpabuf **out_data)
+{
+ int res;
+
+ res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TTLS,
+ data->ttls_version, identifier,
+ in_data, in_len, out_data);
+
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS done, proceed to "
+ "Phase 2");
+ if (data->resuming) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: fast reauth - may "
+ "skip Phase 2");
+ ret->decision = DECISION_COND_SUCC;
+ ret->methodState = METHOD_MAY_CONT;
+ }
+ data->phase2_start = 1;
+ if (data->ttls_version == 0)
+ eap_ttls_v0_derive_key(sm, data);
+
+ if (*out_data == NULL || wpabuf_len(*out_data) == 0) {
+ if (eap_ttls_decrypt(sm, data, ret, identifier,
+ NULL, out_data)) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: "
+ "failed to process early "
+ "start for Phase 2");
+ }
+ res = 0;
+ }
+ data->resuming = 0;
+ }
+
+ if (res == 2) {
+ struct wpabuf msg;
+ /*
+ * Application data included in the handshake message.
+ */
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = *out_data;
+ *out_data = NULL;
+ wpabuf_set(&msg, in_data, in_len);
+ res = eap_ttls_decrypt(sm, data, ret, identifier, &msg,
+ out_data);
+ }
+
+ return res;
+}
+
+
+static void eap_ttls_check_auth_status(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret)
+{
+ if (data->ttls_version == 0 && ret->methodState == METHOD_DONE) {
+ ret->allowNotifications = FALSE;
+ if (ret->decision == DECISION_UNCOND_SUCC ||
+ ret->decision == DECISION_COND_SUCC) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication "
+ "completed successfully");
+ data->phase2_success = 1;
+#ifdef EAP_TNC
+ if (!data->ready_for_tnc && !data->tnc_started) {
+ /*
+ * TNC may be required as the next
+ * authentication method within the tunnel.
+ */
+ ret->methodState = METHOD_MAY_CONT;
+ data->ready_for_tnc = 1;
+ }
+#endif /* EAP_TNC */
+ }
+ } else if (data->ttls_version == 0 &&
+ ret->methodState == METHOD_MAY_CONT &&
+ (ret->decision == DECISION_UNCOND_SUCC ||
+ ret->decision == DECISION_COND_SUCC)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication "
+ "completed successfully (MAY_CONT)");
+ data->phase2_success = 1;
+ }
+}
+
+
+static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ size_t left;
+ int res;
+ u8 flags, id;
+ struct wpabuf *resp;
+ const u8 *pos;
+ struct eap_ttls_data *data = priv;
+
+ pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TTLS, ret,
+ reqData, &left, &flags);
+ if (pos == NULL)
+ return NULL;
+ id = eap_get_id(reqData);
+
+ if (flags & EAP_TLS_FLAGS_START) {
+ if (eap_ttls_process_start(sm, data, flags, ret) < 0)
+ return NULL;
+
+ /* draft-ietf-pppext-eap-ttls-03.txt, Ch. 8.1:
+ * EAP-TTLS Start packet may, in a future specification, be
+ * allowed to contain data. Client based on this draft version
+ * must ignore such data but must not reject the Start packet.
+ */
+ left = 0;
+ } else if (!data->ssl_initialized) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: First message did not "
+ "include Start flag");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = FALSE;
+ return NULL;
+ }
+
+ resp = NULL;
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+ !data->resuming) {
+ struct wpabuf msg;
+ wpabuf_set(&msg, pos, left);
+ res = eap_ttls_decrypt(sm, data, ret, id, &msg, &resp);
+ } else {
+ res = eap_ttls_process_handshake(sm, data, ret, id,
+ pos, left, &resp);
+ }
+
+ eap_ttls_check_auth_status(sm, data, ret);
+
+ /* FIX: what about res == -1? Could just move all error processing into
+ * the other functions and get rid of this res==1 case here. */
+ if (res == 1) {
+ wpabuf_free(resp);
+ return eap_peer_tls_build_ack(id, EAP_TYPE_TTLS,
+ data->ttls_version);
+ }
+ return resp;
+}
+
+
+static Boolean eap_ttls_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+ struct eap_ttls_data *data = priv;
+ return tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+ data->phase2_success;
+}
+
+
+static void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_ttls_data *data = priv;
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = NULL;
+#ifdef EAP_TNC
+ data->ready_for_tnc = 0;
+ data->tnc_started = 0;
+#endif /* EAP_TNC */
+}
+
+
+static void * eap_ttls_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_ttls_data *data = priv;
+ os_free(data->key_data);
+ data->key_data = NULL;
+ if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
+ os_free(data);
+ return NULL;
+ }
+ if (data->phase2_priv && data->phase2_method &&
+ data->phase2_method->init_for_reauth)
+ data->phase2_method->init_for_reauth(sm, data->phase2_priv);
+ data->phase2_start = 0;
+ data->phase2_success = 0;
+ data->resuming = 1;
+ data->reauth = 1;
+ return priv;
+}
+
+
+static int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf,
+ size_t buflen, int verbose)
+{
+ struct eap_ttls_data *data = priv;
+ int len, ret;
+
+ len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);
+ ret = os_snprintf(buf + len, buflen - len,
+ "EAP-TTLSv%d Phase2 method=",
+ data->ttls_version);
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ len += ret;
+ switch (data->phase2_type) {
+ case EAP_TTLS_PHASE2_EAP:
+ ret = os_snprintf(buf + len, buflen - len, "EAP-%s\n",
+ data->phase2_method ?
+ data->phase2_method->name : "?");
+ break;
+ case EAP_TTLS_PHASE2_MSCHAPV2:
+ ret = os_snprintf(buf + len, buflen - len, "MSCHAPV2\n");
+ break;
+ case EAP_TTLS_PHASE2_MSCHAP:
+ ret = os_snprintf(buf + len, buflen - len, "MSCHAP\n");
+ break;
+ case EAP_TTLS_PHASE2_PAP:
+ ret = os_snprintf(buf + len, buflen - len, "PAP\n");
+ break;
+ case EAP_TTLS_PHASE2_CHAP:
+ ret = os_snprintf(buf + len, buflen - len, "CHAP\n");
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ len += ret;
+
+ return len;
+}
+
+
+static Boolean eap_ttls_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_ttls_data *data = priv;
+ return data->key_data != NULL && data->phase2_success;
+}
+
+
+static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_ttls_data *data = priv;
+ u8 *key;
+
+ if (data->key_data == NULL || !data->phase2_success)
+ return NULL;
+
+ key = os_malloc(EAP_TLS_KEY_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_TLS_KEY_LEN;
+ os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
+
+ return key;
+}
+
+
+int eap_peer_ttls_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_TTLS, "TTLS");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_ttls_init;
+ eap->deinit = eap_ttls_deinit;
+ eap->process = eap_ttls_process;
+ eap->isKeyAvailable = eap_ttls_isKeyAvailable;
+ eap->getKey = eap_ttls_getKey;
+ eap->get_status = eap_ttls_get_status;
+ eap->has_reauth_data = eap_ttls_has_reauth_data;
+ eap->deinit_for_reauth = eap_ttls_deinit_for_reauth;
+ eap->init_for_reauth = eap_ttls_init_for_reauth;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_vendor_test.c b/src/eap_peer/eap_vendor_test.c
new file mode 100644
index 0000000..3e114c1
--- /dev/null
+++ b/src/eap_peer/eap_vendor_test.c
@@ -0,0 +1,195 @@
+/*
+ * EAP peer method: Test method for vendor specific (expanded) EAP type
+ * Copyright (c) 2005-2006, 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 file implements a vendor specific test method using EAP expanded types.
+ * This is only for test use and must not be used for authentication since no
+ * security is provided.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#ifdef TEST_PENDING_REQUEST
+#include "eloop.h"
+#endif /* TEST_PENDING_REQUEST */
+
+
+#define EAP_VENDOR_ID 0xfffefd
+#define EAP_VENDOR_TYPE 0xfcfbfaf9
+
+
+/* #define TEST_PENDING_REQUEST */
+
+struct eap_vendor_test_data {
+ enum { INIT, CONFIRM, SUCCESS } state;
+ int first_try;
+};
+
+
+static void * eap_vendor_test_init(struct eap_sm *sm)
+{
+ struct eap_vendor_test_data *data;
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->state = INIT;
+ data->first_try = 1;
+ return data;
+}
+
+
+static void eap_vendor_test_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_vendor_test_data *data = priv;
+ os_free(data);
+}
+
+
+#ifdef TEST_PENDING_REQUEST
+static void eap_vendor_ready(void *eloop_ctx, void *timeout_ctx)
+{
+ struct eap_sm *sm = eloop_ctx;
+ wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Ready to re-process pending "
+ "request");
+ eap_notify_pending(sm);
+}
+#endif /* TEST_PENDING_REQUEST */
+
+
+static struct wpabuf * eap_vendor_test_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct eap_vendor_test_data *data = priv;
+ struct wpabuf *resp;
+ const u8 *pos;
+ size_t len;
+
+ pos = eap_hdr_validate(EAP_VENDOR_ID, EAP_VENDOR_TYPE, reqData, &len);
+ if (pos == NULL || len < 1) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (data->state == INIT && *pos != 1) {
+ wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Unexpected message "
+ "%d in INIT state", *pos);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (data->state == CONFIRM && *pos != 3) {
+ wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Unexpected message "
+ "%d in CONFIRM state", *pos);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (data->state == SUCCESS) {
+ wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Unexpected message "
+ "in SUCCESS state");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (data->state == CONFIRM) {
+#ifdef TEST_PENDING_REQUEST
+ if (data->first_try) {
+ data->first_try = 0;
+ wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Testing "
+ "pending request");
+ ret->ignore = TRUE;
+ eloop_register_timeout(1, 0, eap_vendor_ready, sm,
+ NULL);
+ return NULL;
+ }
+#endif /* TEST_PENDING_REQUEST */
+ }
+
+ ret->ignore = FALSE;
+
+ wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Generating Response");
+ ret->allowNotifications = TRUE;
+
+ resp = eap_msg_alloc(EAP_VENDOR_ID, EAP_VENDOR_TYPE, 1,
+ EAP_CODE_RESPONSE, eap_get_id(reqData));
+ if (resp == NULL)
+ return NULL;
+
+ if (data->state == INIT) {
+ wpabuf_put_u8(resp, 2);
+ data->state = CONFIRM;
+ ret->methodState = METHOD_CONT;
+ ret->decision = DECISION_FAIL;
+ } else {
+ wpabuf_put_u8(resp, 4);
+ data->state = SUCCESS;
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ }
+
+ return resp;
+}
+
+
+static Boolean eap_vendor_test_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_vendor_test_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+static u8 * eap_vendor_test_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_vendor_test_data *data = priv;
+ u8 *key;
+ const int key_len = 64;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(key_len);
+ if (key == NULL)
+ return NULL;
+
+ os_memset(key, 0x11, key_len / 2);
+ os_memset(key + key_len / 2, 0x22, key_len / 2);
+ *len = key_len;
+
+ return key;
+}
+
+
+int eap_peer_vendor_test_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_ID, EAP_VENDOR_TYPE,
+ "VENDOR-TEST");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_vendor_test_init;
+ eap->deinit = eap_vendor_test_deinit;
+ eap->process = eap_vendor_test_process;
+ eap->isKeyAvailable = eap_vendor_test_isKeyAvailable;
+ eap->getKey = eap_vendor_test_getKey;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/ikev2.c b/src/eap_peer/ikev2.c
new file mode 100644
index 0000000..9172e1f
--- /dev/null
+++ b/src/eap_peer/ikev2.c
@@ -0,0 +1,1303 @@
+/*
+ * IKEv2 responder (RFC 4306) for EAP-IKEV2
+ * Copyright (c) 2007, 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "dh_groups.h"
+#include "ikev2.h"
+
+
+void ikev2_responder_deinit(struct ikev2_responder_data *data)
+{
+ ikev2_free_keys(&data->keys);
+ wpabuf_free(data->i_dh_public);
+ wpabuf_free(data->r_dh_private);
+ os_free(data->IDi);
+ os_free(data->IDr);
+ os_free(data->shared_secret);
+ wpabuf_free(data->i_sign_msg);
+ wpabuf_free(data->r_sign_msg);
+ os_free(data->key_pad);
+}
+
+
+static int ikev2_derive_keys(struct ikev2_responder_data *data)
+{
+ u8 *buf, *pos, *pad, skeyseed[IKEV2_MAX_HASH_LEN];
+ size_t buf_len, pad_len;
+ struct wpabuf *shared;
+ const struct ikev2_integ_alg *integ;
+ const struct ikev2_prf_alg *prf;
+ const struct ikev2_encr_alg *encr;
+ int ret;
+ const u8 *addr[2];
+ size_t len[2];
+
+ /* RFC 4306, Sect. 2.14 */
+
+ integ = ikev2_get_integ(data->proposal.integ);
+ prf = ikev2_get_prf(data->proposal.prf);
+ encr = ikev2_get_encr(data->proposal.encr);
+ if (integ == NULL || prf == NULL || encr == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: Unsupported proposal");
+ return -1;
+ }
+
+ shared = dh_derive_shared(data->i_dh_public, data->r_dh_private,
+ data->dh);
+ if (shared == NULL)
+ return -1;
+
+ /* Construct Ni | Nr | SPIi | SPIr */
+
+ buf_len = data->i_nonce_len + data->r_nonce_len + 2 * IKEV2_SPI_LEN;
+ buf = os_malloc(buf_len);
+ if (buf == NULL) {
+ wpabuf_free(shared);
+ return -1;
+ }
+
+ pos = buf;
+ os_memcpy(pos, data->i_nonce, data->i_nonce_len);
+ pos += data->i_nonce_len;
+ os_memcpy(pos, data->r_nonce, data->r_nonce_len);
+ pos += data->r_nonce_len;
+ os_memcpy(pos, data->i_spi, IKEV2_SPI_LEN);
+ pos += IKEV2_SPI_LEN;
+ os_memcpy(pos, data->r_spi, IKEV2_SPI_LEN);
+#ifdef CCNS_PL
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ {
+ int i;
+ u8 *tmp = pos - IKEV2_SPI_LEN;
+ /* Incorrect byte re-ordering on little endian hosts.. */
+ for (i = 0; i < IKEV2_SPI_LEN; i++)
+ *tmp++ = data->i_spi[IKEV2_SPI_LEN - 1 - i];
+ for (i = 0; i < IKEV2_SPI_LEN; i++)
+ *tmp++ = data->r_spi[IKEV2_SPI_LEN - 1 - i];
+ }
+#endif
+#endif /* CCNS_PL */
+
+ /* SKEYSEED = prf(Ni | Nr, g^ir) */
+ /* Use zero-padding per RFC 4306, Sect. 2.14 */
+ pad_len = data->dh->prime_len - wpabuf_len(shared);
+#ifdef CCNS_PL
+ /* Shared secret is not zero-padded correctly */
+ pad_len = 0;
+#endif /* CCNS_PL */
+ pad = os_zalloc(pad_len ? pad_len : 1);
+ if (pad == NULL) {
+ wpabuf_free(shared);
+ os_free(buf);
+ return -1;
+ }
+
+ addr[0] = pad;
+ len[0] = pad_len;
+ addr[1] = wpabuf_head(shared);
+ len[1] = wpabuf_len(shared);
+ if (ikev2_prf_hash(prf->id, buf, data->i_nonce_len + data->r_nonce_len,
+ 2, addr, len, skeyseed) < 0) {
+ wpabuf_free(shared);
+ os_free(buf);
+ os_free(pad);
+ return -1;
+ }
+ os_free(pad);
+ wpabuf_free(shared);
+
+ /* DH parameters are not needed anymore, so free them */
+ wpabuf_free(data->i_dh_public);
+ data->i_dh_public = NULL;
+ wpabuf_free(data->r_dh_private);
+ data->r_dh_private = NULL;
+
+ wpa_hexdump_key(MSG_DEBUG, "IKEV2: SKEYSEED",
+ skeyseed, prf->hash_len);
+
+ ret = ikev2_derive_sk_keys(prf, integ, encr, skeyseed, buf, buf_len,
+ &data->keys);
+ os_free(buf);
+ return ret;
+}
+
+
+static int ikev2_parse_transform(struct ikev2_proposal_data *prop,
+ const u8 *pos, const u8 *end)
+{
+ int transform_len;
+ const struct ikev2_transform *t;
+ u16 transform_id;
+ const u8 *tend;
+
+ if (end - pos < (int) sizeof(*t)) {
+ wpa_printf(MSG_INFO, "IKEV2: Too short transform");
+ return -1;
+ }
+
+ t = (const struct ikev2_transform *) pos;
+ transform_len = WPA_GET_BE16(t->transform_length);
+ if (transform_len < (int) sizeof(*t) || pos + transform_len > end) {
+ wpa_printf(MSG_INFO, "IKEV2: Invalid transform length %d",
+ transform_len);
+ return -1;
+ }
+ tend = pos + transform_len;
+
+ transform_id = WPA_GET_BE16(t->transform_id);
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Transform:");
+ wpa_printf(MSG_DEBUG, "IKEV2: Type: %d Transform Length: %d "
+ "Transform Type: %d Transform ID: %d",
+ t->type, transform_len, t->transform_type, transform_id);
+
+ if (t->type != 0 && t->type != 3) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected Transform type");
+ return -1;
+ }
+
+ pos = (const u8 *) (t + 1);
+ if (pos < tend) {
+ wpa_hexdump(MSG_DEBUG, "IKEV2: Transform Attributes",
+ pos, tend - pos);
+ }
+
+ switch (t->transform_type) {
+ case IKEV2_TRANSFORM_ENCR:
+ if (ikev2_get_encr(transform_id)) {
+ if (transform_id == ENCR_AES_CBC) {
+ if (tend - pos != 4) {
+ wpa_printf(MSG_DEBUG, "IKEV2: No "
+ "Transform Attr for AES");
+ break;
+ }
+#ifdef CCNS_PL
+ if (WPA_GET_BE16(pos) != 0x001d /* ?? */) {
+ wpa_printf(MSG_DEBUG, "IKEV2: Not a "
+ "Key Size attribute for "
+ "AES");
+ break;
+ }
+#else /* CCNS_PL */
+ if (WPA_GET_BE16(pos) != 0x800e) {
+ wpa_printf(MSG_DEBUG, "IKEV2: Not a "
+ "Key Size attribute for "
+ "AES");
+ break;
+ }
+#endif /* CCNS_PL */
+ if (WPA_GET_BE16(pos + 2) != 128) {
+ wpa_printf(MSG_DEBUG, "IKEV2: "
+ "Unsupported AES key size "
+ "%d bits",
+ WPA_GET_BE16(pos + 2));
+ break;
+ }
+ }
+ prop->encr = transform_id;
+ }
+ break;
+ case IKEV2_TRANSFORM_PRF:
+ if (ikev2_get_prf(transform_id))
+ prop->prf = transform_id;
+ break;
+ case IKEV2_TRANSFORM_INTEG:
+ if (ikev2_get_integ(transform_id))
+ prop->integ = transform_id;
+ break;
+ case IKEV2_TRANSFORM_DH:
+ if (dh_groups_get(transform_id))
+ prop->dh = transform_id;
+ break;
+ }
+
+ return transform_len;
+}
+
+
+static int ikev2_parse_proposal(struct ikev2_proposal_data *prop,
+ const u8 *pos, const u8 *end)
+{
+ const u8 *pend, *ppos;
+ int proposal_len, i;
+ const struct ikev2_proposal *p;
+
+ if (end - pos < (int) sizeof(*p)) {
+ wpa_printf(MSG_INFO, "IKEV2: Too short proposal");
+ return -1;
+ }
+
+ /* FIX: AND processing if multiple proposals use the same # */
+
+ p = (const struct ikev2_proposal *) pos;
+ proposal_len = WPA_GET_BE16(p->proposal_length);
+ if (proposal_len < (int) sizeof(*p) || pos + proposal_len > end) {
+ wpa_printf(MSG_INFO, "IKEV2: Invalid proposal length %d",
+ proposal_len);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "IKEV2: SAi1 Proposal # %d",
+ p->proposal_num);
+ wpa_printf(MSG_DEBUG, "IKEV2: Type: %d Proposal Length: %d "
+ " Protocol ID: %d",
+ p->type, proposal_len, p->protocol_id);
+ wpa_printf(MSG_DEBUG, "IKEV2: SPI Size: %d Transforms: %d",
+ p->spi_size, p->num_transforms);
+
+ if (p->type != 0 && p->type != 2) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected Proposal type");
+ return -1;
+ }
+
+ if (p->protocol_id != IKEV2_PROTOCOL_IKE) {
+ wpa_printf(MSG_DEBUG, "IKEV2: Unexpected Protocol ID "
+ "(only IKE allowed for EAP-IKEv2)");
+ return -1;
+ }
+
+ if (p->proposal_num != prop->proposal_num) {
+ if (p->proposal_num == prop->proposal_num + 1)
+ prop->proposal_num = p->proposal_num;
+ else {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected Proposal #");
+ return -1;
+ }
+ }
+
+ ppos = (const u8 *) (p + 1);
+ pend = pos + proposal_len;
+ if (ppos + p->spi_size > pend) {
+ wpa_printf(MSG_INFO, "IKEV2: Not enough room for SPI "
+ "in proposal");
+ return -1;
+ }
+ if (p->spi_size) {
+ wpa_hexdump(MSG_DEBUG, "IKEV2: SPI",
+ ppos, p->spi_size);
+ ppos += p->spi_size;
+ }
+
+ /*
+ * For initial IKE_SA negotiation, SPI Size MUST be zero; for
+ * subsequent negotiations, it must be 8 for IKE. We only support
+ * initial case for now.
+ */
+ if (p->spi_size != 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected SPI Size");
+ return -1;
+ }
+
+ if (p->num_transforms == 0) {
+ wpa_printf(MSG_INFO, "IKEV2: At least one transform required");
+ return -1;
+ }
+
+ for (i = 0; i < (int) p->num_transforms; i++) {
+ int tlen = ikev2_parse_transform(prop, ppos, pend);
+ if (tlen < 0)
+ return -1;
+ ppos += tlen;
+ }
+
+ if (ppos != pend) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected data after "
+ "transforms");
+ return -1;
+ }
+
+ return proposal_len;
+}
+
+
+static int ikev2_process_sai1(struct ikev2_responder_data *data,
+ const u8 *sai1, size_t sai1_len)
+{
+ struct ikev2_proposal_data prop;
+ const u8 *pos, *end;
+ int found = 0;
+
+ /* Security Association Payloads: <Proposals> */
+
+ if (sai1 == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: SAi1 not received");
+ return -1;
+ }
+
+ os_memset(&prop, 0, sizeof(prop));
+ prop.proposal_num = 1;
+
+ pos = sai1;
+ end = sai1 + sai1_len;
+
+ while (pos < end) {
+ int plen;
+
+ prop.integ = -1;
+ prop.prf = -1;
+ prop.encr = -1;
+ prop.dh = -1;
+ plen = ikev2_parse_proposal(&prop, pos, end);
+ if (plen < 0)
+ return -1;
+
+ if (!found && prop.integ != -1 && prop.prf != -1 &&
+ prop.encr != -1 && prop.dh != -1) {
+ os_memcpy(&data->proposal, &prop, sizeof(prop));
+ data->dh = dh_groups_get(prop.dh);
+ found = 1;
+ }
+
+ pos += plen;
+ }
+
+ if (pos != end) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected data after proposals");
+ return -1;
+ }
+
+ if (!found) {
+ wpa_printf(MSG_INFO, "IKEV2: No acceptable proposal found");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Accepted proposal #%d: ENCR:%d PRF:%d "
+ "INTEG:%d D-H:%d", data->proposal.proposal_num,
+ data->proposal.encr, data->proposal.prf,
+ data->proposal.integ, data->proposal.dh);
+
+ return 0;
+}
+
+
+static int ikev2_process_kei(struct ikev2_responder_data *data,
+ const u8 *kei, size_t kei_len)
+{
+ u16 group;
+
+ /*
+ * Key Exchange Payload:
+ * DH Group # (16 bits)
+ * RESERVED (16 bits)
+ * Key Exchange Data (Diffie-Hellman public value)
+ */
+
+ if (kei == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: KEi not received");
+ return -1;
+ }
+
+ if (kei_len < 4 + 96) {
+ wpa_printf(MSG_INFO, "IKEV2: Too show Key Exchange Payload");
+ return -1;
+ }
+
+ group = WPA_GET_BE16(kei);
+ wpa_printf(MSG_DEBUG, "IKEV2: KEi DH Group #%u", group);
+
+ if (group != data->proposal.dh) {
+ wpa_printf(MSG_DEBUG, "IKEV2: KEi DH Group #%u does not match "
+ "with the selected proposal (%u)",
+ group, data->proposal.dh);
+ /* Reject message with Notify payload of type
+ * INVALID_KE_PAYLOAD (RFC 4306, Sect. 3.4) */
+ data->error_type = INVALID_KE_PAYLOAD;
+ data->state = NOTIFY;
+ return -1;
+ }
+
+ if (data->dh == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: Unsupported DH group");
+ return -1;
+ }
+
+ /* RFC 4306, Section 3.4:
+ * The length of DH public value MUST be equal to the lenght of the
+ * prime modulus.
+ */
+ if (kei_len - 4 != data->dh->prime_len) {
+ wpa_printf(MSG_INFO, "IKEV2: Invalid DH public value length "
+ "%ld (expected %ld)",
+ (long) (kei_len - 4), (long) data->dh->prime_len);
+ return -1;
+ }
+
+ wpabuf_free(data->i_dh_public);
+ data->i_dh_public = wpabuf_alloc(kei_len - 4);
+ if (data->i_dh_public == NULL)
+ return -1;
+ wpabuf_put_data(data->i_dh_public, kei + 4, kei_len - 4);
+
+ wpa_hexdump_buf(MSG_DEBUG, "IKEV2: KEi Diffie-Hellman Public Value",
+ data->i_dh_public);
+
+ return 0;
+}
+
+
+static int ikev2_process_ni(struct ikev2_responder_data *data,
+ const u8 *ni, size_t ni_len)
+{
+ if (ni == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: Ni not received");
+ return -1;
+ }
+
+ if (ni_len < IKEV2_NONCE_MIN_LEN || ni_len > IKEV2_NONCE_MAX_LEN) {
+ wpa_printf(MSG_INFO, "IKEV2: Invalid Ni length %ld",
+ (long) ni_len);
+ return -1;
+ }
+
+#ifdef CCNS_PL
+ /* Zeros are removed incorrectly from the beginning of the nonces */
+ while (ni_len > 1 && *ni == 0) {
+ ni_len--;
+ ni++;
+ }
+#endif /* CCNS_PL */
+
+ data->i_nonce_len = ni_len;
+ os_memcpy(data->i_nonce, ni, ni_len);
+ wpa_hexdump(MSG_MSGDUMP, "IKEV2: Ni",
+ data->i_nonce, data->i_nonce_len);
+
+ return 0;
+}
+
+
+static int ikev2_process_sa_init(struct ikev2_responder_data *data,
+ const struct ikev2_hdr *hdr,
+ struct ikev2_payloads *pl)
+{
+ if (ikev2_process_sai1(data, pl->sa, pl->sa_len) < 0 ||
+ ikev2_process_kei(data, pl->ke, pl->ke_len) < 0 ||
+ ikev2_process_ni(data, pl->nonce, pl->nonce_len) < 0)
+ return -1;
+
+ os_memcpy(data->i_spi, hdr->i_spi, IKEV2_SPI_LEN);
+
+ return 0;
+}
+
+
+static int ikev2_process_idi(struct ikev2_responder_data *data,
+ const u8 *idi, size_t idi_len)
+{
+ u8 id_type;
+
+ if (idi == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: No IDi received");
+ return -1;
+ }
+
+ if (idi_len < 4) {
+ wpa_printf(MSG_INFO, "IKEV2: Too short IDi payload");
+ return -1;
+ }
+
+ id_type = idi[0];
+ idi += 4;
+ idi_len -= 4;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: IDi ID Type %d", id_type);
+ wpa_hexdump_ascii(MSG_DEBUG, "IKEV2: IDi", idi, idi_len);
+ os_free(data->IDi);
+ data->IDi = os_malloc(idi_len);
+ if (data->IDi == NULL)
+ return -1;
+ os_memcpy(data->IDi, idi, idi_len);
+ data->IDi_len = idi_len;
+ data->IDi_type = id_type;
+
+ return 0;
+}
+
+
+static int ikev2_process_cert(struct ikev2_responder_data *data,
+ const u8 *cert, size_t cert_len)
+{
+ u8 cert_encoding;
+
+ if (cert == NULL) {
+ if (data->peer_auth == PEER_AUTH_CERT) {
+ wpa_printf(MSG_INFO, "IKEV2: No Certificate received");
+ return -1;
+ }
+ return 0;
+ }
+
+ if (cert_len < 1) {
+ wpa_printf(MSG_INFO, "IKEV2: No Cert Encoding field");
+ return -1;
+ }
+
+ cert_encoding = cert[0];
+ cert++;
+ cert_len--;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Cert Encoding %d", cert_encoding);
+ wpa_hexdump(MSG_MSGDUMP, "IKEV2: Certificate Data", cert, cert_len);
+
+ /* TODO: validate certificate */
+
+ return 0;
+}
+
+
+static int ikev2_process_auth_cert(struct ikev2_responder_data *data,
+ u8 method, const u8 *auth, size_t auth_len)
+{
+ if (method != AUTH_RSA_SIGN) {
+ wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication "
+ "method %d", method);
+ return -1;
+ }
+
+ /* TODO: validate AUTH */
+ return 0;
+}
+
+
+static int ikev2_process_auth_secret(struct ikev2_responder_data *data,
+ u8 method, const u8 *auth,
+ size_t auth_len)
+{
+ u8 auth_data[IKEV2_MAX_HASH_LEN];
+ const struct ikev2_prf_alg *prf;
+
+ if (method != AUTH_SHARED_KEY_MIC) {
+ wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication "
+ "method %d", method);
+ return -1;
+ }
+
+ /* msg | Nr | prf(SK_pi,IDi') */
+ if (ikev2_derive_auth_data(data->proposal.prf, data->i_sign_msg,
+ data->IDi, data->IDi_len, data->IDi_type,
+ &data->keys, 1, data->shared_secret,
+ data->shared_secret_len,
+ data->r_nonce, data->r_nonce_len,
+ data->key_pad, data->key_pad_len,
+ auth_data) < 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data");
+ return -1;
+ }
+
+ wpabuf_free(data->i_sign_msg);
+ data->i_sign_msg = NULL;
+
+ prf = ikev2_get_prf(data->proposal.prf);
+ if (prf == NULL)
+ return -1;
+
+ if (auth_len != prf->hash_len ||
+ os_memcmp(auth, auth_data, auth_len) != 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Invalid Authentication Data");
+ wpa_hexdump(MSG_DEBUG, "IKEV2: Received Authentication Data",
+ auth, auth_len);
+ wpa_hexdump(MSG_DEBUG, "IKEV2: Expected Authentication Data",
+ auth_data, prf->hash_len);
+ data->error_type = AUTHENTICATION_FAILED;
+ data->state = NOTIFY;
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Server authenticated successfully "
+ "using shared keys");
+
+ return 0;
+}
+
+
+static int ikev2_process_auth(struct ikev2_responder_data *data,
+ const u8 *auth, size_t auth_len)
+{
+ u8 auth_method;
+
+ if (auth == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: No Authentication Payload");
+ return -1;
+ }
+
+ if (auth_len < 4) {
+ wpa_printf(MSG_INFO, "IKEV2: Too short Authentication "
+ "Payload");
+ return -1;
+ }
+
+ auth_method = auth[0];
+ auth += 4;
+ auth_len -= 4;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Auth Method %d", auth_method);
+ wpa_hexdump(MSG_MSGDUMP, "IKEV2: Authentication Data", auth, auth_len);
+
+ switch (data->peer_auth) {
+ case PEER_AUTH_CERT:
+ return ikev2_process_auth_cert(data, auth_method, auth,
+ auth_len);
+ case PEER_AUTH_SECRET:
+ return ikev2_process_auth_secret(data, auth_method, auth,
+ auth_len);
+ }
+
+ return -1;
+}
+
+
+static int ikev2_process_sa_auth_decrypted(struct ikev2_responder_data *data,
+ u8 next_payload,
+ u8 *payload, size_t payload_len)
+{
+ struct ikev2_payloads pl;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Processing decrypted payloads");
+
+ if (ikev2_parse_payloads(&pl, next_payload, payload, payload +
+ payload_len) < 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Failed to parse decrypted "
+ "payloads");
+ return -1;
+ }
+
+ if (ikev2_process_idi(data, pl.idi, pl.idi_len) < 0 ||
+ ikev2_process_cert(data, pl.cert, pl.cert_len) < 0 ||
+ ikev2_process_auth(data, pl.auth, pl.auth_len) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+static int ikev2_process_sa_auth(struct ikev2_responder_data *data,
+ const struct ikev2_hdr *hdr,
+ struct ikev2_payloads *pl)
+{
+ u8 *decrypted;
+ size_t decrypted_len;
+ int ret;
+
+ decrypted = ikev2_decrypt_payload(data->proposal.encr,
+ data->proposal.integ,
+ &data->keys, 1, hdr, pl->encrypted,
+ pl->encrypted_len, &decrypted_len);
+ if (decrypted == NULL)
+ return -1;
+
+ ret = ikev2_process_sa_auth_decrypted(data, pl->encr_next_payload,
+ decrypted, decrypted_len);
+ os_free(decrypted);
+
+ return ret;
+}
+
+
+static int ikev2_validate_rx_state(struct ikev2_responder_data *data,
+ u8 exchange_type, u32 message_id)
+{
+ switch (data->state) {
+ case SA_INIT:
+ /* Expect to receive IKE_SA_INIT: HDR, SAi1, KEi, Ni */
+ if (exchange_type != IKE_SA_INIT) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type "
+ "%u in SA_INIT state", exchange_type);
+ return -1;
+ }
+ if (message_id != 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u "
+ "in SA_INIT state", message_id);
+ return -1;
+ }
+ break;
+ case SA_AUTH:
+ /* Expect to receive IKE_SA_AUTH:
+ * HDR, SK {IDi, [CERT,] [CERTREQ,] [IDr,]
+ * AUTH, SAi2, TSi, TSr}
+ */
+ if (exchange_type != IKE_SA_AUTH) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type "
+ "%u in SA_AUTH state", exchange_type);
+ return -1;
+ }
+ if (message_id != 1) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u "
+ "in SA_AUTH state", message_id);
+ return -1;
+ }
+ break;
+ case CHILD_SA:
+ if (exchange_type != CREATE_CHILD_SA) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type "
+ "%u in CHILD_SA state", exchange_type);
+ return -1;
+ }
+ if (message_id != 2) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u "
+ "in CHILD_SA state", message_id);
+ return -1;
+ }
+ break;
+ case NOTIFY:
+ case IKEV2_DONE:
+ case IKEV2_FAILED:
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int ikev2_responder_process(struct ikev2_responder_data *data,
+ const struct wpabuf *buf)
+{
+ const struct ikev2_hdr *hdr;
+ u32 length, message_id;
+ const u8 *pos, *end;
+ struct ikev2_payloads pl;
+
+ wpa_printf(MSG_MSGDUMP, "IKEV2: Received message (len %lu)",
+ (unsigned long) wpabuf_len(buf));
+
+ if (wpabuf_len(buf) < sizeof(*hdr)) {
+ wpa_printf(MSG_INFO, "IKEV2: Too short frame to include HDR");
+ return -1;
+ }
+
+ data->error_type = 0;
+ hdr = (const struct ikev2_hdr *) wpabuf_head(buf);
+ end = wpabuf_head_u8(buf) + wpabuf_len(buf);
+ message_id = WPA_GET_BE32(hdr->message_id);
+ length = WPA_GET_BE32(hdr->length);
+
+ wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Initiator's SPI",
+ hdr->i_spi, IKEV2_SPI_LEN);
+ wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Responder's SPI",
+ hdr->r_spi, IKEV2_SPI_LEN);
+ wpa_printf(MSG_DEBUG, "IKEV2: Next Payload: %u Version: 0x%x "
+ "Exchange Type: %u",
+ hdr->next_payload, hdr->version, hdr->exchange_type);
+ wpa_printf(MSG_DEBUG, "IKEV2: Message ID: %u Length: %u",
+ message_id, length);
+
+ if (hdr->version != IKEV2_VERSION) {
+ wpa_printf(MSG_INFO, "IKEV2: Unsupported HDR version 0x%x "
+ "(expected 0x%x)", hdr->version, IKEV2_VERSION);
+ return -1;
+ }
+
+ if (length != wpabuf_len(buf)) {
+ wpa_printf(MSG_INFO, "IKEV2: Invalid length (HDR: %lu != "
+ "RX: %lu)", (unsigned long) length,
+ (unsigned long) wpabuf_len(buf));
+ return -1;
+ }
+
+ if (ikev2_validate_rx_state(data, hdr->exchange_type, message_id) < 0)
+ return -1;
+
+ if ((hdr->flags & (IKEV2_HDR_INITIATOR | IKEV2_HDR_RESPONSE)) !=
+ IKEV2_HDR_INITIATOR) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected Flags value 0x%x",
+ hdr->flags);
+ return -1;
+ }
+
+ if (data->state != SA_INIT) {
+ if (os_memcmp(data->i_spi, hdr->i_spi, IKEV2_SPI_LEN) != 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA "
+ "Initiator's SPI");
+ return -1;
+ }
+ if (os_memcmp(data->r_spi, hdr->r_spi, IKEV2_SPI_LEN) != 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA "
+ "Responder's SPI");
+ return -1;
+ }
+ }
+
+ pos = (const u8 *) (hdr + 1);
+ if (ikev2_parse_payloads(&pl, hdr->next_payload, pos, end) < 0)
+ return -1;
+
+ if (data->state == SA_INIT) {
+ data->last_msg = LAST_MSG_SA_INIT;
+ if (ikev2_process_sa_init(data, hdr, &pl) < 0) {
+ if (data->state == NOTIFY)
+ return 0;
+ return -1;
+ }
+ wpabuf_free(data->i_sign_msg);
+ data->i_sign_msg = wpabuf_dup(buf);
+ }
+
+ if (data->state == SA_AUTH) {
+ data->last_msg = LAST_MSG_SA_AUTH;
+ if (ikev2_process_sa_auth(data, hdr, &pl) < 0) {
+ if (data->state == NOTIFY)
+ return 0;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+static void ikev2_build_hdr(struct ikev2_responder_data *data,
+ struct wpabuf *msg, u8 exchange_type,
+ u8 next_payload, u32 message_id)
+{
+ struct ikev2_hdr *hdr;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Adding HDR");
+
+ /* HDR - RFC 4306, Sect. 3.1 */
+ hdr = wpabuf_put(msg, sizeof(*hdr));
+ os_memcpy(hdr->i_spi, data->i_spi, IKEV2_SPI_LEN);
+ os_memcpy(hdr->r_spi, data->r_spi, IKEV2_SPI_LEN);
+ hdr->next_payload = next_payload;
+ hdr->version = IKEV2_VERSION;
+ hdr->exchange_type = exchange_type;
+ hdr->flags = IKEV2_HDR_RESPONSE;
+ WPA_PUT_BE32(hdr->message_id, message_id);
+}
+
+
+static int ikev2_build_sar1(struct ikev2_responder_data *data,
+ struct wpabuf *msg, u8 next_payload)
+{
+ struct ikev2_payload_hdr *phdr;
+ size_t plen;
+ struct ikev2_proposal *p;
+ struct ikev2_transform *t;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Adding SAr1 payload");
+
+ /* SAr1 - RFC 4306, Sect. 2.7 and 3.3 */
+ phdr = wpabuf_put(msg, sizeof(*phdr));
+ phdr->next_payload = next_payload;
+ phdr->flags = 0;
+
+ p = wpabuf_put(msg, sizeof(*p));
+#ifdef CCNS_PL
+ /* Seems to require that the Proposal # is 1 even though RFC 4306
+ * Sect 3.3.1 has following requirement "When a proposal is accepted,
+ * all of the proposal numbers in the SA payload MUST be the same and
+ * MUST match the number on the proposal sent that was accepted.".
+ */
+ p->proposal_num = 1;
+#else /* CCNS_PL */
+ p->proposal_num = data->proposal.proposal_num;
+#endif /* CCNS_PL */
+ p->protocol_id = IKEV2_PROTOCOL_IKE;
+ p->num_transforms = 4;
+
+ t = wpabuf_put(msg, sizeof(*t));
+ t->type = 3;
+ t->transform_type = IKEV2_TRANSFORM_ENCR;
+ WPA_PUT_BE16(t->transform_id, data->proposal.encr);
+ if (data->proposal.encr == ENCR_AES_CBC) {
+ /* Transform Attribute: Key Len = 128 bits */
+#ifdef CCNS_PL
+ wpabuf_put_be16(msg, 0x001d); /* ?? */
+#else /* CCNS_PL */
+ wpabuf_put_be16(msg, 0x800e); /* AF=1, AttrType=14 */
+#endif /* CCNS_PL */
+ wpabuf_put_be16(msg, 128); /* 128-bit key */
+ }
+ plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) t;
+ WPA_PUT_BE16(t->transform_length, plen);
+
+ t = wpabuf_put(msg, sizeof(*t));
+ t->type = 3;
+ WPA_PUT_BE16(t->transform_length, sizeof(*t));
+ t->transform_type = IKEV2_TRANSFORM_PRF;
+ WPA_PUT_BE16(t->transform_id, data->proposal.prf);
+
+ t = wpabuf_put(msg, sizeof(*t));
+ t->type = 3;
+ WPA_PUT_BE16(t->transform_length, sizeof(*t));
+ t->transform_type = IKEV2_TRANSFORM_INTEG;
+ WPA_PUT_BE16(t->transform_id, data->proposal.integ);
+
+ t = wpabuf_put(msg, sizeof(*t));
+ WPA_PUT_BE16(t->transform_length, sizeof(*t));
+ t->transform_type = IKEV2_TRANSFORM_DH;
+ WPA_PUT_BE16(t->transform_id, data->proposal.dh);
+
+ plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) p;
+ WPA_PUT_BE16(p->proposal_length, plen);
+
+ plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+ WPA_PUT_BE16(phdr->payload_length, plen);
+
+ return 0;
+}
+
+
+static int ikev2_build_ker(struct ikev2_responder_data *data,
+ struct wpabuf *msg, u8 next_payload)
+{
+ struct ikev2_payload_hdr *phdr;
+ size_t plen;
+ struct wpabuf *pv;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Adding KEr payload");
+
+ pv = dh_init(data->dh, &data->r_dh_private);
+ if (pv == NULL) {
+ wpa_printf(MSG_DEBUG, "IKEV2: Failed to initialize DH");
+ return -1;
+ }
+
+ /* KEr - RFC 4306, Sect. 3.4 */
+ phdr = wpabuf_put(msg, sizeof(*phdr));
+ phdr->next_payload = next_payload;
+ phdr->flags = 0;
+
+ wpabuf_put_be16(msg, data->proposal.dh); /* DH Group # */
+ wpabuf_put(msg, 2); /* RESERVED */
+ /*
+ * RFC 4306, Sect. 3.4: possible zero padding for public value to
+ * match the length of the prime.
+ */
+ wpabuf_put(msg, data->dh->prime_len - wpabuf_len(pv));
+ wpabuf_put_buf(msg, pv);
+ wpabuf_free(pv);
+
+ plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+ WPA_PUT_BE16(phdr->payload_length, plen);
+ return 0;
+}
+
+
+static int ikev2_build_nr(struct ikev2_responder_data *data,
+ struct wpabuf *msg, u8 next_payload)
+{
+ struct ikev2_payload_hdr *phdr;
+ size_t plen;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Adding Nr payload");
+
+ /* Nr - RFC 4306, Sect. 3.9 */
+ phdr = wpabuf_put(msg, sizeof(*phdr));
+ phdr->next_payload = next_payload;
+ phdr->flags = 0;
+ wpabuf_put_data(msg, data->r_nonce, data->r_nonce_len);
+ plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+ WPA_PUT_BE16(phdr->payload_length, plen);
+ return 0;
+}
+
+
+static int ikev2_build_idr(struct ikev2_responder_data *data,
+ struct wpabuf *msg, u8 next_payload)
+{
+ struct ikev2_payload_hdr *phdr;
+ size_t plen;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Adding IDr payload");
+
+ if (data->IDr == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: No IDr available");
+ return -1;
+ }
+
+ /* IDr - RFC 4306, Sect. 3.5 */
+ phdr = wpabuf_put(msg, sizeof(*phdr));
+ phdr->next_payload = next_payload;
+ phdr->flags = 0;
+ wpabuf_put_u8(msg, ID_KEY_ID);
+ wpabuf_put(msg, 3); /* RESERVED */
+ wpabuf_put_data(msg, data->IDr, data->IDr_len);
+ plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+ WPA_PUT_BE16(phdr->payload_length, plen);
+ return 0;
+}
+
+
+static int ikev2_build_auth(struct ikev2_responder_data *data,
+ struct wpabuf *msg, u8 next_payload)
+{
+ struct ikev2_payload_hdr *phdr;
+ size_t plen;
+ const struct ikev2_prf_alg *prf;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Adding AUTH payload");
+
+ prf = ikev2_get_prf(data->proposal.prf);
+ if (prf == NULL)
+ return -1;
+
+ /* Authentication - RFC 4306, Sect. 3.8 */
+ phdr = wpabuf_put(msg, sizeof(*phdr));
+ phdr->next_payload = next_payload;
+ phdr->flags = 0;
+ wpabuf_put_u8(msg, AUTH_SHARED_KEY_MIC);
+ wpabuf_put(msg, 3); /* RESERVED */
+
+ /* msg | Ni | prf(SK_pr,IDr') */
+ if (ikev2_derive_auth_data(data->proposal.prf, data->r_sign_msg,
+ data->IDr, data->IDr_len, ID_KEY_ID,
+ &data->keys, 0, data->shared_secret,
+ data->shared_secret_len,
+ data->i_nonce, data->i_nonce_len,
+ data->key_pad, data->key_pad_len,
+ wpabuf_put(msg, prf->hash_len)) < 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data");
+ return -1;
+ }
+ wpabuf_free(data->r_sign_msg);
+ data->r_sign_msg = NULL;
+
+ plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+ WPA_PUT_BE16(phdr->payload_length, plen);
+ return 0;
+}
+
+
+static int ikev2_build_notification(struct ikev2_responder_data *data,
+ struct wpabuf *msg, u8 next_payload)
+{
+ struct ikev2_payload_hdr *phdr;
+ size_t plen;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Adding Notification payload");
+
+ if (data->error_type == 0) {
+ wpa_printf(MSG_INFO, "IKEV2: No Notify Message Type "
+ "available");
+ return -1;
+ }
+
+ /* Notify - RFC 4306, Sect. 3.10 */
+ phdr = wpabuf_put(msg, sizeof(*phdr));
+ phdr->next_payload = next_payload;
+ phdr->flags = 0;
+#ifdef CCNS_PL
+ wpabuf_put_u8(msg, 1); /* Protocol ID: IKE_SA notification */
+#else /* CCNS_PL */
+ wpabuf_put_u8(msg, 0); /* Protocol ID: no existing SA */
+#endif /* CCNS_PL */
+ wpabuf_put_u8(msg, 0); /* SPI Size */
+ wpabuf_put_be16(msg, data->error_type);
+
+ switch (data->error_type) {
+ case INVALID_KE_PAYLOAD:
+ if (data->proposal.dh == -1) {
+ wpa_printf(MSG_INFO, "IKEV2: No DH Group selected for "
+ "INVALID_KE_PAYLOAD notifications");
+ return -1;
+ }
+ wpabuf_put_be16(msg, data->proposal.dh);
+ wpa_printf(MSG_DEBUG, "IKEV2: INVALID_KE_PAYLOAD - request "
+ "DH Group #%d", data->proposal.dh);
+ break;
+ case AUTHENTICATION_FAILED:
+ /* no associated data */
+ break;
+ default:
+ wpa_printf(MSG_INFO, "IKEV2: Unsupported Notify Message Type "
+ "%d", data->error_type);
+ return -1;
+ }
+
+ plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+ WPA_PUT_BE16(phdr->payload_length, plen);
+ return 0;
+}
+
+
+static struct wpabuf * ikev2_build_sa_init(struct ikev2_responder_data *data)
+{
+ struct wpabuf *msg;
+
+ /* build IKE_SA_INIT: HDR, SAr1, KEr, Nr, [CERTREQ], [SK{IDr}] */
+
+ if (os_get_random(data->r_spi, IKEV2_SPI_LEN))
+ return NULL;
+ wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Responder's SPI",
+ data->r_spi, IKEV2_SPI_LEN);
+
+ data->r_nonce_len = IKEV2_NONCE_MIN_LEN;
+ if (os_get_random(data->r_nonce, data->r_nonce_len))
+ return NULL;
+#ifdef CCNS_PL
+ /* Zeros are removed incorrectly from the beginning of the nonces in
+ * key derivation; as a workaround, make sure Nr does not start with
+ * zero.. */
+ if (data->r_nonce[0] == 0)
+ data->r_nonce[0] = 1;
+#endif /* CCNS_PL */
+ wpa_hexdump(MSG_DEBUG, "IKEV2: Nr", data->r_nonce, data->r_nonce_len);
+
+ msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + data->IDr_len + 1500);
+ if (msg == NULL)
+ return NULL;
+
+ ikev2_build_hdr(data, msg, IKE_SA_INIT, IKEV2_PAYLOAD_SA, 0);
+ if (ikev2_build_sar1(data, msg, IKEV2_PAYLOAD_KEY_EXCHANGE) ||
+ ikev2_build_ker(data, msg, IKEV2_PAYLOAD_NONCE) ||
+ ikev2_build_nr(data, msg, data->peer_auth == PEER_AUTH_SECRET ?
+ IKEV2_PAYLOAD_ENCRYPTED :
+ IKEV2_PAYLOAD_NO_NEXT_PAYLOAD)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ if (ikev2_derive_keys(data)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ if (data->peer_auth == PEER_AUTH_CERT) {
+ /* TODO: CERTREQ with SHA-1 hashes of Subject Public Key Info
+ * for trust agents */
+ }
+
+ if (data->peer_auth == PEER_AUTH_SECRET) {
+ struct wpabuf *plain = wpabuf_alloc(data->IDr_len + 1000);
+ if (plain == NULL) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+ if (ikev2_build_idr(data, plain,
+ IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) ||
+ ikev2_build_encrypted(data->proposal.encr,
+ data->proposal.integ,
+ &data->keys, 0, msg, plain,
+ IKEV2_PAYLOAD_IDr)) {
+ wpabuf_free(plain);
+ wpabuf_free(msg);
+ return NULL;
+ }
+ wpabuf_free(plain);
+ }
+
+ ikev2_update_hdr(msg);
+
+ wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (SA_INIT)", msg);
+
+ data->state = SA_AUTH;
+
+ wpabuf_free(data->r_sign_msg);
+ data->r_sign_msg = wpabuf_dup(msg);
+
+ return msg;
+}
+
+
+static struct wpabuf * ikev2_build_sa_auth(struct ikev2_responder_data *data)
+{
+ struct wpabuf *msg, *plain;
+
+ /* build IKE_SA_AUTH: HDR, SK {IDr, [CERT,] AUTH} */
+
+ msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + data->IDr_len + 1000);
+ if (msg == NULL)
+ return NULL;
+ ikev2_build_hdr(data, msg, IKE_SA_AUTH, IKEV2_PAYLOAD_ENCRYPTED, 1);
+
+ plain = wpabuf_alloc(data->IDr_len + 1000);
+ if (plain == NULL) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ if (ikev2_build_idr(data, plain, IKEV2_PAYLOAD_AUTHENTICATION) ||
+ ikev2_build_auth(data, plain, IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) ||
+ ikev2_build_encrypted(data->proposal.encr, data->proposal.integ,
+ &data->keys, 0, msg, plain,
+ IKEV2_PAYLOAD_IDr)) {
+ wpabuf_free(plain);
+ wpabuf_free(msg);
+ return NULL;
+ }
+ wpabuf_free(plain);
+
+ wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (SA_AUTH)", msg);
+
+ data->state = IKEV2_DONE;
+
+ return msg;
+}
+
+
+static struct wpabuf * ikev2_build_notify(struct ikev2_responder_data *data)
+{
+ struct wpabuf *msg;
+
+ msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + 1000);
+ if (msg == NULL)
+ return NULL;
+ if (data->last_msg == LAST_MSG_SA_AUTH) {
+ /* HDR, SK{N} */
+ struct wpabuf *plain = wpabuf_alloc(100);
+ if (plain == NULL) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+ ikev2_build_hdr(data, msg, IKE_SA_AUTH,
+ IKEV2_PAYLOAD_ENCRYPTED, 1);
+ if (ikev2_build_notification(data, plain,
+ IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) ||
+ ikev2_build_encrypted(data->proposal.encr,
+ data->proposal.integ,
+ &data->keys, 0, msg, plain,
+ IKEV2_PAYLOAD_NOTIFICATION)) {
+ wpabuf_free(plain);
+ wpabuf_free(msg);
+ return NULL;
+ }
+ data->state = IKEV2_FAILED;
+ } else {
+ /* HDR, N */
+ ikev2_build_hdr(data, msg, IKE_SA_INIT,
+ IKEV2_PAYLOAD_NOTIFICATION, 0);
+ if (ikev2_build_notification(data, msg,
+ IKEV2_PAYLOAD_NO_NEXT_PAYLOAD)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+ data->state = SA_INIT;
+ }
+
+ ikev2_update_hdr(msg);
+
+ wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (Notification)",
+ msg);
+
+ return msg;
+}
+
+
+struct wpabuf * ikev2_responder_build(struct ikev2_responder_data *data)
+{
+ switch (data->state) {
+ case SA_INIT:
+ return ikev2_build_sa_init(data);
+ case SA_AUTH:
+ return ikev2_build_sa_auth(data);
+ case CHILD_SA:
+ return NULL;
+ case NOTIFY:
+ return ikev2_build_notify(data);
+ case IKEV2_DONE:
+ case IKEV2_FAILED:
+ return NULL;
+ }
+ return NULL;
+}
diff --git a/src/eap_peer/ikev2.h b/src/eap_peer/ikev2.h
new file mode 100644
index 0000000..9ca0ca5
--- /dev/null
+++ b/src/eap_peer/ikev2.h
@@ -0,0 +1,65 @@
+/*
+ * IKEv2 responder (RFC 4306) for EAP-IKEV2
+ * Copyright (c) 2007, 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.
+ */
+
+#ifndef IKEV2_H
+#define IKEV2_H
+
+#include "eap_common/ikev2_common.h"
+
+struct ikev2_proposal_data {
+ u8 proposal_num;
+ int integ;
+ int prf;
+ int encr;
+ int dh;
+};
+
+
+struct ikev2_responder_data {
+ enum { SA_INIT, SA_AUTH, CHILD_SA, NOTIFY, IKEV2_DONE, IKEV2_FAILED }
+ state;
+ u8 i_spi[IKEV2_SPI_LEN];
+ u8 r_spi[IKEV2_SPI_LEN];
+ u8 i_nonce[IKEV2_NONCE_MAX_LEN];
+ size_t i_nonce_len;
+ u8 r_nonce[IKEV2_NONCE_MAX_LEN];
+ size_t r_nonce_len;
+ struct wpabuf *i_dh_public;
+ struct wpabuf *r_dh_private;
+ struct ikev2_proposal_data proposal;
+ const struct dh_group *dh;
+ struct ikev2_keys keys;
+ u8 *IDi;
+ size_t IDi_len;
+ u8 IDi_type;
+ u8 *IDr;
+ size_t IDr_len;
+ struct wpabuf *r_sign_msg;
+ struct wpabuf *i_sign_msg;
+ u8 *shared_secret;
+ size_t shared_secret_len;
+ enum { PEER_AUTH_CERT, PEER_AUTH_SECRET } peer_auth;
+ u8 *key_pad;
+ size_t key_pad_len;
+ u16 error_type;
+ enum { LAST_MSG_SA_INIT, LAST_MSG_SA_AUTH } last_msg;
+};
+
+
+void ikev2_responder_deinit(struct ikev2_responder_data *data);
+int ikev2_responder_process(struct ikev2_responder_data *data,
+ const struct wpabuf *buf);
+struct wpabuf * ikev2_responder_build(struct ikev2_responder_data *data);
+
+#endif /* IKEV2_H */
diff --git a/src/eap_peer/mschapv2.c b/src/eap_peer/mschapv2.c
new file mode 100644
index 0000000..01c22d8
--- /dev/null
+++ b/src/eap_peer/mschapv2.c
@@ -0,0 +1,119 @@
+/*
+ * MSCHAPV2 (RFC 2759)
+ * Copyright (c) 2004-2008, 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "ms_funcs.h"
+#include "mschapv2.h"
+
+const u8 * mschapv2_remove_domain(const u8 *username, size_t *len)
+{
+ size_t i;
+
+ /*
+ * MSCHAPv2 does not include optional domain name in the
+ * challenge-response calculation, so remove domain prefix
+ * (if present).
+ */
+
+ for (i = 0; i < *len; i++) {
+ if (username[i] == '\\') {
+ *len -= i + 1;
+ return username + i + 1;
+ }
+ }
+
+ return username;
+}
+
+
+void mschapv2_derive_response(const u8 *identity, size_t identity_len,
+ const u8 *password, size_t password_len,
+ int pwhash,
+ const u8 *auth_challenge,
+ const u8 *peer_challenge,
+ u8 *nt_response, u8 *auth_response,
+ u8 *master_key)
+{
+ const u8 *username;
+ size_t username_len;
+ u8 password_hash[16], password_hash_hash[16];
+
+ wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: Identity",
+ identity, identity_len);
+ username_len = identity_len;
+ username = mschapv2_remove_domain(identity, &username_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: Username",
+ username, username_len);
+
+ wpa_hexdump(MSG_DEBUG, "MSCHAPV2: auth_challenge",
+ auth_challenge, MSCHAPV2_CHAL_LEN);
+ wpa_hexdump(MSG_DEBUG, "MSCHAPV2: peer_challenge",
+ peer_challenge, MSCHAPV2_CHAL_LEN);
+ wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: username",
+ username, username_len);
+ /* Authenticator response is not really needed yet, but calculate it
+ * here so that challenges need not be saved. */
+ if (pwhash) {
+ wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: password hash",
+ password, password_len);
+ generate_nt_response_pwhash(auth_challenge, peer_challenge,
+ username, username_len,
+ password, nt_response);
+ generate_authenticator_response_pwhash(
+ password, peer_challenge, auth_challenge,
+ username, username_len, nt_response, auth_response);
+ } else {
+ wpa_hexdump_ascii_key(MSG_DEBUG, "MSCHAPV2: password",
+ password, password_len);
+ generate_nt_response(auth_challenge, peer_challenge,
+ username, username_len,
+ password, password_len, nt_response);
+ generate_authenticator_response(password, password_len,
+ peer_challenge, auth_challenge,
+ username, username_len,
+ nt_response, auth_response);
+ }
+ wpa_hexdump(MSG_DEBUG, "MSCHAPV2: NT Response",
+ nt_response, MSCHAPV2_NT_RESPONSE_LEN);
+ wpa_hexdump(MSG_DEBUG, "MSCHAPV2: Auth Response",
+ auth_response, MSCHAPV2_AUTH_RESPONSE_LEN);
+
+ /* Generate master_key here since we have the needed data available. */
+ if (pwhash) {
+ hash_nt_password_hash(password, password_hash_hash);
+ } else {
+ nt_password_hash(password, password_len, password_hash);
+ hash_nt_password_hash(password_hash, password_hash_hash);
+ }
+ get_master_key(password_hash_hash, nt_response, master_key);
+ wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: Master Key",
+ master_key, MSCHAPV2_MASTER_KEY_LEN);
+}
+
+
+int mschapv2_verify_auth_response(const u8 *auth_response,
+ const u8 *buf, size_t buf_len)
+{
+ u8 recv_response[MSCHAPV2_AUTH_RESPONSE_LEN];
+ if (buf_len < 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN ||
+ buf[0] != 'S' || buf[1] != '=' ||
+ hexstr2bin((char *) (buf + 2), recv_response,
+ MSCHAPV2_AUTH_RESPONSE_LEN) ||
+ os_memcmp(auth_response, recv_response,
+ MSCHAPV2_AUTH_RESPONSE_LEN) != 0)
+ return -1;
+ return 0;
+}
diff --git a/src/eap_peer/mschapv2.h b/src/eap_peer/mschapv2.h
new file mode 100644
index 0000000..c7c36f7
--- /dev/null
+++ b/src/eap_peer/mschapv2.h
@@ -0,0 +1,34 @@
+/*
+ * MSCHAPV2 (RFC 2759)
+ * Copyright (c) 2004-2008, 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.
+ */
+
+#ifndef MSCHAPV2_H
+#define MSCHAPV2_H
+
+#define MSCHAPV2_CHAL_LEN 16
+#define MSCHAPV2_NT_RESPONSE_LEN 24
+#define MSCHAPV2_AUTH_RESPONSE_LEN 20
+#define MSCHAPV2_MASTER_KEY_LEN 16
+
+const u8 * mschapv2_remove_domain(const u8 *username, size_t *len);
+void mschapv2_derive_response(const u8 *username, size_t username_len,
+ const u8 *password, size_t password_len,
+ int pwhash,
+ const u8 *auth_challenge,
+ const u8 *peer_challenge,
+ u8 *nt_response, u8 *auth_response,
+ u8 *master_key);
+int mschapv2_verify_auth_response(const u8 *auth_response,
+ const u8 *buf, size_t buf_len);
+
+#endif /* MSCHAPV2_H */
diff --git a/src/eap_peer/tncc.c b/src/eap_peer/tncc.c
new file mode 100644
index 0000000..2f95b53
--- /dev/null
+++ b/src/eap_peer/tncc.c
@@ -0,0 +1,1204 @@
+/*
+ * EAP-TNC - TNCC (IF-IMC and IF-TNCCS)
+ * Copyright (c) 2007, 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.
+ */
+
+#include "includes.h"
+#ifndef CONFIG_NATIVE_WINDOWS
+#include <dlfcn.h>
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#include "common.h"
+#include "base64.h"
+#include "tncc.h"
+
+
+#ifdef UNICODE
+#define TSTR "%S"
+#else /* UNICODE */
+#define TSTR "%s"
+#endif /* UNICODE */
+
+
+#define TNC_CONFIG_FILE "/etc/tnc_config"
+#define TNC_WINREG_PATH TEXT("SOFTWARE\\Trusted Computing Group\\TNC\\IMCs")
+#define IF_TNCCS_START \
+"<?xml version=\"1.0\"?>\n" \
+"<TNCCS-Batch BatchId=\"%d\" Recipient=\"TNCS\" " \
+"xmlns=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/IF_TNCCS#\" " \
+"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " \
+"xsi:schemaLocation=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/" \
+"IF_TNCCS#https://www.trustedcomputinggroup.org/XML/SCHEMA/TNCCS_1.0.xsd\">\n"
+#define IF_TNCCS_END "\n</TNCCS-Batch>"
+
+/* TNC IF-IMC */
+
+typedef unsigned long TNC_UInt32;
+typedef unsigned char *TNC_BufferReference;
+
+typedef TNC_UInt32 TNC_IMCID;
+typedef TNC_UInt32 TNC_ConnectionID;
+typedef TNC_UInt32 TNC_ConnectionState;
+typedef TNC_UInt32 TNC_RetryReason;
+typedef TNC_UInt32 TNC_MessageType;
+typedef TNC_MessageType *TNC_MessageTypeList;
+typedef TNC_UInt32 TNC_VendorID;
+typedef TNC_UInt32 TNC_MessageSubtype;
+typedef TNC_UInt32 TNC_Version;
+typedef TNC_UInt32 TNC_Result;
+
+typedef TNC_Result (*TNC_TNCC_BindFunctionPointer)(
+ TNC_IMCID imcID,
+ char *functionName,
+ void **pOutfunctionPointer);
+
+#define TNC_RESULT_SUCCESS 0
+#define TNC_RESULT_NOT_INITIALIZED 1
+#define TNC_RESULT_ALREADY_INITIALIZED 2
+#define TNC_RESULT_NO_COMMON_VERSION 3
+#define TNC_RESULT_CANT_RETRY 4
+#define TNC_RESULT_WONT_RETRY 5
+#define TNC_RESULT_INVALID_PARAMETER 6
+#define TNC_RESULT_CANT_RESPOND 7
+#define TNC_RESULT_ILLEGAL_OPERATION 8
+#define TNC_RESULT_OTHER 9
+#define TNC_RESULT_FATAL 10
+
+#define TNC_CONNECTION_STATE_CREATE 0
+#define TNC_CONNECTION_STATE_HANDSHAKE 1
+#define TNC_CONNECTION_STATE_ACCESS_ALLOWED 2
+#define TNC_CONNECTION_STATE_ACCESS_ISOLATED 3
+#define TNC_CONNECTION_STATE_ACCESS_NONE 4
+#define TNC_CONNECTION_STATE_DELETE 5
+
+#define TNC_IFIMC_VERSION_1 1
+
+#define TNC_VENDORID_ANY ((TNC_VendorID) 0xffffff)
+#define TNC_SUBTYPE_ANY ((TNC_MessageSubtype) 0xff)
+
+/* TNCC-TNCS Message Types */
+#define TNC_TNCCS_RECOMMENDATION 0x00000001
+#define TNC_TNCCS_ERROR 0x00000002
+#define TNC_TNCCS_PREFERREDLANGUAGE 0x00000003
+#define TNC_TNCCS_REASONSTRINGS 0x00000004
+
+
+struct tnc_if_imc {
+ struct tnc_if_imc *next;
+ char *name;
+ char *path;
+ void *dlhandle; /* from dlopen() */
+ TNC_IMCID imcID;
+ TNC_ConnectionID connectionID;
+ TNC_MessageTypeList supported_types;
+ size_t num_supported_types;
+ u8 *imc_send;
+ size_t imc_send_len;
+
+ /* Functions implemented by IMCs (with TNC_IMC_ prefix) */
+ TNC_Result (*Initialize)(
+ TNC_IMCID imcID,
+ TNC_Version minVersion,
+ TNC_Version maxVersion,
+ TNC_Version *pOutActualVersion);
+ TNC_Result (*NotifyConnectionChange)(
+ TNC_IMCID imcID,
+ TNC_ConnectionID connectionID,
+ TNC_ConnectionState newState);
+ TNC_Result (*BeginHandshake)(
+ TNC_IMCID imcID,
+ TNC_ConnectionID connectionID);
+ TNC_Result (*ReceiveMessage)(
+ TNC_IMCID imcID,
+ TNC_ConnectionID connectionID,
+ TNC_BufferReference messageBuffer,
+ TNC_UInt32 messageLength,
+ TNC_MessageType messageType);
+ TNC_Result (*BatchEnding)(
+ TNC_IMCID imcID,
+ TNC_ConnectionID connectionID);
+ TNC_Result (*Terminate)(TNC_IMCID imcID);
+ TNC_Result (*ProvideBindFunction)(
+ TNC_IMCID imcID,
+ TNC_TNCC_BindFunctionPointer bindFunction);
+};
+
+struct tncc_data {
+ struct tnc_if_imc *imc;
+ unsigned int last_batchid;
+};
+
+#define TNC_MAX_IMC_ID 10
+static struct tnc_if_imc *tnc_imc[TNC_MAX_IMC_ID] = { NULL };
+
+
+/* TNCC functions that IMCs can call */
+
+TNC_Result TNC_TNCC_ReportMessageTypes(
+ TNC_IMCID imcID,
+ TNC_MessageTypeList supportedTypes,
+ TNC_UInt32 typeCount)
+{
+ TNC_UInt32 i;
+ struct tnc_if_imc *imc;
+
+ wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_ReportMessageTypes(imcID=%lu "
+ "typeCount=%lu)",
+ (unsigned long) imcID, (unsigned long) typeCount);
+
+ for (i = 0; i < typeCount; i++) {
+ wpa_printf(MSG_DEBUG, "TNC: supportedTypes[%lu] = %lu",
+ i, supportedTypes[i]);
+ }
+
+ if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
+ return TNC_RESULT_INVALID_PARAMETER;
+
+ imc = tnc_imc[imcID];
+ os_free(imc->supported_types);
+ imc->supported_types =
+ os_malloc(typeCount * sizeof(TNC_MessageTypeList));
+ if (imc->supported_types == NULL)
+ return TNC_RESULT_FATAL;
+ os_memcpy(imc->supported_types, supportedTypes,
+ typeCount * sizeof(TNC_MessageTypeList));
+ imc->num_supported_types = typeCount;
+
+ return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_TNCC_SendMessage(
+ TNC_IMCID imcID,
+ TNC_ConnectionID connectionID,
+ TNC_BufferReference message,
+ TNC_UInt32 messageLength,
+ TNC_MessageType messageType)
+{
+ struct tnc_if_imc *imc;
+ unsigned char *b64;
+ size_t b64len;
+
+ wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_SendMessage(imcID=%lu "
+ "connectionID=%lu messageType=%lu)",
+ imcID, connectionID, messageType);
+ wpa_hexdump_ascii(MSG_DEBUG, "TNC: TNC_TNCC_SendMessage",
+ message, messageLength);
+
+ if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
+ return TNC_RESULT_INVALID_PARAMETER;
+
+ b64 = base64_encode(message, messageLength, &b64len);
+ if (b64 == NULL)
+ return TNC_RESULT_FATAL;
+
+ imc = tnc_imc[imcID];
+ os_free(imc->imc_send);
+ imc->imc_send_len = 0;
+ imc->imc_send = os_zalloc(b64len + 100);
+ if (imc->imc_send == NULL) {
+ os_free(b64);
+ return TNC_RESULT_OTHER;
+ }
+
+ imc->imc_send_len =
+ os_snprintf((char *) imc->imc_send, b64len + 100,
+ "<IMC-IMV-Message><Type>%08X</Type>"
+ "<Base64>%s</Base64></IMC-IMV-Message>",
+ (unsigned int) messageType, b64);
+
+ os_free(b64);
+
+ return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_TNCC_RequestHandshakeRetry(
+ TNC_IMCID imcID,
+ TNC_ConnectionID connectionID,
+ TNC_RetryReason reason)
+{
+ wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_RequestHandshakeRetry");
+
+ if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
+ return TNC_RESULT_INVALID_PARAMETER;
+
+ /*
+ * TODO: trigger a call to eapol_sm_request_reauth(). This would
+ * require that the IMC continues to be loaded in memory afer
+ * authentication..
+ */
+
+ return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_9048_LogMessage(TNC_IMCID imcID, TNC_UInt32 severity,
+ const char *message)
+{
+ wpa_printf(MSG_DEBUG, "TNC: TNC_9048_LogMessage(imcID=%lu "
+ "severity==%lu message='%s')",
+ imcID, severity, message);
+ return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_9048_UserMessage(TNC_IMCID imcID, TNC_ConnectionID connectionID,
+ const char *message)
+{
+ wpa_printf(MSG_DEBUG, "TNC: TNC_9048_UserMessage(imcID=%lu "
+ "connectionID==%lu message='%s')",
+ imcID, connectionID, message);
+ return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_TNCC_BindFunction(
+ TNC_IMCID imcID,
+ char *functionName,
+ void **pOutfunctionPointer)
+{
+ wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_BindFunction(imcID=%lu, "
+ "functionName='%s')", (unsigned long) imcID, functionName);
+
+ if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
+ return TNC_RESULT_INVALID_PARAMETER;
+
+ if (pOutfunctionPointer == NULL)
+ return TNC_RESULT_INVALID_PARAMETER;
+
+ if (os_strcmp(functionName, "TNC_TNCC_ReportMessageTypes") == 0)
+ *pOutfunctionPointer = TNC_TNCC_ReportMessageTypes;
+ else if (os_strcmp(functionName, "TNC_TNCC_SendMessage") == 0)
+ *pOutfunctionPointer = TNC_TNCC_SendMessage;
+ else if (os_strcmp(functionName, "TNC_TNCC_RequestHandshakeRetry") ==
+ 0)
+ *pOutfunctionPointer = TNC_TNCC_RequestHandshakeRetry;
+ else if (os_strcmp(functionName, "TNC_9048_LogMessage") == 0)
+ *pOutfunctionPointer = TNC_9048_LogMessage;
+ else if (os_strcmp(functionName, "TNC_9048_UserMessage") == 0)
+ *pOutfunctionPointer = TNC_9048_UserMessage;
+ else
+ *pOutfunctionPointer = NULL;
+
+ return TNC_RESULT_SUCCESS;
+}
+
+
+static void * tncc_get_sym(void *handle, char *func)
+{
+ void *fptr;
+
+#ifdef CONFIG_NATIVE_WINDOWS
+#ifdef _WIN32_WCE
+ fptr = GetProcAddressA(handle, func);
+#else /* _WIN32_WCE */
+ fptr = GetProcAddress(handle, func);
+#endif /* _WIN32_WCE */
+#else /* CONFIG_NATIVE_WINDOWS */
+ fptr = dlsym(handle, func);
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+ return fptr;
+}
+
+
+static int tncc_imc_resolve_funcs(struct tnc_if_imc *imc)
+{
+ void *handle = imc->dlhandle;
+
+ /* Mandatory IMC functions */
+ imc->Initialize = tncc_get_sym(handle, "TNC_IMC_Initialize");
+ if (imc->Initialize == NULL) {
+ wpa_printf(MSG_ERROR, "TNC: IMC does not export "
+ "TNC_IMC_Initialize");
+ return -1;
+ }
+
+ imc->BeginHandshake = tncc_get_sym(handle, "TNC_IMC_BeginHandshake");
+ if (imc->BeginHandshake == NULL) {
+ wpa_printf(MSG_ERROR, "TNC: IMC does not export "
+ "TNC_IMC_BeginHandshake");
+ return -1;
+ }
+
+ imc->ProvideBindFunction =
+ tncc_get_sym(handle, "TNC_IMC_ProvideBindFunction");
+ if (imc->ProvideBindFunction == NULL) {
+ wpa_printf(MSG_ERROR, "TNC: IMC does not export "
+ "TNC_IMC_ProvideBindFunction");
+ return -1;
+ }
+
+ /* Optional IMC functions */
+ imc->NotifyConnectionChange =
+ tncc_get_sym(handle, "TNC_IMC_NotifyConnectionC