/*
- * hostapd / EAP-TTLS (draft-ietf-pppext-eap-ttls-05.txt)
+ * hostapd / EAP-TTLS (RFC 5281)
* Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
/* Maximum supported TTLS version
- * 0 = draft-ietf-pppext-eap-ttls-03.txt / draft-funk-eap-ttls-v0-00.txt
+ * 0 = RFC 5281
* 1 = draft-funk-eap-ttls-v1-00.txt
*/
#ifndef EAP_TTLS_VERSION
u8 mschapv2_ident;
int tls_ia_configured;
struct wpabuf *pending_phase2_eap_resp;
+ int tnc_started;
};
}
-static struct wpabuf * eap_ttls_build_req(struct eap_sm *sm,
- struct eap_ttls_data *data, u8 id)
-{
- int res;
- struct wpabuf *req;
-
- res = eap_server_tls_buildReq_helper(sm, &data->ssl, EAP_TYPE_TTLS,
- data->ttls_version, id, &req);
-
- if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
- wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase1 done, starting "
- "Phase2");
- eap_ttls_state(data, PHASE2_START);
- }
-
- if (res == 1)
- return eap_server_tls_build_ack(id, EAP_TYPE_TTLS,
- data->ttls_version);
- return req;
-}
-
-
-static struct wpabuf * eap_ttls_encrypt(struct eap_sm *sm,
- struct eap_ttls_data *data,
- u8 id, u8 *plain, size_t plain_len)
-{
- int res;
- struct wpabuf *buf;
-
- /* TODO: add support for fragmentation, if needed. This will need to
- * add TLS Message Length field, if the frame is fragmented. */
- buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS,
- 1 + data->ssl.tls_out_limit,
- EAP_CODE_REQUEST, id);
- if (buf == NULL)
- return NULL;
-
- wpabuf_put_u8(buf, data->ttls_version);
-
- res = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn,
- plain, plain_len, wpabuf_put(buf, 0),
- data->ssl.tls_out_limit);
- if (res < 0) {
- wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt Phase 2 "
- "data");
- wpabuf_free(buf);
- return NULL;
- }
-
- wpabuf_put(buf, res);
- eap_update_len(buf);
-
- return buf;
-}
-
-
static struct wpabuf * eap_ttls_build_phase2_eap_req(
struct eap_sm *sm, struct eap_ttls_data *data, u8 id)
{
wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS/EAP: Encrypt encapsulated Phase "
"2 data", req, req_len);
- encr_req = eap_ttls_encrypt(sm, data, id, req, req_len);
+ encr_req = eap_server_tls_encrypt(sm, &data->ssl, req, req_len);
wpabuf_free(buf);
return encr_req;
static struct wpabuf * eap_ttls_build_phase2_mschapv2(
- struct eap_sm *sm, struct eap_ttls_data *data, u8 id)
+ struct eap_sm *sm, struct eap_ttls_data *data)
{
struct wpabuf *encr_req;
u8 *req, *pos, *end;
wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Encrypting Phase 2 "
"data", req, req_len);
- encr_req = eap_ttls_encrypt(sm, data, id, req, req_len);
+ encr_req = eap_server_tls_encrypt(sm, &data->ssl, req, req_len);
os_free(req);
return encr_req;
static struct wpabuf * eap_ttls_build_phase_finished(
- struct eap_sm *sm, struct eap_ttls_data *data, u8 id, int final)
+ struct eap_sm *sm, struct eap_ttls_data *data, int final)
{
int len;
struct wpabuf *req;
const int max_len = 300;
- len = 1 + max_len;
- req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS, len,
- EAP_CODE_REQUEST, id);
+ req = wpabuf_alloc(max_len);
if (req == NULL)
return NULL;
- wpabuf_put_u8(req, data->ttls_version);
-
len = tls_connection_ia_send_phase_finished(sm->ssl_ctx,
data->ssl.conn, final,
wpabuf_mhead(req),
return NULL;
}
wpabuf_put(req, len);
- eap_update_len(req);
return req;
}
{
struct eap_ttls_data *data = priv;
+ if (data->ssl.state == FRAG_ACK) {
+ return eap_server_tls_build_ack(id, EAP_TYPE_TTLS,
+ data->ttls_version);
+ }
+
+ if (data->ssl.state == WAIT_FRAG_ACK) {
+ return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TTLS,
+ data->ttls_version, id);
+ }
+
switch (data->state) {
case START:
return eap_ttls_build_start(sm, data, id);
case PHASE1:
- return eap_ttls_build_req(sm, data, id);
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase1 done, "
+ "starting Phase2");
+ eap_ttls_state(data, PHASE2_START);
+ }
+ break;
case PHASE2_METHOD:
- return eap_ttls_build_phase2_eap_req(sm, data, id);
+ wpabuf_free(data->ssl.out_buf);
+ data->ssl.out_used = 0;
+ data->ssl.out_buf = eap_ttls_build_phase2_eap_req(sm, data,
+ id);
+ break;
case PHASE2_MSCHAPV2_RESP:
- return eap_ttls_build_phase2_mschapv2(sm, data, id);
+ wpabuf_free(data->ssl.out_buf);
+ data->ssl.out_used = 0;
+ data->ssl.out_buf = eap_ttls_build_phase2_mschapv2(sm, data);
+ break;
case PHASE_FINISHED:
- return eap_ttls_build_phase_finished(sm, data, id, 1);
+ wpabuf_free(data->ssl.out_buf);
+ data->ssl.out_used = 0;
+ data->ssl.out_buf = eap_ttls_build_phase_finished(sm, data, 1);
+ break;
default:
wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d",
__func__, data->state);
return NULL;
}
+
+ return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TTLS,
+ data->ttls_version, id);
}
sm->init_phase2 = 1;
data->phase2_priv = data->phase2_method->init(sm);
sm->init_phase2 = 0;
- return 0;
+ return data->phase2_priv == NULL ? -1 : 0;
}
sm->user_eap_method_index++].method;
wpa_printf(MSG_DEBUG, "EAP-TTLS: try EAP type %d",
next_type);
- eap_ttls_phase2_eap_init(sm, data, next_type);
+ if (eap_ttls_phase2_eap_init(sm, data, next_type)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to "
+ "initialize EAP type %d",
+ next_type);
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
} else {
eap_ttls_state(data, FAILURE);
}
next_type = sm->user->methods[0].method;
sm->user_eap_method_index = 1;
wpa_printf(MSG_DEBUG, "EAP-TTLS: try EAP type %d", next_type);
+ if (eap_ttls_phase2_eap_init(sm, data, next_type)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to initialize "
+ "EAP type %d", next_type);
+ eap_ttls_state(data, FAILURE);
+ }
break;
case PHASE2_METHOD:
if (data->ttls_version > 0) {
__func__, data->state);
break;
}
-
- eap_ttls_phase2_eap_init(sm, data, next_type);
}
static void eap_ttls_process_phase2(struct eap_sm *sm,
struct eap_ttls_data *data,
- u8 *in_data, size_t in_len)
+ struct wpabuf *in_buf)
{
u8 *in_decrypted;
- int len_decrypted, res;
+ int len_decrypted;
struct eap_ttls_avp parse;
size_t buf_len;
+ u8 *in_data;
+ size_t in_len;
+
+ in_data = wpabuf_mhead(in_buf);
+ in_len = wpabuf_len(in_buf);
wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for"
" Phase 2", (unsigned long) in_len);
return;
}
- res = eap_server_tls_data_reassemble(sm, &data->ssl, &in_data,
- &in_len);
- if (res < 0 || res == 1)
- return;
-
buf_len = in_len;
- if (data->ssl.tls_in_total > buf_len)
- buf_len = data->ssl.tls_in_total;
+ /*
+ * Even though we try to disable TLS compression, it is possible that
+ * this cannot be done with all TLS libraries. Add extra buffer space
+ * to handle the possibility of the decrypted data being longer than
+ * input data.
+ */
+ buf_len += 500;
+ buf_len *= 3;
in_decrypted = os_malloc(buf_len);
if (in_decrypted == NULL) {
- os_free(data->ssl.tls_in);
- data->ssl.tls_in = NULL;
- data->ssl.tls_in_len = 0;
wpa_printf(MSG_WARNING, "EAP-TTLS: failed to allocate memory "
"for decryption");
return;
len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn,
in_data, in_len,
in_decrypted, buf_len);
- os_free(data->ssl.tls_in);
- data->ssl.tls_in = NULL;
- data->ssl.tls_in_len = 0;
if (len_decrypted < 0) {
wpa_printf(MSG_INFO, "EAP-TTLS: Failed to decrypt Phase 2 "
"data");
}
}
+#ifdef EAP_SERVER_TNC
+ if (data->tnc_started && parse.eap == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: TNC started but no EAP "
+ "response from peer");
+ eap_ttls_state(data, FAILURE);
+ goto done;
+ }
+#endif /* EAP_SERVER_TNC */
+
if (parse.eap) {
eap_ttls_process_phase2_eap(sm, data, parse.eap,
parse.eap_len);
}
-static void eap_ttls_process(struct eap_sm *sm, void *priv,
- struct wpabuf *respData)
+static void eap_ttls_start_tnc(struct eap_sm *sm, struct eap_ttls_data *data)
{
- struct eap_ttls_data *data = priv;
- const u8 *pos;
- u8 flags;
- size_t left;
- unsigned int tls_msg_len;
- int peer_version;
+#ifdef EAP_SERVER_TNC
+ if (!sm->tnc || data->state != SUCCESS || data->tnc_started)
+ return;
- pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TTLS, respData,
- &left);
- if (pos == NULL || left < 1)
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Initialize TNC");
+ if (eap_ttls_phase2_eap_init(sm, data, EAP_TYPE_TNC)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to initialize TNC");
+ eap_ttls_state(data, FAILURE);
return;
- flags = *pos++;
- left--;
- wpa_printf(MSG_DEBUG, "EAP-TTLS: Received packet(len=%lu) - "
- "Flags 0x%02x", (unsigned long) wpabuf_len(respData),
- flags);
- peer_version = flags & EAP_PEAP_VERSION_MASK;
+ }
+
+ data->tnc_started = 1;
+ eap_ttls_state(data, PHASE2_METHOD);
+#endif /* EAP_SERVER_TNC */
+}
+
+
+static int eap_ttls_process_version(struct eap_sm *sm, void *priv,
+ int peer_version)
+{
+ struct eap_ttls_data *data = priv;
if (peer_version < data->ttls_version) {
wpa_printf(MSG_DEBUG, "EAP-TTLS: peer ver=%d, own ver=%d; "
"use version %d",
if (tls_connection_set_ia(sm->ssl_ctx, data->ssl.conn, 1)) {
wpa_printf(MSG_INFO, "EAP-TTLS: Failed to enable "
"TLS/IA");
- eap_ttls_state(data, FAILURE);
- return;
+ return -1;
}
data->tls_ia_configured = 1;
}
- if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) {
- if (left < 4) {
- wpa_printf(MSG_INFO, "EAP-TTLS: Short frame with TLS "
- "length");
- eap_ttls_state(data, FAILURE);
- return;
- }
- tls_msg_len = WPA_GET_BE32(pos);
- wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS Message Length: %d",
- tls_msg_len);
- if (data->ssl.tls_in_left == 0) {
- data->ssl.tls_in_total = tls_msg_len;
- data->ssl.tls_in_left = tls_msg_len;
- os_free(data->ssl.tls_in);
- data->ssl.tls_in = NULL;
- data->ssl.tls_in_len = 0;
- }
- pos += 4;
- left -= 4;
- }
+ return 0;
+}
+
+
+static void eap_ttls_process_msg(struct eap_sm *sm, void *priv,
+ const struct wpabuf *respData)
+{
+ struct eap_ttls_data *data = priv;
switch (data->state) {
case PHASE1:
- if (eap_server_tls_process_helper(sm, &data->ssl, pos, left) <
- 0) {
- wpa_printf(MSG_INFO, "EAP-TTLS: TLS processing "
- "failed");
+ if (eap_server_tls_phase1(sm, &data->ssl) < 0)
eap_ttls_state(data, FAILURE);
- }
break;
case PHASE2_START:
case PHASE2_METHOD:
case PHASE_FINISHED:
- /* FIX: get rid of const->non-const typecast */
- eap_ttls_process_phase2(sm, data, (u8 *) pos, left);
+ eap_ttls_process_phase2(sm, data, data->ssl.in_buf);
+ eap_ttls_start_tnc(sm, data);
break;
case PHASE2_MSCHAPV2_RESP:
- if (data->mschapv2_resp_ok && left == 0) {
+ if (data->mschapv2_resp_ok && wpabuf_len(data->ssl.in_buf) ==
+ 0) {
wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer "
"acknowledged response");
eap_ttls_state(data, data->ttls_version > 0 ?
wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Unexpected "
"frame from peer (payload len %lu, "
"expected empty frame)",
- (unsigned long) left);
+ (unsigned long)
+ wpabuf_len(data->ssl.in_buf));
eap_ttls_state(data, FAILURE);
}
+ eap_ttls_start_tnc(sm, data);
break;
default:
wpa_printf(MSG_DEBUG, "EAP-TTLS: Unexpected state %d in %s",
data->state, __func__);
break;
}
+}
+
- if (tls_connection_get_write_alerts(sm->ssl_ctx, data->ssl.conn) > 1) {
- wpa_printf(MSG_INFO, "EAP-TTLS: Locally detected fatal error "
- "in TLS processing");
+static void eap_ttls_process(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ struct eap_ttls_data *data = priv;
+ if (eap_server_tls_process(sm, &data->ssl, respData, data,
+ EAP_TYPE_TTLS, eap_ttls_process_version,
+ eap_ttls_process_msg) < 0)
eap_ttls_state(data, FAILURE);
- }
}