version number selection fix
[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 value */
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 *pos.ptr = data.len/4 + 1;
261 pos = chunk_skip(pos, 1);
262 /* actual length in bytes */
263 *(u_int16_t*)pos.ptr = htons(data.len);
264 pos = chunk_skip(pos, sizeof(u_int16_t));
265 memcpy(pos.ptr, data.ptr, data.len);
266 pos = chunk_skip(pos, data.len);
267 break;
268 }
269 case AT_NONCE_MT:
270 {
271 *pos.ptr = data.len/4 + 1;
272 pos = chunk_skip(pos, 1);
273 memset(pos.ptr, 0, 2);
274 pos = chunk_skip(pos, 2);
275 memcpy(pos.ptr, data.ptr, data.len);
276 pos = chunk_skip(pos, data.len);
277 break;
278 }
279 case AT_MAC:
280 {
281 *pos.ptr++ = 5; pos.len--;
282 *pos.ptr++ = 0; pos.len--;
283 *pos.ptr++ = 0; pos.len--;
284 mac_pos = pos.ptr;
285 memset(mac_pos, 0, MAC_LEN);
286 pos = chunk_skip(pos, MAC_LEN);
287 mac_data = data;
288 break;
289 }
290 case AT_RAND:
291 {
292 *pos.ptr++ = data.len/4 + 1; pos.len--;
293 *pos.ptr++ = 0; pos.len--;
294 *pos.ptr++ = 0; pos.len--;
295 memcpy(pos.ptr, data.ptr, data.len);
296 pos = chunk_skip(pos, data.len);
297 break;
298 }
299 default:
300 DBG1(DBG_IKE, "no rule to build EAP_SIM attribute %N, skipped",
301 sim_attribute_names, attr);
302 break;
303 }
304 }
305 va_end(args);
306
307 /* calculate message length, write into header */
308 message.len = pos.ptr - message.ptr;
309 *(u_int16_t*)(message.ptr + 2) = htons(message.len);
310
311 /* create MAC if AT_MAC attribte was included. Append supplied va_arg
312 * chunk mac_data to "to-sign" chunk */
313 if (mac_pos)
314 {
315 signer_t *signer = signer_create(AUTH_HMAC_SHA1_128);
316 signer->set_key(signer, this->k_auth);
317 mac_data = chunk_cata("cc", message, mac_data);
318 DBG3(DBG_IKE, "AT_MAC signature of %B", &mac_data);
319 DBG3(DBG_IKE, "using k_auth %B", &this->k_auth);
320 signer->get_signature(signer, mac_data, mac_pos);
321 DBG3(DBG_IKE, "is %b", mac_pos, MAC_LEN);
322 signer->destroy(signer);
323 }
324
325 payload = eap_payload_create_data(message);
326
327 DBG3(DBG_IKE, "created EAP message %B", &message);
328 return payload;
329 }
330
331 /**
332 * process an EAP-SIM/Request/Start message
333 */
334 static status_t process_start(private_eap_sim_t *this, eap_payload_t *in,
335 eap_payload_t **out)
336 {
337 chunk_t message, data;
338 sim_attribute_t attribute, include_id = AT_END;
339 u_int8_t identifier;
340
341 identifier = in->get_identifier(in);
342 message = in->get_data(in);
343 read_header(&message);
344
345 while ((attribute = read_attribute(&message, &data)) != AT_END)
346 {
347 switch (attribute)
348 {
349 case AT_VERSION_LIST:
350 {
351 /* check if server supports our implementation */
352 bool found = FALSE;
353
354 if (data.len > 2)
355 {
356 /* read actual length first */
357 data.len = min(data.len, ntohs(*(u_int16_t*)data.ptr) + 2);
358 data = chunk_skip(data, 2);
359 chunk_free(&this->version_list);
360 this->version_list = chunk_clone(data);
361 while (data.len >= this->version.len)
362 {
363 if (memeq(data.ptr, this->version.ptr, this->version.len))
364 {
365 found = TRUE;
366 break;
367 }
368 data = chunk_skip(data, this->version.len);
369 }
370 }
371 if (!found)
372 {
373 DBG1(DBG_IKE, "server does not support EAP_SIM "
374 "version number %#B", &this->version);
375 *out = build_payload(this, identifier, SIM_CLIENT_ERROR,
376 AT_CLIENT_ERROR_CODE, client_error_unsupported,
377 AT_END);
378 return NEED_MORE;
379 }
380 break;
381 }
382 case AT_PERMANENT_ID_REQ:
383 case AT_FULLAUTH_ID_REQ:
384 case AT_ANY_ID_REQ:
385 /* only include AT_IDENTITY if requested */
386 include_id = AT_IDENTITY;
387 break;
388 default:
389 DBG1(DBG_IKE, "ignoring EAP_SIM attribute %N",
390 sim_attribute_names, attribute);
391 break;
392 }
393 }
394
395 /* build payload. If "include_id" is AT_END, AT_IDENTITY is ommited */
396 *out = build_payload(this, identifier, SIM_START,
397 AT_SELECTED_VERSION, this->version,
398 AT_NONCE_MT, this->nonce,
399 include_id, this->peer->get_encoding(this->peer),
400 AT_END);
401 return NEED_MORE;
402 }
403
404 /**
405 * process an EAP-SIM/Request/Challenge message
406 */
407 static status_t process_challenge(private_eap_sim_t *this, eap_payload_t *in,
408 eap_payload_t **out)
409 {
410 chunk_t message, data, tmp, kcs, kc, sreses, sres, mk;
411 sim_attribute_t attribute;
412 u_int8_t identifier;
413 chunk_t mac = chunk_empty, rands = chunk_empty;
414 signer_t *signer;
415 hasher_t *hasher;
416 prf_t *prf;
417
418 identifier = in->get_identifier(in);
419 message = in->get_data(in);
420 read_header(&message);
421
422 while ((attribute = read_attribute(&message, &data)) != AT_END)
423 {
424 switch (attribute)
425 {
426 case AT_RAND:
427 {
428 rands = chunk_skip(data, 2);
429 break;
430 }
431 case AT_MAC:
432 {
433 /* backup MAC, zero it inline for later verification */
434 mac = chunk_clonea(chunk_skip(data, 2));
435 memset(data.ptr, 0, data.len);
436 break;
437 }
438 default:
439 DBG1(DBG_IKE, "ignoring EAP_SIM attribute %N",
440 sim_attribute_names, attribute);
441 break;
442 }
443 }
444
445 /* excepting two or three RAND, each 16 bytes. We require two valid
446 * and different (!) RANDs */
447 if ((rands.len != 2 * RAND_LEN && rands.len != 3 * RAND_LEN) ||
448 memeq(rands.ptr, rands.ptr + RAND_LEN, RAND_LEN))
449 {
450 DBG1(DBG_IKE, "no valid AT_RAND received");
451 *out = build_payload(this, identifier, SIM_CLIENT_ERROR,
452 AT_CLIENT_ERROR_CODE, client_error_insufficient,
453 AT_END);
454 return FAILED;
455 }
456 if (mac.len != MAC_LEN)
457 {
458 DBG1(DBG_IKE, "no valid AT_MAC received");
459 *out = build_payload(this, identifier, SIM_CLIENT_ERROR,
460 AT_CLIENT_ERROR_CODE, client_error_general,
461 AT_END);
462 return NEED_MORE;
463 }
464
465 /* get two or three KCs/SRESes from SIM using RANDs */
466 kcs = kc = chunk_alloca(rands.len / 2);
467 sreses = sres = chunk_alloca(rands.len / 4);
468 while (rands.len > 0)
469 {
470 int kc_len = kc.len, sres_len = sres.len;
471
472 if (this->alg(rands.ptr, RAND_LEN, kc.ptr, &kc_len, sres.ptr, &sres_len))
473 {
474 DBG1(DBG_IKE, "unable to get triplets from SIM");
475 *out = build_payload(this, identifier, SIM_CLIENT_ERROR,
476 AT_CLIENT_ERROR_CODE, client_error_general,
477 AT_END);
478 return NEED_MORE;
479 }
480 kc = chunk_skip(kc, kc_len);
481 sres = chunk_skip(sres, sres_len);
482 rands = chunk_skip(rands, RAND_LEN);
483 }
484
485 /* build MK */
486 tmp = chunk_cata("ccccc", this->peer->get_encoding(this->peer), kcs,
487 this->nonce, this->version_list, this->version);
488 hasher = hasher_create(HASH_SHA1);
489 mk = chunk_alloca(hasher->get_hash_size(hasher));
490 hasher->get_hash(hasher, tmp, mk.ptr);
491 hasher->destroy(hasher);
492
493 /* K_encr | K_auth | MSK | EMSK = prf() | prf() | prf() | prf()
494 * FIPS PRF has 320 bit block size, we need 160 byte for keys
495 * => run prf four times */
496 prf = prf_create(PRF_FIPS_SHA1_160);
497 prf->set_key(prf, mk);
498 tmp = chunk_alloca(prf->get_block_size(prf) * 4);
499 prf->get_bytes(prf, chunk_empty, tmp.ptr);
500 prf->get_bytes(prf, chunk_empty, tmp.ptr + tmp.len / 4 * 1);
501 prf->get_bytes(prf, chunk_empty, tmp.ptr + tmp.len / 4 * 2);
502 prf->get_bytes(prf, chunk_empty, tmp.ptr + tmp.len / 4 * 3);
503 prf->destroy(prf);
504 chunk_free(&this->k_encr);
505 chunk_free(&this->k_auth);
506 chunk_free(&this->msk);
507 chunk_free(&this->emsk);
508 chunk_split(tmp, "aaaa", KENCR_LEN, &this->k_encr, KAUTH_LEN, &this->k_auth,
509 MSK_LEN, &this->msk, EMSK_LEN, &this->emsk);
510 DBG3(DBG_IKE, "MK %B", &mk);
511 DBG3(DBG_IKE, "K_encr %B", &this->k_encr);
512 DBG3(DBG_IKE, "K_auth %B", &this->k_auth);
513 DBG3(DBG_IKE, "MSK %B", &this->msk);
514 DBG3(DBG_IKE, "EMSK %B", &this->emsk);
515
516 /* verify AT_MAC attribute, signature is over "EAP packet | NONCE_MT" */
517 signer = signer_create(AUTH_HMAC_SHA1_128);
518 signer->set_key(signer, this->k_auth);
519 tmp = chunk_cata("cc", message, this->nonce);
520 DBG3(DBG_IKE, "verifying AT_MAC signature of %B", &tmp);
521 DBG3(DBG_IKE, "using k_auth %B", &this->k_auth);
522 if (!signer->verify_signature(signer, tmp, mac))
523 {
524 DBG1(DBG_IKE, "MAC in AT_MAC attribute verification failed");
525 signer->destroy(signer);
526 chunk_free(&this->msk);
527 *out = build_payload(this, identifier, SIM_CLIENT_ERROR,
528 AT_CLIENT_ERROR_CODE, client_error_general,
529 AT_END);
530 return NEED_MORE;
531 }
532 signer->destroy(signer);
533
534 /* build response, AT_MAC is built over "EAP packet | n*SRES" */
535 *out = build_payload(this, identifier, SIM_CHALLENGE,
536 AT_MAC, sreses,
537 AT_END);
538 return NEED_MORE;
539 }
540
541 /**
542 * Implementation of eap_method_t.process for the peer
543 */
544 static status_t process(private_eap_sim_t *this,
545 eap_payload_t *in, eap_payload_t **out)
546 {
547 sim_subtype_t type;
548 chunk_t message;
549
550 message = in->get_data(in);
551 type = read_header(&message);
552
553 switch (type)
554 {
555 case SIM_START:
556 return process_start(this, in, out);
557 case SIM_CHALLENGE:
558 return process_challenge(this, in, out);
559 default:
560 DBG1(DBG_IKE, "unable to process EAP_SIM subtype %N",
561 sim_subtype_names, type);
562 *out = build_payload(this, in->get_identifier(in), SIM_CLIENT_ERROR,
563 AT_CLIENT_ERROR_CODE, client_error_general, AT_END);
564 return NEED_MORE;
565 }
566 }
567
568 /**
569 * Implementation of eap_method_t.initiate for the peer
570 */
571 static status_t initiate(private_eap_sim_t *this, eap_payload_t **out)
572 {
573 /* peer never initiates */
574 return FAILED;
575 }
576
577 /**
578 * Implementation of eap_method_t.get_type.
579 */
580 static eap_type_t get_type(private_eap_sim_t *this)
581 {
582 return EAP_SIM;
583 }
584
585 /**
586 * Implementation of eap_method_t.get_msk.
587 */
588 static status_t get_msk(private_eap_sim_t *this, chunk_t *msk)
589 {
590 if (this->msk.ptr)
591 {
592 *msk = this->msk;
593 return SUCCESS;
594 }
595 return FAILED;
596 }
597
598 /**
599 * Implementation of eap_method_t.is_mutual.
600 */
601 static bool is_mutual(private_eap_sim_t *this)
602 {
603 return TRUE;
604 }
605
606 /**
607 * Implementation of eap_method_t.destroy.
608 */
609 static void destroy(private_eap_sim_t *this)
610 {
611 dlclose(this->handle);
612 chunk_free(&this->nonce);
613 chunk_free(&this->version_list);
614 chunk_free(&this->k_auth);
615 chunk_free(&this->k_encr);
616 chunk_free(&this->msk);
617 chunk_free(&this->emsk);
618 free(this);
619 }
620
621 /*
622 * Described in header.
623 */
624 eap_sim_t *eap_create(eap_role_t role,
625 identification_t *server, identification_t *peer)
626 {
627 private_eap_sim_t *this;
628 randomizer_t *randomizer;
629 static char version[] = {0x00,0x01};
630
631 if (role != EAP_PEER)
632 {
633 return NULL;
634 }
635 this = malloc_thing(private_eap_sim_t);
636
637 this->handle = dlopen(SIM_READER_LIB, RTLD_LAZY);
638 if (this->handle == NULL)
639 {
640 DBG1(DBG_IKE, "unable to open SIM reader '%s'", SIM_READER_LIB);
641 free(this);
642 return NULL;
643 }
644 this->alg = dlsym(this->handle, SIM_READER_ALG);
645 if (this->alg == NULL)
646 {
647 DBG1(DBG_IKE, "unable to open SIM reader function '%s' in '%s'",
648 SIM_READER_ALG, SIM_READER_LIB);
649 dlclose(this->handle);
650 free(this);
651 return NULL;
652 }
653
654 randomizer = randomizer_create();
655 if (randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_LEN,
656 &this->nonce))
657 {
658 DBG1(DBG_IKE, "unable to generate NONCE for EAP_SIM");
659 randomizer->destroy(randomizer);
660 free(this);
661 return NULL;
662 }
663 randomizer->destroy(randomizer);
664
665 /* public functions */
666 this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate;
667 this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process;
668 this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*))get_type;
669 this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual;
670 this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk;
671 this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy;
672
673 /* private data */
674 this->peer = peer;
675 this->version.ptr = version;
676 this->version.len = sizeof(version);
677 this->version_list = chunk_empty;
678 this->k_auth = chunk_empty;
679 this->k_encr = chunk_empty;
680 this->msk = chunk_empty;
681 this->emsk = chunk_empty;
682
683 return &this->public;
684 }