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