Implemented TLS Alert handling
authorMartin Willi <martin@revosec.ch>
Mon, 23 Aug 2010 12:22:38 +0000 (14:22 +0200)
committerMartin Willi <martin@revosec.ch>
Mon, 23 Aug 2010 13:13:37 +0000 (15:13 +0200)
15 files changed:
src/libtls/Makefile.am
src/libtls/tls.c
src/libtls/tls_alert.c [new file with mode: 0644]
src/libtls/tls_alert.h [new file with mode: 0644]
src/libtls/tls_compression.c
src/libtls/tls_compression.h
src/libtls/tls_fragmentation.c
src/libtls/tls_fragmentation.h
src/libtls/tls_handshake.h
src/libtls/tls_peer.c
src/libtls/tls_peer.h
src/libtls/tls_protection.c
src/libtls/tls_protection.h
src/libtls/tls_server.c
src/libtls/tls_server.h

index 5e112f6..ff11ef4 100644 (file)
@@ -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 \
index d46ce00..42f71d7 100644 (file)
@@ -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 (file)
index 0000000..34f159e
--- /dev/null
@@ -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 (file)
index 0000000..95ba4d9
--- /dev/null
@@ -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_ @}*/
index 02a3578..68266cd 100644 (file)
@@ -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;
 
index bd27ab5..b4832ab 100644 (file)
 #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_ @}*/
index 06e1bcb..d69ef39 100644 (file)
 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,
        );
 
index 6adbc36..6992445 100644 (file)
 #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_ @}*/
index 3aab3c5..6703b34 100644 (file)
@@ -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);
index ddd117a..a08e411 100644 (file)
@@ -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(),
index eb97c97..f773ea7 100644 (file)
@@ -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_ @}*/
index 90b30f9..574e691 100644 (file)
@@ -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,
        );
 
index aa7681b..99c94e9 100644 (file)
 #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_ @}*/
index 3248a0c..4f988c6 100644 (file)
@@ -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,
index 6dc26cd..6289dc8 100644 (file)
@@ -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_ @}*/