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