Annotation of embedaddon/strongswan/src/libcharon/plugins/eap_aka/eap_aka_server.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_server.h"
17:
18: #include <daemon.h>
19: #include <library.h>
20:
21: #include <simaka_message.h>
22: #include <simaka_crypto.h>
23: #include <simaka_manager.h>
24:
25: /** length of the AT_NONCE_S value */
26: #define NONCE_LEN 16
27:
28: typedef struct private_eap_aka_server_t private_eap_aka_server_t;
29:
30: /**
31: * Private data of an eap_aka_server_t object.
32: */
33: struct private_eap_aka_server_t {
34:
35: /**
36: * Public authenticator_t interface.
37: */
38: eap_aka_server_t public;
39:
40: /**
41: * AKA backend manager
42: */
43: simaka_manager_t *mgr;
44:
45: /**
46: * EAP-AKA crypto helper
47: */
48: simaka_crypto_t *crypto;
49:
50: /**
51: * permanent ID of the peer
52: */
53: identification_t *permanent;
54:
55: /**
56: * pseudonym ID of peer
57: */
58: identification_t *pseudonym;
59:
60: /**
61: * reauthentication ID of peer
62: */
63: identification_t *reauth;
64:
65: /**
66: * EAP message identifier
67: */
68: uint8_t identifier;
69:
70: /**
71: * Expected Result XRES
72: */
73: chunk_t xres;
74:
75: /**
76: * Random value RAND
77: */
78: chunk_t rand;
79:
80: /**
81: * MSK
82: */
83: chunk_t msk;
84:
85: /**
86: * Nonce value used in AT_NONCE_S
87: */
88: chunk_t nonce;
89:
90: /**
91: * Counter value negotiated, network order
92: */
93: chunk_t counter;
94:
95: /**
96: * Do we request fast reauthentication?
97: */
98: bool use_reauth;
99:
100: /**
101: * Do we request pseudonym identities?
102: */
103: bool use_pseudonym;
104:
105: /**
106: * Do we request permanent identities?
107: */
108: bool use_permanent;
109:
110: /**
111: * EAP-AKA message we have initiated
112: */
113: simaka_subtype_t pending;
114:
115: /**
116: * Did the client send a synchronize request?
117: */
118: bool synchronized;
119: };
120:
121: /**
122: * Generate a payload from a message, destroy message
123: */
124: static bool generate_payload(simaka_message_t *message, chunk_t data,
125: eap_payload_t **out)
126: {
127: chunk_t chunk;
128: bool ok;
129:
130: ok = message->generate(message, data, &chunk);
131: if (ok)
132: {
133: *out = eap_payload_create_data_own(chunk);
134: }
135: message->destroy(message);
136: return ok;
137: }
138:
139: /**
140: * Create EAP-AKA/Request/Identity message
141: */
142: static status_t identity(private_eap_aka_server_t *this, eap_payload_t **out)
143: {
144: simaka_message_t *message;
145:
146: message = simaka_message_create(TRUE, this->identifier++, EAP_AKA,
147: AKA_IDENTITY, this->crypto);
148: if (this->use_reauth)
149: {
150: message->add_attribute(message, AT_ANY_ID_REQ, chunk_empty);
151: }
152: else if (this->use_pseudonym)
153: {
154: message->add_attribute(message, AT_FULLAUTH_ID_REQ, chunk_empty);
155: }
156: else if (this->use_permanent)
157: {
158: message->add_attribute(message, AT_PERMANENT_ID_REQ, chunk_empty);
159: }
160: if (!generate_payload(message, chunk_empty, out))
161: {
162: return FAILED;
163: }
164: this->pending = AKA_IDENTITY;
165: return NEED_MORE;
166: }
167:
168: /**
169: * Create EAP-AKA/Request/Challenge message
170: */
171: static status_t challenge(private_eap_aka_server_t *this, eap_payload_t **out)
172: {
173: simaka_message_t *message;
174: char rand[AKA_RAND_LEN], xres[AKA_RES_MAX];
175: char ck[AKA_CK_LEN], ik[AKA_IK_LEN], autn[AKA_AUTN_LEN];
176: int xres_len;
177: chunk_t data, mk;
178: identification_t *id;
179:
180: if (!this->mgr->provider_get_quintuplet(this->mgr, this->permanent,
181: rand, xres, &xres_len, ck, ik, autn))
182: {
183: if (this->use_pseudonym)
184: {
185: /* probably received a pseudonym/reauth id we couldn't map */
186: DBG1(DBG_IKE, "failed to map pseudonym/reauth identity '%Y', "
187: "fallback to permanent identity request", this->permanent);
188: this->use_pseudonym = FALSE;
189: DESTROY_IF(this->pseudonym);
190: this->pseudonym = NULL;
191: return identity(this, out);
192: }
193: return FAILED;
194: }
195: id = this->permanent;
196: if (this->pseudonym)
197: {
198: id = this->pseudonym;
199: }
200: data = chunk_cata("cc", chunk_create(ik, AKA_IK_LEN),
201: chunk_create(ck, AKA_CK_LEN));
202: chunk_clear(&this->msk);
203: if (!this->crypto->derive_keys_full(this->crypto, id, data, &mk, &this->msk))
204: {
205: return FAILED;
206: }
207: this->rand = chunk_clone(chunk_create(rand, AKA_RAND_LEN));
208: this->xres = chunk_clone(chunk_create(xres, xres_len));
209:
210: message = simaka_message_create(TRUE, this->identifier++, EAP_AKA,
211: AKA_CHALLENGE, this->crypto);
212: message->add_attribute(message, AT_RAND, this->rand);
213: message->add_attribute(message, AT_AUTN, chunk_create(autn, AKA_AUTN_LEN));
214: id = this->mgr->provider_gen_reauth(this->mgr, this->permanent, mk.ptr);
215: free(mk.ptr);
216: if (id)
217: {
218: message->add_attribute(message, AT_NEXT_REAUTH_ID,
219: id->get_encoding(id));
220: id->destroy(id);
221: }
222: id = this->mgr->provider_gen_pseudonym(this->mgr, this->permanent);
223: if (id)
224: {
225: message->add_attribute(message, AT_NEXT_PSEUDONYM,
226: id->get_encoding(id));
227: id->destroy(id);
228: }
229: if (!generate_payload(message, chunk_empty, out))
230: {
231: return FAILED;
232: }
233: this->pending = AKA_CHALLENGE;
234: return NEED_MORE;
235: }
236:
237: /**
238: * Initiate EAP-AKA/Request/Re-authentication message
239: */
240: static status_t reauthenticate(private_eap_aka_server_t *this,
241: char mk[HASH_SIZE_SHA1], uint16_t counter,
242: eap_payload_t **out)
243: {
244: simaka_message_t *message;
245: identification_t *next;
246: chunk_t mkc;
247: rng_t *rng;
248:
249: DBG1(DBG_IKE, "initiating EAP-AKA reauthentication");
250:
251: rng = this->crypto->get_rng(this->crypto);
252: if (!rng->allocate_bytes(rng, NONCE_LEN, &this->nonce))
253: {
254: return FAILED;
255: }
256:
257: mkc = chunk_create(mk, HASH_SIZE_SHA1);
258: counter = htons(counter);
259: this->counter = chunk_clone(chunk_create((char*)&counter, sizeof(counter)));
260:
261: if (!this->crypto->derive_keys_reauth(this->crypto, mkc) ||
262: !this->crypto->derive_keys_reauth_msk(this->crypto,
263: this->reauth, this->counter, this->nonce, mkc, &this->msk))
264: {
265: return FAILED;
266: }
267:
268: message = simaka_message_create(TRUE, this->identifier++, EAP_AKA,
269: AKA_REAUTHENTICATION, this->crypto);
270: message->add_attribute(message, AT_COUNTER, this->counter);
271: message->add_attribute(message, AT_NONCE_S, this->nonce);
272: next = this->mgr->provider_gen_reauth(this->mgr, this->permanent, mk);
273: if (next)
274: {
275: message->add_attribute(message, AT_NEXT_REAUTH_ID,
276: next->get_encoding(next));
277: next->destroy(next);
278: }
279: if (!generate_payload(message, chunk_empty, out))
280: {
281: return FAILED;
282: }
283: this->pending = SIM_REAUTHENTICATION;
284: return NEED_MORE;
285: }
286:
287: METHOD(eap_method_t, initiate, status_t,
288: private_eap_aka_server_t *this, eap_payload_t **out)
289: {
290: if (this->use_permanent || this->use_pseudonym || this->use_reauth)
291: {
292: return identity(this, out);
293: }
294: return challenge(this, out);
295: }
296:
297: /**
298: * Process EAP-AKA/Response/Identity message
299: */
300: static status_t process_identity(private_eap_aka_server_t *this,
301: simaka_message_t *in, eap_payload_t **out)
302: {
303: identification_t *permanent, *id;
304: enumerator_t *enumerator;
305: simaka_attribute_t type;
306: chunk_t data, identity = chunk_empty;
307:
308: if (this->pending != AKA_IDENTITY)
309: {
310: DBG1(DBG_IKE, "received %N, but not expected",
311: simaka_subtype_names, AKA_IDENTITY);
312: return FAILED;
313: }
314:
315: enumerator = in->create_attribute_enumerator(in);
316: while (enumerator->enumerate(enumerator, &type, &data))
317: {
318: switch (type)
319: {
320: case AT_IDENTITY:
321: identity = data;
322: break;
323: default:
324: if (!simaka_attribute_skippable(type))
325: {
326: enumerator->destroy(enumerator);
327: return FAILED;
328: }
329: break;
330: }
331: }
332: enumerator->destroy(enumerator);
333:
334: if (!identity.len)
335: {
336: DBG1(DBG_IKE, "received incomplete Identity response");
337: return FAILED;
338: }
339:
340: id = identification_create_from_data(identity);
341: if (this->use_reauth)
342: {
343: char mk[HASH_SIZE_SHA1];
344: uint16_t counter;
345:
346: permanent = this->mgr->provider_is_reauth(this->mgr, id, mk, &counter);
347: if (permanent)
348: {
349: this->permanent->destroy(this->permanent);
350: this->permanent = permanent;
351: this->reauth = id;
352: return reauthenticate(this, mk, counter, out);
353: }
354: /* unable to map, maybe a pseudonym? */
355: DBG1(DBG_IKE, "'%Y' is not a reauth identity", id);
356: this->use_reauth = FALSE;
357: }
358: if (this->use_pseudonym)
359: {
360: permanent = this->mgr->provider_is_pseudonym(this->mgr, id);
361: if (permanent)
362: {
363: this->permanent->destroy(this->permanent);
364: this->permanent = permanent;
365: this->pseudonym = id->clone(id);
366: /* we already have a new permanent identity now */
367: this->use_permanent = FALSE;
368: }
369: else
370: {
371: DBG1(DBG_IKE, "'%Y' is not a pseudonym", id);
372: }
373: }
374: if (!this->pseudonym && this->use_permanent)
375: {
376: /* got a permanent identity or a pseudonym reauth id wou couldn't map,
377: * try to get quintuplets */
378: DBG1(DBG_IKE, "received identity '%Y'", id);
379: this->permanent->destroy(this->permanent);
380: this->permanent = id->clone(id);
381: }
382: id->destroy(id);
383:
384: return challenge(this, out);
385: }
386:
387: /**
388: * Process EAP-AKA/Response/Challenge message
389: */
390: static status_t process_challenge(private_eap_aka_server_t *this,
391: simaka_message_t *in)
392: {
393: enumerator_t *enumerator;
394: simaka_attribute_t type;
395: chunk_t data, res = chunk_empty;
396:
397: if (this->pending != AKA_CHALLENGE)
398: {
399: DBG1(DBG_IKE, "received %N, but not expected",
400: simaka_subtype_names, AKA_CHALLENGE);
401: return FAILED;
402: }
403: /* verify MAC of EAP message, AT_MAC */
404: if (!in->verify(in, chunk_empty))
405: {
406: return FAILED;
407: }
408: enumerator = in->create_attribute_enumerator(in);
409: while (enumerator->enumerate(enumerator, &type, &data))
410: {
411: switch (type)
412: {
413: case AT_RES:
414: res = data;
415: break;
416: default:
417: if (!simaka_attribute_skippable(type))
418: {
419: enumerator->destroy(enumerator);
420: return FAILED;
421: }
422: break;
423: }
424: }
425: enumerator->destroy(enumerator);
426:
427: /* compare received RES against stored XRES */
428: if (!chunk_equals_const(res, this->xres))
429: {
430: DBG1(DBG_IKE, "received RES does not match XRES");
431: return FAILED;
432: }
433: return SUCCESS;
434: }
435:
436: /**
437: * process an EAP-AKA/Response/Reauthentication message
438: */
439: static status_t process_reauthentication(private_eap_aka_server_t *this,
440: simaka_message_t *in, eap_payload_t **out)
441: {
442: enumerator_t *enumerator;
443: simaka_attribute_t type;
444: chunk_t data, counter = chunk_empty;
445: bool too_small = FALSE;
446:
447: if (this->pending != AKA_REAUTHENTICATION)
448: {
449: DBG1(DBG_IKE, "received %N, but not expected",
450: simaka_subtype_names, AKA_REAUTHENTICATION);
451: return FAILED;
452: }
453: /* verify AT_MAC attribute, signature is over "EAP packet | NONCE_S" */
454: if (!in->verify(in, this->nonce))
455: {
456: return FAILED;
457: }
458:
459: enumerator = in->create_attribute_enumerator(in);
460: while (enumerator->enumerate(enumerator, &type, &data))
461: {
462: switch (type)
463: {
464: case AT_COUNTER:
465: counter = data;
466: break;
467: case AT_COUNTER_TOO_SMALL:
468: too_small = TRUE;
469: break;
470: default:
471: if (!simaka_attribute_skippable(type))
472: {
473: enumerator->destroy(enumerator);
474: return FAILED;
475: }
476: break;
477: }
478: }
479: enumerator->destroy(enumerator);
480:
481: if (too_small)
482: {
483: DBG1(DBG_IKE, "received %N, initiating full authentication",
484: simaka_attribute_names, AT_COUNTER_TOO_SMALL);
485: this->use_reauth = FALSE;
486: this->crypto->clear_keys(this->crypto);
487: return challenge(this, out);
488: }
489: if (!chunk_equals_const(counter, this->counter))
490: {
491: DBG1(DBG_IKE, "received counter does not match");
492: return FAILED;
493: }
494: return SUCCESS;
495: }
496:
497: /**
498: * Process EAP-AKA/Response/SynchronizationFailure message
499: */
500: static status_t process_synchronize(private_eap_aka_server_t *this,
501: simaka_message_t *in, eap_payload_t **out)
502: {
503: enumerator_t *enumerator;
504: simaka_attribute_t type;
505: chunk_t data, auts = chunk_empty;
506:
507: if (this->synchronized)
508: {
509: DBG1(DBG_IKE, "received %N, but peer did already resynchronize",
510: simaka_subtype_names, AKA_SYNCHRONIZATION_FAILURE);
511: return FAILED;
512: }
513:
514: DBG1(DBG_IKE, "received synchronization request, retrying...");
515:
516: enumerator = in->create_attribute_enumerator(in);
517: while (enumerator->enumerate(enumerator, &type, &data))
518: {
519: switch (type)
520: {
521: case AT_AUTS:
522: auts = data;
523: break;
524: default:
525: if (!simaka_attribute_skippable(type))
526: {
527: enumerator->destroy(enumerator);
528: return FAILED;
529: }
530: break;
531: }
532: }
533: enumerator->destroy(enumerator);
534:
535: if (!auts.len)
536: {
537: DBG1(DBG_IKE, "synchronization request didn't contain usable AUTS");
538: return FAILED;
539: }
540:
541: if (!this->mgr->provider_resync(this->mgr, this->permanent,
542: this->rand.ptr, auts.ptr))
543: {
544: DBG1(DBG_IKE, "no AKA provider found supporting "
545: "resynchronization for '%Y'", this->permanent);
546: return FAILED;
547: }
548: this->synchronized = TRUE;
549: return challenge(this, out);
550: }
551:
552: /**
553: * Process EAP-AKA/Response/ClientErrorCode message
554: */
555: static status_t process_client_error(private_eap_aka_server_t *this,
556: simaka_message_t *in)
557: {
558: enumerator_t *enumerator;
559: simaka_attribute_t type;
560: chunk_t data;
561:
562: enumerator = in->create_attribute_enumerator(in);
563: while (enumerator->enumerate(enumerator, &type, &data))
564: {
565: if (type == AT_CLIENT_ERROR_CODE)
566: {
567: uint16_t code;
568:
569: memcpy(&code, data.ptr, sizeof(code));
570: DBG1(DBG_IKE, "received EAP-AKA client error '%N'",
571: simaka_client_error_names, ntohs(code));
572: }
573: else if (!simaka_attribute_skippable(type))
574: {
575: break;
576: }
577: }
578: enumerator->destroy(enumerator);
579: return FAILED;
580: }
581:
582: /**
583: * Process EAP-AKA/Response/AuthenticationReject message
584: */
585: static status_t process_authentication_reject(private_eap_aka_server_t *this,
586: simaka_message_t *in)
587: {
588: DBG1(DBG_IKE, "received %N, authentication failed",
589: simaka_subtype_names, in->get_subtype(in));
590: return FAILED;
591: }
592:
593: METHOD(eap_method_t, process, status_t,
594: private_eap_aka_server_t *this, eap_payload_t *in, eap_payload_t **out)
595: {
596: simaka_message_t *message;
597: status_t status;
598:
599: message = simaka_message_create_from_payload(in->get_data(in), this->crypto);
600: if (!message)
601: {
602: return FAILED;
603: }
604: if (!message->parse(message))
605: {
606: message->destroy(message);
607: return FAILED;
608: }
609: switch (message->get_subtype(message))
610: {
611: case AKA_IDENTITY:
612: status = process_identity(this, message, out);
613: break;
614: case AKA_CHALLENGE:
615: status = process_challenge(this, message);
616: break;
617: case AKA_REAUTHENTICATION:
618: status = process_reauthentication(this, message, out);
619: break;
620: case AKA_SYNCHRONIZATION_FAILURE:
621: status = process_synchronize(this, message, out);
622: break;
623: case AKA_CLIENT_ERROR:
624: status = process_client_error(this, message);
625: break;
626: case AKA_AUTHENTICATION_REJECT:
627: status = process_authentication_reject(this, message);
628: break;
629: default:
630: DBG1(DBG_IKE, "unable to process EAP-AKA subtype %N",
631: simaka_subtype_names, message->get_subtype(message));
632: status = FAILED;
633: break;
634: }
635: message->destroy(message);
636: return status;
637: }
638:
639: METHOD(eap_method_t, get_type, eap_type_t,
640: private_eap_aka_server_t *this, uint32_t *vendor)
641: {
642: *vendor = 0;
643: return EAP_AKA;
644: }
645:
646: METHOD(eap_method_t, get_msk, status_t,
647: private_eap_aka_server_t *this, chunk_t *msk)
648: {
649: if (this->msk.ptr)
650: {
651: *msk = this->msk;
652: return SUCCESS;
653: }
654: return FAILED;
655: }
656:
657: METHOD(eap_method_t, get_identifier, uint8_t,
658: private_eap_aka_server_t *this)
659: {
660: return this->identifier;
661: }
662:
663: METHOD(eap_method_t, set_identifier, void,
664: private_eap_aka_server_t *this, uint8_t identifier)
665: {
666: this->identifier = identifier;
667: }
668:
669: METHOD(eap_method_t, is_mutual, bool,
670: private_eap_aka_server_t *this)
671: {
672: return TRUE;
673: }
674:
675: METHOD(eap_method_t, destroy, void,
676: private_eap_aka_server_t *this)
677: {
678: this->crypto->destroy(this->crypto);
679: this->permanent->destroy(this->permanent);
680: DESTROY_IF(this->pseudonym);
681: DESTROY_IF(this->reauth);
682: free(this->xres.ptr);
683: free(this->rand.ptr);
684: free(this->nonce.ptr);
685: free(this->msk.ptr);
686: free(this->counter.ptr);
687: free(this);
688: }
689:
690: /*
691: * Described in header.
692: */
693: eap_aka_server_t *eap_aka_server_create(identification_t *server,
694: identification_t *peer)
695: {
696: private_eap_aka_server_t *this;
697:
698: INIT(this,
699: .public = {
700: .interface = {
701: .initiate = _initiate,
702: .process = _process,
703: .get_type = _get_type,
704: .is_mutual = _is_mutual,
705: .get_msk = _get_msk,
706: .get_identifier = _get_identifier,
707: .set_identifier = _set_identifier,
708: .destroy = _destroy,
709: },
710: },
711: .crypto = simaka_crypto_create(EAP_AKA),
712: .mgr = lib->get(lib, "aka-manager"),
713: );
714:
715: if (!this->crypto)
716: {
717: free(this);
718: return NULL;
719: }
720:
721: this->permanent = peer->clone(peer);
722: this->use_reauth = this->use_pseudonym = this->use_permanent =
723: lib->settings->get_bool(lib->settings,
724: "%s.plugins.eap-aka.request_identity", TRUE, lib->ns);
725:
726: /* generate a non-zero identifier */
727: do {
728: this->identifier = random();
729: } while (!this->identifier);
730:
731: return &this->public;
732: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>