diff --git a/src/eap_common/eap_common.c b/src/eap_common/eap_common.c index 0d6ef9311..7b077cb9f 100644 --- a/src/eap_common/eap_common.c +++ b/src/eap_common/eap_common.c @@ -1,6 +1,6 @@ /* * EAP common peer/server definitions - * Copyright (c) 2004-2007, Jouni Malinen + * Copyright (c) 2004-2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -12,6 +12,41 @@ #include "eap_defs.h" #include "eap_common.h" +/** + * eap_hdr_len_valid - Validate EAP header length field + * @msg: EAP frame (starting with EAP header) + * @min_payload: Minimum payload length needed + * Returns: 1 for valid header, 0 for invalid + * + * This is a helper function that does minimal validation of EAP messages. The + * length field is verified to be large enough to include the header and not + * too large to go beyond the end of the buffer. + */ +int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload) +{ + const struct eap_hdr *hdr; + size_t len; + + if (msg == NULL) + return 0; + + hdr = wpabuf_head(msg); + + if (wpabuf_len(msg) < sizeof(*hdr)) { + wpa_printf(MSG_INFO, "EAP: Too short EAP frame"); + return 0; + } + + len = be_to_host16(hdr->length); + if (len < sizeof(*hdr) + min_payload || len > wpabuf_len(msg)) { + wpa_printf(MSG_INFO, "EAP: Invalid EAP length"); + return 0; + } + + return 1; +} + + /** * eap_hdr_validate - Validate EAP header * @vendor: Expected EAP Vendor-Id (0 = IETF) @@ -35,19 +70,11 @@ const u8 * eap_hdr_validate(int vendor, EapType eap_type, const u8 *pos; size_t len; + if (!eap_hdr_len_valid(msg, 1)) + return NULL; + hdr = wpabuf_head(msg); - - if (wpabuf_len(msg) < sizeof(*hdr)) { - wpa_printf(MSG_INFO, "EAP: Too short EAP frame"); - return NULL; - } - len = be_to_host16(hdr->length); - if (len < sizeof(*hdr) + 1 || len > wpabuf_len(msg)) { - wpa_printf(MSG_INFO, "EAP: Invalid EAP length"); - return NULL; - } - pos = (const u8 *) (hdr + 1); if (*pos == EAP_TYPE_EXPANDED) { diff --git a/src/eap_common/eap_common.h b/src/eap_common/eap_common.h index 73f279722..8850c1fe5 100644 --- a/src/eap_common/eap_common.h +++ b/src/eap_common/eap_common.h @@ -1,6 +1,6 @@ /* * EAP common peer/server definitions - * Copyright (c) 2004-2007, Jouni Malinen + * Copyright (c) 2004-2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -11,6 +11,7 @@ #include "wpabuf.h" +int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload); const u8 * eap_hdr_validate(int vendor, EapType eap_type, const struct wpabuf *msg, size_t *plen); struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len, diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c index b67249445..c19f98192 100644 --- a/src/eap_peer/eap.c +++ b/src/eap_peer/eap.c @@ -350,6 +350,8 @@ SM_STATE(EAP, METHOD) } eapReqData = eapol_get_eapReqData(sm); + if (!eap_hdr_len_valid(eapReqData, 1)) + return; /* * Get ignore, methodState, decision, allowNotifications, and @@ -438,6 +440,8 @@ SM_STATE(EAP, IDENTITY) SM_ENTRY(EAP, IDENTITY); eapReqData = eapol_get_eapReqData(sm); + if (!eap_hdr_len_valid(eapReqData, 1)) + return; eap_sm_processIdentity(sm, eapReqData); wpabuf_free(sm->eapRespData); sm->eapRespData = NULL; @@ -454,6 +458,8 @@ SM_STATE(EAP, NOTIFICATION) SM_ENTRY(EAP, NOTIFICATION); eapReqData = eapol_get_eapReqData(sm); + if (!eap_hdr_len_valid(eapReqData, 1)) + return; eap_sm_processNotify(sm, eapReqData); wpabuf_free(sm->eapRespData); sm->eapRespData = NULL; @@ -871,13 +877,17 @@ static struct wpabuf * eap_sm_buildNak(struct eap_sm *sm, int id) static void eap_sm_processIdentity(struct eap_sm *sm, const struct wpabuf *req) { - const struct eap_hdr *hdr = wpabuf_head(req); - const u8 *pos = (const u8 *) (hdr + 1); - pos++; + const u8 *pos; + size_t msg_len; wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED "EAP authentication started"); + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, req, + &msg_len); + if (pos == NULL) + return; + /* * RFC 3748 - 5.1: Identity * Data field may contain a displayable message in UTF-8. If this @@ -888,7 +898,7 @@ static void eap_sm_processIdentity(struct eap_sm *sm, const struct wpabuf *req) /* TODO: could save displayable message so that it can be shown to the * user in case of interaction is required */ wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Identity data", - pos, be_to_host16(hdr->length) - 5); + pos, msg_len); } diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c index 44c089fac..15f7e2284 100644 --- a/src/eap_server/eap_server.c +++ b/src/eap_server/eap_server.c @@ -275,6 +275,11 @@ SM_STATE(EAP, INTEGRITY_CHECK) { SM_ENTRY(EAP, INTEGRITY_CHECK); + if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) { + sm->ignore = TRUE; + return; + } + if (sm->m->check) { sm->ignore = sm->m->check(sm, sm->eap_method_priv, sm->eap_if.eapRespData); @@ -309,6 +314,9 @@ SM_STATE(EAP, METHOD_RESPONSE) { SM_ENTRY(EAP, METHOD_RESPONSE); + if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) + return; + sm->m->process(sm, sm->eap_method_priv, sm->eap_if.eapRespData); if (sm->m->isDone(sm, sm->eap_method_priv)) { eap_sm_Policy_update(sm, NULL, 0); @@ -380,6 +388,9 @@ SM_STATE(EAP, NAK) } sm->m = NULL; + if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) + return; + nak = wpabuf_head(sm->eap_if.eapRespData); if (nak && wpabuf_len(sm->eap_if.eapRespData) > sizeof(*nak)) { len = be_to_host16(nak->length);