Annotation of embedaddon/strongswan/src/libtls/tls_eap.c, revision 1.1

1.1     ! misho       1: 
        !             2: /*
        !             3:  * Copyright (C) 2010 Martin Willi
        !             4:  * Copyright (C) 2010 revosec AG
        !             5:  *
        !             6:  * This program is free software; you can redistribute it and/or modify it
        !             7:  * under the terms of the GNU General Public License as published by the
        !             8:  * Free Software Foundation; either version 2 of the License, or (at your
        !             9:  * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
        !            10:  *
        !            11:  * This program is distributed in the hope that it will be useful, but
        !            12:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
        !            13:  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
        !            14:  * for more details.
        !            15:  */
        !            16: 
        !            17: #include "tls_eap.h"
        !            18: 
        !            19: #include "tls.h"
        !            20: 
        !            21: #include <utils/debug.h>
        !            22: #include <library.h>
        !            23: 
        !            24: /**
        !            25:  * Size limit for a TLS message allowing for worst-case protection overhead
        !            26:  * according to section 6.2.3. "Payload Protection" of RFC 5246 TLS 1.2
        !            27:  */
        !            28: #define TLS_MAX_MESSAGE_LEN            4 * (TLS_MAX_FRAGMENT_LEN + 2048)
        !            29: 
        !            30: typedef struct private_tls_eap_t private_tls_eap_t;
        !            31: 
        !            32: /**
        !            33:  * Private data of an tls_eap_t object.
        !            34:  */
        !            35: struct private_tls_eap_t {
        !            36: 
        !            37:        /**
        !            38:         * Public tls_eap_t interface.
        !            39:         */
        !            40:        tls_eap_t public;
        !            41: 
        !            42:        /**
        !            43:         * Type of EAP method, EAP-TLS, EAP-TTLS, or EAP-TNC
        !            44:         */
        !            45:        eap_type_t type;
        !            46: 
        !            47:        /**
        !            48:         * Current value of EAP identifier
        !            49:         */
        !            50:        uint8_t identifier;
        !            51: 
        !            52:        /**
        !            53:         * TLS stack
        !            54:         */
        !            55:        tls_t *tls;
        !            56: 
        !            57:        /**
        !            58:         * Role
        !            59:         */
        !            60:        bool is_server;
        !            61: 
        !            62:        /**
        !            63:         * Supported version of the EAP tunnel protocol
        !            64:         */
        !            65:        uint8_t supported_version;
        !            66: 
        !            67:        /**
        !            68:         * If FALSE include the total length of an EAP message
        !            69:         * in the first fragment of fragmented messages only.
        !            70:         * If TRUE also include the length in non-fragmented messages.
        !            71:         */
        !            72:        bool include_length;
        !            73: 
        !            74:        /**
        !            75:         * First fragment of a multi-fragment record?
        !            76:         */
        !            77:        bool first_fragment;
        !            78: 
        !            79:        /**
        !            80:         * Maximum size of an outgoing EAP-TLS fragment
        !            81:         */
        !            82:        size_t frag_size;
        !            83: 
        !            84:        /**
        !            85:         * Number of EAP messages/fragments processed so far
        !            86:         */
        !            87:        int processed;
        !            88: 
        !            89:        /**
        !            90:         * Maximum number of processed EAP messages/fragments
        !            91:         */
        !            92:        int max_msg_count;
        !            93: };
        !            94: 
        !            95: /**
        !            96:  * Flags of an EAP-TLS/TTLS/TNC message
        !            97:  */
        !            98: typedef enum {
        !            99:        EAP_TLS_LENGTH = (1<<7),                /* shared with EAP-TTLS/TNC/PEAP */
        !           100:        EAP_TLS_MORE_FRAGS = (1<<6),    /* shared with EAP-TTLS/TNC/PEAP */
        !           101:        EAP_TLS_START = (1<<5),                 /* shared with EAP-TTLS/TNC/PEAP */
        !           102:        EAP_TTLS_VERSION = (0x07),              /* shared with EAP-TNC/PEAP/PT-EAP */
        !           103:        EAP_PT_START = (1<<7)                   /* PT-EAP only */
        !           104: } eap_tls_flags_t;
        !           105: 
        !           106: #define EAP_TTLS_SUPPORTED_VERSION             0
        !           107: #define EAP_TNC_SUPPORTED_VERSION              1
        !           108: #define EAP_PEAP_SUPPORTED_VERSION             0
        !           109: #define EAP_PT_EAP_SUPPORTED_VERSION   1
        !           110: 
        !           111: /**
        !           112:  * EAP-TLS/TTLS packet format
        !           113:  */
        !           114: typedef struct __attribute__((packed)) {
        !           115:        uint8_t code;
        !           116:        uint8_t identifier;
        !           117:        uint16_t length;
        !           118:        uint8_t type;
        !           119:        uint8_t flags;
        !           120: } eap_tls_packet_t;
        !           121: 
        !           122: METHOD(tls_eap_t, initiate, status_t,
        !           123:        private_tls_eap_t *this, chunk_t *out)
        !           124: {
        !           125:        if (this->is_server)
        !           126:        {
        !           127:                eap_tls_packet_t pkt = {
        !           128:                        .type = this->type,
        !           129:                        .code = EAP_REQUEST,
        !           130:                        .flags = this->supported_version
        !           131:                };
        !           132:                switch (this->type)
        !           133:                {
        !           134:                        case EAP_TLS:
        !           135:                        case EAP_TTLS:
        !           136:                        case EAP_TNC:
        !           137:                        case EAP_PEAP:
        !           138:                                pkt.flags |= EAP_TLS_START;
        !           139:                                break;
        !           140:                        case EAP_PT_EAP:
        !           141:                                pkt.flags |= EAP_PT_START;
        !           142:                                break;
        !           143:                        default:
        !           144:                                break;
        !           145:                }
        !           146:                htoun16(&pkt.length, sizeof(eap_tls_packet_t));
        !           147:                pkt.identifier = this->identifier;
        !           148: 
        !           149:                *out = chunk_clone(chunk_from_thing(pkt));
        !           150:                DBG2(DBG_TLS, "sending %N start packet (%u bytes)",
        !           151:                         eap_type_names, this->type, sizeof(eap_tls_packet_t));
        !           152:                DBG3(DBG_TLS, "%B", out);
        !           153:                return NEED_MORE;
        !           154:        }
        !           155:        return FAILED;
        !           156: }
        !           157: 
        !           158: /**
        !           159:  * Process a received packet
        !           160:  */
        !           161: static status_t process_pkt(private_tls_eap_t *this, eap_tls_packet_t *pkt)
        !           162: {
        !           163:        uint8_t version;
        !           164:        uint16_t pkt_len;
        !           165:        uint32_t msg_len;
        !           166:        size_t msg_len_offset = 0;
        !           167: 
        !           168:        /* EAP-TLS doesn't have a version field */
        !           169:        if (this->type != EAP_TLS)
        !           170:        {
        !           171:                version = pkt->flags & EAP_TTLS_VERSION;
        !           172:                if (version != this->supported_version)
        !           173:                {
        !           174:                        DBG1(DBG_TLS, "received %N packet with unsupported version v%u",
        !           175:                        eap_type_names, this->type, version);
        !           176:                        return FAILED;
        !           177:                }
        !           178:        }
        !           179:        pkt_len = untoh16(&pkt->length);
        !           180: 
        !           181:        if (this->type != EAP_PT_EAP && (pkt->flags & EAP_TLS_LENGTH))
        !           182:        {
        !           183:                if (pkt_len < sizeof(eap_tls_packet_t) + sizeof(msg_len))
        !           184:                {
        !           185:                        DBG1(DBG_TLS, "%N packet too short", eap_type_names, this->type);
        !           186:                        return FAILED;
        !           187:                }
        !           188:                msg_len = untoh32(pkt + 1);
        !           189:                if (msg_len < pkt_len - sizeof(eap_tls_packet_t) - sizeof(msg_len) ||
        !           190:                        msg_len > TLS_MAX_MESSAGE_LEN)
        !           191:                {
        !           192:                        DBG1(DBG_TLS, "invalid %N packet length (%u bytes)", eap_type_names,
        !           193:                                 this->type, msg_len);
        !           194:                        return FAILED;
        !           195:                }
        !           196:                msg_len_offset = sizeof(msg_len);
        !           197:        }
        !           198: 
        !           199:        return this->tls->process(this->tls, (char*)(pkt + 1) + msg_len_offset,
        !           200:                                           pkt_len - sizeof(eap_tls_packet_t) - msg_len_offset);
        !           201: }
        !           202: 
        !           203: /**
        !           204:  * Build a packet to send
        !           205:  */
        !           206: static status_t build_pkt(private_tls_eap_t *this, chunk_t *out)
        !           207: {
        !           208:        char buf[this->frag_size];
        !           209:        eap_tls_packet_t *pkt;
        !           210:        size_t len, reclen, msg_len_offset;
        !           211:        status_t status;
        !           212:        char *kind;
        !           213: 
        !           214:        if (this->is_server)
        !           215:        {
        !           216:                this->identifier++;
        !           217:        }
        !           218:        pkt = (eap_tls_packet_t*)buf;
        !           219:        pkt->code = this->is_server ? EAP_REQUEST : EAP_RESPONSE;
        !           220:        pkt->identifier = this->identifier;
        !           221:        pkt->type = this->type;
        !           222:        pkt->flags = this->supported_version;
        !           223: 
        !           224:        if (this->first_fragment)
        !           225:        {
        !           226:                len = sizeof(buf) - sizeof(eap_tls_packet_t) - sizeof(uint32_t);
        !           227:                msg_len_offset = sizeof(uint32_t);
        !           228:        }
        !           229:        else
        !           230:        {
        !           231:                len = sizeof(buf) - sizeof(eap_tls_packet_t);
        !           232:                msg_len_offset = 0;
        !           233:        }
        !           234:        status = this->tls->build(this->tls, buf + sizeof(eap_tls_packet_t) +
        !           235:                                                                                 msg_len_offset, &len, &reclen);
        !           236: 
        !           237:        switch (status)
        !           238:        {
        !           239:                case NEED_MORE:
        !           240:                        pkt->flags |= EAP_TLS_MORE_FRAGS;
        !           241:                        kind = "further fragment";
        !           242:                        if (this->first_fragment)
        !           243:                        {
        !           244:                                pkt->flags |= EAP_TLS_LENGTH;
        !           245:                                this->first_fragment = FALSE;
        !           246:                                kind = "first fragment";
        !           247:                        }
        !           248:                        break;
        !           249:                case ALREADY_DONE:
        !           250:                        if (this->first_fragment)
        !           251:                        {
        !           252:                                if (this->include_length)
        !           253:                                {
        !           254:                                        pkt->flags |= EAP_TLS_LENGTH;
        !           255:                                }
        !           256:                                kind = "packet";
        !           257:                        }
        !           258:                        else if (this->type != EAP_TNC && this->type != EAP_PT_EAP)
        !           259:                        {
        !           260:                                this->first_fragment = TRUE;
        !           261:                                kind = "final fragment";
        !           262:                        }
        !           263:                        else
        !           264:                        {
        !           265:                                kind = "packet";
        !           266:                        }
        !           267:                        break;
        !           268:                default:
        !           269:                        return status;
        !           270:        }
        !           271:        if (reclen)
        !           272:        {
        !           273:                if (pkt->flags & EAP_TLS_LENGTH)
        !           274:                {
        !           275:                        htoun32(pkt + 1, reclen);
        !           276:                        len += sizeof(uint32_t);
        !           277:                        pkt->flags |= EAP_TLS_LENGTH;
        !           278:                }
        !           279:                else
        !           280:                {
        !           281:                        /* get rid of the reserved length field */
        !           282:                        memmove(buf + sizeof(eap_tls_packet_t),
        !           283:                                        buf + sizeof(eap_tls_packet_t) + sizeof(uint32_t), len);
        !           284:                }
        !           285:        }
        !           286:        len += sizeof(eap_tls_packet_t);
        !           287:        htoun16(&pkt->length, len);
        !           288:        *out = chunk_clone(chunk_create(buf, len));
        !           289:        DBG2(DBG_TLS, "sending %N %s (%u bytes)",
        !           290:                 eap_type_names, this->type, kind, len);
        !           291:        DBG3(DBG_TLS, "%B", out);
        !           292:        return NEED_MORE;
        !           293: }
        !           294: 
        !           295: /**
        !           296:  * Send an ack to request next fragment
        !           297:  */
        !           298: static chunk_t create_ack(private_tls_eap_t *this)
        !           299: {
        !           300:        eap_tls_packet_t pkt = {
        !           301:                .code = this->is_server ? EAP_REQUEST : EAP_RESPONSE,
        !           302:                .type = this->type,
        !           303:        };
        !           304: 
        !           305:        if (this->is_server)
        !           306:        {
        !           307:                this->identifier++;
        !           308:        }
        !           309:        pkt.identifier = this->identifier;
        !           310:        htoun16(&pkt.length, sizeof(pkt));
        !           311: 
        !           312:        switch (this->type)
        !           313:        {
        !           314:                case EAP_TTLS:
        !           315:                        pkt.flags |= EAP_TTLS_SUPPORTED_VERSION;
        !           316:                        break;
        !           317:                case EAP_TNC:
        !           318:                        pkt.flags |= EAP_TNC_SUPPORTED_VERSION;
        !           319:                        break;
        !           320:                case EAP_PEAP:
        !           321:                        pkt.flags |= EAP_PEAP_SUPPORTED_VERSION;
        !           322:                        break;
        !           323:                default:
        !           324:                        break;
        !           325:        }
        !           326:        DBG2(DBG_TLS, "sending %N acknowledgement packet",
        !           327:                 eap_type_names, this->type);
        !           328:        return chunk_clone(chunk_from_thing(pkt));
        !           329: }
        !           330: 
        !           331: METHOD(tls_eap_t, process, status_t,
        !           332:        private_tls_eap_t *this, chunk_t in, chunk_t *out)
        !           333: {
        !           334:        eap_tls_packet_t *pkt;
        !           335:        status_t status;
        !           336: 
        !           337:        if (this->max_msg_count && ++this->processed > this->max_msg_count)
        !           338:        {
        !           339:                DBG1(DBG_TLS, "%N packet count exceeded (%d > %d)",
        !           340:                         eap_type_names, this->type,
        !           341:                         this->processed, this->max_msg_count);
        !           342:                return FAILED;
        !           343:        }
        !           344: 
        !           345:        pkt = (eap_tls_packet_t*)in.ptr;
        !           346:        if (in.len < sizeof(eap_tls_packet_t) || untoh16(&pkt->length) != in.len)
        !           347:        {
        !           348:                DBG1(DBG_TLS, "invalid %N packet length", eap_type_names, this->type);
        !           349:                return FAILED;
        !           350:        }
        !           351: 
        !           352:        /* update EAP identifier */
        !           353:        if (!this->is_server)
        !           354:        {
        !           355:                this->identifier = pkt->identifier;
        !           356:        }
        !           357:        DBG3(DBG_TLS, "%N payload %B", eap_type_names, this->type, &in);
        !           358: 
        !           359:        if ((this->type == EAP_PT_EAP && (pkt->flags & EAP_PT_START)) ||
        !           360:         (pkt->flags & EAP_TLS_START))
        !           361:        {
        !           362:                if (this->type == EAP_TTLS || this->type == EAP_TNC ||
        !           363:                        this->type == EAP_PEAP || this->type == EAP_PT_EAP)
        !           364:                {
        !           365:                        DBG1(DBG_TLS, "%N version is v%u", eap_type_names, this->type,
        !           366:                                 pkt->flags & EAP_TTLS_VERSION);
        !           367:                }
        !           368:        }
        !           369:        else
        !           370:        {
        !           371:                if (in.len == sizeof(eap_tls_packet_t))
        !           372:                {
        !           373:                        DBG2(DBG_TLS, "received %N acknowledgement packet",
        !           374:                                 eap_type_names, this->type);
        !           375:                        status = build_pkt(this, out);
        !           376:                        if (status == INVALID_STATE && this->tls->is_complete(this->tls))
        !           377:                        {
        !           378:                                return SUCCESS;
        !           379:                        }
        !           380:                        return status;
        !           381:                }
        !           382:                status = process_pkt(this, pkt);
        !           383:                switch (status)
        !           384:                {
        !           385:                        case NEED_MORE:
        !           386:                                break;
        !           387:                        case SUCCESS:
        !           388:                                return this->tls->is_complete(this->tls) ? SUCCESS : FAILED;
        !           389:                        default:
        !           390:                                return status;
        !           391:                }
        !           392:        }
        !           393:        status = build_pkt(this, out);
        !           394:        switch (status)
        !           395:        {
        !           396:                case INVALID_STATE:
        !           397:                        *out = create_ack(this);
        !           398:                        return NEED_MORE;
        !           399:                case FAILED:
        !           400:                        if (!this->is_server)
        !           401:                        {
        !           402:                                *out = create_ack(this);
        !           403:                                return NEED_MORE;
        !           404:                        }
        !           405:                        return FAILED;
        !           406:                default:
        !           407:                        return status;
        !           408:        }
        !           409: }
        !           410: 
        !           411: METHOD(tls_eap_t, get_msk, chunk_t,
        !           412:        private_tls_eap_t *this)
        !           413: {
        !           414:        return this->tls->get_eap_msk(this->tls);
        !           415: }
        !           416: 
        !           417: METHOD(tls_eap_t, get_identifier, uint8_t,
        !           418:        private_tls_eap_t *this)
        !           419: {
        !           420:        return this->identifier;
        !           421: }
        !           422: 
        !           423: METHOD(tls_eap_t, set_identifier, void,
        !           424:        private_tls_eap_t *this, uint8_t identifier)
        !           425: {
        !           426:        this->identifier = identifier;
        !           427: }
        !           428: 
        !           429: METHOD(tls_eap_t, get_auth, auth_cfg_t*,
        !           430:        private_tls_eap_t *this)
        !           431: {
        !           432:        return this->tls->get_auth(this->tls);
        !           433: }
        !           434: 
        !           435: METHOD(tls_eap_t, destroy, void,
        !           436:        private_tls_eap_t *this)
        !           437: {
        !           438:        this->tls->destroy(this->tls);
        !           439:        free(this);
        !           440: }
        !           441: 
        !           442: /**
        !           443:  * See header
        !           444:  */
        !           445: tls_eap_t *tls_eap_create(eap_type_t type, tls_t *tls, size_t frag_size,
        !           446:                                                  int max_msg_count, bool include_length)
        !           447: {
        !           448:        private_tls_eap_t *this;
        !           449: 
        !           450:        if (!tls)
        !           451:        {
        !           452:                return NULL;
        !           453:        }
        !           454: 
        !           455:        INIT(this,
        !           456:                .public = {
        !           457:                        .initiate = _initiate,
        !           458:                        .process = _process,
        !           459:                        .get_msk = _get_msk,
        !           460:                        .get_identifier = _get_identifier,
        !           461:                        .set_identifier = _set_identifier,
        !           462:                        .get_auth = _get_auth,
        !           463:                        .destroy = _destroy,
        !           464:                },
        !           465:                .type = type,
        !           466:                .is_server = tls->is_server(tls),
        !           467:                .first_fragment = (type != EAP_TNC && type != EAP_PT_EAP),
        !           468:                .frag_size = frag_size,
        !           469:                .max_msg_count = max_msg_count,
        !           470:                .include_length = include_length,
        !           471:                .tls = tls,
        !           472:        );
        !           473: 
        !           474:        switch (type)
        !           475:        {
        !           476:                case EAP_TTLS:
        !           477:                        this->supported_version = EAP_TTLS_SUPPORTED_VERSION;
        !           478:                        break;
        !           479:                case EAP_TNC:
        !           480:                        this->supported_version = EAP_TNC_SUPPORTED_VERSION;
        !           481:                        break;
        !           482:                case EAP_PEAP:
        !           483:                        this->supported_version = EAP_PEAP_SUPPORTED_VERSION;
        !           484:                        break;
        !           485:                case EAP_PT_EAP:
        !           486:                        this->supported_version = EAP_PT_EAP_SUPPORTED_VERSION;
        !           487:                        break;
        !           488:                default:
        !           489:                        break;
        !           490:        }
        !           491: 
        !           492:        if (this->is_server)
        !           493:        {
        !           494:                do
        !           495:                {       /* start with non-zero random identifier */
        !           496:                        this->identifier = random();
        !           497:                }
        !           498:                while (!this->identifier);
        !           499:        }
        !           500: 
        !           501:        return &this->public;
        !           502: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>