diff options
author | Martin Willi <martin@revosec.ch> | 2010-08-23 14:22:38 +0200 |
---|---|---|
committer | Martin Willi <martin@revosec.ch> | 2010-08-23 15:13:37 +0200 |
commit | e6f3ef13303313ce4a87983fe640f958e07cc676 (patch) | |
tree | 7dd486d439e7b7975be96d5160a5ceaf5550a2ea | |
parent | 908e752201cc5cfc7f663e724f0ab3dc76c72d0a (diff) | |
download | strongswan-e6f3ef13303313ce4a87983fe640f958e07cc676.tar.bz2 strongswan-e6f3ef13303313ce4a87983fe640f958e07cc676.tar.xz |
Implemented TLS Alert handling
-rw-r--r-- | src/libtls/Makefile.am | 1 | ||||
-rw-r--r-- | src/libtls/tls.c | 18 | ||||
-rw-r--r-- | src/libtls/tls_alert.c | 221 | ||||
-rw-r--r-- | src/libtls/tls_alert.h | 126 | ||||
-rw-r--r-- | src/libtls/tls_compression.c | 3 | ||||
-rw-r--r-- | src/libtls/tls_compression.h | 9 | ||||
-rw-r--r-- | src/libtls/tls_fragmentation.c | 124 | ||||
-rw-r--r-- | src/libtls/tls_fragmentation.h | 11 | ||||
-rw-r--r-- | src/libtls/tls_handshake.h | 7 | ||||
-rw-r--r-- | src/libtls/tls_peer.c | 64 | ||||
-rw-r--r-- | src/libtls/tls_peer.h | 8 | ||||
-rw-r--r-- | src/libtls/tls_protection.c | 48 | ||||
-rw-r--r-- | src/libtls/tls_protection.h | 18 | ||||
-rw-r--r-- | src/libtls/tls_server.c | 54 | ||||
-rw-r--r-- | src/libtls/tls_server.h | 11 |
15 files changed, 643 insertions, 80 deletions
diff --git a/src/libtls/Makefile.am b/src/libtls/Makefile.am index 5e112f601..ff11ef41e 100644 --- a/src/libtls/Makefile.am +++ b/src/libtls/Makefile.am @@ -6,6 +6,7 @@ libtls_la_SOURCES = \ tls_protection.h tls_protection.c \ tls_compression.h tls_compression.c \ tls_fragmentation.h tls_fragmentation.c \ + tls_alert.h tls_alert.c \ tls_crypto.h tls_crypto.c \ tls_prf.h tls_prf.c \ tls_reader.h tls_reader.c \ diff --git a/src/libtls/tls.c b/src/libtls/tls.c index d46ce0084..42f71d753 100644 --- a/src/libtls/tls.c +++ b/src/libtls/tls.c @@ -107,6 +107,11 @@ struct private_tls_t { tls_fragmentation_t *fragmentation; /** + * TLS alert handler + */ + tls_alert_t *alert; + + /** * TLS crypto helper context */ tls_crypto_t *crypto; @@ -159,6 +164,7 @@ METHOD(tls_t, set_version, bool, case TLS_1_1: case TLS_1_2: this->version = version; + this->protection->set_version(this->protection, version); return TRUE; case SSL_2_0: case SSL_3_0: @@ -196,6 +202,7 @@ METHOD(tls_t, destroy, void, this->peer->destroy(this->peer); this->server->destroy(this->server); DESTROY_IF(this->application); + this->alert->destroy(this->alert); free(this); } @@ -239,20 +246,21 @@ tls_t *tls_create(bool is_server, identification_t *server, ); this->crypto = tls_crypto_create(&this->public); + this->alert = tls_alert_create(); if (is_server) { this->handshake = &tls_server_create(&this->public, this->crypto, - this->server, this->peer)->handshake; + this->alert, this->server, this->peer)->handshake; } else { this->handshake = &tls_peer_create(&this->public, this->crypto, - this->peer, this->server)->handshake; + this->alert, this->peer, this->server)->handshake; } - this->fragmentation = tls_fragmentation_create(this->handshake, + this->fragmentation = tls_fragmentation_create(this->handshake, this->alert, this->application); - this->compression = tls_compression_create(this->fragmentation); - this->protection = tls_protection_create(&this->public, this->compression); + this->compression = tls_compression_create(this->fragmentation, this->alert); + this->protection = tls_protection_create(this->compression, this->alert); this->crypto->set_protection(this->crypto, this->protection); return &this->public; diff --git a/src/libtls/tls_alert.c b/src/libtls/tls_alert.c new file mode 100644 index 000000000..34f159e77 --- /dev/null +++ b/src/libtls/tls_alert.c @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "tls_alert.h" + +#include <debug.h> +#include <utils/linked_list.h> + +ENUM_BEGIN(tls_alert_desc_names, TLS_CLOSE_NOTIFY, TLS_CLOSE_NOTIFY, + "close notify", +); +ENUM_NEXT(tls_alert_desc_names, TLS_UNEXPECTED_MESSAGE, TLS_UNEXPECTED_MESSAGE, + TLS_CLOSE_NOTIFY, + "unexpected message", +); +ENUM_NEXT(tls_alert_desc_names, TLS_BAD_RECORD_MAC, TLS_RECORD_OVERFLOW, + TLS_UNEXPECTED_MESSAGE, + "bad record mac", + "decryption failed", + "record overflow", +); +ENUM_NEXT(tls_alert_desc_names, TLS_DECOMPRESSION_FAILURE, TLS_DECOMPRESSION_FAILURE, + TLS_RECORD_OVERFLOW, + "decompression_failure", +); +ENUM_NEXT(tls_alert_desc_names, TLS_HANDSHAKE_FAILURE, TLS_DECRYPT_ERROR, + TLS_DECOMPRESSION_FAILURE, + "handshake failure", + "no certificate", + "bad certificate", + "unsupported certificate", + "certificate revoked", + "certificate expired", + "certificate unknown", + "illegal parameter", + "unknown ca", + "access denied", + "decode error", + "decrypt error", +); +ENUM_NEXT(tls_alert_desc_names, TLS_EXPORT_RESTRICTION, TLS_EXPORT_RESTRICTION, + TLS_DECRYPT_ERROR, + "export restriction", +); +ENUM_NEXT(tls_alert_desc_names, TLS_PROTOCOL_VERSION, TLS_INSUFFICIENT_SECURITY, + TLS_EXPORT_RESTRICTION, + "protocol version", + "insufficient security", +); +ENUM_NEXT(tls_alert_desc_names, TLS_INTERNAL_ERROR, TLS_INTERNAL_ERROR, + TLS_INSUFFICIENT_SECURITY, + "internal error", +); +ENUM_NEXT(tls_alert_desc_names, TLS_USER_CANCELED, TLS_USER_CANCELED, + TLS_INTERNAL_ERROR, + "user canceled", +); +ENUM_NEXT(tls_alert_desc_names, TLS_NO_RENEGOTIATION, TLS_NO_RENEGOTIATION, + TLS_USER_CANCELED, + "no renegotiation", +); +ENUM_NEXT(tls_alert_desc_names, TLS_UNSUPPORTED_EXTENSION, TLS_UNSUPPORTED_EXTENSION, + TLS_NO_RENEGOTIATION, + "unsupported extension", +); +ENUM_END(tls_alert_desc_names, TLS_UNSUPPORTED_EXTENSION); + + +typedef struct private_tls_alert_t private_tls_alert_t; + +/** + * Private data of an tls_alert_t object. + */ +struct private_tls_alert_t { + + /** + * Public tls_alert_t interface. + */ + tls_alert_t public; + + /** + * Warning queue + */ + linked_list_t *warnings; + + /** + * Do we have a fatal alert? + */ + bool fatal; + + /** + * Has the fatal alert been consumed? + */ + bool consumed; + + /** + * Fatal alert discription + */ + tls_alert_desc_t desc; +}; + +METHOD(tls_alert_t, add, void, + private_tls_alert_t *this, tls_alert_level_t level, + tls_alert_desc_t desc) +{ + if (level == TLS_FATAL) + { + if (!this->fatal) + { + this->desc = desc; + this->fatal = TRUE; + } + } + else + { + this->warnings->insert_last(this->warnings, (void*)(uintptr_t)desc); + } +} + +METHOD(tls_alert_t, get, bool, + private_tls_alert_t *this, tls_alert_level_t *level, + tls_alert_desc_t *desc) +{ + if (this->fatal && !this->consumed) + { + this->consumed = TRUE; + *level = TLS_FATAL; + *desc = this->desc; + DBG1(DBG_TLS, "sending fatal TLS alert '%N'", + tls_alert_desc_names, this->desc); + return TRUE; + } + else + { + uintptr_t warning; + + if (this->warnings->remove_first(this->warnings, + (void**)&warning) == SUCCESS) + { + *level = TLS_WARNING; + *desc = warning; + DBG1(DBG_TLS, "sending TLS alert warning '%N'", + tls_alert_desc_names, warning); + return TRUE; + } + } + return FALSE; +} + +METHOD(tls_alert_t, fatal, bool, + private_tls_alert_t *this) +{ + return this->fatal; +} + +METHOD(tls_alert_t, process, status_t, + private_tls_alert_t *this, tls_alert_level_t level, + tls_alert_desc_t desc) +{ + if (desc == TLS_CLOSE_NOTIFY) + { + DBG1(DBG_TLS, "received TLS close notify"); + add(this, TLS_FATAL, TLS_CLOSE_NOTIFY); + return NEED_MORE; + } + switch (level) + { + case TLS_WARNING: + DBG1(DBG_TLS, "received TLS alert warning '%N'", + tls_alert_desc_names, desc); + return NEED_MORE; + case TLS_FATAL: + DBG1(DBG_TLS, "received fatal TLS alert '%N'", + tls_alert_desc_names, desc); + return FAILED; + default: + DBG1(DBG_TLS, "received unknown TLS alert '%N'", + tls_alert_desc_names, desc); + return FAILED; + } +} + +METHOD(tls_alert_t, destroy, void, + private_tls_alert_t *this) +{ + this->warnings->destroy(this->warnings); + free(this); +} + +/** + * See header + */ +tls_alert_t *tls_alert_create() +{ + private_tls_alert_t *this; + + INIT(this, + .public = { + .add = _add, + .get = _get, + .fatal = _fatal, + .process = _process, + .destroy = _destroy, + }, + .warnings = linked_list_create(), + ); + + return &this->public; +} diff --git a/src/libtls/tls_alert.h b/src/libtls/tls_alert.h new file mode 100644 index 000000000..95ba4d91b --- /dev/null +++ b/src/libtls/tls_alert.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup tls_alert tls_alert + * @{ @ingroup libtls + */ + +#ifndef TLS_ALERT_H_ +#define TLS_ALERT_H_ + +#include <library.h> + +typedef struct tls_alert_t tls_alert_t; +typedef enum tls_alert_level_t tls_alert_level_t; +typedef enum tls_alert_desc_t tls_alert_desc_t; + +/** + * Level of a TLS alert + */ +enum tls_alert_level_t { + TLS_WARNING = 1, + TLS_FATAL = 2, +}; + +/** + * Description of a TLS alert + */ +enum tls_alert_desc_t { + TLS_CLOSE_NOTIFY = 0, + TLS_UNEXPECTED_MESSAGE = 10, + TLS_BAD_RECORD_MAC = 20, + TLS_DECRYPTION_FAILED = 21, + TLS_RECORD_OVERFLOW = 22, + TLS_DECOMPRESSION_FAILURE = 30, + TLS_HANDSHAKE_FAILURE = 40, + TLS_NO_CERTIFICATE = 41, + TLS_BAD_CERTIFICATE = 42, + TLS_UNSUPPORTED_CERTIFICATE = 43, + TLS_CERTIFICATE_REVOKED = 44, + TLS_CERTIFICATE_EXPIRED = 45, + TLS_CERTIFICATE_UNKNOWN = 46, + TLS_ILLEGAL_PARAMETER = 47, + TLS_UNKNOWN_CA = 48, + TLS_ACCESS_DENIED = 49, + TLS_DECODE_ERROR = 50, + TLS_DECRYPT_ERROR = 51, + TLS_EXPORT_RESTRICTION = 60, + TLS_PROTOCOL_VERSION = 70, + TLS_INSUFFICIENT_SECURITY = 71, + TLS_INTERNAL_ERROR = 80, + TLS_USER_CANCELED = 90, + TLS_NO_RENEGOTIATION = 100, + TLS_UNSUPPORTED_EXTENSION = 110, +}; + +/** + * Enum names for alert descriptions + */ +extern enum_name_t *tls_alert_desc_names; + +/** + * TLS alert handling. + */ +struct tls_alert_t { + + /** + * Add an alert to the TLS alert queue, will be sent. + * + * @param level level of TLS alert + * @param description description of alert + */ + void (*add)(tls_alert_t *this, tls_alert_level_t level, + tls_alert_desc_t description); + + /** + * Get an alert pushed to the alert queue, to send. + * + * @param level receives TLS alert level + * @param description receives TLS alert description + * @return TRUE if returned an alert + */ + bool (*get)(tls_alert_t *this, tls_alert_level_t *level, + tls_alert_desc_t *description); + + /** + * Did a fatal alert occur?. + * + * @return TRUE if a fatal alert has occured + */ + bool (*fatal)(tls_alert_t *this); + + /** + * Process a received TLS alert. + * + * @param level level of received alert + * @param description alert description + * @return status to pass down to TLS stack + */ + status_t (*process)(tls_alert_t *this, tls_alert_level_t level, + tls_alert_desc_t description); + + /** + * Destroy a tls_alert_t. + */ + void (*destroy)(tls_alert_t *this); +}; + +/** + * Create a tls_alert instance. + */ +tls_alert_t *tls_alert_create(); + +#endif /** TLS_ALERT_H_ @}*/ diff --git a/src/libtls/tls_compression.c b/src/libtls/tls_compression.c index 02a3578e3..68266cd0c 100644 --- a/src/libtls/tls_compression.c +++ b/src/libtls/tls_compression.c @@ -54,7 +54,8 @@ METHOD(tls_compression_t, destroy, void, /** * See header */ -tls_compression_t *tls_compression_create(tls_fragmentation_t *fragmentation) +tls_compression_t *tls_compression_create(tls_fragmentation_t *fragmentation, + tls_alert_t *alert) { private_tls_compression_t *this; diff --git a/src/libtls/tls_compression.h b/src/libtls/tls_compression.h index bd27ab5d7..b4832ab06 100644 --- a/src/libtls/tls_compression.h +++ b/src/libtls/tls_compression.h @@ -21,13 +21,14 @@ #ifndef TLS_COMPRESSION_H_ #define TLS_COMPRESSION_H_ -typedef struct tls_compression_t tls_compression_t; - #include <library.h> #include "tls.h" +#include "tls_alert.h" #include "tls_fragmentation.h" +typedef struct tls_compression_t tls_compression_t; + /** * TLS record protocol compression layer. */ @@ -70,8 +71,10 @@ struct tls_compression_t { * Create a tls_compression instance. * * @param fragmentation fragmentation layer of TLS stack + * @param alert TLS alert handler * @return TLS compression layer. */ -tls_compression_t *tls_compression_create(tls_fragmentation_t *fragmentation); +tls_compression_t *tls_compression_create(tls_fragmentation_t *fragmentation, + tls_alert_t *alert); #endif /** TLS_COMPRESSION_H_ @}*/ diff --git a/src/libtls/tls_fragmentation.c b/src/libtls/tls_fragmentation.c index 06e1bcb53..d69ef3901 100644 --- a/src/libtls/tls_fragmentation.c +++ b/src/libtls/tls_fragmentation.c @@ -22,6 +22,18 @@ typedef struct private_tls_fragmentation_t private_tls_fragmentation_t; /** + * Alert state + */ +typedef enum { + /* no alert received/sent */ + ALERT_NONE, + /* currently sending an alert */ + ALERT_SENDING, + /* alert sent and out */ + ALERT_SENT, +} alert_state_t; + +/** * Private data of an tls_fragmentation_t object. */ struct private_tls_fragmentation_t { @@ -37,6 +49,16 @@ struct private_tls_fragmentation_t { tls_handshake_t *handshake; /** + * TLS alert handler + */ + tls_alert_t *alert; + + /** + * State of alert handling + */ + alert_state_t state; + + /** * Handshake input buffer */ chunk_t input; @@ -73,6 +95,23 @@ struct private_tls_fragmentation_t { #define MAX_TLS_HANDSHAKE_LEN 65536 /** + * Process a TLS alert + */ +static status_t process_alert(private_tls_fragmentation_t *this, + tls_reader_t *reader) +{ + u_int8_t level, description; + + if (!reader->read_uint8(reader, &level) || + !reader->read_uint8(reader, &description)) + { + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + return this->alert->process(this->alert, level, description); +} + +/** * Process TLS handshake protocol data */ static status_t process_handshake(private_tls_fragmentation_t *this, @@ -89,7 +128,8 @@ static status_t process_handshake(private_tls_fragmentation_t *this, if (reader->remaining(reader) > MAX_TLS_FRAGMENT_LEN) { DBG1(DBG_TLS, "TLS fragment has invalid length"); - return FAILED; + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; } if (this->input.len == 0) @@ -97,13 +137,16 @@ static status_t process_handshake(private_tls_fragmentation_t *this, if (!reader->read_uint8(reader, &type) || !reader->read_uint24(reader, &len)) { - return FAILED; + DBG1(DBG_TLS, "TLS handshake header invalid"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; } this->type = type; if (len > MAX_TLS_HANDSHAKE_LEN) { DBG1(DBG_TLS, "TLS handshake message exceeds maximum length"); - return FAILED; + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; } chunk_free(&this->input); this->inpos = 0; @@ -116,7 +159,9 @@ static status_t process_handshake(private_tls_fragmentation_t *this, len = min(this->input.len - this->inpos, reader->remaining(reader)); if (!reader->read_data(reader, len, &data)) { - return FAILED; + DBG1(DBG_TLS, "TLS fragment has invalid length"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; } memcpy(this->input.ptr + this->inpos, data.ptr, len); this->inpos += len; @@ -151,12 +196,14 @@ static status_t process_application(private_tls_fragmentation_t *this, if (reader->remaining(reader) > MAX_TLS_FRAGMENT_LEN) { DBG1(DBG_TLS, "TLS fragment has invalid length"); - return FAILED; + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; } status = this->application->process(this->application, reader); if (status != NEED_MORE) { - return status; + this->alert->add(this->alert, TLS_FATAL, TLS_CLOSE_NOTIFY); + return NEED_MORE; } } return NEED_MORE; @@ -168,6 +215,15 @@ METHOD(tls_fragmentation_t, process, status_t, tls_reader_t *reader; status_t status; + switch (this->state) + { + case ALERT_SENDING: + case ALERT_SENT: + /* don't accept more input, fatal error ocurred */ + return NEED_MORE; + case ALERT_NONE: + break; + } reader = tls_reader_create(data); switch (type) { @@ -180,8 +236,7 @@ METHOD(tls_fragmentation_t, process, status_t, status = FAILED; break; case TLS_ALERT: - /* TODO: handle Alert */ - status = FAILED; + status = process_alert(this, reader); break; case TLS_HANDSHAKE: status = process_handshake(this, reader); @@ -198,6 +253,29 @@ METHOD(tls_fragmentation_t, process, status_t, return status; } +/** + * Check if alerts are pending + */ +static bool check_alerts(private_tls_fragmentation_t *this, chunk_t *data) +{ + tls_alert_level_t level; + tls_alert_desc_t desc; + tls_writer_t *writer; + + if (this->alert->get(this->alert, &level, &desc)) + { + writer = tls_writer_create(2); + + writer->write_uint8(writer, level); + writer->write_uint8(writer, desc); + + *data = chunk_clone(writer->get_buf(writer)); + writer->destroy(writer); + return TRUE; + } + return FALSE; +} + METHOD(tls_fragmentation_t, build, status_t, private_tls_fragmentation_t *this, tls_content_type_t *type, chunk_t *data) { @@ -206,6 +284,22 @@ METHOD(tls_fragmentation_t, build, status_t, tls_writer_t *writer, *msg; status_t status = INVALID_STATE; + switch (this->state) + { + case ALERT_SENDING: + this->state = ALERT_SENT; + return INVALID_STATE; + case ALERT_SENT: + return FAILED; + case ALERT_NONE: + break; + } + if (check_alerts(this, data)) + { + this->state = ALERT_SENDING; + *type = TLS_ALERT; + return NEED_MORE; + } if (this->handshake->cipherspec_changed(this->handshake)) { *type = TLS_CHANGE_CIPHER_SPEC; @@ -227,6 +321,16 @@ METHOD(tls_fragmentation_t, build, status_t, *type = TLS_APPLICATION_DATA; this->output = chunk_clone(msg->get_buf(msg)); } + else if (status != NEED_MORE) + { + this->alert->add(this->alert, TLS_FATAL, TLS_CLOSE_NOTIFY); + if (check_alerts(this, data)) + { + this->state = ALERT_SENDING; + *type = TLS_ALERT; + return NEED_MORE; + } + } } } else @@ -290,7 +394,7 @@ METHOD(tls_fragmentation_t, destroy, void, * See header */ tls_fragmentation_t *tls_fragmentation_create(tls_handshake_t *handshake, - tls_application_t *application) + tls_alert_t *alert, tls_application_t *application) { private_tls_fragmentation_t *this; @@ -301,6 +405,8 @@ tls_fragmentation_t *tls_fragmentation_create(tls_handshake_t *handshake, .destroy = _destroy, }, .handshake = handshake, + .alert = alert, + .state = ALERT_NONE, .application = application, ); diff --git a/src/libtls/tls_fragmentation.h b/src/libtls/tls_fragmentation.h index 6adbc36d0..699244595 100644 --- a/src/libtls/tls_fragmentation.h +++ b/src/libtls/tls_fragmentation.h @@ -21,13 +21,13 @@ #ifndef TLS_FRAGMENTATION_H_ #define TLS_FRAGMENTATION_H_ -typedef struct tls_fragmentation_t tls_fragmentation_t; - #include <library.h> #include "tls.h" +#include "tls_alert.h" #include "tls_handshake.h" -#include "tls_handshake.h" + +typedef struct tls_fragmentation_t tls_fragmentation_t; /** * TLS record protocol fragmentation layer. @@ -71,10 +71,11 @@ struct tls_fragmentation_t { * Create a tls_fragmentation instance. * * @param handshake upper layer handshake protocol + * @param alert TLS alert handler * @param application upper layer application data or NULL - * @return TLS fragmentation layer. + * @return TLS fragmentation layer */ tls_fragmentation_t *tls_fragmentation_create(tls_handshake_t *handshake, - tls_application_t *application); + tls_alert_t *alert, tls_application_t *application); #endif /** TLS_FRAGMENTATION_H_ @}*/ diff --git a/src/libtls/tls_handshake.h b/src/libtls/tls_handshake.h index 3aab3c527..6703b341b 100644 --- a/src/libtls/tls_handshake.h +++ b/src/libtls/tls_handshake.h @@ -38,9 +38,10 @@ struct tls_handshake_t { * @param type TLS handshake message type * @param reader TLS data buffer * @return - * - SUCCESS if handshake complete - * - FAILED if handshake failed - * - NEED_MORE if another invocation of process/build needed + * - SUCCESS if TLS negotiation complete + * - FAILED if a fatal TLS alert queued + * - NEED_MORE if more invocations to process/build needed + * - DESTROY_ME if a fatal TLS alert received */ status_t (*process)(tls_handshake_t *this, tls_handshake_type_t type, tls_reader_t *reader); diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index ddd117a87..a08e411c7 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -58,6 +58,11 @@ struct private_tls_peer_t { tls_crypto_t *crypto; /** + * TLS alert handler + */ + tls_alert_t *alert; + + /** * Peer identity */ identification_t *peer; @@ -125,7 +130,8 @@ static status_t process_server_hello(private_tls_peer_t *this, (reader->remaining(reader) && !reader->read_data16(reader, &ext))) { DBG1(DBG_TLS, "received invalid ServerHello"); - return FAILED; + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; } memcpy(this->server_random, random.ptr, sizeof(this->server_random)); @@ -134,14 +140,16 @@ static status_t process_server_hello(private_tls_peer_t *this, { DBG1(DBG_TLS, "negotiated version %N not supported", tls_version_names, version); - return FAILED; + this->alert->add(this->alert, TLS_FATAL, TLS_PROTOCOL_VERSION); + return NEED_MORE; } suite = cipher; if (!this->crypto->select_cipher_suite(this->crypto, &suite, 1)) { DBG1(DBG_TLS, "received TLS cipher suite %N inacceptable", tls_cipher_suite_names, suite); - return FAILED; + this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); + return NEED_MORE; } DBG1(DBG_TLS, "negotiated TLS version %N with suite %N", tls_version_names, version, tls_cipher_suite_names, suite); @@ -165,15 +173,19 @@ static status_t process_certificate(private_tls_peer_t *this, if (!reader->read_data24(reader, &data)) { - return FAILED; + DBG1(DBG_TLS, "certificate message header invalid"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; } certs = tls_reader_create(data); while (certs->remaining(certs)) { if (!certs->read_data24(certs, &data)) { + DBG1(DBG_TLS, "certificate message invalid"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); certs->destroy(certs); - return FAILED; + return NEED_MORE; } cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, BUILD_BLOB_ASN1_DER, data, BUILD_END); @@ -198,6 +210,7 @@ static status_t process_certificate(private_tls_peer_t *this, else { DBG1(DBG_TLS, "parsing TLS certificate failed, skipped"); + this->alert->add(this->alert, TLS_WARNING, TLS_BAD_CERTIFICATE); } } certs->destroy(certs); @@ -220,27 +233,35 @@ static status_t process_certreq(private_tls_peer_t *this, tls_reader_t *reader) if (!reader->read_data8(reader, &types)) { - return FAILED; + DBG1(DBG_TLS, "certreq message header invalid"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; } if (this->tls->get_version(this->tls) >= TLS_1_2) { if (!reader->read_data16(reader, &hashsig)) { - return FAILED; + DBG1(DBG_TLS, "certreq message invalid"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; } /* TODO: store supported hashsig algorithms */ } if (!reader->read_data16(reader, &data)) { - return FAILED; + DBG1(DBG_TLS, "certreq message invalid"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; } authorities = tls_reader_create(data); while (authorities->remaining(authorities)) { if (!authorities->read_data16(authorities, &data)) { + DBG1(DBG_TLS, "certreq message invalid"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); authorities->destroy(authorities); - return FAILED; + return NEED_MORE; } id = identification_create_from_encoding(ID_DER_ASN1_DN, data); cert = lib->credmgr->get_cert(lib->credmgr, @@ -284,17 +305,20 @@ static status_t process_finished(private_tls_peer_t *this, tls_reader_t *reader) if (!reader->read_data(reader, sizeof(buf), &received)) { DBG1(DBG_TLS, "received server finished too short"); - return FAILED; + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; } if (!this->crypto->calculate_finished(this->crypto, "server finished", buf)) { DBG1(DBG_TLS, "calculating server finished failed"); - return FAILED; + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; } if (!chunk_equals(received, chunk_from_thing(buf))) { DBG1(DBG_TLS, "received server finished invalid"); - return FAILED; + this->alert->add(this->alert, TLS_FATAL, TLS_DECRYPT_ERROR); + return NEED_MORE; } this->state = STATE_COMPLETE; this->crypto->derive_eap_msk(this->crypto, @@ -348,11 +372,13 @@ METHOD(tls_handshake_t, process, status_t, default: DBG1(DBG_TLS, "TLS %N not expected in current state", tls_handshake_type_names, type); - return FAILED; + this->alert->add(this->alert, TLS_FATAL, TLS_UNEXPECTED_MESSAGE); + return NEED_MORE; } DBG1(DBG_TLS, "TLS %N expected, but received %N", tls_handshake_type_names, expected, tls_handshake_type_names, type); - return FAILED; + this->alert->add(this->alert, TLS_FATAL, TLS_UNEXPECTED_MESSAGE); + return NEED_MORE; } /** @@ -370,7 +396,9 @@ static status_t send_client_hello(private_tls_peer_t *this, rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); if (!rng) { - return FAILED; + DBG1(DBG_TLS, "no suitable RNG found to generate client random"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; } rng->get_bytes(rng, sizeof(this->client_random) - 4, this->client_random + 4); rng->destroy(rng); @@ -420,7 +448,8 @@ static status_t send_certificate(private_tls_peer_t *this, if (!this->private) { DBG1(DBG_TLS, "no TLS peer certificate found for '%Y'", this->peer); - return FAILED; + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; } /* generate certificate payload */ @@ -640,7 +669,7 @@ METHOD(tls_handshake_t, destroy, void, /** * See header */ -tls_peer_t *tls_peer_create(tls_t *tls, tls_crypto_t *crypto, +tls_peer_t *tls_peer_create(tls_t *tls, tls_crypto_t *crypto, tls_alert_t *alert, identification_t *peer, identification_t *server) { private_tls_peer_t *this; @@ -659,6 +688,7 @@ tls_peer_t *tls_peer_create(tls_t *tls, tls_crypto_t *crypto, .state = STATE_INIT, .tls = tls, .crypto = crypto, + .alert = alert, .peer = peer, .server = server, .peer_auth = auth_cfg_create(), diff --git a/src/libtls/tls_peer.h b/src/libtls/tls_peer.h index eb97c97e4..f773ea72e 100644 --- a/src/libtls/tls_peer.h +++ b/src/libtls/tls_peer.h @@ -41,8 +41,14 @@ struct tls_peer_t { /** * Create a tls_peer instance. +* + * @param tls TLS stack + * @param crypto TLS crypto helper + * @param alert TLS alert handler + * @param peer peer identity + * @param server server identity */ -tls_peer_t *tls_peer_create(tls_t *tls, tls_crypto_t *crypto, +tls_peer_t *tls_peer_create(tls_t *tls, tls_crypto_t *crypto, tls_alert_t *alert, identification_t *peer, identification_t *server); #endif /** TLS_PEER_H_ @}*/ diff --git a/src/libtls/tls_protection.c b/src/libtls/tls_protection.c index 90b30f99b..574e69167 100644 --- a/src/libtls/tls_protection.c +++ b/src/libtls/tls_protection.c @@ -30,9 +30,9 @@ struct private_tls_protection_t { tls_protection_t public; /** - * TLS context + * negotiated TLS version */ - tls_t *tls; + tls_version_t version; /** * Upper layer, TLS record compression @@ -40,6 +40,11 @@ struct private_tls_protection_t { tls_compression_t *compression; /** + * TLS alert handler + */ + tls_alert_t *alert; + + /** * RNG if we generate IVs ourself */ rng_t *rng; @@ -106,6 +111,11 @@ static chunk_t sigheader(u_int32_t seq, u_int8_t type, METHOD(tls_protection_t, process, status_t, private_tls_protection_t *this, tls_content_type_t type, chunk_t data) { + if (this->alert->fatal(this->alert)) + { /* don't accept more input, fatal error ocurred */ + return NEED_MORE; + } + if (this->crypter_in) { chunk_t iv, next_iv = chunk_empty; @@ -117,7 +127,8 @@ METHOD(tls_protection_t, process, status_t, if (data.len < bs || data.len % bs) { DBG1(DBG_TLS, "encrypted TLS record length invalid"); - return FAILED; + this->alert->add(this->alert, TLS_FATAL, TLS_BAD_RECORD_MAC); + return NEED_MORE; } iv = this->iv_in; next_iv = chunk_clone(chunk_create(data.ptr + data.len - bs, bs)); @@ -130,7 +141,8 @@ METHOD(tls_protection_t, process, status_t, if (data.len < bs || data.len % bs) { DBG1(DBG_TLS, "encrypted TLS record length invalid"); - return FAILED; + this->alert->add(this->alert, TLS_FATAL, TLS_BAD_RECORD_MAC); + return NEED_MORE; } } this->crypter_in->decrypt(this->crypter_in, data, iv, NULL); @@ -145,7 +157,8 @@ METHOD(tls_protection_t, process, status_t, if (padding_length >= data.len) { DBG1(DBG_TLS, "invalid TLS record padding"); - return FAILED; + this->alert->add(this->alert, TLS_FATAL, TLS_BAD_RECORD_MAC); + return NEED_MORE; } data.len -= padding_length + 1; } @@ -158,19 +171,20 @@ METHOD(tls_protection_t, process, status_t, if (data.len <= bs) { DBG1(DBG_TLS, "TLS record too short to verify MAC"); - return FAILED; + this->alert->add(this->alert, TLS_FATAL, TLS_BAD_RECORD_MAC); + return NEED_MORE; } mac = chunk_skip(data, data.len - bs); data.len -= bs; - header = sigheader(this->seq_in, type, - this->tls->get_version(this->tls), data.len); + header = sigheader(this->seq_in, type, this->version, data.len); macdata = chunk_cat("mc", header, data); if (!this->signer_in->verify_signature(this->signer_in, macdata, mac)) { DBG1(DBG_TLS, "TLS record MAC verification failed"); free(macdata.ptr); - return FAILED; + this->alert->add(this->alert, TLS_FATAL, TLS_BAD_RECORD_MAC); + return NEED_MORE; } free(macdata.ptr); } @@ -204,8 +218,7 @@ METHOD(tls_protection_t, build, status_t, { chunk_t mac, header; - header = sigheader(this->seq_out, *type, - this->tls->get_version(this->tls), data->len); + header = sigheader(this->seq_out, *type, this->version, data->len); this->signer_out->get_signature(this->signer_out, header, NULL); free(header.ptr); this->signer_out->allocate_signature(this->signer_out, *data, &mac); @@ -283,6 +296,12 @@ METHOD(tls_protection_t, set_cipher, void, } } +METHOD(tls_protection_t, set_version, void, + private_tls_protection_t *this, tls_version_t version) +{ + this->version = version; +} + METHOD(tls_protection_t, destroy, void, private_tls_protection_t *this) { @@ -293,8 +312,8 @@ METHOD(tls_protection_t, destroy, void, /** * See header */ -tls_protection_t *tls_protection_create(tls_t *tls, - tls_compression_t *compression) +tls_protection_t *tls_protection_create(tls_compression_t *compression, + tls_alert_t *alert) { private_tls_protection_t *this; @@ -303,9 +322,10 @@ tls_protection_t *tls_protection_create(tls_t *tls, .process = _process, .build = _build, .set_cipher = _set_cipher, + .set_version = _set_version, .destroy = _destroy, }, - .tls = tls, + .alert = alert, .compression = compression, ); diff --git a/src/libtls/tls_protection.h b/src/libtls/tls_protection.h index aa7681bd5..99c94e935 100644 --- a/src/libtls/tls_protection.h +++ b/src/libtls/tls_protection.h @@ -21,13 +21,14 @@ #ifndef TLS_PROTECTION_H_ #define TLS_PROTECTION_H_ -typedef struct tls_protection_t tls_protection_t; - #include <library.h> #include "tls.h" +#include "tls_alert.h" #include "tls_compression.h" +typedef struct tls_protection_t tls_protection_t; + /** * TLS record protocol protection layer. */ @@ -72,6 +73,13 @@ struct tls_protection_t { crypter_t *crypter, chunk_t iv); /** + * Set the TLS version negotiated, used for MAC calculation. + * + * @param version TLS version negotiated + */ + void (*set_version)(tls_protection_t *this, tls_version_t version); + + /** * Destroy a tls_protection_t. */ void (*destroy)(tls_protection_t *this); @@ -80,11 +88,11 @@ struct tls_protection_t { /** * Create a tls_protection instance. * - * @param tls TLS context * @param compression compression layer of TLS stack + * @param alert TLS alert handler * @return TLS protection layer. */ -tls_protection_t *tls_protection_create(tls_t *tls, - tls_compression_t *compression); +tls_protection_t *tls_protection_create(tls_compression_t *compression, + tls_alert_t *alert); #endif /** TLS_PROTECTION_H_ @}*/ diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index 3248a0c1a..4f988c603 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -60,6 +60,11 @@ struct private_tls_server_t { tls_crypto_t *crypto; /** + * TLS alert handler + */ + tls_alert_t *alert; + + /** * Server identity */ identification_t *server; @@ -132,7 +137,8 @@ static status_t process_client_hello(private_tls_server_t *this, (reader->remaining(reader) && !reader->read_data16(reader, &ext))) { DBG1(DBG_TLS, "received invalid ClientHello"); - return FAILED; + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; } memcpy(this->client_random, random.ptr, sizeof(this->client_random)); @@ -141,7 +147,8 @@ static status_t process_client_hello(private_tls_server_t *this, { DBG1(DBG_TLS, "negotiated version %N not supported", tls_version_names, version); - return FAILED; + this->alert->add(this->alert, TLS_FATAL, TLS_PROTOCOL_VERSION); + return NEED_MORE; } count = ciphers.len / sizeof(u_int16_t); suites = alloca(count * sizeof(tls_cipher_suite_t)); @@ -155,7 +162,8 @@ static status_t process_client_hello(private_tls_server_t *this, if (!this->suite) { DBG1(DBG_TLS, "received cipher suites inacceptable"); - return FAILED; + this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); + return NEED_MORE; } DBG1(DBG_TLS, "negotiated TLS version %N with suite %N", tls_version_names, version, tls_cipher_suite_names, this->suite); @@ -179,15 +187,19 @@ static status_t process_certificate(private_tls_server_t *this, if (!reader->read_data24(reader, &data)) { - return FAILED; + DBG1(DBG_TLS, "certificate message header invalid"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; } certs = tls_reader_create(data); while (certs->remaining(certs)) { if (!certs->read_data24(certs, &data)) { + DBG1(DBG_TLS, "certificate message invalid"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); certs->destroy(certs); - return FAILED; + return NEED_MORE; } cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, BUILD_BLOB_ASN1_DER, data, BUILD_END); @@ -211,6 +223,7 @@ static status_t process_certificate(private_tls_server_t *this, else { DBG1(DBG_TLS, "parsing TLS certificate failed, skipped"); + this->alert->add(this->alert, TLS_WARNING, TLS_BAD_CERTIFICATE); } } certs->destroy(certs); @@ -232,7 +245,8 @@ static status_t process_key_exchange(private_tls_server_t *this, if (!reader->read_data16(reader, &encrypted)) { DBG1(DBG_TLS, "received invalid Client Key Exchange"); - return FAILED; + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; } if (!this->private || @@ -240,7 +254,8 @@ static status_t process_key_exchange(private_tls_server_t *this, encrypted, &premaster)) { DBG1(DBG_TLS, "decrypting Client Key Exchange data failed"); - return FAILED; + this->alert->add(this->alert, TLS_FATAL, TLS_DECRYPT_ERROR); + return NEED_MORE; } this->crypto->derive_secrets(this->crypto, premaster, chunk_from_thing(this->client_random), @@ -282,7 +297,8 @@ static status_t process_cert_verify(private_tls_server_t *this, { DBG1(DBG_TLS, "no trusted certificate found for '%Y' to verify TLS peer", this->peer); - return FAILED; + this->alert->add(this->alert, TLS_FATAL, TLS_CERTIFICATE_UNKNOWN); + return NEED_MORE; } this->crypto->append_handshake(this->crypto, @@ -303,17 +319,20 @@ static status_t process_finished(private_tls_server_t *this, if (!reader->read_data(reader, sizeof(buf), &received)) { DBG1(DBG_TLS, "received client finished too short"); - return FAILED; + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; } if (!this->crypto->calculate_finished(this->crypto, "client finished", buf)) { DBG1(DBG_TLS, "calculating client finished failed"); - return FAILED; + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; } if (!chunk_equals(received, chunk_from_thing(buf))) { DBG1(DBG_TLS, "received client finished invalid"); - return FAILED; + this->alert->add(this->alert, TLS_FATAL, TLS_DECRYPT_ERROR); + return NEED_MORE; } this->crypto->append_handshake(this->crypto, TLS_FINISHED, received); @@ -377,11 +396,13 @@ METHOD(tls_handshake_t, process, status_t, default: DBG1(DBG_TLS, "TLS %N not expected in current state", tls_handshake_type_names, type); - return FAILED; + this->alert->add(this->alert, TLS_FATAL, TLS_UNEXPECTED_MESSAGE); + return NEED_MORE; } DBG1(DBG_TLS, "TLS %N expected, but received %N", tls_handshake_type_names, expected, tls_handshake_type_names, type); - return FAILED; + this->alert->add(this->alert, TLS_FATAL, TLS_UNEXPECTED_MESSAGE); + return NEED_MORE; } /** @@ -397,6 +418,7 @@ static status_t send_server_hello(private_tls_server_t *this, rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); if (!rng) { + DBG1(DBG_TLS, "no suitable RNG found to generate server random"); return FAILED; } rng->get_bytes(rng, sizeof(this->server_random) - 4, this->server_random + 4); @@ -630,8 +652,9 @@ METHOD(tls_handshake_t, destroy, void, /** * See header */ -tls_server_t *tls_server_create(tls_t *tls, tls_crypto_t *crypto, - identification_t *server, identification_t *peer) +tls_server_t *tls_server_create(tls_t *tls, + tls_crypto_t *crypto, tls_alert_t *alert, + identification_t *server, identification_t *peer) { private_tls_server_t *this; @@ -648,6 +671,7 @@ tls_server_t *tls_server_create(tls_t *tls, tls_crypto_t *crypto, }, .tls = tls, .crypto = crypto, + .alert = alert, .server = server, .peer = peer, .state = STATE_INIT, diff --git a/src/libtls/tls_server.h b/src/libtls/tls_server.h index 6dc26cd3f..6289dc8eb 100644 --- a/src/libtls/tls_server.h +++ b/src/libtls/tls_server.h @@ -41,8 +41,15 @@ struct tls_server_t { /** * Create a tls_server instance. + * + * @param tls TLS stack + * @param crypto TLS crypto helper + * @param alert TLS alert handler + * @param server server identity + * @param peer peer identity */ -tls_server_t *tls_server_create(tls_t *tls, tls_crypto_t *crypto, - identification_t *server, identification_t *peer); +tls_server_t *tls_server_create(tls_t *tls, + tls_crypto_t *crypto, tls_alert_t *alert, + identification_t *server, identification_t *peer); #endif /** TLS_SERVER_H_ @}*/ |