Skip to content

Commit

Permalink
eap-tls: Add support for TLS 1.3
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
tobiasbrunner committed Feb 22, 2023
1 parent 06abdf1 commit 5401a74
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 8 deletions.
156 changes: 148 additions & 8 deletions src/libcharon/plugins/eap_tls/eap_tls.c
@@ -1,4 +1,5 @@
/*
* Copyright (C) 2023 Tobias Brunner
* Copyright (C) 2010 Martin Willi
*
* Copyright (C) secunet Security Networks AG
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -123,17 +144,134 @@ 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
*/
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 = {
Expand All @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions src/libtls/tls_eap.c
Expand Up @@ -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",
Expand Down

0 comments on commit 5401a74

Please sign in to comment.