xref: /netbsd-src/external/bsd/wpa/dist/src/eap_common/eap_common.c (revision bb6183629cf165db498d8e1f4e2de129f7efb21c)
18dbcf02cSchristos /*
28dbcf02cSchristos  * EAP common peer/server definitions
3bb610346Schristos  * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
48dbcf02cSchristos  *
5e604d861Schristos  * This software may be distributed under the terms of the BSD license.
6e604d861Schristos  * See README for more details.
78dbcf02cSchristos  */
88dbcf02cSchristos 
98dbcf02cSchristos #include "includes.h"
108dbcf02cSchristos 
118dbcf02cSchristos #include "common.h"
128dbcf02cSchristos #include "eap_defs.h"
138dbcf02cSchristos #include "eap_common.h"
148dbcf02cSchristos 
158dbcf02cSchristos /**
16e604d861Schristos  * eap_hdr_len_valid - Validate EAP header length field
17e604d861Schristos  * @msg: EAP frame (starting with EAP header)
18e604d861Schristos  * @min_payload: Minimum payload length needed
19e604d861Schristos  * Returns: 1 for valid header, 0 for invalid
20e604d861Schristos  *
21e604d861Schristos  * This is a helper function that does minimal validation of EAP messages. The
22e604d861Schristos  * length field is verified to be large enough to include the header and not
23e604d861Schristos  * too large to go beyond the end of the buffer.
24e604d861Schristos  */
25e604d861Schristos int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload)
26e604d861Schristos {
27e604d861Schristos 	const struct eap_hdr *hdr;
28e604d861Schristos 	size_t len;
29e604d861Schristos 
30e604d861Schristos 	if (msg == NULL)
31e604d861Schristos 		return 0;
32e604d861Schristos 
33e604d861Schristos 	hdr = wpabuf_head(msg);
34e604d861Schristos 
35e604d861Schristos 	if (wpabuf_len(msg) < sizeof(*hdr)) {
36e604d861Schristos 		wpa_printf(MSG_INFO, "EAP: Too short EAP frame");
37e604d861Schristos 		return 0;
38e604d861Schristos 	}
39e604d861Schristos 
40e604d861Schristos 	len = be_to_host16(hdr->length);
41e604d861Schristos 	if (len < sizeof(*hdr) + min_payload || len > wpabuf_len(msg)) {
42e604d861Schristos 		wpa_printf(MSG_INFO, "EAP: Invalid EAP length");
43e604d861Schristos 		return 0;
44e604d861Schristos 	}
45e604d861Schristos 
46e604d861Schristos 	return 1;
47e604d861Schristos }
48e604d861Schristos 
49e604d861Schristos 
50e604d861Schristos /**
518dbcf02cSchristos  * eap_hdr_validate - Validate EAP header
528dbcf02cSchristos  * @vendor: Expected EAP Vendor-Id (0 = IETF)
538dbcf02cSchristos  * @eap_type: Expected EAP type number
548dbcf02cSchristos  * @msg: EAP frame (starting with EAP header)
558dbcf02cSchristos  * @plen: Pointer to variable to contain the returned payload length
568dbcf02cSchristos  * Returns: Pointer to EAP payload (after type field), or %NULL on failure
578dbcf02cSchristos  *
588dbcf02cSchristos  * This is a helper function for EAP method implementations. This is usually
598dbcf02cSchristos  * called in the beginning of struct eap_method::process() function to verify
608dbcf02cSchristos  * that the received EAP request packet has a valid header. This function is
618dbcf02cSchristos  * able to process both legacy and expanded EAP headers and in most cases, the
628dbcf02cSchristos  * caller can just use the returned payload pointer (into *plen) for processing
638dbcf02cSchristos  * the payload regardless of whether the packet used the expanded EAP header or
648dbcf02cSchristos  * not.
658dbcf02cSchristos  */
66*bb618362Schristos const u8 * eap_hdr_validate(int vendor, enum eap_type eap_type,
678dbcf02cSchristos 			    const struct wpabuf *msg, size_t *plen)
688dbcf02cSchristos {
698dbcf02cSchristos 	const struct eap_hdr *hdr;
708dbcf02cSchristos 	const u8 *pos;
718dbcf02cSchristos 	size_t len;
728dbcf02cSchristos 
73e604d861Schristos 	if (!eap_hdr_len_valid(msg, 1))
74e604d861Schristos 		return NULL;
75e604d861Schristos 
768dbcf02cSchristos 	hdr = wpabuf_head(msg);
778dbcf02cSchristos 	len = be_to_host16(hdr->length);
788dbcf02cSchristos 	pos = (const u8 *) (hdr + 1);
798dbcf02cSchristos 
808dbcf02cSchristos 	if (*pos == EAP_TYPE_EXPANDED) {
818dbcf02cSchristos 		int exp_vendor;
828dbcf02cSchristos 		u32 exp_type;
838dbcf02cSchristos 		if (len < sizeof(*hdr) + 8) {
848dbcf02cSchristos 			wpa_printf(MSG_INFO, "EAP: Invalid expanded EAP "
858dbcf02cSchristos 				   "length");
868dbcf02cSchristos 			return NULL;
878dbcf02cSchristos 		}
888dbcf02cSchristos 		pos++;
898dbcf02cSchristos 		exp_vendor = WPA_GET_BE24(pos);
908dbcf02cSchristos 		pos += 3;
918dbcf02cSchristos 		exp_type = WPA_GET_BE32(pos);
928dbcf02cSchristos 		pos += 4;
938dbcf02cSchristos 		if (exp_vendor != vendor || exp_type != (u32) eap_type) {
948dbcf02cSchristos 			wpa_printf(MSG_INFO, "EAP: Invalid expanded frame "
958dbcf02cSchristos 				   "type");
968dbcf02cSchristos 			return NULL;
978dbcf02cSchristos 		}
988dbcf02cSchristos 
998dbcf02cSchristos 		*plen = len - sizeof(*hdr) - 8;
1008dbcf02cSchristos 		return pos;
1018dbcf02cSchristos 	} else {
1028dbcf02cSchristos 		if (vendor != EAP_VENDOR_IETF || *pos != eap_type) {
1038dbcf02cSchristos 			wpa_printf(MSG_INFO, "EAP: Invalid frame type");
1048dbcf02cSchristos 			return NULL;
1058dbcf02cSchristos 		}
1068dbcf02cSchristos 		*plen = len - sizeof(*hdr) - 1;
1078dbcf02cSchristos 		return pos + 1;
1088dbcf02cSchristos 	}
1098dbcf02cSchristos }
1108dbcf02cSchristos 
1118dbcf02cSchristos 
1128dbcf02cSchristos /**
1138dbcf02cSchristos  * eap_msg_alloc - Allocate a buffer for an EAP message
1148dbcf02cSchristos  * @vendor: Vendor-Id (0 = IETF)
1158dbcf02cSchristos  * @type: EAP type
1168dbcf02cSchristos  * @payload_len: Payload length in bytes (data after Type)
1178dbcf02cSchristos  * @code: Message Code (EAP_CODE_*)
1188dbcf02cSchristos  * @identifier: Identifier
1198dbcf02cSchristos  * Returns: Pointer to the allocated message buffer or %NULL on error
1208dbcf02cSchristos  *
1218dbcf02cSchristos  * This function can be used to allocate a buffer for an EAP message and fill
1228dbcf02cSchristos  * in the EAP header. This function is automatically using expanded EAP header
1238dbcf02cSchristos  * if the selected Vendor-Id is not IETF. In other words, most EAP methods do
1248dbcf02cSchristos  * not need to separately select which header type to use when using this
1258dbcf02cSchristos  * function to allocate the message buffers. The returned buffer has room for
1268dbcf02cSchristos  * payload_len bytes and has the EAP header and Type field already filled in.
1278dbcf02cSchristos  */
128*bb618362Schristos struct wpabuf * eap_msg_alloc(int vendor, enum eap_type type,
129*bb618362Schristos 			      size_t payload_len, u8 code, u8 identifier)
1308dbcf02cSchristos {
1318dbcf02cSchristos 	struct wpabuf *buf;
1328dbcf02cSchristos 	struct eap_hdr *hdr;
1338dbcf02cSchristos 	size_t len;
1348dbcf02cSchristos 
1358dbcf02cSchristos 	len = sizeof(struct eap_hdr) + (vendor == EAP_VENDOR_IETF ? 1 : 8) +
1368dbcf02cSchristos 		payload_len;
1378dbcf02cSchristos 	buf = wpabuf_alloc(len);
1388dbcf02cSchristos 	if (buf == NULL)
1398dbcf02cSchristos 		return NULL;
1408dbcf02cSchristos 
1418dbcf02cSchristos 	hdr = wpabuf_put(buf, sizeof(*hdr));
1428dbcf02cSchristos 	hdr->code = code;
1438dbcf02cSchristos 	hdr->identifier = identifier;
1448dbcf02cSchristos 	hdr->length = host_to_be16(len);
1458dbcf02cSchristos 
1468dbcf02cSchristos 	if (vendor == EAP_VENDOR_IETF) {
1478dbcf02cSchristos 		wpabuf_put_u8(buf, type);
1488dbcf02cSchristos 	} else {
1498dbcf02cSchristos 		wpabuf_put_u8(buf, EAP_TYPE_EXPANDED);
1508dbcf02cSchristos 		wpabuf_put_be24(buf, vendor);
1518dbcf02cSchristos 		wpabuf_put_be32(buf, type);
1528dbcf02cSchristos 	}
1538dbcf02cSchristos 
1548dbcf02cSchristos 	return buf;
1558dbcf02cSchristos }
1568dbcf02cSchristos 
1578dbcf02cSchristos 
1588dbcf02cSchristos /**
1598dbcf02cSchristos  * eap_update_len - Update EAP header length
1608dbcf02cSchristos  * @msg: EAP message from eap_msg_alloc
1618dbcf02cSchristos  *
1628dbcf02cSchristos  * This function updates the length field in the EAP header to match with the
1638dbcf02cSchristos  * current length for the buffer. This allows eap_msg_alloc() to be used to
1648dbcf02cSchristos  * allocate a larger buffer than the exact message length (e.g., if exact
1658dbcf02cSchristos  * message length is not yet known).
1668dbcf02cSchristos  */
1678dbcf02cSchristos void eap_update_len(struct wpabuf *msg)
1688dbcf02cSchristos {
1698dbcf02cSchristos 	struct eap_hdr *hdr;
1708dbcf02cSchristos 	hdr = wpabuf_mhead(msg);
1718dbcf02cSchristos 	if (wpabuf_len(msg) < sizeof(*hdr))
1728dbcf02cSchristos 		return;
1738dbcf02cSchristos 	hdr->length = host_to_be16(wpabuf_len(msg));
1748dbcf02cSchristos }
1758dbcf02cSchristos 
1768dbcf02cSchristos 
1778dbcf02cSchristos /**
1788dbcf02cSchristos  * eap_get_id - Get EAP Identifier from wpabuf
1798dbcf02cSchristos  * @msg: Buffer starting with an EAP header
1808dbcf02cSchristos  * Returns: The Identifier field from the EAP header
1818dbcf02cSchristos  */
1828dbcf02cSchristos u8 eap_get_id(const struct wpabuf *msg)
1838dbcf02cSchristos {
1848dbcf02cSchristos 	const struct eap_hdr *eap;
1858dbcf02cSchristos 
1868dbcf02cSchristos 	if (wpabuf_len(msg) < sizeof(*eap))
1878dbcf02cSchristos 		return 0;
1888dbcf02cSchristos 
1898dbcf02cSchristos 	eap = wpabuf_head(msg);
1908dbcf02cSchristos 	return eap->identifier;
1918dbcf02cSchristos }
1928dbcf02cSchristos 
1938dbcf02cSchristos 
1948dbcf02cSchristos /**
19536ebd06eSchristos  * eap_get_type - Get EAP Type from wpabuf
1968dbcf02cSchristos  * @msg: Buffer starting with an EAP header
1978dbcf02cSchristos  * Returns: The EAP Type after the EAP header
1988dbcf02cSchristos  */
199*bb618362Schristos enum eap_type eap_get_type(const struct wpabuf *msg)
2008dbcf02cSchristos {
2018dbcf02cSchristos 	if (wpabuf_len(msg) < sizeof(struct eap_hdr) + 1)
2028dbcf02cSchristos 		return EAP_TYPE_NONE;
2038dbcf02cSchristos 
2048dbcf02cSchristos 	return ((const u8 *) wpabuf_head(msg))[sizeof(struct eap_hdr)];
2058dbcf02cSchristos }
206bb610346Schristos 
207bb610346Schristos 
208bb610346Schristos #ifdef CONFIG_ERP
209bb610346Schristos int erp_parse_tlvs(const u8 *pos, const u8 *end, struct erp_tlvs *tlvs,
210bb610346Schristos 		   int stop_at_keyname)
211bb610346Schristos {
212bb610346Schristos 	os_memset(tlvs, 0, sizeof(*tlvs));
213bb610346Schristos 
214bb610346Schristos 	while (pos < end) {
215bb610346Schristos 		u8 tlv_type, tlv_len;
216bb610346Schristos 
217bb610346Schristos 		tlv_type = *pos++;
218bb610346Schristos 		switch (tlv_type) {
219bb610346Schristos 		case EAP_ERP_TV_RRK_LIFETIME:
220bb610346Schristos 		case EAP_ERP_TV_RMSK_LIFETIME:
221bb610346Schristos 			/* 4-octet TV */
222bb610346Schristos 			if (pos + 4 > end) {
223bb610346Schristos 				wpa_printf(MSG_DEBUG, "EAP: Too short TV");
224bb610346Schristos 				return -1;
225bb610346Schristos 			}
226bb610346Schristos 			pos += 4;
227bb610346Schristos 			break;
228bb610346Schristos 		case EAP_ERP_TLV_DOMAIN_NAME:
229bb610346Schristos 		case EAP_ERP_TLV_KEYNAME_NAI:
230bb610346Schristos 		case EAP_ERP_TLV_CRYPTOSUITES:
231bb610346Schristos 		case EAP_ERP_TLV_AUTHORIZATION_INDICATION:
232bb610346Schristos 		case EAP_ERP_TLV_CALLED_STATION_ID:
233bb610346Schristos 		case EAP_ERP_TLV_CALLING_STATION_ID:
234bb610346Schristos 		case EAP_ERP_TLV_NAS_IDENTIFIER:
235bb610346Schristos 		case EAP_ERP_TLV_NAS_IP_ADDRESS:
236bb610346Schristos 		case EAP_ERP_TLV_NAS_IPV6_ADDRESS:
237bb610346Schristos 			if (pos >= end) {
238bb610346Schristos 				wpa_printf(MSG_DEBUG, "EAP: Too short TLV");
239bb610346Schristos 				return -1;
240bb610346Schristos 			}
241bb610346Schristos 			tlv_len = *pos++;
242bb610346Schristos 			if (tlv_len > (unsigned) (end - pos)) {
243bb610346Schristos 				wpa_printf(MSG_DEBUG, "EAP: Truncated TLV");
244bb610346Schristos 				return -1;
245bb610346Schristos 			}
246bb610346Schristos 			if (tlv_type == EAP_ERP_TLV_KEYNAME_NAI) {
247bb610346Schristos 				if (tlvs->keyname) {
248bb610346Schristos 					wpa_printf(MSG_DEBUG,
249bb610346Schristos 						   "EAP: More than one keyName-NAI");
250bb610346Schristos 					return -1;
251bb610346Schristos 				}
252bb610346Schristos 				tlvs->keyname = pos;
253bb610346Schristos 				tlvs->keyname_len = tlv_len;
254bb610346Schristos 				if (stop_at_keyname)
255bb610346Schristos 					return 0;
256bb610346Schristos 			} else if (tlv_type == EAP_ERP_TLV_DOMAIN_NAME) {
257bb610346Schristos 				tlvs->domain = pos;
258bb610346Schristos 				tlvs->domain_len = tlv_len;
259bb610346Schristos 			}
260bb610346Schristos 			pos += tlv_len;
261bb610346Schristos 			break;
262bb610346Schristos 		default:
263bb610346Schristos 			if (tlv_type >= 128 && tlv_type <= 191) {
264bb610346Schristos 				/* Undefined TLV */
265bb610346Schristos 				if (pos >= end) {
266bb610346Schristos 					wpa_printf(MSG_DEBUG,
267bb610346Schristos 						   "EAP: Too short TLV");
268bb610346Schristos 					return -1;
269bb610346Schristos 				}
270bb610346Schristos 				tlv_len = *pos++;
271bb610346Schristos 				if (tlv_len > (unsigned) (end - pos)) {
272bb610346Schristos 					wpa_printf(MSG_DEBUG,
273bb610346Schristos 						   "EAP: Truncated TLV");
274bb610346Schristos 					return -1;
275bb610346Schristos 				}
276bb610346Schristos 				pos += tlv_len;
277bb610346Schristos 				break;
278bb610346Schristos 			}
279bb610346Schristos 			wpa_printf(MSG_DEBUG, "EAP: Unknown TV/TLV type %u",
280bb610346Schristos 				   tlv_type);
281bb610346Schristos 			pos = end;
282bb610346Schristos 			break;
283bb610346Schristos 		}
284bb610346Schristos 	}
285bb610346Schristos 
286bb610346Schristos 	return 0;
287bb610346Schristos }
288bb610346Schristos #endif /* CONFIG_ERP */
289