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