]> git.ipfire.org Git - thirdparty/strongswan.git/blame - src/libcharon/encoding/payloads/eap_payload.c
Update copyright headers after acquisition by secunet
[thirdparty/strongswan.git] / src / libcharon / encoding / payloads / eap_payload.c
CommitLineData
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
27typedef 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 */
33struct 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 72static 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
103METHOD(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
150METHOD(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
157METHOD(payload_t, get_header_length, int,
158 private_eap_payload_t *this)
159{
160 return 4;
161}
162
db053419
MW
163METHOD(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
169METHOD(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
175METHOD(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
181METHOD(payload_t, get_length, size_t,
182 private_eap_payload_t *this)
c81eb6e7
JH
183{
184 return this->payload_length;
185}
186
db053419
MW
187METHOD(eap_payload_t, get_data, chunk_t,
188 private_eap_payload_t *this)
f27f6296
MW
189{
190 return this->data;
191}
192
db053419
MW
193METHOD(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
201METHOD(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 212METHOD(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 */
227static 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 248METHOD(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 */
264typedef 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
273METHOD(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
284METHOD(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
309METHOD(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
315METHOD2(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 */
325eap_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 */
359eap_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 */
370eap_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 382eap_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 394static 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
412eap_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, &reg_type, &reg_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}