Annotation of embedaddon/strongswan/src/libcharon/plugins/eap_sim/eap_sim_server.c, revision 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>