Implemented a generic TLS EAP helper to implement EAP-TLS, TTLS and other variants
This commit is contained in:
parent
ecd98efa9d
commit
877c910f04
|
@ -12,6 +12,7 @@ libtls_la_SOURCES = \
|
|||
tls_reader.h tls_reader.c \
|
||||
tls_writer.h tls_writer.c \
|
||||
tls_socket.h tls_socket.c \
|
||||
tls_eap.h tls_eap.c \
|
||||
tls_peer.h tls_peer.c \
|
||||
tls_server.h tls_server.c \
|
||||
tls_handshake.h tls_application.h tls.h tls.c
|
||||
|
|
|
@ -0,0 +1,331 @@
|
|||
/*
|
||||
* 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_eap.h"
|
||||
|
||||
#include "tls.h"
|
||||
|
||||
#include <debug.h>
|
||||
#include <library.h>
|
||||
|
||||
/** Size limit for a single TLS message */
|
||||
#define MAX_TLS_MESSAGE_LEN 65536
|
||||
/** Size of a EAP-TLS fragment */
|
||||
#define EAP_TLS_FRAGMENT_LEN 1014
|
||||
|
||||
typedef struct private_tls_eap_t private_tls_eap_t;
|
||||
|
||||
/**
|
||||
* Private data of an tls_eap_t object.
|
||||
*/
|
||||
struct private_tls_eap_t {
|
||||
|
||||
/**
|
||||
* Public tls_eap_t interface.
|
||||
*/
|
||||
tls_eap_t public;
|
||||
|
||||
/**
|
||||
* Type of EAP method, EAP-TLS or EAP-TTLS
|
||||
*/
|
||||
eap_type_t type;
|
||||
|
||||
/**
|
||||
* TLS stack
|
||||
*/
|
||||
tls_t *tls;
|
||||
|
||||
/**
|
||||
* Role
|
||||
*/
|
||||
bool is_server;
|
||||
|
||||
/**
|
||||
* First fragment of a multi-fragment record?
|
||||
*/
|
||||
bool first_fragment;
|
||||
};
|
||||
|
||||
/**
|
||||
* Flags of an EAP-TLS/TTLS message
|
||||
*/
|
||||
typedef enum {
|
||||
EAP_TLS_LENGTH = (1<<7),
|
||||
EAP_TLS_MORE_FRAGS = (1<<6),
|
||||
EAP_TLS_START = (1<<5),
|
||||
EAP_TTLS_VERSION = (0x07),
|
||||
} eap_tls_flags_t;
|
||||
|
||||
/**
|
||||
* EAP-TLS/TTLS packet format
|
||||
*/
|
||||
typedef struct __attribute__((packed)) {
|
||||
u_int8_t code;
|
||||
u_int8_t identifier;
|
||||
u_int16_t length;
|
||||
u_int8_t type;
|
||||
u_int8_t flags;
|
||||
} eap_tls_packet_t;
|
||||
|
||||
METHOD(tls_eap_t, initiate, status_t,
|
||||
private_tls_eap_t *this, chunk_t *out)
|
||||
{
|
||||
if (this->is_server)
|
||||
{
|
||||
eap_tls_packet_t pkt = {
|
||||
.type = this->type,
|
||||
.code = EAP_REQUEST,
|
||||
.flags = EAP_TLS_START,
|
||||
};
|
||||
htoun16(&pkt.length, sizeof(eap_tls_packet_t));
|
||||
do
|
||||
{ /* start with non-zero random identifier */
|
||||
pkt.identifier = random();
|
||||
}
|
||||
while (!pkt.identifier);
|
||||
|
||||
DBG2(DBG_IKE, "sending %N start packet", eap_type_names, this->type);
|
||||
*out = chunk_clone(chunk_from_thing(pkt));
|
||||
return NEED_MORE;
|
||||
}
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a received packet
|
||||
*/
|
||||
static status_t process_pkt(private_tls_eap_t *this, eap_tls_packet_t *pkt)
|
||||
{
|
||||
u_int32_t msg_len;
|
||||
u_int16_t pkt_len;
|
||||
|
||||
pkt_len = untoh16(&pkt->length);
|
||||
if (pkt->flags & EAP_TLS_LENGTH)
|
||||
{
|
||||
if (pkt_len < sizeof(eap_tls_packet_t) + sizeof(msg_len))
|
||||
{
|
||||
DBG1(DBG_TLS, "%N packet too short", eap_type_names, this->type);
|
||||
return FAILED;
|
||||
}
|
||||
msg_len = untoh32(pkt + 1);
|
||||
if (msg_len < pkt_len - sizeof(eap_tls_packet_t) - sizeof(msg_len) ||
|
||||
msg_len > MAX_TLS_MESSAGE_LEN)
|
||||
{
|
||||
DBG1(DBG_TLS, "invalid %N packet length", eap_type_names, this->type);
|
||||
return FAILED;
|
||||
}
|
||||
return this->tls->process(this->tls, (char*)(pkt + 1) + sizeof(msg_len),
|
||||
pkt_len - sizeof(eap_tls_packet_t) - sizeof(msg_len));
|
||||
}
|
||||
return this->tls->process(this->tls, (char*)(pkt + 1),
|
||||
pkt_len - sizeof(eap_tls_packet_t));
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a packet to send
|
||||
*/
|
||||
static status_t build_pkt(private_tls_eap_t *this,
|
||||
u_int8_t identifier, chunk_t *out)
|
||||
{
|
||||
char buf[EAP_TLS_FRAGMENT_LEN];
|
||||
eap_tls_packet_t *pkt;
|
||||
size_t len, reclen;
|
||||
status_t status;
|
||||
char *kind;
|
||||
|
||||
pkt = (eap_tls_packet_t*)buf;
|
||||
pkt->code = this->is_server ? EAP_REQUEST : EAP_RESPONSE;
|
||||
pkt->identifier = this->is_server ? identifier + 1 : identifier;
|
||||
pkt->type = this->type;
|
||||
pkt->flags = 0;
|
||||
|
||||
if (this->first_fragment)
|
||||
{
|
||||
pkt->flags = EAP_TLS_LENGTH;
|
||||
len = sizeof(buf) - sizeof(eap_tls_packet_t) - sizeof(u_int32_t);
|
||||
status = this->tls->build(this->tls, buf + sizeof(eap_tls_packet_t) +
|
||||
sizeof(u_int32_t), &len, &reclen);
|
||||
}
|
||||
else
|
||||
{
|
||||
len = sizeof(buf) - sizeof(eap_tls_packet_t);
|
||||
status = this->tls->build(this->tls, buf + sizeof(eap_tls_packet_t),
|
||||
&len, &reclen);
|
||||
}
|
||||
switch (status)
|
||||
{
|
||||
case NEED_MORE:
|
||||
pkt->flags |= EAP_TLS_MORE_FRAGS;
|
||||
kind = "non-first fragment";
|
||||
if (this->first_fragment)
|
||||
{
|
||||
this->first_fragment = FALSE;
|
||||
kind = "first fragment";
|
||||
}
|
||||
break;
|
||||
case ALREADY_DONE:
|
||||
kind = "packet";
|
||||
if (!this->first_fragment)
|
||||
{
|
||||
this->first_fragment = TRUE;
|
||||
kind = "final fragment";
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return status;
|
||||
}
|
||||
DBG2(DBG_TLS, "sending %N %s (%u bytes)",
|
||||
eap_type_names, this->type, kind, len);
|
||||
if (reclen)
|
||||
{
|
||||
htoun32(pkt + 1, reclen);
|
||||
len += sizeof(u_int32_t);
|
||||
pkt->flags |= EAP_TLS_LENGTH;
|
||||
}
|
||||
len += sizeof(eap_tls_packet_t);
|
||||
htoun16(&pkt->length, len);
|
||||
*out = chunk_clone(chunk_create(buf, len));
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an ack to request next fragment
|
||||
*/
|
||||
static chunk_t create_ack(private_tls_eap_t *this, u_int8_t identifier)
|
||||
{
|
||||
eap_tls_packet_t pkt = {
|
||||
.code = this->is_server ? EAP_REQUEST : EAP_RESPONSE,
|
||||
.identifier = this->is_server ? identifier + 1 : identifier,
|
||||
.type = this->type,
|
||||
};
|
||||
htoun16(&pkt.length, sizeof(pkt));
|
||||
DBG2(DBG_TLS, "sending %N acknowledgement packet",
|
||||
eap_type_names, this->type);
|
||||
return chunk_clone(chunk_from_thing(pkt));
|
||||
}
|
||||
|
||||
METHOD(tls_eap_t, process, status_t,
|
||||
private_tls_eap_t *this, chunk_t in, chunk_t *out)
|
||||
{
|
||||
eap_tls_packet_t *pkt;
|
||||
status_t status;
|
||||
|
||||
pkt = (eap_tls_packet_t*)in.ptr;
|
||||
if (in.len < sizeof(eap_tls_packet_t) ||
|
||||
untoh16(&pkt->length) != in.len)
|
||||
{
|
||||
DBG1(DBG_IKE, "invalid EAP-TLS packet length");
|
||||
return FAILED;
|
||||
}
|
||||
if (pkt->flags & EAP_TLS_START)
|
||||
{
|
||||
if (this->type == EAP_TTLS)
|
||||
{
|
||||
DBG1(DBG_TLS, "EAP-TTLS version is v%u",
|
||||
pkt->flags & EAP_TTLS_VERSION);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (in.len == sizeof(eap_tls_packet_t))
|
||||
{
|
||||
DBG2(DBG_TLS, "received %N acknowledgement packet",
|
||||
eap_type_names, this->type);
|
||||
status = build_pkt(this, pkt->identifier, out);
|
||||
if (status == INVALID_STATE &&
|
||||
this->tls->is_complete(this->tls))
|
||||
{
|
||||
return SUCCESS;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
status = process_pkt(this, pkt);
|
||||
if (status != NEED_MORE)
|
||||
{
|
||||
return status;
|
||||
}
|
||||
}
|
||||
status = build_pkt(this, pkt->identifier, out);
|
||||
switch (status)
|
||||
{
|
||||
case INVALID_STATE:
|
||||
*out = create_ack(this, pkt->identifier);
|
||||
return NEED_MORE;
|
||||
case FAILED:
|
||||
if (!this->is_server)
|
||||
{
|
||||
*out = create_ack(this, pkt->identifier);
|
||||
return NEED_MORE;
|
||||
}
|
||||
return FAILED;
|
||||
default:
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
METHOD(tls_eap_t, get_msk, chunk_t,
|
||||
private_tls_eap_t *this)
|
||||
{
|
||||
return this->tls->get_eap_msk(this->tls);
|
||||
}
|
||||
|
||||
METHOD(tls_eap_t, destroy, void,
|
||||
private_tls_eap_t *this)
|
||||
{
|
||||
this->tls->destroy(this->tls);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
tls_eap_t *tls_eap_create(eap_type_t type, bool is_server,
|
||||
identification_t *server, identification_t *peer,
|
||||
tls_application_t *application)
|
||||
{
|
||||
private_tls_eap_t *this;
|
||||
tls_purpose_t purpose;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case EAP_TLS:
|
||||
purpose = TLS_PURPOSE_EAP_TLS;
|
||||
break;
|
||||
case EAP_TTLS:
|
||||
purpose = TLS_PURPOSE_EAP_TTLS;
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
};
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.initiate = _initiate,
|
||||
.process = _process,
|
||||
.get_msk = _get_msk,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.type = type,
|
||||
.is_server = is_server,
|
||||
.first_fragment = TRUE,
|
||||
.tls = tls_create(is_server, server, peer, purpose, application),
|
||||
);
|
||||
if (!this->tls)
|
||||
{
|
||||
free(this);
|
||||
return NULL;
|
||||
}
|
||||
return &this->public;
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* 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_eap tls_eap
|
||||
* @{ @ingroup libtls
|
||||
*/
|
||||
|
||||
#ifndef TLS_EAP_H_
|
||||
#define TLS_EAP_H_
|
||||
|
||||
typedef struct tls_eap_t tls_eap_t;
|
||||
|
||||
#include <eap/eap.h>
|
||||
|
||||
#include "tls_application.h"
|
||||
|
||||
/**
|
||||
* TLS over EAP helper, as used by EAP-TLS and EAP-TTLS.
|
||||
*/
|
||||
struct tls_eap_t {
|
||||
|
||||
/**
|
||||
* Initiate TLS over EAP exchange (as client).
|
||||
*
|
||||
* @param out allocated EAP packet data to send
|
||||
* @return
|
||||
* - NEED_MORE if more exchanges required
|
||||
* - FAILED if initiation failed
|
||||
*/
|
||||
status_t (*initiate)(tls_eap_t *this, chunk_t *out);
|
||||
|
||||
/**
|
||||
* Process a received EAP-TLS/TTLS packet, create response.
|
||||
*
|
||||
* @param in EAP packet data to process
|
||||
* @param out allocated EAP packet data to send
|
||||
* @return
|
||||
* - SUCCESS if TLS negotiation completed
|
||||
* - FAILED if TLS negotiation failed
|
||||
* - NEED_MORE if more exchanges required
|
||||
*/
|
||||
status_t (*process)(tls_eap_t *this, chunk_t in, chunk_t *out);
|
||||
|
||||
/**
|
||||
* Get the EAP-MSK.
|
||||
*
|
||||
* @return MSK
|
||||
*/
|
||||
chunk_t (*get_msk)(tls_eap_t *this);
|
||||
|
||||
/**
|
||||
* Destroy a tls_eap_t.
|
||||
*/
|
||||
void (*destroy)(tls_eap_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a tls_eap instance.
|
||||
*
|
||||
* @param type EAP type, EAP-TLS or EAP-TTLS
|
||||
* @param is_server role
|
||||
* @param server server identity
|
||||
* @param peer peer identity, NULL to omit peer authentication
|
||||
* @param application TLS application layer, if any
|
||||
*/
|
||||
tls_eap_t *tls_eap_create(eap_type_t type, bool is_server,
|
||||
identification_t *server, identification_t *peer,
|
||||
tls_application_t *application);
|
||||
|
||||
#endif /** TLS_EAP_H_ @}*/
|
Loading…
Reference in New Issue