479fb95fc55b3f8543f8a3bf70418063fc5b4efc
[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 rng->allocate_bytes(rng, NONCE_LEN, &this->nonce);
246
247 message = simaka_message_create(FALSE, this->identifier, EAP_SIM,
248 SIM_START, this->crypto);
249 if (!this->reauth)
250 {
251 message->add_attribute(message, AT_SELECTED_VERSION, version);
252 message->add_attribute(message, AT_NONCE_MT, this->nonce);
253 }
254 if (id.len)
255 {
256 message->add_attribute(message, AT_IDENTITY, id);
257 }
258 if (!generate_payload(message, chunk_empty, out))
259 {
260 return FAILED;
261 }
262 return NEED_MORE;
263 }
264
265 /**
266 * process an EAP-SIM/Request/Challenge message
267 */
268 static status_t process_challenge(private_eap_sim_peer_t *this,
269 simaka_message_t *in, eap_payload_t **out)
270 {
271 simaka_message_t *message;
272 enumerator_t *enumerator;
273 simaka_attribute_t type;
274 chunk_t data, rands = chunk_empty, kcs, kc, sreses, sres, mk;
275 identification_t *id;
276
277 if (this->tries-- <= 0)
278 {
279 /* give up without notification. This hack is required as some buggy
280 * server implementations won't respect our client-error. */
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_RAND:
290 rands = data;
291 break;
292 default:
293 if (!simaka_attribute_skippable(type))
294 {
295 enumerator->destroy(enumerator);
296 if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out))
297 {
298 return FAILED;
299 }
300 return NEED_MORE;
301 }
302 break;
303 }
304 }
305 enumerator->destroy(enumerator);
306
307 /* excepting two or three RAND, each 16 bytes. We require two valid
308 * and different RANDs */
309 if ((rands.len != 2 * SIM_RAND_LEN && rands.len != 3 * SIM_RAND_LEN) ||
310 memeq(rands.ptr, rands.ptr + SIM_RAND_LEN, SIM_RAND_LEN))
311 {
312 DBG1(DBG_IKE, "no valid AT_RAND received");
313 if (!create_client_error(this, SIM_INSUFFICIENT_CHALLENGES, out))
314 {
315 return FAILED;
316 }
317 return NEED_MORE;
318 }
319 /* get two or three KCs/SRESes from SIM using RANDs */
320 kcs = kc = chunk_alloca(rands.len / 2);
321 sreses = sres = chunk_alloca(rands.len / 4);
322 while (rands.len >= SIM_RAND_LEN)
323 {
324 if (!this->mgr->card_get_triplet(this->mgr, this->permanent,
325 rands.ptr, sres.ptr, kc.ptr))
326 {
327 DBG1(DBG_IKE, "unable to get EAP-SIM triplet");
328 if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out))
329 {
330 return FAILED;
331 }
332 return NEED_MORE;
333 }
334 DBG3(DBG_IKE, "got triplet for RAND %b\n Kc %b\n SRES %b",
335 rands.ptr, SIM_RAND_LEN, sres.ptr, SIM_SRES_LEN, kc.ptr, SIM_KC_LEN);
336 kc = chunk_skip(kc, SIM_KC_LEN);
337 sres = chunk_skip(sres, SIM_SRES_LEN);
338 rands = chunk_skip(rands, SIM_RAND_LEN);
339 }
340
341 id = this->permanent;
342 if (this->pseudonym)
343 {
344 id = this->pseudonym;
345 }
346 data = chunk_cata("cccc", kcs, this->nonce, this->version_list, version);
347 chunk_clear(&this->msk);
348 if (!this->crypto->derive_keys_full(this->crypto, id, data, &mk, &this->msk))
349 {
350 return FAILED;
351 }
352 memcpy(this->mk, mk.ptr, mk.len);
353 chunk_clear(&mk);
354
355 /* Verify AT_MAC attribute, signature is over "EAP packet | NONCE_MT", and
356 * parse() again after key derivation, reading encrypted attributes */
357 if (!in->verify(in, this->nonce) || !in->parse(in))
358 {
359 if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out))
360 {
361 return FAILED;
362 }
363 return NEED_MORE;
364 }
365
366 enumerator = in->create_attribute_enumerator(in);
367 while (enumerator->enumerate(enumerator, &type, &data))
368 {
369 switch (type)
370 {
371 case AT_NEXT_REAUTH_ID:
372 this->counter = 0;
373 id = identification_create_from_data(data);
374 this->mgr->card_set_reauth(this->mgr, this->permanent, id,
375 this->mk, this->counter);
376 id->destroy(id);
377 break;
378 case AT_NEXT_PSEUDONYM:
379 id = identification_create_from_data(data);
380 this->mgr->card_set_pseudonym(this->mgr, this->permanent, id);
381 id->destroy(id);
382 break;
383 default:
384 break;
385 }
386 }
387 enumerator->destroy(enumerator);
388
389 /* build response with AT_MAC, built over "EAP packet | n*SRES" */
390 message = simaka_message_create(FALSE, this->identifier, EAP_SIM,
391 SIM_CHALLENGE, this->crypto);
392 if (!generate_payload(message, sreses, out))
393 {
394 return FAILED;
395 }
396 return NEED_MORE;
397 }
398
399 /**
400 * Check if a received counter value is acceptable
401 */
402 static bool counter_too_small(private_eap_sim_peer_t *this, chunk_t chunk)
403 {
404 u_int16_t counter;
405
406 memcpy(&counter, chunk.ptr, sizeof(counter));
407 counter = htons(counter);
408 return counter < this->counter;
409 }
410
411 /**
412 * process an EAP-SIM/Request/Re-Authentication message
413 */
414 static status_t process_reauthentication(private_eap_sim_peer_t *this,
415 simaka_message_t *in, eap_payload_t **out)
416 {
417 simaka_message_t *message;
418 enumerator_t *enumerator;
419 simaka_attribute_t type;
420 chunk_t data, counter = chunk_empty, nonce = chunk_empty, id = chunk_empty;
421
422 if (!this->reauth)
423 {
424 DBG1(DBG_IKE, "received %N, but not expected",
425 simaka_subtype_names, SIM_REAUTHENTICATION);
426 if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out))
427 {
428 return FAILED;
429 }
430 return NEED_MORE;
431 }
432
433 if (!this->crypto->derive_keys_reauth(this->crypto,
434 chunk_create(this->mk, HASH_SIZE_SHA1)))
435 {
436 return FAILED;
437 }
438
439 /* verify MAC and parse again with decryption key */
440 if (!in->verify(in, chunk_empty) || !in->parse(in))
441 {
442 if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out))
443 {
444 return FAILED;
445 }
446 return NEED_MORE;
447 }
448
449 enumerator = in->create_attribute_enumerator(in);
450 while (enumerator->enumerate(enumerator, &type, &data))
451 {
452 switch (type)
453 {
454 case AT_COUNTER:
455 counter = data;
456 break;
457 case AT_NONCE_S:
458 nonce = data;
459 break;
460 case AT_NEXT_REAUTH_ID:
461 id = data;
462 break;
463 default:
464 if (!simaka_attribute_skippable(type))
465 {
466 enumerator->destroy(enumerator);
467 if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out))
468 {
469 return FAILED;
470 }
471 return NEED_MORE;
472 }
473 break;
474 }
475 }
476 enumerator->destroy(enumerator);
477
478 if (!nonce.len || !counter.len)
479 {
480 DBG1(DBG_IKE, "EAP-SIM/Request/Re-Authentication message incomplete");
481 if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out))
482 {
483 return FAILED;
484 }
485 return NEED_MORE;
486 }
487
488 message = simaka_message_create(FALSE, this->identifier, EAP_SIM,
489 SIM_REAUTHENTICATION, this->crypto);
490 if (counter_too_small(this, counter))
491 {
492 DBG1(DBG_IKE, "reauthentication counter too small");
493 message->add_attribute(message, AT_COUNTER_TOO_SMALL, chunk_empty);
494 }
495 else
496 {
497 chunk_clear(&this->msk);
498 if (!this->crypto->derive_keys_reauth_msk(this->crypto,
499 this->reauth, counter, nonce,
500 chunk_create(this->mk, HASH_SIZE_SHA1), &this->msk))
501 {
502 message->destroy(message);
503 return FAILED;
504 }
505 if (id.len)
506 {
507 identification_t *reauth;
508
509 reauth = identification_create_from_data(data);
510 this->mgr->card_set_reauth(this->mgr, this->permanent, reauth,
511 this->mk, this->counter);
512 reauth->destroy(reauth);
513 }
514 }
515 message->add_attribute(message, AT_COUNTER, counter);
516 if (!generate_payload(message, nonce, out))
517 {
518 return FAILED;
519 }
520 return NEED_MORE;
521 }
522
523 /**
524 * process an EAP-SIM/Request/Notification message
525 */
526 static status_t process_notification(private_eap_sim_peer_t *this,
527 simaka_message_t *in, eap_payload_t **out)
528 {
529 simaka_message_t *message;
530 enumerator_t *enumerator;
531 simaka_attribute_t type;
532 chunk_t data;
533 bool success = TRUE;
534
535 enumerator = in->create_attribute_enumerator(in);
536 while (enumerator->enumerate(enumerator, &type, &data))
537 {
538 if (type == AT_NOTIFICATION)
539 {
540 u_int16_t code;
541
542 memcpy(&code, data.ptr, sizeof(code));
543 code = ntohs(code);
544
545 /* test success bit */
546 if (!(data.ptr[0] & 0x80))
547 {
548 DBG1(DBG_IKE, "received EAP-SIM notification error '%N'",
549 simaka_notification_names, code);
550 }
551 else
552 {
553 DBG1(DBG_IKE, "received EAP-SIM notification '%N'",
554 simaka_notification_names, code);
555 }
556 }
557 else if (!simaka_attribute_skippable(type))
558 {
559 success = FALSE;
560 break;
561 }
562 }
563 enumerator->destroy(enumerator);
564
565 if (success)
566 { /* empty notification reply */
567 message = simaka_message_create(FALSE, this->identifier, EAP_SIM,
568 SIM_NOTIFICATION, this->crypto);
569 if (!generate_payload(message, chunk_empty, out))
570 {
571 return FAILED;
572 }
573 }
574 else
575 {
576 if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out))
577 {
578 return FAILED;
579 }
580 }
581 return NEED_MORE;
582 }
583
584 METHOD(eap_method_t, process, status_t,
585 private_eap_sim_peer_t *this, eap_payload_t *in, eap_payload_t **out)
586 {
587 simaka_message_t *message;
588 status_t status;
589
590 /* store received EAP message identifier */
591 this->identifier = in->get_identifier(in);
592
593 message = simaka_message_create_from_payload(in->get_data(in), this->crypto);
594 if (!message)
595 {
596 if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out))
597 {
598 return FAILED;
599 }
600 return NEED_MORE;
601 }
602 if (!message->parse(message))
603 {
604 message->destroy(message);
605 if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out))
606 {
607 return FAILED;
608 }
609 return NEED_MORE;
610 }
611 switch (message->get_subtype(message))
612 {
613 case SIM_START:
614 status = process_start(this, message, out);
615 break;
616 case SIM_CHALLENGE:
617 status = process_challenge(this, message, out);
618 break;
619 case SIM_REAUTHENTICATION:
620 status = process_reauthentication(this, message, out);
621 break;
622 case SIM_NOTIFICATION:
623 status = process_notification(this, message, out);
624 break;
625 default:
626 DBG1(DBG_IKE, "unable to process EAP-SIM subtype %N",
627 simaka_subtype_names, message->get_subtype(message));
628 if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out))
629 {
630 status = FAILED;
631 }
632 else
633 {
634 status = NEED_MORE;
635 }
636 break;
637 }
638 message->destroy(message);
639 return status;
640 }
641
642 METHOD(eap_method_t, initiate, status_t,
643 private_eap_sim_peer_t *this, eap_payload_t **out)
644 {
645 /* peer never initiates */
646 return FAILED;
647 }
648
649 METHOD(eap_method_t, get_type, eap_type_t,
650 private_eap_sim_peer_t *this, u_int32_t *vendor)
651 {
652 *vendor = 0;
653 return EAP_SIM;
654 }
655
656 METHOD(eap_method_t, get_msk, status_t,
657 private_eap_sim_peer_t *this, chunk_t *msk)
658 {
659 if (this->msk.ptr)
660 {
661 *msk = this->msk;
662 return SUCCESS;
663 }
664 return FAILED;
665 }
666
667 METHOD(eap_method_t, get_identifier, u_int8_t,
668 private_eap_sim_peer_t *this)
669 {
670 return this->identifier;
671 }
672
673 METHOD(eap_method_t, set_identifier, void,
674 private_eap_sim_peer_t *this, u_int8_t identifier)
675 {
676 this->identifier = identifier;
677 }
678
679 METHOD(eap_method_t, is_mutual, bool,
680 private_eap_sim_peer_t *this)
681 {
682 return TRUE;
683 }
684
685 METHOD(eap_method_t, destroy, void,
686 private_eap_sim_peer_t *this)
687 {
688 this->permanent->destroy(this->permanent);
689 DESTROY_IF(this->pseudonym);
690 DESTROY_IF(this->reauth);
691 this->crypto->destroy(this->crypto);
692 free(this->version_list.ptr);
693 free(this->nonce.ptr);
694 free(this->msk.ptr);
695 free(this);
696 }
697
698 /*
699 * Described in header.
700 */
701 eap_sim_peer_t *eap_sim_peer_create(identification_t *server,
702 identification_t *peer)
703 {
704 private_eap_sim_peer_t *this;
705
706 INIT(this,
707 .public = {
708 .interface = {
709 .initiate = _initiate,
710 .process = _process,
711 .get_type = _get_type,
712 .is_mutual = _is_mutual,
713 .get_msk = _get_msk,
714 .get_identifier = _get_identifier,
715 .set_identifier = _set_identifier,
716 .destroy = _destroy,
717 },
718 },
719 .crypto = simaka_crypto_create(EAP_SIM),
720 .mgr = lib->get(lib, "sim-manager"),
721 );
722
723 if (!this->crypto)
724 {
725 free(this);
726 return NULL;
727 }
728
729 this->permanent = peer->clone(peer);
730 this->tries = MAX_TRIES;
731
732 return &this->public;
733 }
734