Check rng return value when generating nonces in eap-aka plugin
[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 if (!rng->allocate_bytes(rng, NONCE_LEN, &this->nonce))
253 {
254 return FAILED;
255 }
256
257 mkc = chunk_create(mk, HASH_SIZE_SHA1);
258 counter = htons(counter);
259 this->counter = chunk_clone(chunk_create((char*)&counter, sizeof(counter)));
260
261 if (!this->crypto->derive_keys_reauth(this->crypto, mkc) ||
262 !this->crypto->derive_keys_reauth_msk(this->crypto,
263 this->reauth, this->counter, this->nonce, mkc, &this->msk))
264 {
265 return FAILED;
266 }
267
268 message = simaka_message_create(TRUE, this->identifier++, EAP_AKA,
269 AKA_REAUTHENTICATION, this->crypto);
270 message->add_attribute(message, AT_COUNTER, this->counter);
271 message->add_attribute(message, AT_NONCE_S, this->nonce);
272 next = this->mgr->provider_gen_reauth(this->mgr, this->permanent, mk);
273 if (next)
274 {
275 message->add_attribute(message, AT_NEXT_REAUTH_ID,
276 next->get_encoding(next));
277 next->destroy(next);
278 }
279 if (!generate_payload(message, chunk_empty, out))
280 {
281 return FAILED;
282 }
283 this->pending = SIM_REAUTHENTICATION;
284 return NEED_MORE;
285 }
286
287 METHOD(eap_method_t, initiate, status_t,
288 private_eap_aka_server_t *this, eap_payload_t **out)
289 {
290 if (this->use_permanent || this->use_pseudonym || this->use_reauth)
291 {
292 return identity(this, out);
293 }
294 return challenge(this, out);
295 }
296
297 /**
298 * Process EAP-AKA/Response/Identity message
299 */
300 static status_t process_identity(private_eap_aka_server_t *this,
301 simaka_message_t *in, eap_payload_t **out)
302 {
303 identification_t *permanent, *id;
304 enumerator_t *enumerator;
305 simaka_attribute_t type;
306 chunk_t data, identity = chunk_empty;
307
308 if (this->pending != AKA_IDENTITY)
309 {
310 DBG1(DBG_IKE, "received %N, but not expected",
311 simaka_subtype_names, AKA_IDENTITY);
312 return FAILED;
313 }
314
315 enumerator = in->create_attribute_enumerator(in);
316 while (enumerator->enumerate(enumerator, &type, &data))
317 {
318 switch (type)
319 {
320 case AT_IDENTITY:
321 identity = data;
322 break;
323 default:
324 if (!simaka_attribute_skippable(type))
325 {
326 enumerator->destroy(enumerator);
327 return FAILED;
328 }
329 break;
330 }
331 }
332 enumerator->destroy(enumerator);
333
334 if (!identity.len)
335 {
336 DBG1(DBG_IKE, "received incomplete Identity response");
337 return FAILED;
338 }
339
340 id = identification_create_from_data(identity);
341 if (this->use_reauth)
342 {
343 char mk[HASH_SIZE_SHA1];
344 u_int16_t counter;
345
346 permanent = this->mgr->provider_is_reauth(this->mgr, id, mk, &counter);
347 if (permanent)
348 {
349 this->permanent->destroy(this->permanent);
350 this->permanent = permanent;
351 this->reauth = id;
352 return reauthenticate(this, mk, counter, out);
353 }
354 /* unable to map, maybe a pseudonym? */
355 DBG1(DBG_IKE, "'%Y' is not a reauth identity", id);
356 this->use_reauth = FALSE;
357 }
358 if (this->use_pseudonym)
359 {
360 permanent = this->mgr->provider_is_pseudonym(this->mgr, id);
361 if (permanent)
362 {
363 this->permanent->destroy(this->permanent);
364 this->permanent = permanent;
365 this->pseudonym = id->clone(id);
366 /* we already have a new permanent identity now */
367 this->use_permanent = FALSE;
368 }
369 else
370 {
371 DBG1(DBG_IKE, "'%Y' is not a pseudonym", id);
372 }
373 }
374 if (!this->pseudonym && this->use_permanent)
375 {
376 /* got a permanent identity or a pseudonym reauth id wou couldn't map,
377 * try to get quintuplets */
378 DBG1(DBG_IKE, "received identity '%Y'", id);
379 this->permanent->destroy(this->permanent);
380 this->permanent = id->clone(id);
381 }
382 id->destroy(id);
383
384 return challenge(this, out);
385 }
386
387 /**
388 * Process EAP-AKA/Response/Challenge message
389 */
390 static status_t process_challenge(private_eap_aka_server_t *this,
391 simaka_message_t *in)
392 {
393 enumerator_t *enumerator;
394 simaka_attribute_t type;
395 chunk_t data, res = chunk_empty;
396
397 if (this->pending != AKA_CHALLENGE)
398 {
399 DBG1(DBG_IKE, "received %N, but not expected",
400 simaka_subtype_names, AKA_CHALLENGE);
401 return FAILED;
402 }
403 /* verify MAC of EAP message, AT_MAC */
404 if (!in->verify(in, chunk_empty))
405 {
406 return FAILED;
407 }
408 enumerator = in->create_attribute_enumerator(in);
409 while (enumerator->enumerate(enumerator, &type, &data))
410 {
411 switch (type)
412 {
413 case AT_RES:
414 res = data;
415 break;
416 default:
417 if (!simaka_attribute_skippable(type))
418 {
419 enumerator->destroy(enumerator);
420 return FAILED;
421 }
422 break;
423 }
424 }
425 enumerator->destroy(enumerator);
426
427 /* compare received RES against stored XRES */
428 if (!chunk_equals(res, this->xres))
429 {
430 DBG1(DBG_IKE, "received RES does not match XRES");
431 return FAILED;
432 }
433 return SUCCESS;
434 }
435
436 /**
437 * process an EAP-AKA/Response/Reauthentication message
438 */
439 static status_t process_reauthentication(private_eap_aka_server_t *this,
440 simaka_message_t *in, eap_payload_t **out)
441 {
442 enumerator_t *enumerator;
443 simaka_attribute_t type;
444 chunk_t data, counter = chunk_empty;
445 bool too_small = FALSE;
446
447 if (this->pending != AKA_REAUTHENTICATION)
448 {
449 DBG1(DBG_IKE, "received %N, but not expected",
450 simaka_subtype_names, AKA_REAUTHENTICATION);
451 return FAILED;
452 }
453 /* verify AT_MAC attribute, signature is over "EAP packet | NONCE_S" */
454 if (!in->verify(in, this->nonce))
455 {
456 return FAILED;
457 }
458
459 enumerator = in->create_attribute_enumerator(in);
460 while (enumerator->enumerate(enumerator, &type, &data))
461 {
462 switch (type)
463 {
464 case AT_COUNTER:
465 counter = data;
466 break;
467 case AT_COUNTER_TOO_SMALL:
468 too_small = TRUE;
469 break;
470 default:
471 if (!simaka_attribute_skippable(type))
472 {
473 enumerator->destroy(enumerator);
474 return FAILED;
475 }
476 break;
477 }
478 }
479 enumerator->destroy(enumerator);
480
481 if (too_small)
482 {
483 DBG1(DBG_IKE, "received %N, initiating full authentication",
484 simaka_attribute_names, AT_COUNTER_TOO_SMALL);
485 this->use_reauth = FALSE;
486 this->crypto->clear_keys(this->crypto);
487 return challenge(this, out);
488 }
489 if (!chunk_equals(counter, this->counter))
490 {
491 DBG1(DBG_IKE, "received counter does not match");
492 return FAILED;
493 }
494 return SUCCESS;
495 }
496
497 /**
498 * Process EAP-AKA/Response/SynchronizationFailure message
499 */
500 static status_t process_synchronize(private_eap_aka_server_t *this,
501 simaka_message_t *in, eap_payload_t **out)
502 {
503 enumerator_t *enumerator;
504 simaka_attribute_t type;
505 chunk_t data, auts = chunk_empty;
506
507 if (this->synchronized)
508 {
509 DBG1(DBG_IKE, "received %N, but peer did already resynchronize",
510 simaka_subtype_names, AKA_SYNCHRONIZATION_FAILURE);
511 return FAILED;
512 }
513
514 DBG1(DBG_IKE, "received synchronization request, retrying...");
515
516 enumerator = in->create_attribute_enumerator(in);
517 while (enumerator->enumerate(enumerator, &type, &data))
518 {
519 switch (type)
520 {
521 case AT_AUTS:
522 auts = data;
523 break;
524 default:
525 if (!simaka_attribute_skippable(type))
526 {
527 enumerator->destroy(enumerator);
528 return FAILED;
529 }
530 break;
531 }
532 }
533 enumerator->destroy(enumerator);
534
535 if (!auts.len)
536 {
537 DBG1(DBG_IKE, "synchronization request didn't contain usable AUTS");
538 return FAILED;
539 }
540
541 if (!this->mgr->provider_resync(this->mgr, this->permanent,
542 this->rand.ptr, auts.ptr))
543 {
544 DBG1(DBG_IKE, "no AKA provider found supporting "
545 "resynchronization for '%Y'", this->permanent);
546 return FAILED;
547 }
548 this->synchronized = TRUE;
549 return challenge(this, out);
550 }
551
552 /**
553 * Process EAP-AKA/Response/ClientErrorCode message
554 */
555 static status_t process_client_error(private_eap_aka_server_t *this,
556 simaka_message_t *in)
557 {
558 enumerator_t *enumerator;
559 simaka_attribute_t type;
560 chunk_t data;
561
562 enumerator = in->create_attribute_enumerator(in);
563 while (enumerator->enumerate(enumerator, &type, &data))
564 {
565 if (type == AT_CLIENT_ERROR_CODE)
566 {
567 u_int16_t code;
568
569 memcpy(&code, data.ptr, sizeof(code));
570 DBG1(DBG_IKE, "received EAP-AKA client error '%N'",
571 simaka_client_error_names, ntohs(code));
572 }
573 else if (!simaka_attribute_skippable(type))
574 {
575 break;
576 }
577 }
578 enumerator->destroy(enumerator);
579 return FAILED;
580 }
581
582 /**
583 * Process EAP-AKA/Response/AuthenticationReject message
584 */
585 static status_t process_authentication_reject(private_eap_aka_server_t *this,
586 simaka_message_t *in)
587 {
588 DBG1(DBG_IKE, "received %N, authentication failed",
589 simaka_subtype_names, in->get_subtype(in));
590 return FAILED;
591 }
592
593 METHOD(eap_method_t, process, status_t,
594 private_eap_aka_server_t *this, eap_payload_t *in, eap_payload_t **out)
595 {
596 simaka_message_t *message;
597 status_t status;
598
599 message = simaka_message_create_from_payload(in->get_data(in), this->crypto);
600 if (!message)
601 {
602 return FAILED;
603 }
604 if (!message->parse(message))
605 {
606 message->destroy(message);
607 return FAILED;
608 }
609 switch (message->get_subtype(message))
610 {
611 case AKA_IDENTITY:
612 status = process_identity(this, message, out);
613 break;
614 case AKA_CHALLENGE:
615 status = process_challenge(this, message);
616 break;
617 case AKA_REAUTHENTICATION:
618 status = process_reauthentication(this, message, out);
619 break;
620 case AKA_SYNCHRONIZATION_FAILURE:
621 status = process_synchronize(this, message, out);
622 break;
623 case AKA_CLIENT_ERROR:
624 status = process_client_error(this, message);
625 break;
626 case AKA_AUTHENTICATION_REJECT:
627 status = process_authentication_reject(this, message);
628 break;
629 default:
630 DBG1(DBG_IKE, "unable to process EAP-AKA subtype %N",
631 simaka_subtype_names, message->get_subtype(message));
632 status = FAILED;
633 break;
634 }
635 message->destroy(message);
636 return status;
637 }
638
639 METHOD(eap_method_t, get_type, eap_type_t,
640 private_eap_aka_server_t *this, u_int32_t *vendor)
641 {
642 *vendor = 0;
643 return EAP_AKA;
644 }
645
646 METHOD(eap_method_t, get_msk, status_t,
647 private_eap_aka_server_t *this, chunk_t *msk)
648 {
649 if (this->msk.ptr)
650 {
651 *msk = this->msk;
652 return SUCCESS;
653 }
654 return FAILED;
655 }
656
657 METHOD(eap_method_t, get_identifier, u_int8_t,
658 private_eap_aka_server_t *this)
659 {
660 return this->identifier;
661 }
662
663 METHOD(eap_method_t, set_identifier, void,
664 private_eap_aka_server_t *this, u_int8_t identifier)
665 {
666 this->identifier = identifier;
667 }
668
669 METHOD(eap_method_t, is_mutual, bool,
670 private_eap_aka_server_t *this)
671 {
672 return TRUE;
673 }
674
675 METHOD(eap_method_t, destroy, void,
676 private_eap_aka_server_t *this)
677 {
678 this->crypto->destroy(this->crypto);
679 this->permanent->destroy(this->permanent);
680 DESTROY_IF(this->pseudonym);
681 DESTROY_IF(this->reauth);
682 free(this->xres.ptr);
683 free(this->rand.ptr);
684 free(this->nonce.ptr);
685 free(this->msk.ptr);
686 free(this->counter.ptr);
687 free(this);
688 }
689
690 /*
691 * Described in header.
692 */
693 eap_aka_server_t *eap_aka_server_create(identification_t *server,
694 identification_t *peer)
695 {
696 private_eap_aka_server_t *this;
697
698 INIT(this,
699 .public = {
700 .interface = {
701 .initiate = _initiate,
702 .process = _process,
703 .get_type = _get_type,
704 .is_mutual = _is_mutual,
705 .get_msk = _get_msk,
706 .get_identifier = _get_identifier,
707 .set_identifier = _set_identifier,
708 .destroy = _destroy,
709 },
710 },
711 .crypto = simaka_crypto_create(EAP_AKA),
712 .mgr = lib->get(lib, "aka-manager"),
713 );
714
715 if (!this->crypto)
716 {
717 free(this);
718 return NULL;
719 }
720
721 this->permanent = peer->clone(peer);
722 this->use_reauth = this->use_pseudonym = this->use_permanent =
723 lib->settings->get_bool(lib->settings,
724 "%s.plugins.eap-aka.request_identity", TRUE, charon->name);
725
726 /* generate a non-zero identifier */
727 do {
728 this->identifier = random();
729 } while (!this->identifier);
730
731 return &this->public;
732 }
733