Return to tls.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / strongswan / src / libtls |
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: }