EAP-TEAP server and peer implementation (RFC 7170)

This adds support for a new EAP method: EAP-TEAP (Tunnel Extensible
Authentication Protocol). This should be considered experimental since
RFC 7170 has number of conflicting statements and missing details to
allow unambiguous interpretation. As such, there may be interoperability
issues with other implementations and this version should not be
deployed for production purposes until those unclear areas are resolved.

This does not yet support use of NewSessionTicket message to deliver a
new PAC (either in the server or peer implementation). In other words,
only the in-tunnel distribution of PAC-Opaque is supported for now. Use
of the NewSessionTicket mechanism would require TLS library support to
allow arbitrary data to be specified as the contents of the message.

Signed-off-by: Jouni Malinen <j@w1.fi>
master
Jouni Malinen 5 years ago
parent 7c6f1c5e4a
commit 0ed57c5ea8

@ -487,6 +487,16 @@ NEED_T_PRF=y
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

@ -526,6 +526,16 @@ NEED_T_PRF=y
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

@ -2675,6 +2675,20 @@ static int hostapd_config_fill(struct hostapd_config *conf,
} 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);

@ -110,6 +110,16 @@ CONFIG_EAP_TTLS=y
# 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

@ -121,6 +121,11 @@ int eap_server_register_methods(void)
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();

@ -1174,6 +1174,16 @@ eap_server=0
# (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

@ -423,6 +423,8 @@ struct hostapd_bss_config {
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;

@ -120,6 +120,8 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd)
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;

@ -2371,6 +2371,8 @@ int ieee802_1x_init(struct hostapd_data *hapd)
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;

@ -44,6 +44,13 @@
#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;
@ -4476,7 +4483,7 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
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
@ -4487,7 +4494,7 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
/* 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) {
@ -4541,7 +4548,7 @@ int tls_connection_enable_workaround(void *ssl_ctx,
}
#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. */
@ -4558,7 +4565,7 @@ int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn,
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)
@ -5176,7 +5183,7 @@ int tls_global_set_params(void *tls_ctx,
}
#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. */
@ -5257,7 +5264,7 @@ static int tls_session_ticket_ext_cb(SSL *s, const unsigned char *data,
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,
@ -5265,7 +5272,7 @@ 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;
@ -5282,9 +5289,9 @@ int tls_connection_set_session_ticket_cb(void *tls_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 */
}

@ -92,6 +92,7 @@ typedef enum {
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;

@ -0,0 +1,698 @@
/*
* 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);
}

@ -0,0 +1,218 @@
/*
* 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 */

@ -2603,7 +2603,7 @@ static int eap_allowed_phase2_type(int vendor, int type)
if (vendor != EAP_VENDOR_IETF)
return 0;
return type != EAP_TYPE_PEAP && type != EAP_TYPE_TTLS &&
type != EAP_TYPE_FAST;
type != EAP_TYPE_FAST && type != EAP_TYPE_TEAP;
}

@ -816,6 +816,8 @@ struct eap_peer_config {
EXT_CERT_CHECK_GOOD,
EXT_CERT_CHECK_BAD,
} pending_ext_cert_check;
int teap_anon_dh;
};

@ -97,6 +97,7 @@ int eap_peer_psk_register(void);
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);

File diff suppressed because it is too large Load Diff

@ -0,0 +1,931 @@
/*
* 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;
}

@ -0,0 +1,50 @@
/*
* 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 */

@ -159,7 +159,8 @@ static int eap_tls_params_from_conf(struct eap_sm *sm,
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
@ -171,7 +172,15 @@ static int eap_tls_params_from_conf(struct eap_sm *sm,
*/
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

@ -70,7 +70,8 @@ struct eap_ssl_data {
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;
@ -85,6 +86,7 @@ struct eap_ssl_data {
#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 */

@ -121,6 +121,8 @@ struct eap_config {
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;

@ -190,6 +190,8 @@ struct eap_sm {
} 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;

@ -41,6 +41,7 @@ int eap_server_sake_register(void);
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);

@ -1869,6 +1869,8 @@ struct eap_sm * eap_server_sm_init(void *eapol_ctx,
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;

File diff suppressed because it is too large Load Diff

@ -373,6 +373,8 @@ static int eap_server_tls_reassemble(struct eap_ssl_data *data, u8 flags,
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 "

@ -62,6 +62,7 @@ struct eap_ssl_data {
#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 */

@ -300,7 +300,7 @@ SM_STATE(AUTH_PAE, AUTHENTICATED)
if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authSuccess)
sm->authAuthSuccessesWhileAuthenticating++;
SM_ENTRY_MA(AUTH_PAE, AUTHENTICATED, auth_pae);
sm->authPortStatus = Authorized;
@ -835,6 +835,8 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr,
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;
@ -1231,6 +1233,8 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst,
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;

@ -36,6 +36,8 @@ struct eapol_auth_config {
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;

@ -238,6 +238,9 @@ struct radius_server_data {
*/
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
*
@ -792,6 +795,8 @@ radius_server_get_new_session(struct radius_server_data *data,
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;
@ -2384,6 +2389,8 @@ radius_server_init(struct radius_server_conf *conf)
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;

@ -128,6 +128,9 @@ struct radius_server_conf {
*/
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
*

@ -644,6 +644,23 @@ CONFIG_IEEE8021X_EAPOL=y
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)

@ -671,6 +671,23 @@ CONFIG_IEEE8021X_EAPOL=y
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)
@ -1044,7 +1061,8 @@ endif
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

@ -111,6 +111,16 @@ CONFIG_EAP_TTLS=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

@ -102,6 +102,11 @@ int eap_register_methods(void)
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();
@ -237,6 +242,11 @@ int eap_register_methods(void)
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();

Loading…
Cancel
Save