xref: /freebsd/contrib/wpa/src/eap_common/eap_common.c (revision f05cddf940dbfc5b657f5e9beb9de2c31e509e5b)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * EAP common peer/server definitions
3*f05cddf9SRui Paulo  * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
439beb93cSSam Leffler  *
5*f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6*f05cddf9SRui Paulo  * See README for more details.
739beb93cSSam Leffler  */
839beb93cSSam Leffler 
939beb93cSSam Leffler #include "includes.h"
1039beb93cSSam Leffler 
1139beb93cSSam Leffler #include "common.h"
1239beb93cSSam Leffler #include "eap_defs.h"
1339beb93cSSam Leffler #include "eap_common.h"
1439beb93cSSam Leffler 
1539beb93cSSam Leffler /**
16*f05cddf9SRui Paulo  * eap_hdr_len_valid - Validate EAP header length field
17*f05cddf9SRui Paulo  * @msg: EAP frame (starting with EAP header)
18*f05cddf9SRui Paulo  * @min_payload: Minimum payload length needed
19*f05cddf9SRui Paulo  * Returns: 1 for valid header, 0 for invalid
20*f05cddf9SRui Paulo  *
21*f05cddf9SRui Paulo  * This is a helper function that does minimal validation of EAP messages. The
22*f05cddf9SRui Paulo  * length field is verified to be large enough to include the header and not
23*f05cddf9SRui Paulo  * too large to go beyond the end of the buffer.
24*f05cddf9SRui Paulo  */
25*f05cddf9SRui Paulo int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload)
26*f05cddf9SRui Paulo {
27*f05cddf9SRui Paulo 	const struct eap_hdr *hdr;
28*f05cddf9SRui Paulo 	size_t len;
29*f05cddf9SRui Paulo 
30*f05cddf9SRui Paulo 	if (msg == NULL)
31*f05cddf9SRui Paulo 		return 0;
32*f05cddf9SRui Paulo 
33*f05cddf9SRui Paulo 	hdr = wpabuf_head(msg);
34*f05cddf9SRui Paulo 
35*f05cddf9SRui Paulo 	if (wpabuf_len(msg) < sizeof(*hdr)) {
36*f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "EAP: Too short EAP frame");
37*f05cddf9SRui Paulo 		return 0;
38*f05cddf9SRui Paulo 	}
39*f05cddf9SRui Paulo 
40*f05cddf9SRui Paulo 	len = be_to_host16(hdr->length);
41*f05cddf9SRui Paulo 	if (len < sizeof(*hdr) + min_payload || len > wpabuf_len(msg)) {
42*f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "EAP: Invalid EAP length");
43*f05cddf9SRui Paulo 		return 0;
44*f05cddf9SRui Paulo 	}
45*f05cddf9SRui Paulo 
46*f05cddf9SRui Paulo 	return 1;
47*f05cddf9SRui Paulo }
48*f05cddf9SRui Paulo 
49*f05cddf9SRui Paulo 
50*f05cddf9SRui Paulo /**
5139beb93cSSam Leffler  * eap_hdr_validate - Validate EAP header
5239beb93cSSam Leffler  * @vendor: Expected EAP Vendor-Id (0 = IETF)
5339beb93cSSam Leffler  * @eap_type: Expected EAP type number
5439beb93cSSam Leffler  * @msg: EAP frame (starting with EAP header)
5539beb93cSSam Leffler  * @plen: Pointer to variable to contain the returned payload length
5639beb93cSSam Leffler  * Returns: Pointer to EAP payload (after type field), or %NULL on failure
5739beb93cSSam Leffler  *
5839beb93cSSam Leffler  * This is a helper function for EAP method implementations. This is usually
5939beb93cSSam Leffler  * called in the beginning of struct eap_method::process() function to verify
6039beb93cSSam Leffler  * that the received EAP request packet has a valid header. This function is
6139beb93cSSam Leffler  * able to process both legacy and expanded EAP headers and in most cases, the
6239beb93cSSam Leffler  * caller can just use the returned payload pointer (into *plen) for processing
6339beb93cSSam Leffler  * the payload regardless of whether the packet used the expanded EAP header or
6439beb93cSSam Leffler  * not.
6539beb93cSSam Leffler  */
6639beb93cSSam Leffler const u8 * eap_hdr_validate(int vendor, EapType eap_type,
6739beb93cSSam Leffler 			    const struct wpabuf *msg, size_t *plen)
6839beb93cSSam Leffler {
6939beb93cSSam Leffler 	const struct eap_hdr *hdr;
7039beb93cSSam Leffler 	const u8 *pos;
7139beb93cSSam Leffler 	size_t len;
7239beb93cSSam Leffler 
73*f05cddf9SRui Paulo 	if (!eap_hdr_len_valid(msg, 1))
74*f05cddf9SRui Paulo 		return NULL;
75*f05cddf9SRui Paulo 
7639beb93cSSam Leffler 	hdr = wpabuf_head(msg);
7739beb93cSSam Leffler 	len = be_to_host16(hdr->length);
7839beb93cSSam Leffler 	pos = (const u8 *) (hdr + 1);
7939beb93cSSam Leffler 
8039beb93cSSam Leffler 	if (*pos == EAP_TYPE_EXPANDED) {
8139beb93cSSam Leffler 		int exp_vendor;
8239beb93cSSam Leffler 		u32 exp_type;
8339beb93cSSam Leffler 		if (len < sizeof(*hdr) + 8) {
8439beb93cSSam Leffler 			wpa_printf(MSG_INFO, "EAP: Invalid expanded EAP "
8539beb93cSSam Leffler 				   "length");
8639beb93cSSam Leffler 			return NULL;
8739beb93cSSam Leffler 		}
8839beb93cSSam Leffler 		pos++;
8939beb93cSSam Leffler 		exp_vendor = WPA_GET_BE24(pos);
9039beb93cSSam Leffler 		pos += 3;
9139beb93cSSam Leffler 		exp_type = WPA_GET_BE32(pos);
9239beb93cSSam Leffler 		pos += 4;
9339beb93cSSam Leffler 		if (exp_vendor != vendor || exp_type != (u32) eap_type) {
9439beb93cSSam Leffler 			wpa_printf(MSG_INFO, "EAP: Invalid expanded frame "
9539beb93cSSam Leffler 				   "type");
9639beb93cSSam Leffler 			return NULL;
9739beb93cSSam Leffler 		}
9839beb93cSSam Leffler 
9939beb93cSSam Leffler 		*plen = len - sizeof(*hdr) - 8;
10039beb93cSSam Leffler 		return pos;
10139beb93cSSam Leffler 	} else {
10239beb93cSSam Leffler 		if (vendor != EAP_VENDOR_IETF || *pos != eap_type) {
10339beb93cSSam Leffler 			wpa_printf(MSG_INFO, "EAP: Invalid frame type");
10439beb93cSSam Leffler 			return NULL;
10539beb93cSSam Leffler 		}
10639beb93cSSam Leffler 		*plen = len - sizeof(*hdr) - 1;
10739beb93cSSam Leffler 		return pos + 1;
10839beb93cSSam Leffler 	}
10939beb93cSSam Leffler }
11039beb93cSSam Leffler 
11139beb93cSSam Leffler 
11239beb93cSSam Leffler /**
11339beb93cSSam Leffler  * eap_msg_alloc - Allocate a buffer for an EAP message
11439beb93cSSam Leffler  * @vendor: Vendor-Id (0 = IETF)
11539beb93cSSam Leffler  * @type: EAP type
11639beb93cSSam Leffler  * @payload_len: Payload length in bytes (data after Type)
11739beb93cSSam Leffler  * @code: Message Code (EAP_CODE_*)
11839beb93cSSam Leffler  * @identifier: Identifier
11939beb93cSSam Leffler  * Returns: Pointer to the allocated message buffer or %NULL on error
12039beb93cSSam Leffler  *
12139beb93cSSam Leffler  * This function can be used to allocate a buffer for an EAP message and fill
12239beb93cSSam Leffler  * in the EAP header. This function is automatically using expanded EAP header
12339beb93cSSam Leffler  * if the selected Vendor-Id is not IETF. In other words, most EAP methods do
12439beb93cSSam Leffler  * not need to separately select which header type to use when using this
12539beb93cSSam Leffler  * function to allocate the message buffers. The returned buffer has room for
12639beb93cSSam Leffler  * payload_len bytes and has the EAP header and Type field already filled in.
12739beb93cSSam Leffler  */
12839beb93cSSam Leffler struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len,
12939beb93cSSam Leffler 			      u8 code, u8 identifier)
13039beb93cSSam Leffler {
13139beb93cSSam Leffler 	struct wpabuf *buf;
13239beb93cSSam Leffler 	struct eap_hdr *hdr;
13339beb93cSSam Leffler 	size_t len;
13439beb93cSSam Leffler 
13539beb93cSSam Leffler 	len = sizeof(struct eap_hdr) + (vendor == EAP_VENDOR_IETF ? 1 : 8) +
13639beb93cSSam Leffler 		payload_len;
13739beb93cSSam Leffler 	buf = wpabuf_alloc(len);
13839beb93cSSam Leffler 	if (buf == NULL)
13939beb93cSSam Leffler 		return NULL;
14039beb93cSSam Leffler 
14139beb93cSSam Leffler 	hdr = wpabuf_put(buf, sizeof(*hdr));
14239beb93cSSam Leffler 	hdr->code = code;
14339beb93cSSam Leffler 	hdr->identifier = identifier;
14439beb93cSSam Leffler 	hdr->length = host_to_be16(len);
14539beb93cSSam Leffler 
14639beb93cSSam Leffler 	if (vendor == EAP_VENDOR_IETF) {
14739beb93cSSam Leffler 		wpabuf_put_u8(buf, type);
14839beb93cSSam Leffler 	} else {
14939beb93cSSam Leffler 		wpabuf_put_u8(buf, EAP_TYPE_EXPANDED);
15039beb93cSSam Leffler 		wpabuf_put_be24(buf, vendor);
15139beb93cSSam Leffler 		wpabuf_put_be32(buf, type);
15239beb93cSSam Leffler 	}
15339beb93cSSam Leffler 
15439beb93cSSam Leffler 	return buf;
15539beb93cSSam Leffler }
15639beb93cSSam Leffler 
15739beb93cSSam Leffler 
15839beb93cSSam Leffler /**
15939beb93cSSam Leffler  * eap_update_len - Update EAP header length
16039beb93cSSam Leffler  * @msg: EAP message from eap_msg_alloc
16139beb93cSSam Leffler  *
16239beb93cSSam Leffler  * This function updates the length field in the EAP header to match with the
16339beb93cSSam Leffler  * current length for the buffer. This allows eap_msg_alloc() to be used to
16439beb93cSSam Leffler  * allocate a larger buffer than the exact message length (e.g., if exact
16539beb93cSSam Leffler  * message length is not yet known).
16639beb93cSSam Leffler  */
16739beb93cSSam Leffler void eap_update_len(struct wpabuf *msg)
16839beb93cSSam Leffler {
16939beb93cSSam Leffler 	struct eap_hdr *hdr;
17039beb93cSSam Leffler 	hdr = wpabuf_mhead(msg);
17139beb93cSSam Leffler 	if (wpabuf_len(msg) < sizeof(*hdr))
17239beb93cSSam Leffler 		return;
17339beb93cSSam Leffler 	hdr->length = host_to_be16(wpabuf_len(msg));
17439beb93cSSam Leffler }
17539beb93cSSam Leffler 
17639beb93cSSam Leffler 
17739beb93cSSam Leffler /**
17839beb93cSSam Leffler  * eap_get_id - Get EAP Identifier from wpabuf
17939beb93cSSam Leffler  * @msg: Buffer starting with an EAP header
18039beb93cSSam Leffler  * Returns: The Identifier field from the EAP header
18139beb93cSSam Leffler  */
18239beb93cSSam Leffler u8 eap_get_id(const struct wpabuf *msg)
18339beb93cSSam Leffler {
18439beb93cSSam Leffler 	const struct eap_hdr *eap;
18539beb93cSSam Leffler 
18639beb93cSSam Leffler 	if (wpabuf_len(msg) < sizeof(*eap))
18739beb93cSSam Leffler 		return 0;
18839beb93cSSam Leffler 
18939beb93cSSam Leffler 	eap = wpabuf_head(msg);
19039beb93cSSam Leffler 	return eap->identifier;
19139beb93cSSam Leffler }
19239beb93cSSam Leffler 
19339beb93cSSam Leffler 
19439beb93cSSam Leffler /**
19539beb93cSSam Leffler  * eap_get_id - Get EAP Type from wpabuf
19639beb93cSSam Leffler  * @msg: Buffer starting with an EAP header
19739beb93cSSam Leffler  * Returns: The EAP Type after the EAP header
19839beb93cSSam Leffler  */
19939beb93cSSam Leffler EapType eap_get_type(const struct wpabuf *msg)
20039beb93cSSam Leffler {
20139beb93cSSam Leffler 	if (wpabuf_len(msg) < sizeof(struct eap_hdr) + 1)
20239beb93cSSam Leffler 		return EAP_TYPE_NONE;
20339beb93cSSam Leffler 
20439beb93cSSam Leffler 	return ((const u8 *) wpabuf_head(msg))[sizeof(struct eap_hdr)];
20539beb93cSSam Leffler }
206