Check rng return value when generating nonces in eap-sim plugin
[strongswan.git] / src / libcharon / plugins / eap_sim / eap_sim_peer.c
1 /*
2 * Copyright (C) 2007-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_sim_peer.h"
17
18 #include <daemon.h>
19
20 #include <simaka_message.h>
21 #include <simaka_manager.h>
22
23 /* number of tries we do authenticate */
24 #define MAX_TRIES 3
25
26 /* number of triplets for one authentication */
27 #define TRIPLET_COUNT 3
28
29 /** length of the AT_NONCE_MT nonce value */
30 #define NONCE_LEN 16
31
32 typedef struct private_eap_sim_peer_t private_eap_sim_peer_t;
33
34 /**
35 * Private data of an eap_sim_peer_t object.
36 */
37 struct private_eap_sim_peer_t {
38
39 /**
40 * Public authenticator_t interface.
41 */
42 eap_sim_peer_t public;
43
44 /**
45 * SIM backend manager
46 */
47 simaka_manager_t *mgr;
48
49 /**
50 * permanent ID of peer
51 */
52 identification_t *permanent;
53
54 /**
55 * Pseudonym identity the peer uses
56 */
57 identification_t *pseudonym;
58
59 /**
60 * Reauthentication identity the peer uses
61 */
62 identification_t *reauth;
63
64 /**
65 * EAP message identifier
66 */
67 u_int8_t identifier;
68
69 /**
70 * EAP-SIM crypto helper
71 */
72 simaka_crypto_t *crypto;
73
74 /**
75 * how many times we try to authenticate
76 */
77 int tries;
78
79 /**
80 * version list received from server
81 */
82 chunk_t version_list;
83
84 /**
85 * Nonce value used in AT_NONCE_MT/AT_NONCE_S
86 */
87 chunk_t nonce;
88
89 /**
90 * MSK, used for EAP-SIM based IKEv2 authentication
91 */
92 chunk_t msk;
93
94 /**
95 * Master key, if reauthentication is used
96 */
97 char mk[HASH_SIZE_SHA1];
98
99 /**
100 * Counter value if reauthentication is used
101 */
102 u_int16_t counter;
103 };
104
105 /* version of SIM protocol we speak */
106 static chunk_t version = chunk_from_chars(0x00,0x01);
107
108 /**
109 * Generate a payload from a message, destroy message
110 */
111 static bool generate_payload(simaka_message_t *message, chunk_t data,
112 eap_payload_t **out)
113 {
114 chunk_t chunk;
115 bool ok;
116
117 ok = message->generate(message, data, &chunk);
118 if (ok)
119 {
120 *out = eap_payload_create_data_own(chunk);
121 }
122 message->destroy(message);
123 return ok;
124 }
125
126 /**
127 * Create a SIM_CLIENT_ERROR
128 */
129 static bool create_client_error(private_eap_sim_peer_t *this,
130 simaka_client_error_t code, eap_payload_t **out)
131 {
132 simaka_message_t *message;
133 u_int16_t encoded;
134
135 DBG1(DBG_IKE, "sending client error '%N'", simaka_client_error_names, code);
136
137 message = simaka_message_create(FALSE, this->identifier, EAP_SIM,
138 SIM_CLIENT_ERROR, this->crypto);
139 encoded = htons(code);
140 message->add_attribute(message, AT_CLIENT_ERROR_CODE,
141 chunk_create((char*)&encoded, sizeof(encoded)));
142 return generate_payload(message, chunk_empty, out);
143 }
144
145 /**
146 * process an EAP-SIM/Request/Start message
147 */
148 static status_t process_start(private_eap_sim_peer_t *this,
149 simaka_message_t *in, eap_payload_t **out)
150 {
151 simaka_message_t *message;
152 enumerator_t *enumerator;
153 simaka_attribute_t type;
154 chunk_t data, id = chunk_empty;
155 rng_t *rng;
156 bool supported = FALSE;
157 simaka_attribute_t id_req = 0;
158
159 /* reset previously uses reauthentication/pseudonym data */
160 this->crypto->clear_keys(this->crypto);
161 DESTROY_IF(this->pseudonym);
162 this->pseudonym = NULL;
163 DESTROY_IF(this->reauth);
164 this->reauth = NULL;
165
166 enumerator = in->create_attribute_enumerator(in);
167 while (enumerator->enumerate(enumerator, &type, &data))
168 {
169 switch (type)
170 {
171 case AT_VERSION_LIST:
172 {
173 free(this->version_list.ptr);
174 this->version_list = chunk_clone(data);
175 while (data.len >= version.len)
176 {
177 if (memeq(data.ptr, version.ptr, version.len))
178 {
179 supported = TRUE;
180 break;
181 }
182 }
183 break;
184 }
185 case AT_ANY_ID_REQ:
186 case AT_FULLAUTH_ID_REQ:
187 case AT_PERMANENT_ID_REQ:
188 id_req = type;
189 break;
190 default:
191 if (!simaka_attribute_skippable(type))
192 {
193 enumerator->destroy(enumerator);
194 if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out))
195 {
196 return FAILED;
197 }
198 return NEED_MORE;
199 }
200 break;
201 }
202 }
203 enumerator->destroy(enumerator);
204
205 if (!supported)
206 {
207 DBG1(DBG_IKE, "server does not support EAP-SIM version number 1");
208 if (!create_client_error(this, SIM_UNSUPPORTED_VERSION, out))
209 {
210 return FAILED;
211 }
212 return NEED_MORE;
213 }
214
215 switch (id_req)
216 {
217 case AT_ANY_ID_REQ:
218 this->reauth = this->mgr->card_get_reauth(this->mgr,
219 this->permanent, this->mk, &this->counter);
220 if (this->reauth)
221 {
222 id = this->reauth->get_encoding(this->reauth);
223 break;
224 }
225 /* FALL */
226 case AT_FULLAUTH_ID_REQ:
227 this->pseudonym = this->mgr->card_get_pseudonym(this->mgr,
228 this->permanent);
229 if (this->pseudonym)
230 {
231 id = this->pseudonym->get_encoding(this->pseudonym);
232 break;
233 }
234 /* FALL */
235 case AT_PERMANENT_ID_REQ:
236 id = this->permanent->get_encoding(this->permanent);
237 break;
238 default:
239 break;
240 }
241
242 /* generate AT_NONCE_MT value */
243 rng = this->crypto->get_rng(this->crypto);
244 free(this->nonce.ptr);
245 if (!rng->allocate_bytes(rng, NONCE_LEN, &this->nonce))
246 {
247 return FAILED;
248 }
249
250 message = simaka_message_create(FALSE, this->identifier, EAP_SIM,
251 SIM_START, this->crypto);
252 if (!this->reauth)
253 {
254 message->add_attribute(message, AT_SELECTED_VERSION, version);
255 message->add_attribute(message, AT_NONCE_MT, this->nonce);
256 }
257 if (id.len)
258 {
259 message->add_attribute(message, AT_IDENTITY, id);
260 }
261 if (!generate_payload(message, chunk_empty, out))
262 {
263 return FAILED;
264 }
265 return NEED_MORE;
266 }
267
268 /**
269 * process an EAP-SIM/Request/Challenge message
270 */
271 static status_t process_challenge(private_eap_sim_peer_t *this,
272 simaka_message_t *in, eap_payload_t **out)
273 {
274 simaka_message_t *message;
275 enumerator_t *enumerator;
276 simaka_attribute_t type;
277 chunk_t data, rands = chunk_empty, kcs, kc, sreses, sres, mk;
278 identification_t *id;
279
280 if (this->tries-- <= 0)
281 {
282 /* give up without notification. This hack is required as some buggy
283 * server implementations won't respect our client-error. */
284 return FAILED;
285 }
286
287 enumerator = in->create_attribute_enumerator(in);
288 while (enumerator->enumerate(enumerator, &type, &data))
289 {
290 switch (type)
291 {
292 case AT_RAND:
293 rands = data;
294 break;
295 default:
296 if (!simaka_attribute_skippable(type))
297 {
298 enumerator->destroy(enumerator);
299 if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out))
300 {
301 return FAILED;
302 }
303 return NEED_MORE;
304 }
305 break;
306 }
307 }
308 enumerator->destroy(enumerator);
309
310 /* excepting two or three RAND, each 16 bytes. We require two valid
311 * and different RANDs */
312 if ((rands.len != 2 * SIM_RAND_LEN && rands.len != 3 * SIM_RAND_LEN) ||
313 memeq(rands.ptr, rands.ptr + SIM_RAND_LEN, SIM_RAND_LEN))
314 {
315 DBG1(DBG_IKE, "no valid AT_RAND received");
316 if (!create_client_error(this, SIM_INSUFFICIENT_CHALLENGES, out))
317 {
318 return FAILED;
319 }
320 return NEED_MORE;
321 }
322 /* get two or three KCs/SRESes from SIM using RANDs */
323 kcs = kc = chunk_alloca(rands.len / 2);
324 sreses = sres = chunk_alloca(rands.len / 4);
325 while (rands.len >= SIM_RAND_LEN)
326 {
327 if (!this->mgr->card_get_triplet(this->mgr, this->permanent,
328 rands.ptr, sres.ptr, kc.ptr))
329 {
330 DBG1(DBG_IKE, "unable to get EAP-SIM triplet");
331 if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out))
332 {
333 return FAILED;
334 }
335 return NEED_MORE;
336 }
337 DBG3(DBG_IKE, "got triplet for RAND %b\n Kc %b\n SRES %b",
338 rands.ptr, SIM_RAND_LEN, sres.ptr, SIM_SRES_LEN, kc.ptr, SIM_KC_LEN);
339 kc = chunk_skip(kc, SIM_KC_LEN);
340 sres = chunk_skip(sres, SIM_SRES_LEN);
341 rands = chunk_skip(rands, SIM_RAND_LEN);
342 }
343
344 id = this->permanent;
345 if (this->pseudonym)
346 {
347 id = this->pseudonym;
348 }
349 data = chunk_cata("cccc", kcs, this->nonce, this->version_list, version);
350 chunk_clear(&this->msk);
351 if (!this->crypto->derive_keys_full(this->crypto, id, data, &mk, &this->msk))
352 {
353 return FAILED;
354 }
355 memcpy(this->mk, mk.ptr, mk.len);
356 chunk_clear(&mk);
357
358 /* Verify AT_MAC attribute, signature is over "EAP packet | NONCE_MT", and
359 * parse() again after key derivation, reading encrypted attributes */
360 if (!in->verify(in, this->nonce) || !in->parse(in))
361 {
362 if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out))
363 {
364 return FAILED;
365 }
366 return NEED_MORE;
367 }
368
369 enumerator = in->create_attribute_enumerator(in);
370 while (enumerator->enumerate(enumerator, &type, &data))
371 {
372 switch (type)
373 {
374 case AT_NEXT_REAUTH_ID:
375 this->counter = 0;
376 id = identification_create_from_data(data);
377 this->mgr->card_set_reauth(this->mgr, this->permanent, id,
378 this->mk, this->counter);
379 id->destroy(id);
380 break;
381 case AT_NEXT_PSEUDONYM:
382 id = identification_create_from_data(data);
383 this->mgr->card_set_pseudonym(this->mgr, this->permanent, id);
384 id->destroy(id);
385 break;
386 default:
387 break;
388 }
389 }
390 enumerator->destroy(enumerator);
391
392 /* build response with AT_MAC, built over "EAP packet | n*SRES" */
393 message = simaka_message_create(FALSE, this->identifier, EAP_SIM,
394 SIM_CHALLENGE, this->crypto);
395 if (!generate_payload(message, sreses, out))
396 {
397 return FAILED;
398 }
399 return NEED_MORE;
400 }
401
402 /**
403 * Check if a received counter value is acceptable
404 */
405 static bool counter_too_small(private_eap_sim_peer_t *this, chunk_t chunk)
406 {
407 u_int16_t counter;
408
409 memcpy(&counter, chunk.ptr, sizeof(counter));
410 counter = htons(counter);
411 return counter < this->counter;
412 }
413
414 /**
415 * process an EAP-SIM/Request/Re-Authentication message
416 */
417 static status_t process_reauthentication(private_eap_sim_peer_t *this,
418 simaka_message_t *in, eap_payload_t **out)
419 {
420 simaka_message_t *message;
421 enumerator_t *enumerator;
422 simaka_attribute_t type;
423 chunk_t data, counter = chunk_empty, nonce = chunk_empty, id = chunk_empty;
424
425 if (!this->reauth)
426 {
427 DBG1(DBG_IKE, "received %N, but not expected",
428 simaka_subtype_names, SIM_REAUTHENTICATION);
429 if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out))
430 {
431 return FAILED;
432 }
433 return NEED_MORE;
434 }
435
436 if (!this->crypto->derive_keys_reauth(this->crypto,
437 chunk_create(this->mk, HASH_SIZE_SHA1)))
438 {
439 return FAILED;
440 }
441
442 /* verify MAC and parse again with decryption key */
443 if (!in->verify(in, chunk_empty) || !in->parse(in))
444 {
445 if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out))
446 {
447 return FAILED;
448 }
449 return NEED_MORE;
450 }
451
452 enumerator = in->create_attribute_enumerator(in);
453 while (enumerator->enumerate(enumerator, &type, &data))
454 {
455 switch (type)
456 {
457 case AT_COUNTER:
458 counter = data;
459 break;
460 case AT_NONCE_S:
461 nonce = data;
462 break;
463 case AT_NEXT_REAUTH_ID:
464 id = data;
465 break;
466 default:
467 if (!simaka_attribute_skippable(type))
468 {
469 enumerator->destroy(enumerator);
470 if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out))
471 {
472 return FAILED;
473 }
474 return NEED_MORE;
475 }
476 break;
477 }
478 }
479 enumerator->destroy(enumerator);
480
481 if (!nonce.len || !counter.len)
482 {
483 DBG1(DBG_IKE, "EAP-SIM/Request/Re-Authentication message incomplete");
484 if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out))
485 {
486 return FAILED;
487 }
488 return NEED_MORE;
489 }
490
491 message = simaka_message_create(FALSE, this->identifier, EAP_SIM,
492 SIM_REAUTHENTICATION, this->crypto);
493 if (counter_too_small(this, counter))
494 {
495 DBG1(DBG_IKE, "reauthentication counter too small");
496 message->add_attribute(message, AT_COUNTER_TOO_SMALL, chunk_empty);
497 }
498 else
499 {
500 chunk_clear(&this->msk);
501 if (!this->crypto->derive_keys_reauth_msk(this->crypto,
502 this->reauth, counter, nonce,
503 chunk_create(this->mk, HASH_SIZE_SHA1), &this->msk))
504 {
505 message->destroy(message);
506 return FAILED;
507 }
508 if (id.len)
509 {
510 identification_t *reauth;
511
512 reauth = identification_create_from_data(data);
513 this->mgr->card_set_reauth(this->mgr, this->permanent, reauth,
514 this->mk, this->counter);
515 reauth->destroy(reauth);
516 }
517 }
518 message->add_attribute(message, AT_COUNTER, counter);
519 if (!generate_payload(message, nonce, out))
520 {
521 return FAILED;
522 }
523 return NEED_MORE;
524 }
525
526 /**
527 * process an EAP-SIM/Request/Notification message
528 */
529 static status_t process_notification(private_eap_sim_peer_t *this,
530 simaka_message_t *in, eap_payload_t **out)
531 {
532 simaka_message_t *message;
533 enumerator_t *enumerator;
534 simaka_attribute_t type;
535 chunk_t data;
536 bool success = TRUE;
537
538 enumerator = in->create_attribute_enumerator(in);
539 while (enumerator->enumerate(enumerator, &type, &data))
540 {
541 if (type == AT_NOTIFICATION)
542 {
543 u_int16_t code;
544
545 memcpy(&code, data.ptr, sizeof(code));
546 code = ntohs(code);
547
548 /* test success bit */
549 if (!(data.ptr[0] & 0x80))
550 {
551 DBG1(DBG_IKE, "received EAP-SIM notification error '%N'",
552 simaka_notification_names, code);
553 }
554 else
555 {
556 DBG1(DBG_IKE, "received EAP-SIM notification '%N'",
557 simaka_notification_names, code);
558 }
559 }
560 else if (!simaka_attribute_skippable(type))
561 {
562 success = FALSE;
563 break;
564 }
565 }
566 enumerator->destroy(enumerator);
567
568 if (success)
569 { /* empty notification reply */
570 message = simaka_message_create(FALSE, this->identifier, EAP_SIM,
571 SIM_NOTIFICATION, this->crypto);
572 if (!generate_payload(message, chunk_empty, out))
573 {
574 return FAILED;
575 }
576 }
577 else
578 {
579 if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out))
580 {
581 return FAILED;
582 }
583 }
584 return NEED_MORE;
585 }
586
587 METHOD(eap_method_t, process, status_t,
588 private_eap_sim_peer_t *this, eap_payload_t *in, eap_payload_t **out)
589 {
590 simaka_message_t *message;
591 status_t status;
592
593 /* store received EAP message identifier */
594 this->identifier = in->get_identifier(in);
595
596 message = simaka_message_create_from_payload(in->get_data(in), this->crypto);
597 if (!message)
598 {
599 if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out))
600 {
601 return FAILED;
602 }
603 return NEED_MORE;
604 }
605 if (!message->parse(message))
606 {
607 message->destroy(message);
608 if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out))
609 {
610 return FAILED;
611 }
612 return NEED_MORE;
613 }
614 switch (message->get_subtype(message))
615 {
616 case SIM_START:
617 status = process_start(this, message, out);
618 break;
619 case SIM_CHALLENGE:
620 status = process_challenge(this, message, out);
621 break;
622 case SIM_REAUTHENTICATION:
623 status = process_reauthentication(this, message, out);
624 break;
625 case SIM_NOTIFICATION:
626 status = process_notification(this, message, out);
627 break;
628 default:
629 DBG1(DBG_IKE, "unable to process EAP-SIM subtype %N",
630 simaka_subtype_names, message->get_subtype(message));
631 if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out))
632 {
633 status = FAILED;
634 }
635 else
636 {
637 status = NEED_MORE;
638 }
639 break;
640 }
641 message->destroy(message);
642 return status;
643 }
644
645 METHOD(eap_method_t, initiate, status_t,
646 private_eap_sim_peer_t *this, eap_payload_t **out)
647 {
648 /* peer never initiates */
649 return FAILED;
650 }
651
652 METHOD(eap_method_t, get_type, eap_type_t,
653 private_eap_sim_peer_t *this, u_int32_t *vendor)
654 {
655 *vendor = 0;
656 return EAP_SIM;
657 }
658
659 METHOD(eap_method_t, get_msk, status_t,
660 private_eap_sim_peer_t *this, chunk_t *msk)
661 {
662 if (this->msk.ptr)
663 {
664 *msk = this->msk;
665 return SUCCESS;
666 }
667 return FAILED;
668 }
669
670 METHOD(eap_method_t, get_identifier, u_int8_t,
671 private_eap_sim_peer_t *this)
672 {
673 return this->identifier;
674 }
675
676 METHOD(eap_method_t, set_identifier, void,
677 private_eap_sim_peer_t *this, u_int8_t identifier)
678 {
679 this->identifier = identifier;
680 }
681
682 METHOD(eap_method_t, is_mutual, bool,
683 private_eap_sim_peer_t *this)
684 {
685 return TRUE;
686 }
687
688 METHOD(eap_method_t, destroy, void,
689 private_eap_sim_peer_t *this)
690 {
691 this->permanent->destroy(this->permanent);
692 DESTROY_IF(this->pseudonym);
693 DESTROY_IF(this->reauth);
694 this->crypto->destroy(this->crypto);
695 free(this->version_list.ptr);
696 free(this->nonce.ptr);
697 free(this->msk.ptr);
698 free(this);
699 }
700
701 /*
702 * Described in header.
703 */
704 eap_sim_peer_t *eap_sim_peer_create(identification_t *server,
705 identification_t *peer)
706 {
707 private_eap_sim_peer_t *this;
708
709 INIT(this,
710 .public = {
711 .interface = {
712 .initiate = _initiate,
713 .process = _process,
714 .get_type = _get_type,
715 .is_mutual = _is_mutual,
716 .get_msk = _get_msk,
717 .get_identifier = _get_identifier,
718 .set_identifier = _set_identifier,
719 .destroy = _destroy,
720 },
721 },
722 .crypto = simaka_crypto_create(EAP_SIM),
723 .mgr = lib->get(lib, "sim-manager"),
724 );
725
726 if (!this->crypto)
727 {
728 free(this);
729 return NULL;
730 }
731
732 this->permanent = peer->clone(peer);
733 this->tries = MAX_TRIES;
734
735 return &this->public;
736 }
737