Annotation of embedaddon/strongswan/src/libtls/tls.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.h"
! 17:
! 18: #include <utils/debug.h>
! 19:
! 20: #include "tls_protection.h"
! 21: #include "tls_compression.h"
! 22: #include "tls_fragmentation.h"
! 23: #include "tls_crypto.h"
! 24: #include "tls_server.h"
! 25: #include "tls_peer.h"
! 26:
! 27: ENUM_BEGIN(tls_version_names, SSL_2_0, SSL_2_0,
! 28: "SSLv2");
! 29: ENUM_NEXT(tls_version_names, SSL_3_0, TLS_1_2, SSL_2_0,
! 30: "SSLv3",
! 31: "TLS 1.0",
! 32: "TLS 1.1",
! 33: "TLS 1.2");
! 34: ENUM_END(tls_version_names, TLS_1_2);
! 35:
! 36: ENUM(tls_content_type_names, TLS_CHANGE_CIPHER_SPEC, TLS_APPLICATION_DATA,
! 37: "ChangeCipherSpec",
! 38: "Alert",
! 39: "Handshake",
! 40: "ApplicationData",
! 41: );
! 42:
! 43: ENUM_BEGIN(tls_handshake_type_names, TLS_HELLO_REQUEST, TLS_SERVER_HELLO,
! 44: "HelloRequest",
! 45: "ClientHello",
! 46: "ServerHello");
! 47: ENUM_NEXT(tls_handshake_type_names,
! 48: TLS_CERTIFICATE, TLS_CLIENT_KEY_EXCHANGE, TLS_SERVER_HELLO,
! 49: "Certificate",
! 50: "ServerKeyExchange",
! 51: "CertificateRequest",
! 52: "ServerHelloDone",
! 53: "CertificateVerify",
! 54: "ClientKeyExchange");
! 55: ENUM_NEXT(tls_handshake_type_names,
! 56: TLS_FINISHED, TLS_FINISHED, TLS_CLIENT_KEY_EXCHANGE,
! 57: "Finished");
! 58: ENUM_END(tls_handshake_type_names, TLS_FINISHED);
! 59:
! 60: ENUM_BEGIN(tls_extension_names, TLS_EXT_SERVER_NAME, TLS_EXT_STATUS_REQUEST,
! 61: "server name",
! 62: "max fragment length",
! 63: "client certificate url",
! 64: "trusted ca keys",
! 65: "truncated hmac",
! 66: "status request");
! 67: ENUM_NEXT(tls_extension_names,
! 68: TLS_EXT_ELLIPTIC_CURVES, TLS_EXT_EC_POINT_FORMATS,
! 69: TLS_EXT_STATUS_REQUEST,
! 70: "elliptic curves",
! 71: "ec point formats");
! 72: ENUM_NEXT(tls_extension_names,
! 73: TLS_EXT_SIGNATURE_ALGORITHMS, TLS_EXT_SIGNATURE_ALGORITHMS,
! 74: TLS_EXT_EC_POINT_FORMATS,
! 75: "signature algorithms");
! 76: ENUM_NEXT(tls_extension_names,
! 77: TLS_EXT_RENEGOTIATION_INFO, TLS_EXT_RENEGOTIATION_INFO,
! 78: TLS_EXT_SIGNATURE_ALGORITHMS,
! 79: "renegotiation info");
! 80: ENUM_END(tls_extension_names, TLS_EXT_RENEGOTIATION_INFO);
! 81:
! 82: /**
! 83: * TLS record
! 84: */
! 85: typedef struct __attribute__((packed)) {
! 86: uint8_t type;
! 87: uint16_t version;
! 88: uint16_t length;
! 89: char data[];
! 90: } tls_record_t;
! 91:
! 92: typedef struct private_tls_t private_tls_t;
! 93:
! 94: /**
! 95: * Private data of an tls_protection_t object.
! 96: */
! 97: struct private_tls_t {
! 98:
! 99: /**
! 100: * Public tls_t interface.
! 101: */
! 102: tls_t public;
! 103:
! 104: /**
! 105: * Role this TLS stack acts as.
! 106: */
! 107: bool is_server;
! 108:
! 109: /**
! 110: * Negotiated TLS version
! 111: */
! 112: tls_version_t version;
! 113:
! 114: /**
! 115: * TLS stack purpose, as given to constructor
! 116: */
! 117: tls_purpose_t purpose;
! 118:
! 119: /**
! 120: * TLS record protection layer
! 121: */
! 122: tls_protection_t *protection;
! 123:
! 124: /**
! 125: * TLS record compression layer
! 126: */
! 127: tls_compression_t *compression;
! 128:
! 129: /**
! 130: * TLS record fragmentation layer
! 131: */
! 132: tls_fragmentation_t *fragmentation;
! 133:
! 134: /**
! 135: * TLS alert handler
! 136: */
! 137: tls_alert_t *alert;
! 138:
! 139: /**
! 140: * TLS crypto helper context
! 141: */
! 142: tls_crypto_t *crypto;
! 143:
! 144: /**
! 145: * TLS handshake protocol handler
! 146: */
! 147: tls_handshake_t *handshake;
! 148:
! 149: /**
! 150: * TLS application data handler
! 151: */
! 152: tls_application_t *application;
! 153:
! 154: /**
! 155: * Allocated input buffer
! 156: */
! 157: chunk_t input;
! 158:
! 159: /**
! 160: * Number of bytes read in input buffer
! 161: */
! 162: size_t inpos;
! 163:
! 164: /**
! 165: * Allocated output buffer
! 166: */
! 167: chunk_t output;
! 168:
! 169: /**
! 170: * Number of bytes processed from output buffer
! 171: */
! 172: size_t outpos;
! 173:
! 174: /**
! 175: * Position in partially received record header
! 176: */
! 177: size_t headpos;
! 178:
! 179: /**
! 180: * Partial TLS record header received
! 181: */
! 182: tls_record_t head;
! 183: };
! 184:
! 185: /**
! 186: * Described in header.
! 187: */
! 188: void libtls_init(void)
! 189: {
! 190: /* empty */
! 191: }
! 192:
! 193: METHOD(tls_t, process, status_t,
! 194: private_tls_t *this, void *buf, size_t buflen)
! 195: {
! 196: tls_record_t *record;
! 197: status_t status;
! 198: u_int len;
! 199:
! 200: if (this->headpos)
! 201: { /* have a partial TLS record header, try to complete it */
! 202: len = min(buflen, sizeof(this->head) - this->headpos);
! 203: memcpy(((char*)&this->head) + this->headpos, buf, len);
! 204: this->headpos += len;
! 205: buflen -= len;
! 206: buf += len;
! 207: if (this->headpos == sizeof(this->head))
! 208: { /* header complete, allocate space with new header */
! 209: len = untoh16(&this->head.length);
! 210: this->input = chunk_alloc(len + sizeof(tls_record_t));
! 211: memcpy(this->input.ptr, &this->head, sizeof(this->head));
! 212: this->inpos = sizeof(this->head);
! 213: this->headpos = 0;
! 214: }
! 215: }
! 216:
! 217: while (buflen)
! 218: {
! 219: if (this->input.len == 0)
! 220: {
! 221: while (buflen >= sizeof(tls_record_t))
! 222: {
! 223: /* try to process records inline */
! 224: record = buf;
! 225: len = untoh16(&record->length);
! 226:
! 227: if (len + sizeof(tls_record_t) > buflen)
! 228: { /* not a full record, read to buffer */
! 229: this->input = chunk_alloc(len + sizeof(tls_record_t));
! 230: this->inpos = 0;
! 231: break;
! 232: }
! 233: DBG2(DBG_TLS, "processing TLS %N record (%d bytes)",
! 234: tls_content_type_names, record->type, len);
! 235: status = this->protection->process(this->protection,
! 236: record->type, chunk_create(record->data, len));
! 237: if (status != NEED_MORE)
! 238: {
! 239: return status;
! 240: }
! 241: buf += len + sizeof(tls_record_t);
! 242: buflen -= len + sizeof(tls_record_t);
! 243: if (buflen == 0)
! 244: {
! 245: return NEED_MORE;
! 246: }
! 247: }
! 248: if (buflen < sizeof(tls_record_t))
! 249: {
! 250: DBG2(DBG_TLS, "received incomplete TLS record header");
! 251: memcpy(&this->head, buf, buflen);
! 252: this->headpos = buflen;
! 253: break;
! 254: }
! 255: }
! 256: len = min(buflen, this->input.len - this->inpos);
! 257: memcpy(this->input.ptr + this->inpos, buf, len);
! 258: buf += len;
! 259: buflen -= len;
! 260: this->inpos += len;
! 261: DBG2(DBG_TLS, "buffering %d bytes, %d bytes of %d byte TLS record received",
! 262: len, this->inpos, this->input.len);
! 263: if (this->input.len == this->inpos)
! 264: {
! 265: record = (tls_record_t*)this->input.ptr;
! 266: len = untoh16(&record->length);
! 267:
! 268: DBG2(DBG_TLS, "processing buffered TLS %N record (%d bytes)",
! 269: tls_content_type_names, record->type, len);
! 270: status = this->protection->process(this->protection,
! 271: record->type, chunk_create(record->data, len));
! 272: chunk_free(&this->input);
! 273: this->inpos = 0;
! 274: if (status != NEED_MORE)
! 275: {
! 276: return status;
! 277: }
! 278: }
! 279: }
! 280: return NEED_MORE;
! 281: }
! 282:
! 283: METHOD(tls_t, build, status_t,
! 284: private_tls_t *this, void *buf, size_t *buflen, size_t *msglen)
! 285: {
! 286: tls_content_type_t type;
! 287: tls_record_t record;
! 288: status_t status;
! 289: chunk_t data;
! 290: size_t len;
! 291:
! 292: len = *buflen;
! 293: if (this->output.len == 0)
! 294: {
! 295: /* query upper layers for new records, as many as we can get */
! 296: while (TRUE)
! 297: {
! 298: status = this->protection->build(this->protection, &type, &data);
! 299: switch (status)
! 300: {
! 301: case NEED_MORE:
! 302: record.type = type;
! 303: htoun16(&record.version, this->version);
! 304: htoun16(&record.length, data.len);
! 305: this->output = chunk_cat("mcm", this->output,
! 306: chunk_from_thing(record), data);
! 307: DBG2(DBG_TLS, "sending TLS %N record (%d bytes)",
! 308: tls_content_type_names, type, data.len);
! 309: continue;
! 310: case INVALID_STATE:
! 311: if (this->output.len == 0)
! 312: {
! 313: return INVALID_STATE;
! 314: }
! 315: break;
! 316: default:
! 317: return status;
! 318: }
! 319: break;
! 320: }
! 321: if (msglen)
! 322: {
! 323: *msglen = this->output.len;
! 324: }
! 325: }
! 326: else
! 327: {
! 328: if (msglen)
! 329: {
! 330: *msglen = 0;
! 331: }
! 332: }
! 333: len = min(len, this->output.len - this->outpos);
! 334: memcpy(buf, this->output.ptr + this->outpos, len);
! 335: this->outpos += len;
! 336: *buflen = len;
! 337: if (this->outpos == this->output.len)
! 338: {
! 339: chunk_free(&this->output);
! 340: this->outpos = 0;
! 341: return ALREADY_DONE;
! 342: }
! 343: return NEED_MORE;
! 344: }
! 345:
! 346: METHOD(tls_t, is_server, bool,
! 347: private_tls_t *this)
! 348: {
! 349: return this->is_server;
! 350: }
! 351:
! 352: METHOD(tls_t, get_server_id, identification_t*,
! 353: private_tls_t *this)
! 354: {
! 355: return this->handshake->get_server_id(this->handshake);
! 356: }
! 357:
! 358: METHOD(tls_t, get_peer_id, identification_t*,
! 359: private_tls_t *this)
! 360: {
! 361: return this->handshake->get_peer_id(this->handshake);
! 362: }
! 363:
! 364: METHOD(tls_t, get_version, tls_version_t,
! 365: private_tls_t *this)
! 366: {
! 367: return this->version;
! 368: }
! 369:
! 370: METHOD(tls_t, set_version, bool,
! 371: private_tls_t *this, tls_version_t version)
! 372: {
! 373: if (version > this->version)
! 374: {
! 375: return FALSE;
! 376: }
! 377: switch (version)
! 378: {
! 379: case TLS_1_0:
! 380: case TLS_1_1:
! 381: case TLS_1_2:
! 382: this->version = version;
! 383: this->protection->set_version(this->protection, version);
! 384: return TRUE;
! 385: case SSL_2_0:
! 386: case SSL_3_0:
! 387: default:
! 388: return FALSE;
! 389: }
! 390: }
! 391:
! 392: METHOD(tls_t, get_purpose, tls_purpose_t,
! 393: private_tls_t *this)
! 394: {
! 395: return this->purpose;
! 396: }
! 397:
! 398: METHOD(tls_t, is_complete, bool,
! 399: private_tls_t *this)
! 400: {
! 401: if (this->handshake->finished(this->handshake))
! 402: {
! 403: if (!this->application)
! 404: {
! 405: return TRUE;
! 406: }
! 407: return this->fragmentation->application_finished(this->fragmentation);
! 408: }
! 409: return FALSE;
! 410: }
! 411:
! 412: METHOD(tls_t, get_eap_msk, chunk_t,
! 413: private_tls_t *this)
! 414: {
! 415: return this->crypto->get_eap_msk(this->crypto);
! 416: }
! 417:
! 418: METHOD(tls_t, get_auth, auth_cfg_t*,
! 419: private_tls_t *this)
! 420: {
! 421: return this->handshake->get_auth(this->handshake);
! 422: }
! 423:
! 424: METHOD(tls_t, destroy, void,
! 425: private_tls_t *this)
! 426: {
! 427: this->protection->destroy(this->protection);
! 428: this->compression->destroy(this->compression);
! 429: this->fragmentation->destroy(this->fragmentation);
! 430: this->crypto->destroy(this->crypto);
! 431: this->handshake->destroy(this->handshake);
! 432: DESTROY_IF(this->application);
! 433: this->alert->destroy(this->alert);
! 434:
! 435: free(this->input.ptr);
! 436: free(this->output.ptr);
! 437:
! 438: free(this);
! 439: }
! 440:
! 441: /**
! 442: * See header
! 443: */
! 444: tls_t *tls_create(bool is_server, identification_t *server,
! 445: identification_t *peer, tls_purpose_t purpose,
! 446: tls_application_t *application, tls_cache_t *cache)
! 447: {
! 448: private_tls_t *this;
! 449:
! 450: switch (purpose)
! 451: {
! 452: case TLS_PURPOSE_EAP_TLS:
! 453: case TLS_PURPOSE_EAP_TTLS:
! 454: case TLS_PURPOSE_EAP_PEAP:
! 455: case TLS_PURPOSE_GENERIC:
! 456: case TLS_PURPOSE_GENERIC_NULLOK:
! 457: break;
! 458: default:
! 459: return NULL;
! 460: }
! 461:
! 462: INIT(this,
! 463: .public = {
! 464: .process = _process,
! 465: .build = _build,
! 466: .is_server = _is_server,
! 467: .get_server_id = _get_server_id,
! 468: .get_peer_id = _get_peer_id,
! 469: .get_version = _get_version,
! 470: .set_version = _set_version,
! 471: .get_purpose = _get_purpose,
! 472: .is_complete = _is_complete,
! 473: .get_eap_msk = _get_eap_msk,
! 474: .get_auth = _get_auth,
! 475: .destroy = _destroy,
! 476: },
! 477: .is_server = is_server,
! 478: .version = TLS_1_2,
! 479: .application = application,
! 480: .purpose = purpose,
! 481: );
! 482: lib->settings->add_fallback(lib->settings, "%s.tls", "libtls", lib->ns);
! 483:
! 484: this->crypto = tls_crypto_create(&this->public, cache);
! 485: this->alert = tls_alert_create();
! 486: if (is_server)
! 487: {
! 488: this->handshake = &tls_server_create(&this->public, this->crypto,
! 489: this->alert, server, peer)->handshake;
! 490: }
! 491: else
! 492: {
! 493: this->handshake = &tls_peer_create(&this->public, this->crypto,
! 494: this->alert, peer, server)->handshake;
! 495: }
! 496: this->fragmentation = tls_fragmentation_create(this->handshake, this->alert,
! 497: this->application, purpose);
! 498: this->compression = tls_compression_create(this->fragmentation, this->alert);
! 499: this->protection = tls_protection_create(this->compression, this->alert);
! 500: this->crypto->set_protection(this->crypto, this->protection);
! 501:
! 502: return &this->public;
! 503: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>