2 * Copyright (C) 2006-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 "eap_aka_server.h"
21 #include <simaka_message.h>
22 #include <simaka_crypto.h>
24 typedef struct private_eap_aka_server_t private_eap_aka_server_t
;
27 * Private data of an eap_aka_server_t object.
29 struct private_eap_aka_server_t
{
32 * Public authenticator_t interface.
34 eap_aka_server_t
public;
37 * EAP-AKA crypto helper
39 simaka_crypto_t
*crypto
;
44 identification_t
*peer
;
47 * EAP identifier value
57 * Expected Result XRES
67 * EAP-AKA message we have initiated
69 simaka_subtype_t pending
;
72 * Did the client send a synchronize request?
78 * Check if an unknown attribute is skippable
80 static bool attribute_skippable(simaka_attribute_t attribute
)
82 if (attribute
>= 0 && attribute
<= 127)
84 DBG1(DBG_IKE
, "ignoring skippable attribute %N",
85 simaka_attribute_names
, attribute
);
92 * Implementation of eap_method_t.initiate
94 static status_t
initiate(private_eap_aka_server_t
*this, eap_payload_t
**out
)
96 simaka_message_t
*message
;
97 enumerator_t
*enumerator
;
98 sim_provider_t
*provider
;
99 char rand
[AKA_RAND_LEN
], xres
[AKA_RES_LEN
];
100 char ck
[AKA_CK_LEN
], ik
[AKA_IK_LEN
], autn
[AKA_AUTN_LEN
];
104 enumerator
= charon
->sim
->create_provider_enumerator(charon
->sim
);
105 while (enumerator
->enumerate(enumerator
, &provider
))
107 if (provider
->get_quintuplet(provider
, this->peer
,
108 rand
, xres
, ck
, ik
, autn
))
114 enumerator
->destroy(enumerator
);
117 DBG1(DBG_IKE
, "no AKA provider found with quintuplets for '%Y'",
122 data
= chunk_cata("cc", chunk_create(ik
, AKA_IK_LEN
),
123 chunk_create(ck
, AKA_CK_LEN
));
125 this->msk
= this->crypto
->derive_keys_full(this->crypto
, this->peer
,
128 this->rand
= chunk_clone(chunk_create(rand
, AKA_RAND_LEN
));
129 this->xres
= chunk_clone(chunk_create(xres
, AKA_RES_LEN
));
131 message
= simaka_message_create(TRUE
, this->identifier
++, EAP_AKA
,
132 AKA_CHALLENGE
, this->crypto
);
133 message
->add_attribute(message
, AT_RAND
, this->rand
);
134 message
->add_attribute(message
, AT_AUTN
, chunk_create(autn
, AKA_AUTN_LEN
));
135 *out
= message
->generate(message
, chunk_empty
);
136 message
->destroy(message
);
138 this->pending
= AKA_CHALLENGE
;
143 * Process EAP-AKA/Response/Challenge message
145 static status_t
process_challenge(private_eap_aka_server_t
*this,
146 simaka_message_t
*in
)
148 enumerator_t
*enumerator
;
149 simaka_attribute_t type
;
150 chunk_t data
, res
= chunk_empty
;
152 if (this->pending
!= AKA_CHALLENGE
)
154 DBG1(DBG_IKE
, "received %N, but not expected",
155 simaka_subtype_names
, AKA_CHALLENGE
);
158 enumerator
= in
->create_attribute_enumerator(in
);
159 while (enumerator
->enumerate(enumerator
, &type
, &data
))
167 if (!attribute_skippable(type
))
169 enumerator
->destroy(enumerator
);
170 DBG1(DBG_IKE
, "found non skippable attribute %N",
171 simaka_attribute_names
, type
);
177 enumerator
->destroy(enumerator
);
179 /* verify MAC of EAP message, AT_MAC */
180 if (!in
->verify(in
, chunk_empty
))
182 DBG1(DBG_IKE
, "AT_MAC verification failed");
185 /* compare received RES against stored XRES */
186 if (!chunk_equals(res
, this->xres
))
188 DBG1(DBG_IKE
, "received RES does not match XRES");
195 * Process EAP-AKA/Response/SynchronizationFailure message
197 static status_t
process_synchronize(private_eap_aka_server_t
*this,
198 simaka_message_t
*in
, eap_payload_t
**out
)
200 sim_provider_t
*provider
;
201 enumerator_t
*enumerator
;
202 simaka_attribute_t type
;
203 chunk_t data
, auts
= chunk_empty
;
206 if (this->synchronized
)
208 DBG1(DBG_IKE
, "received %N, but peer did already resynchronize",
209 simaka_subtype_names
, AKA_SYNCHRONIZATION_FAILURE
);
213 DBG1(DBG_IKE
, "received synchronization request, retrying...");
215 enumerator
= in
->create_attribute_enumerator(in
);
216 while (enumerator
->enumerate(enumerator
, &type
, &data
))
224 if (!attribute_skippable(type
))
226 enumerator
->destroy(enumerator
);
227 DBG1(DBG_IKE
, "found non skippable attribute %N",
228 simaka_attribute_names
, type
);
234 enumerator
->destroy(enumerator
);
238 DBG1(DBG_IKE
, "synchronization request didn't contain usable AUTS");
242 enumerator
= charon
->sim
->create_provider_enumerator(charon
->sim
);
243 while (enumerator
->enumerate(enumerator
, &provider
))
245 if (provider
->resync(provider
, this->peer
, this->rand
.ptr
, auts
.ptr
))
251 enumerator
->destroy(enumerator
);
255 DBG1(DBG_IKE
, "no AKA provider found supporting "
256 "resynchronization for '%Y'", this->peer
);
259 this->synchronized
= TRUE
;
260 return initiate(this, out
);
264 * Process EAP-AKA/Response/ClientErrorCode message
266 static status_t
process_client_error(private_eap_aka_server_t
*this,
267 simaka_message_t
*in
)
269 enumerator_t
*enumerator
;
270 simaka_attribute_t type
;
273 enumerator
= in
->create_attribute_enumerator(in
);
274 while (enumerator
->enumerate(enumerator
, &type
, &data
))
276 if (type
== AT_CLIENT_ERROR_CODE
)
280 memcpy(&code
, data
.ptr
, sizeof(code
));
281 DBG1(DBG_IKE
, "received EAP-AKA client error '%N'",
282 simaka_client_error_names
, ntohs(code
));
284 else if (!simaka_attribute_skippable(type
))
289 enumerator
->destroy(enumerator
);
294 * Process EAP-AKA/Response/AuthenticationReject message
296 static status_t
process_authentication_reject(private_eap_aka_server_t
*this,
297 simaka_message_t
*in
)
299 DBG1(DBG_IKE
, "received %N, authentication failed",
300 simaka_subtype_names
, in
->get_subtype(in
));
305 * Implementation of eap_method_t.process
307 static status_t
process(private_eap_aka_server_t
*this,
308 eap_payload_t
*in
, eap_payload_t
**out
)
310 simaka_message_t
*message
;
313 message
= simaka_message_create_from_payload(in
, this->crypto
);
318 if (!message
->parse(message
))
320 message
->destroy(message
);
323 switch (message
->get_subtype(message
))
326 status
= process_challenge(this, message
);
328 case AKA_SYNCHRONIZATION_FAILURE
:
329 status
= process_synchronize(this, message
, out
);
331 case AKA_CLIENT_ERROR
:
332 status
= process_client_error(this, message
);
334 case AKA_AUTHENTICATION_REJECT
:
335 status
= process_authentication_reject(this, message
);
338 DBG1(DBG_IKE
, "unable to process EAP-AKA subtype %N",
339 simaka_subtype_names
, message
->get_subtype(message
));
343 message
->destroy(message
);
348 * Implementation of eap_method_t.get_type.
350 static eap_type_t
get_type(private_eap_aka_server_t
*this, u_int32_t
*vendor
)
357 * Implementation of eap_method_t.get_msk.
359 static status_t
get_msk(private_eap_aka_server_t
*this, chunk_t
*msk
)
370 * Implementation of eap_method_t.is_mutual.
372 static bool is_mutual(private_eap_aka_server_t
*this)
378 * Implementation of eap_method_t.destroy.
380 static void destroy(private_eap_aka_server_t
*this)
382 this->crypto
->destroy(this->crypto
);
383 this->peer
->destroy(this->peer
);
385 free(this->xres
.ptr
);
386 free(this->rand
.ptr
);
391 * Described in header.
393 eap_aka_server_t
*eap_aka_server_create(identification_t
*server
,
394 identification_t
*peer
)
396 private_eap_aka_server_t
*this = malloc_thing(private_eap_aka_server_t
);
398 this->public.interface
.initiate
= (status_t(*)(eap_method_t
*,eap_payload_t
**))initiate
;
399 this->public.interface
.process
= (status_t(*)(eap_method_t
*,eap_payload_t
*,eap_payload_t
**))process
;
400 this->public.interface
.get_type
= (eap_type_t(*)(eap_method_t
*,u_int32_t
*))get_type
;
401 this->public.interface
.is_mutual
= (bool(*)(eap_method_t
*))is_mutual
;
402 this->public.interface
.get_msk
= (status_t(*)(eap_method_t
*,chunk_t
*))get_msk
;
403 this->public.interface
.destroy
= (void(*)(eap_method_t
*))destroy
;
405 this->crypto
= simaka_crypto_create();
411 this->peer
= peer
->clone(peer
);
412 this->msk
= chunk_empty
;
413 this->xres
= chunk_empty
;
414 this->rand
= chunk_empty
;
416 this->synchronized
= FALSE
;
417 /* generate a non-zero identifier */
419 this->identifier
= random();
420 } while (!this->identifier
);
422 return &this->public;