Migrated simaka_message to INIT/METHOD macros
[strongswan.git] / src / libsimaka / simaka_message.c
1 /*
2 * Copyright (C) 2009 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "simaka_message.h"
17
18 #include "simaka_manager.h"
19
20 #include <debug.h>
21 #include <utils/linked_list.h>
22
23 typedef struct private_simaka_message_t private_simaka_message_t;
24 typedef struct hdr_t hdr_t;
25 typedef struct attr_hdr_t attr_hdr_t;
26 typedef struct attr_t attr_t;
27
28 /**
29 * packed EAP-SIM/AKA header struct
30 */
31 struct hdr_t {
32 /** EAP code (REQUEST/RESPONSE) */
33 u_int8_t code;
34 /** unique message identifier */
35 u_int8_t identifier;
36 /** length of whole message */
37 u_int16_t length;
38 /** EAP type => EAP_SIM/EAP_AKA */
39 u_int8_t type;
40 /** SIM subtype */
41 u_int8_t subtype;
42 /** reserved bytes */
43 u_int16_t reserved;
44 } __attribute__((__packed__));
45
46 /**
47 * packed EAP-SIM/AKA attribute header struct
48 */
49 struct attr_hdr_t {
50 /** attribute type */
51 u_int8_t type;
52 /** attibute length */
53 u_int8_t length;
54 } __attribute__((__packed__));
55
56 /**
57 * SIM/AKA attribute, parsed
58 */
59 struct attr_t {
60 /** type of attribute */
61 simaka_attribute_t type;
62 /** length of data */
63 size_t len;
64 /** start of data, variable length */
65 char data[];
66 };
67
68 ENUM_BEGIN(simaka_subtype_names, AKA_CHALLENGE, AKA_IDENTITY,
69 "AKA_CHALLENGE",
70 "AKA_AUTHENTICATION_REJECT",
71 "AKA_3",
72 "AKA_SYNCHRONIZATION_FAILURE",
73 "AKA_IDENTITY");
74 ENUM_NEXT(simaka_subtype_names, SIM_START, AKA_CLIENT_ERROR, AKA_IDENTITY,
75 "SIM_START",
76 "SIM_CHALLENGE",
77 "SIM/AKA_NOTIFICATION",
78 "SIM/AKA_REAUTHENTICATION",
79 "SIM/AKA_CLIENT_ERROR");
80 ENUM_END(simaka_subtype_names, AKA_CLIENT_ERROR);
81
82
83 ENUM_BEGIN(simaka_attribute_names, AT_RAND, AT_CLIENT_ERROR_CODE,
84 "AT_RAND",
85 "AT_AUTN",
86 "AT_RES",
87 "AT_AUTS",
88 "AT_5",
89 "AT_PADDING",
90 "AT_NONCE_MT",
91 "AT_8",
92 "AT_9",
93 "AT_PERMANENT_ID_REQ",
94 "AT_MAC",
95 "AT_NOTIFICATION",
96 "AT_ANY_ID_REQ",
97 "AT_IDENTITY",
98 "AT_VERSION_LIST",
99 "AT_SELECTED_VERSION",
100 "AT_FULLAUTH_ID_REQ",
101 "AT_18",
102 "AT_COUNTER",
103 "AT_COUNTER_TOO_SMALL",
104 "AT_NONCE_S",
105 "AT_CLIENT_ERROR_CODE");
106 ENUM_NEXT(simaka_attribute_names, AT_IV, AT_RESULT_IND, AT_CLIENT_ERROR_CODE,
107 "AT_IV",
108 "AT_ENCR_DATA",
109 "AT_131",
110 "AT_NEXT_PSEUDONYM",
111 "AT_NEXT_REAUTH_ID",
112 "AT_CHECKCODE",
113 "AT_RESULT_IND");
114 ENUM_END(simaka_attribute_names, AT_RESULT_IND);
115
116
117 ENUM_BEGIN(simaka_notification_names, SIM_GENERAL_FAILURE_AA, SIM_GENERAL_FAILURE_AA,
118 "General failure after authentication");
119 ENUM_NEXT(simaka_notification_names, SIM_TEMP_DENIED, SIM_TEMP_DENIED, SIM_GENERAL_FAILURE_AA,
120 "User has been temporarily denied access");
121 ENUM_NEXT(simaka_notification_names, SIM_NOT_SUBSCRIBED, SIM_NOT_SUBSCRIBED, SIM_TEMP_DENIED,
122 "User has not subscribed to the requested service");
123 ENUM_NEXT(simaka_notification_names, SIM_GENERAL_FAILURE, SIM_GENERAL_FAILURE, SIM_NOT_SUBSCRIBED,
124 "General failure");
125 ENUM_NEXT(simaka_notification_names, SIM_SUCCESS, SIM_SUCCESS, SIM_GENERAL_FAILURE,
126 "User has been successfully authenticated");
127 ENUM_END(simaka_notification_names, SIM_SUCCESS);
128
129
130 ENUM(simaka_client_error_names, SIM_UNABLE_TO_PROCESS, SIM_RANDS_NOT_FRESH,
131 "unable to process packet",
132 "unsupported version",
133 "insufficient number of challenges",
134 "RANDs are not fresh",
135 );
136
137 /**
138 * Check if an EAP-SIM/AKA attribute is skippable
139 */
140 bool simaka_attribute_skippable(simaka_attribute_t attribute)
141 {
142 bool skippable = !(attribute >= 0 && attribute <= 127);
143
144 DBG1(DBG_LIB, "%sskippable EAP-SIM/AKA attribute %N",
145 skippable ? "ignoring " : "found non-",
146 simaka_attribute_names, attribute);
147 return skippable;
148 }
149
150 /**
151 * Private data of an simaka_message_t object.
152 */
153 struct private_simaka_message_t {
154
155 /**
156 * Public simaka_message_t interface.
157 */
158 simaka_message_t public;
159
160 /**
161 * EAP message, starting with EAP header
162 */
163 hdr_t *hdr;
164
165 /**
166 * List of parsed attributes, attr_t
167 */
168 linked_list_t *attributes;
169
170 /**
171 * Currently parsing AT_ENCR_DATA wrapped attributes?
172 */
173 bool encrypted;
174
175 /**
176 * crypto helper
177 */
178 simaka_crypto_t *crypto;
179
180 /**
181 * Phase a NOTIFICATION is sent within
182 */
183 bool p_bit;
184
185 /**
186 * MAC value, pointing into message
187 */
188 chunk_t mac;
189
190 /**
191 * ENCR_DATA value, pointing into message
192 */
193 chunk_t encr;
194
195 /**
196 * IV value, pointing into message
197 */
198 chunk_t iv;
199 };
200
201 METHOD(simaka_message_t, is_request, bool,
202 private_simaka_message_t *this)
203 {
204 return this->hdr->code == EAP_REQUEST;
205 }
206
207 METHOD(simaka_message_t, get_identifier, u_int8_t,
208 private_simaka_message_t *this)
209 {
210 return this->hdr->identifier;
211 }
212
213 METHOD(simaka_message_t, get_subtype, simaka_subtype_t,
214 private_simaka_message_t *this)
215 {
216 return this->hdr->subtype;
217 }
218
219 METHOD(simaka_message_t, get_type, eap_type_t,
220 private_simaka_message_t *this)
221 {
222 return this->hdr->type;
223 }
224
225 /**
226 * convert attr_t to type and data enumeration
227 */
228 static bool attr_enum_filter(void *null, attr_t **in, simaka_attribute_t *type,
229 void *dummy, chunk_t *data)
230 {
231 attr_t *attr = *in;
232
233 *type = attr->type;
234 *data = chunk_create(attr->data, attr->len);
235 return TRUE;
236 }
237
238 METHOD(simaka_message_t, create_attribute_enumerator, enumerator_t*,
239 private_simaka_message_t *this)
240 {
241 return enumerator_create_filter(
242 this->attributes->create_enumerator(this->attributes),
243 (void*)attr_enum_filter, NULL, NULL);
244 }
245
246 METHOD(simaka_message_t, add_attribute, void,
247 private_simaka_message_t *this, simaka_attribute_t type, chunk_t data)
248 {
249 attr_t *attr;
250
251 attr = malloc(sizeof(attr_t) + data.len);
252 attr->len = data.len;
253 attr->type = type;
254 memcpy(attr->data, data.ptr, data.len);
255
256 this->attributes->insert_last(this->attributes, attr);
257 }
258
259 /**
260 * Error handling for unencrypted attributes
261 */
262 static bool not_encrypted(simaka_attribute_t type)
263 {
264 DBG1(DBG_LIB, "received unencrypted %N", simaka_attribute_names, type);
265 return FALSE;
266 }
267
268 /**
269 * Error handling for invalid length
270 */
271 static bool invalid_length(simaka_attribute_t type)
272 {
273 DBG1(DBG_LIB, "invalid length of %N", simaka_attribute_names, type);
274 return FALSE;
275 }
276
277 /**
278 * Call SIM/AKA message hooks
279 */
280 static void call_hook(private_simaka_message_t *this,
281 bool inbound, bool decrypted)
282 {
283 simaka_manager_t *mgr;
284
285 switch (this->hdr->type)
286 {
287 case EAP_SIM:
288 mgr = lib->get(lib, "sim-manager");
289 break;
290 case EAP_AKA:
291 mgr = lib->get(lib, "aka-manager");
292 break;
293 default:
294 return;
295 }
296 mgr->message_hook(mgr, &this->public, inbound, decrypted);
297 }
298
299 /**
300 * Parse attributes from a chunk of data
301 */
302 static bool parse_attributes(private_simaka_message_t *this, chunk_t in)
303 {
304 while (in.len)
305 {
306 attr_hdr_t *hdr;
307 chunk_t data;
308
309 if (in.len < sizeof(attr_hdr_t))
310 {
311 DBG1(DBG_LIB, "found short %N attribute header",
312 eap_type_names, this->hdr->type);
313 return FALSE;
314 }
315 hdr = (attr_hdr_t*)in.ptr;
316
317 switch (hdr->type)
318 {
319 /* attributes without data */
320 case AT_COUNTER_TOO_SMALL:
321 if (!this->encrypted)
322 {
323 return not_encrypted(hdr->type);
324 }
325 /* FALL */
326 case AT_ANY_ID_REQ:
327 case AT_PERMANENT_ID_REQ:
328 case AT_FULLAUTH_ID_REQ:
329 {
330 if (hdr->length != 1 || in.len < 4)
331 {
332 return invalid_length(hdr->type);
333 }
334 data = chunk_empty;
335 in = chunk_skip(in, 4);
336 break;
337 }
338 /* attributes with two bytes data */
339 case AT_COUNTER:
340 if (!this->encrypted)
341 {
342 return not_encrypted(hdr->type);
343 }
344 /* FALL */
345 case AT_CLIENT_ERROR_CODE:
346 case AT_SELECTED_VERSION:
347 case AT_NOTIFICATION:
348 {
349 if (hdr->length != 1 || in.len < 4)
350 {
351 return invalid_length(hdr->type);
352 }
353 data = chunk_create(in.ptr + 2, 2);
354 in = chunk_skip(in, 4);
355 break;
356 }
357 /* attributes with an additional actual-length in bits or bytes */
358 case AT_NEXT_PSEUDONYM:
359 case AT_NEXT_REAUTH_ID:
360 if (!this->encrypted)
361 {
362 return not_encrypted(hdr->type);
363 }
364 /* FALL */
365 case AT_RES:
366 case AT_IDENTITY:
367 case AT_VERSION_LIST:
368 {
369 u_int16_t len;
370
371 if (hdr->length < 1 || in.len < 4)
372 {
373 return invalid_length(hdr->type);
374 }
375 memcpy(&len, in.ptr + 2, 2);
376 len = ntohs(len);
377 if (hdr->type == AT_RES)
378 { /* AT_RES uses length encoding in bits */
379 len /= 8;
380 }
381 if (len > hdr->length * 4 || len > in.len)
382 {
383 return invalid_length(hdr->type);
384 }
385 data = chunk_create(in.ptr + 4, len);
386 in = chunk_skip(in, hdr->length * 4);
387 break;
388 }
389 /* attributes with two reserved bytes, 16 bytes length */
390 case AT_NONCE_S:
391 if (!this->encrypted)
392 {
393 return not_encrypted(hdr->type);
394 }
395 /* FALL */
396 case AT_AUTN:
397 case AT_NONCE_MT:
398 case AT_IV:
399 case AT_MAC:
400 {
401 if (hdr->length != 5 || in.len < 20)
402 {
403 return invalid_length(hdr->type);
404 }
405 data = chunk_create(in.ptr + 4, 16);
406 in = chunk_skip(in, 20);
407 break;
408 }
409 /* attributes with two reserved bytes, variable length */
410 case AT_ENCR_DATA:
411 case AT_RAND:
412 {
413 if (hdr->length * 4 > in.len || in.len < 4)
414 {
415 return invalid_length(hdr->type);
416 }
417 data = chunk_create(in.ptr + 4, hdr->length * 4 - 4);
418 in = chunk_skip(in, hdr->length * 4);
419 break;
420 }
421 /* attributes with no reserved bytes, 14 bytes length */
422 case AT_AUTS:
423 {
424 if (hdr->length != 4 || in.len < 16)
425 {
426 return invalid_length(hdr->type);
427 }
428 data = chunk_create(in.ptr + 2, 14);
429 in = chunk_skip(in, 16);
430 break;
431 }
432 /* other attributes (with 4n + 2 length) */
433 case AT_PADDING:
434 default:
435 {
436 if (hdr->length * 4 > in.len || in.len < 4)
437 {
438 return invalid_length(hdr->type);
439 }
440 data = chunk_create(in.ptr + 2, hdr->length * 4 - 2);
441 in = chunk_skip(in, hdr->length * 4);
442 break;
443 }
444 }
445
446 /* handle special attributes */
447 switch (hdr->type)
448 {
449 case AT_MAC:
450 this->mac = data;
451 break;
452 case AT_IV:
453 this->iv = data;
454 break;
455 case AT_ENCR_DATA:
456 this->encr = data;
457 break;
458 case AT_PADDING:
459 break;
460 case AT_NOTIFICATION:
461 if (this->p_bit)
462 { /* remember P bit for MAC verification */
463 this->p_bit = !!(data.ptr[0] & 0x40);
464 }
465 else if (!this->encrypted)
466 {
467 DBG1(DBG_LIB, "found P-bit 0 notify in unencrypted message");
468 return FALSE;
469 }
470 /* FALL */
471 default:
472 add_attribute(this, hdr->type, data);
473 break;
474 }
475 }
476
477 call_hook(this, TRUE, this->encrypted);
478
479 return TRUE;
480 }
481
482 /**
483 * Decrypt a message and parse the decrypted attributes
484 */
485 static bool decrypt(private_simaka_message_t *this)
486 {
487 bool success;
488 crypter_t *crypter;
489 chunk_t plain;
490
491 crypter = this->crypto->get_crypter(this->crypto);
492 if (!crypter || !this->iv.len || !this->encr.len || this->encrypted)
493 {
494 return TRUE;
495 }
496 if (this->encr.len % crypter->get_block_size(crypter))
497 {
498 DBG1(DBG_LIB, "%N ENCR_DATA not a multiple of block size",
499 eap_type_names, this->hdr->type);
500 return FALSE;
501 }
502
503 crypter->decrypt(crypter, this->encr, this->iv, &plain);
504
505 this->encrypted = TRUE;
506 success = parse_attributes(this, plain);
507 this->encrypted = FALSE;
508 free(plain.ptr);
509 return success;
510 }
511
512 METHOD(simaka_message_t, parse, bool,
513 private_simaka_message_t *this)
514 {
515 chunk_t in;
516
517 if (this->attributes->get_count(this->attributes))
518 { /* Already parsed. Try to decrypt and parse AT_ENCR_DATA. */
519 return decrypt(this);
520 }
521
522 in = chunk_create((char*)this->hdr, ntohs(this->hdr->length));
523 if (!parse_attributes(this, chunk_skip(in, sizeof(hdr_t))))
524 {
525 return FALSE;
526 }
527 /* try to decrypt if we already have keys */
528 return decrypt(this);
529 }
530
531 METHOD(simaka_message_t, verify, bool,
532 private_simaka_message_t *this, chunk_t sigdata)
533 {
534 chunk_t data, backup;
535 signer_t *signer;
536
537 signer = this->crypto->get_signer(this->crypto);
538
539 switch (this->hdr->subtype)
540 {
541 case SIM_START:
542 case SIM_CLIENT_ERROR:
543 /* AKA_CLIENT_ERROR: */
544 case AKA_AUTHENTICATION_REJECT:
545 case AKA_SYNCHRONIZATION_FAILURE:
546 case AKA_IDENTITY:
547 /* skip MAC if available */
548 return TRUE;
549 case SIM_CHALLENGE:
550 case AKA_CHALLENGE:
551 case SIM_REAUTHENTICATION:
552 /* AKA_REAUTHENTICATION: */
553 {
554 if (!this->mac.ptr || !signer)
555 { /* require MAC, but not found */
556 DBG1(DBG_LIB, "%N message requires a MAC, but none found",
557 simaka_subtype_names, this->hdr->subtype);
558 return FALSE;
559 }
560 break;
561 }
562 case SIM_NOTIFICATION:
563 /* AKA_NOTIFICATION: */
564 {
565 if (this->p_bit)
566 { /* MAC not verified if in Phase 1 */
567 return TRUE;
568 }
569 if (!this->mac.ptr || !signer)
570 {
571 DBG1(DBG_LIB, "%N message has a phase 0 notify, but "
572 "no MAC found", simaka_subtype_names, this->hdr->subtype);
573 return FALSE;
574 }
575 break;
576 }
577 default:
578 /* unknown message? */
579 DBG1(DBG_LIB, "signature rule for %N messages missing",
580 simaka_subtype_names, this->hdr->subtype);
581 return FALSE;
582 }
583
584 /* zero MAC for verification */
585 backup = chunk_clonea(this->mac);
586 memset(this->mac.ptr, 0, this->mac.len);
587
588 data = chunk_create((char*)this->hdr, ntohs(this->hdr->length));
589 if (sigdata.len)
590 {
591 data = chunk_cata("cc", data, sigdata);
592 }
593 if (!signer->verify_signature(signer, data, backup))
594 {
595 DBG1(DBG_LIB, "%N MAC verification failed",
596 eap_type_names, this->hdr->type);
597 return FALSE;
598 }
599 return TRUE;
600 }
601
602 METHOD(simaka_message_t, generate, chunk_t,
603 private_simaka_message_t *this, chunk_t sigdata)
604 {
605 /* buffers large enough for messages we generate */
606 char out_buf[1024], encr_buf[512];
607 enumerator_t *enumerator;
608 chunk_t out, encr, data, *target, mac = chunk_empty;
609 simaka_attribute_t type;
610 attr_hdr_t *hdr;
611 u_int16_t len;
612 signer_t *signer;
613
614 call_hook(this, FALSE, TRUE);
615
616 out = chunk_create(out_buf, sizeof(out_buf));
617 encr = chunk_create(encr_buf, sizeof(encr_buf));
618
619 /* copy header */
620 memcpy(out.ptr, this->hdr, sizeof(hdr_t));
621 out = chunk_skip(out, sizeof(hdr_t));
622
623 /* encode attributes */
624 enumerator = create_attribute_enumerator(this);
625 while (enumerator->enumerate(enumerator, &type, &data))
626 {
627 /* encrypt this attribute? */
628 switch (type)
629 {
630 case AT_NONCE_S:
631 case AT_NEXT_PSEUDONYM:
632 case AT_NEXT_REAUTH_ID:
633 case AT_COUNTER:
634 case AT_COUNTER_TOO_SMALL:
635 target = &encr;
636 break;
637 case AT_NOTIFICATION:
638 /* P bit not set, encrypt */
639 if (!(data.ptr[0] & 0x40))
640 {
641 target = &encr;
642 break;
643 }
644 /* FALL */
645 default:
646 target = &out;
647 break;
648 }
649
650 hdr = (attr_hdr_t*)target->ptr;
651 hdr->type = type;
652
653 /* encode type specific */
654 switch (type)
655 {
656 /* attributes without data */
657 case AT_COUNTER_TOO_SMALL:
658 case AT_ANY_ID_REQ:
659 case AT_PERMANENT_ID_REQ:
660 case AT_FULLAUTH_ID_REQ:
661 {
662 hdr->length = 1;
663 memset(target->ptr + 2, 0, 2);
664 *target = chunk_skip(*target, 4);
665 break;
666 }
667 /* attributes with two bytes data */
668 case AT_COUNTER:
669 case AT_CLIENT_ERROR_CODE:
670 case AT_SELECTED_VERSION:
671 case AT_NOTIFICATION:
672 {
673 hdr->length = 1;
674 memcpy(target->ptr + 2, data.ptr, 2);
675 *target = chunk_skip(*target, 4);
676 break;
677 }
678 /* attributes with an additional actual-length in bits or bytes */
679 case AT_NEXT_PSEUDONYM:
680 case AT_NEXT_REAUTH_ID:
681 case AT_IDENTITY:
682 case AT_VERSION_LIST:
683 case AT_RES:
684 {
685 u_int16_t len, padding;
686
687 len = htons(data.len);
688 if (type == AT_RES)
689 { /* AT_RES uses length encoding in bits */
690 len *= 8;
691 }
692 memcpy(target->ptr + 2, &len, sizeof(len));
693 memcpy(target->ptr + 4, data.ptr, data.len);
694 hdr->length = data.len / 4 + 1;
695 padding = (4 - (data.len % 4)) % 4;
696 if (padding)
697 {
698 hdr->length++;
699 memset(target->ptr + 4 + data.len, 0, padding);
700 }
701 *target = chunk_skip(*target, hdr->length * 4);
702 break;
703 }
704 /* attributes with two reserved bytes, 16 bytes length */
705 case AT_NONCE_S:
706 case AT_NONCE_MT:
707 case AT_AUTN:
708 {
709 hdr->length = 5;
710 memset(target->ptr + 2, 0, 2);
711 memcpy(target->ptr + 4, data.ptr, data.len);
712 *target = chunk_skip(*target, 20);
713 break;
714 }
715 /* attributes with two reserved bytes, variable length */
716 case AT_RAND:
717 {
718 hdr->length = 1 + data.len / 4;
719 memset(target->ptr + 2, 0, 2);
720 memcpy(target->ptr + 4, data.ptr, data.len);
721 *target = chunk_skip(*target, data.len + 4);
722 break;
723 }
724 /* attributes with no reserved bytes, 14 bytes length */
725 case AT_AUTS:
726 {
727 hdr->length = 4;
728 memcpy(target->ptr + 2, data.ptr, data.len);
729 *target = chunk_skip(*target, 16);
730 break;
731 }
732 default:
733 {
734 DBG1(DBG_LIB, "no rule to encode %N, skipped",
735 simaka_attribute_names, type);
736 break;
737 }
738 }
739 }
740 enumerator->destroy(enumerator);
741
742 /* encrypt attributes, if any */
743 if (encr.len < sizeof(encr_buf))
744 {
745 chunk_t iv;
746 size_t bs, padding;
747 crypter_t *crypter;
748 rng_t *rng;
749
750 crypter = this->crypto->get_crypter(this->crypto);
751 bs = crypter->get_block_size(crypter);
752 iv.len = crypter->get_iv_size(crypter);
753
754 /* add AT_PADDING attribute */
755 padding = bs - ((sizeof(encr_buf) - encr.len) % bs);
756 if (padding)
757 {
758 hdr = (attr_hdr_t*)encr.ptr;
759 hdr->type = AT_PADDING;
760 hdr->length = padding / 4;
761 memset(encr.ptr + 2, 0, padding - 2);
762 encr = chunk_skip(encr, padding);
763 }
764 encr = chunk_create(encr_buf, sizeof(encr_buf) - encr.len);
765
766 /* add IV attribute */
767 hdr = (attr_hdr_t*)out.ptr;
768 hdr->type = AT_IV;
769 hdr->length = iv.len / 4 + 1;
770 memset(out.ptr + 2, 0, 2);
771 out = chunk_skip(out, 4);
772
773 rng = this->crypto->get_rng(this->crypto);
774 rng->get_bytes(rng, iv.len, out.ptr);
775
776 iv = chunk_clonea(chunk_create(out.ptr, iv.len));
777 out = chunk_skip(out, iv.len);
778
779 /* inline encryption */
780 crypter->encrypt(crypter, encr, iv, NULL);
781
782 /* add ENCR_DATA attribute */
783 hdr = (attr_hdr_t*)out.ptr;
784 hdr->type = AT_ENCR_DATA;
785 hdr->length = encr.len / 4 + 1;
786 memset(out.ptr + 2, 0, 2);
787 memcpy(out.ptr + 4, encr.ptr, encr.len);
788 out = chunk_skip(out, encr.len + 4);
789 }
790
791 /* include MAC ? */
792 signer = this->crypto->get_signer(this->crypto);
793 switch (this->hdr->subtype)
794 {
795 case SIM_CHALLENGE:
796 case AKA_CHALLENGE:
797 case SIM_REAUTHENTICATION:
798 /* AKA_REAUTHENTICATION: */
799 /* TODO: Notifications without P bit */
800 {
801 size_t bs;
802
803 bs = signer->get_block_size(signer);
804 hdr = (attr_hdr_t*)out.ptr;
805 hdr->type = AT_MAC;
806 hdr->length = bs / 4 + 1;
807 memset(out.ptr + 2, 0, 2 + bs);
808 mac = chunk_create(out.ptr + 4, bs);
809 out = chunk_skip(out, bs + 4);
810 break;
811 }
812 default:
813 break;
814 }
815
816 /* calculate message length */
817 out = chunk_create(out_buf, sizeof(out_buf) - out.len);
818 len = htons(out.len);
819 memcpy(out.ptr + 2, &len, sizeof(len));
820
821 /* generate MAC */
822 if (mac.len)
823 {
824 data = chunk_cata("cc", out, sigdata);
825 signer->get_signature(signer, data, mac.ptr);
826 }
827
828 call_hook(this, FALSE, FALSE);
829
830 return chunk_clone(out);
831 }
832
833 METHOD(simaka_message_t, destroy, void,
834 private_simaka_message_t *this)
835 {
836 this->attributes->destroy_function(this->attributes, free);
837 free(this->hdr);
838 free(this);
839 }
840
841 /**
842 * Generic constructor.
843 */
844 static simaka_message_t *simaka_message_create_data(chunk_t data,
845 simaka_crypto_t *crypto)
846 {
847 private_simaka_message_t *this;
848 hdr_t *hdr = (hdr_t*)data.ptr;
849
850 if (data.len < sizeof(hdr_t) || hdr->length != htons(data.len))
851 {
852 DBG1(DBG_LIB, "EAP-SIM/AKA header has invalid length");
853 return NULL;
854 }
855 if (hdr->code != EAP_REQUEST && hdr->code != EAP_RESPONSE)
856 {
857 DBG1(DBG_LIB, "invalid EAP code in EAP-SIM/AKA message",
858 eap_type_names, hdr->type);
859 return NULL;
860 }
861 if (hdr->type != EAP_SIM && hdr->type != EAP_AKA)
862 {
863 DBG1(DBG_LIB, "invalid EAP type in EAP-SIM/AKA message",
864 eap_type_names, hdr->type);
865 return NULL;
866 }
867
868 INIT(this,
869 .public = {
870 .is_request = _is_request,
871 .get_identifier = _get_identifier,
872 .get_type = _get_type,
873 .get_subtype = _get_subtype,
874 .create_attribute_enumerator = _create_attribute_enumerator,
875 .add_attribute = _add_attribute,
876 .parse = _parse,
877 .verify = _verify,
878 .generate = _generate,
879 .destroy = _destroy,
880 },
881 .attributes = linked_list_create(),
882 .crypto = crypto,
883 .p_bit = TRUE,
884 .hdr = malloc(data.len),
885 );
886 memcpy(this->hdr, hdr, data.len);
887
888 return &this->public;
889 }
890
891 /**
892 * See header.
893 */
894 simaka_message_t *simaka_message_create_from_payload(chunk_t data,
895 simaka_crypto_t *crypto)
896 {
897 return simaka_message_create_data(data, crypto);
898 }
899
900 /**
901 * See header.
902 */
903 simaka_message_t *simaka_message_create(bool request, u_int8_t identifier,
904 eap_type_t type, simaka_subtype_t subtype,
905 simaka_crypto_t *crypto)
906 {
907 hdr_t hdr = {
908 .code = request ? EAP_REQUEST : EAP_RESPONSE,
909 .identifier = identifier,
910 .length = htons(sizeof(hdr_t)),
911 .type = type,
912 .subtype = subtype,
913 };
914 return simaka_message_create_data(chunk_create((char*)&hdr, sizeof(hdr)),
915 crypto);
916 }
917