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