NEED_AES_UNWRAP=y
endif
+ifdef CONFIG_EAP_TEAP
+L_CFLAGS += -DEAP_SERVER_TEAP
+OBJS += src/eap_server/eap_server_teap.c
+OBJS += src/eap_common/eap_teap_common.c
+TLS_FUNCS=y
+NEED_T_PRF=y
+NEED_SHA384=y
+NEED_AES_UNWRAP=y
+endif
+
ifdef CONFIG_WPS
L_CFLAGS += -DCONFIG_WPS -DEAP_SERVER_WSC
OBJS += src/utils/uuid.c
NEED_AES_UNWRAP=y
endif
+ifdef CONFIG_EAP_TEAP
+CFLAGS += -DEAP_SERVER_TEAP
+OBJS += ../src/eap_server/eap_server_teap.o
+OBJS += ../src/eap_common/eap_teap_common.o
+TLS_FUNCS=y
+NEED_T_PRF=y
+NEED_SHA384=y
+NEED_AES_UNWRAP=y
+endif
+
ifdef CONFIG_WPS
CFLAGS += -DCONFIG_WPS -DEAP_SERVER_WSC
OBJS += ../src/utils/uuid.o
} else if (os_strcmp(buf, "pac_key_refresh_time") == 0) {
bss->pac_key_refresh_time = atoi(pos);
#endif /* EAP_SERVER_FAST */
+#ifdef EAP_SERVER_TEAP
+ } else if (os_strcmp(buf, "eap_teap_auth") == 0) {
+ int val = atoi(pos);
+
+ if (val < 0 || val > 1) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid eap_teap_auth value",
+ line);
+ return 1;
+ }
+ bss->eap_teap_auth = val;
+ } else if (os_strcmp(buf, "eap_teap_pac_no_inner") == 0) {
+ bss->eap_teap_pac_no_inner = atoi(pos);
+#endif /* EAP_SERVER_TEAP */
#ifdef EAP_SERVER_SIM
} else if (os_strcmp(buf, "eap_sim_db") == 0) {
os_free(bss->eap_sim_db);
# EAP-FAST for the integrated EAP server
#CONFIG_EAP_FAST=y
+# EAP-TEAP for the integrated EAP server
+# Note: The current EAP-TEAP implementation is experimental and should not be
+# enabled for production use. The IETF RFC 7170 that defines EAP-TEAP has number
+# of conflicting statements and missing details and the implementation has
+# vendor specific workarounds for those and as such, may not interoperate with
+# any other implementation. This should not be used for anything else than
+# experimentation and interoperability testing until those issues has been
+# resolved.
+#CONFIG_EAP_TEAP=y
+
# Wi-Fi Protected Setup (WPS)
#CONFIG_WPS=y
# Enable UPnP support for external WPS Registrars
ret = eap_server_fast_register();
#endif /* EAP_SERVER_FAST */
+#ifdef EAP_SERVER_TEAP
+ if (ret == 0)
+ ret = eap_server_teap_register();
+#endif /* EAP_SERVER_TEAP */
+
#ifdef EAP_SERVER_WSC
if (ret == 0)
ret = eap_server_wsc_register();
# (or fewer) of the lifetime remains.
#pac_key_refresh_time=86400
+# EAP-TEAP authentication type
+# 0 = inner EAP (default)
+# 1 = Basic-Password-Auth
+#eap_teap_auth=0
+
+# EAP-TEAP authentication behavior when using PAC
+# 0 = perform inner authentication (default)
+# 1 = skip inner authentication (inner EAP/Basic-Password-Auth)
+#eap_teap_pac_no_inner=0
+
# EAP-SIM and EAP-AKA protected success/failure indication using AT_RESULT_IND
# (default: 0 = disabled).
#eap_sim_aka_result_ind=1
int eap_fast_prov;
int pac_key_lifetime;
int pac_key_refresh_time;
+ int eap_teap_auth;
+ int eap_teap_pac_no_inner;
int eap_sim_aka_result_ind;
int tnc;
int fragment_size;
srv.eap_fast_prov = conf->eap_fast_prov;
srv.pac_key_lifetime = conf->pac_key_lifetime;
srv.pac_key_refresh_time = conf->pac_key_refresh_time;
+ srv.eap_teap_auth = conf->eap_teap_auth;
+ srv.eap_teap_pac_no_inner = conf->eap_teap_pac_no_inner;
srv.eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
srv.tnc = conf->tnc;
srv.wps = hapd->wps;
conf.eap_fast_prov = hapd->conf->eap_fast_prov;
conf.pac_key_lifetime = hapd->conf->pac_key_lifetime;
conf.pac_key_refresh_time = hapd->conf->pac_key_refresh_time;
+ conf.eap_teap_auth = hapd->conf->eap_teap_auth;
+ conf.eap_teap_pac_no_inner = hapd->conf->eap_teap_pac_no_inner;
conf.eap_sim_aka_result_ind = hapd->conf->eap_sim_aka_result_ind;
conf.tnc = hapd->conf->tnc;
conf.wps = hapd->wps;
#define OPENSSL_NEED_EAP_FAST_PRF
#endif
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || \
+ defined(EAP_SERVER_FAST) || defined(EAP_TEAP) || \
+ defined(EAP_SERVER_TEAP)
+#define EAP_FAST_OR_TEAP
+#endif
+
+
#if defined(OPENSSL_IS_BORINGSSL)
/* stack_index_t is the return type of OpenSSL's sk_XXX_num() functions. */
typedef size_t stack_index_t;
wpa_printf(MSG_DEBUG, "OpenSSL: cipher suites: %s", buf + 1);
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
-#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+#ifdef EAP_FAST_OR_TEAP
if (os_strstr(buf, ":ADH-")) {
/*
* Need to drop to security level 0 to allow anonymous
/* Force at least security level 1 */
SSL_set_security_level(conn->ssl, 1);
}
-#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+#endif /* EAP_FAST_OR_TEAP */
#endif
if (SSL_set_cipher_list(conn->ssl, buf + 1) != 1) {
}
-#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+#ifdef EAP_FAST_OR_TEAP
/* ClientHello TLS extensions require a patch to openssl, so this function is
* commented out unless explicitly needed for EAP-FAST in order to be able to
* build this file with unmodified openssl. */
return 0;
}
-#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+#endif /* EAP_FAST_OR_TEAP */
int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn)
}
-#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+#ifdef EAP_FAST_OR_TEAP
/* Pre-shared secred requires a patch to openssl, so this function is
* commented out unless explicitly needed for EAP-FAST in order to be able to
* build this file with unmodified openssl. */
return 1;
}
-#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+#endif /* EAP_FAST_OR_TEAP */
int tls_connection_set_session_ticket_cb(void *tls_ctx,
tls_session_ticket_cb cb,
void *ctx)
{
-#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+#ifdef EAP_FAST_OR_TEAP
conn->session_ticket_cb = cb;
conn->session_ticket_cb_ctx = ctx;
}
return 0;
-#else /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+#else /* EAP_FAST_OR_TEAP */
return -1;
-#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+#endif /* EAP_FAST_OR_TEAP */
}
EAP_TYPE_GPSK = 51 /* RFC 5433 */,
EAP_TYPE_PWD = 52 /* RFC 5931 */,
EAP_TYPE_EKE = 53 /* RFC 6124 */,
+ EAP_TYPE_TEAP = 55 /* RFC 7170 */,
EAP_TYPE_EXPANDED = 254 /* RFC 3748 */
} EapType;
--- /dev/null
+/*
+ * EAP-TEAP common helper functions (RFC 7170)
+ * Copyright (c) 2008-2019, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/sha384.h"
+#include "crypto/tls.h"
+#include "eap_defs.h"
+#include "eap_teap_common.h"
+
+
+void eap_teap_put_tlv_hdr(struct wpabuf *buf, u16 type, u16 len)
+{
+ struct teap_tlv_hdr hdr;
+
+ hdr.tlv_type = host_to_be16(type);
+ hdr.length = host_to_be16(len);
+ wpabuf_put_data(buf, &hdr, sizeof(hdr));
+}
+
+
+void eap_teap_put_tlv(struct wpabuf *buf, u16 type, const void *data, u16 len)
+{
+ eap_teap_put_tlv_hdr(buf, type, len);
+ wpabuf_put_data(buf, data, len);
+}
+
+
+void eap_teap_put_tlv_buf(struct wpabuf *buf, u16 type,
+ const struct wpabuf *data)
+{
+ eap_teap_put_tlv_hdr(buf, type, wpabuf_len(data));
+ wpabuf_put_buf(buf, data);
+}
+
+
+struct wpabuf * eap_teap_tlv_eap_payload(struct wpabuf *buf)
+{
+ struct wpabuf *e;
+
+ if (!buf)
+ return NULL;
+
+ /* Encapsulate EAP packet in EAP-Payload TLV */
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Add EAP-Payload TLV");
+ e = wpabuf_alloc(sizeof(struct teap_tlv_hdr) + wpabuf_len(buf));
+ if (!e) {
+ wpa_printf(MSG_ERROR,
+ "EAP-TEAP: Failed to allocate memory for TLV encapsulation");
+ wpabuf_free(buf);
+ return NULL;
+ }
+ eap_teap_put_tlv_buf(e, TEAP_TLV_MANDATORY | TEAP_TLV_EAP_PAYLOAD, buf);
+ wpabuf_free(buf);
+
+ /* TODO: followed by optional TLVs associated with the EAP packet */
+
+ return e;
+}
+
+
+static int eap_teap_tls_prf(const u8 *secret, size_t secret_len,
+ const char *label, const u8 *seed, size_t seed_len,
+ u8 *out, size_t outlen)
+{
+ /* TODO: TLS-PRF for TLSv1.3 */
+ return tls_prf_sha256(secret, secret_len, label, seed, seed_len,
+ out, outlen);
+}
+
+
+int eap_teap_derive_eap_msk(const u8 *simck, u8 *msk)
+{
+ /*
+ * RFC 7170, Section 5.4: EAP Master Session Key Generation
+ * MSK = TLS-PRF(S-IMCK[j], "Session Key Generating Function", 64)
+ */
+
+ if (eap_teap_tls_prf(simck, EAP_TEAP_SIMCK_LEN,
+ "Session Key Generating Function", (u8 *) "", 0,
+ msk, EAP_TEAP_KEY_LEN) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: Derived key (MSK)",
+ msk, EAP_TEAP_KEY_LEN);
+ return 0;
+}
+
+
+int eap_teap_derive_eap_emsk(const u8 *simck, u8 *emsk)
+{
+ /*
+ * RFC 7170, Section 5.4: EAP Master Session Key Generation
+ * EMSK = TLS-PRF(S-IMCK[j],
+ * "Extended Session Key Generating Function", 64)
+ */
+
+ if (eap_teap_tls_prf(simck, EAP_TEAP_SIMCK_LEN,
+ "Extended Session Key Generating Function",
+ (u8 *) "", 0, emsk, EAP_EMSK_LEN) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: Derived key (EMSK)",
+ emsk, EAP_EMSK_LEN);
+ return 0;
+}
+
+
+int eap_teap_derive_cmk_basic_pw_auth(const u8 *s_imck_msk, u8 *cmk)
+{
+ u8 imsk[32], imck[EAP_TEAP_IMCK_LEN];
+ int res;
+
+ /* FIX: The Basic-Password-Auth (i.e., no inner EAP) case is
+ * not fully defined in RFC 7170, so this CMK derivation may
+ * need to be changed if a fixed definition is eventually
+ * published. For now, derive CMK[0] based on S-IMCK[0] and
+ * IMSK of 32 octets of zeros. */
+ os_memset(imsk, 0, 32);
+ res = eap_teap_tls_prf(s_imck_msk, EAP_TEAP_SIMCK_LEN,
+ "Inner Methods Compound Keys",
+ imsk, 32, imck, sizeof(imck));
+ if (res < 0)
+ return -1;
+ os_memcpy(cmk, &imck[EAP_TEAP_SIMCK_LEN], EAP_TEAP_CMK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: CMK[no-inner-EAP]",
+ cmk, EAP_TEAP_CMK_LEN);
+ forced_memzero(imck, sizeof(imck));
+ return 0;
+}
+
+
+int eap_teap_derive_imck(const u8 *prev_s_imck_msk, const u8 *prev_s_imck_emsk,
+ const u8 *msk, size_t msk_len,
+ const u8 *emsk, size_t emsk_len,
+ u8 *s_imck_msk, u8 *cmk_msk,
+ u8 *s_imck_emsk, u8 *cmk_emsk)
+{
+ u8 imsk[64], imck[EAP_TEAP_IMCK_LEN];
+ int res;
+
+ /*
+ * RFC 7170, Section 5.2:
+ * IMSK = First 32 octets of TLS-PRF(EMSK, "TEAPbindkey@ietf.org" |
+ * "\0" | 64)
+ * (if EMSK is not available, MSK is used instead; if neither is
+ * available, IMSK is 32 octets of zeros; MSK is truncated to 32 octets
+ * or padded to 32 octets, if needed)
+ * (64 is encoded as a 2-octet field in network byte order)
+ *
+ * S-IMCK[0] = session_key_seed
+ * IMCK[j] = TLS-PRF(S-IMCK[j-1], "Inner Methods Compound Keys",
+ * IMSK[j], 60)
+ * S-IMCK[j] = first 40 octets of IMCK[j]
+ * CMK[j] = last 20 octets of IMCK[j]
+ */
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: MSK[j]", msk, msk_len);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: EMSK[j]", emsk, emsk_len);
+
+ if (emsk && emsk_len > 0) {
+ u8 context[3];
+
+ context[0] = 0;
+ context[1] = 0;
+ context[2] = 64;
+ if (eap_teap_tls_prf(emsk, emsk_len, "TEAPbindkey@ietf.org",
+ context, sizeof(context), imsk, 64) < 0)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: IMSK from EMSK",
+ imsk, 32);
+
+ res = eap_teap_tls_prf(prev_s_imck_emsk, EAP_TEAP_SIMCK_LEN,
+ "Inner Methods Compound Keys",
+ imsk, 32, imck, EAP_TEAP_IMCK_LEN);
+ forced_memzero(imsk, sizeof(imsk));
+ if (res < 0)
+ return -1;
+
+ os_memcpy(s_imck_emsk, imck, EAP_TEAP_SIMCK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: EMSK S-IMCK[j]",
+ s_imck_emsk, EAP_TEAP_SIMCK_LEN);
+ os_memcpy(cmk_emsk, &imck[EAP_TEAP_SIMCK_LEN],
+ EAP_TEAP_CMK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: EMSK CMK[j]",
+ cmk_emsk, EAP_TEAP_CMK_LEN);
+ forced_memzero(imck, EAP_TEAP_IMCK_LEN);
+ }
+
+ if (msk && msk_len > 0) {
+ size_t copy_len = msk_len;
+
+ os_memset(imsk, 0, 32); /* zero pad, if needed */
+ if (copy_len > 32)
+ copy_len = 32;
+ os_memcpy(imsk, msk, copy_len);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: IMSK from MSK", imsk, 32);
+ } else {
+ os_memset(imsk, 0, 32);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: Zero IMSK", imsk, 32);
+ }
+
+ res = eap_teap_tls_prf(prev_s_imck_msk, EAP_TEAP_SIMCK_LEN,
+ "Inner Methods Compound Keys",
+ imsk, 32, imck, EAP_TEAP_IMCK_LEN);
+ forced_memzero(imsk, sizeof(imsk));
+ if (res < 0)
+ return -1;
+
+ os_memcpy(s_imck_msk, imck, EAP_TEAP_SIMCK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: MSK S-IMCK[j]",
+ s_imck_msk, EAP_TEAP_SIMCK_LEN);
+ os_memcpy(cmk_msk, &imck[EAP_TEAP_SIMCK_LEN], EAP_TEAP_CMK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: MSK CMK[j]",
+ cmk_msk, EAP_TEAP_CMK_LEN);
+ forced_memzero(imck, EAP_TEAP_IMCK_LEN);
+
+ return 0;
+}
+
+
+static int tls_cipher_suite_match(const u16 *list, size_t count, u16 cs)
+{
+ size_t i;
+
+ for (i = 0; i < count; i++) {
+ if (list[i] == cs)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int tls_cipher_suite_mac_sha1(u16 cs)
+{
+ static const u16 sha1_cs[] = {
+ 0x0005, 0x0007, 0x000a, 0x000d, 0x0010, 0x0013, 0x0016, 0x001b,
+ 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036,
+ 0x0037, 0x0038, 0x0039, 0x003a, 0x0041, 0x0042, 0x0043, 0x0044,
+ 0x0045, 0x0046, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089,
+ 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091,
+ 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099,
+ 0x009a, 0x009b,
+ 0xc002, 0xc003, 0xc004, 0xc005, 0xc007, 0xc008, 0xc009, 0xc009,
+ 0xc00a, 0xc00c, 0xc00d, 0xc00e, 0xc00f, 0xc011, 0xc012, 0xc013,
+ 0xc014, 0xc016, 0xc017, 0xc018, 0xc019, 0xc01a, 0xc01b, 0xc01c,
+ 0xc014, 0xc01e, 0xc01f, 0xc020, 0xc021, 0xc022, 0xc033, 0xc034,
+ 0xc035, 0xc036
+ };
+
+ return tls_cipher_suite_match(sha1_cs, ARRAY_SIZE(sha1_cs), cs);
+}
+
+
+static int tls_cipher_suite_mac_sha256(u16 cs)
+{
+ static const u16 sha256_cs[] = {
+ 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0067, 0x0068, 0x0069,
+ 0x006a, 0x006b, 0x006c, 0x006d, 0x009c, 0x009e, 0x00a0, 0x00a2,
+ 0x00a4, 0x00a6, 0x00a8, 0x00aa, 0x00ac, 0x00ae, 0x00b2, 0x00b6,
+ 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bd, 0x00be, 0x00be,
+ 0x00bf, 0x00bf, 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5,
+ 0x1301, 0x1303, 0x1304, 0x1305,
+ 0xc023, 0xc025, 0xc027, 0xc029, 0xc02b, 0xc02d, 0xc02f, 0xc031,
+ 0xc037, 0xc03c, 0xc03e, 0xc040, 0xc040, 0xc042, 0xc044, 0xc046,
+ 0xc048, 0xc04a, 0xc04c, 0xc04e, 0xc050, 0xc052, 0xc054, 0xc056,
+ 0xc058, 0xc05a, 0xc05c, 0xc05e, 0xc060, 0xc062, 0xc064, 0xc066,
+ 0xc068, 0xc06a, 0xc06c, 0xc06e, 0xc070, 0xc072, 0xc074, 0xc076,
+ 0xc078, 0xc07a, 0xc07c, 0xc07e, 0xc080, 0xc082, 0xc084, 0xc086,
+ 0xc088, 0xc08a, 0xc08c, 0xc08e, 0xc090, 0xc092, 0xc094, 0xc096,
+ 0xc098, 0xc09a, 0xc0b0, 0xc0b2, 0xc0b4,
+ 0xcca8, 0xcca9, 0xccaa, 0xccab, 0xccac, 0xccad, 0xccae,
+ 0xd001, 0xd003, 0xd005
+ };
+
+ return tls_cipher_suite_match(sha256_cs, ARRAY_SIZE(sha256_cs), cs);
+}
+
+
+static int tls_cipher_suite_mac_sha384(u16 cs)
+{
+ static const u16 sha384_cs[] = {
+ 0x009d, 0x009f, 0x00a1, 0x00a3, 0x00a5, 0x00a7, 0x00a9, 0x00ab,
+ 0x00ad, 0x00af, 0x00b3, 0x00b7, 0x1302,
+ 0xc024, 0xc026, 0xc028, 0xc02a, 0xc02c, 0xc02e, 0xc030, 0xc032,
+ 0xc038, 0xc03d, 0xc03f, 0xc041, 0xc043, 0xc045, 0xc047, 0xc049,
+ 0xc04b, 0xc04d, 0xc04f, 0xc051, 0xc053, 0xc055, 0xc057, 0xc059,
+ 0xc05b, 0xc05d, 0xc05f, 0xc061, 0xc063, 0xc065, 0xc067, 0xc069,
+ 0xc06b, 0xc06d, 0xc06f, 0xc071, 0xc073, 0xc075, 0xc077, 0xc079,
+ 0xc07b, 0xc07d, 0xc07f, 0xc081, 0xc083, 0xc085, 0xc087, 0xc089,
+ 0xc08b, 0xc08d, 0xc08f, 0xc091, 0xc093, 0xc095, 0xc097, 0xc099,
+ 0xc09b, 0xc0b1, 0xc0b3, 0xc0b5,
+ 0xd002
+ };
+
+ return tls_cipher_suite_match(sha384_cs, ARRAY_SIZE(sha384_cs), cs);
+}
+
+
+static int eap_teap_tls_mac(u16 tls_cs, const u8 *cmk, size_t cmk_len,
+ const u8 *buffer, size_t buffer_len,
+ u8 *mac, size_t mac_len)
+{
+ int res;
+ u8 tmp[48];
+
+ os_memset(tmp, 0, sizeof(tmp));
+ os_memset(mac, 0, mac_len);
+
+ if (tls_cipher_suite_mac_sha1(tls_cs)) {
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: MAC algorithm: HMAC-SHA1");
+ res = hmac_sha1(cmk, cmk_len, buffer, buffer_len, tmp);
+ } else if (tls_cipher_suite_mac_sha256(tls_cs)) {
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: MAC algorithm: HMAC-SHA256");
+ res = hmac_sha256(cmk, cmk_len, buffer, buffer_len, tmp);
+ } else if (tls_cipher_suite_mac_sha384(tls_cs)) {
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: MAC algorithm: HMAC-SHA384");
+ res = hmac_sha384(cmk, cmk_len, buffer, buffer_len, tmp);
+ } else {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Unsupported TLS cipher suite 0x%04x",
+ tls_cs);
+ res = -1;
+ }
+ if (res < 0)
+ return res;
+
+ /* FIX: RFC 7170 does not describe how to handle truncation of the
+ * Compound MAC or if the fields are supposed to be of variable length
+ * based on the negotiated TLS cipher suite (they are defined as having
+ * fixed size of 20 octets in the TLV description) */
+ if (mac_len > sizeof(tmp))
+ mac_len = sizeof(tmp);
+ os_memcpy(mac, tmp, mac_len);
+ return 0;
+}
+
+
+int eap_teap_compound_mac(u16 tls_cs, const struct teap_tlv_crypto_binding *cb,
+ const struct wpabuf *server_outer_tlvs,
+ const struct wpabuf *peer_outer_tlvs,
+ const u8 *cmk, u8 *compound_mac)
+{
+ u8 *pos, *buffer;
+ size_t bind_len, buffer_len;
+ struct teap_tlv_crypto_binding *tmp_cb;
+ int res;
+
+ /* RFC 7170, Section 5.3 */
+ bind_len = sizeof(struct teap_tlv_hdr) + be_to_host16(cb->length);
+ buffer_len = bind_len + 1;
+ if (server_outer_tlvs)
+ buffer_len += wpabuf_len(server_outer_tlvs);
+ if (peer_outer_tlvs)
+ buffer_len += wpabuf_len(peer_outer_tlvs);
+ buffer = os_malloc(buffer_len);
+ if (!buffer)
+ return -1;
+
+ pos = buffer;
+ /* 1. The entire Crypto-Binding TLV attribute with both the EMSK and MSK
+ * Compound MAC fields zeroed out. */
+ os_memcpy(pos, cb, bind_len);
+ pos += bind_len;
+ tmp_cb = (struct teap_tlv_crypto_binding *) buffer;
+ os_memset(tmp_cb->emsk_compound_mac, 0, EAP_TEAP_COMPOUND_MAC_LEN);
+ os_memset(tmp_cb->msk_compound_mac, 0, EAP_TEAP_COMPOUND_MAC_LEN);
+
+ /* 2. The EAP Type sent by the other party in the first TEAP message. */
+ /* This is supposed to be the EAP Type sent by the other party in the
+ * first TEAP message, but since we cannot get here without having
+ * successfully negotiated use of TEAP, this can only be the fixed EAP
+ * Type of TEAP. */
+ *pos++ = EAP_TYPE_TEAP;
+
+ /* 3. All the Outer TLVs from the first TEAP message sent by EAP server
+ * to peer. */
+ if (server_outer_tlvs) {
+ os_memcpy(pos, wpabuf_head(server_outer_tlvs),
+ wpabuf_len(server_outer_tlvs));
+ pos += wpabuf_len(server_outer_tlvs);
+ }
+
+ /* 4. All the Outer TLVs from the first TEAP message sent by the peer to
+ * the EAP server. */
+ if (peer_outer_tlvs) {
+ os_memcpy(pos, wpabuf_head(peer_outer_tlvs),
+ wpabuf_len(peer_outer_tlvs));
+ pos += wpabuf_len(peer_outer_tlvs);
+ }
+
+ buffer_len = pos - buffer;
+
+ wpa_hexdump_key(MSG_MSGDUMP,
+ "EAP-TEAP: CMK for Compound MAC calculation",
+ cmk, EAP_TEAP_CMK_LEN);
+ wpa_hexdump(MSG_MSGDUMP,
+ "EAP-TEAP: BUFFER for Compound MAC calculation",
+ buffer, buffer_len);
+ res = eap_teap_tls_mac(tls_cs, cmk, EAP_TEAP_CMK_LEN,
+ buffer, buffer_len,
+ compound_mac, EAP_TEAP_COMPOUND_MAC_LEN);
+ os_free(buffer);
+
+ return res;
+}
+
+
+int eap_teap_parse_tlv(struct eap_teap_tlv_parse *tlv,
+ int tlv_type, u8 *pos, size_t len)
+{
+ switch (tlv_type) {
+ case TEAP_TLV_RESULT:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Result TLV", pos, len);
+ if (tlv->result) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: More than one Result TLV in the message");
+ tlv->result = TEAP_STATUS_FAILURE;
+ return -2;
+ }
+ if (len < 2) {
+ wpa_printf(MSG_INFO, "EAP-TEAP: Too short Result TLV");
+ tlv->result = TEAP_STATUS_FAILURE;
+ break;
+ }
+ tlv->result = WPA_GET_BE16(pos);
+ if (tlv->result != TEAP_STATUS_SUCCESS &&
+ tlv->result != TEAP_STATUS_FAILURE) {
+ wpa_printf(MSG_INFO, "EAP-TEAP: Unknown Result %d",
+ tlv->result);
+ tlv->result = TEAP_STATUS_FAILURE;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Result: %s",
+ tlv->result == TEAP_STATUS_SUCCESS ?
+ "Success" : "Failure");
+ break;
+ case TEAP_TLV_NAK:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: NAK TLV", pos, len);
+ if (len < 6) {
+ wpa_printf(MSG_INFO, "EAP-TEAP: Too short NAK TLV");
+ tlv->result = TEAP_STATUS_FAILURE;
+ break;
+ }
+ tlv->nak = pos;
+ tlv->nak_len = len;
+ break;
+ case TEAP_TLV_REQUEST_ACTION:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Request-Action TLV",
+ pos, len);
+ if (tlv->request_action) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: More than one Request-Action TLV in the message");
+ tlv->iresult = TEAP_STATUS_FAILURE;
+ return -2;
+ }
+ if (len < 2) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Too short Request-Action TLV");
+ tlv->iresult = TEAP_STATUS_FAILURE;
+ break;
+ }
+ tlv->request_action_status = pos[0];
+ tlv->request_action = pos[1];
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Request-Action: Status=%u Action=%u",
+ tlv->request_action_status, tlv->request_action);
+ break;
+ case TEAP_TLV_EAP_PAYLOAD:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: EAP-Payload TLV",
+ pos, len);
+ if (tlv->eap_payload_tlv) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: More than one EAP-Payload TLV in the message");
+ tlv->iresult = TEAP_STATUS_FAILURE;
+ return -2;
+ }
+ tlv->eap_payload_tlv = pos;
+ tlv->eap_payload_tlv_len = len;
+ break;
+ case TEAP_TLV_INTERMEDIATE_RESULT:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Intermediate-Result TLV",
+ pos, len);
+ if (len < 2) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Too short Intermediate-Result TLV");
+ tlv->iresult = TEAP_STATUS_FAILURE;
+ break;
+ }
+ if (tlv->iresult) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: More than one Intermediate-Result TLV in the message");
+ tlv->iresult = TEAP_STATUS_FAILURE;
+ return -2;
+ }
+ tlv->iresult = WPA_GET_BE16(pos);
+ if (tlv->iresult != TEAP_STATUS_SUCCESS &&
+ tlv->iresult != TEAP_STATUS_FAILURE) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Unknown Intermediate Result %d",
+ tlv->iresult);
+ tlv->iresult = TEAP_STATUS_FAILURE;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Intermediate Result: %s",
+ tlv->iresult == TEAP_STATUS_SUCCESS ?
+ "Success" : "Failure");
+ break;
+ case TEAP_TLV_PAC:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: PAC TLV", pos, len);
+ if (tlv->pac) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: More than one PAC TLV in the message");
+ tlv->iresult = TEAP_STATUS_FAILURE;
+ return -2;
+ }
+ tlv->pac = pos;
+ tlv->pac_len = len;
+ break;
+ case TEAP_TLV_CRYPTO_BINDING:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Crypto-Binding TLV",
+ pos, len);
+ if (tlv->crypto_binding) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: More than one Crypto-Binding TLV in the message");
+ tlv->iresult = TEAP_STATUS_FAILURE;
+ return -2;
+ }
+ tlv->crypto_binding_len = sizeof(struct teap_tlv_hdr) + len;
+ if (tlv->crypto_binding_len < sizeof(*tlv->crypto_binding)) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Too short Crypto-Binding TLV");
+ tlv->iresult = TEAP_STATUS_FAILURE;
+ return -2;
+ }
+ tlv->crypto_binding = (struct teap_tlv_crypto_binding *)
+ (pos - sizeof(struct teap_tlv_hdr));
+ break;
+ case TEAP_TLV_BASIC_PASSWORD_AUTH_REQ:
+ wpa_hexdump_ascii(MSG_MSGDUMP,
+ "EAP-TEAP: Basic-Password-Auth-Req TLV",
+ pos, len);
+ if (tlv->basic_auth_req) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: More than one Basic-Password-Auth-Req TLV in the message");
+ tlv->iresult = TEAP_STATUS_FAILURE;
+ return -2;
+ }
+ tlv->basic_auth_req = pos;
+ tlv->basic_auth_req_len = len;
+ break;
+ case TEAP_TLV_BASIC_PASSWORD_AUTH_RESP:
+ wpa_hexdump_ascii(MSG_MSGDUMP,
+ "EAP-TEAP: Basic-Password-Auth-Resp TLV",
+ pos, len);
+ if (tlv->basic_auth_resp) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: More than one Basic-Password-Auth-Resp TLV in the message");
+ tlv->iresult = TEAP_STATUS_FAILURE;
+ return -2;
+ }
+ tlv->basic_auth_resp = pos;
+ tlv->basic_auth_resp_len = len;
+ break;
+ default:
+ /* Unknown TLV */
+ return -1;
+ }
+
+ return 0;
+}
+
+
+const char * eap_teap_tlv_type_str(enum teap_tlv_types type)
+{
+ switch (type) {
+ case TEAP_TLV_AUTHORITY_ID:
+ return "Authority-ID";
+ case TEAP_TLV_IDENTITY_TYPE:
+ return "Identity-Type";
+ case TEAP_TLV_RESULT:
+ return "Result";
+ case TEAP_TLV_NAK:
+ return "NAK";
+ case TEAP_TLV_ERROR:
+ return "Error";
+ case TEAP_TLV_CHANNEL_BINDING:
+ return "Channel-Binding";
+ case TEAP_TLV_VENDOR_SPECIFIC:
+ return "Vendor-Specific";
+ case TEAP_TLV_REQUEST_ACTION:
+ return "Request-Action";
+ case TEAP_TLV_EAP_PAYLOAD:
+ return "EAP-Payload";
+ case TEAP_TLV_INTERMEDIATE_RESULT:
+ return "Intermediate-Result";
+ case TEAP_TLV_PAC:
+ return "PAC";
+ case TEAP_TLV_CRYPTO_BINDING:
+ return "Crypto-Binding";
+ case TEAP_TLV_BASIC_PASSWORD_AUTH_REQ:
+ return "Basic-Password-Auth-Req";
+ case TEAP_TLV_BASIC_PASSWORD_AUTH_RESP:
+ return "Basic-Password-Auth-Resp";
+ case TEAP_TLV_PKCS7:
+ return "PKCS#7";
+ case TEAP_TLV_PKCS10:
+ return "PKCS#10";
+ case TEAP_TLV_TRUSTED_SERVER_ROOT:
+ return "Trusted-Server-Root";
+ }
+
+ return "?";
+}
+
+
+struct wpabuf * eap_teap_tlv_result(int status, int intermediate)
+{
+ struct wpabuf *buf;
+ struct teap_tlv_result *result;
+
+ if (status != TEAP_STATUS_FAILURE && status != TEAP_STATUS_SUCCESS)
+ return NULL;
+
+ buf = wpabuf_alloc(sizeof(*result));
+ if (!buf)
+ return NULL;
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Add %sResult TLV(status=%s)",
+ intermediate ? "Intermediate-" : "",
+ status == TEAP_STATUS_SUCCESS ? "Success" : "Failure");
+ result = wpabuf_put(buf, sizeof(*result));
+ result->tlv_type = host_to_be16(TEAP_TLV_MANDATORY |
+ (intermediate ?
+ TEAP_TLV_INTERMEDIATE_RESULT :
+ TEAP_TLV_RESULT));
+ result->length = host_to_be16(2);
+ result->status = host_to_be16(status);
+ return buf;
+}
+
+
+struct wpabuf * eap_teap_tlv_error(enum teap_error_codes error)
+{
+ struct wpabuf *buf;
+
+ buf = wpabuf_alloc(4 + 4);
+ if (!buf)
+ return NULL;
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Add Error TLV(Error Code=%d)",
+ error);
+ wpabuf_put_be16(buf, TEAP_TLV_MANDATORY | TEAP_TLV_ERROR);
+ wpabuf_put_be16(buf, 4);
+ wpabuf_put_be32(buf, error);
+ return buf;
+}
+
+
+int eap_teap_allowed_anon_prov_phase2_method(u8 type)
+{
+ /* RFC 7170, Section 3.8.3: MUST provide mutual authentication,
+ * provide key generation, and be resistant to dictionary attack.
+ * Section 3.8 also mentions requirement for using EMSK Compound MAC. */
+ return type == EAP_TYPE_PWD || type == EAP_TYPE_EKE;
+}
+
+
+int eap_teap_allowed_anon_prov_cipher_suite(u16 cs)
+{
+ /* RFC 7170, Section 3.8.3: anonymous ciphersuites MAY be supported as
+ * long as the TLS pre-master secret is generated form contribution from
+ * both peers. Accept the recommended TLS_DH_anon_WITH_AES_128_CBC_SHA
+ * cipher suite and other ciphersuites that use DH in some form, have
+ * SHA-1 or stronger MAC function, and use reasonable strong cipher. */
+ static const u16 ok_cs[] = {
+ /* DH-anon */
+ 0x0034, 0x003a, 0x006c, 0x006d, 0x00a6, 0x00a7,
+ /* DHE-RSA */
+ 0x0033, 0x0039, 0x0067, 0x006b, 0x009e, 0x009f,
+ /* ECDH-anon */
+ 0xc018, 0xc019,
+ /* ECDH-RSA */
+ 0xc003, 0xc00f, 0xc029, 0xc02a, 0xc031, 0xc032,
+ /* ECDH-ECDSA */
+ 0xc004, 0xc005, 0xc025, 0xc026, 0xc02d, 0xc02e,
+ /* ECDHE-RSA */
+ 0xc013, 0xc014, 0xc027, 0xc028, 0xc02f, 0xc030,
+ /* ECDHE-ECDSA */
+ 0xc009, 0xc00a, 0xc023, 0xc024, 0xc02b, 0xc02c,
+ };
+
+ return tls_cipher_suite_match(ok_cs, ARRAY_SIZE(ok_cs), cs);
+}
--- /dev/null
+/*
+ * EAP-TEAP definitions (RFC 7170)
+ * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_TEAP_H
+#define EAP_TEAP_H
+
+#define EAP_TEAP_VERSION 1
+#define EAP_TEAP_KEY_LEN 64
+#define EAP_TEAP_IMCK_LEN 60
+#define EAP_TEAP_SIMCK_LEN 40
+#define EAP_TEAP_CMK_LEN 20
+#define EAP_TEAP_COMPOUND_MAC_LEN 20
+#define EAP_TEAP_NONCE_LEN 32
+
+#define TEAP_TLS_EXPORTER_LABEL_SKS "EXPORTER: teap session key seed"
+
+#define TLS_EXT_PAC_OPAQUE 35
+
+/*
+ * RFC 7170: Section 4.2.12.1 - Formats for PAC Attributes
+ * Note: bit 0x8000 (Mandatory) and bit 0x4000 (Reserved) are also defined
+ * in the general TLV format (Section 4.2.1).
+ */
+#define PAC_TYPE_PAC_KEY 1
+#define PAC_TYPE_PAC_OPAQUE 2
+#define PAC_TYPE_CRED_LIFETIME 3
+#define PAC_TYPE_A_ID 4
+#define PAC_TYPE_I_ID 5
+/* 6 - Reserved */
+#define PAC_TYPE_A_ID_INFO 7
+#define PAC_TYPE_PAC_ACKNOWLEDGEMENT 8
+#define PAC_TYPE_PAC_INFO 9
+#define PAC_TYPE_PAC_TYPE 10
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct pac_attr_hdr {
+ be16 type;
+ be16 len;
+} STRUCT_PACKED;
+
+struct teap_tlv_hdr {
+ be16 tlv_type;
+ be16 length;
+} STRUCT_PACKED;
+
+/* Result TLV and Intermediate-Result TLV */
+struct teap_tlv_result {
+ be16 tlv_type;
+ be16 length;
+ be16 status;
+ /* for Intermediate-Result TLV, followed by optional TLVs */
+} STRUCT_PACKED;
+
+struct teap_tlv_nak {
+ be16 tlv_type;
+ be16 length;
+ be32 vendor_id;
+ be16 nak_type;
+ /* followed by optional TLVs */
+} STRUCT_PACKED;
+
+struct teap_tlv_crypto_binding {
+ be16 tlv_type; /* TLV Type[14b] and M/R flags */
+ be16 length;
+ u8 reserved;
+ u8 version;
+ u8 received_version;
+ u8 subtype; /* Flags[4b] and Sub-Type[4b] */
+ u8 nonce[EAP_TEAP_NONCE_LEN];
+ u8 emsk_compound_mac[EAP_TEAP_COMPOUND_MAC_LEN];
+ u8 msk_compound_mac[EAP_TEAP_COMPOUND_MAC_LEN];
+} STRUCT_PACKED;
+
+struct teap_tlv_request_action {
+ be16 tlv_type;
+ be16 length;
+ u8 status;
+ u8 action;
+ /* followed by optional TLVs */
+} STRUCT_PACKED;
+
+enum teap_request_action {
+ TEAP_REQUEST_ACTION_PROCESS_TLV = 1,
+ TEAP_REQUEST_ACTION_NEGOTIATE_EAP = 2,
+};
+
+/* PAC TLV with PAC-Acknowledgement TLV attribute */
+struct teap_tlv_pac_ack {
+ be16 tlv_type;
+ be16 length;
+ be16 pac_type;
+ be16 pac_len;
+ be16 result;
+} STRUCT_PACKED;
+
+struct teap_attr_pac_type {
+ be16 type; /* PAC_TYPE_PAC_TYPE */
+ be16 length; /* 2 */
+ be16 pac_type;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+#define TEAP_CRYPTO_BINDING_SUBTYPE_REQUEST 0
+#define TEAP_CRYPTO_BINDING_SUBTYPE_RESPONSE 1
+
+#define TEAP_CRYPTO_BINDING_EMSK_CMAC 1
+#define TEAP_CRYPTO_BINDING_MSK_CMAC 2
+#define TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC 3
+
+
+#define EAP_TEAP_PAC_KEY_LEN 48
+
+/* RFC 7170: 4.2.12.6 PAC-Type TLV */
+#define PAC_TYPE_TUNNEL_PAC 1
+
+
+/* RFC 7170, 4.2.1: General TLV Format */
+enum teap_tlv_types {
+ TEAP_TLV_AUTHORITY_ID = 1,
+ TEAP_TLV_IDENTITY_TYPE = 2,
+ TEAP_TLV_RESULT = 3,
+ TEAP_TLV_NAK = 4,
+ TEAP_TLV_ERROR = 5,
+ TEAP_TLV_CHANNEL_BINDING = 6,
+ TEAP_TLV_VENDOR_SPECIFIC = 7,
+ TEAP_TLV_REQUEST_ACTION = 8,
+ TEAP_TLV_EAP_PAYLOAD = 9,
+ TEAP_TLV_INTERMEDIATE_RESULT = 10,
+ TEAP_TLV_PAC = 11,
+ TEAP_TLV_CRYPTO_BINDING = 12,
+ TEAP_TLV_BASIC_PASSWORD_AUTH_REQ = 13,
+ TEAP_TLV_BASIC_PASSWORD_AUTH_RESP = 14,
+ TEAP_TLV_PKCS7 = 15,
+ TEAP_TLV_PKCS10 = 16,
+ TEAP_TLV_TRUSTED_SERVER_ROOT = 17,
+};
+
+enum teap_tlv_result_status {
+ TEAP_STATUS_SUCCESS = 1,
+ TEAP_STATUS_FAILURE = 2
+};
+
+#define TEAP_TLV_MANDATORY 0x8000
+#define TEAP_TLV_TYPE_MASK 0x3fff
+
+/* RFC 7170, 4.2.6: Error TLV */
+enum teap_error_codes {
+ TEAP_ERROR_INNER_METHOD = 1001,
+ TEAP_ERROR_UNSPEC_AUTH_INFRA_PROBLEM = 1002,
+ TEAP_ERROR_UNSPEC_AUTHENTICATION_FAILURE = 1003,
+ TEAP_ERROR_UNSPEC_AUTHORIZATION_FAILURE = 1004,
+ TEAP_ERROR_USER_ACCOUNT_CRED_UNAVAILABLE = 1005,
+ TEAP_ERROR_USER_ACCOUNT_EXPIRED = 1006,
+ TEAP_ERROR_USER_ACCOUNT_LOCKED_TRY_AGAIN_LATER = 1007,
+ TEAP_ERROR_USER_ACCOUNT_LOCKED_ADMIN_REQ = 1008,
+ TEAP_ERROR_TUNNEL_COMPROMISE_ERROR = 2001,
+ TEAP_ERROR_UNEXPECTED_TLVS_EXCHANGED = 2002,
+};
+
+struct wpabuf;
+struct tls_connection;
+
+struct eap_teap_tlv_parse {
+ u8 *eap_payload_tlv;
+ size_t eap_payload_tlv_len;
+ struct teap_tlv_crypto_binding *crypto_binding;
+ size_t crypto_binding_len;
+ int iresult;
+ int result;
+ u8 *nak;
+ size_t nak_len;
+ u8 request_action;
+ u8 request_action_status;
+ u8 *pac;
+ size_t pac_len;
+ u8 *basic_auth_req;
+ size_t basic_auth_req_len;
+ u8 *basic_auth_resp;
+ size_t basic_auth_resp_len;
+};
+
+void eap_teap_put_tlv_hdr(struct wpabuf *buf, u16 type, u16 len);
+void eap_teap_put_tlv(struct wpabuf *buf, u16 type, const void *data, u16 len);
+void eap_teap_put_tlv_buf(struct wpabuf *buf, u16 type,
+ const struct wpabuf *data);
+struct wpabuf * eap_teap_tlv_eap_payload(struct wpabuf *buf);
+int eap_teap_derive_eap_msk(const u8 *simck, u8 *msk);
+int eap_teap_derive_eap_emsk(const u8 *simck, u8 *emsk);
+int eap_teap_derive_cmk_basic_pw_auth(const u8 *s_imck_msk, u8 *cmk);
+int eap_teap_derive_imck(const u8 *prev_s_imck_msk, const u8 *prev_s_imck_emsk,
+ const u8 *msk, size_t msk_len,
+ const u8 *emsk, size_t emsk_len,
+ u8 *s_imck_msk, u8 *cmk_msk,
+ u8 *s_imck_emsk, u8 *cmk_emsk);
+int eap_teap_compound_mac(u16 tls_cs, const struct teap_tlv_crypto_binding *cb,
+ const struct wpabuf *server_outer_tlvs,
+ const struct wpabuf *peer_outer_tlvs,
+ const u8 *cmk, u8 *compound_mac);
+int eap_teap_parse_tlv(struct eap_teap_tlv_parse *tlv,
+ int tlv_type, u8 *pos, size_t len);
+const char * eap_teap_tlv_type_str(enum teap_tlv_types type);
+struct wpabuf * eap_teap_tlv_result(int status, int intermediate);
+struct wpabuf * eap_teap_tlv_error(enum teap_error_codes error);
+int eap_teap_allowed_anon_prov_phase2_method(u8 type);
+int eap_teap_allowed_anon_prov_cipher_suite(u16 cs);
+
+#endif /* EAP_TEAP_H */
if (vendor != EAP_VENDOR_IETF)
return 0;
return type != EAP_TYPE_PEAP && type != EAP_TYPE_TTLS &&
- type != EAP_TYPE_FAST;
+ type != EAP_TYPE_FAST && type != EAP_TYPE_TEAP;
}
EXT_CERT_CHECK_GOOD,
EXT_CERT_CHECK_BAD,
} pending_ext_cert_check;
+
+ int teap_anon_dh;
};
int eap_peer_aka_register(void);
int eap_peer_aka_prime_register(void);
int eap_peer_fast_register(void);
+int eap_peer_teap_register(void);
int eap_peer_pax_register(void);
int eap_peer_sake_register(void);
int eap_peer_gpsk_register(void);
--- /dev/null
+/*
+ * EAP peer method: EAP-TEAP (RFC 7170)
+ * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/tls.h"
+#include "eap_common/eap_teap_common.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "eap_config.h"
+#include "eap_teap_pac.h"
+
+#ifdef EAP_TEAP_DYNAMIC
+#include "eap_teap_pac.c"
+#endif /* EAP_TEAP_DYNAMIC */
+
+
+static void eap_teap_deinit(struct eap_sm *sm, void *priv);
+
+
+struct eap_teap_data {
+ struct eap_ssl_data ssl;
+
+ u8 teap_version; /* Negotiated version */
+ u8 received_version; /* Version number received during negotiation */
+ u16 tls_cs;
+
+ const struct eap_method *phase2_method;
+ void *phase2_priv;
+ int phase2_success;
+ int inner_method_done;
+ int result_success_done;
+
+ struct eap_method_type phase2_type;
+ struct eap_method_type *phase2_types;
+ size_t num_phase2_types;
+ int resuming; /* starting a resumed session */
+#define EAP_TEAP_PROV_UNAUTH 1
+#define EAP_TEAP_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;
+ int test_outer_tlvs;
+
+ u8 key_data[EAP_TEAP_KEY_LEN];
+ u8 *session_id;
+ size_t id_len;
+ u8 emsk[EAP_EMSK_LEN];
+ int success;
+
+ struct eap_teap_pac *pac;
+ struct eap_teap_pac *current_pac;
+ size_t max_pac_list_len;
+ int use_pac_binary_format;
+
+ u8 simck_msk[EAP_TEAP_SIMCK_LEN];
+ u8 simck_emsk[EAP_TEAP_SIMCK_LEN];
+ int simck_idx;
+ int cmk_emsk_available;
+
+ struct wpabuf *pending_phase2_req;
+ struct wpabuf *pending_resp;
+ struct wpabuf *server_outer_tlvs;
+ struct wpabuf *peer_outer_tlvs;
+};
+
+
+static int eap_teap_session_ticket_cb(void *ctx, const u8 *ticket, size_t len,
+ const u8 *client_random,
+ const u8 *server_random,
+ u8 *master_secret)
+{
+ struct eap_teap_data *data = ctx;
+
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: SessionTicket callback");
+
+ if (!master_secret) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: SessionTicket failed - fall back to full TLS handshake");
+ data->session_ticket_used = 0;
+ if (data->provisioning_allowed) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Try to provision a new PAC-Key");
+ data->provisioning = 1;
+ data->current_pac = NULL;
+ }
+ return 0;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "EAP-TEAP: SessionTicket", ticket, len);
+
+ if (!data->current_pac) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: No PAC-Key available for using SessionTicket");
+ data->session_ticket_used = 0;
+ return 0;
+ }
+
+ /* EAP-TEAP uses PAC-Key as the TLS master_secret */
+ os_memcpy(master_secret, data->current_pac->pac_key,
+ EAP_TEAP_PAC_KEY_LEN);
+
+ data->session_ticket_used = 1;
+
+ return 1;
+}
+
+
+static void eap_teap_parse_phase1(struct eap_teap_data *data,
+ const char *phase1)
+{
+ const char *pos;
+
+ pos = os_strstr(phase1, "teap_provisioning=");
+ if (pos) {
+ data->provisioning_allowed = atoi(pos + 18);
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Automatic PAC provisioning mode: %d",
+ data->provisioning_allowed);
+ }
+
+ pos = os_strstr(phase1, "teap_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-TEAP: Maximum PAC list length: %lu",
+ (unsigned long) data->max_pac_list_len);
+ }
+
+ if (os_strstr(phase1, "teap_pac_format=binary")) {
+ data->use_pac_binary_format = 1;
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Using binary format for PAC list");
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (os_strstr(phase1, "teap_test_outer_tlvs=1"))
+ data->test_outer_tlvs = 1;
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
+static void * eap_teap_init(struct eap_sm *sm)
+{
+ struct eap_teap_data *data;
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ if (!config)
+ return NULL;
+
+ data = os_zalloc(sizeof(*data));
+ if (!data)
+ return NULL;
+ data->teap_version = EAP_TEAP_VERSION;
+ data->max_pac_list_len = 10;
+
+ if (config->phase1)
+ eap_teap_parse_phase1(data, config->phase1);
+
+ if ((data->provisioning_allowed & EAP_TEAP_PROV_AUTH) &&
+ !config->ca_cert && !config->ca_path) {
+ /* Prevent PAC provisioning without mutual authentication
+ * (either by validating server certificate or by suitable
+ * inner EAP method). */
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Disable authenticated provisioning due to no ca_cert/ca_path");
+ data->provisioning_allowed &= ~EAP_TEAP_PROV_AUTH;
+ }
+
+ if (eap_peer_select_phase2_methods(config, "auth=",
+ &data->phase2_types,
+ &data->num_phase2_types) < 0) {
+ eap_teap_deinit(sm, data);
+ return NULL;
+ }
+
+ data->phase2_type.vendor = EAP_VENDOR_IETF;
+ data->phase2_type.method = EAP_TYPE_NONE;
+
+ config->teap_anon_dh = !!(data->provisioning_allowed &
+ EAP_TEAP_PROV_UNAUTH);
+ if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TEAP)) {
+ wpa_printf(MSG_INFO, "EAP-TEAP: Failed to initialize SSL");
+ eap_teap_deinit(sm, data);
+ return NULL;
+ }
+
+ if (tls_connection_set_session_ticket_cb(sm->ssl_ctx, data->ssl.conn,
+ eap_teap_session_ticket_cb,
+ data) < 0) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Failed to set SessionTicket callback");
+ eap_teap_deinit(sm, data);
+ return NULL;
+ }
+
+ if (!config->pac_file) {
+ wpa_printf(MSG_INFO, "EAP-TEAP: No PAC file configured");
+ eap_teap_deinit(sm, data);
+ return NULL;
+ }
+
+ if (data->use_pac_binary_format &&
+ eap_teap_load_pac_bin(sm, &data->pac, config->pac_file) < 0) {
+ wpa_printf(MSG_INFO, "EAP-TEAP: Failed to load PAC file");
+ eap_teap_deinit(sm, data);
+ return NULL;
+ }
+
+ if (!data->use_pac_binary_format &&
+ eap_teap_load_pac(sm, &data->pac, config->pac_file) < 0) {
+ wpa_printf(MSG_INFO, "EAP-TEAP: Failed to load PAC file");
+ eap_teap_deinit(sm, data);
+ return NULL;
+ }
+ eap_teap_pac_list_truncate(data->pac, data->max_pac_list_len);
+
+ return data;
+}
+
+
+static void eap_teap_clear(struct eap_teap_data *data)
+{
+ forced_memzero(data->key_data, EAP_TEAP_KEY_LEN);
+ forced_memzero(data->emsk, EAP_EMSK_LEN);
+ os_free(data->session_id);
+ data->session_id = NULL;
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = NULL;
+ wpabuf_free(data->pending_resp);
+ data->pending_resp = NULL;
+ wpabuf_free(data->server_outer_tlvs);
+ data->server_outer_tlvs = NULL;
+ wpabuf_free(data->peer_outer_tlvs);
+ data->peer_outer_tlvs = NULL;
+ forced_memzero(data->simck_msk, EAP_TEAP_SIMCK_LEN);
+ forced_memzero(data->simck_emsk, EAP_TEAP_SIMCK_LEN);
+}
+
+
+static void eap_teap_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_teap_data *data = priv;
+ struct eap_teap_pac *pac, *prev;
+
+ if (!data)
+ return;
+ if (data->phase2_priv && data->phase2_method)
+ data->phase2_method->deinit(sm, data->phase2_priv);
+ eap_teap_clear(data);
+ os_free(data->phase2_types);
+ eap_peer_tls_ssl_deinit(sm, &data->ssl);
+
+ pac = data->pac;
+ prev = NULL;
+ while (pac) {
+ prev = pac;
+ pac = pac->next;
+ eap_teap_free_pac(prev);
+ }
+
+ os_free(data);
+}
+
+
+static int eap_teap_derive_msk(struct eap_teap_data *data)
+{
+ /* FIX: RFC 7170 does not describe whether MSK or EMSK based S-IMCK[j]
+ * is used in this derivation */
+ if (eap_teap_derive_eap_msk(data->simck_msk, data->key_data) < 0 ||
+ eap_teap_derive_eap_emsk(data->simck_msk, data->emsk) < 0)
+ return -1;
+ data->success = 1;
+ return 0;
+}
+
+
+static int eap_teap_derive_key_auth(struct eap_sm *sm,
+ struct eap_teap_data *data)
+{
+ int res;
+
+ /* RFC 7170, Section 5.1 */
+ res = tls_connection_export_key(sm->ssl_ctx, data->ssl.conn,
+ TEAP_TLS_EXPORTER_LABEL_SKS, NULL, 0,
+ data->simck_msk, EAP_TEAP_SIMCK_LEN);
+ if (res)
+ return res;
+ wpa_hexdump_key(MSG_DEBUG,
+ "EAP-TEAP: session_key_seed (S-IMCK[0])",
+ data->simck_msk, EAP_TEAP_SIMCK_LEN);
+ os_memcpy(data->simck_emsk, data->simck_msk, EAP_TEAP_SIMCK_LEN);
+ data->simck_idx = 0;
+ return 0;
+}
+
+
+static int eap_teap_init_phase2_method(struct eap_sm *sm,
+ struct eap_teap_data *data)
+{
+ data->inner_method_done = 0;
+ data->phase2_method =
+ eap_peer_get_eap_method(data->phase2_type.vendor,
+ data->phase2_type.method);
+ if (!data->phase2_method)
+ return -1;
+
+ sm->init_phase2 = 1;
+ data->phase2_priv = data->phase2_method->init(sm);
+ sm->init_phase2 = 0;
+
+ return data->phase2_priv == NULL ? -1 : 0;
+}
+
+
+static int eap_teap_select_phase2_method(struct eap_teap_data *data, u8 type)
+{
+ size_t i;
+
+ /* TODO: TNC with anonymous provisioning; need to require both
+ * completed inner EAP authentication (EAP-pwd or EAP-EKE) and TNC */
+
+ if (data->anon_provisioning &&
+ !eap_teap_allowed_anon_prov_phase2_method(type)) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: EAP type %u not allowed during unauthenticated provisioning",
+ type);
+ return -1;
+ }
+
+#ifdef EAP_TNC
+ if (type == EAP_TYPE_TNC) {
+ data->phase2_type.vendor = EAP_VENDOR_IETF;
+ data->phase2_type.method = EAP_TYPE_TNC;
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Selected Phase 2 EAP vendor %d method %d for TNC",
+ data->phase2_type.vendor,
+ data->phase2_type.method);
+ return 0;
+ }
+#endif /* EAP_TNC */
+
+ 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-TEAP: 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_teap_phase2_request(struct eap_sm *sm,
+ struct eap_teap_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-TEAP: too short Phase 2 request (len=%lu)",
+ (unsigned long) len);
+ return -1;
+ }
+ pos = (u8 *) (hdr + 1);
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Phase 2 Request: type=%d", *pos);
+ if (*pos == EAP_TYPE_IDENTITY) {
+ *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1);
+ return 0;
+ }
+
+ if (data->phase2_priv && data->phase2_method &&
+ *pos != data->phase2_type.method) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Phase 2 EAP sequence - deinitialize previous method");
+ data->phase2_method->deinit(sm, data->phase2_priv);
+ data->phase2_method = NULL;
+ data->phase2_priv = NULL;
+ data->phase2_type.vendor = EAP_VENDOR_IETF;
+ data->phase2_type.method = EAP_TYPE_NONE;
+ }
+
+ if (data->phase2_type.vendor == EAP_VENDOR_IETF &&
+ data->phase2_type.method == EAP_TYPE_NONE &&
+ eap_teap_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 && eap_teap_init_phase2_method(sm, data) < 0) ||
+ !data->phase2_method) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: 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 (iret.methodState == METHOD_DONE)
+ data->inner_method_done = 1;
+ if (!(*resp) ||
+ (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) && config &&
+ (config->pending_req_identity || config->pending_req_password ||
+ config->pending_req_otp || config->pending_req_new_password ||
+ config->pending_req_sim)) {
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = wpabuf_alloc_copy(hdr, len);
+ } else if (!(*resp))
+ return -1;
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_teap_tlv_nak(int vendor_id, int tlv_type)
+{
+ struct wpabuf *buf;
+ struct teap_tlv_nak *nak;
+
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Add NAK TLV (Vendor-Id %u NAK-Type %u)",
+ vendor_id, tlv_type);
+ buf = wpabuf_alloc(sizeof(*nak));
+ if (!buf)
+ return NULL;
+ nak = wpabuf_put(buf, sizeof(*nak));
+ nak->tlv_type = host_to_be16(TEAP_TLV_MANDATORY | TEAP_TLV_NAK);
+ 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_teap_tlv_pac_ack(void)
+{
+ struct wpabuf *buf;
+ struct teap_tlv_result *res;
+ struct teap_tlv_pac_ack *ack;
+
+ buf = wpabuf_alloc(sizeof(*res) + sizeof(*ack));
+ if (!buf)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Add PAC TLV (ack)");
+ ack = wpabuf_put(buf, sizeof(*ack));
+ ack->tlv_type = host_to_be16(TEAP_TLV_PAC | TEAP_TLV_MANDATORY);
+ ack->length = host_to_be16(sizeof(*ack) - sizeof(struct teap_tlv_hdr));
+ ack->pac_type = host_to_be16(PAC_TYPE_PAC_ACKNOWLEDGEMENT);
+ ack->pac_len = host_to_be16(2);
+ ack->result = host_to_be16(TEAP_STATUS_SUCCESS);
+
+ return buf;
+}
+
+
+static struct wpabuf * eap_teap_process_eap_payload_tlv(
+ struct eap_sm *sm, struct eap_teap_data *data,
+ struct eap_method_ret *ret,
+ 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-TEAP: 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-TEAP: EAP packet overflow in EAP Payload TLV");
+ return NULL;
+ }
+
+ if (hdr->code != EAP_CODE_REQUEST) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Unexpected code=%d in Phase 2 EAP header",
+ hdr->code);
+ return NULL;
+ }
+
+ if (eap_teap_phase2_request(sm, data, ret, hdr, &resp)) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Phase 2 Request processing failed");
+ return NULL;
+ }
+
+ return eap_teap_tlv_eap_payload(resp);
+}
+
+
+static struct wpabuf * eap_teap_process_basic_auth_req(
+ struct eap_sm *sm, struct eap_teap_data *data,
+ u8 *basic_auth_req, size_t basic_auth_req_len)
+{
+ const u8 *identity, *password;
+ size_t identity_len, password_len, plen;
+ struct wpabuf *resp;
+
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-TEAP: Basic-Password-Auth-Req prompt",
+ basic_auth_req, basic_auth_req_len);
+ /* TODO: send over control interface */
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ password = eap_get_config_password(sm, &password_len);
+ if (!identity || !password ||
+ identity_len > 255 || password_len > 255) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: No username/password suitable for Basic-Password-Auth");
+ return eap_teap_tlv_nak(0, TEAP_TLV_BASIC_PASSWORD_AUTH_REQ);
+ }
+
+ plen = 1 + identity_len + 1 + password_len;
+ resp = wpabuf_alloc(sizeof(struct teap_tlv_hdr) + plen);
+ if (!resp)
+ return NULL;
+ eap_teap_put_tlv_hdr(resp, TEAP_TLV_BASIC_PASSWORD_AUTH_RESP, plen);
+ wpabuf_put_u8(resp, identity_len);
+ wpabuf_put_data(resp, identity, identity_len);
+ wpabuf_put_u8(resp, password_len);
+ wpabuf_put_data(resp, password, password_len);
+ wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TEAP: Basic-Password-Auth-Resp",
+ resp);
+
+ /* Assume this succeeds so that Result TLV(Success) from the server can
+ * be used to terminate TEAP. */
+ data->phase2_success = 1;
+
+ return resp;
+}
+
+
+static int
+eap_teap_validate_crypto_binding(struct eap_teap_data *data,
+ const struct teap_tlv_crypto_binding *cb)
+{
+ u8 flags, subtype;
+
+ subtype = cb->subtype & 0x0f;
+ flags = cb->subtype >> 4;
+
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Crypto-Binding TLV: Version %u Received Version %u Flags %u Sub-Type %u",
+ cb->version, cb->received_version, flags, subtype);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Nonce",
+ cb->nonce, sizeof(cb->nonce));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: EMSK Compound MAC",
+ cb->emsk_compound_mac, sizeof(cb->emsk_compound_mac));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: MSK Compound MAC",
+ cb->msk_compound_mac, sizeof(cb->msk_compound_mac));
+
+ if (cb->version != EAP_TEAP_VERSION ||
+ cb->received_version != data->received_version ||
+ subtype != TEAP_CRYPTO_BINDING_SUBTYPE_REQUEST ||
+ flags < 1 || flags > 3) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Invalid Version/Flags/Sub-Type in Crypto-Binding TLV: Version %u Received Version %u Flags %u Sub-Type %u",
+ cb->version, cb->received_version, flags, subtype);
+ return -1;
+ }
+
+ if (cb->nonce[EAP_TEAP_NONCE_LEN - 1] & 0x01) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Invalid Crypto-Binding TLV Nonce in request");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int eap_teap_write_crypto_binding(
+ struct eap_teap_data *data,
+ struct teap_tlv_crypto_binding *rbind,
+ const struct teap_tlv_crypto_binding *cb,
+ const u8 *cmk_msk, const u8 *cmk_emsk)
+{
+ u8 subtype, flags;
+
+ rbind->tlv_type = host_to_be16(TEAP_TLV_MANDATORY |
+ TEAP_TLV_CRYPTO_BINDING);
+ rbind->length = host_to_be16(sizeof(*rbind) -
+ sizeof(struct teap_tlv_hdr));
+ rbind->version = EAP_TEAP_VERSION;
+ rbind->received_version = data->received_version;
+ /* FIX: RFC 7170 is not clear on which Flags value to use when
+ * Crypto-Binding TLV is used with Basic-Password-Auth */
+ flags = cmk_emsk ? TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC :
+ TEAP_CRYPTO_BINDING_MSK_CMAC;
+ subtype = TEAP_CRYPTO_BINDING_SUBTYPE_RESPONSE;
+ rbind->subtype = (flags << 4) | subtype;
+ os_memcpy(rbind->nonce, cb->nonce, sizeof(cb->nonce));
+ inc_byte_array(rbind->nonce, sizeof(rbind->nonce));
+ os_memset(rbind->emsk_compound_mac, 0, EAP_TEAP_COMPOUND_MAC_LEN);
+ os_memset(rbind->msk_compound_mac, 0, EAP_TEAP_COMPOUND_MAC_LEN);
+
+ if (eap_teap_compound_mac(data->tls_cs, rbind, data->server_outer_tlvs,
+ data->peer_outer_tlvs, cmk_msk,
+ rbind->msk_compound_mac) < 0)
+ return -1;
+ if (cmk_emsk &&
+ eap_teap_compound_mac(data->tls_cs, rbind, data->server_outer_tlvs,
+ data->peer_outer_tlvs, cmk_emsk,
+ rbind->emsk_compound_mac) < 0)
+ return -1;
+
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Reply Crypto-Binding TLV: Version %u Received Version %u Flags %u SubType %u",
+ rbind->version, rbind->received_version, flags, subtype);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Nonce",
+ rbind->nonce, sizeof(rbind->nonce));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: EMSK Compound MAC",
+ rbind->emsk_compound_mac, sizeof(rbind->emsk_compound_mac));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: MSK Compound MAC",
+ rbind->msk_compound_mac, sizeof(rbind->msk_compound_mac));
+
+ return 0;
+}
+
+
+static int eap_teap_get_cmk(struct eap_sm *sm, struct eap_teap_data *data,
+ u8 *cmk_msk, u8 *cmk_emsk)
+{
+ u8 *msk = NULL, *emsk = NULL;
+ size_t msk_len = 0, emsk_len = 0;
+ int res;
+
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Determining CMK[%d] for Compound MAC calculation",
+ data->simck_idx + 1);
+
+ if (!data->phase2_method)
+ return eap_teap_derive_cmk_basic_pw_auth(data->simck_msk,
+ cmk_msk);
+
+ if (!data->phase2_method || !data->phase2_priv) {
+ wpa_printf(MSG_INFO, "EAP-TEAP: Phase 2 method not available");
+ return -1;
+ }
+
+ if (data->phase2_method->isKeyAvailable &&
+ !data->phase2_method->isKeyAvailable(sm, data->phase2_priv)) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Phase 2 key material not available");
+ return -1;
+ }
+
+ if (data->phase2_method->isKeyAvailable &&
+ data->phase2_method->getKey) {
+ msk = data->phase2_method->getKey(sm, data->phase2_priv,
+ &msk_len);
+ if (!msk) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Could not fetch Phase 2 MSK");
+ return -1;
+ }
+ }
+
+ if (data->phase2_method->isKeyAvailable &&
+ data->phase2_method->get_emsk) {
+ emsk = data->phase2_method->get_emsk(sm, data->phase2_priv,
+ &emsk_len);
+ }
+
+ res = eap_teap_derive_imck(data->simck_msk, data->simck_emsk,
+ msk, msk_len, emsk, emsk_len,
+ data->simck_msk, cmk_msk,
+ data->simck_emsk, cmk_emsk);
+ bin_clear_free(msk, msk_len);
+ bin_clear_free(emsk, emsk_len);
+ if (res == 0) {
+ data->simck_idx++;
+ if (emsk)
+ data->cmk_emsk_available = 1;
+ }
+ return res;
+}
+
+
+static int eap_teap_session_id(struct eap_teap_data *data)
+{
+ const size_t max_id_len = 100;
+ int res;
+
+ os_free(data->session_id);
+ data->session_id = os_malloc(max_id_len);
+ if (!data->session_id)
+ return -1;
+
+ data->session_id[0] = EAP_TYPE_TEAP;
+ res = tls_get_tls_unique(data->ssl.conn, data->session_id + 1,
+ max_id_len - 1);
+ if (res < 0) {
+ os_free(data->session_id);
+ data->session_id = NULL;
+ wpa_printf(MSG_ERROR, "EAP-TEAP: Failed to derive Session-Id");
+ return -1;
+ }
+
+ data->id_len = 1 + res;
+ wpa_hexdump(MSG_DEBUG, "EAP-TEAP: Derived Session-Id",
+ data->session_id, data->id_len);
+ return 0;
+}
+
+
+static struct wpabuf * eap_teap_process_crypto_binding(
+ struct eap_sm *sm, struct eap_teap_data *data,
+ struct eap_method_ret *ret,
+ const struct teap_tlv_crypto_binding *cb, size_t bind_len)
+{
+ struct wpabuf *resp;
+ u8 *pos;
+ u8 cmk_msk[EAP_TEAP_CMK_LEN];
+ u8 cmk_emsk[EAP_TEAP_CMK_LEN];
+ const u8 *cmk_emsk_ptr = NULL;
+ int res;
+ size_t len;
+ u8 flags;
+
+ if (eap_teap_validate_crypto_binding(data, cb) < 0 ||
+ eap_teap_get_cmk(sm, data, cmk_msk, cmk_emsk) < 0)
+ return NULL;
+
+ /* Validate received MSK/EMSK Compound MAC */
+ flags = cb->subtype >> 4;
+
+ if (flags == TEAP_CRYPTO_BINDING_MSK_CMAC ||
+ flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC) {
+ u8 msk_compound_mac[EAP_TEAP_COMPOUND_MAC_LEN];
+
+ if (eap_teap_compound_mac(data->tls_cs, cb,
+ data->server_outer_tlvs,
+ data->peer_outer_tlvs, cmk_msk,
+ msk_compound_mac) < 0)
+ return NULL;
+ res = os_memcmp_const(msk_compound_mac, cb->msk_compound_mac,
+ EAP_TEAP_COMPOUND_MAC_LEN);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Received MSK Compound MAC",
+ cb->msk_compound_mac, EAP_TEAP_COMPOUND_MAC_LEN);
+ wpa_hexdump(MSG_MSGDUMP,
+ "EAP-TEAP: Calculated MSK Compound MAC",
+ msk_compound_mac, EAP_TEAP_COMPOUND_MAC_LEN);
+ if (res != 0) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: MSK Compound MAC did not match");
+ return NULL;
+ }
+ }
+
+ if ((flags == TEAP_CRYPTO_BINDING_EMSK_CMAC ||
+ flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC) &&
+ data->cmk_emsk_available) {
+ u8 emsk_compound_mac[EAP_TEAP_COMPOUND_MAC_LEN];
+
+ if (eap_teap_compound_mac(data->tls_cs, cb,
+ data->server_outer_tlvs,
+ data->peer_outer_tlvs, cmk_emsk,
+ emsk_compound_mac) < 0)
+ return NULL;
+ res = os_memcmp_const(emsk_compound_mac, cb->emsk_compound_mac,
+ EAP_TEAP_COMPOUND_MAC_LEN);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Received EMSK Compound MAC",
+ cb->emsk_compound_mac, EAP_TEAP_COMPOUND_MAC_LEN);
+ wpa_hexdump(MSG_MSGDUMP,
+ "EAP-TEAP: Calculated EMSK Compound MAC",
+ emsk_compound_mac, EAP_TEAP_COMPOUND_MAC_LEN);
+ if (res != 0) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: EMSK Compound MAC did not match");
+ return NULL;
+ }
+
+ cmk_emsk_ptr = cmk_emsk;
+ }
+
+ if (flags == TEAP_CRYPTO_BINDING_EMSK_CMAC &&
+ !data->cmk_emsk_available) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Server included only EMSK Compound MAC, but no locally generated inner EAP EMSK to validate this");
+ return NULL;
+ }
+
+ /*
+ * Compound MAC was valid, so authentication succeeded. Reply with
+ * crypto binding to allow server to complete authentication.
+ */
+
+ len = sizeof(struct teap_tlv_crypto_binding);
+ resp = wpabuf_alloc(len);
+ if (!resp)
+ return NULL;
+
+ if (data->phase2_success && eap_teap_derive_msk(data) < 0) {
+ wpa_printf(MSG_INFO, "EAP-TEAP: Failed to generate MSK");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ data->phase2_success = 0;
+ wpabuf_free(resp);
+ return NULL;
+ }
+
+ if (data->phase2_success && eap_teap_session_id(data) < 0) {
+ wpabuf_free(resp);
+ return NULL;
+ }
+
+ pos = wpabuf_put(resp, sizeof(struct teap_tlv_crypto_binding));
+ if (eap_teap_write_crypto_binding(
+ data, (struct teap_tlv_crypto_binding *) pos,
+ cb, cmk_msk, cmk_emsk_ptr) < 0) {
+ wpabuf_free(resp);
+ return NULL;
+ }
+
+ return resp;
+}
+
+
+static void eap_teap_parse_pac_tlv(struct eap_teap_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-TEAP: PAC-Key", pos, len);
+ if (len != EAP_TEAP_PAC_KEY_LEN) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: 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-TEAP: PAC-Opaque", pos, len);
+ entry->pac_opaque = pos;
+ entry->pac_opaque_len = len;
+ break;
+ case PAC_TYPE_PAC_INFO:
+ wpa_hexdump(MSG_DEBUG, "EAP-TEAP: PAC-Info", pos, len);
+ entry->pac_info = pos;
+ entry->pac_info_len = len;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Ignored unknown PAC type %d",
+ type);
+ break;
+ }
+}
+
+
+static int eap_teap_process_pac_tlv(struct eap_teap_pac *entry,
+ u8 *pac, size_t pac_len)
+{
+ struct pac_attr_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_attr_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-TEAP: PAC TLV overrun (type=%d len=%lu left=%lu)",
+ type, (unsigned long) len,
+ (unsigned long) left);
+ return -1;
+ }
+
+ eap_teap_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-TEAP: PAC TLV does not include all the required fields");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int eap_teap_parse_pac_info(struct eap_teap_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-TEAP: 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-TEAP: 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-TEAP: 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-TEAP: 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-TEAP: PAC-Info - A-ID-Info",
+ pos, len);
+ entry->a_id_info = pos;
+ entry->a_id_info_len = len;
+ break;
+ case PAC_TYPE_PAC_TYPE:
+ /* RFC 7170, Section 4.2.12.6 - PAC-Type TLV */
+ if (len != 2) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Invalid PAC-Type length %lu (expected 2)",
+ (unsigned long) len);
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "EAP-TEAP: PAC-Info - PAC-Type",
+ pos, len);
+ return -1;
+ }
+ pac_type = WPA_GET_BE16(pos);
+ if (pac_type != PAC_TYPE_TUNNEL_PAC) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Unsupported PAC Type %d",
+ pac_type);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: PAC-Info - PAC-Type %d",
+ pac_type);
+ entry->pac_type = pac_type;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Ignored unknown PAC-Info type %d", type);
+ break;
+ }
+
+ return 0;
+}
+
+
+static int eap_teap_process_pac_info(struct eap_teap_pac *entry)
+{
+ struct pac_attr_hdr *hdr;
+ u8 *pos;
+ size_t left, len;
+ int type;
+
+ /* RFC 7170, Section 4.2.12.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_attr_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-TEAP: PAC-Info overrun (type=%d len=%lu left=%lu)",
+ type, (unsigned long) len,
+ (unsigned long) left);
+ return -1;
+ }
+
+ if (eap_teap_parse_pac_info(entry, type, pos, len) < 0)
+ return -1;
+
+ pos += len;
+ left -= len;
+ }
+
+ if (!entry->a_id || !entry->a_id_info) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: PAC-Info does not include all the required fields");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_teap_process_pac(struct eap_sm *sm,
+ struct eap_teap_data *data,
+ struct eap_method_ret *ret,
+ u8 *pac, size_t pac_len)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ struct eap_teap_pac entry;
+
+ os_memset(&entry, 0, sizeof(entry));
+ if (eap_teap_process_pac_tlv(&entry, pac, pac_len) ||
+ eap_teap_process_pac_info(&entry))
+ return NULL;
+
+ eap_teap_add_pac(&data->pac, &data->current_pac, &entry);
+ eap_teap_pac_list_truncate(data->pac, data->max_pac_list_len);
+ if (data->use_pac_binary_format)
+ eap_teap_save_pac_bin(sm, data->pac, config->pac_file);
+ else
+ eap_teap_save_pac(sm, data->pac, config->pac_file);
+
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Send PAC-Acknowledgement - %s initiated provisioning completed successfully",
+ data->provisioning ? "peer" : "server");
+ return eap_teap_tlv_pac_ack();
+}
+
+
+static int eap_teap_parse_decrypted(struct wpabuf *decrypted,
+ struct eap_teap_tlv_parse *tlv,
+ struct wpabuf **resp)
+{
+ u16 tlv_type;
+ int mandatory, res;
+ size_t len;
+ 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 (end - pos >= 4) {
+ mandatory = pos[0] & 0x80;
+ tlv_type = WPA_GET_BE16(pos) & 0x3fff;
+ pos += 2;
+ len = WPA_GET_BE16(pos);
+ pos += 2;
+ if (len > (size_t) (end - pos)) {
+ wpa_printf(MSG_INFO, "EAP-TEAP: TLV overflow");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Received Phase 2: TLV type %u (%s) length %u%s",
+ tlv_type, eap_teap_tlv_type_str(tlv_type),
+ (unsigned int) len,
+ mandatory ? " (mandatory)" : "");
+
+ res = eap_teap_parse_tlv(tlv, tlv_type, pos, len);
+ if (res == -2)
+ break;
+ if (res < 0) {
+ if (mandatory) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: NAK unknown mandatory TLV type %u",
+ tlv_type);
+ *resp = eap_teap_tlv_nak(0, tlv_type);
+ break;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Ignore unknown optional TLV type %u",
+ tlv_type);
+ }
+
+ pos += len;
+ }
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_teap_pac_request(void)
+{
+ struct wpabuf *req;
+ struct teap_tlv_request_action *act;
+ struct teap_tlv_hdr *pac;
+ struct teap_attr_pac_type *type;
+
+ req = wpabuf_alloc(sizeof(*act) + sizeof(*pac) + sizeof(*type));
+ if (!req)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Add Request Action TLV (Process TLV)");
+ act = wpabuf_put(req, sizeof(*act));
+ act->tlv_type = host_to_be16(TEAP_TLV_REQUEST_ACTION);
+ act->length = host_to_be16(2);
+ act->status = TEAP_STATUS_SUCCESS;
+ act->action = TEAP_REQUEST_ACTION_PROCESS_TLV;
+
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Add PAC TLV (PAC-Type = Tunnel)");
+ pac = wpabuf_put(req, sizeof(*pac));
+ pac->tlv_type = host_to_be16(TEAP_TLV_PAC);
+ pac->length = host_to_be16(sizeof(*type));
+
+ type = wpabuf_put(req, sizeof(*type));
+ type->type = host_to_be16(PAC_TYPE_PAC_TYPE);
+ type->length = host_to_be16(2);
+ type->pac_type = host_to_be16(PAC_TYPE_TUNNEL_PAC);
+
+ return req;
+}
+
+
+static int eap_teap_process_decrypted(struct eap_sm *sm,
+ struct eap_teap_data *data,
+ struct eap_method_ret *ret,
+ u8 identifier,
+ struct wpabuf *decrypted,
+ struct wpabuf **out_data)
+{
+ struct wpabuf *resp = NULL, *tmp;
+ struct eap_teap_tlv_parse tlv;
+ int failed = 0;
+ enum teap_error_codes error = 0;
+
+ if (eap_teap_parse_decrypted(decrypted, &tlv, &resp) < 0) {
+ /* Parsing failed - no response available */
+ return 0;
+ }
+
+ if (resp) {
+ /* Parsing rejected the message - send out an error response */
+ goto send_resp;
+ }
+
+ if (tlv.result == TEAP_STATUS_FAILURE) {
+ /* Server indicated failure - respond similarly per
+ * RFC 7170, 3.6.3. This authentication exchange cannot succeed
+ * and will be terminated with a cleartext EAP Failure. */
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Server rejected authentication");
+ resp = eap_teap_tlv_result(TEAP_STATUS_FAILURE, 0);
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ goto send_resp;
+ }
+
+ if ((tlv.iresult == TEAP_STATUS_SUCCESS ||
+ (!data->result_success_done &&
+ tlv.result == TEAP_STATUS_SUCCESS)) &&
+ !tlv.crypto_binding) {
+ /* Result TLV or Intermediate-Result TLV indicating success,
+ * but no Crypto-Binding TLV */
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Result TLV or Intermediate-Result TLV indicating success, but no Crypto-Binding TLV");
+ failed = 1;
+ error = TEAP_ERROR_TUNNEL_COMPROMISE_ERROR;
+ goto done;
+ }
+
+ if (tlv.iresult != TEAP_STATUS_SUCCESS &&
+ tlv.iresult != TEAP_STATUS_FAILURE &&
+ data->inner_method_done) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Inner EAP method exchange completed, but no Intermediate-Result TLV included");
+ failed = 1;
+ error = TEAP_ERROR_TUNNEL_COMPROMISE_ERROR;
+ goto done;
+ }
+
+ if (tlv.basic_auth_req) {
+ tmp = eap_teap_process_basic_auth_req(sm, data,
+ tlv.basic_auth_req,
+ tlv.basic_auth_req_len);
+ if (!tmp)
+ failed = 1;
+ resp = wpabuf_concat(resp, tmp);
+ } else if (tlv.eap_payload_tlv) {
+ tmp = eap_teap_process_eap_payload_tlv(sm, data, ret,
+ tlv.eap_payload_tlv,
+ tlv.eap_payload_tlv_len);
+ if (!tmp)
+ failed = 1;
+ resp = wpabuf_concat(resp, tmp);
+
+ if (tlv.iresult == TEAP_STATUS_SUCCESS ||
+ tlv.iresult == TEAP_STATUS_FAILURE) {
+ tmp = eap_teap_tlv_result(failed ?
+ TEAP_STATUS_FAILURE :
+ TEAP_STATUS_SUCCESS, 1);
+ resp = wpabuf_concat(resp, tmp);
+ if (tlv.iresult == TEAP_STATUS_FAILURE)
+ failed = 1;
+ }
+ }
+
+ if (tlv.crypto_binding) {
+ if (tlv.iresult != TEAP_STATUS_SUCCESS &&
+ tlv.result != TEAP_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Unexpected Crypto-Binding TLV without Result TLV or Intermediate-Result TLV indicating success");
+ failed = 1;
+ error = TEAP_ERROR_UNEXPECTED_TLVS_EXCHANGED;
+ goto done;
+ }
+
+ tmp = eap_teap_process_crypto_binding(sm, data, ret,
+ tlv.crypto_binding,
+ tlv.crypto_binding_len);
+ if (!tmp) {
+ failed = 1;
+ error = TEAP_ERROR_TUNNEL_COMPROMISE_ERROR;
+ } else {
+ resp = wpabuf_concat(resp, tmp);
+ if (tlv.result == TEAP_STATUS_SUCCESS && !failed)
+ data->result_success_done = 1;
+ if (tlv.iresult == TEAP_STATUS_SUCCESS && !failed)
+ data->inner_method_done = 0;
+ }
+ }
+
+ if (data->result_success_done && data->session_ticket_used &&
+ eap_teap_derive_msk(data) == 0) {
+ /* Assume the server might accept authentication without going
+ * through inner authentication. */
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: PAC used - server may decide to skip inner authentication");
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_COND_SUCC;
+ }
+
+ if (tlv.pac) {
+ if (tlv.result == TEAP_STATUS_SUCCESS) {
+ tmp = eap_teap_process_pac(sm, data, ret,
+ tlv.pac, tlv.pac_len);
+ resp = wpabuf_concat(resp, tmp);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: PAC TLV without Result TLV acknowledging success");
+ failed = 1;
+ error = TEAP_ERROR_UNEXPECTED_TLVS_EXCHANGED;
+ }
+ }
+
+ if (!data->current_pac && data->provisioning && !failed && !tlv.pac &&
+ tlv.crypto_binding &&
+ (!data->anon_provisioning ||
+ (data->phase2_success && data->phase2_method &&
+ data->phase2_method->vendor == 0 &&
+ eap_teap_allowed_anon_prov_cipher_suite(data->tls_cs) &&
+ eap_teap_allowed_anon_prov_phase2_method(
+ data->phase2_method->method))) &&
+ (tlv.iresult == TEAP_STATUS_SUCCESS ||
+ tlv.result == TEAP_STATUS_SUCCESS)) {
+ /*
+ * Need to request Tunnel PAC when using authenticated
+ * provisioning.
+ */
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Request Tunnel PAC");
+ tmp = eap_teap_pac_request();
+ resp = wpabuf_concat(resp, tmp);
+ }
+
+done:
+ if (failed) {
+ tmp = eap_teap_tlv_result(TEAP_STATUS_FAILURE, 0);
+ resp = wpabuf_concat(tmp, resp);
+
+ if (error != 0) {
+ tmp = eap_teap_tlv_error(error);
+ resp = wpabuf_concat(tmp, resp);
+ }
+
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ } else if (tlv.result == TEAP_STATUS_SUCCESS) {
+ tmp = eap_teap_tlv_result(TEAP_STATUS_SUCCESS, 0);
+ resp = wpabuf_concat(tmp, resp);
+ }
+
+ if (resp && tlv.result == TEAP_STATUS_SUCCESS && !failed &&
+ tlv.crypto_binding && data->phase2_success) {
+ /* Successfully completed Phase 2 */
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Authentication completed successfully");
+ ret->methodState = data->provisioning ?
+ METHOD_MAY_CONT : METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ }
+
+ if (!resp) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: No recognized TLVs - send empty response packet");
+ resp = wpabuf_alloc(1);
+ }
+
+send_resp:
+ if (!resp)
+ return 0;
+
+ wpa_hexdump_buf(MSG_DEBUG, "EAP-TEAP: Encrypting Phase 2 data", resp);
+ if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TEAP,
+ data->teap_version, identifier,
+ resp, out_data)) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Failed to encrypt a Phase 2 frame");
+ }
+ wpabuf_free(resp);
+
+ return 0;
+}
+
+
+static int eap_teap_decrypt(struct eap_sm *sm, struct eap_teap_data *data,
+ struct eap_method_ret *ret, u8 identifier,
+ const struct wpabuf *in_data,
+ struct wpabuf **out_data)
+{
+ struct wpabuf *in_decrypted;
+ int res;
+
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Received %lu bytes encrypted data for Phase 2",
+ (unsigned long) wpabuf_len(in_data));
+
+ if (data->pending_phase2_req) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: 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_TEAP,
+ data->teap_version,
+ 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-TEAP: Decrypted Phase 2 TLV(s)",
+ in_decrypted);
+
+ if (wpabuf_len(in_decrypted) < 4) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Too short Phase 2 TLV frame (len=%lu)",
+ (unsigned long) wpabuf_len(in_decrypted));
+ wpabuf_free(in_decrypted);
+ return -1;
+ }
+
+ res = eap_teap_process_decrypted(sm, data, ret, identifier,
+ in_decrypted, out_data);
+
+ wpabuf_free(in_decrypted);
+
+ return res;
+}
+
+
+static void eap_teap_select_pac(struct eap_teap_data *data,
+ const u8 *a_id, size_t a_id_len)
+{
+ if (!a_id)
+ return;
+ data->current_pac = eap_teap_get_pac(data->pac, a_id, a_id_len,
+ PAC_TYPE_TUNNEL_PAC);
+ if (data->current_pac) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: PAC found for this A-ID (PAC-Type %d)",
+ data->current_pac->pac_type);
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TEAP: A-ID-Info",
+ data->current_pac->a_id_info,
+ data->current_pac->a_id_info_len);
+ }
+}
+
+
+static int eap_teap_use_pac_opaque(struct eap_sm *sm,
+ struct eap_teap_data *data,
+ struct eap_teap_pac *pac)
+{
+ u8 *tlv;
+ size_t tlv_len, olen;
+ struct teap_tlv_hdr *ehdr;
+
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Add PAC-Opaque TLS extension");
+ olen = pac->pac_opaque_len;
+ tlv_len = sizeof(*ehdr) + olen;
+ tlv = os_malloc(tlv_len);
+ if (tlv) {
+ ehdr = (struct teap_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 ||
+ tls_connection_client_hello_ext(sm->ssl_ctx, data->ssl.conn,
+ TLS_EXT_PAC_OPAQUE,
+ tlv, tlv_len) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Failed to add PAC-Opaque TLS extension");
+ os_free(tlv);
+ return -1;
+ }
+ os_free(tlv);
+
+ return 0;
+}
+
+
+static int eap_teap_clear_pac_opaque_ext(struct eap_sm *sm,
+ struct eap_teap_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-TEAP: Failed to remove PAC-Opaque TLS extension");
+ return -1;
+ }
+ return 0;
+}
+
+
+static int eap_teap_process_start(struct eap_sm *sm,
+ struct eap_teap_data *data, u8 flags,
+ const u8 *pos, size_t left)
+{
+ const u8 *a_id = NULL;
+ size_t a_id_len = 0;
+
+ /* TODO: Support (mostly theoretical) case of TEAP/Start request being
+ * fragmented */
+
+ /* EAP-TEAP version negotiation (RFC 7170, Section 3.2) */
+ data->received_version = flags & EAP_TLS_VERSION_MASK;
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Start (server ver=%u, own ver=%u)",
+ data->received_version, data->teap_version);
+ if (data->received_version < 1) {
+ /* Version 1 was the first defined version, so reject 0 */
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Server used unknown TEAP version %u",
+ data->received_version);
+ return -1;
+ }
+ if (data->received_version < data->teap_version)
+ data->teap_version = data->received_version;
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Using TEAP version %d",
+ data->teap_version);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Start message payload", pos, left);
+
+ /* Parse Authority-ID TLV from Outer TLVs, if present */
+ if (flags & EAP_TEAP_FLAGS_OUTER_TLV_LEN) {
+ const u8 *outer_pos, *outer_end;
+ u32 outer_tlv_len;
+
+ if (left < 4) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Not enough room for the Outer TLV Length field");
+ return -1;
+ }
+
+ outer_tlv_len = WPA_GET_BE32(pos);
+ pos += 4;
+ left -= 4;
+
+ if (outer_tlv_len > left) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Truncated Outer TLVs field (Outer TLV Length: %u; remaining buffer: %u)",
+ outer_tlv_len, (unsigned int) left);
+ return -1;
+ }
+
+ outer_pos = pos + left - outer_tlv_len;
+ outer_end = outer_pos + outer_tlv_len;
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Start message Outer TLVs",
+ outer_pos, outer_tlv_len);
+ wpabuf_free(data->server_outer_tlvs);
+ data->server_outer_tlvs = wpabuf_alloc_copy(outer_pos,
+ outer_tlv_len);
+ if (!data->server_outer_tlvs)
+ return -1;
+ left -= outer_tlv_len;
+ if (left > 0) {
+ wpa_hexdump(MSG_INFO,
+ "EAP-TEAP: Unexpected TLS Data in Start message",
+ pos, left);
+ return -1;
+ }
+
+ while (outer_pos < outer_end) {
+ u16 tlv_type, tlv_len;
+
+ if (outer_end - outer_pos < 4) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Truncated Outer TLV header");
+ return -1;
+ }
+ tlv_type = WPA_GET_BE16(outer_pos);
+ outer_pos += 2;
+ tlv_len = WPA_GET_BE16(outer_pos);
+ outer_pos += 2;
+ /* Outer TLVs are required to be optional, so no need to
+ * check the M flag */
+ tlv_type &= TEAP_TLV_TYPE_MASK;
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Outer TLV: Type=%u Length=%u",
+ tlv_type, tlv_len);
+ if (outer_end - outer_pos < tlv_len) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Truncated Outer TLV (Type %u)",
+ tlv_type);
+ return -1;
+ }
+ if (tlv_type == TEAP_TLV_AUTHORITY_ID) {
+ wpa_hexdump(MSG_DEBUG, "EAP-TEAP: Authority-ID",
+ outer_pos, tlv_len);
+ if (a_id) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Multiple Authority-ID TLVs in TEAP/Start");
+ return -1;
+ }
+ a_id = outer_pos;
+ a_id_len = tlv_len;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Ignore unknown Outer TLV (Type %u)",
+ tlv_type);
+ }
+ outer_pos += tlv_len;
+ }
+ } else if (left > 0) {
+ wpa_hexdump(MSG_INFO,
+ "EAP-TEAP: Unexpected TLS Data in Start message",
+ pos, left);
+ return -1;
+ }
+
+ eap_teap_select_pac(data, a_id, a_id_len);
+
+ if (data->resuming && data->current_pac) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Trying to resume session - do not add PAC-Opaque to TLS ClientHello");
+ if (eap_teap_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_teap_use_pac_opaque(sm, data, data->current_pac) < 0)
+ return -1;
+ } else if (data->provisioning_allowed) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: No PAC found - starting provisioning");
+ if (eap_teap_clear_pac_opaque_ext(sm, data) < 0)
+ return -1;
+ data->provisioning = 1;
+ }
+
+ return 0;
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+static struct wpabuf * eap_teap_add_dummy_outer_tlvs(struct eap_teap_data *data,
+ struct wpabuf *resp)
+{
+ struct wpabuf *resp2;
+ u16 len;
+ const u8 *pos;
+ u8 flags;
+
+ wpabuf_free(data->peer_outer_tlvs);
+ data->peer_outer_tlvs = wpabuf_alloc(4 + 4);
+ if (!data->peer_outer_tlvs) {
+ wpabuf_free(resp);
+ return NULL;
+ }
+
+ /* Outer TLVs (dummy Vendor-Specific TLV for testing) */
+ wpabuf_put_be16(data->peer_outer_tlvs, TEAP_TLV_VENDOR_SPECIFIC);
+ wpabuf_put_be16(data->peer_outer_tlvs, 4);
+ wpabuf_put_be32(data->peer_outer_tlvs, EAP_VENDOR_HOSTAP);
+ wpa_hexdump_buf(MSG_DEBUG, "EAP-TEAP: TESTING - Add dummy Outer TLVs",
+ data->peer_outer_tlvs);
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "EAP-TEAP: TEAP/Start response before modification",
+ resp);
+ resp2 = wpabuf_alloc(wpabuf_len(resp) + 4 +
+ wpabuf_len(data->peer_outer_tlvs));
+ if (!resp2) {
+ wpabuf_free(resp);
+ return NULL;
+ }
+
+ pos = wpabuf_head(resp);
+ wpabuf_put_u8(resp2, *pos++); /* Code */
+ wpabuf_put_u8(resp2, *pos++); /* Identifier */
+ len = WPA_GET_BE16(pos);
+ pos += 2;
+ wpabuf_put_be16(resp2, len + 4 + wpabuf_len(data->peer_outer_tlvs));
+ wpabuf_put_u8(resp2, *pos++); /* Type */
+ /* Flags | Ver (with Outer TLV length included flag set to 1) */
+ flags = *pos++;
+ if (flags & (EAP_TEAP_FLAGS_OUTER_TLV_LEN |
+ EAP_TLS_FLAGS_LENGTH_INCLUDED)) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Cannot add Outer TLVs for testing");
+ wpabuf_free(resp);
+ wpabuf_free(resp2);
+ return NULL;
+ }
+ flags |= EAP_TEAP_FLAGS_OUTER_TLV_LEN;
+ wpabuf_put_u8(resp2, flags);
+ /* Outer TLV Length */
+ wpabuf_put_be32(resp2, wpabuf_len(data->peer_outer_tlvs));
+ /* TLS Data */
+ wpabuf_put_data(resp2, pos, wpabuf_len(resp) - 6);
+ wpabuf_put_buf(resp2, data->peer_outer_tlvs); /* Outer TLVs */
+
+ wpabuf_free(resp);
+ wpa_hexdump_buf(MSG_DEBUG,
+ "EAP-TEAP: TEAP/Start response after modification",
+ resp2);
+ return resp2;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+static struct wpabuf * eap_teap_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_teap_data *data = priv;
+ struct wpabuf msg;
+
+ pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TEAP, ret,
+ reqData, &left, &flags);
+ if (!pos)
+ return NULL;
+
+ req = wpabuf_head(reqData);
+ id = req->identifier;
+
+ if (flags & EAP_TLS_FLAGS_START) {
+ if (eap_teap_process_start(sm, data, flags, pos, left) < 0)
+ return NULL;
+
+ /* Outer TLVs are not used in further packet processing and
+ * there cannot be TLS Data in this TEAP/Start message, so
+ * enforce that by ignoring whatever data might remain in the
+ * buffer. */
+ left = 0;
+ } else if (flags & EAP_TEAP_FLAGS_OUTER_TLV_LEN) {
+ /* TODO: RFC 7170, Section 4.3.1 indicates that the unexpected
+ * Outer TLVs MUST be ignored instead of ignoring the full
+ * message. */
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Outer TLVs present in non-Start message -> ignore message");
+ return NULL;
+ }
+
+ wpabuf_set(&msg, pos, left);
+
+ resp = NULL;
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+ !data->resuming) {
+ /* Process tunneled (encrypted) phase 2 data. */
+ res = eap_teap_decrypt(sm, data, ret, id, &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 {
+ if (sm->waiting_ext_cert_check && data->pending_resp) {
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ if (config->pending_ext_cert_check ==
+ EXT_CERT_CHECK_GOOD) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: External certificate check succeeded - continue handshake");
+ resp = data->pending_resp;
+ data->pending_resp = NULL;
+ sm->waiting_ext_cert_check = 0;
+ return resp;
+ }
+
+ if (config->pending_ext_cert_check ==
+ EXT_CERT_CHECK_BAD) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: External certificate check failed - force authentication failure");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ sm->waiting_ext_cert_check = 0;
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Continuing to wait external server certificate validation");
+ return NULL;
+ }
+
+ /* Continue processing TLS handshake (phase 1). */
+ res = eap_peer_tls_process_helper(sm, &data->ssl,
+ EAP_TYPE_TEAP,
+ data->teap_version, id, &msg,
+ &resp);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: TLS processing failed");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return resp;
+ }
+
+ if (sm->waiting_ext_cert_check) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Waiting external server certificate validation");
+ wpabuf_free(data->pending_resp);
+ data->pending_resp = resp;
+ return NULL;
+ }
+
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+ char cipher[80];
+
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: TLS done, proceed to Phase 2");
+ data->tls_cs =
+ tls_connection_get_cipher_suite(data->ssl.conn);
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: TLS cipher suite 0x%04x",
+ data->tls_cs);
+
+ if (data->provisioning &&
+ (!(data->provisioning_allowed &
+ EAP_TEAP_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-TEAP: Using anonymous (unauthenticated) provisioning");
+ data->anon_provisioning = 1;
+ } else {
+ data->anon_provisioning = 0;
+ }
+ data->resuming = 0;
+ if (eap_teap_derive_key_auth(sm, data) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Could not derive keys");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ wpabuf_free(resp);
+ return NULL;
+ }
+ }
+
+ if (res == 2) {
+ /*
+ * Application data included in the handshake message.
+ */
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = resp;
+ resp = NULL;
+ res = eap_teap_decrypt(sm, data, ret, id, &msg, &resp);
+ }
+ }
+
+ if (res == 1) {
+ wpabuf_free(resp);
+ return eap_peer_tls_build_ack(id, EAP_TYPE_TEAP,
+ data->teap_version);
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (data->test_outer_tlvs && res == 0 && resp &&
+ (flags & EAP_TLS_FLAGS_START) && wpabuf_len(resp) >= 6)
+ resp = eap_teap_add_dummy_outer_tlvs(data, resp);
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ return resp;
+}
+
+
+#if 0 /* TODO */
+static Boolean eap_teap_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+ struct eap_teap_data *data = priv;
+
+ return tls_connection_established(sm->ssl_ctx, data->ssl.conn);
+}
+
+
+static void eap_teap_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_teap_data *data = priv;
+
+ if (data->phase2_priv && data->phase2_method &&
+ data->phase2_method->deinit_for_reauth)
+ data->phase2_method->deinit_for_reauth(sm, data->phase2_priv);
+ eap_teap_clear(data);
+}
+
+
+static void * eap_teap_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_teap_data *data = priv;
+
+ if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
+ eap_teap_deinit(sm, 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->inner_method_done = 0;
+ data->result_success_done = 0;
+ data->resuming = 1;
+ data->provisioning = 0;
+ data->anon_provisioning = 0;
+ data->simck_idx = 0;
+ return priv;
+}
+#endif
+
+
+static int eap_teap_get_status(struct eap_sm *sm, void *priv, char *buf,
+ size_t buflen, int verbose)
+{
+ struct eap_teap_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-TEAP Phase 2 method=%s\n",
+ data->phase2_method->name);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+ return len;
+}
+
+
+static Boolean eap_teap_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_teap_data *data = priv;
+
+ return data->success;
+}
+
+
+static u8 * eap_teap_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_teap_data *data = priv;
+ u8 *key;
+
+ if (!data->success)
+ return NULL;
+
+ key = os_memdup(data->key_data, EAP_TEAP_KEY_LEN);
+ if (!key)
+ return NULL;
+
+ *len = EAP_TEAP_KEY_LEN;
+
+ return key;
+}
+
+
+static u8 * eap_teap_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_teap_data *data = priv;
+ u8 *id;
+
+ if (!data->success || !data->session_id)
+ return NULL;
+
+ id = os_memdup(data->session_id, data->id_len);
+ if (!id)
+ return NULL;
+
+ *len = data->id_len;
+
+ return id;
+}
+
+
+static u8 * eap_teap_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_teap_data *data = priv;
+ u8 *key;
+
+ if (!data->success)
+ return NULL;
+
+ key = os_memdup(data->emsk, EAP_EMSK_LEN);
+ if (!key)
+ return NULL;
+
+ *len = EAP_EMSK_LEN;
+
+ return key;
+}
+
+
+int eap_peer_teap_register(void)
+{
+ struct eap_method *eap;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_TEAP, "TEAP");
+ if (!eap)
+ return -1;
+
+ eap->init = eap_teap_init;
+ eap->deinit = eap_teap_deinit;
+ eap->process = eap_teap_process;
+ eap->isKeyAvailable = eap_teap_isKeyAvailable;
+ eap->getKey = eap_teap_getKey;
+ eap->getSessionId = eap_teap_get_session_id;
+ eap->get_status = eap_teap_get_status;
+#if 0 /* TODO */
+ eap->has_reauth_data = eap_teap_has_reauth_data;
+ eap->deinit_for_reauth = eap_teap_deinit_for_reauth;
+ eap->init_for_reauth = eap_teap_init_for_reauth;
+#endif
+ eap->get_emsk = eap_teap_get_emsk;
+
+ return eap_peer_method_register(eap);
+}
--- /dev/null
+/*
+ * EAP peer method: EAP-TEAP PAC file processing
+ * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_config.h"
+#include "eap_i.h"
+#include "eap_teap_pac.h"
+
+/* TODO: encrypt PAC-Key in the PAC file */
+
+
+/* Text data format */
+static const char *pac_file_hdr =
+ "wpa_supplicant EAP-TEAP PAC file - version 1";
+
+/*
+ * Binary data format
+ * 4-octet magic value: 6A E4 92 1C
+ * 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_TEAP_PAC_BINARY_MAGIC 0x6ae4921c
+#define EAP_TEAP_PAC_BINARY_FORMAT_VERSION 0
+
+
+/**
+ * eap_teap_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_teap_free_pac(struct eap_teap_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_teap_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_teap_pac * eap_teap_get_pac(struct eap_teap_pac *pac_root,
+ const u8 *a_id, size_t a_id_len,
+ u16 pac_type)
+{
+ struct eap_teap_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_teap_remove_pac(struct eap_teap_pac **pac_root,
+ struct eap_teap_pac **pac_current,
+ const u8 *a_id, size_t a_id_len, u16 pac_type)
+{
+ struct eap_teap_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)
+ *pac_root = pac->next;
+ else
+ prev->next = pac->next;
+ if (*pac_current == pac)
+ *pac_current = NULL;
+ eap_teap_free_pac(pac);
+ break;
+ }
+ prev = pac;
+ pac = pac->next;
+ }
+}
+
+
+static int eap_teap_copy_buf(u8 **dst, size_t *dst_len,
+ const u8 *src, size_t src_len)
+{
+ if (src) {
+ *dst = os_memdup(src, src_len);
+ if (!(*dst))
+ return -1;
+ *dst_len = src_len;
+ }
+ return 0;
+}
+
+
+/**
+ * eap_teap_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_teap_add_pac(struct eap_teap_pac **pac_root,
+ struct eap_teap_pac **pac_current,
+ struct eap_teap_pac *entry)
+{
+ struct eap_teap_pac *pac;
+
+ if (!entry || !entry->a_id)
+ return -1;
+
+ /* Remove a possible old entry for the matching A-ID. */
+ eap_teap_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)
+ return -1;
+
+ pac->pac_type = entry->pac_type;
+ os_memcpy(pac->pac_key, entry->pac_key, EAP_TEAP_PAC_KEY_LEN);
+ if (eap_teap_copy_buf(&pac->pac_opaque, &pac->pac_opaque_len,
+ entry->pac_opaque, entry->pac_opaque_len) < 0 ||
+ eap_teap_copy_buf(&pac->pac_info, &pac->pac_info_len,
+ entry->pac_info, entry->pac_info_len) < 0 ||
+ eap_teap_copy_buf(&pac->a_id, &pac->a_id_len,
+ entry->a_id, entry->a_id_len) < 0 ||
+ eap_teap_copy_buf(&pac->i_id, &pac->i_id_len,
+ entry->i_id, entry->i_id_len) < 0 ||
+ eap_teap_copy_buf(&pac->a_id_info, &pac->a_id_info_len,
+ entry->a_id_info, entry->a_id_info_len) < 0) {
+ eap_teap_free_pac(pac);
+ return -1;
+ }
+
+ pac->next = *pac_root;
+ *pac_root = pac;
+
+ return 0;
+}
+
+
+struct eap_teap_read_ctx {
+ FILE *f;
+ const char *pos;
+ const char *end;
+ int line;
+ char *buf;
+ size_t buf_len;
+};
+
+static int eap_teap_read_line(struct eap_teap_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_teap_parse_hex(const char *value, size_t *len)
+{
+ int hlen;
+ u8 *buf;
+
+ if (!value)
+ return NULL;
+ hlen = os_strlen(value);
+ if (hlen & 1)
+ return NULL;
+ *len = hlen / 2;
+ buf = os_malloc(*len);
+ if (!buf)
+ return NULL;
+ if (hexstr2bin(value, buf, *len)) {
+ os_free(buf);
+ return NULL;
+ }
+ return buf;
+}
+
+
+static int eap_teap_init_pac_data(struct eap_sm *sm, const char *pac_file,
+ struct eap_teap_read_ctx *rc)
+{
+ os_memset(rc, 0, sizeof(*rc));
+
+ rc->buf_len = 2048;
+ rc->buf = os_malloc(rc->buf_len);
+ if (!rc->buf)
+ 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) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: 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) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: No PAC file '%s' - assume no PAC entries have been provisioned",
+ pac_file);
+ os_free(rc->buf);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+static void eap_teap_deinit_pac_data(struct eap_teap_read_ctx *rc)
+{
+ os_free(rc->buf);
+ if (rc->f)
+ fclose(rc->f);
+}
+
+
+static const char * eap_teap_parse_start(struct eap_teap_pac **pac)
+{
+ if (*pac)
+ return "START line without END";
+
+ *pac = os_zalloc(sizeof(struct eap_teap_pac));
+ if (!(*pac))
+ return "No memory for PAC entry";
+ (*pac)->pac_type = PAC_TYPE_TUNNEL_PAC;
+ return NULL;
+}
+
+
+static const char * eap_teap_parse_end(struct eap_teap_pac **pac_root,
+ struct eap_teap_pac **pac)
+{
+ if (!(*pac))
+ return "END line without START";
+ if (*pac_root) {
+ struct eap_teap_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_teap_parse_pac_type(struct eap_teap_pac *pac,
+ char *pos)
+{
+ if (!pos)
+ return "Cannot parse pac type";
+ pac->pac_type = atoi(pos);
+ if (pac->pac_type != PAC_TYPE_TUNNEL_PAC)
+ return "Unrecognized PAC-Type";
+
+ return NULL;
+}
+
+
+static const char * eap_teap_parse_pac_key(struct eap_teap_pac *pac, char *pos)
+{
+ u8 *key;
+ size_t key_len;
+
+ key = eap_teap_parse_hex(pos, &key_len);
+ if (!key || key_len != EAP_TEAP_PAC_KEY_LEN) {
+ os_free(key);
+ return "Invalid PAC-Key";
+ }
+
+ os_memcpy(pac->pac_key, key, EAP_TEAP_PAC_KEY_LEN);
+ os_free(key);
+
+ return NULL;
+}
+
+
+static const char * eap_teap_parse_pac_opaque(struct eap_teap_pac *pac,
+ char *pos)
+{
+ os_free(pac->pac_opaque);
+ pac->pac_opaque = eap_teap_parse_hex(pos, &pac->pac_opaque_len);
+ if (!pac->pac_opaque)
+ return "Invalid PAC-Opaque";
+ return NULL;
+}
+
+
+static const char * eap_teap_parse_a_id(struct eap_teap_pac *pac, char *pos)
+{
+ os_free(pac->a_id);
+ pac->a_id = eap_teap_parse_hex(pos, &pac->a_id_len);
+ if (!pac->a_id)
+ return "Invalid A-ID";
+ return NULL;
+}
+
+
+static const char * eap_teap_parse_i_id(struct eap_teap_pac *pac, char *pos)
+{
+ os_free(pac->i_id);
+ pac->i_id = eap_teap_parse_hex(pos, &pac->i_id_len);
+ if (!pac->i_id)
+ return "Invalid I-ID";
+ return NULL;
+}
+
+
+static const char * eap_teap_parse_a_id_info(struct eap_teap_pac *pac,
+ char *pos)
+{
+ os_free(pac->a_id_info);
+ pac->a_id_info = eap_teap_parse_hex(pos, &pac->a_id_info_len);
+ if (!pac->a_id_info)
+ return "Invalid A-ID-Info";
+ return NULL;
+}
+
+
+/**
+ * eap_teap_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_teap_load_pac(struct eap_sm *sm, struct eap_teap_pac **pac_root,
+ const char *pac_file)
+{
+ struct eap_teap_read_ctx rc;
+ struct eap_teap_pac *pac = NULL;
+ int count = 0;
+ char *pos;
+ const char *err = NULL;
+
+ if (!pac_file)
+ return -1;
+
+ if (eap_teap_init_pac_data(sm, pac_file, &rc) < 0)
+ return 0;
+
+ if (eap_teap_read_line(&rc, &pos) < 0) {
+ /* empty file - assume it is fine to overwrite */
+ eap_teap_deinit_pac_data(&rc);
+ return 0;
+ }
+ if (os_strcmp(pac_file_hdr, rc.buf) != 0)
+ err = "Unrecognized header line";
+
+ while (!err && eap_teap_read_line(&rc, &pos) == 0) {
+ if (os_strcmp(rc.buf, "START") == 0)
+ err = eap_teap_parse_start(&pac);
+ else if (os_strcmp(rc.buf, "END") == 0) {
+ err = eap_teap_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_teap_parse_pac_type(pac, pos);
+ else if (os_strcmp(rc.buf, "PAC-Key") == 0)
+ err = eap_teap_parse_pac_key(pac, pos);
+ else if (os_strcmp(rc.buf, "PAC-Opaque") == 0)
+ err = eap_teap_parse_pac_opaque(pac, pos);
+ else if (os_strcmp(rc.buf, "A-ID") == 0)
+ err = eap_teap_parse_a_id(pac, pos);
+ else if (os_strcmp(rc.buf, "I-ID") == 0)
+ err = eap_teap_parse_i_id(pac, pos);
+ else if (os_strcmp(rc.buf, "A-ID-Info") == 0)
+ err = eap_teap_parse_a_id_info(pac, pos);
+ }
+
+ if (pac) {
+ if (!err)
+ err = "PAC block not terminated with END";
+ eap_teap_free_pac(pac);
+ }
+
+ eap_teap_deinit_pac_data(&rc);
+
+ if (err) {
+ wpa_printf(MSG_INFO, "EAP-TEAP: %s in '%s:%d'",
+ err, pac_file, rc.line);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Read %d PAC entries from '%s'",
+ count, pac_file);
+
+ return 0;
+}
+
+
+static void eap_teap_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;
+ char *end;
+
+ if (!data || !buf || !(*buf) || !pos || !(*pos) || *pos < *buf)
+ 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) {
+ os_free(*buf);
+ *buf = NULL;
+ return;
+ }
+ *pos = nbuf + (*pos - *buf);
+ *buf = nbuf;
+ *buf_len += need;
+ }
+ end = *buf + *buf_len;
+
+ ret = os_snprintf(*pos, end - *pos, "%s=", field);
+ if (os_snprintf_error(end - *pos, ret))
+ return;
+ *pos += ret;
+ *pos += wpa_snprintf_hex(*pos, end - *pos, data, len);
+ ret = os_snprintf(*pos, end - *pos, "\n");
+ if (os_snprintf_error(end - *pos, ret))
+ return;
+ *pos += ret;
+
+ if (txt) {
+ ret = os_snprintf(*pos, end - *pos, "%s-txt=", field);
+ if (os_snprintf_error(end - *pos, ret))
+ return;
+ *pos += ret;
+ for (i = 0; i < len; i++) {
+ ret = os_snprintf(*pos, end - *pos, "%c", data[i]);
+ if (os_snprintf_error(end - *pos, ret))
+ return;
+ *pos += ret;
+ }
+ ret = os_snprintf(*pos, end - *pos, "\n");
+ if (os_snprintf_error(end - *pos, ret))
+ return;
+ *pos += ret;
+ }
+}
+
+
+static int eap_teap_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)
+ return -1;
+ blob->data = (u8 *) buf;
+ blob->len = len;
+ buf = NULL;
+ blob->name = os_strdup(pac_file + 7);
+ if (!blob->name) {
+ os_free(blob);
+ return -1;
+ }
+ eap_set_config_blob(sm, blob);
+ } else {
+ FILE *f;
+
+ f = fopen(pac_file, "wb");
+ if (!f) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Failed to open PAC file '%s' for writing",
+ pac_file);
+ return -1;
+ }
+ if (fwrite(buf, 1, len, f) != len) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Failed to write all PACs into '%s'",
+ pac_file);
+ fclose(f);
+ return -1;
+ }
+ os_free(buf);
+ fclose(f);
+ }
+
+ return 0;
+}
+
+
+static int eap_teap_add_pac_data(struct eap_teap_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 (os_snprintf_error(*buf + *buf_len - *pos, ret))
+ return -1;
+
+ *pos += ret;
+ eap_teap_write(buf, pos, buf_len, "PAC-Key",
+ pac->pac_key, EAP_TEAP_PAC_KEY_LEN, 0);
+ eap_teap_write(buf, pos, buf_len, "PAC-Opaque",
+ pac->pac_opaque, pac->pac_opaque_len, 0);
+ eap_teap_write(buf, pos, buf_len, "PAC-Info",
+ pac->pac_info, pac->pac_info_len, 0);
+ eap_teap_write(buf, pos, buf_len, "A-ID",
+ pac->a_id, pac->a_id_len, 0);
+ eap_teap_write(buf, pos, buf_len, "I-ID",
+ pac->i_id, pac->i_id_len, 1);
+ eap_teap_write(buf, pos, buf_len, "A-ID-Info",
+ pac->a_id_info, pac->a_id_info_len, 1);
+ if (!(*buf)) {
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: No memory for PAC data");
+ return -1;
+ }
+ ret = os_snprintf(*pos, *buf + *buf_len - *pos, "END\n");
+ if (os_snprintf_error(*buf + *buf_len - *pos, ret))
+ return -1;
+ *pos += ret;
+
+ return 0;
+}
+
+
+/**
+ * eap_teap_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_teap_save_pac(struct eap_sm *sm, struct eap_teap_pac *pac_root,
+ const char *pac_file)
+{
+ struct eap_teap_pac *pac;
+ int ret, count = 0;
+ char *buf, *pos;
+ size_t buf_len;
+
+ if (!pac_file)
+ return -1;
+
+ buf_len = 1024;
+ pos = buf = os_malloc(buf_len);
+ if (!buf)
+ return -1;
+
+ ret = os_snprintf(pos, buf + buf_len - pos, "%s\n", pac_file_hdr);
+ if (os_snprintf_error(buf + buf_len - pos, ret)) {
+ os_free(buf);
+ return -1;
+ }
+ pos += ret;
+
+ pac = pac_root;
+ while (pac) {
+ if (eap_teap_add_pac_data(pac, &buf, &pos, &buf_len)) {
+ os_free(buf);
+ return -1;
+ }
+ count++;
+ pac = pac->next;
+ }
+
+ if (eap_teap_write_pac(sm, pac_file, buf, pos - buf)) {
+ os_free(buf);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Wrote %d PAC entries into '%s'",
+ count, pac_file);
+
+ return 0;
+}
+
+
+/**
+ * eap_teap_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_teap_pac_list_truncate(struct eap_teap_pac *pac_root,
+ size_t max_len)
+{
+ struct eap_teap_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)
+ return 0;
+
+ count = 0;
+ prev->next = NULL;
+
+ while (pac) {
+ prev = pac;
+ pac = pac->next;
+ eap_teap_free_pac(prev);
+ count++;
+ }
+
+ return count;
+}
+
+
+static void eap_teap_pac_get_a_id(struct eap_teap_pac *pac)
+{
+ u8 *pos, *end;
+ u16 type, len;
+
+ pos = pac->pac_info;
+ end = pos + pac->pac_info_len;
+
+ while (end - pos > 4) {
+ type = WPA_GET_BE16(pos);
+ pos += 2;
+ len = WPA_GET_BE16(pos);
+ pos += 2;
+ if (len > (unsigned int) (end - pos))
+ break;
+
+ if (type == PAC_TYPE_A_ID) {
+ os_free(pac->a_id);
+ pac->a_id = os_memdup(pos, len);
+ if (!pac->a_id)
+ break;
+ pac->a_id_len = len;
+ }
+
+ if (type == PAC_TYPE_A_ID_INFO) {
+ os_free(pac->a_id_info);
+ pac->a_id_info = os_memdup(pos, len);
+ if (!pac->a_id_info)
+ break;
+ pac->a_id_info_len = len;
+ }
+
+ pos += len;
+ }
+}
+
+
+/**
+ * eap_teap_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_teap_load_pac_bin(struct eap_sm *sm, struct eap_teap_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_teap_pac *pac, *prev;
+
+ *pac_root = NULL;
+
+ if (!pac_file)
+ return -1;
+
+ if (os_strncmp(pac_file, "blob://", 7) == 0) {
+ blob = eap_get_config_blob(sm, pac_file + 7);
+ if (!blob) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: 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) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: No PAC file '%s' - assume no PAC entries have been provisioned",
+ pac_file);
+ return 0;
+ }
+ }
+
+ if (len == 0) {
+ if (!blob)
+ os_free(buf);
+ return 0;
+ }
+
+ if (len < 6 || WPA_GET_BE32(buf) != EAP_TEAP_PAC_BINARY_MAGIC ||
+ WPA_GET_BE16(buf + 4) != EAP_TEAP_PAC_BINARY_FORMAT_VERSION) {
+ wpa_printf(MSG_INFO, "EAP-TEAP: Invalid PAC file '%s' (bin)",
+ pac_file);
+ if (!blob)
+ os_free(buf);
+ return -1;
+ }
+
+ pac = prev = NULL;
+ pos = buf + 6;
+ end = buf + len;
+ while (pos < end) {
+ u16 val;
+
+ if (end - pos < 2 + EAP_TEAP_PAC_KEY_LEN + 2 + 2) {
+ pac = NULL;
+ goto parse_fail;
+ }
+
+ pac = os_zalloc(sizeof(*pac));
+ if (!pac)
+ goto parse_fail;
+
+ pac->pac_type = WPA_GET_BE16(pos);
+ pos += 2;
+ os_memcpy(pac->pac_key, pos, EAP_TEAP_PAC_KEY_LEN);
+ pos += EAP_TEAP_PAC_KEY_LEN;
+ val = WPA_GET_BE16(pos);
+ pos += 2;
+ if (val > end - pos)
+ goto parse_fail;
+ pac->pac_opaque_len = val;
+ pac->pac_opaque = os_memdup(pos, pac->pac_opaque_len);
+ if (!pac->pac_opaque)
+ goto parse_fail;
+ pos += pac->pac_opaque_len;
+ if (end - pos < 2)
+ goto parse_fail;
+ val = WPA_GET_BE16(pos);
+ pos += 2;
+ if (val > end - pos)
+ goto parse_fail;
+ pac->pac_info_len = val;
+ pac->pac_info = os_memdup(pos, pac->pac_info_len);
+ if (!pac->pac_info)
+ goto parse_fail;
+ pos += pac->pac_info_len;
+ eap_teap_pac_get_a_id(pac);
+
+ count++;
+ if (prev)
+ prev->next = pac;
+ else
+ *pac_root = pac;
+ prev = pac;
+ }
+
+ if (!blob)
+ os_free(buf);
+
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Read %lu PAC entries from '%s' (bin)",
+ (unsigned long) count, pac_file);
+
+ return 0;
+
+parse_fail:
+ wpa_printf(MSG_INFO, "EAP-TEAP: Failed to parse PAC file '%s' (bin)",
+ pac_file);
+ if (!blob)
+ os_free(buf);
+ if (pac)
+ eap_teap_free_pac(pac);
+ return -1;
+}
+
+
+/**
+ * eap_teap_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_teap_save_pac_bin(struct eap_sm *sm, struct eap_teap_pac *pac_root,
+ const char *pac_file)
+{
+ size_t len, count = 0;
+ struct eap_teap_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_TEAP_PAC_KEY_LEN + 2 + pac->pac_opaque_len +
+ 2 + pac->pac_info_len;
+ pac = pac->next;
+ }
+
+ buf = os_malloc(len);
+ if (!buf)
+ return -1;
+
+ pos = buf;
+ WPA_PUT_BE32(pos, EAP_TEAP_PAC_BINARY_MAGIC);
+ pos += 4;
+ WPA_PUT_BE16(pos, EAP_TEAP_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_TEAP_PAC_KEY_LEN);
+ pos += EAP_TEAP_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_teap_write_pac(sm, pac_file, (char *) buf, len)) {
+ os_free(buf);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Wrote %lu PAC entries into '%s' (bin)",
+ (unsigned long) count, pac_file);
+
+ return 0;
+}
--- /dev/null
+/*
+ * EAP peer method: EAP-TEAP PAC file processing
+ * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_TEAP_PAC_H
+#define EAP_TEAP_PAC_H
+
+#include "eap_common/eap_teap_common.h"
+
+struct eap_teap_pac {
+ struct eap_teap_pac *next;
+
+ u8 pac_key[EAP_TEAP_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_teap_free_pac(struct eap_teap_pac *pac);
+struct eap_teap_pac * eap_teap_get_pac(struct eap_teap_pac *pac_root,
+ const u8 *a_id, size_t a_id_len,
+ u16 pac_type);
+int eap_teap_add_pac(struct eap_teap_pac **pac_root,
+ struct eap_teap_pac **pac_current,
+ struct eap_teap_pac *entry);
+int eap_teap_load_pac(struct eap_sm *sm, struct eap_teap_pac **pac_root,
+ const char *pac_file);
+int eap_teap_save_pac(struct eap_sm *sm, struct eap_teap_pac *pac_root,
+ const char *pac_file);
+size_t eap_teap_pac_list_truncate(struct eap_teap_pac *pac_root,
+ size_t max_len);
+int eap_teap_load_pac_bin(struct eap_sm *sm, struct eap_teap_pac **pac_root,
+ const char *pac_file);
+int eap_teap_save_pac_bin(struct eap_sm *sm, struct eap_teap_pac *pac_root,
+ const char *pac_file);
+
+#endif /* EAP_TEAP_PAC_H */
struct eap_peer_config *config, int phase2)
{
os_memset(params, 0, sizeof(*params));
- if (sm->workaround && data->eap_type != EAP_TYPE_FAST) {
+ if (sm->workaround && data->eap_type != EAP_TYPE_FAST &&
+ data->eap_type != EAP_TYPE_TEAP) {
/*
* Some deployed authentication servers seem to be unable to
* handle the TLS Session Ticket extension (they are supposed
*/
params->flags |= TLS_CONN_DISABLE_SESSION_TICKET;
}
+ if (data->eap_type == EAP_TYPE_TEAP) {
+ /* RFC 7170 requires TLS v1.2 or newer to be used with TEAP */
+ params->flags |= TLS_CONN_DISABLE_TLSv1_0 |
+ TLS_CONN_DISABLE_TLSv1_1;
+ if (config->teap_anon_dh)
+ params->flags |= TLS_CONN_TEAP_ANON_DH;
+ }
if (data->eap_type == EAP_TYPE_FAST ||
+ data->eap_type == EAP_TYPE_TEAP ||
data->eap_type == EAP_TYPE_TTLS ||
data->eap_type == EAP_TYPE_PEAP) {
/* The current EAP peer implementation is not yet ready for the
void *ssl_ctx;
/**
- * eap_type - EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST)
+ * eap_type - EAP method used in Phase 1
+ * (EAP_TYPE_TLS/PEAP/TTLS/FAST/TEAP)
*/
u8 eap_type;
#define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80
#define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40
#define EAP_TLS_FLAGS_START 0x20
+#define EAP_TEAP_FLAGS_OUTER_TLV_LEN 0x10
#define EAP_TLS_VERSION_MASK 0x07
/* could be up to 128 bytes, but only the first 64 bytes are used */
int eap_fast_prov;
int pac_key_lifetime;
int pac_key_refresh_time;
+ int eap_teap_auth;
+ int eap_teap_pac_no_inner;
int eap_sim_aka_result_ind;
int tnc;
struct wps_context *wps;
} eap_fast_prov;
int pac_key_lifetime;
int pac_key_refresh_time;
+ int eap_teap_auth;
+ int eap_teap_pac_no_inner;
int eap_sim_aka_result_ind;
int tnc;
u16 pwd_group;
int eap_server_gpsk_register(void);
int eap_server_vendor_test_register(void);
int eap_server_fast_register(void);
+int eap_server_teap_register(void);
int eap_server_wsc_register(void);
int eap_server_ikev2_register(void);
int eap_server_tnc_register(void);
sm->eap_fast_prov = conf->eap_fast_prov;
sm->pac_key_lifetime = conf->pac_key_lifetime;
sm->pac_key_refresh_time = conf->pac_key_refresh_time;
+ sm->eap_teap_auth = conf->eap_teap_auth;
+ sm->eap_teap_pac_no_inner = conf->eap_teap_pac_no_inner;
sm->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
sm->tnc = conf->tnc;
sm->wps = conf->wps;
--- /dev/null
+/*
+ * EAP-TEAP server (RFC 7170)
+ * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/tls.h"
+#include "crypto/random.h"
+#include "eap_common/eap_teap_common.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+
+
+static void eap_teap_reset(struct eap_sm *sm, void *priv);
+
+
+/* Private PAC-Opaque TLV types */
+#define PAC_OPAQUE_TYPE_PAD 0
+#define PAC_OPAQUE_TYPE_KEY 1
+#define PAC_OPAQUE_TYPE_LIFETIME 2
+#define PAC_OPAQUE_TYPE_IDENTITY 3
+
+struct eap_teap_data {
+ struct eap_ssl_data ssl;
+ enum {
+ START, PHASE1, PHASE1B, PHASE2_START, PHASE2_ID,
+ PHASE2_BASIC_AUTH, PHASE2_METHOD, CRYPTO_BINDING, REQUEST_PAC,
+ FAILURE_SEND_RESULT, SUCCESS, FAILURE
+ } state;
+
+ u8 teap_version;
+ u8 peer_version;
+ u16 tls_cs;
+
+ const struct eap_method *phase2_method;
+ void *phase2_priv;
+
+ u8 crypto_binding_nonce[32];
+ int final_result;
+
+ u8 simck_msk[EAP_TEAP_SIMCK_LEN];
+ u8 cmk_msk[EAP_TEAP_CMK_LEN];
+ u8 simck_emsk[EAP_TEAP_SIMCK_LEN];
+ u8 cmk_emsk[EAP_TEAP_CMK_LEN];
+ int simck_idx;
+ int cmk_emsk_available;
+
+ u8 pac_opaque_encr[16];
+ u8 *srv_id;
+ size_t srv_id_len;
+ char *srv_id_info;
+
+ int anon_provisioning;
+ int send_new_pac; /* server triggered re-keying of Tunnel PAC */
+ struct wpabuf *pending_phase2_resp;
+ struct wpabuf *server_outer_tlvs;
+ struct wpabuf *peer_outer_tlvs;
+ u8 *identity; /* from PAC-Opaque */
+ size_t identity_len;
+ int eap_seq;
+ int tnc_started;
+
+ int pac_key_lifetime;
+ int pac_key_refresh_time;
+
+ enum teap_error_codes error_code;
+};
+
+
+static int eap_teap_process_phase2_start(struct eap_sm *sm,
+ struct eap_teap_data *data);
+
+
+static const char * eap_teap_state_txt(int state)
+{
+ switch (state) {
+ case START:
+ return "START";
+ case PHASE1:
+ return "PHASE1";
+ case PHASE1B:
+ return "PHASE1B";
+ case PHASE2_START:
+ return "PHASE2_START";
+ case PHASE2_ID:
+ return "PHASE2_ID";
+ case PHASE2_BASIC_AUTH:
+ return "PHASE2_BASIC_AUTH";
+ case PHASE2_METHOD:
+ return "PHASE2_METHOD";
+ case CRYPTO_BINDING:
+ return "CRYPTO_BINDING";
+ case REQUEST_PAC:
+ return "REQUEST_PAC";
+ case FAILURE_SEND_RESULT:
+ return "FAILURE_SEND_RESULT";
+ case SUCCESS:
+ return "SUCCESS";
+ case FAILURE:
+ return "FAILURE";
+ default:
+ return "Unknown?!";
+ }
+}
+
+
+static void eap_teap_state(struct eap_teap_data *data, int state)
+{
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: %s -> %s",
+ eap_teap_state_txt(data->state),
+ eap_teap_state_txt(state));
+ data->state = state;
+}
+
+
+static EapType eap_teap_req_failure(struct eap_teap_data *data,
+ enum teap_error_codes error)
+{
+ eap_teap_state(data, FAILURE_SEND_RESULT);
+ return EAP_TYPE_NONE;
+}
+
+
+static int eap_teap_session_ticket_cb(void *ctx, const u8 *ticket, size_t len,
+ const u8 *client_random,
+ const u8 *server_random,
+ u8 *master_secret)
+{
+ struct eap_teap_data *data = ctx;
+ const u8 *pac_opaque;
+ size_t pac_opaque_len;
+ u8 *buf, *pos, *end, *pac_key = NULL;
+ os_time_t lifetime = 0;
+ struct os_time now;
+ u8 *identity = NULL;
+ size_t identity_len = 0;
+
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: SessionTicket callback");
+ wpa_hexdump(MSG_DEBUG, "EAP-TEAP: SessionTicket (PAC-Opaque)",
+ ticket, len);
+
+ if (len < 4 || WPA_GET_BE16(ticket) != PAC_TYPE_PAC_OPAQUE) {
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Ignore invalid SessionTicket");
+ return 0;
+ }
+
+ pac_opaque_len = WPA_GET_BE16(ticket + 2);
+ pac_opaque = ticket + 4;
+ if (pac_opaque_len < 8 || pac_opaque_len % 8 ||
+ pac_opaque_len > len - 4) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Ignore invalid PAC-Opaque (len=%lu left=%lu)",
+ (unsigned long) pac_opaque_len,
+ (unsigned long) len);
+ return 0;
+ }
+ wpa_hexdump(MSG_DEBUG, "EAP-TEAP: Received PAC-Opaque",
+ pac_opaque, pac_opaque_len);
+
+ buf = os_malloc(pac_opaque_len - 8);
+ if (!buf) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Failed to allocate memory for decrypting PAC-Opaque");
+ return 0;
+ }
+
+ if (aes_unwrap(data->pac_opaque_encr, sizeof(data->pac_opaque_encr),
+ (pac_opaque_len - 8) / 8, pac_opaque, buf) < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Failed to decrypt PAC-Opaque");
+ os_free(buf);
+ /*
+ * This may have been caused by server changing the PAC-Opaque
+ * encryption key, so just ignore this PAC-Opaque instead of
+ * failing the authentication completely. Provisioning can now
+ * be used to provision a new PAC.
+ */
+ return 0;
+ }
+
+ end = buf + pac_opaque_len - 8;
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: Decrypted PAC-Opaque",
+ buf, end - buf);
+
+ pos = buf;
+ while (end - pos > 1) {
+ u8 id, elen;
+
+ id = *pos++;
+ elen = *pos++;
+ if (elen > end - pos)
+ break;
+
+ switch (id) {
+ case PAC_OPAQUE_TYPE_PAD:
+ goto done;
+ case PAC_OPAQUE_TYPE_KEY:
+ if (elen != EAP_TEAP_PAC_KEY_LEN) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Invalid PAC-Key length %d",
+ elen);
+ os_free(buf);
+ return -1;
+ }
+ pac_key = pos;
+ wpa_hexdump_key(MSG_DEBUG,
+ "EAP-TEAP: PAC-Key from decrypted PAC-Opaque",
+ pac_key, EAP_TEAP_PAC_KEY_LEN);
+ break;
+ case PAC_OPAQUE_TYPE_LIFETIME:
+ if (elen != 4) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Invalid PAC-Key lifetime length %d",
+ elen);
+ os_free(buf);
+ return -1;
+ }
+ lifetime = WPA_GET_BE32(pos);
+ break;
+ case PAC_OPAQUE_TYPE_IDENTITY:
+ identity = pos;
+ identity_len = elen;
+ break;
+ }
+
+ pos += elen;
+ }
+done:
+
+ if (!pac_key) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: No PAC-Key included in PAC-Opaque");
+ os_free(buf);
+ return -1;
+ }
+
+ if (identity) {
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "EAP-TEAP: Identity from PAC-Opaque",
+ identity, identity_len);
+ os_free(data->identity);
+ data->identity = os_malloc(identity_len);
+ if (data->identity) {
+ os_memcpy(data->identity, identity, identity_len);
+ data->identity_len = identity_len;
+ }
+ }
+
+ if (os_get_time(&now) < 0 || lifetime <= 0 || now.sec > lifetime) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: PAC-Key not valid anymore (lifetime=%ld now=%ld)",
+ lifetime, now.sec);
+ data->send_new_pac = 2;
+ /*
+ * Allow PAC to be used to allow a PAC update with some level
+ * of server authentication (i.e., do not fall back to full TLS
+ * handshake since we cannot be sure that the peer would be
+ * able to validate server certificate now). However, reject
+ * the authentication since the PAC was not valid anymore. Peer
+ * can connect again with the newly provisioned PAC after this.
+ */
+ } else if (lifetime - now.sec < data->pac_key_refresh_time) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: PAC-Key soft timeout; send an update if authentication succeeds");
+ data->send_new_pac = 1;
+ }
+
+ /* EAP-TEAP uses PAC-Key as the TLS master_secret */
+ os_memcpy(master_secret, pac_key, EAP_TEAP_PAC_KEY_LEN);
+
+ os_free(buf);
+
+ return 1;
+}
+
+
+static int eap_teap_derive_key_auth(struct eap_sm *sm,
+ struct eap_teap_data *data)
+{
+ int res;
+
+ /* RFC 7170, Section 5.1 */
+ res = tls_connection_export_key(sm->ssl_ctx, data->ssl.conn,
+ TEAP_TLS_EXPORTER_LABEL_SKS, NULL, 0,
+ data->simck_msk, EAP_TEAP_SIMCK_LEN);
+ if (res)
+ return res;
+ wpa_hexdump_key(MSG_DEBUG,
+ "EAP-TEAP: session_key_seed (S-IMCK[0])",
+ data->simck_msk, EAP_TEAP_SIMCK_LEN);
+ os_memcpy(data->simck_emsk, data->simck_msk, EAP_TEAP_SIMCK_LEN);
+ data->simck_idx = 0;
+ return 0;
+}
+
+
+static int eap_teap_update_icmk(struct eap_sm *sm, struct eap_teap_data *data)
+{
+ u8 *msk = NULL, *emsk = NULL;
+ size_t msk_len = 0, emsk_len = 0;
+ int res;
+
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Deriving ICMK[%d] (S-IMCK and CMK)",
+ data->simck_idx + 1);
+
+ if (sm->eap_teap_auth == 1)
+ return eap_teap_derive_cmk_basic_pw_auth(data->simck_msk,
+ data->cmk_msk);
+
+ if (!data->phase2_method || !data->phase2_priv) {
+ wpa_printf(MSG_INFO, "EAP-TEAP: Phase 2 method not available");
+ return -1;
+ }
+
+ if (data->phase2_method->getKey) {
+ msk = data->phase2_method->getKey(sm, data->phase2_priv,
+ &msk_len);
+ if (!msk) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Could not fetch Phase 2 MSK");
+ return -1;
+ }
+ }
+
+ if (data->phase2_method->get_emsk) {
+ emsk = data->phase2_method->get_emsk(sm, data->phase2_priv,
+ &emsk_len);
+ }
+
+ res = eap_teap_derive_imck(data->simck_msk, data->simck_emsk,
+ msk, msk_len, emsk, emsk_len,
+ data->simck_msk, data->cmk_msk,
+ data->simck_emsk, data->cmk_emsk);
+ bin_clear_free(msk, msk_len);
+ bin_clear_free(emsk, emsk_len);
+ if (res == 0) {
+ data->simck_idx++;
+ if (emsk)
+ data->cmk_emsk_available = 1;
+ }
+ return 0;
+}
+
+
+static void * eap_teap_init(struct eap_sm *sm)
+{
+ struct eap_teap_data *data;
+
+ data = os_zalloc(sizeof(*data));
+ if (!data)
+ return NULL;
+ data->teap_version = EAP_TEAP_VERSION;
+ data->state = START;
+
+ if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_TYPE_TEAP)) {
+ wpa_printf(MSG_INFO, "EAP-TEAP: Failed to initialize SSL.");
+ eap_teap_reset(sm, data);
+ return NULL;
+ }
+
+ /* TODO: Add anon-DH TLS cipher suites (and if one is negotiated,
+ * enforce inner EAP with mutual authentication to be used) */
+
+ if (tls_connection_set_session_ticket_cb(sm->ssl_ctx, data->ssl.conn,
+ eap_teap_session_ticket_cb,
+ data) < 0) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Failed to set SessionTicket callback");
+ eap_teap_reset(sm, data);
+ return NULL;
+ }
+
+ if (!sm->pac_opaque_encr_key) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: No PAC-Opaque encryption key configured");
+ eap_teap_reset(sm, data);
+ return NULL;
+ }
+ os_memcpy(data->pac_opaque_encr, sm->pac_opaque_encr_key,
+ sizeof(data->pac_opaque_encr));
+
+ if (!sm->eap_fast_a_id) {
+ wpa_printf(MSG_INFO, "EAP-TEAP: No A-ID configured");
+ eap_teap_reset(sm, data);
+ return NULL;
+ }
+ data->srv_id = os_malloc(sm->eap_fast_a_id_len);
+ if (!data->srv_id) {
+ eap_teap_reset(sm, data);
+ return NULL;
+ }
+ os_memcpy(data->srv_id, sm->eap_fast_a_id, sm->eap_fast_a_id_len);
+ data->srv_id_len = sm->eap_fast_a_id_len;
+
+ if (!sm->eap_fast_a_id_info) {
+ wpa_printf(MSG_INFO, "EAP-TEAP: No A-ID-Info configured");
+ eap_teap_reset(sm, data);
+ return NULL;
+ }
+ data->srv_id_info = os_strdup(sm->eap_fast_a_id_info);
+ if (!data->srv_id_info) {
+ eap_teap_reset(sm, data);
+ return NULL;
+ }
+
+ /* PAC-Key lifetime in seconds (hard limit) */
+ data->pac_key_lifetime = sm->pac_key_lifetime;
+
+ /*
+ * PAC-Key refresh time in seconds (soft limit on remaining hard
+ * limit). The server will generate a new PAC-Key when this number of
+ * seconds (or fewer) of the lifetime remains.
+ */
+ data->pac_key_refresh_time = sm->pac_key_refresh_time;
+
+ return data;
+}
+
+
+static void eap_teap_reset(struct eap_sm *sm, void *priv)
+{
+ struct eap_teap_data *data = priv;
+
+ if (!data)
+ return;
+ if (data->phase2_priv && data->phase2_method)
+ data->phase2_method->reset(sm, data->phase2_priv);
+ eap_server_tls_ssl_deinit(sm, &data->ssl);
+ os_free(data->srv_id);
+ os_free(data->srv_id_info);
+ wpabuf_free(data->pending_phase2_resp);
+ wpabuf_free(data->server_outer_tlvs);
+ wpabuf_free(data->peer_outer_tlvs);
+ os_free(data->identity);
+ forced_memzero(data->simck_msk, EAP_TEAP_SIMCK_LEN);
+ forced_memzero(data->simck_emsk, EAP_TEAP_SIMCK_LEN);
+ forced_memzero(data->cmk_msk, EAP_TEAP_CMK_LEN);
+ forced_memzero(data->cmk_emsk, EAP_TEAP_CMK_LEN);
+ forced_memzero(data->pac_opaque_encr, sizeof(data->pac_opaque_encr));
+ bin_clear_free(data, sizeof(*data));
+}
+
+
+static struct wpabuf * eap_teap_build_start(struct eap_sm *sm,
+ struct eap_teap_data *data, u8 id)
+{
+ struct wpabuf *req;
+ size_t outer_tlv_len = sizeof(struct teap_tlv_hdr) + data->srv_id_len;
+ const u8 *start, *end;
+
+ req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TEAP,
+ 1 + 4 + outer_tlv_len, EAP_CODE_REQUEST, id);
+ if (!req) {
+ wpa_printf(MSG_ERROR,
+ "EAP-TEAP: Failed to allocate memory for request");
+ eap_teap_state(data, FAILURE);
+ return NULL;
+ }
+
+ wpabuf_put_u8(req, EAP_TLS_FLAGS_START | EAP_TEAP_FLAGS_OUTER_TLV_LEN |
+ data->teap_version);
+ wpabuf_put_be32(req, outer_tlv_len);
+
+ start = wpabuf_put(req, 0);
+
+ /* RFC 7170, Section 4.2.2: Authority-ID TLV */
+ eap_teap_put_tlv(req, TEAP_TLV_AUTHORITY_ID,
+ data->srv_id, data->srv_id_len);
+
+ end = wpabuf_put(req, 0);
+ wpabuf_free(data->server_outer_tlvs);
+ data->server_outer_tlvs = wpabuf_alloc_copy(start, end - start);
+ if (!data->server_outer_tlvs) {
+ eap_teap_state(data, FAILURE);
+ return NULL;
+ }
+
+ eap_teap_state(data, PHASE1);
+
+ return req;
+}
+
+
+static int eap_teap_phase1_done(struct eap_sm *sm, struct eap_teap_data *data)
+{
+ char cipher[64];
+
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Phase 1 done, starting Phase 2");
+
+ data->tls_cs = tls_connection_get_cipher_suite(data->ssl.conn);
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: TLS cipher suite 0x%04x",
+ data->tls_cs);
+
+ if (tls_get_cipher(sm->ssl_ctx, data->ssl.conn, cipher, sizeof(cipher))
+ < 0) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Failed to get cipher information");
+ eap_teap_state(data, FAILURE);
+ return -1;
+ }
+ data->anon_provisioning = os_strstr(cipher, "ADH") != NULL;
+
+ if (data->anon_provisioning)
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Anonymous provisioning");
+
+ if (eap_teap_derive_key_auth(sm, data) < 0) {
+ eap_teap_state(data, FAILURE);
+ return -1;
+ }
+
+ eap_teap_state(data, PHASE2_START);
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_teap_build_phase2_req(struct eap_sm *sm,
+ struct eap_teap_data *data,
+ u8 id)
+{
+ struct wpabuf *req;
+
+ if (sm->eap_teap_auth == 1) {
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Initiate Basic-Password-Auth");
+ req = wpabuf_alloc(sizeof(struct teap_tlv_hdr));
+ if (!req)
+ return NULL;
+ eap_teap_put_tlv_hdr(req, TEAP_TLV_BASIC_PASSWORD_AUTH_REQ, 0);
+ return req;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Initiate inner EAP method");
+ if (!data->phase2_priv) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Phase 2 method not initialized");
+ return NULL;
+ }
+
+ req = data->phase2_method->buildReq(sm, data->phase2_priv, id);
+ if (!req)
+ return NULL;
+
+ wpa_hexdump_buf_key(MSG_MSGDUMP, "EAP-TEAP: Phase 2 EAP-Request", req);
+ return eap_teap_tlv_eap_payload(req);
+}
+
+
+static struct wpabuf * eap_teap_build_crypto_binding(
+ struct eap_sm *sm, struct eap_teap_data *data)
+{
+ struct wpabuf *buf;
+ struct teap_tlv_result *result;
+ struct teap_tlv_crypto_binding *cb;
+ u8 subtype, flags;
+
+ buf = wpabuf_alloc(2 * sizeof(*result) + sizeof(*cb));
+ if (!buf)
+ return NULL;
+
+ if (data->send_new_pac || data->anon_provisioning ||
+ data->phase2_method)
+ data->final_result = 0;
+ else
+ data->final_result = 1;
+
+ if (!data->final_result || data->eap_seq > 0) {
+ /* Intermediate-Result */
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Add Intermediate-Result TLV (status=SUCCESS)");
+ result = wpabuf_put(buf, sizeof(*result));
+ result->tlv_type = host_to_be16(TEAP_TLV_MANDATORY |
+ TEAP_TLV_INTERMEDIATE_RESULT);
+ result->length = host_to_be16(2);
+ result->status = host_to_be16(TEAP_STATUS_SUCCESS);
+ }
+
+ if (data->final_result) {
+ /* Result TLV */
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Add Result TLV (status=SUCCESS)");
+ result = wpabuf_put(buf, sizeof(*result));
+ result->tlv_type = host_to_be16(TEAP_TLV_MANDATORY |
+ TEAP_TLV_RESULT);
+ result->length = host_to_be16(2);
+ result->status = host_to_be16(TEAP_STATUS_SUCCESS);
+ }
+
+ /* Crypto-Binding TLV */
+ cb = wpabuf_put(buf, sizeof(*cb));
+ cb->tlv_type = host_to_be16(TEAP_TLV_MANDATORY |
+ TEAP_TLV_CRYPTO_BINDING);
+ cb->length = host_to_be16(sizeof(*cb) - sizeof(struct teap_tlv_hdr));
+ cb->version = EAP_TEAP_VERSION;
+ cb->received_version = data->peer_version;
+ /* FIX: RFC 7170 is not clear on which Flags value to use when
+ * Crypto-Binding TLV is used with Basic-Password-Auth */
+ flags = data->cmk_emsk_available ?
+ TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC :
+ TEAP_CRYPTO_BINDING_MSK_CMAC;
+ subtype = TEAP_CRYPTO_BINDING_SUBTYPE_REQUEST;
+ cb->subtype = (flags << 4) | subtype;
+ if (random_get_bytes(cb->nonce, sizeof(cb->nonce)) < 0) {
+ wpabuf_free(buf);
+ return NULL;
+ }
+
+ /*
+ * RFC 7170, Section 4.2.13:
+ * The nonce in a request MUST have its least significant bit set to 0.
+ */
+ cb->nonce[sizeof(cb->nonce) - 1] &= ~0x01;
+
+ os_memcpy(data->crypto_binding_nonce, cb->nonce, sizeof(cb->nonce));
+
+ if (eap_teap_compound_mac(data->tls_cs, cb, data->server_outer_tlvs,
+ data->peer_outer_tlvs, data->cmk_msk,
+ cb->msk_compound_mac) < 0) {
+ wpabuf_free(buf);
+ return NULL;
+ }
+
+ if (data->cmk_emsk_available &&
+ eap_teap_compound_mac(data->tls_cs, cb, data->server_outer_tlvs,
+ data->peer_outer_tlvs, data->cmk_emsk,
+ cb->emsk_compound_mac) < 0) {
+ wpabuf_free(buf);
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Add Crypto-Binding TLV: Version %u Received Version %u Flags %u Sub-Type %u",
+ cb->version, cb->received_version, flags, subtype);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Nonce",
+ cb->nonce, sizeof(cb->nonce));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: EMSK Compound MAC",
+ cb->emsk_compound_mac, sizeof(cb->emsk_compound_mac));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: MSK Compound MAC",
+ cb->msk_compound_mac, sizeof(cb->msk_compound_mac));
+
+ return buf;
+}
+
+
+static struct wpabuf * eap_teap_build_pac(struct eap_sm *sm,
+ struct eap_teap_data *data)
+{
+ u8 pac_key[EAP_TEAP_PAC_KEY_LEN];
+ u8 *pac_buf, *pac_opaque;
+ struct wpabuf *buf;
+ u8 *pos;
+ size_t buf_len, srv_id_info_len, pac_len;
+ struct teap_tlv_hdr *pac_tlv;
+ struct pac_attr_hdr *pac_info;
+ struct teap_tlv_result *result;
+ struct os_time now;
+
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Build a new PAC");
+
+ if (random_get_bytes(pac_key, EAP_TEAP_PAC_KEY_LEN) < 0 ||
+ os_get_time(&now) < 0)
+ return NULL;
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: Generated PAC-Key",
+ pac_key, EAP_TEAP_PAC_KEY_LEN);
+
+ pac_len = (2 + EAP_TEAP_PAC_KEY_LEN) + (2 + 4) +
+ (2 + sm->identity_len) + 8;
+ pac_buf = os_malloc(pac_len);
+ if (!pac_buf)
+ return NULL;
+
+ srv_id_info_len = os_strlen(data->srv_id_info);
+
+ pos = pac_buf;
+ *pos++ = PAC_OPAQUE_TYPE_KEY;
+ *pos++ = EAP_TEAP_PAC_KEY_LEN;
+ os_memcpy(pos, pac_key, EAP_TEAP_PAC_KEY_LEN);
+ pos += EAP_TEAP_PAC_KEY_LEN;
+
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: PAC-Key lifetime: %u seconds",
+ data->pac_key_lifetime);
+ *pos++ = PAC_OPAQUE_TYPE_LIFETIME;
+ *pos++ = 4;
+ WPA_PUT_BE32(pos, now.sec + data->pac_key_lifetime);
+ pos += 4;
+
+ if (sm->identity) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-TEAP: PAC-Opaque Identity",
+ sm->identity, sm->identity_len);
+ *pos++ = PAC_OPAQUE_TYPE_IDENTITY;
+ *pos++ = sm->identity_len;
+ os_memcpy(pos, sm->identity, sm->identity_len);
+ pos += sm->identity_len;
+ }
+
+ pac_len = pos - pac_buf;
+ while (pac_len % 8) {
+ *pos++ = PAC_OPAQUE_TYPE_PAD;
+ pac_len++;
+ }
+
+ pac_opaque = os_malloc(pac_len + 8);
+ if (!pac_opaque) {
+ os_free(pac_buf);
+ return NULL;
+ }
+ if (aes_wrap(data->pac_opaque_encr, sizeof(data->pac_opaque_encr),
+ pac_len / 8, pac_buf, pac_opaque) < 0) {
+ os_free(pac_buf);
+ os_free(pac_opaque);
+ return NULL;
+ }
+ os_free(pac_buf);
+
+ pac_len += 8;
+ wpa_hexdump(MSG_DEBUG, "EAP-TEAP: PAC-Opaque", pac_opaque, pac_len);
+
+ buf_len = sizeof(*pac_tlv) +
+ sizeof(struct pac_attr_hdr) + EAP_TEAP_PAC_KEY_LEN +
+ sizeof(struct pac_attr_hdr) + pac_len +
+ data->srv_id_len + srv_id_info_len + 100 + sizeof(*result);
+ buf = wpabuf_alloc(buf_len);
+ if (!buf) {
+ os_free(pac_opaque);
+ return NULL;
+ }
+
+ /* Result TLV */
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Add Result TLV (status=SUCCESS)");
+ result = wpabuf_put(buf, sizeof(*result));
+ WPA_PUT_BE16((u8 *) &result->tlv_type,
+ TEAP_TLV_MANDATORY | TEAP_TLV_RESULT);
+ WPA_PUT_BE16((u8 *) &result->length, 2);
+ WPA_PUT_BE16((u8 *) &result->status, TEAP_STATUS_SUCCESS);
+
+ /* PAC TLV */
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Add PAC TLV");
+ pac_tlv = wpabuf_put(buf, sizeof(*pac_tlv));
+ pac_tlv->tlv_type = host_to_be16(TEAP_TLV_MANDATORY | TEAP_TLV_PAC);
+
+ /* PAC-Key */
+ eap_teap_put_tlv(buf, PAC_TYPE_PAC_KEY, pac_key, EAP_TEAP_PAC_KEY_LEN);
+
+ /* PAC-Opaque */
+ eap_teap_put_tlv(buf, PAC_TYPE_PAC_OPAQUE, pac_opaque, pac_len);
+ os_free(pac_opaque);
+
+ /* PAC-Info */
+ pac_info = wpabuf_put(buf, sizeof(*pac_info));
+ pac_info->type = host_to_be16(PAC_TYPE_PAC_INFO);
+
+ /* PAC-Lifetime (inside PAC-Info) */
+ eap_teap_put_tlv_hdr(buf, PAC_TYPE_CRED_LIFETIME, 4);
+ wpabuf_put_be32(buf, now.sec + data->pac_key_lifetime);
+
+ /* A-ID (inside PAC-Info) */
+ eap_teap_put_tlv(buf, PAC_TYPE_A_ID, data->srv_id, data->srv_id_len);
+
+ /* Note: headers may be misaligned after A-ID */
+
+ if (sm->identity) {
+ eap_teap_put_tlv(buf, PAC_TYPE_I_ID, sm->identity,
+ sm->identity_len);
+ }
+
+ /* A-ID-Info (inside PAC-Info) */
+ eap_teap_put_tlv(buf, PAC_TYPE_A_ID_INFO, data->srv_id_info,
+ srv_id_info_len);
+
+ /* PAC-Type (inside PAC-Info) */
+ eap_teap_put_tlv_hdr(buf, PAC_TYPE_PAC_TYPE, 2);
+ wpabuf_put_be16(buf, PAC_TYPE_TUNNEL_PAC);
+
+ /* Update PAC-Info and PAC TLV Length fields */
+ pos = wpabuf_put(buf, 0);
+ pac_info->len = host_to_be16(pos - (u8 *) (pac_info + 1));
+ pac_tlv->length = host_to_be16(pos - (u8 *) (pac_tlv + 1));
+
+ return buf;
+}
+
+
+static int eap_teap_encrypt_phase2(struct eap_sm *sm,
+ struct eap_teap_data *data,
+ struct wpabuf *plain, int piggyback)
+{
+ struct wpabuf *encr;
+
+ wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TEAP: Encrypting Phase 2 TLVs",
+ plain);
+ encr = eap_server_tls_encrypt(sm, &data->ssl, plain);
+ wpabuf_free(plain);
+
+ if (!encr)
+ return -1;
+
+ if (data->ssl.tls_out && piggyback) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Piggyback Phase 2 data (len=%d) with last Phase 1 Message (len=%d used=%d)",
+ (int) wpabuf_len(encr),
+ (int) wpabuf_len(data->ssl.tls_out),
+ (int) data->ssl.tls_out_pos);
+ if (wpabuf_resize(&data->ssl.tls_out, wpabuf_len(encr)) < 0) {
+ wpa_printf(MSG_WARNING,
+ "EAP-TEAP: Failed to resize output buffer");
+ wpabuf_free(encr);
+ return -1;
+ }
+ wpabuf_put_buf(data->ssl.tls_out, encr);
+ wpabuf_free(encr);
+ } else {
+ wpabuf_free(data->ssl.tls_out);
+ data->ssl.tls_out_pos = 0;
+ data->ssl.tls_out = encr;
+ }
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_teap_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+ struct eap_teap_data *data = priv;
+ struct wpabuf *req = NULL;
+ int piggyback = 0;
+
+ if (data->ssl.state == FRAG_ACK) {
+ return eap_server_tls_build_ack(id, EAP_TYPE_TEAP,
+ data->teap_version);
+ }
+
+ if (data->ssl.state == WAIT_FRAG_ACK) {
+ return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TEAP,
+ data->teap_version, id);
+ }
+
+ switch (data->state) {
+ case START:
+ return eap_teap_build_start(sm, data, id);
+ case PHASE1B:
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+ if (eap_teap_phase1_done(sm, data) < 0)
+ return NULL;
+ if (data->state == PHASE2_START) {
+ int res;
+
+ /*
+ * Try to generate Phase 2 data to piggyback
+ * with the end of Phase 1 to avoid extra
+ * roundtrip.
+ */
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Try to start Phase 2");
+ res = eap_teap_process_phase2_start(sm, data);
+ if (res == 1) {
+ req = eap_teap_build_crypto_binding(
+ sm, data);
+ piggyback = 1;
+ break;
+ }
+
+ if (res)
+ break;
+ req = eap_teap_build_phase2_req(sm, data, id);
+ piggyback = 1;
+ }
+ }
+ break;
+ case PHASE2_ID:
+ case PHASE2_BASIC_AUTH:
+ case PHASE2_METHOD:
+ req = eap_teap_build_phase2_req(sm, data, id);
+ break;
+ case CRYPTO_BINDING:
+ req = eap_teap_build_crypto_binding(sm, data);
+ if (data->phase2_method) {
+ /*
+ * Include the start of the next EAP method in the
+ * sequence in the same message with Crypto-Binding to
+ * save a round-trip.
+ */
+ struct wpabuf *eap;
+
+ eap = eap_teap_build_phase2_req(sm, data, id);
+ req = wpabuf_concat(req, eap);
+ eap_teap_state(data, PHASE2_METHOD);
+ }
+ break;
+ case REQUEST_PAC:
+ req = eap_teap_build_pac(sm, data);
+ break;
+ case FAILURE_SEND_RESULT:
+ req = eap_teap_tlv_result(TEAP_STATUS_FAILURE, 0);
+ if (data->error_code)
+ req = wpabuf_concat(
+ req, eap_teap_tlv_error(data->error_code));
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: %s - unexpected state %d",
+ __func__, data->state);
+ return NULL;
+ }
+
+ if (req && eap_teap_encrypt_phase2(sm, data, req, piggyback) < 0)
+ return NULL;
+
+ return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TEAP,
+ data->teap_version, id);
+}
+
+
+static Boolean eap_teap_check(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ const u8 *pos;
+ size_t len;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TEAP, respData, &len);
+ if (!pos || len < 1) {
+ wpa_printf(MSG_INFO, "EAP-TEAP: Invalid frame");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static int eap_teap_phase2_init(struct eap_sm *sm, struct eap_teap_data *data,
+ EapType eap_type)
+{
+ if (data->phase2_priv && data->phase2_method) {
+ data->phase2_method->reset(sm, data->phase2_priv);
+ data->phase2_method = NULL;
+ data->phase2_priv = NULL;
+ }
+ data->phase2_method = eap_server_get_eap_method(EAP_VENDOR_IETF,
+ eap_type);
+ if (!data->phase2_method)
+ return -1;
+
+ sm->init_phase2 = 1;
+ data->phase2_priv = data->phase2_method->init(sm);
+ sm->init_phase2 = 0;
+
+ return data->phase2_priv ? 0 : -1;
+}
+
+
+static void eap_teap_process_phase2_response(struct eap_sm *sm,
+ struct eap_teap_data *data,
+ u8 *in_data, size_t in_len)
+{
+ u8 next_type = EAP_TYPE_NONE;
+ struct eap_hdr *hdr;
+ u8 *pos;
+ size_t left;
+ struct wpabuf buf;
+ const struct eap_method *m = data->phase2_method;
+ void *priv = data->phase2_priv;
+
+ if (!priv) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: %s - Phase 2 not initialized?!",
+ __func__);
+ return;
+ }
+
+ hdr = (struct eap_hdr *) in_data;
+ pos = (u8 *) (hdr + 1);
+
+ if (in_len > sizeof(*hdr) && *pos == EAP_TYPE_NAK) {
+ left = in_len - sizeof(*hdr);
+ wpa_hexdump(MSG_DEBUG,
+ "EAP-TEAP: Phase 2 type Nak'ed; allowed types",
+ pos + 1, left - 1);
+#ifdef EAP_SERVER_TNC
+ if (m && m->vendor == EAP_VENDOR_IETF &&
+ m->method == EAP_TYPE_TNC) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Peer Nak'ed required TNC negotiation");
+ next_type = eap_teap_req_failure(data, 0);
+ eap_teap_phase2_init(sm, data, next_type);
+ return;
+ }
+#endif /* EAP_SERVER_TNC */
+ eap_sm_process_nak(sm, pos + 1, left - 1);
+ if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
+ sm->user->methods[sm->user_eap_method_index].method !=
+ EAP_TYPE_NONE) {
+ next_type = sm->user->methods[
+ sm->user_eap_method_index++].method;
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: try EAP type %d",
+ next_type);
+ } else {
+ next_type = eap_teap_req_failure(data, 0);
+ }
+ eap_teap_phase2_init(sm, data, next_type);
+ return;
+ }
+
+ wpabuf_set(&buf, in_data, in_len);
+
+ if (m->check(sm, priv, &buf)) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Phase 2 check() asked to ignore the packet");
+ eap_teap_req_failure(data, TEAP_ERROR_INNER_METHOD);
+ return;
+ }
+
+ m->process(sm, priv, &buf);
+
+ if (!m->isDone(sm, priv))
+ return;
+
+ if (!m->isSuccess(sm, priv)) {
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Phase 2 method failed");
+ next_type = eap_teap_req_failure(data, TEAP_ERROR_INNER_METHOD);
+ eap_teap_phase2_init(sm, data, next_type);
+ return;
+ }
+
+ switch (data->state) {
+ case PHASE2_ID:
+ if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "EAP-TEAP: Phase 2 Identity not found in the user database",
+ sm->identity, sm->identity_len);
+ next_type = eap_teap_req_failure(
+ data, TEAP_ERROR_INNER_METHOD);
+ break;
+ }
+
+ eap_teap_state(data, PHASE2_METHOD);
+ if (data->anon_provisioning) {
+ /* TODO: Allow any inner EAP method that provides
+ * mutual authentication and EMSK derivation (i.e.,
+ * EAP-pwd or EAP-EKE). */
+ next_type = EAP_TYPE_PWD;
+ sm->user_eap_method_index = 0;
+ } else {
+ next_type = sm->user->methods[0].method;
+ sm->user_eap_method_index = 1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Try EAP type %d", next_type);
+ break;
+ case PHASE2_METHOD:
+ case CRYPTO_BINDING:
+ eap_teap_update_icmk(sm, data);
+ eap_teap_state(data, CRYPTO_BINDING);
+ data->eap_seq++;
+ next_type = EAP_TYPE_NONE;
+#ifdef EAP_SERVER_TNC
+ if (sm->tnc && !data->tnc_started) {
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Initialize TNC");
+ next_type = EAP_TYPE_TNC;
+ data->tnc_started = 1;
+ }
+#endif /* EAP_SERVER_TNC */
+ break;
+ case FAILURE:
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: %s - unexpected state %d",
+ __func__, data->state);
+ break;
+ }
+
+ eap_teap_phase2_init(sm, data, next_type);
+}
+
+
+static void eap_teap_process_phase2_eap(struct eap_sm *sm,
+ struct eap_teap_data *data,
+ u8 *in_data, size_t in_len)
+{
+ struct eap_hdr *hdr;
+ size_t len;
+
+ hdr = (struct eap_hdr *) in_data;
+ if (in_len < (int) sizeof(*hdr)) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Too short Phase 2 EAP frame (len=%lu)",
+ (unsigned long) in_len);
+ eap_teap_req_failure(data, TEAP_ERROR_INNER_METHOD);
+ return;
+ }
+ len = be_to_host16(hdr->length);
+ if (len > in_len) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Length mismatch in Phase 2 EAP frame (len=%lu hdr->length=%lu)",
+ (unsigned long) in_len, (unsigned long) len);
+ eap_teap_req_failure(data, TEAP_ERROR_INNER_METHOD);
+ return;
+ }
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Received Phase 2: code=%d identifier=%d length=%lu",
+ hdr->code, hdr->identifier,
+ (unsigned long) len);
+ switch (hdr->code) {
+ case EAP_CODE_RESPONSE:
+ eap_teap_process_phase2_response(sm, data, (u8 *) hdr, len);
+ break;
+ default:
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Unexpected code=%d in Phase 2 EAP header",
+ hdr->code);
+ break;
+ }
+}
+
+
+static void eap_teap_process_basic_auth_resp(struct eap_sm *sm,
+ struct eap_teap_data *data,
+ u8 *in_data, size_t in_len)
+{
+ u8 *pos, *end, *username, *password, *new_id;
+ u8 userlen, passlen;
+
+ pos = in_data;
+ end = pos + in_len;
+
+ if (end - pos < 1) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: No room for Basic-Password-Auth-Resp Userlen field");
+ eap_teap_req_failure(data, 0);
+ return;
+ }
+ userlen = *pos++;
+ if (end - pos < userlen) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Truncated Basic-Password-Auth-Resp Username field");
+ eap_teap_req_failure(data, 0);
+ return;
+ }
+ username = pos;
+ pos += userlen;
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "EAP-TEAP: Basic-Password-Auth-Resp Username",
+ username, userlen);
+
+ if (end - pos < 1) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: No room for Basic-Password-Auth-Resp Passlen field");
+ eap_teap_req_failure(data, 0);
+ return;
+ }
+ passlen = *pos++;
+ if (end - pos < passlen) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Truncated Basic-Password-Auth-Resp Password field");
+ eap_teap_req_failure(data, 0);
+ return;
+ }
+ password = pos;
+ pos += passlen;
+ wpa_hexdump_ascii_key(MSG_DEBUG,
+ "EAP-TEAP: Basic-Password-Auth-Resp Password",
+ password, passlen);
+
+ if (end > pos) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Unexpected %d extra octet(s) at the end of Basic-Password-Auth-Resp TLV",
+ (int) (end - pos));
+ eap_teap_req_failure(data, 0);
+ return;
+ }
+
+ if (eap_user_get(sm, username, userlen, 1) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Username not found in the user database");
+ eap_teap_req_failure(data, 0);
+ return;
+ }
+
+ if (!sm->user || !sm->user->password || sm->user->password_hash) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: No plaintext user password configured");
+ eap_teap_req_failure(data, 0);
+ return;
+ }
+
+ if (sm->user->password_len != passlen ||
+ os_memcmp_const(sm->user->password, password, passlen) != 0) {
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Invalid password");
+ eap_teap_req_failure(data, 0);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Correct password");
+ new_id = os_memdup(username, userlen);
+ if (new_id) {
+ os_free(sm->identity);
+ sm->identity = new_id;
+ sm->identity_len = userlen;
+ }
+ eap_teap_state(data, CRYPTO_BINDING);
+ eap_teap_update_icmk(sm, data);
+}
+
+
+static int eap_teap_parse_tlvs(struct wpabuf *data,
+ struct eap_teap_tlv_parse *tlv)
+{
+ u16 tlv_type;
+ int mandatory, res;
+ size_t len;
+ u8 *pos, *end;
+
+ os_memset(tlv, 0, sizeof(*tlv));
+
+ pos = wpabuf_mhead(data);
+ end = pos + wpabuf_len(data);
+ while (end - pos > 4) {
+ mandatory = pos[0] & 0x80;
+ tlv_type = WPA_GET_BE16(pos) & 0x3fff;
+ pos += 2;
+ len = WPA_GET_BE16(pos);
+ pos += 2;
+ if (len > (size_t) (end - pos)) {
+ wpa_printf(MSG_INFO, "EAP-TEAP: TLV overflow");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Received Phase 2: TLV type %u (%s) length %u%s",
+ tlv_type, eap_teap_tlv_type_str(tlv_type),
+ (unsigned int) len,
+ mandatory ? " (mandatory)" : "");
+
+ res = eap_teap_parse_tlv(tlv, tlv_type, pos, len);
+ if (res == -2)
+ break;
+ if (res < 0) {
+ if (mandatory) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: NAK unknown mandatory TLV type %u",
+ tlv_type);
+ /* TODO: generate NAK TLV */
+ break;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Ignore unknown optional TLV type %u",
+ tlv_type);
+ }
+
+ pos += len;
+ }
+
+ return 0;
+}
+
+
+static int eap_teap_validate_crypto_binding(
+ struct eap_teap_data *data, const struct teap_tlv_crypto_binding *cb,
+ size_t bind_len)
+{
+ u8 flags, subtype;
+
+ subtype = cb->subtype & 0x0f;
+ flags = cb->subtype >> 4;
+
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Reply Crypto-Binding TLV: Version %u Received Version %u Flags %u Sub-Type %u",
+ cb->version, cb->received_version, flags, subtype);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Nonce",
+ cb->nonce, sizeof(cb->nonce));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: EMSK Compound MAC",
+ cb->emsk_compound_mac, sizeof(cb->emsk_compound_mac));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: MSK Compound MAC",
+ cb->msk_compound_mac, sizeof(cb->msk_compound_mac));
+
+ if (cb->version != EAP_TEAP_VERSION ||
+ cb->received_version != data->peer_version) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Unexpected version in Crypto-Binding: Version %u Received Version %u",
+ cb->version, cb->received_version);
+ return -1;
+ }
+
+ if (flags < 1 || flags > 3) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Unexpected Flags in Crypto-Binding: %u",
+ flags);
+ return -1;
+ }
+
+ if (subtype != TEAP_CRYPTO_BINDING_SUBTYPE_RESPONSE) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Unexpected Sub-Type in Crypto-Binding: %u",
+ subtype);
+ return -1;
+ }
+
+ if (os_memcmp_const(data->crypto_binding_nonce, cb->nonce,
+ EAP_TEAP_NONCE_LEN - 1) != 0 ||
+ (data->crypto_binding_nonce[EAP_TEAP_NONCE_LEN - 1] | 1) !=
+ cb->nonce[EAP_TEAP_NONCE_LEN - 1]) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Invalid Nonce in Crypto-Binding");
+ return -1;
+ }
+
+ if (flags == TEAP_CRYPTO_BINDING_MSK_CMAC ||
+ flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC) {
+ u8 msk_compound_mac[EAP_TEAP_COMPOUND_MAC_LEN];
+
+ if (eap_teap_compound_mac(data->tls_cs, cb,
+ data->server_outer_tlvs,
+ data->peer_outer_tlvs, data->cmk_msk,
+ msk_compound_mac) < 0)
+ return -1;
+ if (os_memcmp_const(msk_compound_mac, cb->msk_compound_mac,
+ EAP_TEAP_COMPOUND_MAC_LEN) != 0) {
+ wpa_hexdump(MSG_DEBUG,
+ "EAP-TEAP: Calculated MSK Compound MAC",
+ msk_compound_mac,
+ EAP_TEAP_COMPOUND_MAC_LEN);
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: MSK Compound MAC did not match");
+ return -1;
+ }
+ }
+
+ if ((flags == TEAP_CRYPTO_BINDING_EMSK_CMAC ||
+ flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC) &&
+ data->cmk_emsk_available) {
+ u8 emsk_compound_mac[EAP_TEAP_COMPOUND_MAC_LEN];
+
+ if (eap_teap_compound_mac(data->tls_cs, cb,
+ data->server_outer_tlvs,
+ data->peer_outer_tlvs, data->cmk_emsk,
+ emsk_compound_mac) < 0)
+ return -1;
+ if (os_memcmp_const(emsk_compound_mac, cb->emsk_compound_mac,
+ EAP_TEAP_COMPOUND_MAC_LEN) != 0) {
+ wpa_hexdump(MSG_DEBUG,
+ "EAP-TEAP: Calculated EMSK Compound MAC",
+ emsk_compound_mac,
+ EAP_TEAP_COMPOUND_MAC_LEN);
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: EMSK Compound MAC did not match");
+ return -1;
+ }
+ }
+
+ if (flags == TEAP_CRYPTO_BINDING_EMSK_CMAC &&
+ !data->cmk_emsk_available) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Peer included only EMSK Compound MAC, but no locally generated inner EAP EMSK to validate this");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int eap_teap_pac_type(u8 *pac, size_t len, u16 type)
+{
+ struct teap_attr_pac_type *tlv;
+
+ if (!pac || len != sizeof(*tlv))
+ return 0;
+
+ tlv = (struct teap_attr_pac_type *) pac;
+
+ return be_to_host16(tlv->type) == PAC_TYPE_PAC_TYPE &&
+ be_to_host16(tlv->length) == 2 &&
+ be_to_host16(tlv->pac_type) == type;
+}
+
+
+static void eap_teap_process_phase2_tlvs(struct eap_sm *sm,
+ struct eap_teap_data *data,
+ struct wpabuf *in_data)
+{
+ struct eap_teap_tlv_parse tlv;
+ int check_crypto_binding = data->state == CRYPTO_BINDING;
+
+ if (eap_teap_parse_tlvs(in_data, &tlv) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Failed to parse received Phase 2 TLVs");
+ return;
+ }
+
+ if (tlv.result == TEAP_STATUS_FAILURE) {
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Result TLV indicated failure");
+ eap_teap_state(data, FAILURE);
+ return;
+ }
+
+ if (tlv.nak) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Peer NAK'ed Vendor-Id %u NAK-Type %u",
+ WPA_GET_BE32(tlv.nak), WPA_GET_BE16(tlv.nak + 4));
+ eap_teap_state(data, FAILURE_SEND_RESULT);
+ return;
+ }
+
+ if (data->state == REQUEST_PAC) {
+ u16 type, len, res;
+
+ if (!tlv.pac || tlv.pac_len < 6) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: No PAC Acknowledgement received");
+ eap_teap_state(data, FAILURE);
+ return;
+ }
+
+ type = WPA_GET_BE16(tlv.pac);
+ len = WPA_GET_BE16(tlv.pac + 2);
+ res = WPA_GET_BE16(tlv.pac + 4);
+
+ if (type != PAC_TYPE_PAC_ACKNOWLEDGEMENT || len != 2 ||
+ res != TEAP_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: PAC TLV did not contain acknowledgement");
+ eap_teap_state(data, FAILURE);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: PAC-Acknowledgement received - PAC provisioning succeeded");
+ eap_teap_state(data, SUCCESS);
+ return;
+ }
+
+ if (check_crypto_binding) {
+ if (!tlv.crypto_binding) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: No Crypto-Binding TLV received");
+ eap_teap_state(data, FAILURE);
+ return;
+ }
+
+ if (data->final_result &&
+ tlv.result != TEAP_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Crypto-Binding TLV without Success Result");
+ eap_teap_state(data, FAILURE);
+ return;
+ }
+
+ if (!data->final_result &&
+ tlv.iresult != TEAP_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Crypto-Binding TLV without intermediate Success Result");
+ eap_teap_state(data, FAILURE);
+ return;
+ }
+
+ if (eap_teap_validate_crypto_binding(data, tlv.crypto_binding,
+ tlv.crypto_binding_len)) {
+ eap_teap_state(data, FAILURE);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Valid Crypto-Binding TLV received");
+ if (data->final_result) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Authentication completed successfully");
+ }
+
+ if (data->anon_provisioning &&
+ sm->eap_fast_prov != ANON_PROV &&
+ sm->eap_fast_prov != BOTH_PROV) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Client is trying to use unauthenticated provisioning which is disabled");
+ eap_teap_state(data, FAILURE);
+ return;
+ }
+
+ if (sm->eap_fast_prov != AUTH_PROV &&
+ sm->eap_fast_prov != BOTH_PROV &&
+ tlv.request_action == TEAP_REQUEST_ACTION_PROCESS_TLV &&
+ eap_teap_pac_type(tlv.pac, tlv.pac_len,
+ PAC_TYPE_TUNNEL_PAC)) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Client is trying to use authenticated provisioning which is disabled");
+ eap_teap_state(data, FAILURE);
+ return;
+ }
+
+ if (data->anon_provisioning ||
+ (tlv.request_action == TEAP_REQUEST_ACTION_PROCESS_TLV &&
+ eap_teap_pac_type(tlv.pac, tlv.pac_len,
+ PAC_TYPE_TUNNEL_PAC))) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Requested a new Tunnel PAC");
+ eap_teap_state(data, REQUEST_PAC);
+ } else if (data->send_new_pac) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Server triggered re-keying of Tunnel PAC");
+ eap_teap_state(data, REQUEST_PAC);
+ } else if (data->final_result)
+ eap_teap_state(data, SUCCESS);
+ }
+
+ if (tlv.basic_auth_resp) {
+ if (sm->eap_teap_auth != 1) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Unexpected Basic-Password-Auth-Resp when trying to use inner EAP");
+ eap_teap_state(data, FAILURE);
+ return;
+ }
+ eap_teap_process_basic_auth_resp(sm, data, tlv.basic_auth_resp,
+ tlv.basic_auth_resp_len);
+ }
+
+ if (tlv.eap_payload_tlv) {
+ if (sm->eap_teap_auth == 1) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Unexpected EAP Payload TLV when trying to use Basic-Password-Auth");
+ eap_teap_state(data, FAILURE);
+ return;
+ }
+ eap_teap_process_phase2_eap(sm, data, tlv.eap_payload_tlv,
+ tlv.eap_payload_tlv_len);
+ }
+}
+
+
+static void eap_teap_process_phase2(struct eap_sm *sm,
+ struct eap_teap_data *data,
+ struct wpabuf *in_buf)
+{
+ struct wpabuf *in_decrypted;
+
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Received %lu bytes encrypted data for Phase 2",
+ (unsigned long) wpabuf_len(in_buf));
+
+ if (data->pending_phase2_resp) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Pending Phase 2 response - skip decryption and use old data");
+ eap_teap_process_phase2_tlvs(sm, data,
+ data->pending_phase2_resp);
+ wpabuf_free(data->pending_phase2_resp);
+ data->pending_phase2_resp = NULL;
+ return;
+ }
+
+ in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn,
+ in_buf);
+ if (!in_decrypted) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Failed to decrypt Phase 2 data");
+ eap_teap_state(data, FAILURE);
+ return;
+ }
+
+ wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TEAP: Decrypted Phase 2 TLVs",
+ in_decrypted);
+
+ eap_teap_process_phase2_tlvs(sm, data, in_decrypted);
+
+ if (sm->method_pending == METHOD_PENDING_WAIT) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Phase 2 method is in pending wait state - save decrypted response");
+ wpabuf_free(data->pending_phase2_resp);
+ data->pending_phase2_resp = in_decrypted;
+ return;
+ }
+
+ wpabuf_free(in_decrypted);
+}
+
+
+static int eap_teap_process_version(struct eap_sm *sm, void *priv,
+ int peer_version)
+{
+ struct eap_teap_data *data = priv;
+
+ if (peer_version < 1) {
+ /* Version 1 was the first defined version, so reject 0 */
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Peer used unknown TEAP version %u",
+ peer_version);
+ return -1;
+ }
+
+ if (peer_version < data->teap_version) {
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: peer ver=%u, own ver=%u; "
+ "use version %u",
+ peer_version, data->teap_version, peer_version);
+ data->teap_version = peer_version;
+ }
+
+ data->peer_version = peer_version;
+
+ return 0;
+}
+
+
+static int eap_teap_process_phase1(struct eap_sm *sm,
+ struct eap_teap_data *data)
+{
+ if (eap_server_tls_phase1(sm, &data->ssl) < 0) {
+ wpa_printf(MSG_INFO, "EAP-TEAP: TLS processing failed");
+ eap_teap_state(data, FAILURE);
+ return -1;
+ }
+
+ if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) ||
+ wpabuf_len(data->ssl.tls_out) > 0)
+ return 1;
+
+ /*
+ * Phase 1 was completed with the received message (e.g., when using
+ * abbreviated handshake), so Phase 2 can be started immediately
+ * without having to send through an empty message to the peer.
+ */
+
+ return eap_teap_phase1_done(sm, data);
+}
+
+
+static int eap_teap_process_phase2_start(struct eap_sm *sm,
+ struct eap_teap_data *data)
+{
+ u8 next_type;
+
+ if (data->identity) {
+ /* Used PAC and identity is from PAC-Opaque */
+ os_free(sm->identity);
+ sm->identity = data->identity;
+ data->identity = NULL;
+ sm->identity_len = data->identity_len;
+ data->identity_len = 0;
+ if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "EAP-TEAP: Phase 2 Identity not found in the user database",
+ sm->identity, sm->identity_len);
+ next_type = EAP_TYPE_NONE;
+ eap_teap_state(data, PHASE2_METHOD);
+ } else if (sm->eap_teap_pac_no_inner) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Used PAC and identity already known - skip inner auth");
+ /* FIX: Need to derive CMK here. However, how is that
+ * supposed to be done? RFC 7170 does not tell that for
+ * the no-inner-auth case. */
+ eap_teap_derive_cmk_basic_pw_auth(data->simck_msk,
+ data->cmk_msk);
+ eap_teap_state(data, CRYPTO_BINDING);
+ return 1;
+ } else if (sm->eap_teap_auth == 1) {
+ eap_teap_state(data, PHASE2_BASIC_AUTH);
+ return 1;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Identity already known - skip Phase 2 Identity Request");
+ next_type = sm->user->methods[0].method;
+ sm->user_eap_method_index = 1;
+ eap_teap_state(data, PHASE2_METHOD);
+ }
+
+ } else if (sm->eap_teap_auth == 1) {
+ eap_teap_state(data, PHASE2_BASIC_AUTH);
+ return 0;
+ } else {
+ eap_teap_state(data, PHASE2_ID);
+ next_type = EAP_TYPE_IDENTITY;
+ }
+
+ return eap_teap_phase2_init(sm, data, next_type);
+}
+
+
+static void eap_teap_process_msg(struct eap_sm *sm, void *priv,
+ const struct wpabuf *respData)
+{
+ struct eap_teap_data *data = priv;
+
+ switch (data->state) {
+ case PHASE1:
+ case PHASE1B:
+ if (eap_teap_process_phase1(sm, data))
+ break;
+
+ /* fall through */
+ case PHASE2_START:
+ eap_teap_process_phase2_start(sm, data);
+ break;
+ case PHASE2_ID:
+ case PHASE2_BASIC_AUTH:
+ case PHASE2_METHOD:
+ case CRYPTO_BINDING:
+ case REQUEST_PAC:
+ eap_teap_process_phase2(sm, data, data->ssl.tls_in);
+ break;
+ case FAILURE_SEND_RESULT:
+ /* Protected failure result indication completed. Ignore the
+ * received message (which is supposed to include Result TLV
+ * indicating failure) and terminate exchange with cleartext
+ * EAP-Failure. */
+ eap_teap_state(data, FAILURE);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-TEAP: Unexpected state %d in %s",
+ data->state, __func__);
+ break;
+ }
+}
+
+
+static void eap_teap_process(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ struct eap_teap_data *data = priv;
+ const u8 *pos;
+ size_t len;
+ struct wpabuf *resp = respData;
+ u8 flags;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TEAP, respData, &len);
+ if (!pos || len < 1)
+ return;
+
+ flags = *pos++;
+ len--;
+
+ if (flags & EAP_TEAP_FLAGS_OUTER_TLV_LEN) {
+ /* Extract Outer TLVs from the message before common TLS
+ * processing */
+ u32 message_len = 0, outer_tlv_len;
+ const u8 *hdr;
+
+ if (data->state != PHASE1) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Unexpected Outer TLVs in a message that is not the first message from the peer");
+ return;
+ }
+
+ if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) {
+ if (len < 4) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Too short message to include Message Length field");
+ return;
+ }
+
+ message_len = WPA_GET_BE32(pos);
+ pos += 4;
+ len -= 4;
+ if (message_len < 4) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Message Length field has too msall value to include Outer TLV Length field");
+ return;
+ }
+ }
+
+ if (len < 4) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Too short message to include Outer TLVs Length field");
+ return;
+ }
+
+ outer_tlv_len = WPA_GET_BE32(pos);
+ pos += 4;
+ len -= 4;
+
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Message Length %u Outer TLV Length %u",
+ message_len, outer_tlv_len);
+ if (len < outer_tlv_len) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Too short message to include Outer TLVs field");
+ return;
+ }
+
+ if (message_len &&
+ (message_len < outer_tlv_len ||
+ message_len < 4 + outer_tlv_len)) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Message Length field has too small value to include Outer TLVs");
+ return;
+ }
+
+ if (wpabuf_len(respData) < 4 + outer_tlv_len ||
+ len < outer_tlv_len)
+ return;
+ resp = wpabuf_alloc(wpabuf_len(respData) - 4 - outer_tlv_len);
+ if (!resp)
+ return;
+ hdr = wpabuf_head(respData);
+ wpabuf_put_u8(resp, *hdr++); /* Code */
+ wpabuf_put_u8(resp, *hdr++); /* Identifier */
+ wpabuf_put_be16(resp, WPA_GET_BE16(hdr) - 4 - outer_tlv_len);
+ hdr += 2;
+ wpabuf_put_u8(resp, *hdr++); /* Type */
+ /* Flags | Ver */
+ wpabuf_put_u8(resp, flags & ~EAP_TEAP_FLAGS_OUTER_TLV_LEN);
+
+ if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED)
+ wpabuf_put_be32(resp, message_len - 4 - outer_tlv_len);
+
+ wpabuf_put_data(resp, pos, len - outer_tlv_len);
+ pos += len - outer_tlv_len;
+ wpabuf_free(data->peer_outer_tlvs);
+ data->peer_outer_tlvs = wpabuf_alloc_copy(pos, outer_tlv_len);
+ if (!data->peer_outer_tlvs)
+ return;
+ wpa_hexdump_buf(MSG_DEBUG, "EAP-TEAP: Outer TLVs",
+ data->peer_outer_tlvs);
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "EAP-TEAP: TLS Data message after Outer TLV removal",
+ resp);
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TEAP, resp,
+ &len);
+ if (!pos || len < 1) {
+ wpa_printf(MSG_INFO,
+ "EAP-TEAP: Invalid frame after Outer TLV removal");
+ return;
+ }
+ }
+
+ if (data->state == PHASE1)
+ eap_teap_state(data, PHASE1B);
+
+ if (eap_server_tls_process(sm, &data->ssl, resp, data,
+ EAP_TYPE_TEAP, eap_teap_process_version,
+ eap_teap_process_msg) < 0)
+ eap_teap_state(data, FAILURE);
+
+ if (resp != respData)
+ wpabuf_free(resp);
+}
+
+
+static Boolean eap_teap_isDone(struct eap_sm *sm, void *priv)
+{
+ struct eap_teap_data *data = priv;
+
+ return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_teap_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_teap_data *data = priv;
+ u8 *eapKeyData;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ eapKeyData = os_malloc(EAP_TEAP_KEY_LEN);
+ if (!eapKeyData)
+ return NULL;
+
+ /* FIX: RFC 7170 does not describe whether MSK or EMSK based S-IMCK[j]
+ * is used in this derivation */
+ if (eap_teap_derive_eap_msk(data->simck_msk, eapKeyData) < 0) {
+ os_free(eapKeyData);
+ return NULL;
+ }
+ *len = EAP_TEAP_KEY_LEN;
+
+ return eapKeyData;
+}
+
+
+static u8 * eap_teap_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_teap_data *data = priv;
+ u8 *eapKeyData;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ eapKeyData = os_malloc(EAP_EMSK_LEN);
+ if (!eapKeyData)
+ return NULL;
+
+ /* FIX: RFC 7170 does not describe whether MSK or EMSK based S-IMCK[j]
+ * is used in this derivation */
+ if (eap_teap_derive_eap_emsk(data->simck_msk, eapKeyData) < 0) {
+ os_free(eapKeyData);
+ return NULL;
+ }
+ *len = EAP_EMSK_LEN;
+
+ return eapKeyData;
+}
+
+
+static Boolean eap_teap_isSuccess(struct eap_sm *sm, void *priv)
+{
+ struct eap_teap_data *data = priv;
+
+ return data->state == SUCCESS;
+}
+
+
+static u8 * eap_teap_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_teap_data *data = priv;
+ const size_t max_id_len = 100;
+ int res;
+ u8 *id;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ id = os_malloc(max_id_len);
+ if (!id)
+ return NULL;
+
+ id[0] = EAP_TYPE_TEAP;
+ res = tls_get_tls_unique(data->ssl.conn, id + 1, max_id_len - 1);
+ if (res < 0) {
+ os_free(id);
+ wpa_printf(MSG_ERROR, "EAP-TEAP: Failed to derive Session-Id");
+ return NULL;
+ }
+
+ *len = 1 + res;
+ wpa_hexdump(MSG_DEBUG, "EAP-TEAP: Derived Session-Id", id, *len);
+ return id;
+}
+
+
+int eap_server_teap_register(void)
+{
+ struct eap_method *eap;
+
+ eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_TEAP, "TEAP");
+ if (!eap)
+ return -1;
+
+ eap->init = eap_teap_init;
+ eap->reset = eap_teap_reset;
+ eap->buildReq = eap_teap_buildReq;
+ eap->check = eap_teap_check;
+ eap->process = eap_teap_process;
+ eap->isDone = eap_teap_isDone;
+ eap->getKey = eap_teap_getKey;
+ eap->get_emsk = eap_teap_get_emsk;
+ eap->isSuccess = eap_teap_isSuccess;
+ eap->getSessionId = eap_teap_get_session_id;
+
+ return eap_server_method_register(eap);
+}
unsigned int tls_msg_len = 0;
const u8 *end = *pos + *left;
+ wpa_hexdump(MSG_MSGDUMP, "SSL: Received data", *pos, *left);
+
if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) {
if (*left < 4) {
wpa_printf(MSG_INFO, "SSL: Short frame with TLS "
#define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80
#define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40
#define EAP_TLS_FLAGS_START 0x20
+#define EAP_TEAP_FLAGS_OUTER_TLV_LEN 0x10
#define EAP_TLS_VERSION_MASK 0x07
/* could be up to 128 bytes, but only the first 64 bytes are used */
if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authSuccess)
sm->authAuthSuccessesWhileAuthenticating++;
-
+
SM_ENTRY_MA(AUTH_PAE, AUTHENTICATED, auth_pae);
sm->authPortStatus = Authorized;
eap_conf.eap_fast_prov = eapol->conf.eap_fast_prov;
eap_conf.pac_key_lifetime = eapol->conf.pac_key_lifetime;
eap_conf.pac_key_refresh_time = eapol->conf.pac_key_refresh_time;
+ eap_conf.eap_teap_auth = eapol->conf.eap_teap_auth;
+ eap_conf.eap_teap_pac_no_inner = eapol->conf.eap_teap_pac_no_inner;
eap_conf.eap_sim_aka_result_ind = eapol->conf.eap_sim_aka_result_ind;
eap_conf.tnc = eapol->conf.tnc;
eap_conf.wps = eapol->conf.wps;
dst->eap_fast_prov = src->eap_fast_prov;
dst->pac_key_lifetime = src->pac_key_lifetime;
dst->pac_key_refresh_time = src->pac_key_refresh_time;
+ dst->eap_teap_auth = src->eap_teap_auth;
+ dst->eap_teap_pac_no_inner = src->eap_teap_pac_no_inner;
dst->eap_sim_aka_result_ind = src->eap_sim_aka_result_ind;
dst->tnc = src->tnc;
dst->wps = src->wps;
int eap_fast_prov;
int pac_key_lifetime;
int pac_key_refresh_time;
+ int eap_teap_auth;
+ int eap_teap_pac_no_inner;
int eap_sim_aka_result_ind;
int tnc;
struct wps_context *wps;
*/
int pac_key_refresh_time;
+ int eap_teap_auth;
+ int eap_teap_pac_no_inner;
+
/**
* eap_sim_aka_result_ind - EAP-SIM/AKA protected success indication
*
eap_conf.eap_fast_prov = data->eap_fast_prov;
eap_conf.pac_key_lifetime = data->pac_key_lifetime;
eap_conf.pac_key_refresh_time = data->pac_key_refresh_time;
+ eap_conf.eap_teap_auth = data->eap_teap_auth;
+ eap_conf.eap_teap_pac_no_inner = data->eap_teap_pac_no_inner;
eap_conf.eap_sim_aka_result_ind = data->eap_sim_aka_result_ind;
eap_conf.tnc = data->tnc;
eap_conf.wps = data->wps;
data->eap_fast_prov = conf->eap_fast_prov;
data->pac_key_lifetime = conf->pac_key_lifetime;
data->pac_key_refresh_time = conf->pac_key_refresh_time;
+ data->eap_teap_auth = conf->eap_teap_auth;
+ data->eap_teap_pac_no_inner = conf->eap_teap_pac_no_inner;
data->get_eap_user = conf->get_eap_user;
data->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
data->tnc = conf->tnc;
*/
int pac_key_refresh_time;
+ int eap_teap_auth;
+ int eap_teap_pac_no_inner;
+
/**
* eap_sim_aka_result_ind - EAP-SIM/AKA protected success indication
*
NEED_T_PRF=y
endif
+ifdef CONFIG_EAP_TEAP
+# EAP-TEAP
+ifeq ($(CONFIG_EAP_TEAP), dyn)
+L_CFLAGS += -DEAP_YEAP_DYNAMIC
+EAPDYN += src/eap_peer/eap_teap.so
+EAPDYN += src/eap_common/eap_teap_common.c
+else
+L_CFLAGS += -DEAP_TEAP
+OBJS += src/eap_peer/eap_teap.c src/eap_peer/eap_teap_pac.c
+OBJS += src/eap_common/eap_teap_common.c
+endif
+TLS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+NEED_T_PRF=y
+NEED_SHA384=y
+endif
+
ifdef CONFIG_EAP_PAX
# EAP-PAX
ifeq ($(CONFIG_EAP_PAX), dyn)
NEED_T_PRF=y
endif
+ifdef CONFIG_EAP_TEAP
+# EAP-TEAP
+ifeq ($(CONFIG_EAP_TEAP), dyn)
+CFLAGS += -DEAP_TEAP_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_teap.so
+EAPDYN += ../src/eap_common/eap_teap_common.o
+else
+CFLAGS += -DEAP_TEAP
+OBJS += ../src/eap_peer/eap_teap.o ../src/eap_peer/eap_teap_pac.o
+OBJS += ../src/eap_common/eap_teap_common.o
+endif
+TLS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+NEED_T_PRF=y
+NEED_SHA384=y
+endif
+
ifdef CONFIG_EAP_PAX
# EAP-PAX
ifeq ($(CONFIG_EAP_PAX), dyn)
ifdef TLS_FUNCS
NEED_DES=y
-# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, and EAP_FAST)
+# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, EAP_FAST, and
+# EAP_TEAP)
OBJS += ../src/eap_peer/eap_tls_common.o
ifndef CONFIG_FIPS
NEED_TLS_PRF=y
# EAP-FAST
CONFIG_EAP_FAST=y
+# EAP-TEAP
+# Note: The current EAP-TEAP implementation is experimental and should not be
+# enabled for production use. The IETF RFC 7170 that defines EAP-TEAP has number
+# of conflicting statements and missing details and the implementation has
+# vendor specific workarounds for those and as such, may not interoperate with
+# any other implementation. This should not be used for anything else than
+# experimentation and interoperability testing until those issues has been
+# resolved.
+#CONFIG_EAP_TEAP=y
+
# EAP-GTC
CONFIG_EAP_GTC=y
ret = eap_peer_fast_register();
#endif /* EAP_FAST */
+#ifdef EAP_TEAP
+ if (ret == 0)
+ ret = eap_peer_teap_register();
+#endif /* EAP_TEAP */
+
#ifdef EAP_PAX
if (ret == 0)
ret = eap_peer_pax_register();
ret = eap_server_fast_register();
#endif /* EAP_SERVER_FAST */
+#ifdef EAP_SERVER_TEAP
+ if (ret == 0)
+ ret = eap_server_teap_register();
+#endif /* EAP_SERVER_TEAP */
+
#ifdef EAP_SERVER_WSC
if (ret == 0)
ret = eap_server_wsc_register();