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 \
*/
tls_fragmentation_t *fragmentation;
+ /**
+ * TLS alert handler
+ */
+ tls_alert_t *alert;
+
/**
* TLS crypto helper context
*/
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:
this->peer->destroy(this->peer);
this->server->destroy(this->server);
DESTROY_IF(this->application);
+ this->alert->destroy(this->alert);
free(this);
}
);
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;
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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_ @}*/
/**
* 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;
#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.
*/
* 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_ @}*/
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.
*/
*/
tls_handshake_t *handshake;
+ /**
+ * TLS alert handler
+ */
+ tls_alert_t *alert;
+
+ /**
+ * State of alert handling
+ */
+ alert_state_t state;
+
/**
* Handshake input buffer
*/
*/
#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
*/
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)
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;
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;
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;
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)
{
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);
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)
{
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;
*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
* 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;
.destroy = _destroy,
},
.handshake = handshake,
+ .alert = alert,
+ .state = ALERT_NONE,
.application = application,
);
#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.
* 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_ @}*/
* @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);
*/
tls_crypto_t *crypto;
+ /**
+ * TLS alert handler
+ */
+ tls_alert_t *alert;
+
/**
* Peer identity
*/
(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));
{
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);
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);
else
{
DBG1(DBG_TLS, "parsing TLS certificate failed, skipped");
+ this->alert->add(this->alert, TLS_WARNING, TLS_BAD_CERTIFICATE);
}
}
certs->destroy(certs);
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,
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,
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;
}
/**
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);
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 */
/**
* 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;
.state = STATE_INIT,
.tls = tls,
.crypto = crypto,
+ .alert = alert,
.peer = peer,
.server = server,
.peer_auth = auth_cfg_create(),
/**
* 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_ @}*/
tls_protection_t public;
/**
- * TLS context
+ * negotiated TLS version
*/
- tls_t *tls;
+ tls_version_t version;
/**
* Upper layer, TLS record compression
*/
tls_compression_t *compression;
+ /**
+ * TLS alert handler
+ */
+ tls_alert_t *alert;
+
/**
* RNG if we generate IVs ourself
*/
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;
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));
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);
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;
}
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);
}
{
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);
}
}
+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)
{
/**
* 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;
.process = _process,
.build = _build,
.set_cipher = _set_cipher,
+ .set_version = _set_version,
.destroy = _destroy,
},
- .tls = tls,
+ .alert = alert,
.compression = compression,
);
#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.
*/
void (*set_cipher)(tls_protection_t *this, bool inbound, signer_t *signer,
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.
*/
/**
* 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_ @}*/
*/
tls_crypto_t *crypto;
+ /**
+ * TLS alert handler
+ */
+ tls_alert_t *alert;
+
/**
* Server identity
*/
(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));
{
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));
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);
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);
else
{
DBG1(DBG_TLS, "parsing TLS certificate failed, skipped");
+ this->alert->add(this->alert, TLS_WARNING, TLS_BAD_CERTIFICATE);
}
}
certs->destroy(certs);
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 ||
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),
{
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,
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);
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;
}
/**
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);
/**
* 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;
},
.tls = tls,
.crypto = crypto,
+ .alert = alert,
.server = server,
.peer = peer,
.state = STATE_INIT,
/**
* 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_ @}*/