Return to eap_sim_peer.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / strongswan / src / libcharon / plugins / eap_sim |
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_peer.h" 17: 18: #include <daemon.h> 19: 20: #include <simaka_message.h> 21: #include <simaka_manager.h> 22: 23: /* number of tries we do authenticate */ 24: #define MAX_TRIES 3 25: 26: /* number of triplets for one authentication */ 27: #define TRIPLET_COUNT 3 28: 29: /** length of the AT_NONCE_MT nonce value */ 30: #define NONCE_LEN 16 31: 32: typedef struct private_eap_sim_peer_t private_eap_sim_peer_t; 33: 34: /** 35: * Private data of an eap_sim_peer_t object. 36: */ 37: struct private_eap_sim_peer_t { 38: 39: /** 40: * Public authenticator_t interface. 41: */ 42: eap_sim_peer_t public; 43: 44: /** 45: * SIM backend manager 46: */ 47: simaka_manager_t *mgr; 48: 49: /** 50: * permanent ID of peer 51: */ 52: identification_t *permanent; 53: 54: /** 55: * Pseudonym identity the peer uses 56: */ 57: identification_t *pseudonym; 58: 59: /** 60: * Reauthentication identity the peer uses 61: */ 62: identification_t *reauth; 63: 64: /** 65: * EAP message identifier 66: */ 67: uint8_t identifier; 68: 69: /** 70: * EAP-SIM crypto helper 71: */ 72: simaka_crypto_t *crypto; 73: 74: /** 75: * how many times we try to authenticate 76: */ 77: int tries; 78: 79: /** 80: * version list received from server 81: */ 82: chunk_t version_list; 83: 84: /** 85: * Nonce value used in AT_NONCE_MT/AT_NONCE_S 86: */ 87: chunk_t nonce; 88: 89: /** 90: * MSK, used for EAP-SIM based IKEv2 authentication 91: */ 92: chunk_t msk; 93: 94: /** 95: * Master key, if reauthentication is used 96: */ 97: char mk[HASH_SIZE_SHA1]; 98: 99: /** 100: * Counter value if reauthentication is used 101: */ 102: uint16_t counter; 103: }; 104: 105: /* version of SIM protocol we speak */ 106: static chunk_t version = chunk_from_chars(0x00,0x01); 107: 108: /** 109: * Generate a payload from a message, destroy message 110: */ 111: static bool generate_payload(simaka_message_t *message, chunk_t data, 112: eap_payload_t **out) 113: { 114: chunk_t chunk; 115: bool ok; 116: 117: ok = message->generate(message, data, &chunk); 118: if (ok) 119: { 120: *out = eap_payload_create_data_own(chunk); 121: } 122: message->destroy(message); 123: return ok; 124: } 125: 126: /** 127: * Create a SIM_CLIENT_ERROR 128: */ 129: static bool create_client_error(private_eap_sim_peer_t *this, 130: simaka_client_error_t code, eap_payload_t **out) 131: { 132: simaka_message_t *message; 133: uint16_t encoded; 134: 135: DBG1(DBG_IKE, "sending client error '%N'", simaka_client_error_names, code); 136: 137: message = simaka_message_create(FALSE, this->identifier, EAP_SIM, 138: SIM_CLIENT_ERROR, this->crypto); 139: encoded = htons(code); 140: message->add_attribute(message, AT_CLIENT_ERROR_CODE, 141: chunk_create((char*)&encoded, sizeof(encoded))); 142: return generate_payload(message, chunk_empty, out); 143: } 144: 145: /** 146: * process an EAP-SIM/Request/Start message 147: */ 148: static status_t process_start(private_eap_sim_peer_t *this, 149: simaka_message_t *in, eap_payload_t **out) 150: { 151: simaka_message_t *message; 152: enumerator_t *enumerator; 153: simaka_attribute_t type; 154: chunk_t data, id = chunk_empty; 155: rng_t *rng; 156: bool supported = FALSE; 157: simaka_attribute_t id_req = 0; 158: 159: /* reset previously uses reauthentication/pseudonym data */ 160: this->crypto->clear_keys(this->crypto); 161: DESTROY_IF(this->pseudonym); 162: this->pseudonym = NULL; 163: DESTROY_IF(this->reauth); 164: this->reauth = NULL; 165: 166: enumerator = in->create_attribute_enumerator(in); 167: while (enumerator->enumerate(enumerator, &type, &data)) 168: { 169: switch (type) 170: { 171: case AT_VERSION_LIST: 172: { 173: free(this->version_list.ptr); 174: this->version_list = chunk_clone(data); 175: while (data.len >= version.len) 176: { 177: if (memeq(data.ptr, version.ptr, version.len)) 178: { 179: supported = TRUE; 180: break; 181: } 182: } 183: break; 184: } 185: case AT_ANY_ID_REQ: 186: case AT_FULLAUTH_ID_REQ: 187: case AT_PERMANENT_ID_REQ: 188: id_req = type; 189: break; 190: default: 191: if (!simaka_attribute_skippable(type)) 192: { 193: enumerator->destroy(enumerator); 194: if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out)) 195: { 196: return FAILED; 197: } 198: return NEED_MORE; 199: } 200: break; 201: } 202: } 203: enumerator->destroy(enumerator); 204: 205: if (!supported) 206: { 207: DBG1(DBG_IKE, "server does not support EAP-SIM version number 1"); 208: if (!create_client_error(this, SIM_UNSUPPORTED_VERSION, out)) 209: { 210: return FAILED; 211: } 212: return NEED_MORE; 213: } 214: 215: switch (id_req) 216: { 217: case AT_ANY_ID_REQ: 218: this->reauth = this->mgr->card_get_reauth(this->mgr, 219: this->permanent, this->mk, &this->counter); 220: if (this->reauth) 221: { 222: id = this->reauth->get_encoding(this->reauth); 223: break; 224: } 225: /* FALL */ 226: case AT_FULLAUTH_ID_REQ: 227: this->pseudonym = this->mgr->card_get_pseudonym(this->mgr, 228: this->permanent); 229: if (this->pseudonym) 230: { 231: id = this->pseudonym->get_encoding(this->pseudonym); 232: break; 233: } 234: /* FALL */ 235: case AT_PERMANENT_ID_REQ: 236: id = this->permanent->get_encoding(this->permanent); 237: break; 238: default: 239: break; 240: } 241: 242: /* generate AT_NONCE_MT value */ 243: rng = this->crypto->get_rng(this->crypto); 244: free(this->nonce.ptr); 245: if (!rng->allocate_bytes(rng, NONCE_LEN, &this->nonce)) 246: { 247: return FAILED; 248: } 249: 250: message = simaka_message_create(FALSE, this->identifier, EAP_SIM, 251: SIM_START, this->crypto); 252: if (!this->reauth) 253: { 254: message->add_attribute(message, AT_SELECTED_VERSION, version); 255: message->add_attribute(message, AT_NONCE_MT, this->nonce); 256: } 257: if (id.len) 258: { 259: message->add_attribute(message, AT_IDENTITY, id); 260: } 261: if (!generate_payload(message, chunk_empty, out)) 262: { 263: return FAILED; 264: } 265: return NEED_MORE; 266: } 267: 268: /** 269: * process an EAP-SIM/Request/Challenge message 270: */ 271: static status_t process_challenge(private_eap_sim_peer_t *this, 272: simaka_message_t *in, eap_payload_t **out) 273: { 274: simaka_message_t *message; 275: enumerator_t *enumerator; 276: simaka_attribute_t type; 277: chunk_t data, rands = chunk_empty, kcs, kc, sreses, sres, mk; 278: identification_t *id; 279: 280: if (this->tries-- <= 0) 281: { 282: /* give up without notification. This hack is required as some buggy 283: * server implementations won't respect our client-error. */ 284: return FAILED; 285: } 286: 287: enumerator = in->create_attribute_enumerator(in); 288: while (enumerator->enumerate(enumerator, &type, &data)) 289: { 290: switch (type) 291: { 292: case AT_RAND: 293: rands = data; 294: break; 295: default: 296: if (!simaka_attribute_skippable(type)) 297: { 298: enumerator->destroy(enumerator); 299: if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out)) 300: { 301: return FAILED; 302: } 303: return NEED_MORE; 304: } 305: break; 306: } 307: } 308: enumerator->destroy(enumerator); 309: 310: /* excepting two or three RAND, each 16 bytes. We require two valid 311: * and different RANDs */ 312: if ((rands.len != 2 * SIM_RAND_LEN && rands.len != 3 * SIM_RAND_LEN) || 313: memeq_const(rands.ptr, rands.ptr + SIM_RAND_LEN, SIM_RAND_LEN)) 314: { 315: DBG1(DBG_IKE, "no valid AT_RAND received"); 316: if (!create_client_error(this, SIM_INSUFFICIENT_CHALLENGES, out)) 317: { 318: return FAILED; 319: } 320: return NEED_MORE; 321: } 322: /* get two or three KCs/SRESes from SIM using RANDs */ 323: kcs = kc = chunk_alloca(rands.len / 2); 324: sreses = sres = chunk_alloca(rands.len / 4); 325: while (rands.len >= SIM_RAND_LEN) 326: { 327: if (!this->mgr->card_get_triplet(this->mgr, this->permanent, 328: rands.ptr, sres.ptr, kc.ptr)) 329: { 330: DBG1(DBG_IKE, "unable to get EAP-SIM triplet"); 331: if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out)) 332: { 333: return FAILED; 334: } 335: return NEED_MORE; 336: } 337: DBG3(DBG_IKE, "got triplet for RAND %b\n Kc %b\n SRES %b", 338: rands.ptr, SIM_RAND_LEN, sres.ptr, SIM_SRES_LEN, kc.ptr, SIM_KC_LEN); 339: kc = chunk_skip(kc, SIM_KC_LEN); 340: sres = chunk_skip(sres, SIM_SRES_LEN); 341: rands = chunk_skip(rands, SIM_RAND_LEN); 342: } 343: 344: id = this->permanent; 345: if (this->pseudonym) 346: { 347: id = this->pseudonym; 348: } 349: data = chunk_cata("cccc", kcs, this->nonce, this->version_list, version); 350: chunk_clear(&this->msk); 351: if (!this->crypto->derive_keys_full(this->crypto, id, data, &mk, &this->msk)) 352: { 353: return FAILED; 354: } 355: memcpy(this->mk, mk.ptr, mk.len); 356: chunk_clear(&mk); 357: 358: /* Verify AT_MAC attribute, signature is over "EAP packet | NONCE_MT", and 359: * parse() again after key derivation, reading encrypted attributes */ 360: if (!in->verify(in, this->nonce) || !in->parse(in)) 361: { 362: if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out)) 363: { 364: return FAILED; 365: } 366: return NEED_MORE; 367: } 368: 369: enumerator = in->create_attribute_enumerator(in); 370: while (enumerator->enumerate(enumerator, &type, &data)) 371: { 372: switch (type) 373: { 374: case AT_NEXT_REAUTH_ID: 375: this->counter = 0; 376: id = identification_create_from_data(data); 377: this->mgr->card_set_reauth(this->mgr, this->permanent, id, 378: this->mk, this->counter); 379: id->destroy(id); 380: break; 381: case AT_NEXT_PSEUDONYM: 382: id = identification_create_from_data(data); 383: this->mgr->card_set_pseudonym(this->mgr, this->permanent, id); 384: id->destroy(id); 385: break; 386: default: 387: break; 388: } 389: } 390: enumerator->destroy(enumerator); 391: 392: /* build response with AT_MAC, built over "EAP packet | n*SRES" */ 393: message = simaka_message_create(FALSE, this->identifier, EAP_SIM, 394: SIM_CHALLENGE, this->crypto); 395: if (!generate_payload(message, sreses, out)) 396: { 397: return FAILED; 398: } 399: return NEED_MORE; 400: } 401: 402: /** 403: * Check if a received counter value is acceptable 404: */ 405: static bool counter_too_small(private_eap_sim_peer_t *this, chunk_t chunk) 406: { 407: uint16_t counter; 408: 409: memcpy(&counter, chunk.ptr, sizeof(counter)); 410: counter = htons(counter); 411: return counter < this->counter; 412: } 413: 414: /** 415: * process an EAP-SIM/Request/Re-Authentication message 416: */ 417: static status_t process_reauthentication(private_eap_sim_peer_t *this, 418: simaka_message_t *in, eap_payload_t **out) 419: { 420: simaka_message_t *message; 421: enumerator_t *enumerator; 422: simaka_attribute_t type; 423: chunk_t data, counter = chunk_empty, nonce = chunk_empty, id = chunk_empty; 424: 425: if (!this->reauth) 426: { 427: DBG1(DBG_IKE, "received %N, but not expected", 428: simaka_subtype_names, SIM_REAUTHENTICATION); 429: if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out)) 430: { 431: return FAILED; 432: } 433: return NEED_MORE; 434: } 435: 436: if (!this->crypto->derive_keys_reauth(this->crypto, 437: chunk_create(this->mk, HASH_SIZE_SHA1))) 438: { 439: return FAILED; 440: } 441: 442: /* verify MAC and parse again with decryption key */ 443: if (!in->verify(in, chunk_empty) || !in->parse(in)) 444: { 445: if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out)) 446: { 447: return FAILED; 448: } 449: return NEED_MORE; 450: } 451: 452: enumerator = in->create_attribute_enumerator(in); 453: while (enumerator->enumerate(enumerator, &type, &data)) 454: { 455: switch (type) 456: { 457: case AT_COUNTER: 458: counter = data; 459: break; 460: case AT_NONCE_S: 461: nonce = data; 462: break; 463: case AT_NEXT_REAUTH_ID: 464: id = data; 465: break; 466: default: 467: if (!simaka_attribute_skippable(type)) 468: { 469: enumerator->destroy(enumerator); 470: if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out)) 471: { 472: return FAILED; 473: } 474: return NEED_MORE; 475: } 476: break; 477: } 478: } 479: enumerator->destroy(enumerator); 480: 481: if (!nonce.len || !counter.len) 482: { 483: DBG1(DBG_IKE, "EAP-SIM/Request/Re-Authentication message incomplete"); 484: if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out)) 485: { 486: return FAILED; 487: } 488: return NEED_MORE; 489: } 490: 491: message = simaka_message_create(FALSE, this->identifier, EAP_SIM, 492: SIM_REAUTHENTICATION, this->crypto); 493: if (counter_too_small(this, counter)) 494: { 495: DBG1(DBG_IKE, "reauthentication counter too small"); 496: message->add_attribute(message, AT_COUNTER_TOO_SMALL, chunk_empty); 497: } 498: else 499: { 500: chunk_clear(&this->msk); 501: if (!this->crypto->derive_keys_reauth_msk(this->crypto, 502: this->reauth, counter, nonce, 503: chunk_create(this->mk, HASH_SIZE_SHA1), &this->msk)) 504: { 505: message->destroy(message); 506: return FAILED; 507: } 508: if (id.len) 509: { 510: identification_t *reauth; 511: 512: reauth = identification_create_from_data(data); 513: this->mgr->card_set_reauth(this->mgr, this->permanent, reauth, 514: this->mk, this->counter); 515: reauth->destroy(reauth); 516: } 517: } 518: message->add_attribute(message, AT_COUNTER, counter); 519: if (!generate_payload(message, nonce, out)) 520: { 521: return FAILED; 522: } 523: return NEED_MORE; 524: } 525: 526: /** 527: * process an EAP-SIM/Request/Notification message 528: */ 529: static status_t process_notification(private_eap_sim_peer_t *this, 530: simaka_message_t *in, eap_payload_t **out) 531: { 532: simaka_message_t *message; 533: enumerator_t *enumerator; 534: simaka_attribute_t type; 535: chunk_t data; 536: bool success = TRUE; 537: 538: enumerator = in->create_attribute_enumerator(in); 539: while (enumerator->enumerate(enumerator, &type, &data)) 540: { 541: if (type == AT_NOTIFICATION) 542: { 543: uint16_t code; 544: 545: memcpy(&code, data.ptr, sizeof(code)); 546: code = ntohs(code); 547: 548: /* test success bit */ 549: if (!(data.ptr[0] & 0x80)) 550: { 551: DBG1(DBG_IKE, "received EAP-SIM notification error '%N'", 552: simaka_notification_names, code); 553: } 554: else 555: { 556: DBG1(DBG_IKE, "received EAP-SIM notification '%N'", 557: simaka_notification_names, code); 558: } 559: } 560: else if (!simaka_attribute_skippable(type)) 561: { 562: success = FALSE; 563: break; 564: } 565: } 566: enumerator->destroy(enumerator); 567: 568: if (success) 569: { /* empty notification reply */ 570: message = simaka_message_create(FALSE, this->identifier, EAP_SIM, 571: SIM_NOTIFICATION, this->crypto); 572: if (!generate_payload(message, chunk_empty, out)) 573: { 574: return FAILED; 575: } 576: } 577: else 578: { 579: if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out)) 580: { 581: return FAILED; 582: } 583: } 584: return NEED_MORE; 585: } 586: 587: METHOD(eap_method_t, process, status_t, 588: private_eap_sim_peer_t *this, eap_payload_t *in, eap_payload_t **out) 589: { 590: simaka_message_t *message; 591: status_t status; 592: 593: /* store received EAP message identifier */ 594: this->identifier = in->get_identifier(in); 595: 596: message = simaka_message_create_from_payload(in->get_data(in), this->crypto); 597: if (!message) 598: { 599: if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out)) 600: { 601: return FAILED; 602: } 603: return NEED_MORE; 604: } 605: if (!message->parse(message)) 606: { 607: message->destroy(message); 608: if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out)) 609: { 610: return FAILED; 611: } 612: return NEED_MORE; 613: } 614: switch (message->get_subtype(message)) 615: { 616: case SIM_START: 617: status = process_start(this, message, out); 618: break; 619: case SIM_CHALLENGE: 620: status = process_challenge(this, message, out); 621: break; 622: case SIM_REAUTHENTICATION: 623: status = process_reauthentication(this, message, out); 624: break; 625: case SIM_NOTIFICATION: 626: status = process_notification(this, message, out); 627: break; 628: default: 629: DBG1(DBG_IKE, "unable to process EAP-SIM subtype %N", 630: simaka_subtype_names, message->get_subtype(message)); 631: if (!create_client_error(this, SIM_UNABLE_TO_PROCESS, out)) 632: { 633: status = FAILED; 634: } 635: else 636: { 637: status = NEED_MORE; 638: } 639: break; 640: } 641: message->destroy(message); 642: return status; 643: } 644: 645: METHOD(eap_method_t, initiate, status_t, 646: private_eap_sim_peer_t *this, eap_payload_t **out) 647: { 648: /* peer never initiates */ 649: return FAILED; 650: } 651: 652: METHOD(eap_method_t, get_type, eap_type_t, 653: private_eap_sim_peer_t *this, uint32_t *vendor) 654: { 655: *vendor = 0; 656: return EAP_SIM; 657: } 658: 659: METHOD(eap_method_t, get_msk, status_t, 660: private_eap_sim_peer_t *this, chunk_t *msk) 661: { 662: if (this->msk.ptr) 663: { 664: *msk = this->msk; 665: return SUCCESS; 666: } 667: return FAILED; 668: } 669: 670: METHOD(eap_method_t, get_identifier, uint8_t, 671: private_eap_sim_peer_t *this) 672: { 673: return this->identifier; 674: } 675: 676: METHOD(eap_method_t, set_identifier, void, 677: private_eap_sim_peer_t *this, uint8_t identifier) 678: { 679: this->identifier = identifier; 680: } 681: 682: METHOD(eap_method_t, is_mutual, bool, 683: private_eap_sim_peer_t *this) 684: { 685: return TRUE; 686: } 687: 688: METHOD(eap_method_t, destroy, void, 689: private_eap_sim_peer_t *this) 690: { 691: this->permanent->destroy(this->permanent); 692: DESTROY_IF(this->pseudonym); 693: DESTROY_IF(this->reauth); 694: this->crypto->destroy(this->crypto); 695: free(this->version_list.ptr); 696: free(this->nonce.ptr); 697: free(this->msk.ptr); 698: free(this); 699: } 700: 701: /* 702: * Described in header. 703: */ 704: eap_sim_peer_t *eap_sim_peer_create(identification_t *server, 705: identification_t *peer) 706: { 707: private_eap_sim_peer_t *this; 708: 709: INIT(this, 710: .public = { 711: .interface = { 712: .initiate = _initiate, 713: .process = _process, 714: .get_type = _get_type, 715: .is_mutual = _is_mutual, 716: .get_msk = _get_msk, 717: .get_identifier = _get_identifier, 718: .set_identifier = _set_identifier, 719: .destroy = _destroy, 720: }, 721: }, 722: .crypto = simaka_crypto_create(EAP_SIM), 723: .mgr = lib->get(lib, "sim-manager"), 724: ); 725: 726: if (!this->crypto) 727: { 728: free(this); 729: return NULL; 730: } 731: 732: this->permanent = peer->clone(peer); 733: this->tries = MAX_TRIES; 734: 735: return &this->public; 736: }