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