/src/hostap/src/eap_common/eap_common.c
Line | Count | Source (jump to first uncovered line) |
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 | | */ |
25 | | int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload) |
26 | 785k | { |
27 | 785k | const struct eap_hdr *hdr; |
28 | 785k | size_t len; |
29 | | |
30 | 785k | if (msg == NULL) |
31 | 0 | return 0; |
32 | | |
33 | 785k | hdr = wpabuf_head(msg); |
34 | | |
35 | 785k | if (wpabuf_len(msg) < sizeof(*hdr)) { |
36 | 737k | wpa_printf(MSG_INFO, "EAP: Too short EAP frame"); |
37 | 737k | return 0; |
38 | 737k | } |
39 | | |
40 | 48.3k | len = be_to_host16(hdr->length); |
41 | 48.3k | if (len < sizeof(*hdr) + min_payload || len > wpabuf_len(msg)) { |
42 | 10.8k | wpa_printf(MSG_INFO, "EAP: Invalid EAP length"); |
43 | 10.8k | return 0; |
44 | 10.8k | } |
45 | | |
46 | 37.5k | return 1; |
47 | 48.3k | } |
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, enum eap_type eap_type, |
67 | | const struct wpabuf *msg, size_t *plen) |
68 | 785k | { |
69 | 785k | const struct eap_hdr *hdr; |
70 | 785k | const u8 *pos; |
71 | 785k | size_t len; |
72 | | |
73 | 785k | if (!eap_hdr_len_valid(msg, 1)) |
74 | 748k | return NULL; |
75 | | |
76 | 37.5k | hdr = wpabuf_head(msg); |
77 | 37.5k | len = be_to_host16(hdr->length); |
78 | 37.5k | pos = (const u8 *) (hdr + 1); |
79 | | |
80 | 37.5k | if (*pos == EAP_TYPE_EXPANDED) { |
81 | 2.12k | int exp_vendor; |
82 | 2.12k | u32 exp_type; |
83 | 2.12k | if (len < sizeof(*hdr) + 8) { |
84 | 202 | wpa_printf(MSG_INFO, "EAP: Invalid expanded EAP " |
85 | 202 | "length"); |
86 | 202 | return NULL; |
87 | 202 | } |
88 | 1.91k | pos++; |
89 | 1.91k | exp_vendor = WPA_GET_BE24(pos); |
90 | 1.91k | pos += 3; |
91 | 1.91k | exp_type = WPA_GET_BE32(pos); |
92 | 1.91k | pos += 4; |
93 | 1.91k | if (exp_vendor != vendor || exp_type != (u32) eap_type) { |
94 | 938 | wpa_printf(MSG_INFO, "EAP: Invalid expanded frame " |
95 | 938 | "type"); |
96 | 938 | return NULL; |
97 | 938 | } |
98 | | |
99 | 981 | *plen = len - sizeof(*hdr) - 8; |
100 | 981 | return pos; |
101 | 35.4k | } else { |
102 | 35.4k | if (vendor != EAP_VENDOR_IETF || *pos != eap_type) { |
103 | 753 | wpa_printf(MSG_INFO, "EAP: Invalid frame type"); |
104 | 753 | return NULL; |
105 | 753 | } |
106 | 34.6k | *plen = len - sizeof(*hdr) - 1; |
107 | 34.6k | return pos + 1; |
108 | 35.4k | } |
109 | 37.5k | } |
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, enum eap_type type, |
129 | | size_t payload_len, u8 code, u8 identifier) |
130 | 20.0k | { |
131 | 20.0k | struct wpabuf *buf; |
132 | 20.0k | struct eap_hdr *hdr; |
133 | 20.0k | size_t len; |
134 | | |
135 | 20.0k | len = sizeof(struct eap_hdr) + (vendor == EAP_VENDOR_IETF ? 1 : 8) + |
136 | 20.0k | payload_len; |
137 | 20.0k | buf = wpabuf_alloc(len); |
138 | 20.0k | if (buf == NULL) |
139 | 0 | return NULL; |
140 | | |
141 | 20.0k | hdr = wpabuf_put(buf, sizeof(*hdr)); |
142 | 20.0k | hdr->code = code; |
143 | 20.0k | hdr->identifier = identifier; |
144 | 20.0k | hdr->length = host_to_be16(len); |
145 | | |
146 | 20.0k | if (vendor == EAP_VENDOR_IETF) { |
147 | 20.0k | wpabuf_put_u8(buf, type); |
148 | 20.0k | } else { |
149 | 0 | wpabuf_put_u8(buf, EAP_TYPE_EXPANDED); |
150 | 0 | wpabuf_put_be24(buf, vendor); |
151 | 0 | wpabuf_put_be32(buf, type); |
152 | 0 | } |
153 | | |
154 | 20.0k | return buf; |
155 | 20.0k | } |
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 | 0 | { |
169 | 0 | struct eap_hdr *hdr; |
170 | 0 | hdr = wpabuf_mhead(msg); |
171 | 0 | if (wpabuf_len(msg) < sizeof(*hdr)) |
172 | 0 | return; |
173 | 0 | hdr->length = host_to_be16(wpabuf_len(msg)); |
174 | 0 | } |
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 | 33.4k | { |
184 | 33.4k | const struct eap_hdr *eap; |
185 | | |
186 | 33.4k | if (wpabuf_len(msg) < sizeof(*eap)) |
187 | 0 | return 0; |
188 | | |
189 | 33.4k | eap = wpabuf_head(msg); |
190 | 33.4k | return eap->identifier; |
191 | 33.4k | } |
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 | | enum eap_type eap_get_type(const struct wpabuf *msg) |
200 | 0 | { |
201 | 0 | if (wpabuf_len(msg) < sizeof(struct eap_hdr) + 1) |
202 | 0 | return EAP_TYPE_NONE; |
203 | | |
204 | 0 | return ((const u8 *) wpabuf_head(msg))[sizeof(struct eap_hdr)]; |
205 | 0 | } |
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 */ |