aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJouni Malinen <j@w1.fi>2008-02-10 18:47:03 (GMT)
committerJouni Malinen <j@w1.fi>2008-02-10 18:47:03 (GMT)
commit9d5f4d2cc11d73ba7074fe4a60e3e796156b744e (patch)
tree502a0deec27bf39c231a25adff961c322e4d2583
parenta797bb3ca2922ff225cac59a69dddf5b8983783b (diff)
downloadhostap-history-9d5f4d2cc11d73ba7074fe4a60e3e796156b744e.zip
hostap-history-9d5f4d2cc11d73ba7074fe4a60e3e796156b744e.tar.gz
hostap-history-9d5f4d2cc11d73ba7074fe4a60e3e796156b744e.tar.bz2
Added some preliminary code for PEAPv2 style TLV encapsulation
The Phase 2 EAP messages are now encapsulated in EAP-Payload TLV if PEAPv2 is used. In addition, the EAP-Request/Identity is sent with the Phase 1 Server Finished message.
-rw-r--r--src/eap_peer/eap_peap.c80
-rw-r--r--src/eap_server/eap_peap.c162
2 files changed, 236 insertions, 6 deletions
diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c
index 5045caa..027027d 100644
--- a/src/eap_peer/eap_peap.c
+++ b/src/eap_peer/eap_peap.c
@@ -1,5 +1,5 @@
/*
- * EAP peer method: EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-07.txt)
+ * EAP peer method: EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt)
* Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
@@ -25,7 +25,7 @@
/* Maximum supported PEAP version
* 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt
* 1 = draft-josefsson-ppext-eap-tls-eap-05.txt
- * 2 = draft-josefsson-ppext-eap-tls-eap-07.txt
+ * 2 = draft-josefsson-ppext-eap-tls-eap-10.txt
*/
#define EAP_PEAP_VERSION 1
@@ -152,6 +152,33 @@ static void eap_peap_deinit(struct eap_sm *sm, void *priv)
}
+static struct wpabuf * eap_peapv2_tlv_eap_payload(struct wpabuf *buf)
+{
+ struct wpabuf *e;
+ struct eap_tlv_hdr *tlv;
+
+ if (buf == NULL)
+ return NULL;
+
+ /* Encapsulate EAP packet in EAP-Payload TLV */
+ wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Add EAP-Payload TLV");
+ e = wpabuf_alloc(sizeof(*tlv) + wpabuf_len(buf));
+ if (e == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Failed to allocate memory "
+ "for TLV encapsulation");
+ wpabuf_free(buf);
+ return NULL;
+ }
+ tlv = wpabuf_put(e, sizeof(*tlv));
+ tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+ EAP_TLV_EAP_PAYLOAD_TLV);
+ tlv->length = host_to_be16(wpabuf_len(buf));
+ wpabuf_put_buf(e, buf);
+ wpabuf_free(buf);
+ return e;
+}
+
+
static int eap_peap_phase2_request(struct eap_sm *sm,
struct eap_peap_data *data,
struct eap_method_ret *ret,
@@ -344,6 +371,50 @@ continue_req:
wpabuf_free(in_decrypted);
in_decrypted = nmsg;
}
+
+ if (data->peap_version >= 2) {
+ struct eap_tlv_hdr *tlv;
+ struct wpabuf *nmsg;
+
+ if (wpabuf_len(in_decrypted) < sizeof(*tlv) + sizeof(*hdr)) {
+ wpa_printf(MSG_INFO, "EAP-PEAPv2: Too short Phase 2 "
+ "EAP TLV");
+ wpabuf_free(in_decrypted);
+ return 0;
+ }
+ tlv = wpabuf_mhead(in_decrypted);
+ if ((be_to_host16(tlv->tlv_type) & 0x3fff) !=
+ EAP_TLV_EAP_PAYLOAD_TLV) {
+ wpa_printf(MSG_INFO, "EAP-PEAPv2: Not an EAP TLV");
+ wpabuf_free(in_decrypted);
+ return 0;
+ }
+ if (sizeof(*tlv) + be_to_host16(tlv->length) >
+ wpabuf_len(in_decrypted)) {
+ wpa_printf(MSG_INFO, "EAP-PEAPv2: Invalid EAP TLV "
+ "length");
+ wpabuf_free(in_decrypted);
+ return 0;
+ }
+ hdr = (struct eap_hdr *) (tlv + 1);
+ if (be_to_host16(hdr->length) > be_to_host16(tlv->length)) {
+ wpa_printf(MSG_INFO, "EAP-PEAPv2: No room for full "
+ "EAP packet in EAP TLV");
+ wpabuf_free(in_decrypted);
+ return 0;
+ }
+
+ nmsg = wpabuf_alloc(be_to_host16(hdr->length));
+ if (nmsg == NULL) {
+ wpabuf_free(in_decrypted);
+ return 0;
+ }
+
+ wpabuf_put_data(nmsg, hdr, be_to_host16(hdr->length));
+ wpabuf_free(in_decrypted);
+ in_decrypted = nmsg;
+ }
+
hdr = wpabuf_mhead(in_decrypted);
if (wpabuf_len(in_decrypted) < sizeof(*hdr)) {
wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 "
@@ -449,6 +520,11 @@ continue_req:
wpa_hexdump_buf_key(MSG_DEBUG,
"EAP-PEAP: Encrypting Phase 2 data", resp);
/* PEAP version changes */
+ if (data->peap_version >= 2) {
+ resp = eap_peapv2_tlv_eap_payload(resp);
+ if (resp == NULL)
+ return -1;
+ }
if (wpabuf_len(resp) >= 5 &&
wpabuf_head_u8(resp)[0] == EAP_CODE_RESPONSE &&
eap_get_type(resp) == EAP_TYPE_TLV)
diff --git a/src/eap_server/eap_peap.c b/src/eap_server/eap_peap.c
index 0b8d184..a5033d3 100644
--- a/src/eap_server/eap_peap.c
+++ b/src/eap_server/eap_peap.c
@@ -1,5 +1,5 @@
/*
- * hostapd / EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-07.txt)
+ * hostapd / EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt)
* Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
@@ -17,13 +17,14 @@
#include "common.h"
#include "eap_i.h"
#include "eap_tls_common.h"
+#include "eap_common/eap_tlv_common.h"
#include "tls.h"
/* Maximum supported PEAP version
* 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt
* 1 = draft-josefsson-ppext-eap-tls-eap-05.txt
- * 2 = draft-josefsson-ppext-eap-tls-eap-07.txt
+ * 2 = draft-josefsson-ppext-eap-tls-eap-10.txt
*/
#define EAP_PEAP_VERSION 1
@@ -34,7 +35,8 @@ static void eap_peap_reset(struct eap_sm *sm, void *priv);
struct eap_peap_data {
struct eap_ssl_data ssl;
enum {
- START, PHASE1, PHASE2_START, PHASE2_ID, PHASE2_METHOD,
+ START, PHASE1, PHASE1_ID2, PHASE2_START, PHASE2_ID,
+ PHASE2_METHOD,
PHASE2_TLV, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE
} state;
@@ -53,6 +55,8 @@ static const char * eap_peap_state_txt(int state)
return "START";
case PHASE1:
return "PHASE1";
+ case PHASE1_ID2:
+ return "PHASE1_ID2";
case PHASE2_START:
return "PHASE2_START";
case PHASE2_ID:
@@ -84,6 +88,33 @@ static void eap_peap_state(struct eap_peap_data *data, int state)
}
+static struct wpabuf * eap_peapv2_tlv_eap_payload(struct wpabuf *buf)
+{
+ struct wpabuf *e;
+ struct eap_tlv_hdr *tlv;
+
+ if (buf == NULL)
+ return NULL;
+
+ /* Encapsulate EAP packet in EAP-Payload TLV */
+ wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Add EAP-Payload TLV");
+ e = wpabuf_alloc(sizeof(*tlv) + wpabuf_len(buf));
+ if (e == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Failed to allocate memory "
+ "for TLV encapsulation");
+ wpabuf_free(buf);
+ return NULL;
+ }
+ tlv = wpabuf_put(e, sizeof(*tlv));
+ tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+ EAP_TLV_EAP_PAYLOAD_TLV);
+ tlv->length = host_to_be16(wpabuf_len(buf));
+ wpabuf_put_buf(e, buf);
+ wpabuf_free(buf);
+ return e;
+}
+
+
static EapType eap_peap_req_success(struct eap_sm *sm,
struct eap_peap_data *data)
{
@@ -196,7 +227,8 @@ static struct wpabuf * eap_peap_build_req(struct eap_sm *sm,
res = eap_server_tls_buildReq_helper(sm, &data->ssl, EAP_TYPE_PEAP,
data->peap_version, id, &req);
- if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+ if (data->peap_version < 2 &&
+ tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase1 done, starting "
"Phase2");
eap_peap_state(data, PHASE2_START);
@@ -253,6 +285,8 @@ static struct wpabuf * eap_peap_build_phase2_req(struct eap_sm *sm,
size_t req_len;
buf = data->phase2_method->buildReq(sm, data->phase2_priv, id);
+ if (data->peap_version >= 2 && buf)
+ buf = eap_peapv2_tlv_eap_payload(buf);
if (buf == NULL)
return NULL;
@@ -309,6 +343,7 @@ static struct wpabuf * eap_peap_buildReq(struct eap_sm *sm, void *priv, u8 id)
case START:
return eap_peap_build_start(sm, data, id);
case PHASE1:
+ case PHASE1_ID2:
return eap_peap_build_req(sm, data, id);
case PHASE2_ID:
case PHASE2_METHOD:
@@ -425,6 +460,7 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm,
}
switch (data->state) {
+ case PHASE1_ID2:
case PHASE2_ID:
if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
wpa_hexdump_ascii(MSG_DEBUG, "EAP_PEAP: Phase2 "
@@ -547,7 +583,49 @@ static void eap_peap_process_phase2(struct eap_sm *sm,
wpabuf_free(in_decrypted);
in_decrypted = nbuf;
+ } else if (data->peap_version >= 2) {
+ struct eap_tlv_hdr *tlv;
+ struct wpabuf *nmsg;
+
+ if (wpabuf_len(in_decrypted) < sizeof(*tlv) + sizeof(*hdr)) {
+ wpa_printf(MSG_INFO, "EAP-PEAPv2: Too short Phase 2 "
+ "EAP TLV");
+ wpabuf_free(in_decrypted);
+ return;
+ }
+ tlv = wpabuf_mhead(in_decrypted);
+ if ((be_to_host16(tlv->tlv_type) & 0x3fff) !=
+ EAP_TLV_EAP_PAYLOAD_TLV) {
+ wpa_printf(MSG_INFO, "EAP-PEAPv2: Not an EAP TLV");
+ wpabuf_free(in_decrypted);
+ return;
+ }
+ if (sizeof(*tlv) + be_to_host16(tlv->length) >
+ wpabuf_len(in_decrypted)) {
+ wpa_printf(MSG_INFO, "EAP-PEAPv2: Invalid EAP TLV "
+ "length");
+ wpabuf_free(in_decrypted);
+ return;
+ }
+ hdr = (struct eap_hdr *) (tlv + 1);
+ if (be_to_host16(hdr->length) > be_to_host16(tlv->length)) {
+ wpa_printf(MSG_INFO, "EAP-PEAPv2: No room for full "
+ "EAP packet in EAP TLV");
+ wpabuf_free(in_decrypted);
+ return;
+ }
+
+ nmsg = wpabuf_alloc(be_to_host16(hdr->length));
+ if (nmsg == NULL) {
+ wpabuf_free(in_decrypted);
+ return;
+ }
+
+ wpabuf_put_data(nmsg, hdr, be_to_host16(hdr->length));
+ wpabuf_free(in_decrypted);
+ in_decrypted = nmsg;
}
+
hdr = wpabuf_head(in_decrypted);
if (wpabuf_len(in_decrypted) < (int) sizeof(*hdr)) {
wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 "
@@ -594,6 +672,72 @@ static void eap_peap_process_phase2(struct eap_sm *sm,
}
+static int eap_peapv2_start_phase2(struct eap_sm *sm,
+ struct eap_peap_data *data)
+{
+ struct wpabuf *buf, *buf2;
+ int res;
+ u8 *tls_out;
+
+ wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Phase1 done, include first Phase2 "
+ "payload in the same message");
+ eap_peap_state(data, PHASE1_ID2);
+ if (eap_peap_phase2_init(sm, data, EAP_TYPE_IDENTITY))
+ return -1;
+
+ /* TODO: which Id to use here? */
+ buf = data->phase2_method->buildReq(sm, data->phase2_priv, 6);
+ if (buf == NULL)
+ return -1;
+
+ buf2 = eap_peapv2_tlv_eap_payload(buf);
+ if (buf2 == NULL)
+ return -1;
+
+ wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAPv2: Identity Request", buf2);
+
+ buf = wpabuf_alloc(data->ssl.tls_out_limit);
+ if (buf == NULL) {
+ wpabuf_free(buf2);
+ return -1;
+ }
+
+ res = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn,
+ wpabuf_head(buf2), wpabuf_len(buf2),
+ wpabuf_put(buf, 0),
+ data->ssl.tls_out_limit);
+ wpabuf_free(buf2);
+
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "EAP-PEAPv2: Failed to encrypt Phase 2 "
+ "data");
+ wpabuf_free(buf);
+ return -1;
+ }
+
+ wpabuf_put(buf, res);
+ wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAPv2: Encrypted Identity Request",
+ buf);
+
+ /* Append TLS data into the pending buffer after the Server Finished */
+ tls_out = os_realloc(data->ssl.tls_out,
+ data->ssl.tls_out_len + wpabuf_len(buf));
+ if (tls_out == NULL) {
+ wpabuf_free(buf);
+ return -1;
+ }
+
+ os_memcpy(tls_out + data->ssl.tls_out_len, wpabuf_head(buf),
+ wpabuf_len(buf));
+ data->ssl.tls_out = tls_out;
+ data->ssl.tls_out_len += wpabuf_len(buf);
+
+ wpabuf_free(buf);
+
+ return 0;
+}
+
+
static void eap_peap_process(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
@@ -655,12 +799,22 @@ static void eap_peap_process(struct eap_sm *sm, void *priv,
wpa_printf(MSG_INFO, "EAP-PEAP: TLS processing "
"failed");
eap_peap_state(data, FAILURE);
+ break;
+ }
+
+ if (data->peap_version >= 2 &&
+ tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+ if (eap_peapv2_start_phase2(sm, data)) {
+ eap_peap_state(data, FAILURE);
+ break;
+ }
}
break;
case PHASE2_START:
eap_peap_state(data, PHASE2_ID);
eap_peap_phase2_init(sm, data, EAP_TYPE_IDENTITY);
break;
+ case PHASE1_ID2:
case PHASE2_ID:
case PHASE2_METHOD:
case PHASE2_TLV: