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