Annotation of embedaddon/strongswan/src/libsimaka/simaka_message.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (C) 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 "simaka_message.h"
! 17:
! 18: #include "simaka_manager.h"
! 19:
! 20: #include <utils/debug.h>
! 21: #include <collections/linked_list.h>
! 22:
! 23: typedef struct private_simaka_message_t private_simaka_message_t;
! 24: typedef struct hdr_t hdr_t;
! 25: typedef struct attr_hdr_t attr_hdr_t;
! 26: typedef struct attr_t attr_t;
! 27:
! 28: /**
! 29: * packed EAP-SIM/AKA header struct
! 30: */
! 31: struct hdr_t {
! 32: /** EAP code (REQUEST/RESPONSE) */
! 33: uint8_t code;
! 34: /** unique message identifier */
! 35: uint8_t identifier;
! 36: /** length of whole message */
! 37: uint16_t length;
! 38: /** EAP type => EAP_SIM/EAP_AKA */
! 39: uint8_t type;
! 40: /** SIM subtype */
! 41: uint8_t subtype;
! 42: /** reserved bytes */
! 43: uint16_t reserved;
! 44: } __attribute__((__packed__));
! 45:
! 46: /**
! 47: * packed EAP-SIM/AKA attribute header struct
! 48: */
! 49: struct attr_hdr_t {
! 50: /** attribute type */
! 51: uint8_t type;
! 52: /** attribute length */
! 53: uint8_t length;
! 54: } __attribute__((__packed__));
! 55:
! 56: /**
! 57: * SIM/AKA attribute, parsed
! 58: */
! 59: struct attr_t {
! 60: /** type of attribute */
! 61: simaka_attribute_t type;
! 62: /** length of data */
! 63: size_t len;
! 64: /** start of data, variable length */
! 65: char data[];
! 66: };
! 67:
! 68: ENUM_BEGIN(simaka_subtype_names, AKA_CHALLENGE, AKA_IDENTITY,
! 69: "AKA_CHALLENGE",
! 70: "AKA_AUTHENTICATION_REJECT",
! 71: "AKA_3",
! 72: "AKA_SYNCHRONIZATION_FAILURE",
! 73: "AKA_IDENTITY");
! 74: ENUM_NEXT(simaka_subtype_names, SIM_START, AKA_CLIENT_ERROR, AKA_IDENTITY,
! 75: "SIM_START",
! 76: "SIM_CHALLENGE",
! 77: "SIM/AKA_NOTIFICATION",
! 78: "SIM/AKA_REAUTHENTICATION",
! 79: "SIM/AKA_CLIENT_ERROR");
! 80: ENUM_END(simaka_subtype_names, AKA_CLIENT_ERROR);
! 81:
! 82:
! 83: ENUM_BEGIN(simaka_attribute_names, AT_RAND, AT_CLIENT_ERROR_CODE,
! 84: "AT_RAND",
! 85: "AT_AUTN",
! 86: "AT_RES",
! 87: "AT_AUTS",
! 88: "AT_5",
! 89: "AT_PADDING",
! 90: "AT_NONCE_MT",
! 91: "AT_8",
! 92: "AT_9",
! 93: "AT_PERMANENT_ID_REQ",
! 94: "AT_MAC",
! 95: "AT_NOTIFICATION",
! 96: "AT_ANY_ID_REQ",
! 97: "AT_IDENTITY",
! 98: "AT_VERSION_LIST",
! 99: "AT_SELECTED_VERSION",
! 100: "AT_FULLAUTH_ID_REQ",
! 101: "AT_18",
! 102: "AT_COUNTER",
! 103: "AT_COUNTER_TOO_SMALL",
! 104: "AT_NONCE_S",
! 105: "AT_CLIENT_ERROR_CODE");
! 106: ENUM_NEXT(simaka_attribute_names, AT_IV, AT_RESULT_IND, AT_CLIENT_ERROR_CODE,
! 107: "AT_IV",
! 108: "AT_ENCR_DATA",
! 109: "AT_131",
! 110: "AT_NEXT_PSEUDONYM",
! 111: "AT_NEXT_REAUTH_ID",
! 112: "AT_CHECKCODE",
! 113: "AT_RESULT_IND");
! 114: ENUM_END(simaka_attribute_names, AT_RESULT_IND);
! 115:
! 116:
! 117: ENUM_BEGIN(simaka_notification_names, SIM_GENERAL_FAILURE_AA, SIM_GENERAL_FAILURE_AA,
! 118: "General failure after authentication");
! 119: ENUM_NEXT(simaka_notification_names, SIM_TEMP_DENIED, SIM_TEMP_DENIED, SIM_GENERAL_FAILURE_AA,
! 120: "User has been temporarily denied access");
! 121: ENUM_NEXT(simaka_notification_names, SIM_NOT_SUBSCRIBED, SIM_NOT_SUBSCRIBED, SIM_TEMP_DENIED,
! 122: "User has not subscribed to the requested service");
! 123: ENUM_NEXT(simaka_notification_names, SIM_GENERAL_FAILURE, SIM_GENERAL_FAILURE, SIM_NOT_SUBSCRIBED,
! 124: "General failure");
! 125: ENUM_NEXT(simaka_notification_names, SIM_SUCCESS, SIM_SUCCESS, SIM_GENERAL_FAILURE,
! 126: "User has been successfully authenticated");
! 127: ENUM_END(simaka_notification_names, SIM_SUCCESS);
! 128:
! 129:
! 130: ENUM(simaka_client_error_names, SIM_UNABLE_TO_PROCESS, SIM_RANDS_NOT_FRESH,
! 131: "unable to process packet",
! 132: "unsupported version",
! 133: "insufficient number of challenges",
! 134: "RANDs are not fresh",
! 135: );
! 136:
! 137: /**
! 138: * Check if an EAP-SIM/AKA attribute is skippable
! 139: */
! 140: bool simaka_attribute_skippable(simaka_attribute_t attribute)
! 141: {
! 142: bool skippable = !((int)attribute >= 0 && attribute <= 127);
! 143:
! 144: DBG1(DBG_LIB, "%sskippable EAP-SIM/AKA attribute %N",
! 145: skippable ? "ignoring " : "found non-",
! 146: simaka_attribute_names, attribute);
! 147: return skippable;
! 148: }
! 149:
! 150: /**
! 151: * Private data of an simaka_message_t object.
! 152: */
! 153: struct private_simaka_message_t {
! 154:
! 155: /**
! 156: * Public simaka_message_t interface.
! 157: */
! 158: simaka_message_t public;
! 159:
! 160: /**
! 161: * EAP message, starting with EAP header
! 162: */
! 163: hdr_t *hdr;
! 164:
! 165: /**
! 166: * List of parsed attributes, attr_t
! 167: */
! 168: linked_list_t *attributes;
! 169:
! 170: /**
! 171: * Currently parsing AT_ENCR_DATA wrapped attributes?
! 172: */
! 173: bool encrypted;
! 174:
! 175: /**
! 176: * crypto helper
! 177: */
! 178: simaka_crypto_t *crypto;
! 179:
! 180: /**
! 181: * Phase a NOTIFICATION is sent within
! 182: */
! 183: bool p_bit;
! 184:
! 185: /**
! 186: * MAC value, pointing into message
! 187: */
! 188: chunk_t mac;
! 189:
! 190: /**
! 191: * ENCR_DATA value, pointing into message
! 192: */
! 193: chunk_t encr;
! 194:
! 195: /**
! 196: * IV value, pointing into message
! 197: */
! 198: chunk_t iv;
! 199: };
! 200:
! 201: METHOD(simaka_message_t, is_request, bool,
! 202: private_simaka_message_t *this)
! 203: {
! 204: return this->hdr->code == EAP_REQUEST;
! 205: }
! 206:
! 207: METHOD(simaka_message_t, get_identifier, uint8_t,
! 208: private_simaka_message_t *this)
! 209: {
! 210: return this->hdr->identifier;
! 211: }
! 212:
! 213: METHOD(simaka_message_t, get_subtype, simaka_subtype_t,
! 214: private_simaka_message_t *this)
! 215: {
! 216: return this->hdr->subtype;
! 217: }
! 218:
! 219: METHOD(simaka_message_t, get_type, eap_type_t,
! 220: private_simaka_message_t *this)
! 221: {
! 222: return this->hdr->type;
! 223: }
! 224:
! 225: CALLBACK(attr_enum_filter, bool,
! 226: void *null, enumerator_t *orig, va_list args)
! 227: {
! 228: attr_t *attr;
! 229: simaka_attribute_t *type;
! 230: chunk_t *data;
! 231:
! 232: VA_ARGS_VGET(args, type, data);
! 233:
! 234: if (orig->enumerate(orig, &attr))
! 235: {
! 236: *type = attr->type;
! 237: *data = chunk_create(attr->data, attr->len);
! 238: return TRUE;
! 239: }
! 240: return FALSE;
! 241: }
! 242:
! 243: METHOD(simaka_message_t, create_attribute_enumerator, enumerator_t*,
! 244: private_simaka_message_t *this)
! 245: {
! 246: return enumerator_create_filter(
! 247: this->attributes->create_enumerator(this->attributes),
! 248: attr_enum_filter, NULL, NULL);
! 249: }
! 250:
! 251: METHOD(simaka_message_t, add_attribute, void,
! 252: private_simaka_message_t *this, simaka_attribute_t type, chunk_t data)
! 253: {
! 254: attr_t *attr;
! 255:
! 256: attr = malloc(sizeof(attr_t) + data.len);
! 257: attr->len = data.len;
! 258: attr->type = type;
! 259: memcpy(attr->data, data.ptr, data.len);
! 260:
! 261: this->attributes->insert_last(this->attributes, attr);
! 262: }
! 263:
! 264: /**
! 265: * Error handling for unencrypted attributes
! 266: */
! 267: static bool not_encrypted(simaka_attribute_t type)
! 268: {
! 269: DBG1(DBG_LIB, "received unencrypted %N", simaka_attribute_names, type);
! 270: return FALSE;
! 271: }
! 272:
! 273: /**
! 274: * Error handling for invalid length
! 275: */
! 276: static bool invalid_length(simaka_attribute_t type)
! 277: {
! 278: DBG1(DBG_LIB, "invalid length of %N", simaka_attribute_names, type);
! 279: return FALSE;
! 280: }
! 281:
! 282: /**
! 283: * Call SIM/AKA message hooks
! 284: */
! 285: static void call_hook(private_simaka_message_t *this,
! 286: bool inbound, bool decrypted)
! 287: {
! 288: simaka_manager_t *mgr;
! 289:
! 290: switch (this->hdr->type)
! 291: {
! 292: case EAP_SIM:
! 293: mgr = lib->get(lib, "sim-manager");
! 294: break;
! 295: case EAP_AKA:
! 296: mgr = lib->get(lib, "aka-manager");
! 297: break;
! 298: default:
! 299: return;
! 300: }
! 301: mgr->message_hook(mgr, &this->public, inbound, decrypted);
! 302: }
! 303:
! 304: /**
! 305: * Parse attributes from a chunk of data
! 306: */
! 307: static bool parse_attributes(private_simaka_message_t *this, chunk_t in)
! 308: {
! 309: while (in.len)
! 310: {
! 311: attr_hdr_t *hdr;
! 312: chunk_t data;
! 313:
! 314: if (in.len < sizeof(attr_hdr_t))
! 315: {
! 316: DBG1(DBG_LIB, "found short %N attribute header",
! 317: eap_type_names, this->hdr->type);
! 318: return FALSE;
! 319: }
! 320: hdr = (attr_hdr_t*)in.ptr;
! 321:
! 322: switch (hdr->type)
! 323: {
! 324: /* attributes without data */
! 325: case AT_COUNTER_TOO_SMALL:
! 326: if (!this->encrypted)
! 327: {
! 328: return not_encrypted(hdr->type);
! 329: }
! 330: /* FALL */
! 331: case AT_ANY_ID_REQ:
! 332: case AT_PERMANENT_ID_REQ:
! 333: case AT_FULLAUTH_ID_REQ:
! 334: {
! 335: if (hdr->length != 1 || in.len < 4)
! 336: {
! 337: return invalid_length(hdr->type);
! 338: }
! 339: data = chunk_empty;
! 340: in = chunk_skip(in, 4);
! 341: break;
! 342: }
! 343: /* attributes with two bytes data */
! 344: case AT_COUNTER:
! 345: if (!this->encrypted)
! 346: {
! 347: return not_encrypted(hdr->type);
! 348: }
! 349: /* FALL */
! 350: case AT_CLIENT_ERROR_CODE:
! 351: case AT_SELECTED_VERSION:
! 352: case AT_NOTIFICATION:
! 353: {
! 354: if (hdr->length != 1 || in.len < 4)
! 355: {
! 356: return invalid_length(hdr->type);
! 357: }
! 358: data = chunk_create(in.ptr + 2, 2);
! 359: in = chunk_skip(in, 4);
! 360: break;
! 361: }
! 362: /* attributes with an additional actual-length in bits or bytes */
! 363: case AT_NEXT_PSEUDONYM:
! 364: case AT_NEXT_REAUTH_ID:
! 365: if (!this->encrypted)
! 366: {
! 367: return not_encrypted(hdr->type);
! 368: }
! 369: /* FALL */
! 370: case AT_RES:
! 371: case AT_IDENTITY:
! 372: case AT_VERSION_LIST:
! 373: {
! 374: uint16_t len;
! 375:
! 376: if (hdr->length < 1 || in.len < 4)
! 377: {
! 378: return invalid_length(hdr->type);
! 379: }
! 380: memcpy(&len, in.ptr + 2, 2);
! 381: len = ntohs(len);
! 382: if (hdr->type == AT_RES)
! 383: { /* AT_RES uses length encoding in bits */
! 384: len /= 8;
! 385: }
! 386: if (len > hdr->length * 4 || len > in.len)
! 387: {
! 388: return invalid_length(hdr->type);
! 389: }
! 390: data = chunk_create(in.ptr + 4, len);
! 391: in = chunk_skip(in, hdr->length * 4);
! 392: break;
! 393: }
! 394: /* attributes with two reserved bytes, 16 bytes length */
! 395: case AT_NONCE_S:
! 396: if (!this->encrypted)
! 397: {
! 398: return not_encrypted(hdr->type);
! 399: }
! 400: /* FALL */
! 401: case AT_AUTN:
! 402: case AT_NONCE_MT:
! 403: case AT_IV:
! 404: case AT_MAC:
! 405: {
! 406: if (hdr->length != 5 || in.len < 20)
! 407: {
! 408: return invalid_length(hdr->type);
! 409: }
! 410: data = chunk_create(in.ptr + 4, 16);
! 411: in = chunk_skip(in, 20);
! 412: break;
! 413: }
! 414: /* attributes with two reserved bytes, variable length */
! 415: case AT_ENCR_DATA:
! 416: case AT_RAND:
! 417: {
! 418: if (hdr->length * 4 > in.len || in.len < 4)
! 419: {
! 420: return invalid_length(hdr->type);
! 421: }
! 422: data = chunk_create(in.ptr + 4, hdr->length * 4 - 4);
! 423: in = chunk_skip(in, hdr->length * 4);
! 424: break;
! 425: }
! 426: /* attributes with no reserved bytes, 14 bytes length */
! 427: case AT_AUTS:
! 428: {
! 429: if (hdr->length != 4 || in.len < 16)
! 430: {
! 431: return invalid_length(hdr->type);
! 432: }
! 433: data = chunk_create(in.ptr + 2, 14);
! 434: in = chunk_skip(in, 16);
! 435: break;
! 436: }
! 437: /* other attributes (with 4n + 2 length) */
! 438: case AT_PADDING:
! 439: default:
! 440: {
! 441: if (hdr->length * 4 > in.len || in.len < 4)
! 442: {
! 443: return invalid_length(hdr->type);
! 444: }
! 445: data = chunk_create(in.ptr + 2, hdr->length * 4 - 2);
! 446: in = chunk_skip(in, hdr->length * 4);
! 447: break;
! 448: }
! 449: }
! 450:
! 451: /* handle special attributes */
! 452: switch (hdr->type)
! 453: {
! 454: case AT_MAC:
! 455: this->mac = data;
! 456: break;
! 457: case AT_IV:
! 458: this->iv = data;
! 459: break;
! 460: case AT_ENCR_DATA:
! 461: this->encr = data;
! 462: break;
! 463: case AT_PADDING:
! 464: break;
! 465: case AT_NOTIFICATION:
! 466: if (this->p_bit)
! 467: { /* remember P bit for MAC verification */
! 468: this->p_bit = !!(data.ptr[0] & 0x40);
! 469: }
! 470: else if (!this->encrypted)
! 471: {
! 472: DBG1(DBG_LIB, "found P-bit 0 notify in unencrypted message");
! 473: return FALSE;
! 474: }
! 475: /* FALL */
! 476: default:
! 477: add_attribute(this, hdr->type, data);
! 478: break;
! 479: }
! 480: }
! 481:
! 482: call_hook(this, TRUE, this->encrypted);
! 483:
! 484: return TRUE;
! 485: }
! 486:
! 487: /**
! 488: * Decrypt a message and parse the decrypted attributes
! 489: */
! 490: static bool decrypt(private_simaka_message_t *this)
! 491: {
! 492: bool success;
! 493: crypter_t *crypter;
! 494: chunk_t plain;
! 495:
! 496: crypter = this->crypto->get_crypter(this->crypto);
! 497: if (!crypter || !this->iv.len || !this->encr.len || this->encrypted)
! 498: {
! 499: return TRUE;
! 500: }
! 501: if (this->encr.len % crypter->get_block_size(crypter))
! 502: {
! 503: DBG1(DBG_LIB, "%N ENCR_DATA not a multiple of block size",
! 504: eap_type_names, this->hdr->type);
! 505: return FALSE;
! 506: }
! 507: if (!crypter->decrypt(crypter, this->encr, this->iv, &plain))
! 508: {
! 509: return FALSE;
! 510: }
! 511:
! 512: this->encrypted = TRUE;
! 513: success = parse_attributes(this, plain);
! 514: this->encrypted = FALSE;
! 515: free(plain.ptr);
! 516: return success;
! 517: }
! 518:
! 519: METHOD(simaka_message_t, parse, bool,
! 520: private_simaka_message_t *this)
! 521: {
! 522: chunk_t in;
! 523:
! 524: if (this->attributes->get_count(this->attributes))
! 525: { /* Already parsed. Try to decrypt and parse AT_ENCR_DATA. */
! 526: return decrypt(this);
! 527: }
! 528:
! 529: in = chunk_create((char*)this->hdr, ntohs(this->hdr->length));
! 530: if (!parse_attributes(this, chunk_skip(in, sizeof(hdr_t))))
! 531: {
! 532: return FALSE;
! 533: }
! 534: /* try to decrypt if we already have keys */
! 535: return decrypt(this);
! 536: }
! 537:
! 538: METHOD(simaka_message_t, verify, bool,
! 539: private_simaka_message_t *this, chunk_t sigdata)
! 540: {
! 541: chunk_t data, backup;
! 542: signer_t *signer;
! 543:
! 544: signer = this->crypto->get_signer(this->crypto);
! 545:
! 546: switch (this->hdr->subtype)
! 547: {
! 548: case SIM_START:
! 549: case SIM_CLIENT_ERROR:
! 550: /* AKA_CLIENT_ERROR: */
! 551: case AKA_AUTHENTICATION_REJECT:
! 552: case AKA_SYNCHRONIZATION_FAILURE:
! 553: case AKA_IDENTITY:
! 554: /* skip MAC if available */
! 555: return TRUE;
! 556: case SIM_CHALLENGE:
! 557: case AKA_CHALLENGE:
! 558: case SIM_REAUTHENTICATION:
! 559: /* AKA_REAUTHENTICATION: */
! 560: {
! 561: if (!this->mac.ptr || !signer)
! 562: { /* require MAC, but not found */
! 563: DBG1(DBG_LIB, "%N message requires a MAC, but none found",
! 564: simaka_subtype_names, this->hdr->subtype);
! 565: return FALSE;
! 566: }
! 567: break;
! 568: }
! 569: case SIM_NOTIFICATION:
! 570: /* AKA_NOTIFICATION: */
! 571: {
! 572: if (this->p_bit)
! 573: { /* MAC not verified if in Phase 1 */
! 574: return TRUE;
! 575: }
! 576: if (!this->mac.ptr || !signer)
! 577: {
! 578: DBG1(DBG_LIB, "%N message has a phase 0 notify, but "
! 579: "no MAC found", simaka_subtype_names, this->hdr->subtype);
! 580: return FALSE;
! 581: }
! 582: break;
! 583: }
! 584: default:
! 585: /* unknown message? */
! 586: DBG1(DBG_LIB, "signature rule for %N messages missing",
! 587: simaka_subtype_names, this->hdr->subtype);
! 588: return FALSE;
! 589: }
! 590:
! 591: /* zero MAC for verification */
! 592: backup = chunk_clonea(this->mac);
! 593: memset(this->mac.ptr, 0, this->mac.len);
! 594:
! 595: data = chunk_create((char*)this->hdr, ntohs(this->hdr->length));
! 596: if (sigdata.len)
! 597: {
! 598: data = chunk_cata("cc", data, sigdata);
! 599: }
! 600: if (!signer->verify_signature(signer, data, backup))
! 601: {
! 602: DBG1(DBG_LIB, "%N MAC verification failed",
! 603: eap_type_names, this->hdr->type);
! 604: return FALSE;
! 605: }
! 606: return TRUE;
! 607: }
! 608:
! 609: METHOD(simaka_message_t, generate, bool,
! 610: private_simaka_message_t *this, chunk_t sigdata, chunk_t *gen)
! 611: {
! 612: /* buffers large enough for messages we generate */
! 613: char out_buf[1024], encr_buf[512];
! 614: enumerator_t *enumerator;
! 615: chunk_t out, encr, data, *target, mac = chunk_empty;
! 616: simaka_attribute_t type;
! 617: attr_hdr_t *hdr;
! 618: uint16_t len;
! 619: signer_t *signer;
! 620:
! 621: call_hook(this, FALSE, TRUE);
! 622:
! 623: out = chunk_create(out_buf, sizeof(out_buf));
! 624: encr = chunk_create(encr_buf, sizeof(encr_buf));
! 625:
! 626: /* copy header */
! 627: memcpy(out.ptr, this->hdr, sizeof(hdr_t));
! 628: out = chunk_skip(out, sizeof(hdr_t));
! 629:
! 630: /* encode attributes */
! 631: enumerator = create_attribute_enumerator(this);
! 632: while (enumerator->enumerate(enumerator, &type, &data))
! 633: {
! 634: /* encrypt this attribute? */
! 635: switch (type)
! 636: {
! 637: case AT_NONCE_S:
! 638: case AT_NEXT_PSEUDONYM:
! 639: case AT_NEXT_REAUTH_ID:
! 640: case AT_COUNTER:
! 641: case AT_COUNTER_TOO_SMALL:
! 642: target = &encr;
! 643: break;
! 644: case AT_NOTIFICATION:
! 645: /* P bit not set, encrypt */
! 646: if (!(data.ptr[0] & 0x40))
! 647: {
! 648: target = &encr;
! 649: break;
! 650: }
! 651: /* FALL */
! 652: default:
! 653: target = &out;
! 654: break;
! 655: }
! 656:
! 657: hdr = (attr_hdr_t*)target->ptr;
! 658: hdr->type = type;
! 659:
! 660: /* encode type specific */
! 661: switch (type)
! 662: {
! 663: /* attributes without data */
! 664: case AT_COUNTER_TOO_SMALL:
! 665: case AT_ANY_ID_REQ:
! 666: case AT_PERMANENT_ID_REQ:
! 667: case AT_FULLAUTH_ID_REQ:
! 668: {
! 669: hdr->length = 1;
! 670: memset(target->ptr + 2, 0, 2);
! 671: *target = chunk_skip(*target, 4);
! 672: break;
! 673: }
! 674: /* attributes with two bytes data */
! 675: case AT_COUNTER:
! 676: case AT_CLIENT_ERROR_CODE:
! 677: case AT_SELECTED_VERSION:
! 678: case AT_NOTIFICATION:
! 679: {
! 680: hdr->length = 1;
! 681: memcpy(target->ptr + 2, data.ptr, 2);
! 682: *target = chunk_skip(*target, 4);
! 683: break;
! 684: }
! 685: /* attributes with an additional actual-length in bits or bytes */
! 686: case AT_NEXT_PSEUDONYM:
! 687: case AT_NEXT_REAUTH_ID:
! 688: case AT_IDENTITY:
! 689: case AT_VERSION_LIST:
! 690: case AT_RES:
! 691: {
! 692: uint16_t len, padding;
! 693:
! 694: len = htons(data.len);
! 695: if (type == AT_RES)
! 696: { /* AT_RES uses length encoding in bits */
! 697: len *= 8;
! 698: }
! 699: memcpy(target->ptr + 2, &len, sizeof(len));
! 700: memcpy(target->ptr + 4, data.ptr, data.len);
! 701: hdr->length = data.len / 4 + 1;
! 702: padding = (4 - (data.len % 4)) % 4;
! 703: if (padding)
! 704: {
! 705: hdr->length++;
! 706: memset(target->ptr + 4 + data.len, 0, padding);
! 707: }
! 708: *target = chunk_skip(*target, hdr->length * 4);
! 709: break;
! 710: }
! 711: /* attributes with two reserved bytes, 16 bytes length */
! 712: case AT_NONCE_S:
! 713: case AT_NONCE_MT:
! 714: case AT_AUTN:
! 715: {
! 716: hdr->length = 5;
! 717: memset(target->ptr + 2, 0, 2);
! 718: memcpy(target->ptr + 4, data.ptr, data.len);
! 719: *target = chunk_skip(*target, 20);
! 720: break;
! 721: }
! 722: /* attributes with two reserved bytes, variable length */
! 723: case AT_RAND:
! 724: {
! 725: hdr->length = 1 + data.len / 4;
! 726: memset(target->ptr + 2, 0, 2);
! 727: memcpy(target->ptr + 4, data.ptr, data.len);
! 728: *target = chunk_skip(*target, data.len + 4);
! 729: break;
! 730: }
! 731: /* attributes with no reserved bytes, 14 bytes length */
! 732: case AT_AUTS:
! 733: {
! 734: hdr->length = 4;
! 735: memcpy(target->ptr + 2, data.ptr, data.len);
! 736: *target = chunk_skip(*target, 16);
! 737: break;
! 738: }
! 739: default:
! 740: {
! 741: DBG1(DBG_LIB, "no rule to encode %N, skipped",
! 742: simaka_attribute_names, type);
! 743: break;
! 744: }
! 745: }
! 746: }
! 747: enumerator->destroy(enumerator);
! 748:
! 749: /* encrypt attributes, if any */
! 750: if (encr.len < sizeof(encr_buf))
! 751: {
! 752: chunk_t iv;
! 753: size_t bs, padding;
! 754: crypter_t *crypter;
! 755: rng_t *rng;
! 756:
! 757: crypter = this->crypto->get_crypter(this->crypto);
! 758: bs = crypter->get_block_size(crypter);
! 759: iv.len = crypter->get_iv_size(crypter);
! 760:
! 761: /* add AT_PADDING attribute */
! 762: padding = bs - ((sizeof(encr_buf) - encr.len) % bs);
! 763: if (padding)
! 764: {
! 765: hdr = (attr_hdr_t*)encr.ptr;
! 766: hdr->type = AT_PADDING;
! 767: hdr->length = padding / 4;
! 768: memset(encr.ptr + 2, 0, padding - 2);
! 769: encr = chunk_skip(encr, padding);
! 770: }
! 771: encr = chunk_create(encr_buf, sizeof(encr_buf) - encr.len);
! 772:
! 773: /* add IV attribute */
! 774: hdr = (attr_hdr_t*)out.ptr;
! 775: hdr->type = AT_IV;
! 776: hdr->length = iv.len / 4 + 1;
! 777: memset(out.ptr + 2, 0, 2);
! 778: out = chunk_skip(out, 4);
! 779:
! 780: rng = this->crypto->get_rng(this->crypto);
! 781: if (!rng->get_bytes(rng, iv.len, out.ptr))
! 782: {
! 783: return FALSE;
! 784: }
! 785:
! 786: iv = chunk_clonea(chunk_create(out.ptr, iv.len));
! 787: out = chunk_skip(out, iv.len);
! 788:
! 789: /* inline encryption */
! 790: if (!crypter->encrypt(crypter, encr, iv, NULL))
! 791: {
! 792: return FALSE;
! 793: }
! 794:
! 795: /* add ENCR_DATA attribute */
! 796: hdr = (attr_hdr_t*)out.ptr;
! 797: hdr->type = AT_ENCR_DATA;
! 798: hdr->length = encr.len / 4 + 1;
! 799: memset(out.ptr + 2, 0, 2);
! 800: memcpy(out.ptr + 4, encr.ptr, encr.len);
! 801: out = chunk_skip(out, encr.len + 4);
! 802: }
! 803:
! 804: /* include MAC ? */
! 805: signer = this->crypto->get_signer(this->crypto);
! 806: switch (this->hdr->subtype)
! 807: {
! 808: case SIM_CHALLENGE:
! 809: case AKA_CHALLENGE:
! 810: case SIM_REAUTHENTICATION:
! 811: /* AKA_REAUTHENTICATION: */
! 812: /* TODO: Notifications without P bit */
! 813: {
! 814: size_t bs;
! 815:
! 816: bs = signer->get_block_size(signer);
! 817: hdr = (attr_hdr_t*)out.ptr;
! 818: hdr->type = AT_MAC;
! 819: hdr->length = bs / 4 + 1;
! 820: memset(out.ptr + 2, 0, 2 + bs);
! 821: mac = chunk_create(out.ptr + 4, bs);
! 822: out = chunk_skip(out, bs + 4);
! 823: break;
! 824: }
! 825: default:
! 826: break;
! 827: }
! 828:
! 829: /* calculate message length */
! 830: out = chunk_create(out_buf, sizeof(out_buf) - out.len);
! 831: len = htons(out.len);
! 832: memcpy(out.ptr + 2, &len, sizeof(len));
! 833:
! 834: /* generate MAC */
! 835: if (mac.len)
! 836: {
! 837: data = chunk_cata("cc", out, sigdata);
! 838: if (!signer->get_signature(signer, data, mac.ptr))
! 839: {
! 840: return FALSE;
! 841: }
! 842: }
! 843:
! 844: call_hook(this, FALSE, FALSE);
! 845:
! 846: *gen = chunk_clone(out);
! 847: return TRUE;
! 848: }
! 849:
! 850: METHOD(simaka_message_t, destroy, void,
! 851: private_simaka_message_t *this)
! 852: {
! 853: this->attributes->destroy_function(this->attributes, free);
! 854: free(this->hdr);
! 855: free(this);
! 856: }
! 857:
! 858: /**
! 859: * Generic constructor.
! 860: */
! 861: static simaka_message_t *simaka_message_create_data(chunk_t data,
! 862: simaka_crypto_t *crypto)
! 863: {
! 864: private_simaka_message_t *this;
! 865: hdr_t *hdr = (hdr_t*)data.ptr;
! 866:
! 867: if (data.len < sizeof(hdr_t) || hdr->length != htons(data.len))
! 868: {
! 869: DBG1(DBG_LIB, "EAP-SIM/AKA header has invalid length");
! 870: return NULL;
! 871: }
! 872: if (hdr->code != EAP_REQUEST && hdr->code != EAP_RESPONSE)
! 873: {
! 874: DBG1(DBG_LIB, "invalid EAP code in EAP-SIM/AKA message",
! 875: eap_type_names, hdr->type);
! 876: return NULL;
! 877: }
! 878: if (hdr->type != EAP_SIM && hdr->type != EAP_AKA)
! 879: {
! 880: DBG1(DBG_LIB, "invalid EAP type in EAP-SIM/AKA message",
! 881: eap_type_names, hdr->type);
! 882: return NULL;
! 883: }
! 884:
! 885: INIT(this,
! 886: .public = {
! 887: .is_request = _is_request,
! 888: .get_identifier = _get_identifier,
! 889: .get_type = _get_type,
! 890: .get_subtype = _get_subtype,
! 891: .create_attribute_enumerator = _create_attribute_enumerator,
! 892: .add_attribute = _add_attribute,
! 893: .parse = _parse,
! 894: .verify = _verify,
! 895: .generate = _generate,
! 896: .destroy = _destroy,
! 897: },
! 898: .attributes = linked_list_create(),
! 899: .crypto = crypto,
! 900: .p_bit = TRUE,
! 901: .hdr = malloc(data.len),
! 902: );
! 903: memcpy(this->hdr, hdr, data.len);
! 904:
! 905: return &this->public;
! 906: }
! 907:
! 908: /**
! 909: * See header.
! 910: */
! 911: simaka_message_t *simaka_message_create_from_payload(chunk_t data,
! 912: simaka_crypto_t *crypto)
! 913: {
! 914: return simaka_message_create_data(data, crypto);
! 915: }
! 916:
! 917: /**
! 918: * See header.
! 919: */
! 920: simaka_message_t *simaka_message_create(bool request, uint8_t identifier,
! 921: eap_type_t type, simaka_subtype_t subtype,
! 922: simaka_crypto_t *crypto)
! 923: {
! 924: hdr_t hdr = {
! 925: .code = request ? EAP_REQUEST : EAP_RESPONSE,
! 926: .identifier = identifier,
! 927: .length = htons(sizeof(hdr_t)),
! 928: .type = type,
! 929: .subtype = subtype,
! 930: };
! 931: return simaka_message_create_data(chunk_create((char*)&hdr, sizeof(hdr)),
! 932: crypto);
! 933: }
! 934:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>