xref: /freebsd/contrib/wpa/src/eap_common/eap_common.c (revision c1d255d3ffdbe447de3ab875bf4e7d7accc5bfc5)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * EAP common peer/server definitions
35b9c547cSRui Paulo  * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
439beb93cSSam Leffler  *
5f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui 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 /**
16f05cddf9SRui Paulo  * eap_hdr_len_valid - Validate EAP header length field
17f05cddf9SRui Paulo  * @msg: EAP frame (starting with EAP header)
18f05cddf9SRui Paulo  * @min_payload: Minimum payload length needed
19f05cddf9SRui Paulo  * Returns: 1 for valid header, 0 for invalid
20f05cddf9SRui Paulo  *
21f05cddf9SRui Paulo  * This is a helper function that does minimal validation of EAP messages. The
22f05cddf9SRui Paulo  * length field is verified to be large enough to include the header and not
23f05cddf9SRui Paulo  * too large to go beyond the end of the buffer.
24f05cddf9SRui Paulo  */
eap_hdr_len_valid(const struct wpabuf * msg,size_t min_payload)25f05cddf9SRui Paulo int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload)
26f05cddf9SRui Paulo {
27f05cddf9SRui Paulo 	const struct eap_hdr *hdr;
28f05cddf9SRui Paulo 	size_t len;
29f05cddf9SRui Paulo 
30f05cddf9SRui Paulo 	if (msg == NULL)
31f05cddf9SRui Paulo 		return 0;
32f05cddf9SRui Paulo 
33f05cddf9SRui Paulo 	hdr = wpabuf_head(msg);
34f05cddf9SRui Paulo 
35f05cddf9SRui Paulo 	if (wpabuf_len(msg) < sizeof(*hdr)) {
36f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "EAP: Too short EAP frame");
37f05cddf9SRui Paulo 		return 0;
38f05cddf9SRui Paulo 	}
39f05cddf9SRui Paulo 
40f05cddf9SRui Paulo 	len = be_to_host16(hdr->length);
41f05cddf9SRui Paulo 	if (len < sizeof(*hdr) + min_payload || len > wpabuf_len(msg)) {
42f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "EAP: Invalid EAP length");
43f05cddf9SRui Paulo 		return 0;
44f05cddf9SRui Paulo 	}
45f05cddf9SRui Paulo 
46f05cddf9SRui Paulo 	return 1;
47f05cddf9SRui Paulo }
48f05cddf9SRui Paulo 
49f05cddf9SRui Paulo 
50f05cddf9SRui 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  */
eap_hdr_validate(int vendor,enum eap_type eap_type,const struct wpabuf * msg,size_t * plen)66*c1d255d3SCy Schubert const u8 * eap_hdr_validate(int vendor, enum eap_type 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 
73f05cddf9SRui Paulo 	if (!eap_hdr_len_valid(msg, 1))
74f05cddf9SRui Paulo 		return NULL;
75f05cddf9SRui 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  */
eap_msg_alloc(int vendor,enum eap_type type,size_t payload_len,u8 code,u8 identifier)128*c1d255d3SCy Schubert struct wpabuf * eap_msg_alloc(int vendor, enum eap_type type,
129*c1d255d3SCy Schubert 			      size_t payload_len, 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  */
eap_update_len(struct wpabuf * msg)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  */
eap_get_id(const struct wpabuf * msg)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 /**
195325151a3SRui Paulo  * eap_get_type - 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  */
eap_get_type(const struct wpabuf * msg)199*c1d255d3SCy Schubert enum eap_type 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 }
2065b9c547cSRui Paulo 
2075b9c547cSRui Paulo 
2085b9c547cSRui Paulo #ifdef CONFIG_ERP
erp_parse_tlvs(const u8 * pos,const u8 * end,struct erp_tlvs * tlvs,int stop_at_keyname)2095b9c547cSRui Paulo int erp_parse_tlvs(const u8 *pos, const u8 *end, struct erp_tlvs *tlvs,
2105b9c547cSRui Paulo 		   int stop_at_keyname)
2115b9c547cSRui Paulo {
2125b9c547cSRui Paulo 	os_memset(tlvs, 0, sizeof(*tlvs));
2135b9c547cSRui Paulo 
2145b9c547cSRui Paulo 	while (pos < end) {
2155b9c547cSRui Paulo 		u8 tlv_type, tlv_len;
2165b9c547cSRui Paulo 
2175b9c547cSRui Paulo 		tlv_type = *pos++;
2185b9c547cSRui Paulo 		switch (tlv_type) {
2195b9c547cSRui Paulo 		case EAP_ERP_TV_RRK_LIFETIME:
2205b9c547cSRui Paulo 		case EAP_ERP_TV_RMSK_LIFETIME:
2215b9c547cSRui Paulo 			/* 4-octet TV */
2225b9c547cSRui Paulo 			if (pos + 4 > end) {
2235b9c547cSRui Paulo 				wpa_printf(MSG_DEBUG, "EAP: Too short TV");
2245b9c547cSRui Paulo 				return -1;
2255b9c547cSRui Paulo 			}
2265b9c547cSRui Paulo 			pos += 4;
2275b9c547cSRui Paulo 			break;
2285b9c547cSRui Paulo 		case EAP_ERP_TLV_DOMAIN_NAME:
2295b9c547cSRui Paulo 		case EAP_ERP_TLV_KEYNAME_NAI:
2305b9c547cSRui Paulo 		case EAP_ERP_TLV_CRYPTOSUITES:
2315b9c547cSRui Paulo 		case EAP_ERP_TLV_AUTHORIZATION_INDICATION:
2325b9c547cSRui Paulo 		case EAP_ERP_TLV_CALLED_STATION_ID:
2335b9c547cSRui Paulo 		case EAP_ERP_TLV_CALLING_STATION_ID:
2345b9c547cSRui Paulo 		case EAP_ERP_TLV_NAS_IDENTIFIER:
2355b9c547cSRui Paulo 		case EAP_ERP_TLV_NAS_IP_ADDRESS:
2365b9c547cSRui Paulo 		case EAP_ERP_TLV_NAS_IPV6_ADDRESS:
2375b9c547cSRui Paulo 			if (pos >= end) {
2385b9c547cSRui Paulo 				wpa_printf(MSG_DEBUG, "EAP: Too short TLV");
2395b9c547cSRui Paulo 				return -1;
2405b9c547cSRui Paulo 			}
2415b9c547cSRui Paulo 			tlv_len = *pos++;
2425b9c547cSRui Paulo 			if (tlv_len > (unsigned) (end - pos)) {
2435b9c547cSRui Paulo 				wpa_printf(MSG_DEBUG, "EAP: Truncated TLV");
2445b9c547cSRui Paulo 				return -1;
2455b9c547cSRui Paulo 			}
2465b9c547cSRui Paulo 			if (tlv_type == EAP_ERP_TLV_KEYNAME_NAI) {
2475b9c547cSRui Paulo 				if (tlvs->keyname) {
2485b9c547cSRui Paulo 					wpa_printf(MSG_DEBUG,
2495b9c547cSRui Paulo 						   "EAP: More than one keyName-NAI");
2505b9c547cSRui Paulo 					return -1;
2515b9c547cSRui Paulo 				}
2525b9c547cSRui Paulo 				tlvs->keyname = pos;
2535b9c547cSRui Paulo 				tlvs->keyname_len = tlv_len;
2545b9c547cSRui Paulo 				if (stop_at_keyname)
2555b9c547cSRui Paulo 					return 0;
2565b9c547cSRui Paulo 			} else if (tlv_type == EAP_ERP_TLV_DOMAIN_NAME) {
2575b9c547cSRui Paulo 				tlvs->domain = pos;
2585b9c547cSRui Paulo 				tlvs->domain_len = tlv_len;
2595b9c547cSRui Paulo 			}
2605b9c547cSRui Paulo 			pos += tlv_len;
2615b9c547cSRui Paulo 			break;
2625b9c547cSRui Paulo 		default:
2635b9c547cSRui Paulo 			if (tlv_type >= 128 && tlv_type <= 191) {
2645b9c547cSRui Paulo 				/* Undefined TLV */
2655b9c547cSRui Paulo 				if (pos >= end) {
2665b9c547cSRui Paulo 					wpa_printf(MSG_DEBUG,
2675b9c547cSRui Paulo 						   "EAP: Too short TLV");
2685b9c547cSRui Paulo 					return -1;
2695b9c547cSRui Paulo 				}
2705b9c547cSRui Paulo 				tlv_len = *pos++;
2715b9c547cSRui Paulo 				if (tlv_len > (unsigned) (end - pos)) {
2725b9c547cSRui Paulo 					wpa_printf(MSG_DEBUG,
2735b9c547cSRui Paulo 						   "EAP: Truncated TLV");
2745b9c547cSRui Paulo 					return -1;
2755b9c547cSRui Paulo 				}
2765b9c547cSRui Paulo 				pos += tlv_len;
2775b9c547cSRui Paulo 				break;
2785b9c547cSRui Paulo 			}
2795b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "EAP: Unknown TV/TLV type %u",
2805b9c547cSRui Paulo 				   tlv_type);
2815b9c547cSRui Paulo 			pos = end;
2825b9c547cSRui Paulo 			break;
2835b9c547cSRui Paulo 		}
2845b9c547cSRui Paulo 	}
2855b9c547cSRui Paulo 
2865b9c547cSRui Paulo 	return 0;
2875b9c547cSRui Paulo }
2885b9c547cSRui Paulo #endif /* CONFIG_ERP */
289