Home | History | Annotate | Download | only in eap_common
      1 /*
      2  * EAP common peer/server definitions
      3  * Copyright (c) 2004-2014, Jouni Malinen <j (at) w1.fi>
      4  *
      5  * This software may be distributed under the terms of the BSD license.
      6  * See README for more details.
      7  */
      8 
      9 #include "includes.h"
     10 
     11 #include "common.h"
     12 #include "eap_defs.h"
     13 #include "eap_common.h"
     14 
     15 /**
     16  * eap_hdr_len_valid - Validate EAP header length field
     17  * @msg: EAP frame (starting with EAP header)
     18  * @min_payload: Minimum payload length needed
     19  * Returns: 1 for valid header, 0 for invalid
     20  *
     21  * This is a helper function that does minimal validation of EAP messages. The
     22  * length field is verified to be large enough to include the header and not
     23  * too large to go beyond the end of the buffer.
     24  */
     25 int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload)
     26 {
     27 	const struct eap_hdr *hdr;
     28 	size_t len;
     29 
     30 	if (msg == NULL)
     31 		return 0;
     32 
     33 	hdr = wpabuf_head(msg);
     34 
     35 	if (wpabuf_len(msg) < sizeof(*hdr)) {
     36 		wpa_printf(MSG_INFO, "EAP: Too short EAP frame");
     37 		return 0;
     38 	}
     39 
     40 	len = be_to_host16(hdr->length);
     41 	if (len < sizeof(*hdr) + min_payload || len > wpabuf_len(msg)) {
     42 		wpa_printf(MSG_INFO, "EAP: Invalid EAP length");
     43 		return 0;
     44 	}
     45 
     46 	return 1;
     47 }
     48 
     49 
     50 /**
     51  * eap_hdr_validate - Validate EAP header
     52  * @vendor: Expected EAP Vendor-Id (0 = IETF)
     53  * @eap_type: Expected EAP type number
     54  * @msg: EAP frame (starting with EAP header)
     55  * @plen: Pointer to variable to contain the returned payload length
     56  * Returns: Pointer to EAP payload (after type field), or %NULL on failure
     57  *
     58  * This is a helper function for EAP method implementations. This is usually
     59  * called in the beginning of struct eap_method::process() function to verify
     60  * that the received EAP request packet has a valid header. This function is
     61  * able to process both legacy and expanded EAP headers and in most cases, the
     62  * caller can just use the returned payload pointer (into *plen) for processing
     63  * the payload regardless of whether the packet used the expanded EAP header or
     64  * not.
     65  */
     66 const u8 * eap_hdr_validate(int vendor, EapType eap_type,
     67 			    const struct wpabuf *msg, size_t *plen)
     68 {
     69 	const struct eap_hdr *hdr;
     70 	const u8 *pos;
     71 	size_t len;
     72 
     73 	if (!eap_hdr_len_valid(msg, 1))
     74 		return NULL;
     75 
     76 	hdr = wpabuf_head(msg);
     77 	len = be_to_host16(hdr->length);
     78 	pos = (const u8 *) (hdr + 1);
     79 
     80 	if (*pos == EAP_TYPE_EXPANDED) {
     81 		int exp_vendor;
     82 		u32 exp_type;
     83 		if (len < sizeof(*hdr) + 8) {
     84 			wpa_printf(MSG_INFO, "EAP: Invalid expanded EAP "
     85 				   "length");
     86 			return NULL;
     87 		}
     88 		pos++;
     89 		exp_vendor = WPA_GET_BE24(pos);
     90 		pos += 3;
     91 		exp_type = WPA_GET_BE32(pos);
     92 		pos += 4;
     93 		if (exp_vendor != vendor || exp_type != (u32) eap_type) {
     94 			wpa_printf(MSG_INFO, "EAP: Invalid expanded frame "
     95 				   "type");
     96 			return NULL;
     97 		}
     98 
     99 		*plen = len - sizeof(*hdr) - 8;
    100 		return pos;
    101 	} else {
    102 		if (vendor != EAP_VENDOR_IETF || *pos != eap_type) {
    103 			wpa_printf(MSG_INFO, "EAP: Invalid frame type");
    104 			return NULL;
    105 		}
    106 		*plen = len - sizeof(*hdr) - 1;
    107 		return pos + 1;
    108 	}
    109 }
    110 
    111 
    112 /**
    113  * eap_msg_alloc - Allocate a buffer for an EAP message
    114  * @vendor: Vendor-Id (0 = IETF)
    115  * @type: EAP type
    116  * @payload_len: Payload length in bytes (data after Type)
    117  * @code: Message Code (EAP_CODE_*)
    118  * @identifier: Identifier
    119  * Returns: Pointer to the allocated message buffer or %NULL on error
    120  *
    121  * This function can be used to allocate a buffer for an EAP message and fill
    122  * in the EAP header. This function is automatically using expanded EAP header
    123  * if the selected Vendor-Id is not IETF. In other words, most EAP methods do
    124  * not need to separately select which header type to use when using this
    125  * function to allocate the message buffers. The returned buffer has room for
    126  * payload_len bytes and has the EAP header and Type field already filled in.
    127  */
    128 struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len,
    129 			      u8 code, u8 identifier)
    130 {
    131 	struct wpabuf *buf;
    132 	struct eap_hdr *hdr;
    133 	size_t len;
    134 
    135 	len = sizeof(struct eap_hdr) + (vendor == EAP_VENDOR_IETF ? 1 : 8) +
    136 		payload_len;
    137 	buf = wpabuf_alloc(len);
    138 	if (buf == NULL)
    139 		return NULL;
    140 
    141 	hdr = wpabuf_put(buf, sizeof(*hdr));
    142 	hdr->code = code;
    143 	hdr->identifier = identifier;
    144 	hdr->length = host_to_be16(len);
    145 
    146 	if (vendor == EAP_VENDOR_IETF) {
    147 		wpabuf_put_u8(buf, type);
    148 	} else {
    149 		wpabuf_put_u8(buf, EAP_TYPE_EXPANDED);
    150 		wpabuf_put_be24(buf, vendor);
    151 		wpabuf_put_be32(buf, type);
    152 	}
    153 
    154 	return buf;
    155 }
    156 
    157 
    158 /**
    159  * eap_update_len - Update EAP header length
    160  * @msg: EAP message from eap_msg_alloc
    161  *
    162  * This function updates the length field in the EAP header to match with the
    163  * current length for the buffer. This allows eap_msg_alloc() to be used to
    164  * allocate a larger buffer than the exact message length (e.g., if exact
    165  * message length is not yet known).
    166  */
    167 void eap_update_len(struct wpabuf *msg)
    168 {
    169 	struct eap_hdr *hdr;
    170 	hdr = wpabuf_mhead(msg);
    171 	if (wpabuf_len(msg) < sizeof(*hdr))
    172 		return;
    173 	hdr->length = host_to_be16(wpabuf_len(msg));
    174 }
    175 
    176 
    177 /**
    178  * eap_get_id - Get EAP Identifier from wpabuf
    179  * @msg: Buffer starting with an EAP header
    180  * Returns: The Identifier field from the EAP header
    181  */
    182 u8 eap_get_id(const struct wpabuf *msg)
    183 {
    184 	const struct eap_hdr *eap;
    185 
    186 	if (wpabuf_len(msg) < sizeof(*eap))
    187 		return 0;
    188 
    189 	eap = wpabuf_head(msg);
    190 	return eap->identifier;
    191 }
    192 
    193 
    194 /**
    195  * eap_get_type - Get EAP Type from wpabuf
    196  * @msg: Buffer starting with an EAP header
    197  * Returns: The EAP Type after the EAP header
    198  */
    199 EapType eap_get_type(const struct wpabuf *msg)
    200 {
    201 	if (wpabuf_len(msg) < sizeof(struct eap_hdr) + 1)
    202 		return EAP_TYPE_NONE;
    203 
    204 	return ((const u8 *) wpabuf_head(msg))[sizeof(struct eap_hdr)];
    205 }
    206 
    207 
    208 #ifdef CONFIG_ERP
    209 int erp_parse_tlvs(const u8 *pos, const u8 *end, struct erp_tlvs *tlvs,
    210 		   int stop_at_keyname)
    211 {
    212 	os_memset(tlvs, 0, sizeof(*tlvs));
    213 
    214 	while (pos < end) {
    215 		u8 tlv_type, tlv_len;
    216 
    217 		tlv_type = *pos++;
    218 		switch (tlv_type) {
    219 		case EAP_ERP_TV_RRK_LIFETIME:
    220 		case EAP_ERP_TV_RMSK_LIFETIME:
    221 			/* 4-octet TV */
    222 			if (pos + 4 > end) {
    223 				wpa_printf(MSG_DEBUG, "EAP: Too short TV");
    224 				return -1;
    225 			}
    226 			pos += 4;
    227 			break;
    228 		case EAP_ERP_TLV_DOMAIN_NAME:
    229 		case EAP_ERP_TLV_KEYNAME_NAI:
    230 		case EAP_ERP_TLV_CRYPTOSUITES:
    231 		case EAP_ERP_TLV_AUTHORIZATION_INDICATION:
    232 		case EAP_ERP_TLV_CALLED_STATION_ID:
    233 		case EAP_ERP_TLV_CALLING_STATION_ID:
    234 		case EAP_ERP_TLV_NAS_IDENTIFIER:
    235 		case EAP_ERP_TLV_NAS_IP_ADDRESS:
    236 		case EAP_ERP_TLV_NAS_IPV6_ADDRESS:
    237 			if (pos >= end) {
    238 				wpa_printf(MSG_DEBUG, "EAP: Too short TLV");
    239 				return -1;
    240 			}
    241 			tlv_len = *pos++;
    242 			if (tlv_len > (unsigned) (end - pos)) {
    243 				wpa_printf(MSG_DEBUG, "EAP: Truncated TLV");
    244 				return -1;
    245 			}
    246 			if (tlv_type == EAP_ERP_TLV_KEYNAME_NAI) {
    247 				if (tlvs->keyname) {
    248 					wpa_printf(MSG_DEBUG,
    249 						   "EAP: More than one keyName-NAI");
    250 					return -1;
    251 				}
    252 				tlvs->keyname = pos;
    253 				tlvs->keyname_len = tlv_len;
    254 				if (stop_at_keyname)
    255 					return 0;
    256 			} else if (tlv_type == EAP_ERP_TLV_DOMAIN_NAME) {
    257 				tlvs->domain = pos;
    258 				tlvs->domain_len = tlv_len;
    259 			}
    260 			pos += tlv_len;
    261 			break;
    262 		default:
    263 			if (tlv_type >= 128 && tlv_type <= 191) {
    264 				/* Undefined TLV */
    265 				if (pos >= end) {
    266 					wpa_printf(MSG_DEBUG,
    267 						   "EAP: Too short TLV");
    268 					return -1;
    269 				}
    270 				tlv_len = *pos++;
    271 				if (tlv_len > (unsigned) (end - pos)) {
    272 					wpa_printf(MSG_DEBUG,
    273 						   "EAP: Truncated TLV");
    274 					return -1;
    275 				}
    276 				pos += tlv_len;
    277 				break;
    278 			}
    279 			wpa_printf(MSG_DEBUG, "EAP: Unknown TV/TLV type %u",
    280 				   tlv_type);
    281 			pos = end;
    282 			break;
    283 		}
    284 	}
    285 
    286 	return 0;
    287 }
    288 #endif /* CONFIG_ERP */
    289