1  /*
2   * EAP common peer/server definitions
3   * Copyright (c) 2004-2014, Jouni Malinen <j@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   */
eap_hdr_len_valid(const struct wpabuf * msg,size_t min_payload)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   */
eap_hdr_validate(int vendor,enum eap_type eap_type,const struct wpabuf * msg,size_t * plen)66  const u8 * eap_hdr_validate(int vendor, enum eap_type 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   */
eap_msg_alloc(int vendor,enum eap_type type,size_t payload_len,u8 code,u8 identifier)128  struct wpabuf * eap_msg_alloc(int vendor, enum eap_type type,
129  			      size_t payload_len, 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   */
eap_update_len(struct wpabuf * msg)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   */
eap_get_id(const struct wpabuf * msg)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   */
eap_get_type(const struct wpabuf * msg)199  enum eap_type 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
erp_parse_tlvs(const u8 * pos,const u8 * end,struct erp_tlvs * tlvs,int stop_at_keyname)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