Annotation of embedaddon/strongswan/src/libcharon/plugins/eap_aka/eap_aka_peer.c, revision 1.1.1.1
1.1 misho 1: /*
2: * Copyright (C) 2006-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_aka_peer.h"
17:
18: #include <library.h>
19: #include <daemon.h>
20:
21: #include <simaka_message.h>
22: #include <simaka_crypto.h>
23: #include <simaka_manager.h>
24:
25: typedef struct private_eap_aka_peer_t private_eap_aka_peer_t;
26:
27: /**
28: * Private data of an eap_aka_peer_t object.
29: */
30: struct private_eap_aka_peer_t {
31:
32: /**
33: * Public authenticator_t interface.
34: */
35: eap_aka_peer_t public;
36:
37: /**
38: * AKA backend manager
39: */
40: simaka_manager_t *mgr;
41:
42: /**
43: * EAP-AKA crypto helper
44: */
45: simaka_crypto_t *crypto;
46:
47: /**
48: * permanent ID of peer
49: */
50: identification_t *permanent;
51:
52: /**
53: * Pseudonym identity the peer uses
54: */
55: identification_t *pseudonym;
56:
57: /**
58: * Reauthentication identity the peer uses
59: */
60: identification_t *reauth;
61:
62: /**
63: * EAP message identifier
64: */
65: uint8_t identifier;
66:
67: /**
68: * MSK
69: */
70: chunk_t msk;
71:
72: /**
73: * Master key, if reauthentication is used
74: */
75: char mk[HASH_SIZE_SHA1];
76:
77: /**
78: * Counter value if reauthentication is used
79: */
80: uint16_t counter;
81: };
82:
83: /**
84: * Generate a payload from a message, destroy message
85: */
86: static bool generate_payload(simaka_message_t *message, chunk_t data,
87: eap_payload_t **out)
88: {
89: chunk_t chunk;
90: bool ok;
91:
92: ok = message->generate(message, data, &chunk);
93: if (ok)
94: {
95: *out = eap_payload_create_data_own(chunk);
96: }
97: message->destroy(message);
98: return ok;
99: }
100:
101: /**
102: * Create a AKA_CLIENT_ERROR: "Unable to process"
103: */
104: static bool create_client_error(private_eap_aka_peer_t *this,
105: eap_payload_t **out)
106: {
107: simaka_message_t *message;
108: uint16_t encoded;
109:
110: DBG1(DBG_IKE, "sending client error '%N'",
111: simaka_client_error_names, AKA_UNABLE_TO_PROCESS);
112:
113: message = simaka_message_create(FALSE, this->identifier, EAP_AKA,
114: AKA_CLIENT_ERROR, this->crypto);
115: encoded = htons(AKA_UNABLE_TO_PROCESS);
116: message->add_attribute(message, AT_CLIENT_ERROR_CODE,
117: chunk_create((char*)&encoded, sizeof(encoded)));
118:
119: return generate_payload(message, chunk_empty, out);
120: }
121:
122: /**
123: * process an EAP-AKA/Request/Identity message
124: */
125: static status_t process_identity(private_eap_aka_peer_t *this,
126: simaka_message_t *in, eap_payload_t **out)
127: {
128: simaka_message_t *message;
129: enumerator_t *enumerator;
130: simaka_attribute_t type;
131: chunk_t data, id = chunk_empty;
132: simaka_attribute_t id_req = 0;
133:
134: /* reset previously uses reauthentication/pseudonym data */
135: this->crypto->clear_keys(this->crypto);
136: DESTROY_IF(this->pseudonym);
137: this->pseudonym = NULL;
138: DESTROY_IF(this->reauth);
139: this->reauth = NULL;
140:
141: enumerator = in->create_attribute_enumerator(in);
142: while (enumerator->enumerate(enumerator, &type, &data))
143: {
144: switch (type)
145: {
146: case AT_ANY_ID_REQ:
147: case AT_FULLAUTH_ID_REQ:
148: case AT_PERMANENT_ID_REQ:
149: id_req = type;
150: break;
151: default:
152: if (!simaka_attribute_skippable(type))
153: {
154: enumerator->destroy(enumerator);
155: if (!create_client_error(this, out))
156: {
157: return FAILED;
158: }
159: return NEED_MORE;
160: }
161: break;
162: }
163: }
164: enumerator->destroy(enumerator);
165:
166: switch (id_req)
167: {
168: case AT_ANY_ID_REQ:
169: this->reauth = this->mgr->card_get_reauth(this->mgr,
170: this->permanent, this->mk, &this->counter);
171: if (this->reauth)
172: {
173: id = this->reauth->get_encoding(this->reauth);
174: break;
175: }
176: /* FALL */
177: case AT_FULLAUTH_ID_REQ:
178: this->pseudonym = this->mgr->card_get_pseudonym(this->mgr,
179: this->permanent);
180: if (this->pseudonym)
181: {
182: id = this->pseudonym->get_encoding(this->pseudonym);
183: break;
184: }
185: /* FALL */
186: case AT_PERMANENT_ID_REQ:
187: id = this->permanent->get_encoding(this->permanent);
188: break;
189: default:
190: break;
191: }
192: message = simaka_message_create(FALSE, this->identifier, EAP_AKA,
193: AKA_IDENTITY, this->crypto);
194: if (id.len)
195: {
196: message->add_attribute(message, AT_IDENTITY, id);
197: }
198: if (!generate_payload(message, chunk_empty, out))
199: {
200: return FAILED;
201: }
202: return NEED_MORE;
203: }
204:
205: /**
206: * Process an EAP-AKA/Request/Challenge message
207: */
208: static status_t process_challenge(private_eap_aka_peer_t *this,
209: simaka_message_t *in, eap_payload_t **out)
210: {
211: simaka_message_t *message;
212: enumerator_t *enumerator;
213: simaka_attribute_t type;
214: chunk_t data, rand = chunk_empty, autn = chunk_empty, mk;
215: u_char res[AKA_RES_MAX], ck[AKA_CK_LEN], ik[AKA_IK_LEN], auts[AKA_AUTS_LEN];
216: int res_len;
217: identification_t *id;
218: status_t status;
219:
220: enumerator = in->create_attribute_enumerator(in);
221: while (enumerator->enumerate(enumerator, &type, &data))
222: {
223: switch (type)
224: {
225: case AT_RAND:
226: rand = data;
227: break;
228: case AT_AUTN:
229: autn = data;
230: break;
231: default:
232: if (!simaka_attribute_skippable(type))
233: {
234: enumerator->destroy(enumerator);
235: if (!create_client_error(this, out))
236: {
237: return FAILED;
238: }
239: return NEED_MORE;
240: }
241: break;
242: }
243: }
244: enumerator->destroy(enumerator);
245:
246: if (!rand.len || !autn.len)
247: {
248: DBG1(DBG_IKE, "received invalid EAP-AKA challenge message");
249: if (!create_client_error(this, out))
250: {
251: return FAILED;
252: }
253: return NEED_MORE;
254: }
255:
256: status = this->mgr->card_get_quintuplet(this->mgr, this->permanent,
257: rand.ptr, autn.ptr, ck, ik, res, &res_len);
258: if (status == INVALID_STATE &&
259: this->mgr->card_resync(this->mgr, this->permanent, rand.ptr, auts))
260: {
261: DBG1(DBG_IKE, "received SQN invalid, sending %N",
262: simaka_subtype_names, AKA_SYNCHRONIZATION_FAILURE);
263: message = simaka_message_create(FALSE, in->get_identifier(in), EAP_AKA,
264: AKA_SYNCHRONIZATION_FAILURE, this->crypto);
265: message->add_attribute(message, AT_AUTS,
266: chunk_create(auts, AKA_AUTS_LEN));
267: if (!generate_payload(message, chunk_empty, out))
268: {
269: return FAILED;
270: }
271: return NEED_MORE;
272: }
273: if (status != SUCCESS)
274: {
275: DBG1(DBG_IKE, "no USIM found with quintuplets for '%Y', sending %N",
276: this->permanent, simaka_subtype_names, AKA_AUTHENTICATION_REJECT);
277: message = simaka_message_create(FALSE, in->get_identifier(in), EAP_AKA,
278: AKA_AUTHENTICATION_REJECT, this->crypto);
279: if (!generate_payload(message, chunk_empty, out))
280: {
281: return FAILED;
282: }
283: return NEED_MORE;
284: }
285:
286: id = this->permanent;
287: if (this->pseudonym)
288: {
289: id = this->pseudonym;
290: }
291: data = chunk_cata("cc", chunk_create(ik, AKA_IK_LEN),
292: chunk_create(ck, AKA_CK_LEN));
293: chunk_clear(&this->msk);
294: if (!this->crypto->derive_keys_full(this->crypto, id, data, &mk, &this->msk))
295: {
296: return FAILED;
297: }
298: memcpy(this->mk, mk.ptr, mk.len);
299: chunk_clear(&mk);
300:
301: /* Verify AT_MAC attribute and parse() again after key derivation,
302: * reading encrypted attributes */
303: if (!in->verify(in, chunk_empty) || !in->parse(in))
304: {
305: if (!create_client_error(this, out))
306: {
307: return FAILED;
308: }
309: return NEED_MORE;
310: }
311:
312: enumerator = in->create_attribute_enumerator(in);
313: while (enumerator->enumerate(enumerator, &type, &data))
314: {
315: switch (type)
316: {
317: case AT_NEXT_REAUTH_ID:
318: this->counter = 0;
319: id = identification_create_from_data(data);
320: this->mgr->card_set_reauth(this->mgr, this->permanent, id,
321: this->mk, this->counter);
322: id->destroy(id);
323: break;
324: case AT_NEXT_PSEUDONYM:
325: id = identification_create_from_data(data);
326: this->mgr->card_set_pseudonym(this->mgr, this->permanent, id);
327: id->destroy(id);
328: break;
329: default:
330: break;
331: }
332: }
333: enumerator->destroy(enumerator);
334:
335: message = simaka_message_create(FALSE, this->identifier, EAP_AKA,
336: AKA_CHALLENGE, this->crypto);
337: message->add_attribute(message, AT_RES, chunk_create(res, res_len));
338: if (!generate_payload(message, chunk_empty, out))
339: {
340: return FAILED;
341: }
342: return NEED_MORE;
343: }
344:
345: /**
346: * Check if a received counter value is acceptable
347: */
348: static bool counter_too_small(private_eap_aka_peer_t *this, chunk_t chunk)
349: {
350: uint16_t counter;
351:
352: memcpy(&counter, chunk.ptr, sizeof(counter));
353: counter = htons(counter);
354: return counter < this->counter;
355: }
356:
357: /**
358: * process an EAP-AKA/Request/Reauthentication message
359: */
360: static status_t process_reauthentication(private_eap_aka_peer_t *this,
361: simaka_message_t *in, eap_payload_t **out)
362: {
363: simaka_message_t *message;
364: enumerator_t *enumerator;
365: simaka_attribute_t type;
366: chunk_t data, counter = chunk_empty, nonce = chunk_empty, id = chunk_empty;
367:
368: if (!this->reauth)
369: {
370: DBG1(DBG_IKE, "received %N, but not expected",
371: simaka_subtype_names, AKA_REAUTHENTICATION);
372: if (!create_client_error(this, out))
373: {
374: return FAILED;
375: }
376: return NEED_MORE;
377: }
378:
379: if (!this->crypto->derive_keys_reauth(this->crypto,
380: chunk_create(this->mk, HASH_SIZE_SHA1)))
381: {
382: return FAILED;
383: }
384:
385: /* verify MAC and parse again with decryption key */
386: if (!in->verify(in, chunk_empty) || !in->parse(in))
387: {
388: if (!create_client_error(this, out))
389: {
390: return FAILED;
391: }
392: return NEED_MORE;
393: }
394:
395: enumerator = in->create_attribute_enumerator(in);
396: while (enumerator->enumerate(enumerator, &type, &data))
397: {
398: switch (type)
399: {
400: case AT_COUNTER:
401: counter = data;
402: break;
403: case AT_NONCE_S:
404: nonce = data;
405: break;
406: case AT_NEXT_REAUTH_ID:
407: id = data;
408: break;
409: default:
410: if (!simaka_attribute_skippable(type))
411: {
412: enumerator->destroy(enumerator);
413: if (!create_client_error(this, out))
414: {
415: return FAILED;
416: }
417: return NEED_MORE;
418: }
419: break;
420: }
421: }
422: enumerator->destroy(enumerator);
423:
424: if (!nonce.len || !counter.len)
425: {
426: DBG1(DBG_IKE, "EAP-AKA/Request/Reauthentication message incomplete");
427: if (!create_client_error(this, out))
428: {
429: return FAILED;
430: }
431: return NEED_MORE;
432: }
433:
434: message = simaka_message_create(FALSE, in->get_identifier(in), EAP_AKA,
435: AKA_REAUTHENTICATION, this->crypto);
436: if (counter_too_small(this, counter))
437: {
438: DBG1(DBG_IKE, "reauthentication counter too small");
439: message->add_attribute(message, AT_COUNTER_TOO_SMALL, chunk_empty);
440: }
441: else
442: {
443: chunk_clear(&this->msk);
444: if (!this->crypto->derive_keys_reauth_msk(this->crypto,
445: this->reauth, counter, nonce,
446: chunk_create(this->mk, HASH_SIZE_SHA1), &this->msk))
447: {
448: message->destroy(message);
449: return FAILED;
450: }
451: if (id.len)
452: {
453: identification_t *reauth;
454:
455: reauth = identification_create_from_data(data);
456: this->mgr->card_set_reauth(this->mgr, this->permanent, reauth,
457: this->mk, this->counter);
458: reauth->destroy(reauth);
459: }
460: }
461: message->add_attribute(message, AT_COUNTER, counter);
462: if (!generate_payload(message, nonce, out))
463: {
464: return FAILED;
465: }
466: return NEED_MORE;
467: }
468:
469: /**
470: * Process an EAP-AKA/Request/Notification message
471: */
472: static status_t process_notification(private_eap_aka_peer_t *this,
473: simaka_message_t *in, eap_payload_t **out)
474: {
475: simaka_message_t *message;
476: enumerator_t *enumerator;
477: simaka_attribute_t type;
478: chunk_t data;
479: bool success = TRUE;
480:
481: enumerator = in->create_attribute_enumerator(in);
482: while (enumerator->enumerate(enumerator, &type, &data))
483: {
484: if (type == AT_NOTIFICATION)
485: {
486: uint16_t code;
487:
488: memcpy(&code, data.ptr, sizeof(code));
489: code = ntohs(code);
490:
491: /* test success bit */
492: if (!(data.ptr[0] & 0x80))
493: {
494: DBG1(DBG_IKE, "received EAP-AKA notification error '%N'",
495: simaka_notification_names, code);
496: }
497: else
498: {
499: DBG1(DBG_IKE, "received EAP-AKA notification '%N'",
500: simaka_notification_names, code);
501: }
502: }
503: else if (!simaka_attribute_skippable(type))
504: {
505: success = FALSE;
506: break;
507: }
508: }
509: enumerator->destroy(enumerator);
510:
511: if (success)
512: { /* empty notification reply */
513: message = simaka_message_create(FALSE, this->identifier, EAP_AKA,
514: AKA_NOTIFICATION, this->crypto);
515: if (!generate_payload(message, chunk_empty, out))
516: {
517: return FAILED;
518: }
519: }
520: else
521: {
522: if (!create_client_error(this, out))
523: {
524: return FAILED;
525: }
526: }
527: return NEED_MORE;
528: }
529:
530:
531: METHOD(eap_method_t, process, status_t,
532: private_eap_aka_peer_t *this, eap_payload_t *in, eap_payload_t **out)
533: {
534: simaka_message_t *message;
535: status_t status;
536:
537: /* store received EAP message identifier */
538: this->identifier = in->get_identifier(in);
539:
540: message = simaka_message_create_from_payload(in->get_data(in), this->crypto);
541: if (!message)
542: {
543: if (!create_client_error(this, out))
544: {
545: return FAILED;
546: }
547: return NEED_MORE;
548: }
549: if (!message->parse(message))
550: {
551: message->destroy(message);
552: if (!create_client_error(this, out))
553: {
554: return FAILED;
555: }
556: return NEED_MORE;
557: }
558: switch (message->get_subtype(message))
559: {
560: case AKA_IDENTITY:
561: status = process_identity(this, message, out);
562: break;
563: case AKA_CHALLENGE:
564: status = process_challenge(this, message, out);
565: break;
566: case AKA_REAUTHENTICATION:
567: status = process_reauthentication(this, message, out);
568: break;
569: case AKA_NOTIFICATION:
570: status = process_notification(this, message, out);
571: break;
572: default:
573: DBG1(DBG_IKE, "unable to process EAP-AKA subtype %N",
574: simaka_subtype_names, message->get_subtype(message));
575: if (!create_client_error(this, out))
576: {
577: status = FAILED;
578: }
579: else
580: {
581: status = NEED_MORE;
582: }
583: break;
584: }
585: message->destroy(message);
586: return status;
587: }
588:
589: METHOD(eap_method_t, initiate, status_t,
590: private_eap_aka_peer_t *this, eap_payload_t **out)
591: {
592: /* peer never initiates */
593: return FAILED;
594: }
595:
596: METHOD(eap_method_t, get_type, eap_type_t,
597: private_eap_aka_peer_t *this, uint32_t *vendor)
598: {
599: *vendor = 0;
600: return EAP_AKA;
601: }
602:
603: METHOD(eap_method_t, get_msk, status_t,
604: private_eap_aka_peer_t *this, chunk_t *msk)
605: {
606: if (this->msk.ptr)
607: {
608: *msk = this->msk;
609: return SUCCESS;
610: }
611: return FAILED;
612: }
613:
614: METHOD(eap_method_t, get_identifier, uint8_t,
615: private_eap_aka_peer_t *this)
616: {
617: return this->identifier;
618: }
619:
620: METHOD(eap_method_t, set_identifier, void,
621: private_eap_aka_peer_t *this, uint8_t identifier)
622: {
623: this->identifier = identifier;
624: }
625:
626: METHOD(eap_method_t, is_mutual, bool,
627: private_eap_aka_peer_t *this)
628: {
629: return TRUE;
630: }
631:
632: METHOD(eap_method_t, destroy, void,
633: private_eap_aka_peer_t *this)
634: {
635: this->crypto->destroy(this->crypto);
636: this->permanent->destroy(this->permanent);
637: DESTROY_IF(this->pseudonym);
638: DESTROY_IF(this->reauth);
639: free(this->msk.ptr);
640: free(this);
641: }
642:
643: /*
644: * Described in header.
645: */
646: eap_aka_peer_t *eap_aka_peer_create(identification_t *server,
647: identification_t *peer)
648: {
649: private_eap_aka_peer_t *this;
650:
651: INIT(this,
652: .public = {
653: .interface = {
654: .initiate = _initiate,
655: .process = _process,
656: .get_type = _get_type,
657: .is_mutual = _is_mutual,
658: .get_msk = _get_msk,
659: .get_identifier = _get_identifier,
660: .set_identifier = _set_identifier,
661: .destroy = _destroy,
662: },
663: },
664: .crypto = simaka_crypto_create(EAP_AKA),
665: .mgr = lib->get(lib, "aka-manager"),
666: );
667:
668: if (!this->crypto)
669: {
670: free(this);
671: return NULL;
672: }
673:
674: this->permanent = peer->clone(peer);
675:
676: return &this->public;
677: }
678:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>