]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
eap-tls: Add support for TLS 1.3
authorTobias Brunner <tobias@strongswan.org>
Tue, 17 Jan 2023 14:06:40 +0000 (15:06 +0100)
committerTobias Brunner <tobias@strongswan.org>
Wed, 22 Feb 2023 12:34:53 +0000 (13:34 +0100)
As defined in RFC 9190, a "protected success indication" (0x00) is sent
from the server to the client over the TLS connection when using TLS 1.3.

The client responds with an empty EAP message, which is interpreted as
acknowledgement in our stack.

If we ever support session resumption with tunneled methods such as
EAP-TTLS, we'd have to send such an indication there too.

src/libcharon/plugins/eap_tls/eap_tls.c
src/libtls/tls_eap.c

index 3d34d7a840684b41667b99f505940c6096d9aeae..575488961e6057b96d91009ae7efc2cdb37f28ff 100644 (file)
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2023 Tobias Brunner
  * Copyright (C) 2010 Martin Willi
  *
  * Copyright (C) secunet Security Networks AG
@@ -34,9 +35,20 @@ struct private_eap_tls_t {
        eap_tls_t public;
 
        /**
-        * TLS stack, wrapped by EAP helper
+        * TLS stack, wrapped by EAP helper below
+        */
+       tls_t *tls;
+
+       /**
+        * EAP helper
         */
        tls_eap_t *tls_eap;
+
+       /**
+        * Whether the "protected success indication" has been sent/received with
+        * TLS 1.3
+        */
+       bool indication_sent_received;
 };
 
 /** Maximum number of EAP-TLS messages/fragments allowed */
