xref: /dflybsd-src/contrib/wpa_supplicant/src/eap_common/eap_common.c (revision 3a84a4273475ed07d0ab1c2dfeffdfedef35d9cd)
16d49e1aeSJan Lentfer /*
26d49e1aeSJan Lentfer  * EAP common peer/server definitions
3*a1157835SDaniel Fojt  * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
46d49e1aeSJan Lentfer  *
53ff40c12SJohn Marino  * This software may be distributed under the terms of the BSD license.
63ff40c12SJohn Marino  * See README for more details.
76d49e1aeSJan Lentfer  */
86d49e1aeSJan Lentfer 
96d49e1aeSJan Lentfer #include "includes.h"
106d49e1aeSJan Lentfer 
116d49e1aeSJan Lentfer #include "common.h"
126d49e1aeSJan Lentfer #include "eap_defs.h"
136d49e1aeSJan Lentfer #include "eap_common.h"
146d49e1aeSJan Lentfer 
156d49e1aeSJan Lentfer /**
163ff40c12SJohn Marino  * eap_hdr_len_valid - Validate EAP header length field
173ff40c12SJohn Marino  * @msg: EAP frame (starting with EAP header)
183ff40c12SJohn Marino  * @min_payload: Minimum payload length needed
193ff40c12SJohn Marino  * Returns: 1 for valid header, 0 for invalid
203ff40c12SJohn Marino  *
213ff40c12SJohn Marino  * This is a helper function that does minimal validation of EAP messages. The
223ff40c12SJohn Marino  * length field is verified to be large enough to include the header and not
233ff40c12SJohn Marino  * too large to go beyond the end of the buffer.
243ff40c12SJohn Marino  */
eap_hdr_len_valid(const struct wpabuf * msg,size_t min_payload)253ff40c12SJohn Marino int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload)
263ff40c12SJohn Marino {
273ff40c12SJohn Marino 	const struct eap_hdr *hdr;
283ff40c12SJohn Marino 	size_t len;
293ff40c12SJohn Marino 
303ff40c12SJohn Marino 	if (msg == NULL)
313ff40c12SJohn Marino 		return 0;
323ff40c12SJohn Marino 
333ff40c12SJohn Marino 	hdr = wpabuf_head(msg);
343ff40c12SJohn Marino 
353ff40c12SJohn Marino 	if (wpabuf_len(msg) < sizeof(*hdr)) {
363ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "EAP: Too short EAP frame");
373ff40c12SJohn Marino 		return 0;
383ff40c12SJohn Marino 	}
393ff40c12SJohn Marino 
403ff40c12SJohn Marino 	len = be_to_host16(hdr->length);
413ff40c12SJohn Marino 	if (len < sizeof(*hdr) + min_payload || len > wpabuf_len(msg)) {
423ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "EAP: Invalid EAP length");
433ff40c12SJohn Marino 		return 0;
443ff40c12SJohn Marino 	}
453ff40c12SJohn Marino 
463ff40c12SJohn Marino 	return 1;
473ff40c12SJohn Marino }
483ff40c12SJohn Marino 
493ff40c12SJohn Marino 
503ff40c12SJohn Marino /**
516d49e1aeSJan Lentfer  * eap_hdr_validate - Validate EAP header
526d49e1aeSJan Lentfer  * @vendor: Expected EAP Vendor-Id (0 = IETF)
536d49e1aeSJan Lentfer  * @eap_type: Expected EAP type number
546d49e1aeSJan Lentfer  * @msg: EAP frame (starting with EAP header)
556d49e1aeSJan Lentfer  * @plen: Pointer to variable to contain the returned payload length
566d49e1aeSJan Lentfer  * Returns: Pointer to EAP payload (after type field), or %NULL on failure
576d49e1aeSJan Lentfer  *
586d49e1aeSJan Lentfer  * This is a helper function for EAP method implementations. This is usually
596d49e1aeSJan Lentfer  * called in the beginning of struct eap_method::process() function to verify
606d49e1aeSJan Lentfer  * that the received EAP request packet has a valid header. This function is
616d49e1aeSJan Lentfer  * able to process both legacy and expanded EAP headers and in most cases, the
626d49e1aeSJan Lentfer  * caller can just use the returned payload pointer (into *plen) for processing
636d49e1aeSJan Lentfer  * the payload regardless of whether the packet used the expanded EAP header or
646d49e1aeSJan Lentfer  * not.
656d49e1aeSJan Lentfer  */
eap_hdr_validate(int vendor,EapType eap_type,const struct wpabuf * msg,size_t * plen)666d49e1aeSJan Lentfer const u8 * eap_hdr_validate(int vendor, EapType eap_type,
676d49e1aeSJan Lentfer 			    const struct wpabuf *msg, size_t *plen)
686d49e1aeSJan Lentfer {
696d49e1aeSJan Lentfer 	const struct eap_hdr *hdr;
706d49e1aeSJan Lentfer 	const u8 *pos;
716d49e1aeSJan Lentfer 	size_t len;
726d49e1aeSJan Lentfer 
733ff40c12SJohn Marino 	if (!eap_hdr_len_valid(msg, 1))
743ff40c12SJohn Marino 		return NULL;
753ff40c12SJohn Marino 
766d49e1aeSJan Lentfer 	hdr = wpabuf_head(msg);
776d49e1aeSJan Lentfer 	len = be_to_host16(hdr->length);
786d49e1aeSJan Lentfer 	pos = (const u8 *) (hdr + 1);
796d49e1aeSJan Lentfer 
806d49e1aeSJan Lentfer 	if (*pos == EAP_TYPE_EXPANDED) {
816d49e1aeSJan Lentfer 		int exp_vendor;
826d49e1aeSJan Lentfer 		u32 exp_type;
836d49e1aeSJan Lentfer 		if (len < sizeof(*hdr) + 8) {
846d49e1aeSJan Lentfer 			wpa_printf(MSG_INFO, "EAP: Invalid expanded EAP "
856d49e1aeSJan Lentfer 				   "length");
866d49e1aeSJan Lentfer 			return NULL;
876d49e1aeSJan Lentfer 		}
886d49e1aeSJan Lentfer 		pos++;
896d49e1aeSJan Lentfer 		exp_vendor = WPA_GET_BE24(pos);
906d49e1aeSJan Lentfer 		pos += 3;
916d49e1aeSJan Lentfer 		exp_type = WPA_GET_BE32(pos);
926d49e1aeSJan Lentfer 		pos += 4;
936d49e1aeSJan Lentfer 		if (exp_vendor != vendor || exp_type != (u32) eap_type) {
946d49e1aeSJan Lentfer 			wpa_printf(MSG_INFO, "EAP: Invalid expanded frame "
956d49e1aeSJan Lentfer 				   "type");
966d49e1aeSJan Lentfer 			return NULL;
976d49e1aeSJan Lentfer 		}
986d49e1aeSJan Lentfer 
996d49e1aeSJan Lentfer 		*plen = len - sizeof(*hdr) - 8;
1006d49e1aeSJan Lentfer 		return pos;
1016d49e1aeSJan Lentfer 	} else {
1026d49e1aeSJan Lentfer 		if (vendor != EAP_VENDOR_IETF || *pos != eap_type) {
1036d49e1aeSJan Lentfer 			wpa_printf(MSG_INFO, "EAP: Invalid frame type");
1046d49e1aeSJan Lentfer 			return NULL;
1056d49e1aeSJan Lentfer 		}
1066d49e1aeSJan Lentfer 		*plen = len - sizeof(*hdr) - 1;
1076d49e1aeSJan Lentfer 		return pos + 1;
1086d49e1aeSJan Lentfer 	}
1096d49e1aeSJan Lentfer }
1106d49e1aeSJan Lentfer 
1116d49e1aeSJan Lentfer 
1126d49e1aeSJan Lentfer /**
1136d49e1aeSJan Lentfer  * eap_msg_alloc - Allocate a buffer for an EAP message
1146d49e1aeSJan Lentfer  * @vendor: Vendor-Id (0 = IETF)
1156d49e1aeSJan Lentfer  * @type: EAP type
1166d49e1aeSJan Lentfer  * @payload_len: Payload length in bytes (data after Type)
1176d49e1aeSJan Lentfer  * @code: Message Code (EAP_CODE_*)
1186d49e1aeSJan Lentfer  * @identifier: Identifier
1196d49e1aeSJan Lentfer  * Returns: Pointer to the allocated message buffer or %NULL on error
1206d49e1aeSJan Lentfer  *
1216d49e1aeSJan Lentfer  * This function can be used to allocate a buffer for an EAP message and fill
1226d49e1aeSJan Lentfer  * in the EAP header. This function is automatically using expanded EAP header
1236d49e1aeSJan Lentfer  * if the selected Vendor-Id is not IETF. In other words, most EAP methods do
1246d49e1aeSJan Lentfer  * not need to separately select which header type to use when using this
1256d49e1aeSJan Lentfer  * function to allocate the message buffers. The returned buffer has room for
1266d49e1aeSJan Lentfer  * payload_len bytes and has the EAP header and Type field already filled in.
1276d49e1aeSJan Lentfer  */
eap_msg_alloc(int vendor,EapType type,size_t payload_len,u8 code,u8 identifier)1286d49e1aeSJan Lentfer struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len,
1296d49e1aeSJan Lentfer 			      u8 code, u8 identifier)
1306d49e1aeSJan Lentfer {
1316d49e1aeSJan Lentfer 	struct wpabuf *buf;
1326d49e1aeSJan Lentfer 	struct eap_hdr *hdr;
1336d49e1aeSJan Lentfer 	size_t len;
1346d49e1aeSJan Lentfer 
1356d49e1aeSJan Lentfer 	len = sizeof(struct eap_hdr) + (vendor == EAP_VENDOR_IETF ? 1 : 8) +
1366d49e1aeSJan Lentfer 		payload_len;
1376d49e1aeSJan Lentfer 	buf = wpabuf_alloc(len);
1386d49e1aeSJan Lentfer 	if (buf == NULL)
1396d49e1aeSJan Lentfer 		return NULL;
1406d49e1aeSJan Lentfer 
1416d49e1aeSJan Lentfer 	hdr = wpabuf_put(buf, sizeof(*hdr));
1426d49e1aeSJan Lentfer 	hdr->code = code;
1436d49e1aeSJan Lentfer 	hdr->identifier = identifier;
1446d49e1aeSJan Lentfer 	hdr->length = host_to_be16(len);
1456d49e1aeSJan Lentfer 
1466d49e1aeSJan Lentfer 	if (vendor == EAP_VENDOR_IETF) {
1476d49e1aeSJan Lentfer 		wpabuf_put_u8(buf, type);
1486d49e1aeSJan Lentfer 	} else {
1496d49e1aeSJan Lentfer 		wpabuf_put_u8(buf, EAP_TYPE_EXPANDED);
1506d49e1aeSJan Lentfer 		wpabuf_put_be24(buf, vendor);
1516d49e1aeSJan Lentfer 		wpabuf_put_be32(buf, type);
1526d49e1aeSJan Lentfer 	}
1536d49e1aeSJan Lentfer 
1546d49e1aeSJan Lentfer 	return buf;
1556d49e1aeSJan Lentfer }
1566d49e1aeSJan Lentfer 
1576d49e1aeSJan Lentfer 
1586d49e1aeSJan Lentfer /**
1596d49e1aeSJan Lentfer  * eap_update_len - Update EAP header length
1606d49e1aeSJan Lentfer  * @msg: EAP message from eap_msg_alloc
1616d49e1aeSJan Lentfer  *
1626d49e1aeSJan Lentfer  * This function updates the length field in the EAP header to match with the
1636d49e1aeSJan Lentfer  * current length for the buffer. This allows eap_msg_alloc() to be used to
1646d49e1aeSJan Lentfer  * allocate a larger buffer than the exact message length (e.g., if exact
1656d49e1aeSJan Lentfer  * message length is not yet known).
1666d49e1aeSJan Lentfer  */
eap_update_len(struct wpabuf * msg)1676d49e1aeSJan Lentfer void eap_update_len(struct wpabuf *msg)
1686d49e1aeSJan Lentfer {
1696d49e1aeSJan Lentfer 	struct eap_hdr *hdr;
1706d49e1aeSJan Lentfer 	hdr = wpabuf_mhead(msg);
1716d49e1aeSJan Lentfer 	if (wpabuf_len(msg) < sizeof(*hdr))
1726d49e1aeSJan Lentfer 		return;
1736d49e1aeSJan Lentfer 	hdr->length = host_to_be16(wpabuf_len(msg));
1746d49e1aeSJan Lentfer }
1756d49e1aeSJan Lentfer 
1766d49e1aeSJan Lentfer 
1776d49e1aeSJan Lentfer /**
1786d49e1aeSJan Lentfer  * eap_get_id - Get EAP Identifier from wpabuf
1796d49e1aeSJan Lentfer  * @msg: Buffer starting with an EAP header
1806d49e1aeSJan Lentfer  * Returns: The Identifier field from the EAP header
1816d49e1aeSJan Lentfer  */
eap_get_id(const struct wpabuf * msg)1826d49e1aeSJan Lentfer u8 eap_get_id(const struct wpabuf *msg)
1836d49e1aeSJan Lentfer {
1846d49e1aeSJan Lentfer 	const struct eap_hdr *eap;
1856d49e1aeSJan Lentfer 
1866d49e1aeSJan Lentfer 	if (wpabuf_len(msg) < sizeof(*eap))
1876d49e1aeSJan Lentfer 		return 0;
1886d49e1aeSJan Lentfer 
1896d49e1aeSJan Lentfer 	eap = wpabuf_head(msg);
1906d49e1aeSJan Lentfer 	return eap->identifier;
1916d49e1aeSJan Lentfer }
1926d49e1aeSJan Lentfer 
1936d49e1aeSJan Lentfer 
1946d49e1aeSJan Lentfer /**
195*a1157835SDaniel Fojt  * eap_get_type - Get EAP Type from wpabuf
1966d49e1aeSJan Lentfer  * @msg: Buffer starting with an EAP header
1976d49e1aeSJan Lentfer  * Returns: The EAP Type after the EAP header
1986d49e1aeSJan Lentfer  */
eap_get_type(const struct wpabuf * msg)1996d49e1aeSJan Lentfer EapType eap_get_type(const struct wpabuf *msg)
2006d49e1aeSJan Lentfer {
2016d49e1aeSJan Lentfer 	if (wpabuf_len(msg) < sizeof(struct eap_hdr) + 1)
2026d49e1aeSJan Lentfer 		return EAP_TYPE_NONE;
2036d49e1aeSJan Lentfer 
2046d49e1aeSJan Lentfer 	return ((const u8 *) wpabuf_head(msg))[sizeof(struct eap_hdr)];
2056d49e1aeSJan Lentfer }
206*a1157835SDaniel Fojt 
207*a1157835SDaniel Fojt 
208*a1157835SDaniel Fojt #ifdef CONFIG_ERP
erp_parse_tlvs(const u8 * pos,const u8 * end,struct erp_tlvs * tlvs,int stop_at_keyname)209*a1157835SDaniel Fojt int erp_parse_tlvs(const u8 *pos, const u8 *end, struct erp_tlvs *tlvs,
210*a1157835SDaniel Fojt 		   int stop_at_keyname)
211*a1157835SDaniel Fojt {
212*a1157835SDaniel Fojt 	os_memset(tlvs, 0, sizeof(*tlvs));
213*a1157835SDaniel Fojt 
214*a1157835SDaniel Fojt 	while (pos < end) {
215*a1157835SDaniel Fojt 		u8 tlv_type, tlv_len;
216*a1157835SDaniel Fojt 
217*a1157835SDaniel Fojt 		tlv_type = *pos++;
218*a1157835SDaniel Fojt 		switch (tlv_type) {
219*a1157835SDaniel Fojt 		case EAP_ERP_TV_RRK_LIFETIME:
220*a1157835SDaniel Fojt 		case EAP_ERP_TV_RMSK_LIFETIME:
221*a1157835SDaniel Fojt 			/* 4-octet TV */
222*a1157835SDaniel Fojt 			if (pos + 4 > end) {
223*a1157835SDaniel Fojt 				wpa_printf(MSG_DEBUG, "EAP: Too short TV");
224*a1157835SDaniel Fojt 				return -1;
225*a1157835SDaniel Fojt 			}
226*a1157835SDaniel Fojt 			pos += 4;
227*a1157835SDaniel Fojt 			break;
228*a1157835SDaniel Fojt 		case EAP_ERP_TLV_DOMAIN_NAME:
229*a1157835SDaniel Fojt 		case EAP_ERP_TLV_KEYNAME_NAI:
230*a1157835SDaniel Fojt 		case EAP_ERP_TLV_CRYPTOSUITES:
231*a1157835SDaniel Fojt 		case EAP_ERP_TLV_AUTHORIZATION_INDICATION:
232*a1157835SDaniel Fojt 		case EAP_ERP_TLV_CALLED_STATION_ID:
233*a1157835SDaniel Fojt 		case EAP_ERP_TLV_CALLING_STATION_ID:
234*a1157835SDaniel Fojt 		case EAP_ERP_TLV_NAS_IDENTIFIER:
235*a1157835SDaniel Fojt 		case EAP_ERP_TLV_NAS_IP_ADDRESS:
236*a1157835SDaniel Fojt 		case EAP_ERP_TLV_NAS_IPV6_ADDRESS:
237*a1157835SDaniel Fojt 			if (pos >= end) {
238*a1157835SDaniel Fojt 				wpa_printf(MSG_DEBUG, "EAP: Too short TLV");
239*a1157835SDaniel Fojt 				return -1;
240*a1157835SDaniel Fojt 			}
241*a1157835SDaniel Fojt 			tlv_len = *pos++;
242*a1157835SDaniel Fojt 			if (tlv_len > (unsigned) (end - pos)) {
243*a1157835SDaniel Fojt 				wpa_printf(MSG_DEBUG, "EAP: Truncated TLV");
244*a1157835SDaniel Fojt 				return -1;
245*a1157835SDaniel Fojt 			}
246*a1157835SDaniel Fojt 			if (tlv_type == EAP_ERP_TLV_KEYNAME_NAI) {
247*a1157835SDaniel Fojt 				if (tlvs->keyname) {
248*a1157835SDaniel Fojt 					wpa_printf(MSG_DEBUG,
249*a1157835SDaniel Fojt 						   "EAP: More than one keyName-NAI");
250*a1157835SDaniel Fojt 					return -1;
251*a1157835SDaniel Fojt 				}
252*a1157835SDaniel Fojt 				tlvs->keyname = pos;
253*a1157835SDaniel Fojt 				tlvs->keyname_len = tlv_len;
254*a1157835SDaniel Fojt 				if (stop_at_keyname)
255*a1157835SDaniel Fojt 					return 0;
256*a1157835SDaniel Fojt 			} else if (tlv_type == EAP_ERP_TLV_DOMAIN_NAME) {
257*a1157835SDaniel Fojt 				tlvs->domain = pos;
258*a1157835SDaniel Fojt 				tlvs->domain_len = tlv_len;
259*a1157835SDaniel Fojt 			}
260*a1157835SDaniel Fojt 			pos += tlv_len;
261*a1157835SDaniel Fojt 			break;
262*a1157835SDaniel Fojt 		default:
263*a1157835SDaniel Fojt 			if (tlv_type >= 128 && tlv_type <= 191) {
264*a1157835SDaniel Fojt 				/* Undefined TLV */
265*a1157835SDaniel Fojt 				if (pos >= end) {
266*a1157835SDaniel Fojt 					wpa_printf(MSG_DEBUG,
267*a1157835SDaniel Fojt 						   "EAP: Too short TLV");
268*a1157835SDaniel Fojt 					return -1;
269*a1157835SDaniel Fojt 				}
270*a1157835SDaniel Fojt 				tlv_len = *pos++;
271*a1157835SDaniel Fojt 				if (tlv_len > (unsigned) (end - pos)) {
272*a1157835SDaniel Fojt 					wpa_printf(MSG_DEBUG,
273*a1157835SDaniel Fojt 						   "EAP: Truncated TLV");
274*a1157835SDaniel Fojt 					return -1;
275*a1157835SDaniel Fojt 				}
276*a1157835SDaniel Fojt 				pos += tlv_len;
277*a1157835SDaniel Fojt 				break;
278*a1157835SDaniel Fojt 			}
279*a1157835SDaniel Fojt 			wpa_printf(MSG_DEBUG, "EAP: Unknown TV/TLV type %u",
280*a1157835SDaniel Fojt 				   tlv_type);
281*a1157835SDaniel Fojt 			pos = end;
282*a1157835SDaniel Fojt 			break;
283*a1157835SDaniel Fojt 		}
284*a1157835SDaniel Fojt 	}
285*a1157835SDaniel Fojt 
286*a1157835SDaniel Fojt 	return 0;
287*a1157835SDaniel Fojt }
288*a1157835SDaniel Fojt #endif /* CONFIG_ERP */
289