diff options
author | Martin Willi <martin@revosec.ch> | 2010-08-31 09:11:09 +0200 |
---|---|---|
committer | Martin Willi <martin@revosec.ch> | 2010-08-31 16:16:58 +0200 |
commit | 877c910f04677af6414f390f8d750619737c9997 (patch) | |
tree | 8afb579d2975441bf435fa4b6b043840551d94f7 /src | |
parent | ecd98efa9d5cb1c6f3e14ee5c8a97d405d3a1ef9 (diff) | |
download | strongswan-877c910f04677af6414f390f8d750619737c9997.tar.bz2 strongswan-877c910f04677af6414f390f8d750619737c9997.tar.xz |
Implemented a generic TLS EAP helper to implement EAP-TLS, TTLS and other variants
Diffstat (limited to 'src')
-rw-r--r-- | src/libtls/Makefile.am | 1 | ||||
-rw-r--r-- | src/libtls/tls_eap.c | 331 | ||||
-rw-r--r-- | src/libtls/tls_eap.h | 83 |
3 files changed, 415 insertions, 0 deletions
diff --git a/src/libtls/Makefile.am b/src/libtls/Makefile.am index 66819f2a2..a58e783d7 100644 --- a/src/libtls/Makefile.am +++ b/src/libtls/Makefile.am @@ -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 diff --git a/src/libtls/tls_eap.c b/src/libtls/tls_eap.c new file mode 100644 index 000000000..027929806 --- /dev/null +++ b/src/libtls/tls_eap.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; +} diff --git a/src/libtls/tls_eap.h b/src/libtls/tls_eap.h new file mode 100644 index 000000000..48b4dec62 --- /dev/null +++ b/src/libtls/tls_eap.h @@ -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_ @}*/ |