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>