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

1.1     ! misho       1: /*
        !             2:  * Copyright (C) 2010 Martin Willi
        !             3:  * Copyright (C) 2010 revosec AG
        !             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 "tls_fragmentation.h"
        !            17: 
        !            18: #include <bio/bio_reader.h>
        !            19: #include <utils/debug.h>
        !            20: 
        !            21: /**
        !            22:  * Maximum size of a TLS handshake message we accept
        !            23:  */
        !            24: #define TLS_MAX_HANDSHAKE_LEN  65536
        !            25: 
        !            26: typedef struct private_tls_fragmentation_t private_tls_fragmentation_t;
        !            27: 
        !            28: /**
        !            29:  * Alert state
        !            30:  */
        !            31: typedef enum {
        !            32:        /* no alert received/sent */
        !            33:        ALERT_NONE,
        !            34:        /* currently sending an alert */
        !            35:        ALERT_SENDING,
        !            36:        /* alert sent and out */
        !            37:        ALERT_SENT,
        !            38: } alert_state_t;
        !            39: 
        !            40: /**
        !            41:  * Private data of an tls_fragmentation_t object.
        !            42:  */
        !            43: struct private_tls_fragmentation_t {
        !            44: 
        !            45:        /**
        !            46:         * Public tls_fragmentation_t interface.
        !            47:         */
        !            48:        tls_fragmentation_t public;
        !            49: 
        !            50:        /**
        !            51:         * Upper layer handshake protocol
        !            52:         */
        !            53:        tls_handshake_t *handshake;
        !            54: 
        !            55:        /**
        !            56:         * TLS alert handler
        !            57:         */
        !            58:        tls_alert_t *alert;
        !            59: 
        !            60:        /**
        !            61:         * State of alert handling
        !            62:         */
        !            63:        alert_state_t state;
        !            64: 
        !            65:        /**
        !            66:         * Did the application layer complete successfully?
        !            67:         */
        !            68:        bool application_finished;
        !            69: 
        !            70:        /**
        !            71:         * Handshake input buffer
        !            72:         */
        !            73:        chunk_t input;
        !            74: 
        !            75:        /**
        !            76:         * Position in input buffer
        !            77:         */
        !            78:        size_t inpos;
        !            79: 
        !            80:        /**
        !            81:         * Currently processed handshake message type
        !            82:         */
        !            83:        tls_handshake_type_t type;
        !            84: 
        !            85:        /**
        !            86:         * Handshake output buffer
        !            87:         */
        !            88:        chunk_t output;
        !            89: 
        !            90:        /**
        !            91:         * Type of data in output buffer
        !            92:         */
        !            93:        tls_content_type_t output_type;
        !            94: 
        !            95:        /**
        !            96:         * Upper layer application data protocol
        !            97:         */
        !            98:        tls_application_t *application;
        !            99: 
        !           100:        /**
        !           101:         * Type of context this TLS instance runs in
        !           102:         */
        !           103:        tls_purpose_t purpose;
        !           104: };
        !           105: 
        !           106: /**
        !           107:  * Check if we should send a close notify once the application finishes
        !           108:  */
        !           109: static bool send_close_notify(private_tls_fragmentation_t *this)
        !           110: {
        !           111:        switch (this->purpose)
        !           112:        {
        !           113:                case TLS_PURPOSE_EAP_TLS:
        !           114:                case TLS_PURPOSE_EAP_TTLS:
        !           115:                case TLS_PURPOSE_EAP_PEAP:
        !           116:                        /* not for TLS-in-EAP, as we indicate completion with EAP-SUCCCESS.
        !           117:                         * Windows does not like close notifies, and hangs/disconnects. */
        !           118:                        return FALSE;
        !           119:                default:
        !           120:                        return TRUE;
        !           121:        }
        !           122: }
        !           123: 
        !           124: /**
        !           125:  * Process a TLS alert
        !           126:  */
        !           127: static status_t process_alert(private_tls_fragmentation_t *this,
        !           128:                                                          bio_reader_t *reader)
        !           129: {
        !           130:        uint8_t level, description;
        !           131: 
        !           132:        if (!reader->read_uint8(reader, &level) ||
        !           133:                !reader->read_uint8(reader, &description))
        !           134:        {
        !           135:                this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
        !           136:                return NEED_MORE;
        !           137:        }
        !           138:        return this->alert->process(this->alert, level, description);
        !           139: }
        !           140: 
        !           141: /**
        !           142:  * Process TLS handshake protocol data
        !           143:  */
        !           144: static status_t process_handshake(private_tls_fragmentation_t *this,
        !           145:                                                                  bio_reader_t *reader)
        !           146: {
        !           147:        while (reader->remaining(reader))
        !           148:        {
        !           149:                bio_reader_t *msg;
        !           150:                uint8_t type;
        !           151:                uint32_t len;
        !           152:                status_t status;
        !           153:                chunk_t data;
        !           154: 
        !           155:                if (reader->remaining(reader) > TLS_MAX_FRAGMENT_LEN)
        !           156:                {
        !           157:                        DBG1(DBG_TLS, "TLS fragment has invalid length");
        !           158:                        this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
        !           159:                        return NEED_MORE;
        !           160:                }
        !           161: 
        !           162:                if (this->input.len == 0)
        !           163:                {       /* new handshake message */
        !           164:                        if (!reader->read_uint8(reader, &type) ||
        !           165:                                !reader->read_uint24(reader, &len))
        !           166:                        {
        !           167:                                DBG1(DBG_TLS, "TLS handshake header invalid");
        !           168:                                this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
        !           169:                                return NEED_MORE;
        !           170:                        }
        !           171:                        this->type = type;
        !           172:                        if (len > TLS_MAX_HANDSHAKE_LEN)
        !           173:                        {
        !           174:                                DBG1(DBG_TLS, "TLS handshake exceeds maximum length");
        !           175:                                this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
        !           176:                                return NEED_MORE;
        !           177:                        }
        !           178:                        chunk_free(&this->input);
        !           179:                        this->inpos = 0;
        !           180:                        if (len)
        !           181:                        {
        !           182:                                this->input = chunk_alloc(len);
        !           183:                        }
        !           184:                }
        !           185: 
        !           186:                len = min(this->input.len - this->inpos, reader->remaining(reader));
        !           187:                if (!reader->read_data(reader, len, &data))
        !           188:                {
        !           189:                        DBG1(DBG_TLS, "TLS fragment has invalid length");
        !           190:                        this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
        !           191:                        return NEED_MORE;
        !           192:                }
        !           193:                memcpy(this->input.ptr + this->inpos, data.ptr, len);
        !           194:                this->inpos += len;
        !           195: 
        !           196:                if (this->input.len == this->inpos)
        !           197:                {       /* message completely defragmented, process */
        !           198:                        msg = bio_reader_create(this->input);
        !           199:                        DBG2(DBG_TLS, "received TLS %N handshake (%u bytes)",
        !           200:                                 tls_handshake_type_names, this->type, this->input.len);
        !           201:                        status = this->handshake->process(this->handshake, this->type, msg);
        !           202:                        msg->destroy(msg);
        !           203:                        chunk_free(&this->input);
        !           204:                        if (status != NEED_MORE)
        !           205:                        {
        !           206:                                return status;
        !           207:                        }
        !           208:                }
        !           209:                if (this->alert->fatal(this->alert))
        !           210:                {
        !           211:                        break;
        !           212:                }
        !           213:        }
        !           214:        return NEED_MORE;
        !           215: }
        !           216: 
        !           217: /**
        !           218:  * Process TLS application data
        !           219:  */
        !           220: static status_t process_application(private_tls_fragmentation_t *this,
        !           221:                                                                        bio_reader_t *reader)
        !           222: {
        !           223:        if (!this->handshake->finished(this->handshake))
        !           224:        {
        !           225:                DBG1(DBG_TLS, "received TLS application data, "
        !           226:                         "but handshake not finished");
        !           227:                return FAILED;
        !           228:        }
        !           229:        while (reader->remaining(reader))
        !           230:        {
        !           231:                status_t status;
        !           232:                chunk_t data;
        !           233: 
        !           234:                if (reader->remaining(reader) > TLS_MAX_FRAGMENT_LEN)
        !           235:                {
        !           236:                        DBG1(DBG_TLS, "TLS fragment has invalid length");
        !           237:                        this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR);
        !           238:                        return NEED_MORE;
        !           239:                }
        !           240:                data = reader->peek(reader);
        !           241:                DBG3(DBG_TLS, "%B", &data);
        !           242:                status = this->application->process(this->application, reader);
        !           243:                switch (status)
        !           244:                {
        !           245:                        case NEED_MORE:
        !           246:                                continue;
        !           247:                        case SUCCESS:
        !           248:                                this->application_finished = TRUE;
        !           249:                                if (!send_close_notify(this))
        !           250:                                {
        !           251:                                        return SUCCESS;
        !           252:                                }
        !           253:                                /* FALL */
        !           254:                        case FAILED:
        !           255:                        default:
        !           256:                                this->alert->add(this->alert, TLS_FATAL, TLS_CLOSE_NOTIFY);
        !           257:                                return NEED_MORE;
        !           258:                }
        !           259:        }
        !           260:        return NEED_MORE;
        !           261: }
        !           262: 
        !           263: METHOD(tls_fragmentation_t, process, status_t,
        !           264:        private_tls_fragmentation_t *this, tls_content_type_t type, chunk_t data)
        !           265: {
        !           266:        bio_reader_t *reader;
        !           267:        status_t status;
        !           268: 
        !           269:        switch (this->state)
        !           270:        {
        !           271:                case ALERT_SENDING:
        !           272:                case ALERT_SENT:
        !           273:                        /* don't accept more input, fatal error occurred */
        !           274:                        return NEED_MORE;
        !           275:                case ALERT_NONE:
        !           276:                        break;
        !           277:        }
        !           278:        reader = bio_reader_create(data);
        !           279:        switch (type)
        !           280:        {
        !           281:                case TLS_CHANGE_CIPHER_SPEC:
        !           282:                        if (this->handshake->cipherspec_changed(this->handshake, TRUE))
        !           283:                        {
        !           284:                                this->handshake->change_cipherspec(this->handshake, TRUE);
        !           285:                                status = NEED_MORE;
        !           286:                                break;
        !           287:                        }
        !           288:                        status = FAILED;
        !           289:                        break;
        !           290:                case TLS_ALERT:
        !           291:                        status = process_alert(this, reader);
        !           292:                        break;
        !           293:                case TLS_HANDSHAKE:
        !           294:                        status = process_handshake(this, reader);
        !           295:                        break;
        !           296:                case TLS_APPLICATION_DATA:
        !           297:                        status = process_application(this, reader);
        !           298:                        break;
        !           299:                default:
        !           300:                        DBG1(DBG_TLS, "received unknown TLS content type %d, ignored", type);
        !           301:                        status = NEED_MORE;
        !           302:                        break;
        !           303:        }
        !           304:        reader->destroy(reader);
        !           305:        return status;
        !           306: }
        !           307: 
        !           308: /**
        !           309:  * Check if alerts are pending
        !           310:  */
        !           311: static bool check_alerts(private_tls_fragmentation_t *this, chunk_t *data)
        !           312: {
        !           313:        tls_alert_level_t level;
        !           314:        tls_alert_desc_t desc;
        !           315:        bio_writer_t *writer;
        !           316: 
        !           317:        if (this->alert->get(this->alert, &level, &desc))
        !           318:        {
        !           319:                writer = bio_writer_create(2);
        !           320: 
        !           321:                writer->write_uint8(writer, level);
        !           322:                writer->write_uint8(writer, desc);
        !           323: 
        !           324:                *data = chunk_clone(writer->get_buf(writer));
        !           325:                writer->destroy(writer);
        !           326:                return TRUE;
        !           327:        }
        !           328:        return FALSE;
        !           329: }
        !           330: 
        !           331: /**
        !           332:  * Build handshake message
        !           333:  */
        !           334: static status_t build_handshake(private_tls_fragmentation_t *this)
        !           335: {
        !           336:        bio_writer_t *hs, *msg;
        !           337:        tls_handshake_type_t type;
        !           338:        status_t status;
        !           339: 
        !           340:        msg = bio_writer_create(64);
        !           341:        while (TRUE)
        !           342:        {
        !           343:                hs = bio_writer_create(64);
        !           344:                status = this->handshake->build(this->handshake, &type, hs);
        !           345:                switch (status)
        !           346:                {
        !           347:                        case NEED_MORE:
        !           348:                                if (this->alert->fatal(this->alert))
        !           349:                                {
        !           350:                                        break;
        !           351:                                }
        !           352:                                msg->write_uint8(msg, type);
        !           353:                                msg->write_data24(msg, hs->get_buf(hs));
        !           354:                                DBG2(DBG_TLS, "sending TLS %N handshake (%u bytes)",
        !           355:                                         tls_handshake_type_names, type, hs->get_buf(hs).len);
        !           356:                                if (!this->handshake->cipherspec_changed(this->handshake, FALSE))
        !           357:                                {
        !           358:                                        hs->destroy(hs);
        !           359:                                        continue;
        !           360:                                }
        !           361:                                /* FALL */
        !           362:                        case INVALID_STATE:
        !           363:                                this->output_type = TLS_HANDSHAKE;
        !           364:                                this->output = chunk_clone(msg->get_buf(msg));
        !           365:                                break;
        !           366:                        default:
        !           367:                                break;
        !           368:                }
        !           369:                hs->destroy(hs);
        !           370:                break;
        !           371:        }
        !           372:        msg->destroy(msg);
        !           373:        return status;
        !           374: }
        !           375: 
        !           376: /**
        !           377:  * Build TLS application data
        !           378:  */
        !           379: static status_t build_application(private_tls_fragmentation_t *this)
        !           380: {
        !           381:        bio_writer_t *msg;
        !           382:        status_t status;
        !           383: 
        !           384:        msg = bio_writer_create(64);
        !           385:        while (TRUE)
        !           386:        {
        !           387:                status = this->application->build(this->application, msg);
        !           388:                switch (status)
        !           389:                {
        !           390:                        case NEED_MORE:
        !           391:                                continue;
        !           392:                        case INVALID_STATE:
        !           393:                                this->output_type = TLS_APPLICATION_DATA;
        !           394:                                this->output = chunk_clone(msg->get_buf(msg));
        !           395:                                break;
        !           396:                        case SUCCESS:
        !           397:                                this->application_finished = TRUE;
        !           398:                                if (!send_close_notify(this))
        !           399:                                {
        !           400:                                        break;
        !           401:                                }
        !           402:                                /* FALL */
        !           403:                        case FAILED:
        !           404:                        default:
        !           405:                                this->alert->add(this->alert, TLS_FATAL, TLS_CLOSE_NOTIFY);
        !           406:                                break;
        !           407:                }
        !           408:                break;
        !           409:        }
        !           410:        msg->destroy(msg);
        !           411:        return status;
        !           412: }
        !           413: 
        !           414: METHOD(tls_fragmentation_t, build, status_t,
        !           415:        private_tls_fragmentation_t *this, tls_content_type_t *type, chunk_t *data)
        !           416: {
        !           417:        status_t status = INVALID_STATE;
        !           418: 
        !           419:        switch (this->state)
        !           420:        {
        !           421:                case ALERT_SENDING:
        !           422:                        this->state = ALERT_SENT;
        !           423:                        return INVALID_STATE;
        !           424:                case ALERT_SENT:
        !           425:                        if (this->application_finished)
        !           426:                        {
        !           427:                                return SUCCESS;
        !           428:                        }
        !           429:                        return FAILED;
        !           430:                case ALERT_NONE:
        !           431:                        break;
        !           432:        }
        !           433:        if (check_alerts(this, data))
        !           434:        {
        !           435:                this->state = ALERT_SENDING;
        !           436:                *type = TLS_ALERT;
        !           437:                return NEED_MORE;
        !           438:        }
        !           439:        if (!this->output.len)
        !           440:        {
        !           441:                if (this->handshake->cipherspec_changed(this->handshake, FALSE))
        !           442:                {
        !           443:                        this->handshake->change_cipherspec(this->handshake, FALSE);
        !           444:                        *type = TLS_CHANGE_CIPHER_SPEC;
        !           445:                        *data = chunk_clone(chunk_from_chars(0x01));
        !           446:                        return NEED_MORE;
        !           447:                }
        !           448:                if (!this->handshake->finished(this->handshake))
        !           449:                {
        !           450:                        status = build_handshake(this);
        !           451:                }
        !           452:                else if (this->application)
        !           453:                {
        !           454:                        status = build_application(this);
        !           455:                }
        !           456:                if (check_alerts(this, data))
        !           457:                {
        !           458:                        this->state = ALERT_SENDING;
        !           459:                        *type = TLS_ALERT;
        !           460:                        return NEED_MORE;
        !           461:                }
        !           462:        }
        !           463:        if (this->output.len)
        !           464:        {
        !           465:                *type = this->output_type;
        !           466:                if (this->output.len <= TLS_MAX_FRAGMENT_LEN)
        !           467:                {
        !           468:                        *data = this->output;
        !           469:                        this->output = chunk_empty;
        !           470:                        return NEED_MORE;
        !           471:                }
        !           472:                *data = chunk_create(this->output.ptr, TLS_MAX_FRAGMENT_LEN);
        !           473:                this->output = chunk_clone(chunk_skip(this->output, TLS_MAX_FRAGMENT_LEN));
        !           474:                return NEED_MORE;
        !           475:        }
        !           476:        return status;
        !           477: }
        !           478: 
        !           479: METHOD(tls_fragmentation_t, application_finished, bool,
        !           480:        private_tls_fragmentation_t *this)
        !           481: {
        !           482:        return this->application_finished;
        !           483: }
        !           484: 
        !           485: METHOD(tls_fragmentation_t, destroy, void,
        !           486:        private_tls_fragmentation_t *this)
        !           487: {
        !           488:        free(this->input.ptr);
        !           489:        free(this->output.ptr);
        !           490:        free(this);
        !           491: }
        !           492: 
        !           493: /**
        !           494:  * See header
        !           495:  */
        !           496: tls_fragmentation_t *tls_fragmentation_create(tls_handshake_t *handshake,
        !           497:                                                        tls_alert_t *alert, tls_application_t *application,
        !           498:                                                        tls_purpose_t purpose)
        !           499: {
        !           500:        private_tls_fragmentation_t *this;
        !           501: 
        !           502:        INIT(this,
        !           503:                .public = {
        !           504:                        .process = _process,
        !           505:                        .build = _build,
        !           506:                        .application_finished = _application_finished,
        !           507:                        .destroy = _destroy,
        !           508:                },
        !           509:                .handshake = handshake,
        !           510:                .alert = alert,
        !           511:                .state = ALERT_NONE,
        !           512:                .application = application,
        !           513:                .purpose = purpose,
        !           514:        );
        !           515: 
        !           516:        return &this->public;
        !           517: }

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