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 ENUM_BEGIN(simaka_notification_names
, SIM_GENERAL_FAILURE_AA
, SIM_GENERAL_FAILURE_AA
,
113 "General failure after authentication");
114 ENUM_NEXT(simaka_notification_names
, SIM_TEMP_DENIED
, SIM_TEMP_DENIED
, SIM_GENERAL_FAILURE_AA
,
115 "User has been temporarily denied access");
116 ENUM_NEXT(simaka_notification_names
, SIM_NOT_SUBSCRIBED
, SIM_NOT_SUBSCRIBED
, SIM_TEMP_DENIED
,
117 "User has not subscribed to the requested service");
118 ENUM_NEXT(simaka_notification_names
, SIM_GENERAL_FAILURE
, SIM_GENERAL_FAILURE
, SIM_NOT_SUBSCRIBED
,
120 ENUM_NEXT(simaka_notification_names
, SIM_SUCCESS
, SIM_SUCCESS
, SIM_GENERAL_FAILURE
,
121 "User has been successfully authenticated");
122 ENUM_END(simaka_notification_names
, SIM_SUCCESS
);
125 ENUM(simaka_client_error_names
, SIM_UNABLE_TO_PROCESS
, SIM_RANDS_NOT_FRESH
,
126 "unable to process packet",
127 "unsupported version",
128 "insufficient number of challenges",
129 "RANDs are not fresh",
133 * Check if an EAP-SIM/AKA attribute is skippable
135 bool simaka_attribute_skippable(simaka_attribute_t attribute
)
137 bool skippable
= !(attribute
>= 0 && attribute
<= 127);
139 DBG1(DBG_IKE
, "%sskippable EAP-SIM/AKA attribute %N",
140 skippable ?
"ignoring " : "found non-",
141 simaka_attribute_names
, attribute
);
146 * Private data of an simaka_message_t object.
148 struct private_simaka_message_t
{
151 * Public simaka_message_t interface.
153 simaka_message_t
public;
156 * EAP message, starting with EAP header
161 * List of parsed attributes, attr_t
163 linked_list_t
*attributes
;
166 * Currently parsing AT_ENCR_DATA wrapped attributes?
173 simaka_crypto_t
*crypto
;
176 * Phase a NOTIFICATION is sent within
181 * MAC value, pointing into message
186 * ENCR_DATA value, pointing into message
191 * IV value, pointing into message
197 * Implementation of simaka_message_t.is_request
199 static bool is_request(private_simaka_message_t
*this)
201 return this->hdr
->code
== EAP_REQUEST
;
205 * Implementation of simaka_message_t.get_identifier
207 static u_int8_t
get_identifier(private_simaka_message_t
*this)
209 return this->hdr
->identifier
;
213 * Implementation of simaka_message_t.get_subtype
215 static simaka_subtype_t
get_subtype(private_simaka_message_t
*this)
217 return this->hdr
->subtype
;
221 * Implementation of simaka_message_t.get_type
223 static eap_type_t
get_type(private_simaka_message_t
*this)
225 return this->hdr
->type
;
229 * convert attr_t to type and data enumeration
231 static bool attr_enum_filter(void *null
, attr_t
**in
, simaka_attribute_t
*type
,
232 void *dummy
, chunk_t
*data
)
237 *data
= chunk_create(attr
->data
, attr
->len
);
242 * Implementation of simaka_message_t.create_attribute_enumerator
244 static enumerator_t
* create_attribute_enumerator(private_simaka_message_t
*this)
246 return enumerator_create_filter(
247 this->attributes
->create_enumerator(this->attributes
),
248 (void*)attr_enum_filter
, NULL
, NULL
);
252 * Implementation of simaka_message_t.add_attribute
254 static void add_attribute(private_simaka_message_t
*this,
255 simaka_attribute_t type
, chunk_t data
)
259 attr
= malloc(sizeof(attr_t
) + data
.len
);
260 attr
->len
= data
.len
;
262 memcpy(attr
->data
, data
.ptr
, data
.len
);
264 this->attributes
->insert_last(this->attributes
, attr
);
268 * Error handling for unencrypted attributes
270 static bool not_encrypted(simaka_attribute_t type
)
272 DBG1(DBG_IKE
, "received unencrypted %N", simaka_attribute_names
, type
);
277 * Error handling for invalid length
279 static bool invalid_length(simaka_attribute_t type
)
281 DBG1(DBG_IKE
, "invalid length of %N", simaka_attribute_names
, type
);
286 * Parse attributes from a chunk of data
288 static bool parse_attributes(private_simaka_message_t
*this, chunk_t in
)
295 if (in
.len
< sizeof(attr_hdr_t
))
297 DBG1(DBG_IKE
, "found short %N attribute header",
298 eap_type_names
, this->hdr
->type
);
301 hdr
= (attr_hdr_t
*)in
.ptr
;
305 /* attributes without data */
306 case AT_COUNTER_TOO_SMALL
:
307 if (!this->encrypted
)
309 return not_encrypted(hdr
->type
);
313 case AT_PERMANENT_ID_REQ
:
314 case AT_FULLAUTH_ID_REQ
:
316 if (hdr
->length
!= 1 || in
.len
< 4)
318 return invalid_length(hdr
->type
);
321 in
= chunk_skip(in
, 4);
324 /* attributes with two bytes data */
326 if (!this->encrypted
)
328 return not_encrypted(hdr
->type
);
331 case AT_CLIENT_ERROR_CODE
:
332 case AT_SELECTED_VERSION
:
333 case AT_NOTIFICATION
:
335 if (hdr
->length
!= 1 || in
.len
< 4)
337 return invalid_length(hdr
->type
);
339 data
= chunk_create(in
.ptr
+ 2, 2);
340 in
= chunk_skip(in
, 4);
343 /* attributes with an additional actual-length in bits or bytes */
344 case AT_NEXT_PSEUDONYM
:
345 case AT_NEXT_REAUTH_ID
:
346 if (!this->encrypted
)
348 return not_encrypted(hdr
->type
);
353 case AT_VERSION_LIST
:
357 if (hdr
->length
< 1 || in
.len
< 4)
359 return invalid_length(hdr
->type
);
361 memcpy(&len
, in
.ptr
+ 2, 2);
363 if (hdr
->type
== AT_RES
)
364 { /* AT_RES uses length encoding in bits */
367 if (len
> hdr
->length
* 4 || len
> in
.len
)
369 return invalid_length(hdr
->type
);
371 data
= chunk_create(in
.ptr
+ 4, len
);
372 in
= chunk_skip(in
, hdr
->length
* 4);
375 /* attributes with two reserved bytes, 16 bytes length */
377 if (!this->encrypted
)
379 return not_encrypted(hdr
->type
);
387 if (hdr
->length
!= 5 || in
.len
< 20)
389 return invalid_length(hdr
->type
);
391 data
= chunk_create(in
.ptr
+ 4, 16);
392 in
= chunk_skip(in
, 20);
395 /* attributes with two reserved bytes, variable length */
399 if (hdr
->length
* 4 > in
.len
|| in
.len
< 4)
401 return invalid_length(hdr
->type
);
403 data
= chunk_create(in
.ptr
+ 4, hdr
->length
* 4 - 4);
404 in
= chunk_skip(in
, hdr
->length
* 4);
407 /* attributes with no reserved bytes, 14 bytes length */
410 if (hdr
->length
!= 4 || in
.len
< 16)
412 return invalid_length(hdr
->type
);
414 data
= chunk_create(in
.ptr
+ 2, 14);
415 in
= chunk_skip(in
, 16);
418 /* other attributes (with 4n + 2 length) */
422 if (hdr
->length
* 4 > in
.len
|| in
.len
< 4)
424 return invalid_length(hdr
->type
);
426 data
= chunk_create(in
.ptr
+ 2, hdr
->length
* 4 - 2);
427 in
= chunk_skip(in
, hdr
->length
* 4);
432 /* handle special attributes */
446 case AT_NOTIFICATION
:
448 { /* remember P bit for MAC verification */
449 this->p_bit
= !!(data
.ptr
[0] & 0x40);
451 else if (!this->encrypted
)
453 DBG1(DBG_IKE
, "found P-bit 0 notify in unencrypted message");
458 add_attribute(this, hdr
->type
, data
);
463 charon
->sim
->message_hook(charon
->sim
, &this->public, TRUE
, this->encrypted
);
469 * Decrypt a message and parse the decrypted attributes
471 static bool decrypt(private_simaka_message_t
*this)
477 crypter
= this->crypto
->get_crypter(this->crypto
);
478 if (!crypter
|| !this->iv
.len
|| !this->encr
.len
|| this->encrypted
)
482 if (this->encr
.len
% crypter
->get_block_size(crypter
))
484 DBG1(DBG_IKE
, "%N ENCR_DATA not a multiple of block size",
485 eap_type_names
, this->hdr
->type
);
489 crypter
->decrypt(crypter
, this->encr
, this->iv
, &plain
);
491 this->encrypted
= TRUE
;
492 success
= parse_attributes(this, plain
);
493 this->encrypted
= FALSE
;
499 * Implementation of simaka_message_t.parse
501 static bool parse(private_simaka_message_t
*this)
505 if (this->attributes
->get_count(this->attributes
))
506 { /* Already parsed. Try to decrypt and parse AT_ENCR_DATA. */
507 return decrypt(this);
510 in
= chunk_create((char*)this->hdr
, ntohs(this->hdr
->length
));
511 if (!parse_attributes(this, chunk_skip(in
, sizeof(hdr_t
))))
515 /* try to decrypt if we already have keys */
516 return decrypt(this);
520 * Implementation of simaka_message_t.verify
522 static bool verify(private_simaka_message_t
*this, chunk_t sigdata
)
524 chunk_t data
, backup
;
527 signer
= this->crypto
->get_signer(this->crypto
);
529 switch (this->hdr
->subtype
)
532 case SIM_CLIENT_ERROR
:
533 /* AKA_CLIENT_ERROR: */
534 case AKA_AUTHENTICATION_REJECT
:
535 case AKA_SYNCHRONIZATION_FAILURE
:
537 /* skip MAC if available */
541 case SIM_REAUTHENTICATION
:
542 /* AKA_REAUTHENTICATION: */
544 if (!this->mac
.ptr
|| !signer
)
545 { /* require MAC, but not found */
546 DBG1(DBG_IKE
, "%N message requires a MAC, but none found",
547 simaka_subtype_names
, this->hdr
->subtype
);
552 case SIM_NOTIFICATION
:
553 /* AKA_NOTIFICATION: */
556 { /* MAC not verified if in Phase 1 */
559 if (!this->mac
.ptr
|| !signer
)
561 DBG1(DBG_IKE
, "%N message has a phase 0 notify, but "
562 "no MAC found", simaka_subtype_names
, this->hdr
->subtype
);
568 /* unknown message? */
569 DBG1(DBG_IKE
, "signature rule for %N messages missing",
570 simaka_subtype_names
, this->hdr
->subtype
);
574 /* zero MAC for verification */
575 backup
= chunk_clonea(this->mac
);
576 memset(this->mac
.ptr
, 0, this->mac
.len
);
578 data
= chunk_create((char*)this->hdr
, ntohs(this->hdr
->length
));
581 data
= chunk_cata("cc", data
, sigdata
);
583 if (!signer
->verify_signature(signer
, data
, backup
))
585 DBG1(DBG_IKE
, "%N MAC verification failed",
586 eap_type_names
, this->hdr
->type
);
593 * Implementation of simaka_message_t.generate
595 static eap_payload_t
* generate(private_simaka_message_t
*this, chunk_t sigdata
)
597 /* buffers large enough for messages we generate */
598 char out_buf
[1024], encr_buf
[512];
599 enumerator_t
*enumerator
;
600 chunk_t out
, encr
, data
, *target
, mac
= chunk_empty
;
601 simaka_attribute_t type
;
606 charon
->sim
->message_hook(charon
->sim
, &this->public, FALSE
, TRUE
);
608 out
= chunk_create(out_buf
, sizeof(out_buf
));
609 encr
= chunk_create(encr_buf
, sizeof(encr_buf
));
612 memcpy(out
.ptr
, this->hdr
, sizeof(hdr_t
));
613 out
= chunk_skip(out
, sizeof(hdr_t
));
615 /* encode attributes */
616 enumerator
= create_attribute_enumerator(this);
617 while (enumerator
->enumerate(enumerator
, &type
, &data
))
619 /* encrypt this attribute? */
623 case AT_NEXT_PSEUDONYM
:
624 case AT_NEXT_REAUTH_ID
:
626 case AT_COUNTER_TOO_SMALL
:
629 case AT_NOTIFICATION
:
630 /* P bit not set, encrypt */
631 if (!(data
.ptr
[0] & 0x40))
642 hdr
= (attr_hdr_t
*)target
->ptr
;
645 /* encode type specific */
648 /* attributes without data */
649 case AT_COUNTER_TOO_SMALL
:
651 case AT_PERMANENT_ID_REQ
:
652 case AT_FULLAUTH_ID_REQ
:
655 memset(target
->ptr
+ 2, 0, 2);
656 *target
= chunk_skip(*target
, 4);
659 /* attributes with two bytes data */
661 case AT_CLIENT_ERROR_CODE
:
662 case AT_SELECTED_VERSION
:
663 case AT_NOTIFICATION
:
666 memcpy(target
->ptr
+ 2, data
.ptr
, 2);
667 *target
= chunk_skip(*target
, 4);
670 /* attributes with an additional actual-length in bits or bytes */
671 case AT_NEXT_PSEUDONYM
:
672 case AT_NEXT_REAUTH_ID
:
674 case AT_VERSION_LIST
:
677 u_int16_t len
, padding
;
679 len
= htons(data
.len
);
681 { /* AT_RES uses length encoding in bits */
684 memcpy(target
->ptr
+ 2, &len
, sizeof(len
));
685 memcpy(target
->ptr
+ 4, data
.ptr
, data
.len
);
686 hdr
->length
= data
.len
/ 4 + 1;
687 padding
= (4 - (data
.len
% 4)) % 4;
691 memset(target
->ptr
+ 4 + data
.len
, 0, padding
);
693 *target
= chunk_skip(*target
, hdr
->length
* 4);
696 /* attributes with two reserved bytes, 16 bytes length */
702 memset(target
->ptr
+ 2, 0, 2);
703 memcpy(target
->ptr
+ 4, data
.ptr
, data
.len
);
704 *target
= chunk_skip(*target
, 20);
707 /* attributes with two reserved bytes, variable length */
710 hdr
->length
= 1 + data
.len
/ 4;
711 memset(target
->ptr
+ 2, 0, 2);
712 memcpy(target
->ptr
+ 4, data
.ptr
, data
.len
);
713 *target
= chunk_skip(*target
, data
.len
+ 4);
716 /* attributes with no reserved bytes, 14 bytes length */
720 memcpy(target
->ptr
+ 2, data
.ptr
, data
.len
);
721 *target
= chunk_skip(*target
, 16);
726 DBG1(DBG_IKE
, "no rule to encode %N, skipped",
727 simaka_attribute_names
, type
);
732 enumerator
->destroy(enumerator
);
734 /* encrypt attributes, if any */
735 if (encr
.len
< sizeof(encr_buf
))
742 crypter
= this->crypto
->get_crypter(this->crypto
);
743 bs
= crypter
->get_block_size(crypter
);
744 iv
.len
= crypter
->get_iv_size(crypter
);
746 /* add AT_PADDING attribute */
747 padding
= bs
- ((sizeof(encr_buf
) - encr
.len
) % bs
);
750 hdr
= (attr_hdr_t
*)encr
.ptr
;
751 hdr
->type
= AT_PADDING
;
752 hdr
->length
= padding
/ 4;
753 memset(encr
.ptr
+ 2, 0, padding
- 2);
754 encr
= chunk_skip(encr
, padding
);
756 encr
= chunk_create(encr_buf
, sizeof(encr_buf
) - encr
.len
);
758 /* add IV attribute */
759 hdr
= (attr_hdr_t
*)out
.ptr
;
761 hdr
->length
= iv
.len
/ 4 + 1;
762 memset(out
.ptr
+ 2, 0, 2);
763 out
= chunk_skip(out
, 4);
765 rng
= this->crypto
->get_rng(this->crypto
);
766 rng
->get_bytes(rng
, iv
.len
, out
.ptr
);
768 iv
= chunk_clonea(chunk_create(out
.ptr
, iv
.len
));
769 out
= chunk_skip(out
, iv
.len
);
771 /* inline encryption */
772 crypter
->encrypt(crypter
, encr
, iv
, NULL
);
774 /* add ENCR_DATA attribute */
775 hdr
= (attr_hdr_t
*)out
.ptr
;
776 hdr
->type
= AT_ENCR_DATA
;
777 hdr
->length
= encr
.len
/ 4 + 1;
778 memset(out
.ptr
+ 2, 0, 2);
779 memcpy(out
.ptr
+ 4, encr
.ptr
, encr
.len
);
780 out
= chunk_skip(out
, encr
.len
+ 4);
784 signer
= this->crypto
->get_signer(this->crypto
);
785 switch (this->hdr
->subtype
)
789 case SIM_REAUTHENTICATION
:
790 /* AKA_REAUTHENTICATION: */
791 /* TODO: Notifications without P bit */
795 bs
= signer
->get_block_size(signer
);
796 hdr
= (attr_hdr_t
*)out
.ptr
;
798 hdr
->length
= bs
/ 4 + 1;
799 memset(out
.ptr
+ 2, 0, 2 + bs
);
800 mac
= chunk_create(out
.ptr
+ 4, bs
);
801 out
= chunk_skip(out
, bs
+ 4);
808 /* calculate message length */
809 out
= chunk_create(out_buf
, sizeof(out_buf
) - out
.len
);
810 len
= htons(out
.len
);
811 memcpy(out
.ptr
+ 2, &len
, sizeof(len
));
816 data
= chunk_cata("cc", out
, sigdata
);
817 signer
->get_signature(signer
, data
, mac
.ptr
);
820 charon
->sim
->message_hook(charon
->sim
, &this->public, FALSE
, FALSE
);
822 return eap_payload_create_data(out
);
826 * Implementation of simaka_message_t.destroy.
828 static void destroy(private_simaka_message_t
*this)
830 this->attributes
->destroy_function(this->attributes
, free
);
836 * Generic constructor.
838 static simaka_message_t
*simaka_message_create_data(chunk_t data
,
839 simaka_crypto_t
*crypto
)
841 private_simaka_message_t
*this;
842 hdr_t
*hdr
= (hdr_t
*)data
.ptr
;
844 if (data
.len
< sizeof(hdr_t
) || hdr
->length
!= htons(data
.len
))
846 DBG1(DBG_IKE
, "EAP-SIM/AKA header has invalid length");
849 if (hdr
->code
!= EAP_REQUEST
&& hdr
->code
!= EAP_RESPONSE
)
851 DBG1(DBG_IKE
, "invalid EAP code in EAP-SIM/AKA message",
852 eap_type_names
, hdr
->type
);
855 if (hdr
->type
!= EAP_SIM
&& hdr
->type
!= EAP_AKA
)
857 DBG1(DBG_IKE
, "invalid EAP type in EAP-SIM/AKA message",
858 eap_type_names
, hdr
->type
);
862 this = malloc_thing(private_simaka_message_t
);
864 this->public.is_request
= (bool(*)(simaka_message_t
*))is_request
;
865 this->public.get_identifier
= (u_int8_t(*)(simaka_message_t
*))get_identifier
;
866 this->public.get_type
= (eap_type_t(*)(simaka_message_t
*))get_type
;
867 this->public.get_subtype
= (simaka_subtype_t(*)(simaka_message_t
*))get_subtype
;
868 this->public.create_attribute_enumerator
= (enumerator_t
*(*)(simaka_message_t
*))create_attribute_enumerator
;
869 this->public.add_attribute
= (void(*)(simaka_message_t
*, simaka_attribute_t type
, chunk_t data
))add_attribute
;
870 this->public.parse
= (bool(*)(simaka_message_t
*))parse
;
871 this->public.verify
= (bool(*)(simaka_message_t
*, chunk_t sigdata
))verify
;
872 this->public.generate
= (eap_payload_t
*(*)(simaka_message_t
*, chunk_t sigdata
))generate
;
873 this->public.destroy
= (void(*)(simaka_message_t
*))destroy
;
875 this->attributes
= linked_list_create();
876 this->encrypted
= FALSE
;
877 this->crypto
= crypto
;
879 this->mac
= chunk_empty
;
880 this->encr
= chunk_empty
;
881 this->iv
= chunk_empty
;
882 this->hdr
= malloc(data
.len
);
883 memcpy(this->hdr
, hdr
, data
.len
);
885 return &this->public;
891 simaka_message_t
*simaka_message_create_from_payload(eap_payload_t
*payload
,
892 simaka_crypto_t
*crypto
)
894 return simaka_message_create_data(payload
->get_data(payload
), crypto
);
900 simaka_message_t
*simaka_message_create(bool request
, u_int8_t identifier
,
901 eap_type_t type
, simaka_subtype_t subtype
,
902 simaka_crypto_t
*crypto
)
905 .code
= request ? EAP_REQUEST
: EAP_RESPONSE
,
906 .identifier
= identifier
,
907 .length
= htons(sizeof(hdr_t
)),
911 return simaka_message_create_data(chunk_create((char*)&hdr
, sizeof(hdr
)),