/src/strongswan/src/libcharon/encoding/payloads/eap_payload.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2012 Tobias Brunner |
3 | | * Copyright (C) 2005-2010 Martin Willi |
4 | | * Copyright (C) 2005 Jan Hutter |
5 | | * |
6 | | * Copyright (C) secunet Security Networks AG |
7 | | * |
8 | | * This program is free software; you can redistribute it and/or modify it |
9 | | * under the terms of the GNU General Public License as published by the |
10 | | * Free Software Foundation; either version 2 of the License, or (at your |
11 | | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. |
12 | | * |
13 | | * This program is distributed in the hope that it will be useful, but |
14 | | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
15 | | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
16 | | * for more details. |
17 | | */ |
18 | | |
19 | | #include <stddef.h> |
20 | | |
21 | | #include "eap_payload.h" |
22 | | |
23 | | #include <daemon.h> |
24 | | #include <eap/eap.h> |
25 | | #include <bio/bio_writer.h> |
26 | | |
27 | | typedef struct private_eap_payload_t private_eap_payload_t; |
28 | | |
29 | | /** |
30 | | * Private data of an eap_payload_t object. |
31 | | * |
32 | | */ |
33 | | struct private_eap_payload_t { |
34 | | /** |
35 | | * Public eap_payload_t interface. |
36 | | */ |
37 | | eap_payload_t public; |
38 | | |
39 | | /** |
40 | | * Next payload type. |
41 | | */ |
42 | | uint8_t next_payload; |
43 | | |
44 | | /** |
45 | | * Critical flag. |
46 | | */ |
47 | | bool critical; |
48 | | |
49 | | /** |
50 | | * Reserved bits |
51 | | */ |
52 | | bool reserved[7]; |
53 | | |
54 | | /** |
55 | | * Length of this payload. |
56 | | */ |
57 | | uint16_t payload_length; |
58 | | |
59 | | /** |
60 | | * EAP message data, if available |
61 | | */ |
62 | | chunk_t data; |
63 | | }; |
64 | | |
65 | | /** |
66 | | * Encoding rules to parse or generate a EAP payload. |
67 | | * |
68 | | * The defined offsets are the positions in a object of type |
69 | | * private_eap_payload_t. |
70 | | * |
71 | | */ |
72 | | static encoding_rule_t encodings[] = { |
73 | | /* 1 Byte next payload type, stored in the field next_payload */ |
74 | | { U_INT_8, offsetof(private_eap_payload_t, next_payload) }, |
75 | | /* the critical bit */ |
76 | | { FLAG, offsetof(private_eap_payload_t, critical) }, |
77 | | /* 7 Bit reserved bits, nowhere stored */ |
78 | | { RESERVED_BIT, offsetof(private_eap_payload_t, reserved[0]) }, |
79 | | { RESERVED_BIT, offsetof(private_eap_payload_t, reserved[1]) }, |
80 | | { RESERVED_BIT, offsetof(private_eap_payload_t, reserved[2]) }, |
81 | | { RESERVED_BIT, offsetof(private_eap_payload_t, reserved[3]) }, |
82 | | { RESERVED_BIT, offsetof(private_eap_payload_t, reserved[4]) }, |
83 | | { RESERVED_BIT, offsetof(private_eap_payload_t, reserved[5]) }, |
84 | | { RESERVED_BIT, offsetof(private_eap_payload_t, reserved[6]) }, |
85 | | /* Length of the whole payload*/ |
86 | | { PAYLOAD_LENGTH, offsetof(private_eap_payload_t, payload_length) }, |
87 | | /* chunk to data, starting at "code" */ |
88 | | { CHUNK_DATA, offsetof(private_eap_payload_t, data) }, |
89 | | }; |
90 | | |
91 | | /* |
92 | | 1 2 3 |
93 | | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
94 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
95 | | ! Next Payload !C! RESERVED ! Payload Length ! |
96 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
97 | | ! Code ! Identifier ! Length ! |
98 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
99 | | ! Type ! Type_Data... |
100 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- |
101 | | */ |
102 | | |
103 | | METHOD(payload_t, verify, status_t, |
104 | | private_eap_payload_t *this) |
105 | 1.02k | { |
106 | 1.02k | uint16_t length; |
107 | 1.02k | uint8_t code; |
108 | | |
109 | 1.02k | if (this->data.len < 4) |
110 | 8 | { |
111 | 8 | DBG1(DBG_ENC, "EAP payloads EAP message too short (%d)", this->data.len); |
112 | 8 | return FAILED; |
113 | 8 | } |
114 | 1.01k | length = untoh16(this->data.ptr + 2); |
115 | 1.01k | if (this->data.len != length) |
116 | 27 | { |
117 | 27 | DBG1(DBG_ENC, "EAP payload length (%d) does not match contained " |
118 | 27 | "message length (%d)", this->data.len, length); |
119 | 27 | return FAILED; |
120 | 27 | } |
121 | 990 | code = this->data.ptr[0]; |
122 | 990 | switch (code) |
123 | 990 | { |
124 | 268 | case EAP_REQUEST: |
125 | 471 | case EAP_RESPONSE: |
126 | 471 | { |
127 | 471 | if (this->data.len < 4) |
128 | 0 | { |
129 | 0 | DBG1(DBG_ENC, "EAP Request/Response does not have any data"); |
130 | 0 | return FAILED; |
131 | 0 | } |
132 | 471 | break; |
133 | 471 | } |
134 | 471 | case EAP_SUCCESS: |
135 | 518 | case EAP_FAILURE: |
136 | 518 | { |
137 | 518 | if (this->data.len != 4) |
138 | 10 | { |
139 | 10 | DBG1(DBG_ENC, "EAP Success/Failure has data"); |
140 | 10 | return FAILED; |
141 | 10 | } |
142 | 508 | break; |
143 | 518 | } |
144 | 508 | default: |
145 | 1 | return FAILED; |
146 | 990 | } |
147 | 979 | return SUCCESS; |
148 | 990 | } |
149 | | |
150 | | METHOD(payload_t, get_encoding_rules, int, |
151 | | private_eap_payload_t *this, encoding_rule_t **rules) |
152 | 1.06k | { |
153 | 1.06k | *rules = encodings; |
154 | 1.06k | return countof(encodings); |
155 | 1.06k | } |
156 | | |
157 | | METHOD(payload_t, get_header_length, int, |
158 | | private_eap_payload_t *this) |
159 | 12.4k | { |
160 | 12.4k | return 4; |
161 | 12.4k | } |
162 | | |
163 | | METHOD(payload_t, get_payload_type, payload_type_t, |
164 | | private_eap_payload_t *this) |
165 | 1.86k | { |
166 | 1.86k | return PLV2_EAP; |
167 | 1.86k | } |
168 | | |
169 | | METHOD(payload_t, get_next_type, payload_type_t, |
170 | | private_eap_payload_t *this) |
171 | 979 | { |
172 | 979 | return (this->next_payload); |
173 | 979 | } |
174 | | |
175 | | METHOD(payload_t, set_next_type, void, |
176 | | private_eap_payload_t *this, payload_type_t type) |
177 | 0 | { |
178 | 0 | this->next_payload = type; |
179 | 0 | } |
180 | | |
181 | | METHOD(payload_t, get_length, size_t, |
182 | | private_eap_payload_t *this) |
183 | 0 | { |
184 | 0 | return this->payload_length; |
185 | 0 | } |
186 | | |
187 | | METHOD(eap_payload_t, get_data, chunk_t, |
188 | | private_eap_payload_t *this) |
189 | 0 | { |
190 | 0 | return this->data; |
191 | 0 | } |
192 | | |
193 | | METHOD(eap_payload_t, set_data, void, |
194 | | private_eap_payload_t *this, chunk_t data) |
195 | 0 | { |
196 | 0 | free(this->data.ptr); |
197 | 0 | this->data = chunk_clone(data); |
198 | 0 | this->payload_length = this->data.len + 4; |
199 | 0 | } |
200 | | |
201 | | METHOD(eap_payload_t, get_code, eap_code_t, |
202 | | private_eap_payload_t *this) |
203 | 0 | { |
204 | 0 | if (this->data.len > 0) |
205 | 0 | { |
206 | 0 | return this->data.ptr[0]; |
207 | 0 | } |
208 | | /* should not happen, as it is verified */ |
209 | 0 | return 0; |
210 | 0 | } |
211 | | |
212 | | METHOD(eap_payload_t, get_identifier, uint8_t, |
213 | | private_eap_payload_t *this) |
214 | 0 | { |
215 | 0 | if (this->data.len > 1) |
216 | 0 | { |
217 | 0 | return this->data.ptr[1]; |
218 | 0 | } |
219 | | /* should not happen, as it is verified */ |
220 | 0 | return 0; |
221 | 0 | } |
222 | | |
223 | | /** |
224 | | * Get the current type at the given offset into this->data. |
225 | | * @return the new offset or 0 if failed |
226 | | */ |
227 | | static size_t extract_type(private_eap_payload_t *this, size_t offset, |
228 | | eap_type_t *type, pen_t *vendor) |
229 | 0 | { |
230 | 0 | if (this->data.len > offset) |
231 | 0 | { |
232 | 0 | *vendor = 0; |
233 | 0 | *type = this->data.ptr[offset]; |
234 | 0 | if (*type != EAP_EXPANDED) |
235 | 0 | { |
236 | 0 | return offset + 1; |
237 | 0 | } |
238 | 0 | if (this->data.len >= offset + 8) |
239 | 0 | { |
240 | 0 | *vendor = untoh32(this->data.ptr + offset) & 0x00FFFFFF; |
241 | 0 | *type = untoh32(this->data.ptr + offset + 4); |
242 | 0 | return offset + 8; |
243 | 0 | } |
244 | 0 | } |
245 | 0 | return 0; |
246 | 0 | } |
247 | | |
248 | | METHOD(eap_payload_t, get_type, eap_type_t, |
249 | | private_eap_payload_t *this, pen_t *vendor) |
250 | 0 | { |
251 | 0 | eap_type_t type; |
252 | |
|
253 | 0 | *vendor = 0; |
254 | 0 | if (extract_type(this, 4, &type, vendor)) |
255 | 0 | { |
256 | 0 | return type; |
257 | 0 | } |
258 | 0 | return 0; |
259 | 0 | } |
260 | | |
261 | | /** |
262 | | * Type enumerator |
263 | | */ |
264 | | typedef struct { |
265 | | /** public interface */ |
266 | | enumerator_t public; |
267 | | /** payload */ |
268 | | private_eap_payload_t *payload; |
269 | | /** current offset in the data */ |
270 | | size_t offset; |
271 | | } type_enumerator_t; |
272 | | |
273 | | METHOD(enumerator_t, enumerate_types, bool, |
274 | | type_enumerator_t *this, va_list args) |
275 | 0 | { |
276 | 0 | eap_type_t *type; |
277 | 0 | pen_t *vendor; |
278 | |
|
279 | 0 | VA_ARGS_VGET(args, type, vendor); |
280 | 0 | this->offset = extract_type(this->payload, this->offset, type, vendor); |
281 | 0 | return this->offset; |
282 | 0 | } |
283 | | |
284 | | METHOD(eap_payload_t, get_types, enumerator_t*, |
285 | | private_eap_payload_t *this) |
286 | 0 | { |
287 | 0 | type_enumerator_t *enumerator; |
288 | 0 | eap_type_t type; |
289 | 0 | pen_t vendor; |
290 | 0 | size_t offset; |
291 | |
|
292 | 0 | offset = extract_type(this, 4, &type, &vendor); |
293 | 0 | if (offset && type == EAP_NAK) |
294 | 0 | { |
295 | 0 | INIT(enumerator, |
296 | 0 | .public = { |
297 | 0 | .enumerate = enumerator_enumerate_default, |
298 | 0 | .venumerate = _enumerate_types, |
299 | 0 | .destroy = (void*)free, |
300 | 0 | }, |
301 | 0 | .payload = this, |
302 | 0 | .offset = offset, |
303 | 0 | ); |
304 | 0 | return &enumerator->public; |
305 | 0 | } |
306 | 0 | return enumerator_create_empty(); |
307 | 0 | } |
308 | | |
309 | | METHOD(eap_payload_t, is_expanded, bool, |
310 | | private_eap_payload_t *this) |
311 | 0 | { |
312 | 0 | return this->data.len > 4 ? this->data.ptr[4] == EAP_EXPANDED : FALSE; |
313 | 0 | } |
314 | | |
315 | | METHOD2(payload_t, eap_payload_t, destroy, void, |
316 | | private_eap_payload_t *this) |
317 | 1.06k | { |
318 | 1.06k | chunk_free(&this->data); |
319 | 1.06k | free(this); |
320 | 1.06k | } |
321 | | |
322 | | /* |
323 | | * Described in header |
324 | | */ |
325 | | eap_payload_t *eap_payload_create() |
326 | 1.06k | { |
327 | 1.06k | private_eap_payload_t *this; |
328 | | |
329 | 1.06k | INIT(this, |
330 | 1.06k | .public = { |
331 | 1.06k | .payload_interface = { |
332 | 1.06k | .verify = _verify, |
333 | 1.06k | .get_encoding_rules = _get_encoding_rules, |
334 | 1.06k | .get_header_length = _get_header_length, |
335 | 1.06k | .get_length = _get_length, |
336 | 1.06k | .get_next_type = _get_next_type, |
337 | 1.06k | .set_next_type = _set_next_type, |
338 | 1.06k | .get_type = _get_payload_type, |
339 | 1.06k | .destroy = _destroy, |
340 | 1.06k | }, |
341 | 1.06k | .get_data = _get_data, |
342 | 1.06k | .set_data = _set_data, |
343 | 1.06k | .get_code = _get_code, |
344 | 1.06k | .get_identifier = _get_identifier, |
345 | 1.06k | .get_type = _get_type, |
346 | 1.06k | .get_types = _get_types, |
347 | 1.06k | .is_expanded = _is_expanded, |
348 | 1.06k | .destroy = _destroy, |
349 | 1.06k | }, |
350 | 1.06k | .next_payload = PL_NONE, |
351 | 1.06k | .payload_length = get_header_length(this), |
352 | 1.06k | ); |
353 | 1.06k | return &this->public; |
354 | 1.06k | } |
355 | | |
356 | | /* |
357 | | * Described in header |
358 | | */ |
359 | | eap_payload_t *eap_payload_create_data(chunk_t data) |
360 | 0 | { |
361 | 0 | eap_payload_t *this = eap_payload_create(); |
362 | |
|
363 | 0 | this->set_data(this, data); |
364 | 0 | return this; |
365 | 0 | } |
366 | | |
367 | | /* |
368 | | * Described in header |
369 | | */ |
370 | | eap_payload_t *eap_payload_create_data_own(chunk_t data) |
371 | 0 | { |
372 | 0 | eap_payload_t *this = eap_payload_create(); |
373 | |
|
374 | 0 | this->set_data(this, data); |
375 | 0 | free(data.ptr); |
376 | 0 | return this; |
377 | 0 | } |
378 | | |
379 | | /* |
380 | | * Described in header |
381 | | */ |
382 | | eap_payload_t *eap_payload_create_code(eap_code_t code, uint8_t identifier) |
383 | 0 | { |
384 | 0 | chunk_t data; |
385 | |
|
386 | 0 | data = chunk_from_chars(code, identifier, 0, 0); |
387 | 0 | htoun16(data.ptr + 2, data.len); |
388 | 0 | return eap_payload_create_data(data); |
389 | 0 | } |
390 | | |
391 | | /** |
392 | | * Write the given type either expanded or not |
393 | | */ |
394 | | static void write_type(bio_writer_t *writer, eap_type_t type, pen_t vendor, |
395 | | bool expanded) |
396 | 0 | { |
397 | 0 | if (expanded) |
398 | 0 | { |
399 | 0 | writer->write_uint8(writer, EAP_EXPANDED); |
400 | 0 | writer->write_uint24(writer, vendor); |
401 | 0 | writer->write_uint32(writer, type); |
402 | 0 | } |
403 | 0 | else |
404 | 0 | { |
405 | 0 | writer->write_uint8(writer, type); |
406 | 0 | } |
407 | 0 | } |
408 | | |
409 | | /* |
410 | | * Described in header |
411 | | */ |
412 | | eap_payload_t *eap_payload_create_nak(uint8_t identifier, eap_type_t type, |
413 | | pen_t vendor, bool expanded) |
414 | 0 | { |
415 | 0 | enumerator_t *enumerator; |
416 | 0 | eap_type_t reg_type; |
417 | 0 | pen_t reg_vendor; |
418 | 0 | bio_writer_t *writer; |
419 | 0 | chunk_t data; |
420 | 0 | bool added_any = FALSE, found_vendor = FALSE; |
421 | 0 | eap_payload_t *payload; |
422 | |
|
423 | 0 | writer = bio_writer_create(12); |
424 | 0 | writer->write_uint8(writer, EAP_RESPONSE); |
425 | 0 | writer->write_uint8(writer, identifier); |
426 | | /* write zero length, we update it once we know the length */ |
427 | 0 | writer->write_uint16(writer, 0); |
428 | |
|
429 | 0 | write_type(writer, EAP_NAK, 0, expanded); |
430 | |
|
431 | 0 | enumerator = charon->eap->create_enumerator(charon->eap, EAP_PEER); |
432 | 0 | while (enumerator->enumerate(enumerator, ®_type, ®_vendor)) |
433 | 0 | { |
434 | 0 | if ((type && type != reg_type) || |
435 | 0 | (type && vendor && vendor != reg_vendor)) |
436 | 0 | { /* the preferred type is only sent if we actually find it */ |
437 | 0 | continue; |
438 | 0 | } |
439 | 0 | if (!reg_vendor || expanded) |
440 | 0 | { |
441 | 0 | write_type(writer, reg_type, reg_vendor, expanded); |
442 | 0 | added_any = TRUE; |
443 | 0 | } |
444 | 0 | else if (reg_vendor) |
445 | 0 | { /* found vendor specific method, but this is not an expanded Nak */ |
446 | 0 | found_vendor = TRUE; |
447 | 0 | } |
448 | 0 | } |
449 | 0 | enumerator->destroy(enumerator); |
450 | |
|
451 | 0 | if (found_vendor) |
452 | 0 | { /* request an expanded authentication type */ |
453 | 0 | write_type(writer, EAP_EXPANDED, 0, expanded); |
454 | 0 | added_any = TRUE; |
455 | 0 | } |
456 | 0 | if (!added_any) |
457 | 0 | { /* no methods added */ |
458 | 0 | write_type(writer, 0, 0, expanded); |
459 | 0 | } |
460 | | |
461 | | /* set length */ |
462 | 0 | data = writer->get_buf(writer); |
463 | 0 | htoun16(data.ptr + offsetof(eap_packet_t, length), data.len); |
464 | |
|
465 | 0 | payload = eap_payload_create_data(data); |
466 | 0 | writer->destroy(writer); |
467 | 0 | return payload; |
468 | 0 | } |