Removed strayed code fragment
[strongswan.git] / src / charon / plugins / eap_aka / eap_aka_server.c
1 /*
2 * Copyright (C) 2006-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 "eap_aka_server.h"
17
18 #include <daemon.h>
19 #include <library.h>
20
21 #include <simaka_message.h>
22 #include <simaka_crypto.h>
23
24 /** length of the AT_NONCE_S value */
25 #define NONCE_LEN 16
26
27 typedef struct private_eap_aka_server_t private_eap_aka_server_t;
28
29 /**
30 * Private data of an eap_aka_server_t object.
31 */
32 struct private_eap_aka_server_t {
33
34 /**
35 * Public authenticator_t interface.
36 */
37 eap_aka_server_t public;
38
39 /**
40 * EAP-AKA crypto helper
41 */
42 simaka_crypto_t *crypto;
43
44 /**
45 * permanent ID of the peer
46 */
47 identification_t *permanent;
48
49 /**
50 * pseudonym ID of peer
51 */
52 identification_t *pseudonym;
53
54 /**
55 * reauthentication ID of peer
56 */
57 identification_t *reauth;
58
59 /**
60 * EAP identifier value
61 */
62 u_int8_t identifier;
63
64 /**
65 * Expected Result XRES
66 */
67 chunk_t xres;
68
69 /**
70 * Random value RAND
71 */
72 chunk_t rand;
73
74 /**
75 * MSK
76 */
77 chunk_t msk;
78
79 /**
80 * Nonce value used in AT_NONCE_S
81 */
82 chunk_t nonce;
83
84 /**
85 * Counter value negotiated, network order
86 */
87 chunk_t counter;
88
89 /**
90 * Do we request fast reauthentication?
91 */
92 bool use_reauth;
93
94 /**
95 * Do we request pseudonym identities?
96 */
97 bool use_pseudonym;
98
99 /**
100 * Do we request permanent identities?
101 */
102 bool use_permanent;
103
104 /**
105 * EAP-AKA message we have initiated
106 */
107 simaka_subtype_t pending;
108
109 /**
110 * Did the client send a synchronize request?
111 */
112 bool synchronized;
113 };
114
115 /**
116 * Create EAP-AKA/Request/Identity message
117 */
118 static status_t identity(private_eap_aka_server_t *this, eap_payload_t **out)
119 {
120 simaka_message_t *message;
121
122 message = simaka_message_create(TRUE, this->identifier++, EAP_AKA,
123 AKA_IDENTITY, this->crypto);
124 if (this->use_reauth)
125 {
126 message->add_attribute(message, AT_ANY_ID_REQ, chunk_empty);
127 }
128 else if (this->use_pseudonym)
129 {
130 message->add_attribute(message, AT_FULLAUTH_ID_REQ, chunk_empty);
131 }
132 else if (this->use_permanent)
133 {
134 message->add_attribute(message, AT_PERMANENT_ID_REQ, chunk_empty);
135 }
136 *out = message->generate(message, chunk_empty);
137 message->destroy(message);
138
139 this->pending = AKA_IDENTITY;
140 return NEED_MORE;
141 }
142
143 /**
144 * Create EAP-AKA/Request/Challenge message
145 */
146 static status_t challenge(private_eap_aka_server_t *this, eap_payload_t **out)
147 {
148 simaka_message_t *message;
149 char rand[AKA_RAND_LEN], xres[AKA_RES_MAX];
150 char ck[AKA_CK_LEN], ik[AKA_IK_LEN], autn[AKA_AUTN_LEN];
151 int xres_len;
152 chunk_t data, mk;
153 identification_t *id;
154
155 if (!charon->sim->provider_get_quintuplet(charon->sim, this->permanent,
156 rand, xres, &xres_len, ck, ik, autn))
157 {
158 if (this->use_pseudonym)
159 {
160 /* probably received a pseudonym/reauth id we couldn't map */
161 DBG1(DBG_IKE, "failed to map pseudonym/reauth identity '%Y', "
162 "fallback to permanent identity request", this->permanent);
163 this->use_pseudonym = FALSE;
164 DESTROY_IF(this->pseudonym);
165 this->pseudonym = NULL;
166 return identity(this, out);
167 }
168 return FAILED;
169 }
170 id = this->permanent;
171 if (this->pseudonym)
172 {
173 id = this->pseudonym;
174 }
175 data = chunk_cata("cc", chunk_create(ik, AKA_IK_LEN),
176 chunk_create(ck, AKA_CK_LEN));
177 free(this->msk.ptr);
178 this->msk = this->crypto->derive_keys_full(this->crypto, id, data, &mk);
179 this->rand = chunk_clone(chunk_create(rand, AKA_RAND_LEN));
180 this->xres = chunk_clone(chunk_create(xres, xres_len));
181
182 message = simaka_message_create(TRUE, this->identifier++, EAP_AKA,
183 AKA_CHALLENGE, this->crypto);
184 message->add_attribute(message, AT_RAND, this->rand);
185 message->add_attribute(message, AT_AUTN, chunk_create(autn, AKA_AUTN_LEN));
186 id = charon->sim->provider_gen_reauth(charon->sim, this->permanent, mk.ptr);
187 if (id)
188 {
189 message->add_attribute(message, AT_NEXT_REAUTH_ID,
190 id->get_encoding(id));
191 id->destroy(id);
192 }
193 else
194 {
195 id = charon->sim->provider_gen_pseudonym(charon->sim, this->permanent);
196 if (id)
197 {
198 message->add_attribute(message, AT_NEXT_PSEUDONYM,
199 id->get_encoding(id));
200 id->destroy(id);
201 }
202 }
203 *out = message->generate(message, chunk_empty);
204 message->destroy(message);
205
206 free(mk.ptr);
207 this->pending = AKA_CHALLENGE;
208 return NEED_MORE;
209 }
210
211 /**
212 * Initiate EAP-AKA/Request/Re-authentication message
213 */
214 static status_t reauthenticate(private_eap_aka_server_t *this,
215 char mk[HASH_SIZE_SHA1], u_int16_t counter,
216 eap_payload_t **out)
217 {
218 simaka_message_t *message;
219 identification_t *next;
220 chunk_t mkc;
221 rng_t *rng;
222
223 DBG1(DBG_IKE, "initiating EAP-AKA reauthentication");
224
225 rng = this->crypto->get_rng(this->crypto);
226 rng->allocate_bytes(rng, NONCE_LEN, &this->nonce);
227
228 mkc = chunk_create(mk, HASH_SIZE_SHA1);
229 counter = htons(counter);
230 this->counter = chunk_clone(chunk_create((char*)&counter, sizeof(counter)));
231
232 this->crypto->derive_keys_reauth(this->crypto, mkc);
233 this->msk = this->crypto->derive_keys_reauth_msk(this->crypto,
234 this->reauth, this->counter, this->nonce, mkc);
235
236 message = simaka_message_create(TRUE, this->identifier++, EAP_AKA,
237 AKA_REAUTHENTICATION, this->crypto);
238 message->add_attribute(message, AT_COUNTER, this->counter);
239 message->add_attribute(message, AT_NONCE_S, this->nonce);
240 next = charon->sim->provider_gen_reauth(charon->sim, this->permanent, mk);
241 if (next)
242 {
243 message->add_attribute(message, AT_NEXT_REAUTH_ID,
244 next->get_encoding(next));
245 next->destroy(next);
246 }
247 *out = message->generate(message, chunk_empty);
248 message->destroy(message);
249
250 this->pending = SIM_REAUTHENTICATION;
251 return NEED_MORE;
252 }
253
254 /**
255 * Implementation of eap_method_t.initiate
256 */
257 static status_t initiate(private_eap_aka_server_t *this, eap_payload_t **out)
258 {
259 if (this->use_permanent || this->use_pseudonym || this->use_reauth)
260 {
261 return identity(this, out);
262 }
263 return challenge(this, out);
264 }
265
266 /**
267 * Process EAP-AKA/Response/Identity message
268 */
269 static status_t process_identity(private_eap_aka_server_t *this,
270 simaka_message_t *in, eap_payload_t **out)
271 {
272 identification_t *permanent, *id;
273 enumerator_t *enumerator;
274 simaka_attribute_t type;
275 chunk_t data, identity = chunk_empty;
276
277 if (this->pending != AKA_IDENTITY)
278 {
279 DBG1(DBG_IKE, "received %N, but not expected",
280 simaka_subtype_names, AKA_IDENTITY);
281 return FAILED;
282 }
283
284 enumerator = in->create_attribute_enumerator(in);
285 while (enumerator->enumerate(enumerator, &type, &data))
286 {
287 switch (type)
288 {
289 case AT_IDENTITY:
290 identity = data;
291 break;
292 default:
293 if (!simaka_attribute_skippable(type))
294 {
295 enumerator->destroy(enumerator);
296 return FAILED;
297 }
298 break;
299 }
300 }
301 enumerator->destroy(enumerator);
302
303 if (!identity.len)
304 {
305 DBG1(DBG_IKE, "received incomplete Identity response");
306 return FAILED;
307 }
308
309 id = identification_create_from_data(identity);
310 if (this->use_reauth)
311 {
312 char mk[HASH_SIZE_SHA1];
313 u_int16_t counter;
314
315 permanent = charon->sim->provider_is_reauth(charon->sim, id,
316 mk, &counter);
317 if (permanent)
318 {
319 this->permanent->destroy(this->permanent);
320 this->permanent = permanent;
321 this->reauth = id;
322 return reauthenticate(this, mk, counter, out);
323 }
324 /* unable to map, maybe a pseudonym? */
325 DBG1(DBG_IKE, "'%Y' is not a reauth identity", id);
326 this->use_reauth = FALSE;
327 }
328 if (this->use_pseudonym)
329 {
330 permanent = charon->sim->provider_is_pseudonym(charon->sim, id);
331 if (permanent)
332 {
333 this->permanent->destroy(this->permanent);
334 this->permanent = permanent;
335 this->pseudonym = id->clone(id);
336 /* we already have a new permanent identity now */
337 this->use_permanent = FALSE;
338 }
339 else
340 {
341 DBG1(DBG_IKE, "'%Y' is not a pseudonym", id);
342 }
343 }
344 if (!this->pseudonym && this->use_permanent)
345 {
346 /* got a permanent identity or a pseudonym reauth id wou couldn't map,
347 * try to get quintuplets */
348 DBG1(DBG_IKE, "received identity '%Y'", id);
349 this->permanent->destroy(this->permanent);
350 this->permanent = id->clone(id);
351 }
352 id->destroy(id);
353
354 return challenge(this, out);
355 }
356
357 /**
358 * Process EAP-AKA/Response/Challenge message
359 */
360 static status_t process_challenge(private_eap_aka_server_t *this,
361 simaka_message_t *in)
362 {
363 enumerator_t *enumerator;
364 simaka_attribute_t type;
365 chunk_t data, res = chunk_empty;
366
367 if (this->pending != AKA_CHALLENGE)
368 {
369 DBG1(DBG_IKE, "received %N, but not expected",
370 simaka_subtype_names, AKA_CHALLENGE);
371 return FAILED;
372 }
373 /* verify MAC of EAP message, AT_MAC */
374 if (!in->verify(in, chunk_empty))
375 {
376 return FAILED;
377 }
378 enumerator = in->create_attribute_enumerator(in);
379 while (enumerator->enumerate(enumerator, &type, &data))
380 {
381 switch (type)
382 {
383 case AT_RES:
384 res = data;
385 break;
386 default:
387 if (!simaka_attribute_skippable(type))
388 {
389 enumerator->destroy(enumerator);
390 return FAILED;
391 }
392 break;
393 }
394 }
395 enumerator->destroy(enumerator);
396
397 /* compare received RES against stored XRES */
398 if (!chunk_equals(res, this->xres))
399 {
400 DBG1(DBG_IKE, "received RES does not match XRES");
401 return FAILED;
402 }
403 return SUCCESS;
404 }
405
406 /**
407 * process an EAP-AKA/Response/Reauthentication message
408 */
409 static status_t process_reauthentication(private_eap_aka_server_t *this,
410 simaka_message_t *in, eap_payload_t **out)
411 {
412 enumerator_t *enumerator;
413 simaka_attribute_t type;
414 chunk_t data, counter = chunk_empty;
415 bool too_small = FALSE;
416
417 if (this->pending != AKA_REAUTHENTICATION)
418 {
419 DBG1(DBG_IKE, "received %N, but not expected",
420 simaka_subtype_names, AKA_REAUTHENTICATION);
421 return FAILED;
422 }
423 /* verify AT_MAC attribute, signature is over "EAP packet | NONCE_S" */
424 if (!in->verify(in, this->nonce))
425 {
426 return FAILED;
427 }
428
429 enumerator = in->create_attribute_enumerator(in);
430 while (enumerator->enumerate(enumerator, &type, &data))
431 {
432 switch (type)
433 {
434 case AT_COUNTER:
435 counter = data;
436 break;
437 case AT_COUNTER_TOO_SMALL:
438 too_small = TRUE;
439 break;
440 default:
441 if (!simaka_attribute_skippable(type))
442 {
443 enumerator->destroy(enumerator);
444 return FAILED;
445 }
446 break;
447 }
448 }
449 enumerator->destroy(enumerator);
450
451 if (too_small)
452 {
453 DBG1(DBG_IKE, "received %N, initiating full authentication",
454 simaka_attribute_names, AT_COUNTER_TOO_SMALL);
455 this->use_reauth = FALSE;
456 this->crypto->clear_keys(this->crypto);
457 return challenge(this, out);
458 }
459 if (!chunk_equals(counter, this->counter))
460 {
461 DBG1(DBG_IKE, "received counter does not match");
462 return FAILED;
463 }
464 return SUCCESS;
465 }
466
467 /**
468 * Process EAP-AKA/Response/SynchronizationFailure message
469 */
470 static status_t process_synchronize(private_eap_aka_server_t *this,
471 simaka_message_t *in, eap_payload_t **out)
472 {
473 enumerator_t *enumerator;
474 simaka_attribute_t type;
475 chunk_t data, auts = chunk_empty;
476
477 if (this->synchronized)
478 {
479 DBG1(DBG_IKE, "received %N, but peer did already resynchronize",
480 simaka_subtype_names, AKA_SYNCHRONIZATION_FAILURE);
481 return FAILED;
482 }
483
484 DBG1(DBG_IKE, "received synchronization request, retrying...");
485
486 enumerator = in->create_attribute_enumerator(in);
487 while (enumerator->enumerate(enumerator, &type, &data))
488 {
489 switch (type)
490 {
491 case AT_AUTS:
492 auts = data;
493 break;
494 default:
495 if (!simaka_attribute_skippable(type))
496 {
497 enumerator->destroy(enumerator);
498 return FAILED;
499 }
500 break;
501 }
502 }
503 enumerator->destroy(enumerator);
504
505 if (!auts.len)
506 {
507 DBG1(DBG_IKE, "synchronization request didn't contain usable AUTS");
508 return FAILED;
509 }
510
511 if (!charon->sim->provider_resync(charon->sim, this->permanent,
512 this->rand.ptr, auts.ptr))
513 {
514 DBG1(DBG_IKE, "no AKA provider found supporting "
515 "resynchronization for '%Y'", this->permanent);
516 return FAILED;
517 }
518 this->synchronized = TRUE;
519 return challenge(this, out);
520 }
521
522 /**
523 * Process EAP-AKA/Response/ClientErrorCode message
524 */
525 static status_t process_client_error(private_eap_aka_server_t *this,
526 simaka_message_t *in)
527 {
528 enumerator_t *enumerator;
529 simaka_attribute_t type;
530 chunk_t data;
531
532 enumerator = in->create_attribute_enumerator(in);
533 while (enumerator->enumerate(enumerator, &type, &data))
534 {
535 if (type == AT_CLIENT_ERROR_CODE)
536 {
537 u_int16_t code;
538
539 memcpy(&code, data.ptr, sizeof(code));
540 DBG1(DBG_IKE, "received EAP-AKA client error '%N'",
541 simaka_client_error_names, ntohs(code));
542 }
543 else if (!simaka_attribute_skippable(type))
544 {
545 break;
546 }
547 }
548 enumerator->destroy(enumerator);
549 return FAILED;
550 }
551
552 /**
553 * Process EAP-AKA/Response/AuthenticationReject message
554 */
555 static status_t process_authentication_reject(private_eap_aka_server_t *this,
556 simaka_message_t *in)
557 {
558 DBG1(DBG_IKE, "received %N, authentication failed",
559 simaka_subtype_names, in->get_subtype(in));
560 return FAILED;
561 }
562
563 /**
564 * Implementation of eap_method_t.process
565 */
566 static status_t process(private_eap_aka_server_t *this,
567 eap_payload_t *in, eap_payload_t **out)
568 {
569 simaka_message_t *message;
570 status_t status;
571
572 message = simaka_message_create_from_payload(in, this->crypto);
573 if (!message)
574 {
575 return FAILED;
576 }
577 if (!message->parse(message))
578 {
579 message->destroy(message);
580 return FAILED;
581 }
582 switch (message->get_subtype(message))
583 {
584 case AKA_IDENTITY:
585 status = process_identity(this, message, out);
586 break;
587 case AKA_CHALLENGE:
588 status = process_challenge(this, message);
589 break;
590 case AKA_REAUTHENTICATION:
591 status = process_reauthentication(this, message, out);
592 break;
593 case AKA_SYNCHRONIZATION_FAILURE:
594 status = process_synchronize(this, message, out);
595 break;
596 case AKA_CLIENT_ERROR:
597 status = process_client_error(this, message);
598 break;
599 case AKA_AUTHENTICATION_REJECT:
600 status = process_authentication_reject(this, message);
601 break;
602 default:
603 DBG1(DBG_IKE, "unable to process EAP-AKA subtype %N",
604 simaka_subtype_names, message->get_subtype(message));
605 status = FAILED;
606 break;
607 }
608 message->destroy(message);
609 return status;
610 }
611
612 /**
613 * Implementation of eap_method_t.get_type.
614 */
615 static eap_type_t get_type(private_eap_aka_server_t *this, u_int32_t *vendor)
616 {
617 *vendor = 0;
618 return EAP_AKA;
619 }
620
621 /**
622 * Implementation of eap_method_t.get_msk.
623 */
624 static status_t get_msk(private_eap_aka_server_t *this, chunk_t *msk)
625 {
626 if (this->msk.ptr)
627 {
628 *msk = this->msk;
629 return SUCCESS;
630 }
631 return FAILED;
632 }
633
634 /**
635 * Implementation of eap_method_t.is_mutual.
636 */
637 static bool is_mutual(private_eap_aka_server_t *this)
638 {
639 return TRUE;
640 }
641
642 /**
643 * Implementation of eap_method_t.destroy.
644 */
645 static void destroy(private_eap_aka_server_t *this)
646 {
647 this->crypto->destroy(this->crypto);
648 this->permanent->destroy(this->permanent);
649 DESTROY_IF(this->pseudonym);
650 DESTROY_IF(this->reauth);
651 free(this->xres.ptr);
652 free(this->rand.ptr);
653 free(this->nonce.ptr);
654 free(this->msk.ptr);
655 free(this->counter.ptr);
656 free(this);
657 }
658
659 /*
660 * Described in header.
661 */
662 eap_aka_server_t *eap_aka_server_create(identification_t *server,
663 identification_t *peer)
664 {
665 private_eap_aka_server_t *this = malloc_thing(private_eap_aka_server_t);
666
667 this->public.interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate;
668 this->public.interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process;
669 this->public.interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type;
670 this->public.interface.is_mutual = (bool(*)(eap_method_t*))is_mutual;
671 this->public.interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk;
672 this->public.interface.destroy = (void(*)(eap_method_t*))destroy;
673
674 this->crypto = simaka_crypto_create();
675 if (!this->crypto)
676 {
677 free(this);
678 return NULL;
679 }
680 this->permanent = peer->clone(peer);
681 this->pseudonym = NULL;
682 this->reauth = NULL;
683 this->xres = chunk_empty;
684 this->rand = chunk_empty;
685 this->nonce = chunk_empty;
686 this->msk = chunk_empty;
687 this->counter = chunk_empty;
688 this->pending = 0;
689 this->synchronized = FALSE;
690 this->use_reauth = this->use_pseudonym = this->use_permanent =
691 lib->settings->get_bool(lib->settings,
692 "charon.plugins.eap-aka.request_identity", TRUE);
693 /* generate a non-zero identifier */
694 do {
695 this->identifier = random();
696 } while (!this->identifier);
697
698 return &this->public;
699 }
700