@@ -84,10 +96,19 @@ METHOD(eap_method_t, get_type, eap_type_t,
 METHOD(eap_method_t, get_msk, status_t,
        private_eap_tls_t *this, chunk_t *msk)
 {
-       *msk = this->tls_eap->get_msk(this->tls_eap);
-       if (msk->len)
+       if (this->tls->get_version_max(this->tls) < TLS_1_3 ||
+               this->indication_sent_received)
        {
-               return SUCCESS;
+               *msk = this->tls_eap->get_msk(this->tls_eap);
+               if (msk->len)
+               {
+                       return SUCCESS;
+               }
+       }
+       else
+       {
+               DBG1(DBG_TLS, "missing protected success indication for EAP-TLS with "
+                        "%N", tls_version_names, this->tls->get_version_max(this->tls));
        }
        return FAILED;
 }
@@ -123,6 +144,123 @@ METHOD(eap_method_t, destroy, void,
        free(this);
 }
 
+/**
+ * Application to send/process the "protected success indication" with TLS 1.3
+ * as specified in RFC 9190
+ */
+typedef struct {
+
+       /**
+        * Public interface
+        */
+       tls_application_t public;
+
+       /**
+        * Reference to the EAP-TLS object
+        */
+       private_eap_tls_t *this;
+
+       /**
+        * Whether the server sent the indication
+        */
+       bool indication_sent;
+
+} eap_tls_app_t;
+
+METHOD(tls_application_t, server_process, status_t,
+       eap_tls_app_t *app, bio_reader_t *reader)
+{
+       /* we don't expect any data from the client, the empty response to our
+        * indication is handled as ACK in tls_eap_t */
+       DBG1(DBG_TLS, "peer sent unexpected TLS data");
+       return FAILED;
+}
+
+METHOD(tls_application_t, server_build, status_t,
+       eap_tls_app_t *app, bio_writer_t *writer)
+{
+       if (app->this->tls->get_version_max(app->this->tls) < TLS_1_3 ||
+               app->this->indication_sent_received)
+       {
+               return SUCCESS;
+       }
+       /* build() is called twice when sending the indication, return the same
+        * status but data only once */
+       if (app->indication_sent)
+       {
+               app->this->indication_sent_received = TRUE;
+       }
+       else
+       {       /* send a single 0x00 */
+               DBG2(DBG_TLS, "sending protected success indication via TLS");
+               writer->write_uint8(writer, 0);
+               app->indication_sent = TRUE;
+       }
+       return INVALID_STATE;
+}
+
+METHOD(tls_application_t, client_process, status_t,
+       eap_tls_app_t *app, bio_reader_t *reader)
+{
+       uint8_t indication;
+
+       if (app->this->tls->get_version_max(app->this->tls) < TLS_1_3 ||
+               app->this->indication_sent_received)
+       {
+               DBG1(DBG_TLS, "peer sent unexpected TLS data");
+               return FAILED;
+       }
+       if (!reader->read_uint8(reader, &indication) || indication != 0)
+       {
+               DBG1(DBG_TLS, "received incorrect protected success indication via TLS");
+               return FAILED;
+       }
+       DBG2(DBG_TLS, "received protected success indication via TLS");
+       app->this->indication_sent_received = TRUE;
+       return NEED_MORE;
+}
+
+METHOD(tls_application_t, client_build, status_t,
+       eap_tls_app_t *app, bio_writer_t *writer)
+{
+       if (app->this->tls->get_version_max(app->this->tls) < TLS_1_3 ||
+               app->this->indication_sent_received)
+       {       /* trigger an empty response/ACK */
+               return INVALID_STATE;
+       }
+       return FAILED;
+}
+
+METHOD(tls_application_t, app_destroy, void,
+       eap_tls_app_t *this)
+{
+       free(this);
+}
+
+/**
+ * Create the server/peer implementation to handle the "protected success
+ * indication" with TLS 1.3
+ */
+tls_application_t *eap_tls_app_create(private_eap_tls_t *this, bool is_server)
+{
+       eap_tls_app_t *app;
+
+       INIT(app,
+               .public = {
+                       .process = _client_process,
+                       .build = _client_build,
+                       .destroy = _app_destroy,
+               },
+               .this = this,
+       );
+       if (is_server)
+       {
+               app->public.process = _server_process;
+               app->public.build = _server_build;
+       }
+       return &app->public;
+}
+
 /**
  * Generic private constructor
  */
@@ -130,10 +268,10 @@ static eap_tls_t *eap_tls_create(identification_t *server,
                                                                 identification_t *peer, bool is_server)
 {
        private_eap_tls_t *this;
+       tls_application_t *app;
        size_t frag_size;
        int max_msg_count;
        bool include_length;
-       tls_t *tls;
 
        INIT(this,
                .public = {
@@ -159,9 +297,11 @@ static eap_tls_t *eap_tls_create(identification_t *server,
                                        lib->ns);
        include_length = lib->settings->get_bool(lib->settings,
                                        "%s.plugins.eap-tls.include_length", TRUE, lib->ns);
-       tls = tls_create(is_server, server, peer, TLS_PURPOSE_EAP_TLS, NULL, NULL, 0);
-       this->tls_eap = tls_eap_create(EAP_TLS, tls, frag_size, max_msg_count,
-                                                                                                include_length);
+       app = eap_tls_app_create(this, is_server);
+       this->tls = tls_create(is_server, server, peer, TLS_PURPOSE_EAP_TLS, app,
+                                                  NULL, 0);
+       this->tls_eap = tls_eap_create(EAP_TLS, this->tls, frag_size, max_msg_count,
+                                                                  include_length);
        if (!this->tls_eap)
        {
                free(this);
index a0e5d5be78055ea92a4bb521440370c13c3e8f48..4b16504b557544b69646e945540b617198099eed 100644 (file)
@@ -369,6 +369,9 @@ METHOD(tls_eap_t, process, status_t,
        }
        else
        {
+               /* note that with TLS 1.3 the client sends an empty EAP packet after the
+                * server sent the "protected success indication" over the TLS
+                * connection, which is interpreted here as an ACK packet */
                if (in.len == sizeof(eap_tls_packet_t))
                {
                        DBG2(DBG_TLS, "received %N acknowledgment packet",