added --with-sim-reader option to configure script
[strongswan.git] / src / charon / sa / authenticators / eap / eap_sim.c
1 /**
2 * @file eap_sim.c
3 *
4 * @brief Implementation of eap_sim_t.
5 *
6 */
7
8 /*
9 * Copyright (C) 2007 Martin Willi
10 * Hochschule fuer Technik Rapperswil
11 *
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * for more details.
21 */
22
23 #include "eap_sim.h"
24
25 #include <dlfcn.h>
26
27 #include <daemon.h>
28 #include <library.h>
29
30 ENUM(sim_subtype_names, SIM_START, SIM_CLIENT_ERROR,
31 "SIM_START",
32 "SIM_CHALLENGE",
33 "SIM_NOTIFICATION",
34 "SIM_13",
35 "SIM_CLIENT_ERROR",
36 );
37
38 ENUM_BEGIN(sim_attribute_names, AT_END, AT_CLIENT_ERROR_CODE,
39 "AT_END",
40 "AT_0",
41 "AT_RAND",
42 "AT_AUTN",
43 "AT_RES",
44 "AT_AUTS",
45 "AT_5",
46 "AT_PADDING",
47 "AT_NONCE_MT",
48 "AT_8",
49 "AT_9",
50 "AT_PERMANENT_ID_REQ",
51 "AT_MAC",
52 "AT_NOTIFICATION",
53 "AT_ANY_ID_REQ",
54 "AT_IDENTITY",
55 "AT_VERSION_LIST",
56 "AT_SELECTED_VERSION",
57 "AT_FULLAUTH_ID_REQ",
58 "AT_18",
59 "AT_COUNTER",
60 "AT_COUNTER_TOO_SMALL",
61 "AT_NONCE_S",
62 "AT_CLIENT_ERROR_CODE");
63 ENUM_NEXT(sim_attribute_names, AT_IV, AT_RESULT_IND, AT_CLIENT_ERROR_CODE,
64 "AT_IV",
65 "AT_ENCR_DATA",
66 "AT_131",
67 "AT_NEXT_PSEUDONYM",
68 "AT_NEXT_REAUTH_ID",
69 "AT_CHECKCODE",
70 "AT_RESULT_IND");
71 ENUM_END(sim_attribute_names, AT_RESULT_IND);
72
73
74 typedef struct private_eap_sim_t private_eap_sim_t;
75
76 /**
77 * Private data of an eap_sim_t object.
78 */
79 struct private_eap_sim_t {
80
81 /**
82 * Public authenticator_t interface.
83 */
84 eap_sim_t public;
85
86 /**
87 * ID of ourself
88 */
89 identification_t *peer;
90
91 /**
92 * SIM cardreader function loaded from library
93 */
94 sim_algo_t alg;
95
96 /**
97 * handle of the loaded library
98 */
99 void *handle;
100
101 /**
102 * version this implementation uses
103 */
104 chunk_t version;
105
106 /**
107 * version list received from server
108 */
109 chunk_t version_list;
110
111 /**
112 * Nonce value used in AT_NONCE_MT
113 */
114 chunk_t nonce;
115
116 /**
117 * k_encr key derived from MK
118 */
119 chunk_t k_encr;
120
121 /**
122 * k_auth key derived from MK, used for AT_MAC verification
123 */
124 chunk_t k_auth;
125
126 /**
127 * MSK, used for EAP-SIM based IKEv2 authentication
128 */
129 chunk_t msk;
130
131 /**
132 * EMSK, extendes MSK for further uses
133 */
134 chunk_t emsk;
135 };
136
137 /** length of the AT_NONCE_MT nonce value */
138 #define NONCE_LEN 16
139 /** length of the AT_MAC value */
140 #define MAC_LEN 16
141 /** length of the AT_RAND value */
142 #define RAND_LEN 16
143 /** length of the k_encr key */
144 #define KENCR_LEN 16
145 /** length of the k_auth key */
146 #define KAUTH_LEN 16
147 /** length of the MSK */
148 #define MSK_LEN 64
149 /** length of the EMSK */
150 #define EMSK_LEN 64
151
152 /* client error codes used in AT_CLIENT_ERROR_CODE */
153 char client_error_general_buf[] = {0x00, 0x01};
154 char client_error_unsupported_buf[] = {0x00, 0x02};
155 char client_error_insufficient_buf[] = {0x00, 0x03};
156 char client_error_notfresh_buf[] = {0x00, 0x04};
157 chunk_t client_error_general = chunk_from_buf(client_error_general_buf);
158 chunk_t client_error_unsupported = chunk_from_buf(client_error_unsupported_buf);
159 chunk_t client_error_insufficient = chunk_from_buf(client_error_insufficient_buf);
160 chunk_t client_error_notfresh = chunk_from_buf(client_error_notfresh_buf);
161
162 /**
163 * Read EAP and EAP-SIM header, return SIM type
164 */
165 static sim_subtype_t read_header(chunk_t *message)
166 {
167 sim_subtype_t type;
168
169 if (message->len < 8)
170 {
171 *message = chunk_empty;
172 return 0;
173 }
174 type = *(message->ptr + 5);
175 *message = chunk_skip(*message, 8);
176 return type;
177 }
178
179 /**
180 * read the next attribute from the chunk data
181 */
182 static sim_attribute_t read_attribute(chunk_t *message, chunk_t *data)
183 {
184 sim_attribute_t attribute;
185 size_t length;
186
187 DBG3(DBG_IKE, "reading attribute from %B", message);
188
189 if (message->len < 2)
190 {
191 return AT_END;
192 }
193 attribute = *message->ptr++;
194 length = *message->ptr++ * 4 - 2;
195 message->len -= 2;
196 DBG3(DBG_IKE, "found attribute %N with length %d",
197 sim_attribute_names, attribute, length);
198
199 if (length > message->len)
200 {
201 return AT_END;
202 }
203 data->len = length;
204 data->ptr = message->ptr;
205 *message = chunk_skip(*message, length);
206 return attribute;
207 }
208
209 /**
210 * Build an EAP-SIM payload using a variable length attribute list.
211 * The variable argument takes a sim_attribute_t followed by its data in a chunk.
212 */
213 static eap_payload_t *build_payload(private_eap_sim_t *this, u_int8_t identifier,
214 sim_subtype_t type, ...)
215 {
216 chunk_t message = chunk_alloca(512);
217 chunk_t pos = message;
218 eap_payload_t *payload;
219 va_list args;
220 sim_attribute_t attr;
221 u_int8_t *mac_pos = NULL;
222 chunk_t mac_data = chunk_empty;
223
224 /* write EAP header, skip length bytes */
225 *pos.ptr++ = EAP_RESPONSE;
226 *pos.ptr++ = identifier;
227 pos.ptr += 2;
228 pos.len -= 4;
229 /* write SIM header with type and subtype, zero reserved bytes */
230 *pos.ptr++ = EAP_SIM;
231 *pos.ptr++ = type;
232 *pos.ptr++ = 0;
233 *pos.ptr++ = 0;
234 pos.len -= 4;
235
236 va_start(args, type);
237 while ((attr = va_arg(args, sim_attribute_t)) != AT_END)
238 {
239 chunk_t data = va_arg(args, chunk_t);
240
241 DBG3(DBG_IKE, "building %N %B", sim_attribute_names, attr, &data);
242
243 /* write attribute header */
244 *pos.ptr++ = attr;
245 pos.len--;
246
247 switch (attr)
248 {
249 case AT_CLIENT_ERROR_CODE:
250 case AT_SELECTED_VERSION:
251 {
252 *pos.ptr = data.len/4 + 1;
253 pos = chunk_skip(pos, 1);
254 memcpy(pos.ptr, data.ptr, data.len);
255 pos = chunk_skip(pos, data.len);
256 break;
257 }
258 case AT_IDENTITY:
259 {
260 /* align up to four byte */
261 if (data.len % 4)
262 {
263 chunk_t tmp = chunk_alloca((data.len/4)*4 + 4);
264 memset(tmp.ptr, 0, tmp.len);
265 memcpy(tmp.ptr, data.ptr, data.len);
266 data = tmp;
267 }
268 *pos.ptr = data.len/4 + 1;
269 pos = chunk_skip(pos, 1);
270 /* actual length in bytes */
271 *(u_int16_t*)pos.ptr = htons(data.len);
272 pos = chunk_skip(pos, sizeof(u_int16_t));
273 memcpy(pos.ptr, data.ptr, data.len);
274 pos = chunk_skip(pos, data.len);
275 break;
276 }
277 case AT_NONCE_MT:
278 {
279 *pos.ptr = data.len/4 + 1;
280 pos = chunk_skip(pos, 1);
281 memset(pos.ptr, 0, 2);
282 pos = chunk_skip(pos, 2);
283 memcpy(pos.ptr, data.ptr, data.len);
284 pos = chunk_skip(pos, data.len);
285 break;
286 }
287 case AT_MAC:
288 {
289 *pos.ptr++ = 5; pos.len--;
290 *pos.ptr++ = 0; pos.len--;
291 *pos.ptr++ = 0; pos.len--;
292 mac_pos = pos.ptr;
293 memset(mac_pos, 0, MAC_LEN);
294 pos = chunk_skip(pos, MAC_LEN);
295 mac_data = data;
296 break;
297 }
298 case AT_RAND:
299 {
300 *pos.ptr++ = data.len/4 + 1; pos.len--;
301 *pos.ptr++ = 0; pos.len--;
302 *pos.ptr++ = 0; pos.len--;
303 memcpy(pos.ptr, data.ptr, data.len);
304 pos = chunk_skip(pos, data.len);
305 break;
306 }
307 default:
308 DBG1(DBG_IKE, "no rule to build EAP_SIM attribute %N, skipped",
309 sim_attribute_names, attr);
310 break;
311 }
312 }
313 va_end(args);
314
315 /* calculate message length, write into header */
316 message.len = pos.ptr - message.ptr;
317 *(u_int16_t*)(message.ptr + 2) = htons(message.len);
318
319 /* create MAC if AT_MAC attribte was included. Append supplied va_arg
320 * chunk mac_data to "to-sign" chunk */
321 if (mac_pos)
322 {
323 signer_t *signer = signer_create(AUTH_HMAC_SHA1_128);
324 signer->set_key(signer, this->k_auth);
325 mac_data = chunk_cata("cc", message, mac_data);
326 signer->get_signature(signer, mac_data, mac_pos);
327 DBG3(DBG_IKE, "AT_MAC signature of %B\n is %b",
328 &mac_data, mac_pos, MAC_LEN);
329 signer->destroy(signer);
330 }
331
332 payload = eap_payload_create_data(message);
333
334 DBG3(DBG_IKE, "created EAP message %B", &message);
335 return payload;
336 }
337
338 /**
339 * process an EAP-SIM/Request/Start message
340 */
341 static status_t process_start(private_eap_sim_t *this, eap_payload_t *in,
342 eap_payload_t **out)
343 {
344 chunk_t message, data;
345 sim_attribute_t attribute, include_id = AT_END;
346 u_int8_t identifier;
347
348 identifier = in->get_identifier(in);
349 message = in->get_data(in);
350 read_header(&message);
351
352 while ((attribute = read_attribute(&message, &data)) != AT_END)
353 {
354 switch (attribute)
355 {
356 case AT_VERSION_LIST:
357 {
358 /* check if server supports our implementation */
359 bool found = FALSE;
360 if (data.len > 2)
361 {
362 /* read actual length first */
363 data.len = min(data.len, ntohs(*(u_int16_t*)data.ptr) + 2);
364 data = chunk_skip(data, 2);
365 chunk_free(&this->version_list);
366 this->version_list = chunk_clone(data);
367 while (data.len >= this->version.len)
368 {
369 if (memeq(data.ptr, this->version.ptr, this->version.len))
370 {
371 found = TRUE;
372 break;
373 }
374 data = chunk_skip(data, this->version.len);
375 }
376 }
377 if (!found)
378 {
379 DBG1(DBG_IKE, "server does not support EAP_SIM "
380 "version number %#B", &this->version);
381 *out = build_payload(this, identifier, SIM_CLIENT_ERROR,
382 AT_CLIENT_ERROR_CODE, client_error_unsupported,
383 AT_END);
384 return NEED_MORE;
385 }
386 break;
387 }
388 case AT_PERMANENT_ID_REQ:
389 case AT_FULLAUTH_ID_REQ:
390 case AT_ANY_ID_REQ:
391 /* only include AT_IDENTITY if requested */
392 include_id = AT_IDENTITY;
393 break;
394 default:
395 DBG1(DBG_IKE, "ignoring EAP_SIM attribute %N",
396 sim_attribute_names, attribute);
397 break;
398 }
399 }
400
401 /* build payload. If "include_id" is AT_END, AT_IDENTITY is ommited */
402 *out = build_payload(this, identifier, SIM_START,
403 AT_SELECTED_VERSION, this->version,
404 AT_NONCE_MT, this->nonce,
405 include_id, this->peer->get_encoding(this->peer),
406 AT_END);
407 return NEED_MORE;
408 }
409
410 /**
411 * process an EAP-SIM/Request/Challenge message
412 */
413 static status_t process_challenge(private_eap_sim_t *this, eap_payload_t *in,
414 eap_payload_t **out)
415 {
416 chunk_t message, data, tmp, kcs, kc, sreses, sres, mk;
417 sim_attribute_t attribute;
418 u_int8_t identifier, i;
419 chunk_t mac = chunk_empty, rands = chunk_empty;
420 signer_t *signer;
421 hasher_t *hasher;
422 prf_t *prf;
423
424 identifier = in->get_identifier(in);
425 message = in->get_data(in);
426 read_header(&message);
427
428 while ((attribute = read_attribute(&message, &data)) != AT_END)
429 {
430 switch (attribute)
431 {
432 case AT_RAND:
433 {
434 rands = chunk_skip(data, 2);
435 break;
436 }
437 case AT_MAC:
438 {
439 /* backup MAC, zero it inline for later verification */
440 data = chunk_skip(data, 2);
441 mac = chunk_clonea(data);
442 memset(data.ptr, 0, data.len);
443 break;
444 }
445 default:
446 DBG1(DBG_IKE, "ignoring EAP_SIM attribute %N",
447 sim_attribute_names, attribute);
448 break;
449 }
450 }
451
452 /* excepting two or three RAND, each 16 bytes. We require two valid
453 * and different RANDs */
454 if ((rands.len != 2 * RAND_LEN && rands.len != 3 * RAND_LEN) ||
455 memeq(rands.ptr, rands.ptr + RAND_LEN, RAND_LEN))
456 {
457 DBG1(DBG_IKE, "no valid AT_RAND received");
458 *out = build_payload(this, identifier, SIM_CLIENT_ERROR,
459 AT_CLIENT_ERROR_CODE, client_error_insufficient,
460 AT_END);
461 return FAILED;
462 }
463 if (mac.len != MAC_LEN)
464 {
465 DBG1(DBG_IKE, "no valid AT_MAC received");
466 *out = build_payload(this, identifier, SIM_CLIENT_ERROR,
467 AT_CLIENT_ERROR_CODE, client_error_general,
468 AT_END);
469 return NEED_MORE;
470 }
471
472 /* get two or three KCs/SRESes from SIM using RANDs */
473 kcs = kc = chunk_alloca(rands.len / 2);
474 sreses = sres = chunk_alloca(rands.len / 4);
475 while (rands.len > 0)
476 {
477 int kc_len = kc.len, sres_len = sres.len;
478
479 if (this->alg(rands.ptr, RAND_LEN, sres.ptr, &sres_len, kc.ptr, &kc_len))
480 {
481 DBG1(DBG_IKE, "unable to get triplets from SIM");
482 *out = build_payload(this, identifier, SIM_CLIENT_ERROR,
483 AT_CLIENT_ERROR_CODE, client_error_general,
484 AT_END);
485 return NEED_MORE;
486 }
487 DBG3(DBG_IKE, "got triplet for RAND %b\n Kc %b\n SRES %b",
488 rands.ptr, RAND_LEN, sres.ptr, sres_len, kc.ptr, kc_len);
489 kc = chunk_skip(kc, kc_len);
490 sres = chunk_skip(sres, sres_len);
491 rands = chunk_skip(rands, RAND_LEN);
492 }
493
494 /* build MK = SHA1(Identity|n*Kc|NONCE_MT|Version List|Selected Version) */
495 tmp = chunk_cata("ccccc", this->peer->get_encoding(this->peer), kcs,
496 this->nonce, this->version_list, this->version);
497 hasher = hasher_create(HASH_SHA1);
498 mk = chunk_alloca(hasher->get_hash_size(hasher));
499 hasher->get_hash(hasher, tmp, mk.ptr);
500 hasher->destroy(hasher);
501 DBG3(DBG_IKE, "MK = SHA1(%B\n) = %B", &tmp, &mk);
502
503 /* K_encr | K_auth | MSK | EMSK = prf() | prf() | prf() | prf()
504 * FIPS PRF has 320 bit block size, we need 160 byte for keys
505 * => run prf four times */
506 prf = prf_create(PRF_FIPS_SHA1_160);
507 prf->set_key(prf, mk);
508 tmp = chunk_alloca(prf->get_block_size(prf) * 4);
509 for (i = 0; i < 4; i++)
510 {
511 prf->get_bytes(prf, chunk_empty, tmp.ptr + tmp.len / 4 * i);
512 }
513 prf->destroy(prf);
514 chunk_free(&this->k_encr);
515 chunk_free(&this->k_auth);
516 chunk_free(&this->msk);
517 chunk_free(&this->emsk);
518 chunk_split(tmp, "aaaa", KENCR_LEN, &this->k_encr, KAUTH_LEN, &this->k_auth,
519 MSK_LEN, &this->msk, EMSK_LEN, &this->emsk);
520 DBG3(DBG_IKE, "K_encr %B\nK_auth %B\nMSK %B\nEMSK %B",
521 &this->k_encr, &this->k_auth, &this->msk, &this->emsk);
522
523 /* verify AT_MAC attribute, signature is over "EAP packet | NONCE_MT" */
524 signer = signer_create(AUTH_HMAC_SHA1_128);
525 signer->set_key(signer, this->k_auth);
526 tmp = chunk_cata("cc", in->get_data(in), this->nonce);
527 if (!signer->verify_signature(signer, tmp, mac))
528 {
529 DBG1(DBG_IKE, "AT_MAC verification failed");
530 signer->destroy(signer);
531 *out = build_payload(this, identifier, SIM_CLIENT_ERROR,
532 AT_CLIENT_ERROR_CODE, client_error_general,
533 AT_END);
534 return NEED_MORE;
535 }
536 signer->destroy(signer);
537
538 /* build response, AT_MAC is built over "EAP packet | n*SRES" */
539 *out = build_payload(this, identifier, SIM_CHALLENGE,
540 AT_MAC, sreses,
541 AT_END);
542 return NEED_MORE;
543 }
544
545 /**
546 * Implementation of eap_method_t.process for the peer
547 */
548 static status_t process(private_eap_sim_t *this,
549 eap_payload_t *in, eap_payload_t **out)
550 {
551 sim_subtype_t type;
552 chunk_t message;
553
554 message = in->get_data(in);
555 type = read_header(&message);
556
557 switch (type)
558 {
559 case SIM_START:
560 return process_start(this, in, out);
561 case SIM_CHALLENGE:
562 return process_challenge(this, in, out);
563 default:
564 DBG1(DBG_IKE, "unable to process EAP_SIM subtype %N",
565 sim_subtype_names, type);
566 *out = build_payload(this, in->get_identifier(in), SIM_CLIENT_ERROR,
567 AT_CLIENT_ERROR_CODE, client_error_general, AT_END);
568 return NEED_MORE;
569 }
570 }
571
572 /**
573 * Implementation of eap_method_t.initiate for the peer
574 */
575 static status_t initiate(private_eap_sim_t *this, eap_payload_t **out)
576 {
577 /* peer never initiates */
578 return FAILED;
579 }
580
581 /**
582 * Implementation of eap_method_t.get_type.
583 */
584 static eap_type_t get_type(private_eap_sim_t *this)
585 {
586 return EAP_SIM;
587 }
588
589 /**
590 * Implementation of eap_method_t.get_msk.
591 */
592 static status_t get_msk(private_eap_sim_t *this, chunk_t *msk)
593 {
594 if (this->msk.ptr)
595 {
596 *msk = this->msk;
597 return SUCCESS;
598 }
599 return FAILED;
600 }
601
602 /**
603 * Implementation of eap_method_t.is_mutual.
604 */
605 static bool is_mutual(private_eap_sim_t *this)
606 {
607 return TRUE;
608 }
609
610 /**
611 * Implementation of eap_method_t.destroy.
612 */
613 static void destroy(private_eap_sim_t *this)
614 {
615 dlclose(this->handle);
616 chunk_free(&this->nonce);
617 chunk_free(&this->version_list);
618 chunk_free(&this->k_auth);
619 chunk_free(&this->k_encr);
620 chunk_free(&this->msk);
621 chunk_free(&this->emsk);
622 free(this);
623 }
624
625 /*
626 * Described in header.
627 */
628 eap_sim_t *eap_create(eap_role_t role,
629 identification_t *server, identification_t *peer)
630 {
631 private_eap_sim_t *this;
632 randomizer_t *randomizer;
633 static char version[] = {0x00,0x01};
634
635 if (role != EAP_PEER)
636 {
637 return NULL;
638 }
639 this = malloc_thing(private_eap_sim_t);
640
641 this->handle = dlopen(SIM_READER_LIB, RTLD_LAZY);
642 if (this->handle == NULL)
643 {
644 DBG1(DBG_IKE, "unable to open SIM reader '%s'", SIM_READER_LIB);
645 free(this);
646 return NULL;
647 }
648 this->alg = dlsym(this->handle, SIM_READER_ALG);
649 if (this->alg == NULL)
650 {
651 DBG1(DBG_IKE, "unable to open SIM reader function '%s' in '%s'",
652 SIM_READER_ALG, SIM_READER_LIB);
653 dlclose(this->handle);
654 free(this);
655 return NULL;
656 }
657
658 randomizer = randomizer_create();
659 if (randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_LEN,
660 &this->nonce))
661 {
662 DBG1(DBG_IKE, "unable to generate NONCE for EAP_SIM");
663 randomizer->destroy(randomizer);
664 free(this);
665 return NULL;
666 }
667 randomizer->destroy(randomizer);
668
669 /* public functions */
670 this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate;
671 this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process;
672 this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*))get_type;
673 this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual;
674 this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk;
675 this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy;
676
677 /* private data */
678 this->peer = peer;
679 this->version.ptr = version;
680 this->version.len = sizeof(version);
681 this->version_list = chunk_empty;
682 this->k_auth = chunk_empty;
683 this->k_encr = chunk_empty;
684 this->msk = chunk_empty;
685 this->emsk = chunk_empty;
686
687 return &this->public;
688 }