11d2e74bc5afe35d72622db2125f2b38c5218e17
[strongswan.git] / src / charon / plugins / eap_sim / eap_sim.c
1 /*
2 * Copyright (C) 2007 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 * $Id$
16 */
17
18 #include "eap_sim.h"
19
20 #include <dlfcn.h>
21
22 #include <daemon.h>
23 #include <library.h>
24
25 #define MAX_TRIES 3
26
27 /* number of triplets for one authentication */
28 #define TRIPLET_COUNT 3
29
30 typedef enum sim_subtype_t sim_subtype_t;
31
32 /**
33 * Subtypes of SIM messages
34 */
35 enum sim_subtype_t {
36 SIM_START = 10,
37 SIM_CHALLENGE = 11,
38 SIM_NOTIFICATION = 12,
39 SIM_CLIENT_ERROR = 14,
40 };
41
42 ENUM(sim_subtype_names, SIM_START, SIM_CLIENT_ERROR,
43 "SIM_START",
44 "SIM_CHALLENGE",
45 "SIM_NOTIFICATION",
46 "SIM_13",
47 "SIM_CLIENT_ERROR",
48 );
49
50 typedef enum sim_attribute_t sim_attribute_t;
51
52 /**
53 * Attributes in SIM messages
54 */
55 enum sim_attribute_t {
56 /** defines the end of attribute list */
57 AT_END = -1,
58 AT_RAND = 1,
59 AT_AUTN = 2,
60 AT_RES = 3,
61 AT_AUTS = 4,
62 AT_PADDING = 6,
63 AT_NONCE_MT = 7,
64 AT_PERMANENT_ID_REQ = 10,
65 AT_MAC = 11,
66 AT_NOTIFICATION = 12,
67 AT_ANY_ID_REQ = 13,
68 AT_IDENTITY = 14,
69 AT_VERSION_LIST = 15,
70 AT_SELECTED_VERSION = 16,
71 AT_FULLAUTH_ID_REQ = 17,
72 AT_COUNTER = 19,
73 AT_COUNTER_TOO_SMALL = 20,
74 AT_NONCE_S = 21,
75 AT_CLIENT_ERROR_CODE = 22,
76 AT_IV = 129,
77 AT_ENCR_DATA = 130,
78 AT_NEXT_PSEUDONYM = 132,
79 AT_NEXT_REAUTH_ID = 133,
80 AT_CHECKCODE = 134,
81 AT_RESULT_IND = 135,
82 };
83
84 ENUM_BEGIN(sim_attribute_names, AT_END, AT_CLIENT_ERROR_CODE,
85 "AT_END",
86 "AT_0",
87 "AT_RAND",
88 "AT_AUTN",
89 "AT_RES",
90 "AT_AUTS",
91 "AT_5",
92 "AT_PADDING",
93 "AT_NONCE_MT",
94 "AT_8",
95 "AT_9",
96 "AT_PERMANENT_ID_REQ",
97 "AT_MAC",
98 "AT_NOTIFICATION",
99 "AT_ANY_ID_REQ",
100 "AT_IDENTITY",
101 "AT_VERSION_LIST",
102 "AT_SELECTED_VERSION",
103 "AT_FULLAUTH_ID_REQ",
104 "AT_18",
105 "AT_COUNTER",
106 "AT_COUNTER_TOO_SMALL",
107 "AT_NONCE_S",
108 "AT_CLIENT_ERROR_CODE");
109 ENUM_NEXT(sim_attribute_names, AT_IV, AT_RESULT_IND, AT_CLIENT_ERROR_CODE,
110 "AT_IV",
111 "AT_ENCR_DATA",
112 "AT_131",
113 "AT_NEXT_PSEUDONYM",
114 "AT_NEXT_REAUTH_ID",
115 "AT_CHECKCODE",
116 "AT_RESULT_IND");
117 ENUM_END(sim_attribute_names, AT_RESULT_IND);
118
119
120 typedef struct private_eap_sim_t private_eap_sim_t;
121
122 /**
123 * Private data of an eap_sim_t object.
124 */
125 struct private_eap_sim_t {
126
127 /**
128 * Public authenticator_t interface.
129 */
130 eap_sim_t public;
131
132 /**
133 * ID of ourself
134 */
135 identification_t *peer;
136
137 /**
138 * hashing function
139 */
140 hasher_t *hasher;
141
142 /**
143 * prf
144 */
145 prf_t *prf;
146
147 /**
148 * MAC function
149 */
150 signer_t *signer;
151
152 /**
153 * SIM cardreader function loaded from library
154 */
155 sim_algo_t alg;
156
157 /**
158 * libraries get_triplet() function returning a triplet
159 */
160 sim_get_triplet_t get_triplet;
161
162 /**
163 * handle of the loaded library
164 */
165 void *handle;
166
167 /**
168 * how many times we try to authenticate
169 */
170 int tries;
171
172 /**
173 * unique EAP identifier
174 */
175 u_int8_t identifier;
176
177 /**
178 * EAP message type this role sends
179 */
180 u_int8_t type;
181
182 /**
183 * version this implementation uses
184 */
185 chunk_t version;
186
187 /**
188 * version list received from server
189 */
190 chunk_t version_list;
191
192 /**
193 * Nonce value used in AT_NONCE_MT
194 */
195 chunk_t nonce;
196
197 /**
198 * concatenated SRES values
199 */
200 chunk_t sreses;
201
202 /**
203 * k_encr key derived from MK
204 */
205 chunk_t k_encr;
206
207 /**
208 * k_auth key derived from MK, used for AT_MAC verification
209 */
210 chunk_t k_auth;
211
212 /**
213 * MSK, used for EAP-SIM based IKEv2 authentication
214 */
215 chunk_t msk;
216
217 /**
218 * EMSK, extendes MSK for further uses
219 */
220 chunk_t emsk;
221 };
222
223 /** length of the AT_NONCE_MT nonce value */
224 #define NONCE_LEN 16
225 /** length of the AT_MAC value */
226 #define MAC_LEN 16
227 /** length of the AT_RAND value */
228 #define RAND_LEN 16
229 /** length of Kc */
230 #define KC_LEN 8
231 /** length of SRES */
232 #define SRES_LEN 4
233 /** length of the k_encr key */
234 #define KENCR_LEN 16
235 /** length of the k_auth key */
236 #define KAUTH_LEN 16
237 /** length of the MSK */
238 #define MSK_LEN 64
239 /** length of the EMSK */
240 #define EMSK_LEN 64
241
242 static char version[] = {0x00,0x01};
243 /* client error codes used in AT_CLIENT_ERROR_CODE */
244 char client_error_general_buf[] = {0x00, 0x01};
245 char client_error_unsupported_buf[] = {0x00, 0x02};
246 char client_error_insufficient_buf[] = {0x00, 0x03};
247 char client_error_notfresh_buf[] = {0x00, 0x04};
248 chunk_t client_error_general = chunk_from_buf(client_error_general_buf);
249 chunk_t client_error_unsupported = chunk_from_buf(client_error_unsupported_buf);
250 chunk_t client_error_insufficient = chunk_from_buf(client_error_insufficient_buf);
251 chunk_t client_error_notfresh = chunk_from_buf(client_error_notfresh_buf);
252
253 /**
254 * Read EAP and EAP-SIM header, return SIM type
255 */
256 static sim_subtype_t read_header(chunk_t *message)
257 {
258 sim_subtype_t type;
259
260 if (message->len < 8)
261 {
262 *message = chunk_empty;
263 return 0;
264 }
265 type = *(message->ptr + 5);
266 *message = chunk_skip(*message, 8);
267 return type;
268 }
269
270 /**
271 * read the next attribute from the chunk data
272 */
273 static sim_attribute_t read_attribute(chunk_t *message, chunk_t *data)
274 {
275 sim_attribute_t attribute;
276 size_t length;
277
278 DBG3(DBG_IKE, "reading attribute from %B", message);
279
280 if (message->len < 2)
281 {
282 return AT_END;
283 }
284 attribute = *message->ptr++;
285 length = *message->ptr++ * 4 - 2;
286 message->len -= 2;
287 DBG3(DBG_IKE, "found attribute %N with length %d",
288 sim_attribute_names, attribute, length);
289
290 if (length > message->len)
291 {
292 return AT_END;
293 }
294 data->len = length;
295 data->ptr = message->ptr;
296 *message = chunk_skip(*message, length);
297 return attribute;
298 }
299
300 /**
301 * Build an EAP-SIM payload using a variable length attribute list.
302 * The variable argument takes a sim_attribute_t followed by its data in a chunk.
303 */
304 static eap_payload_t *build_payload(private_eap_sim_t *this, u_int8_t identifier,
305 sim_subtype_t type, ...)
306 {
307 chunk_t message = chunk_alloca(512);
308 chunk_t pos = message;
309 eap_payload_t *payload;
310 va_list args;
311 sim_attribute_t attr;
312 u_int8_t *mac_pos = NULL;
313 chunk_t mac_data = chunk_empty;
314
315 /* write EAP header, skip length bytes */
316 *pos.ptr++ = this->type;
317 *pos.ptr++ = identifier;
318 pos.ptr += 2;
319 pos.len -= 4;
320 /* write SIM header with type and subtype, zero reserved bytes */
321 *pos.ptr++ = EAP_SIM;
322 *pos.ptr++ = type;
323 *pos.ptr++ = 0;
324 *pos.ptr++ = 0;
325 pos.len -= 4;
326
327 va_start(args, type);
328 while ((attr = va_arg(args, sim_attribute_t)) != AT_END)
329 {
330 chunk_t data = va_arg(args, chunk_t);
331
332 DBG3(DBG_IKE, "building %N %B", sim_attribute_names, attr, &data);
333
334 /* write attribute header */
335 *pos.ptr++ = attr;
336 pos.len--;
337
338 switch (attr)
339 {
340 case AT_CLIENT_ERROR_CODE:
341 case AT_SELECTED_VERSION:
342 {
343 *pos.ptr = data.len/4 + 1;
344 pos = chunk_skip(pos, 1);
345 memcpy(pos.ptr, data.ptr, data.len);
346 pos = chunk_skip(pos, data.len);
347 break;
348 }
349 case AT_IDENTITY:
350 case AT_VERSION_LIST:
351 {
352 u_int16_t act_len = data.len;
353 /* align up to four byte */
354 if (data.len % 4)
355 {
356 chunk_t tmp = chunk_alloca((data.len/4)*4 + 4);
357 memset(tmp.ptr, 0, tmp.len);
358 memcpy(tmp.ptr, data.ptr, data.len);
359 data = tmp;
360 }
361 *pos.ptr = data.len/4 + 1;
362 pos = chunk_skip(pos, 1);
363 /* actual length in bytes */
364 *(u_int16_t*)pos.ptr = htons(act_len);
365 pos = chunk_skip(pos, sizeof(u_int16_t));
366 memcpy(pos.ptr, data.ptr, data.len);
367 pos = chunk_skip(pos, data.len);
368 break;
369 }
370 case AT_NONCE_MT:
371 {
372 *pos.ptr = data.len/4 + 1;
373 pos = chunk_skip(pos, 1);
374 memset(pos.ptr, 0, 2);
375 pos = chunk_skip(pos, 2);
376 memcpy(pos.ptr, data.ptr, data.len);
377 pos = chunk_skip(pos, data.len);
378 break;
379 }
380 case AT_MAC:
381 {
382 *pos.ptr++ = 5; pos.len--;
383 *pos.ptr++ = 0; pos.len--;
384 *pos.ptr++ = 0; pos.len--;
385 mac_pos = pos.ptr;
386 memset(mac_pos, 0, MAC_LEN);
387 pos = chunk_skip(pos, MAC_LEN);
388 mac_data = data;
389 break;
390 }
391 case AT_RAND:
392 {
393 *pos.ptr++ = data.len/4 + 1; pos.len--;
394 *pos.ptr++ = 0; pos.len--;
395 *pos.ptr++ = 0; pos.len--;
396 memcpy(pos.ptr, data.ptr, data.len);
397 pos = chunk_skip(pos, data.len);
398 break;
399 }
400 default:
401 DBG1(DBG_IKE, "no rule to build EAP_SIM attribute %N, skipped",
402 sim_attribute_names, attr);
403 break;
404 }
405 }
406 va_end(args);
407
408 /* calculate message length, write into header */
409 message.len = pos.ptr - message.ptr;
410 *(u_int16_t*)(message.ptr + 2) = htons(message.len);
411
412 /* create MAC if AT_MAC attribte was included. Append supplied va_arg
413 * chunk mac_data to "to-sign" chunk */
414 if (mac_pos)
415 {
416 this->signer->set_key(this->signer, this->k_auth);
417 mac_data = chunk_cata("cc", message, mac_data);
418 this->signer->get_signature(this->signer, mac_data, mac_pos);
419 DBG3(DBG_IKE, "AT_MAC signature of %B\n is %b",
420 &mac_data, mac_pos, MAC_LEN);
421 }
422
423 payload = eap_payload_create_data(message);
424
425 DBG3(DBG_IKE, "created EAP message %B", &message);
426 return payload;
427 }
428
429 /**
430 * process an EAP-SIM/Request/Start message
431 */
432 static status_t peer_process_start(private_eap_sim_t *this, eap_payload_t *in,
433 eap_payload_t **out)
434 {
435 chunk_t message, data;
436 sim_attribute_t attribute, include_id = AT_END;
437 u_int8_t identifier;
438
439 identifier = in->get_identifier(in);
440 message = in->get_data(in);
441 read_header(&message);
442
443 while ((attribute = read_attribute(&message, &data)) != AT_END)
444 {
445 switch (attribute)
446 {
447 case AT_VERSION_LIST:
448 {
449 /* check if server supports our implementation */
450 bool found = FALSE;
451 if (data.len > 2)
452 {
453 /* read actual length first */
454 data.len = min(data.len, ntohs(*(u_int16_t*)data.ptr) + 2);
455 data = chunk_skip(data, 2);
456 chunk_free(&this->version_list);
457 this->version_list = chunk_clone(data);
458 while (data.len >= this->version.len)
459 {
460 if (memeq(data.ptr, this->version.ptr, this->version.len))
461 {
462 found = TRUE;
463 break;
464 }
465 data = chunk_skip(data, this->version.len);
466 }
467 }
468 if (!found)
469 {
470 DBG1(DBG_IKE, "server does not support EAP_SIM "
471 "version number %#B", &this->version);
472 *out = build_payload(this, identifier, SIM_CLIENT_ERROR,
473 AT_CLIENT_ERROR_CODE, client_error_unsupported,
474 AT_END);
475 return NEED_MORE;
476 }
477 break;
478 }
479 case AT_PERMANENT_ID_REQ:
480 case AT_FULLAUTH_ID_REQ:
481 case AT_ANY_ID_REQ:
482 /* only include AT_IDENTITY if requested */
483 include_id = AT_IDENTITY;
484 break;
485 case AT_NOTIFICATION:
486 {
487 u_int16_t code = 0;
488 if (data.len == 2)
489 {
490 code = ntohs(*(u_int16_t*)data.ptr);
491 }
492 if (code <= 32767) /* no success bit */
493 {
494 DBG1(DBG_IKE, "received %N error %d",
495 sim_attribute_names, attribute, code);
496 *out = build_payload(this,
497 in->get_identifier(in), SIM_CLIENT_ERROR,
498 AT_CLIENT_ERROR_CODE, client_error_general,
499 AT_END);
500 return NEED_MORE;
501 }
502 else
503 {
504 DBG1(DBG_IKE, "received %N code %d",
505 sim_attribute_names, attribute, code);
506 }
507 break;
508 }
509 default:
510 DBG1(DBG_IKE, "ignoring EAP_SIM attribute %N",
511 sim_attribute_names, attribute);
512 break;
513 }
514 }
515
516 /* build payload. If "include_id" is AT_END, AT_IDENTITY is ommited */
517 *out = build_payload(this, identifier, SIM_START,
518 AT_SELECTED_VERSION, this->version,
519 AT_NONCE_MT, this->nonce,
520 include_id, this->peer->get_encoding(this->peer),
521 AT_END);
522 return NEED_MORE;
523 }
524
525 /**
526 * derive EAP keys from kc
527 */
528 static void derive_keys(private_eap_sim_t *this, chunk_t kcs)
529 {
530 chunk_t tmp, mk;
531 int i;
532
533 /* build MK = SHA1(Identity|n*Kc|NONCE_MT|Version List|Selected Version) */
534 tmp = chunk_cata("ccccc", this->peer->get_encoding(this->peer), kcs,
535 this->nonce, this->version_list, this->version);
536 mk = chunk_alloca(this->hasher->get_hash_size(this->hasher));
537 this->hasher->get_hash(this->hasher, tmp, mk.ptr);
538 DBG3(DBG_IKE, "MK = SHA1(%B\n) = %B", &tmp, &mk);
539
540 /* K_encr | K_auth | MSK | EMSK = prf() | prf() | prf() | prf()
541 * FIPS PRF has 320 bit block size, we need 160 byte for keys
542 * => run prf four times */
543 this->prf->set_key(this->prf, mk);
544 tmp = chunk_alloca(this->prf->get_block_size(this->prf) * 4);
545 for (i = 0; i < 4; i++)
546 {
547 this->prf->get_bytes(this->prf, chunk_empty, tmp.ptr + tmp.len / 4 * i);
548 }
549 chunk_free(&this->k_encr);
550 chunk_free(&this->k_auth);
551 chunk_free(&this->msk);
552 chunk_free(&this->emsk);
553 chunk_split(tmp, "aaaa", KENCR_LEN, &this->k_encr, KAUTH_LEN, &this->k_auth,
554 MSK_LEN, &this->msk, EMSK_LEN, &this->emsk);
555 DBG3(DBG_IKE, "K_encr %B\nK_auth %B\nMSK %B\nEMSK %B",
556 &this->k_encr, &this->k_auth, &this->msk, &this->emsk);
557 }
558
559 /**
560 * process an EAP-SIM/Request/Challenge message
561 */
562 static status_t peer_process_challenge(private_eap_sim_t *this,
563 eap_payload_t *in, eap_payload_t **out)
564 {
565 chunk_t message, data, tmp, kcs, kc, sreses, sres;
566 sim_attribute_t attribute;
567 u_int8_t identifier;
568 chunk_t mac = chunk_empty, rands = chunk_empty;
569
570 if (this->tries-- <= 0)
571 {
572 /* give up without notification. This hack is required as some buggy
573 * server implementations won't respect our client-error. */
574 return FAILED;
575 }
576
577 identifier = in->get_identifier(in);
578 message = in->get_data(in);
579 read_header(&message);
580
581 while ((attribute = read_attribute(&message, &data)) != AT_END)
582 {
583 switch (attribute)
584 {
585 case AT_RAND:
586 {
587 rands = chunk_skip(data, 2);
588 break;
589 }
590 case AT_MAC:
591 {
592 /* backup MAC, zero it inline for later verification */
593 data = chunk_skip(data, 2);
594 mac = chunk_clonea(data);
595 memset(data.ptr, 0, data.len);
596 break;
597 }
598 case AT_NOTIFICATION:
599 {
600 u_int16_t code = 0;
601 if (data.len == 2)
602 {
603 code = ntohs(*(u_int16_t*)data.ptr);
604 }
605 if (code <= 32767) /* no success bit */
606 {
607 DBG1(DBG_IKE, "received %N error %d",
608 sim_attribute_names, attribute, code);
609 *out = build_payload(this,
610 in->get_identifier(in), SIM_CLIENT_ERROR,
611 AT_CLIENT_ERROR_CODE, client_error_general,
612 AT_END);
613 return NEED_MORE;
614 }
615 else
616 {
617 DBG1(DBG_IKE, "received %N code %d",
618 sim_attribute_names, attribute, code);
619 }
620 break;
621 }
622 default:
623 DBG1(DBG_IKE, "ignoring EAP_SIM attribute %N",
624 sim_attribute_names, attribute);
625 break;
626 }
627 }
628
629 /* excepting two or three RAND, each 16 bytes. We require two valid
630 * and different RANDs */
631 if ((rands.len != 2 * RAND_LEN && rands.len != 3 * RAND_LEN) ||
632 memeq(rands.ptr, rands.ptr + RAND_LEN, RAND_LEN))
633 {
634 DBG1(DBG_IKE, "no valid AT_RAND received");
635 *out = build_payload(this, identifier, SIM_CLIENT_ERROR,
636 AT_CLIENT_ERROR_CODE, client_error_insufficient,
637 AT_END);
638 return NEED_MORE;
639 }
640 if (mac.len != MAC_LEN)
641 {
642 DBG1(DBG_IKE, "no valid AT_MAC received");
643 *out = build_payload(this, identifier, SIM_CLIENT_ERROR,
644 AT_CLIENT_ERROR_CODE, client_error_general,
645 AT_END);
646 return NEED_MORE;
647 }
648
649 /* get two or three KCs/SRESes from SIM using RANDs */
650 kcs = kc = chunk_alloca(rands.len / 2);
651 sreses = sres = chunk_alloca(rands.len / 4);
652 while (rands.len > 0)
653 {
654 int kc_len = kc.len, sres_len = sres.len;
655
656 if (this->alg(rands.ptr, RAND_LEN, sres.ptr, &sres_len, kc.ptr, &kc_len))
657 {
658 DBG1(DBG_IKE, "unable to get EAP-SIM triplet");
659 *out = build_payload(this, identifier, SIM_CLIENT_ERROR,
660 AT_CLIENT_ERROR_CODE, client_error_general,
661 AT_END);
662 return NEED_MORE;
663 }
664 DBG3(DBG_IKE, "got triplet for RAND %b\n Kc %b\n SRES %b",
665 rands.ptr, RAND_LEN, sres.ptr, sres_len, kc.ptr, kc_len);
666 kc = chunk_skip(kc, kc_len);
667 sres = chunk_skip(sres, sres_len);
668 rands = chunk_skip(rands, RAND_LEN);
669 }
670
671 derive_keys(this, kcs);
672
673 /* verify AT_MAC attribute, signature is over "EAP packet | NONCE_MT" */
674 this->signer->set_key(this->signer, this->k_auth);
675 tmp = chunk_cata("cc", in->get_data(in), this->nonce);
676 if (!this->signer->verify_signature(this->signer, tmp, mac))
677 {
678 DBG1(DBG_IKE, "AT_MAC verification failed");
679 *out = build_payload(this, identifier, SIM_CLIENT_ERROR,
680 AT_CLIENT_ERROR_CODE, client_error_general,
681 AT_END);
682 return NEED_MORE;
683 }
684
685 /* build response, AT_MAC is built over "EAP packet | n*SRES" */
686 *out = build_payload(this, identifier, SIM_CHALLENGE,
687 AT_MAC, sreses,
688 AT_END);
689 return NEED_MORE;
690 }
691
692 /**
693 * process an EAP-SIM/Response/Challenge message
694 */
695 static status_t server_process_challenge(private_eap_sim_t *this,
696 eap_payload_t *in, eap_payload_t **out)
697 {
698 chunk_t message, data;
699 sim_attribute_t attribute;
700 chunk_t mac = chunk_empty, tmp;
701
702 message = in->get_data(in);
703 read_header(&message);
704
705 while ((attribute = read_attribute(&message, &data)) != AT_END)
706 {
707 switch (attribute)
708 {
709 case AT_MAC:
710 /* MAC has two reserved bytes */
711 if (data.len == MAC_LEN + 2)
712 { /* clone and zero MAC for verification */
713 mac = chunk_clonea(chunk_skip(data, 2));
714 memset(data.ptr, 0, data.len);
715 }
716 break;
717 default:
718 DBG1(DBG_IKE, "ignoring EAP_SIM attribute %N",
719 sim_attribute_names, attribute);
720 break;
721 }
722 }
723 if (!mac.ptr)
724 {
725 DBG1(DBG_IKE, "no valid AT_MAC attribute received");
726 return FAILED;
727 }
728 /* verify AT_MAC attribute, signature is over "EAP packet | n*SRES" */
729 this->signer->set_key(this->signer, this->k_auth);
730 tmp = chunk_cata("cc", in->get_data(in), this->sreses);
731 if (!this->signer->verify_signature(this->signer, tmp, mac))
732 {
733 DBG1(DBG_IKE, "AT_MAC verification failed");
734 return FAILED;
735 }
736 return SUCCESS;
737 }
738
739 /**
740 * process an EAP-SIM/Response/Start message
741 */
742 static status_t server_process_start(private_eap_sim_t *this,
743 eap_payload_t *in, eap_payload_t **out)
744 {
745 chunk_t message, data;
746 sim_attribute_t attribute;
747 bool supported = FALSE;
748 chunk_t rands, rand, kcs, kc, sreses, sres;
749 char id[64];
750 int len, i, rand_len, kc_len, sres_len;
751
752 message = in->get_data(in);
753 read_header(&message);
754
755 while ((attribute = read_attribute(&message, &data)) != AT_END)
756 {
757 switch (attribute)
758 {
759 case AT_NONCE_MT:
760 if (data.len == NONCE_LEN + 2)
761 {
762 this->nonce = chunk_clone(chunk_skip(data, 2));
763 }
764 break;
765 case AT_SELECTED_VERSION:
766 if (chunk_equals(data, this->version))
767 {
768 supported = TRUE;
769 }
770 break;
771 default:
772 DBG1(DBG_IKE, "ignoring EAP_SIM attribute %N",
773 sim_attribute_names, attribute);
774 break;
775 }
776 }
777 if (!supported || !this->nonce.ptr)
778 {
779 DBG1(DBG_IKE, "received incomplete EAP-SIM/Response/Start");
780 return FAILED;
781 }
782 len = snprintf(id, sizeof(id), "%D", this->peer);
783 if (len > sizeof(id) || len < 0)
784 {
785 return FAILED;
786 }
787
788 /* read triplets from provider */
789 rand = rands = chunk_alloca(RAND_LEN * TRIPLET_COUNT);
790 kc = kcs = chunk_alloca(KC_LEN * TRIPLET_COUNT);
791 sres = sreses = chunk_alloca(SRES_LEN * TRIPLET_COUNT);
792 rands.len = 0;
793 kcs.len = 0;
794 sreses.len = 0;
795 for (i = 0; i < TRIPLET_COUNT; i++)
796 {
797 rand_len = RAND_LEN;
798 kc_len = KC_LEN;
799 sres_len = SRES_LEN;
800 if (this->get_triplet(id, rand.ptr, &rand_len, sres.ptr, &sres_len,
801 kc.ptr, &kc_len))
802 {
803 DBG1(DBG_IKE, "getting EAP-SIM triplet %d failed", i);
804 return FAILED;
805 }
806 rands.len += rand_len;
807 kcs.len += kc_len;
808 sreses.len += sres_len;
809 rand = chunk_skip(rand, rand_len);
810 kc = chunk_skip(kc, kc_len);
811 sres = chunk_skip(sres, sres_len);
812 }
813 derive_keys(this, kcs);
814
815 /* build MAC over "EAP packet | NONCE_MT" */
816 *out = build_payload(this, this->identifier++, SIM_CHALLENGE, AT_RAND,
817 rands, AT_MAC, this->nonce, AT_END);
818 this->sreses = chunk_clone(sreses);
819 return NEED_MORE;
820 }
821
822 /**
823 * process an EAP-SIM/Request/Notification message
824 */
825 static status_t peer_process_notification(private_eap_sim_t *this,
826 eap_payload_t *in, eap_payload_t **out)
827 {
828 chunk_t message, data;
829 sim_attribute_t attribute;
830
831 message = in->get_data(in);
832 read_header(&message);
833
834 while ((attribute = read_attribute(&message, &data)) != AT_END)
835 {
836 switch (attribute)
837 {
838 case AT_NOTIFICATION:
839 {
840 u_int16_t code = 0;
841 if (data.len == 2)
842 {
843 code = ntohs(*(u_int16_t*)data.ptr);
844 }
845 if (code <= 32767) /* no success bit */
846 {
847 DBG1(DBG_IKE, "received %N error %d",
848 sim_attribute_names, attribute, code);
849 *out = build_payload(this,
850 in->get_identifier(in), SIM_CLIENT_ERROR,
851 AT_CLIENT_ERROR_CODE, client_error_general,
852 AT_END);
853 return NEED_MORE;
854 }
855 else
856 {
857 DBG1(DBG_IKE, "received %N code %d",
858 sim_attribute_names, attribute, code);
859 }
860 break;
861 }
862 default:
863 DBG1(DBG_IKE, "ignoring EAP_SIM attribute %N",
864 sim_attribute_names, attribute);
865 break;
866 }
867 }
868 /* reply with empty notification */
869 *out = build_payload(this, in->get_identifier(in), SIM_NOTIFICATION, AT_END);
870 return NEED_MORE;
871 }
872
873 /**
874 * Process a client error
875 */
876 static status_t server_process_client_error(private_eap_sim_t *this,
877 eap_payload_t *in, eap_payload_t **out)
878 {
879 chunk_t message, data;
880 sim_attribute_t attribute;
881
882 message = in->get_data(in);
883 read_header(&message);
884
885 while ((attribute = read_attribute(&message, &data)) != AT_END)
886 {
887 if (attribute == AT_CLIENT_ERROR_CODE)
888 {
889 u_int16_t code = 0;
890 if (data.len == 2)
891 {
892 code = ntohs(*(u_int16_t*)data.ptr);
893 }
894 DBG1(DBG_IKE, "received %N error %d",
895 sim_attribute_names, attribute, code);
896 }
897 else
898 {
899 DBG1(DBG_IKE, "ignoring EAP_SIM attribute %N",
900 sim_attribute_names, attribute);
901 }
902 }
903 return FAILED;
904 }
905
906 /**
907 * Implementation of eap_method_t.process for the peer
908 */
909 static status_t peer_process(private_eap_sim_t *this,
910 eap_payload_t *in, eap_payload_t **out)
911 {
912 sim_subtype_t type;
913 chunk_t message;
914
915 message = in->get_data(in);
916 type = read_header(&message);
917
918 switch (type)
919 {
920 case SIM_START:
921 return peer_process_start(this, in, out);
922 case SIM_CHALLENGE:
923 return peer_process_challenge(this, in, out);
924 case SIM_NOTIFICATION:
925 return peer_process_notification(this, in, out);
926 default:
927 DBG1(DBG_IKE, "unable to process EAP_SIM subtype %N",
928 sim_subtype_names, type);
929 *out = build_payload(this, in->get_identifier(in), SIM_CLIENT_ERROR,
930 AT_CLIENT_ERROR_CODE, client_error_general, AT_END);
931 return NEED_MORE;
932 }
933 }
934
935 /**
936 * Implementation of eap_method_t.process for the server
937 */
938 static status_t server_process(private_eap_sim_t *this,
939 eap_payload_t *in, eap_payload_t **out)
940 {
941 sim_subtype_t type;
942 chunk_t message;
943
944 message = in->get_data(in);
945 type = read_header(&message);
946
947 switch (type)
948 {
949 case SIM_START:
950 return server_process_start(this, in, out);
951 case SIM_CHALLENGE:
952 return server_process_challenge(this, in, out);
953 case SIM_CLIENT_ERROR:
954 return server_process_client_error(this, in, out);
955 default:
956 DBG1(DBG_IKE, "unable to process EAP_SIM subtype %N",
957 sim_subtype_names, type);
958 return FAILED;
959 }
960 }
961
962 /**
963 * Implementation of eap_method_t.initiate for the peer
964 */
965 static status_t peer_initiate(private_eap_sim_t *this, eap_payload_t **out)
966 {
967 /* peer never initiates */
968 return FAILED;
969 }
970
971 /**
972 * Implementation of eap_method_t.initiate for the server
973 */
974 static status_t server_initiate(private_eap_sim_t *this, eap_payload_t **out)
975 {
976 /* version_list to derive MK, no padding */
977 this->version_list = chunk_clone(this->version);
978 /* build_payloads adds padding itself */
979 *out = build_payload(this, this->identifier++, SIM_START,
980 AT_VERSION_LIST, this->version, AT_END);
981 return NEED_MORE;
982 }
983
984 /**
985 * Implementation of eap_method_t.get_type.
986 */
987 static eap_type_t get_type(private_eap_sim_t *this, u_int32_t *vendor)
988 {
989 *vendor = 0;
990 return EAP_SIM;
991 }
992
993 /**
994 * Implementation of eap_method_t.get_msk.
995 */
996 static status_t get_msk(private_eap_sim_t *this, chunk_t *msk)
997 {
998 if (this->msk.ptr)
999 {
1000 *msk = this->msk;
1001 return SUCCESS;
1002 }
1003 return FAILED;
1004 }
1005
1006 /**
1007 * Implementation of eap_method_t.is_mutual.
1008 */
1009 static bool is_mutual(private_eap_sim_t *this)
1010 {
1011 return TRUE;
1012 }
1013
1014 /**
1015 * Implementation of eap_method_t.destroy.
1016 */
1017 static void destroy(private_eap_sim_t *this)
1018 {
1019 this->peer->destroy(this->peer);
1020 dlclose(this->handle);
1021 DESTROY_IF(this->hasher);
1022 DESTROY_IF(this->prf);
1023 DESTROY_IF(this->signer);
1024 chunk_free(&this->nonce);
1025 chunk_free(&this->sreses);
1026 chunk_free(&this->version_list);
1027 chunk_free(&this->k_auth);
1028 chunk_free(&this->k_encr);
1029 chunk_free(&this->msk);
1030 chunk_free(&this->emsk);
1031 free(this);
1032 }
1033
1034 /**
1035 * Generic constructor for both roles
1036 */
1037 eap_sim_t *eap_sim_create_generic(eap_role_t role, identification_t *server,
1038 identification_t *peer)
1039 {
1040 private_eap_sim_t *this;
1041 rng_t *rng;
1042 void *symbol;
1043 char *name;
1044
1045 this = malloc_thing(private_eap_sim_t);
1046 this->alg = NULL;
1047 this->get_triplet = NULL;
1048 this->nonce = chunk_empty;
1049 this->sreses = chunk_empty;
1050 this->peer = peer->clone(peer);
1051 this->tries = MAX_TRIES;
1052 this->version.ptr = version;
1053 this->version.len = sizeof(version);
1054 this->version_list = chunk_empty;
1055 this->k_auth = chunk_empty;
1056 this->k_encr = chunk_empty;
1057 this->msk = chunk_empty;
1058 this->emsk = chunk_empty;
1059 /* generate a non-zero identifier */
1060 do {
1061 this->identifier = random();
1062 } while (!this->identifier);
1063
1064 this->handle = dlopen(SIM_READER_LIB, RTLD_LAZY);
1065 if (this->handle == NULL)
1066 {
1067 DBG1(DBG_IKE, "unable to open SIM reader '%s'", SIM_READER_LIB);
1068 free(this);
1069 return NULL;
1070 }
1071 switch (role)
1072 {
1073 case EAP_PEER:
1074 name = SIM_READER_ALG;
1075 break;
1076 case EAP_SERVER:
1077 name = SIM_READER_GET_TRIPLET;
1078 break;
1079 default:
1080 free(this);
1081 return NULL;
1082 }
1083 symbol = dlsym(this->handle, name);
1084 if (symbol == NULL)
1085 {
1086 DBG1(DBG_IKE, "unable to open SIM function '%s' in '%s'",
1087 name, SIM_READER_LIB);
1088 dlclose(this->handle);
1089 free(this);
1090 return NULL;
1091 }
1092 switch (role)
1093 {
1094 case EAP_SERVER:
1095 this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))server_initiate;
1096 this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))server_process;
1097 this->get_triplet = symbol;
1098 this->type = EAP_REQUEST;
1099 break;
1100 case EAP_PEER:
1101 this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))peer_initiate;
1102 this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))peer_process;
1103 this->alg = symbol;
1104 this->type = EAP_RESPONSE;
1105 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
1106 if (!rng)
1107 {
1108 DBG1(DBG_IKE, "unable to generate NONCE for EAP_SIM");
1109 free(this);
1110 return NULL;
1111 }
1112 rng->allocate_bytes(rng, NONCE_LEN, &this->nonce);
1113 rng->destroy(rng);
1114 break;
1115 default:
1116 free(this);
1117 return NULL;
1118 }
1119 this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type;
1120 this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual;
1121 this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk;
1122 this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy;
1123
1124 this->hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
1125 this->prf = lib->crypto->create_prf(lib->crypto, PRF_FIPS_SHA1_160);
1126 this->signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_SHA1_128);
1127 if (!this->hasher || !this->prf || !this->signer)
1128 {
1129 DBG1(DBG_IKE, "initiating EAP-SIM failed, FIPS-PRF/SHA1 not supported");
1130 destroy(this);
1131 return NULL;
1132 }
1133 return &this->public;
1134 }
1135
1136 /*
1137 * Described in header.
1138 */
1139 eap_sim_t *eap_sim_create_server(identification_t *server,
1140 identification_t *peer)
1141 {
1142 return eap_sim_create_generic(EAP_SERVER, server, peer);
1143 }
1144
1145 /*
1146 * Described in header.
1147 */
1148 eap_sim_t *eap_sim_create_peer(identification_t *server,
1149 identification_t *peer)
1150 {
1151 return eap_sim_create_generic(EAP_PEER, server, peer);
1152 }
1153