Return to eap_aka_server.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / strongswan / src / libcharon / plugins / eap_aka |
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: }