2 * Copyright (C) 2009 Martin Willi
3 * Hochschule fuer Technik Rapperswil
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>.
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
16 #include "simaka_message.h"
18 typedef struct private_simaka_message_t private_simaka_message_t
;
19 typedef struct hdr_t hdr_t
;
20 typedef struct attr_hdr_t attr_hdr_t
;
21 typedef struct attr_t attr_t
;
24 * packed EAP-SIM/AKA header struct
27 /** EAP code (REQUEST/RESPONSE) */
29 /** unique message identifier */
31 /** length of whole message */
33 /** EAP type => EAP_SIM/EAP_AKA */
39 } __attribute__((__packed__
));
42 * packed EAP-SIM/AKA attribute header struct
47 /** attibute length */
49 } __attribute__((__packed__
));
52 * SIM/AKA attribute, parsed
55 /** type of attribute */
56 simaka_attribute_t type
;
59 /** start of data, variable length */
63 ENUM_BEGIN(simaka_subtype_names
, AKA_CHALLENGE
, AKA_IDENTITY
,
65 "AKA_AUTHENTICATION_REJECT",
67 "AKA_SYNCHRONIZATION_FAILURE",
69 ENUM_NEXT(simaka_subtype_names
, SIM_START
, AKA_CLIENT_ERROR
, AKA_IDENTITY
,
72 "SIM/AKA_NOTIFICATION",
73 "SIM/AKA_REAUTHENTICATION",
74 "SIM/AKA_CLIENT_ERROR");
75 ENUM_END(simaka_subtype_names
, AKA_CLIENT_ERROR
);
78 ENUM_BEGIN(simaka_attribute_names
, AT_RAND
, AT_CLIENT_ERROR_CODE
,
88 "AT_PERMANENT_ID_REQ",
94 "AT_SELECTED_VERSION",
98 "AT_COUNTER_TOO_SMALL",
100 "AT_CLIENT_ERROR_CODE");
101 ENUM_NEXT(simaka_attribute_names
, AT_IV
, AT_RESULT_IND
, AT_CLIENT_ERROR_CODE
,
109 ENUM_END(simaka_attribute_names
, AT_RESULT_IND
);
112 * Private data of an simaka_message_t object.
114 struct private_simaka_message_t
{
117 * Public simaka_message_t interface.
119 simaka_message_t
public;
122 * EAP message, starting with EAP header
127 * List of parsed attributes, attr_t
129 linked_list_t
*attributes
;
132 * Currently parsing AT_ENCR_DATA wrapped attributes?
137 * Phase a NOTIFICATION is sent within */
141 * MAC value, pointing into message
147 * Implementation of simaka_message_t.is_request
149 static bool is_request(private_simaka_message_t
*this)
151 return this->hdr
->code
== EAP_REQUEST
;
155 * Implementation of simaka_message_t.get_identifier
157 static u_int8_t
get_identifier(private_simaka_message_t
*this)
159 return this->hdr
->identifier
;
163 * Implementation of simaka_message_t.get_subtype
165 static simaka_subtype_t
get_subtype(private_simaka_message_t
*this)
167 return this->hdr
->subtype
;
171 * Implementation of simaka_message_t.get_type
173 static eap_type_t
get_type(private_simaka_message_t
*this)
175 return this->hdr
->type
;
179 * convert attr_t to type and data enumeration
181 static bool attr_enum_filter(void *null
, attr_t
**in
, simaka_attribute_t
*type
,
182 void *dummy
, chunk_t
*data
)
187 *data
= chunk_create(attr
->data
, attr
->len
);
192 * Implementation of simaka_message_t.create_attribute_enumerator
194 static enumerator_t
* create_attribute_enumerator(private_simaka_message_t
*this)
196 return enumerator_create_filter(
197 this->attributes
->create_enumerator(this->attributes
),
198 (void*)attr_enum_filter
, NULL
, NULL
);
202 * Implementation of simaka_message_t.add_attribute
204 static void add_attribute(private_simaka_message_t
*this,
205 simaka_attribute_t type
, chunk_t data
)
209 attr
= malloc(sizeof(attr_t
) + data
.len
);
210 attr
->len
= data
.len
;
212 memcpy(attr
->data
, data
.ptr
, data
.len
);
214 this->attributes
->insert_last(this->attributes
, attr
);
218 * Implementation of simaka_message_t.parse
220 static bool parse(private_simaka_message_t
*this, simaka_crypto_t
*crypto
,
223 chunk_t in
, iv
= chunk_empty
, encr
= chunk_empty
;
225 in
= chunk_create((char*)(this->hdr
+ 1),
226 ntohs(this->hdr
->length
) - sizeof(hdr_t
));
233 if (in
.len
< sizeof(attr_hdr_t
))
235 DBG1(DBG_IKE
, "found short %N attribute header",
236 eap_type_names
, this->hdr
->type
);
239 hdr
= (attr_hdr_t
*)in
.ptr
;
243 /* attributes without data */
244 case AT_COUNTER_TOO_SMALL
:
245 if (!this->encrypted
)
251 case AT_PERMANENT_ID_REQ
:
252 case AT_FULLAUTH_ID_REQ
:
254 if (hdr
->length
!= 1 || in
.len
< 4)
259 in
= chunk_skip(in
, 4);
262 /* attributes with two bytes data */
264 if (!this->encrypted
)
269 case AT_CLIENT_ERROR_CODE
:
270 case AT_SELECTED_VERSION
:
271 case AT_NOTIFICATION
:
273 if (hdr
->length
!= 1 || in
.len
< 4)
277 data
= chunk_create(in
.ptr
+ 2, 2);
278 in
= chunk_skip(in
, 4);
281 /* attributes with an additional actual-length */
282 case AT_NEXT_PSEUDONYM
:
283 case AT_NEXT_REAUTH_ID
:
284 if (!this->encrypted
)
290 case AT_VERSION_LIST
:
294 if (hdr
->length
< 1 || in
.len
< 4)
298 memcpy(&len
, in
.ptr
+ 2, 2);
300 if (len
> hdr
->length
* 4 || len
> in
.len
)
304 data
= chunk_create(in
.ptr
+ 4, len
);
305 in
= chunk_skip(in
, hdr
->length
* 4);
308 /* attributes with two reserved bytes, 16 bytes length */
310 if (!this->encrypted
)
321 if (hdr
->length
!= 5 || in
.len
< 20)
325 data
= chunk_create(in
.ptr
+ 4, 16);
326 in
= chunk_skip(in
, 20);
329 /* attributes with two reserved bytes, variable length */
333 if (hdr
->length
* 4 > in
.len
|| in
.len
< 4)
337 data
= chunk_create(in
.ptr
+ 4, hdr
->length
* 4 - 4);
338 in
= chunk_skip(in
, hdr
->length
* 4);
341 /* attributes with no reserved bytes, 14 bytes length */
344 if (hdr
->length
!= 4 || in
.len
< 16)
348 data
= chunk_create(in
.ptr
+ 2, 14);
349 in
= chunk_skip(in
, 16);
352 /* other attributes (with 4n + 2 length) */
356 if (hdr
->length
* 4 > in
.len
|| in
.len
< 4)
360 data
= chunk_create(in
.ptr
+ 2, hdr
->length
* 4 - 2);
361 in
= chunk_skip(in
, hdr
->length
* 4);
366 /* handle special attributes */
380 case AT_NOTIFICATION
:
382 { /* remember P bit for MAC verification */
383 this->p_bit
= !!(data
.ptr
[0] & 0x40);
385 else if (!this->encrypted
)
386 { /* found a P bit notify in an unencrypted message */
391 add_attribute(this, hdr
->type
, data
);
396 /* decrypt, invoke parser recursively */
397 if (iv
.len
&& encr
.len
)
404 DBG1(DBG_IKE
, "%N message is recursively encrypted",
405 eap_type_names
, this->hdr
->type
);
408 crypter
= crypto
->get_crypter(crypto
);
411 DBG1(DBG_IKE
, "%N message contains unexpected encrypted data",
412 eap_type_names
, this->hdr
->type
);
415 if (encr
.len
% crypter
->get_block_size(crypter
))
417 DBG1(DBG_IKE
, "%N ENCR_DATA not a multiple of block size",
418 eap_type_names
, this->hdr
->type
);
423 crypter
->decrypt(crypter
, encr
, iv
, NULL
);
425 this->encrypted
= TRUE
;
426 success
= parse(this, crypto
, chunk_empty
);
427 this->encrypted
= FALSE
;
434 * Implementation of simaka_message_t.verify
436 static bool verify(private_simaka_message_t
*this,
437 simaka_crypto_t
*crypto
, chunk_t sigdata
)
439 chunk_t data
, backup
;
442 signer
= crypto
->get_signer(crypto
);
444 switch (this->hdr
->subtype
)
447 case SIM_CLIENT_ERROR
:
448 /* AKA_CLIENT_ERROR: */
449 case AKA_AUTHENTICATION_REJECT
:
450 case AKA_SYNCHRONIZATION_FAILURE
:
454 { /* invalid if it contains a MAC */
461 case SIM_REAUTHENTICATION
:
462 /* AKA_REAUTHENTICATION: */
464 if (!this->mac
.ptr
|| !signer
)
465 { /* require MAC, but not found */
470 case SIM_NOTIFICATION
:
471 /* AKA_NOTIFICATION: */
474 { /* MAC not verified if in Phase 1 */
477 if (!this->mac
.ptr
|| !signer
)
478 { /* require MAC, but not found */
484 /* unknown message? */
488 /* zero MAC for verification */
489 backup
= chunk_clonea(this->mac
);
490 memset(this->mac
.ptr
, 0, this->mac
.len
);
492 data
= chunk_create((char*)this->hdr
, ntohs(this->hdr
->length
));
495 data
= chunk_cata("cc", data
, sigdata
);
497 if (!signer
->verify_signature(signer
, data
, backup
))
499 DBG1(DBG_IKE
, "%N MAC verification failed",
500 eap_type_names
, this->hdr
->type
);
507 * Implementation of simaka_message_t.generate
509 static eap_payload_t
* generate(private_simaka_message_t
*this,
510 simaka_crypto_t
*crypto
, chunk_t sigdata
)
512 /* buffers large enough for messages we generate */
513 char out_buf
[1024], encr_buf
[512];
514 enumerator_t
*enumerator
;
515 chunk_t out
, encr
, data
, *target
, mac
= chunk_empty
;
516 simaka_attribute_t type
;
521 out
= chunk_create(out_buf
, sizeof(out_buf
));
522 encr
= chunk_create(encr_buf
, sizeof(encr_buf
));
525 memcpy(out
.ptr
, this->hdr
, sizeof(hdr_t
));
526 out
= chunk_skip(out
, sizeof(hdr_t
));
528 /* encode attributes */
529 enumerator
= create_attribute_enumerator(this);
530 while (enumerator
->enumerate(enumerator
, &type
, &data
))
532 /* encrypt this attribute? */
536 case AT_NEXT_PSEUDONYM
:
537 case AT_NEXT_REAUTH_ID
:
539 case AT_COUNTER_TOO_SMALL
:
542 case AT_NOTIFICATION
:
543 /* P bit not set, encrypt */
544 if (!(data
.ptr
[0] & 0x40))
555 hdr
= (attr_hdr_t
*)target
->ptr
;
558 /* encode type specific */
561 /* attributes without data */
562 case AT_COUNTER_TOO_SMALL
:
564 case AT_PERMANENT_ID_REQ
:
565 case AT_FULLAUTH_ID_REQ
:
568 memset(target
->ptr
+ 2, 0, 2);
569 *target
= chunk_skip(*target
, 4);
572 /* attributes with two bytes data */
574 case AT_CLIENT_ERROR_CODE
:
575 case AT_SELECTED_VERSION
:
576 case AT_NOTIFICATION
:
579 memcpy(target
->ptr
+ 2, data
.ptr
, 2);
580 *target
= chunk_skip(*target
, 4);
583 /* attributes with an additional actual-length */
584 case AT_NEXT_PSEUDONYM
:
585 case AT_NEXT_REAUTH_ID
:
587 case AT_VERSION_LIST
:
589 u_int16_t len
, padding
;
591 len
= htons(data
.len
);
592 memcpy(target
->ptr
+ 2, &len
, sizeof(len
));
593 memcpy(target
->ptr
+ 4, data
.ptr
, data
.len
);
594 hdr
->length
= data
.len
/ 4 + 1;
595 padding
= (4 - (data
.len
% 4)) % 4;
599 memset(target
->ptr
+ 4 + data
.len
, 0, padding
);
601 *target
= chunk_skip(*target
, hdr
->length
* 4);
604 /* attributes with two reserved bytes, 16 bytes length */
611 memset(target
->ptr
+ 2, 0, 2);
612 memcpy(target
->ptr
+ 4, data
.ptr
, data
.len
);
613 *target
= chunk_skip(*target
, 20);
616 /* attributes with two reserved bytes, variable length */
619 hdr
->length
= 1 + data
.len
/ 4;
620 memset(target
->ptr
+ 2, 0, 2);
621 memcpy(target
->ptr
+ 4, data
.ptr
, data
.len
);
622 *target
= chunk_skip(*target
, data
.len
+ 4);
625 /* attributes with no reserved bytes, 14 bytes length */
629 memcpy(target
->ptr
+ 2, data
.ptr
, data
.len
);
630 *target
= chunk_skip(*target
, 16);
635 DBG1(DBG_IKE
, "no rule to encode %N, skipped",
636 simaka_attribute_names
, type
);
641 enumerator
->destroy(enumerator
);
643 /* encrypt attributes, if any */
644 if (encr
.len
< sizeof(encr_buf
))
651 crypter
= crypto
->get_crypter(crypto
);
652 encr
= chunk_create(encr_buf
, sizeof(encr_buf
) - encr
.len
);
653 bs
= crypter
->get_block_size(crypter
);
655 /* add IV attribute */
656 hdr
= (attr_hdr_t
*)out
.ptr
;
658 hdr
->length
= bs
/ 4 + 1;
659 memset(out
.ptr
+ 2, 0, 2);
660 out
= chunk_skip(out
, 4);
662 rng
= crypto
->get_rng(crypto
);
663 rng
->get_bytes(rng
, bs
, out
.ptr
);
665 iv
= chunk_clonea(chunk_create(out
.ptr
, bs
));
666 out
= chunk_skip(out
, bs
);
668 /* inline encryption */
669 crypter
->encrypt(crypter
, encr
, iv
, NULL
);
671 /* add ENCR_DATA attribute */
672 hdr
= (attr_hdr_t
*)out
.ptr
;
673 hdr
->type
= AT_ENCR_DATA
;
674 hdr
->length
= encr
.len
/ 4 + 1;
675 memset(out
.ptr
+ 2, 0, 2);
676 memcpy(out
.ptr
+ 4, encr
.ptr
, encr
.len
);
677 out
= chunk_skip(out
, encr
.len
+ 4);
681 signer
= crypto
->get_signer(crypto
);
682 switch (this->hdr
->subtype
)
686 case SIM_REAUTHENTICATION
:
687 /* AKA_REAUTHENTICATION: */
688 /* TODO: Notifications without P bit */
692 bs
= signer
->get_block_size(signer
);
693 hdr
= (attr_hdr_t
*)out
.ptr
;
695 hdr
->length
= bs
/ 4 + 1;
696 memset(out
.ptr
+ 2, 0, 2 + bs
);
697 mac
= chunk_create(out
.ptr
+ 4, bs
);
698 out
= chunk_skip(out
, bs
+ 4);
705 /* calculate message length */
706 out
= chunk_create(out_buf
, sizeof(out_buf
) - out
.len
);
707 len
= htons(out
.len
);
708 memcpy(out
.ptr
+ 2, &len
, sizeof(len
));
713 data
= chunk_cata("cc", out
, sigdata
);
714 signer
->get_signature(signer
, data
, mac
.ptr
);
716 return eap_payload_create_data(out
);
720 * Implementation of simaka_message_t.destroy.
722 static void destroy(private_simaka_message_t
*this)
724 this->attributes
->destroy_function(this->attributes
, free
);
730 * Generic constructor.
732 static simaka_message_t
*simaka_message_create_data(chunk_t data
)
734 private_simaka_message_t
*this;
735 hdr_t
*hdr
= (hdr_t
*)data
.ptr
;
737 if (data
.len
< sizeof(hdr_t
) || hdr
->length
!= htons(data
.len
))
739 DBG1(DBG_IKE
, "EAP-SIM/AKA header has invalid length");
742 if (hdr
->code
!= EAP_REQUEST
&& hdr
->code
!= EAP_RESPONSE
)
744 DBG1(DBG_IKE
, "invalid EAP code in EAP-SIM/AKA message",
745 eap_type_names
, hdr
->type
);
748 if (hdr
->type
!= EAP_SIM
&& hdr
->type
!= EAP_AKA
)
750 DBG1(DBG_IKE
, "invalid EAP type in EAP-SIM/AKA message",
751 eap_type_names
, hdr
->type
);
755 this = malloc_thing(private_simaka_message_t
);
757 this->public.is_request
= (bool(*)(simaka_message_t
*))is_request
;
758 this->public.get_identifier
= (u_int8_t(*)(simaka_message_t
*))get_identifier
;
759 this->public.get_type
= (eap_type_t(*)(simaka_message_t
*))get_type
;
760 this->public.get_subtype
= (simaka_subtype_t(*)(simaka_message_t
*))get_subtype
;
761 this->public.create_attribute_enumerator
= (enumerator_t
*(*)(simaka_message_t
*))create_attribute_enumerator
;
762 this->public.add_attribute
= (void(*)(simaka_message_t
*, simaka_attribute_t type
, chunk_t data
))add_attribute
;
763 this->public.parse
= (bool(*)(simaka_message_t
*, simaka_crypto_t
* crypto
))parse
;
764 this->public.verify
= (bool(*)(simaka_message_t
*, simaka_crypto_t
* crypto
, chunk_t sigdata
))verify
;
765 this->public.generate
= (eap_payload_t
*(*)(simaka_message_t
*, simaka_crypto_t
* crypto
, chunk_t sigdata
))generate
;
766 this->public.destroy
= (void(*)(simaka_message_t
*))destroy
;
768 this->attributes
= linked_list_create();
769 this->encrypted
= FALSE
;
771 this->mac
= chunk_empty
;
772 this->hdr
= malloc(data
.len
);
773 memcpy(this->hdr
, hdr
, data
.len
);
775 return &this->public;
781 simaka_message_t
*simaka_message_create_from_payload(eap_payload_t
*payload
)
783 return simaka_message_create_data(payload
->get_data(payload
));
789 simaka_message_t
*simaka_message_create(bool request
, u_int8_t identifier
,
790 eap_type_t type
, simaka_subtype_t subtype
)
793 .code
= request ? EAP_REQUEST
: EAP_RESPONSE
,
794 .identifier
= identifier
,
795 .length
= htons(sizeof(hdr_t
)),
799 return simaka_message_create_data(chunk_create((char*)&hdr
, sizeof(hdr
)));