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