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