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>