Moved eap-tls plugin to libcharon, updated to 4.4.1 APIs
This commit is contained in:
@@ -344,6 +344,14 @@ if MONOLITHIC
|
||||
endif
|
||||
endif
|
||||
|
||||
if USE_EAP_TLS
|
||||
SUBDIRS += plugins/eap_tls
|
||||
PLUGINS += eap-tls
|
||||
if MONOLITHIC
|
||||
libcharon_la_LIBADD += plugins/eap_tls/libstrongswan-eap-tls.la
|
||||
endif
|
||||
endif
|
||||
|
||||
if USE_MEDSRV
|
||||
SUBDIRS += plugins/medsrv
|
||||
PLUGINS += medsrv
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/libhydra \
|
||||
-I$(top_srcdir)/src/libcharon
|
||||
|
||||
AM_CFLAGS = -rdynamic
|
||||
|
||||
if MONOLITHIC
|
||||
noinst_LTLIBRARIES = libstrongswan-eap-tls.la
|
||||
else
|
||||
plugin_LTLIBRARIES = libstrongswan-eap-tls.la
|
||||
endif
|
||||
|
||||
libstrongswan_eap_tls_la_SOURCES = eap_tls_plugin.h eap_tls_plugin.c \
|
||||
eap_tls.h eap_tls.c tls/tls.h tls/tls.c \
|
||||
tls/tls_protection.h tls/tls_protection.c \
|
||||
tls/tls_compression.h tls/tls_compression.c \
|
||||
tls/tls_fragmentation.h tls/tls_fragmentation.c \
|
||||
tls/tls_crypto.h tls/tls_crypto.c \
|
||||
tls/tls_prf.h tls/tls_prf.c \
|
||||
tls/tls_reader.h tls/tls_reader.c \
|
||||
tls/tls_writer.h tls/tls_writer.c \
|
||||
tls/tls_peer.h tls/tls_peer.c \
|
||||
tls/tls_server.h tls/tls_server.c \
|
||||
tls/tls_handshake.h
|
||||
libstrongswan_eap_tls_la_LDFLAGS = -module -avoid-version
|
||||
@@ -0,0 +1,442 @@
|
||||
/*
|
||||
* 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 "eap_tls.h"
|
||||
|
||||
#include "tls/tls.h"
|
||||
|
||||
#include <daemon.h>
|
||||
#include <library.h>
|
||||
|
||||
typedef struct private_eap_tls_t private_eap_tls_t;
|
||||
|
||||
/**
|
||||
* Private data of an eap_tls_t object.
|
||||
*/
|
||||
struct private_eap_tls_t {
|
||||
|
||||
/**
|
||||
* Public interface.
|
||||
*/
|
||||
eap_tls_t public;
|
||||
|
||||
/**
|
||||
* Number of EAP-TLS messages processed so far
|
||||
*/
|
||||
int processed;
|
||||
|
||||
/**
|
||||
* Is this method instance acting as server?
|
||||
*/
|
||||
bool is_server;
|
||||
|
||||
/**
|
||||
* TLS layers
|
||||
*/
|
||||
tls_t *tls;
|
||||
|
||||
/**
|
||||
* Allocated input buffer
|
||||
*/
|
||||
chunk_t input;
|
||||
|
||||
/**
|
||||
* Number of bytes read in input buffer
|
||||
*/
|
||||
size_t inpos;
|
||||
|
||||
/**
|
||||
* Allocated ouput buffer
|
||||
*/
|
||||
chunk_t output;
|
||||
|
||||
/**
|
||||
* Number of bytes sent from output buffer
|
||||
*/
|
||||
size_t outpos;
|
||||
};
|
||||
|
||||
/** Size limit for a single TLS message */
|
||||
#define MAX_TLS_MESSAGE_LEN 16384
|
||||
/** Size of a EAP-TLS fragment */
|
||||
#define EAP_TLS_FRAGMENT_LEN 1014
|
||||
/** Maximum number of EAP-TLS messages/fragments allowed */
|
||||
#define MAX_EAP_TLS_MESSAGE_COUNT 16
|
||||
|
||||
/**
|
||||
* Flags of an EAP-TLS message
|
||||
*/
|
||||
typedef enum {
|
||||
EAP_TLS_LENGTH = (1<<7),
|
||||
EAP_TLS_MORE_FRAGS = (1<<6),
|
||||
EAP_TLS_START = (1<<5),
|
||||
} eap_tls_flags_t;
|
||||
|
||||
/**
|
||||
* EAP-TLS 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;
|
||||
|
||||
/**
|
||||
* TLS record
|
||||
*/
|
||||
typedef struct __attribute__((packed)) {
|
||||
u_int8_t type;
|
||||
u_int16_t version;
|
||||
u_int16_t length;
|
||||
char data[];
|
||||
} tls_record_t;
|
||||
|
||||
METHOD(eap_method_t, initiate, status_t,
|
||||
private_eap_tls_t *this, eap_payload_t **out)
|
||||
{
|
||||
if (this->is_server)
|
||||
{
|
||||
eap_tls_packet_t pkt = {
|
||||
.type = EAP_TLS,
|
||||
.code = EAP_REQUEST,
|
||||
.flags = EAP_TLS_START,
|
||||
};
|
||||
htoun16(&pkt.length, sizeof(eap_tls_packet_t));
|
||||
/* start with non-zero random identifier */
|
||||
do {
|
||||
pkt.identifier = random();
|
||||
} while (!pkt.identifier);
|
||||
|
||||
*out = eap_payload_create_data(chunk_from_thing(pkt));
|
||||
return NEED_MORE;
|
||||
}
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write received TLS data to the input buffer
|
||||
*/
|
||||
static bool write_buf(private_eap_tls_t *this, eap_tls_packet_t *pkt)
|
||||
{
|
||||
u_int32_t msg_len;
|
||||
u_int16_t pkt_len;
|
||||
chunk_t data;
|
||||
|
||||
pkt_len = untoh16(&pkt->length);
|
||||
|
||||
if (pkt->flags & EAP_TLS_LENGTH)
|
||||
{
|
||||
if (pkt_len < sizeof(eap_tls_packet_t) + sizeof(msg_len))
|
||||
{
|
||||
DBG1(DBG_IKE, "EAP-TLS packet too short");
|
||||
return FALSE;
|
||||
}
|
||||
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_IKE, "invalid EAP-TLS packet length");
|
||||
return FALSE;
|
||||
}
|
||||
if (this->input.ptr)
|
||||
{
|
||||
if (msg_len != this->input.len)
|
||||
{
|
||||
DBG1(DBG_IKE, "received unexpected TLS message length");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this->input = chunk_alloc(msg_len);
|
||||
this->inpos = 0;
|
||||
}
|
||||
data = chunk_create((char*)(pkt + 1) + sizeof(msg_len),
|
||||
pkt_len - sizeof(eap_tls_packet_t) - sizeof(msg_len));
|
||||
}
|
||||
else
|
||||
{
|
||||
data = chunk_create((char*)(pkt + 1),
|
||||
pkt_len - sizeof(eap_tls_packet_t));
|
||||
}
|
||||
if (data.len > this->input.len - this->inpos)
|
||||
{
|
||||
DBG1(DBG_IKE, "EAP-TLS fragment exceeds TLS message length");
|
||||
return FALSE;
|
||||
}
|
||||
memcpy(this->input.ptr + this->inpos, data.ptr, data.len);
|
||||
this->inpos += data.len;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an ack to request next fragment
|
||||
*/
|
||||
static eap_payload_t *create_ack(private_eap_tls_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 = EAP_TLS,
|
||||
};
|
||||
htoun16(&pkt.length, sizeof(pkt));
|
||||
|
||||
return eap_payload_create_data(chunk_from_thing(pkt));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a eap response from data in the TLS output buffer
|
||||
*/
|
||||
static eap_payload_t *read_buf(private_eap_tls_t *this, u_int8_t identifier)
|
||||
{
|
||||
char buf[EAP_TLS_FRAGMENT_LEN + sizeof(eap_tls_packet_t) + 4], *start;
|
||||
eap_tls_packet_t *pkt = (eap_tls_packet_t*)buf;
|
||||
u_int16_t pkt_len = sizeof(eap_tls_packet_t);
|
||||
|
||||
pkt->code = this->is_server ? EAP_REQUEST : EAP_RESPONSE;
|
||||
pkt->identifier = this->is_server ? identifier + 1 : identifier;
|
||||
pkt->type = EAP_TLS;
|
||||
pkt->flags = 0;
|
||||
|
||||
if (this->output.len)
|
||||
{
|
||||
start = (char*)(pkt + 1);
|
||||
if (this->outpos == 0)
|
||||
{ /* first fragment */
|
||||
pkt->flags = EAP_TLS_LENGTH;
|
||||
pkt_len += 4;
|
||||
start += 4;
|
||||
htoun32(pkt + 1, this->output.len);
|
||||
}
|
||||
|
||||
if (this->output.len - this->outpos > EAP_TLS_FRAGMENT_LEN)
|
||||
{
|
||||
pkt->flags |= EAP_TLS_MORE_FRAGS;
|
||||
pkt_len += EAP_TLS_FRAGMENT_LEN;
|
||||
memcpy(start, this->output.ptr + this->outpos, EAP_TLS_FRAGMENT_LEN);
|
||||
this->outpos += EAP_TLS_FRAGMENT_LEN;
|
||||
}
|
||||
else
|
||||
{
|
||||
pkt_len += this->output.len - this->outpos;
|
||||
memcpy(start, this->output.ptr + this->outpos,
|
||||
this->output.len - this->outpos);
|
||||
chunk_free(&this->output);
|
||||
this->outpos = 0;
|
||||
}
|
||||
}
|
||||
htoun16(&pkt->length, pkt_len);
|
||||
return eap_payload_create_data(chunk_create(buf, pkt_len));
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass data in input buffer to upper layers, write result to output buffer
|
||||
*/
|
||||
static status_t process_buf(private_eap_tls_t *this)
|
||||
{
|
||||
tls_record_t *in, out;
|
||||
chunk_t data;
|
||||
u_int16_t len;
|
||||
status_t status;
|
||||
|
||||
/* pass input buffer to upper layer, record for record */
|
||||
data = this->input;
|
||||
while (data.len > sizeof(tls_record_t))
|
||||
{
|
||||
in = (tls_record_t*)data.ptr;
|
||||
len = untoh16(&in->length);
|
||||
if (len > data.len - sizeof(tls_record_t))
|
||||
{
|
||||
DBG1(DBG_IKE, "TLS record length invalid");
|
||||
return FAILED;
|
||||
}
|
||||
if (untoh16(&in->version) < TLS_1_0)
|
||||
{
|
||||
DBG1(DBG_IKE, "%N invalid with EAP-TLS",
|
||||
tls_version_names, untoh16(&in->version));
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
status = this->tls->process(this->tls, in->type,
|
||||
chunk_create(in->data, len));
|
||||
if (status != NEED_MORE)
|
||||
{
|
||||
return status;
|
||||
}
|
||||
data = chunk_skip(data, len + sizeof(tls_record_t));
|
||||
}
|
||||
chunk_free(&this->input);
|
||||
this->inpos = 0;
|
||||
|
||||
/* read in records from upper layer, append to output buffer */
|
||||
chunk_free(&this->output);
|
||||
while (TRUE)
|
||||
{
|
||||
tls_content_type_t type;
|
||||
chunk_t header = chunk_from_thing(out);
|
||||
|
||||
status = this->tls->build(this->tls, &type, &data);
|
||||
switch (status)
|
||||
{
|
||||
case NEED_MORE:
|
||||
break;
|
||||
case INVALID_STATE:
|
||||
/* invalid state means we need more input from peer first */
|
||||
return NEED_MORE;
|
||||
case SUCCESS:
|
||||
return SUCCESS;
|
||||
case FAILED:
|
||||
default:
|
||||
return FAILED;
|
||||
}
|
||||
out.type = type;
|
||||
htoun16(&out.version, this->tls->get_version(this->tls));
|
||||
htoun16(&out.length, data.len);
|
||||
this->output = chunk_cat("mcm", this->output, header, data);
|
||||
}
|
||||
}
|
||||
|
||||
METHOD(eap_method_t, process, status_t,
|
||||
private_eap_tls_t *this, eap_payload_t *in, eap_payload_t **out)
|
||||
{
|
||||
eap_tls_packet_t *pkt;
|
||||
chunk_t data;
|
||||
status_t status;
|
||||
|
||||
if (++this->processed > MAX_EAP_TLS_MESSAGE_COUNT)
|
||||
{
|
||||
DBG1(DBG_IKE, "EAP-TLS packet count exceeded");
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
data = in->get_data(in);
|
||||
|
||||
pkt = (eap_tls_packet_t*)data.ptr;
|
||||
if (data.len < sizeof(eap_tls_packet_t) ||
|
||||
untoh16(&pkt->length) != data.len)
|
||||
{
|
||||
DBG1(DBG_IKE, "invalid EAP-TLS packet length");
|
||||
return FAILED;
|
||||
}
|
||||
if (!(pkt->flags & EAP_TLS_START))
|
||||
{
|
||||
if (data.len == sizeof(eap_tls_packet_t))
|
||||
{
|
||||
if (this->output.len)
|
||||
{ /* ACK to our fragment, send next */
|
||||
*out = read_buf(this, pkt->identifier);
|
||||
return NEED_MORE;
|
||||
}
|
||||
if (this->tls->is_complete(this->tls))
|
||||
{
|
||||
return SUCCESS;
|
||||
}
|
||||
return FAILED;
|
||||
}
|
||||
if (!write_buf(this, pkt))
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
if (pkt->flags & EAP_TLS_MORE_FRAGS)
|
||||
{ /* more fragments follow */
|
||||
*out = create_ack(this, pkt->identifier);
|
||||
return NEED_MORE;
|
||||
}
|
||||
else if (this->input.len != this->inpos)
|
||||
{
|
||||
DBG1(DBG_IKE, "defragemented TLS message has invalid length");
|
||||
return FAILED;
|
||||
}
|
||||
}
|
||||
status = process_buf(this);
|
||||
if (status == NEED_MORE)
|
||||
{
|
||||
*out = read_buf(this, pkt->identifier);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
METHOD(eap_method_t, get_type, eap_type_t,
|
||||
private_eap_tls_t *this, u_int32_t *vendor)
|
||||
{
|
||||
*vendor = 0;
|
||||
return EAP_TLS;
|
||||
}
|
||||
|
||||
METHOD(eap_method_t, get_msk, status_t,
|
||||
private_eap_tls_t *this, chunk_t *msk)
|
||||
{
|
||||
*msk = this->tls->get_eap_msk(this->tls);
|
||||
if (msk->len)
|
||||
{
|
||||
return SUCCESS;
|
||||
}
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
METHOD(eap_method_t, is_mutual, bool,
|
||||
private_eap_tls_t *this)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(eap_method_t, destroy, void,
|
||||
private_eap_tls_t *this)
|
||||
{
|
||||
free(this->input.ptr);
|
||||
free(this->output.ptr);
|
||||
|
||||
this->tls->destroy(this->tls);
|
||||
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic private constructor
|
||||
*/
|
||||
static eap_tls_t *eap_tls_create(identification_t *server,
|
||||
identification_t *peer, bool is_server)
|
||||
{
|
||||
private_eap_tls_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public.eap_method = {
|
||||
.initiate = _initiate,
|
||||
.process = _process,
|
||||
.get_type = _get_type,
|
||||
.is_mutual = _is_mutual,
|
||||
.get_msk = _get_msk,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.is_server = is_server,
|
||||
);
|
||||
this->tls = tls_create(is_server, server, peer);
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
|
||||
eap_tls_t *eap_tls_create_server(identification_t *server,
|
||||
identification_t *peer)
|
||||
{
|
||||
return eap_tls_create(server, peer, TRUE);
|
||||
}
|
||||
|
||||
eap_tls_t *eap_tls_create_peer(identification_t *server,
|
||||
identification_t *peer)
|
||||
{
|
||||
return eap_tls_create(server, peer, FALSE);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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 eap_tls eap_tls
|
||||
* @{ @ingroup eap_tls
|
||||
*/
|
||||
|
||||
#ifndef EAP_TLS_H_
|
||||
#define EAP_TLS_H_
|
||||
|
||||
typedef struct eap_tls_t eap_tls_t;
|
||||
|
||||
#include <sa/authenticators/eap/eap_method.h>
|
||||
|
||||
/**
|
||||
* Implementation of eap_method_t using EAP-TLS.
|
||||
*/
|
||||
struct eap_tls_t {
|
||||
|
||||
/**
|
||||
* Implements eap_method_t interface.
|
||||
*/
|
||||
eap_method_t eap_method;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates the EAP method EAP-TLS acting as server.
|
||||
*
|
||||
* @param server ID of the EAP server
|
||||
* @param peer ID of the EAP client
|
||||
* @return eap_tls_t object
|
||||
*/
|
||||
eap_tls_t *eap_tls_create_server(identification_t *server,
|
||||
identification_t *peer);
|
||||
|
||||
/**
|
||||
* Creates the EAP method EAP-TLS acting as peer.
|
||||
*
|
||||
* @param server ID of the EAP server
|
||||
* @param peer ID of the EAP client
|
||||
* @return eap_tls_t object
|
||||
*/
|
||||
eap_tls_t *eap_tls_create_peer(identification_t *server,
|
||||
identification_t *peer);
|
||||
|
||||
#endif /** EAP_TLS_H_ @}*/
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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 "eap_tls_plugin.h"
|
||||
|
||||
#include "eap_tls.h"
|
||||
|
||||
#include <daemon.h>
|
||||
|
||||
|
||||
METHOD(plugin_t, destroy, void,
|
||||
eap_tls_plugin_t *this)
|
||||
{
|
||||
charon->eap->remove_method(charon->eap,
|
||||
(eap_constructor_t)eap_tls_create_server);
|
||||
charon->eap->remove_method(charon->eap,
|
||||
(eap_constructor_t)eap_tls_create_peer);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/*
|
||||
* see header file
|
||||
*/
|
||||
plugin_t *eap_tls_plugin_create()
|
||||
{
|
||||
eap_tls_plugin_t *this;
|
||||
|
||||
INIT(this,
|
||||
.plugin.destroy = _destroy,
|
||||
);
|
||||
|
||||
charon->eap->add_method(charon->eap, EAP_TLS, 0, EAP_SERVER,
|
||||
(eap_constructor_t)eap_tls_create_server);
|
||||
charon->eap->add_method(charon->eap, EAP_TLS, 0, EAP_PEER,
|
||||
(eap_constructor_t)eap_tls_create_peer);
|
||||
|
||||
return &this->plugin;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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 eap_tls eap_tls
|
||||
* @ingroup cplugins
|
||||
*
|
||||
* @defgroup eap_tls_plugin eap_tls_plugin
|
||||
* @{ @ingroup eap_tls
|
||||
*/
|
||||
|
||||
#ifndef EAP_TLS_PLUGIN_H_
|
||||
#define EAP_TLS_PLUGIN_H_
|
||||
|
||||
#include <plugins/plugin.h>
|
||||
|
||||
typedef struct eap_tls_plugin_t eap_tls_plugin_t;
|
||||
|
||||
/**
|
||||
* EAP-TLS plugin
|
||||
*/
|
||||
struct eap_tls_plugin_t {
|
||||
|
||||
/**
|
||||
* implements plugin interface
|
||||
*/
|
||||
plugin_t plugin;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a eap_tls_plugin instance.
|
||||
*/
|
||||
plugin_t *eap_tls_plugin_create();
|
||||
|
||||
#endif /** EAP_TLS_PLUGIN_H_ @}*/
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* 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.h"
|
||||
|
||||
#include "tls_protection.h"
|
||||
#include "tls_compression.h"
|
||||
#include "tls_fragmentation.h"
|
||||
#include "tls_crypto.h"
|
||||
#include "tls_server.h"
|
||||
#include "tls_peer.h"
|
||||
|
||||
#include <daemon.h>
|
||||
|
||||
ENUM_BEGIN(tls_version_names, SSL_2_0, SSL_2_0,
|
||||
"SSLv2");
|
||||
ENUM_NEXT(tls_version_names, SSL_3_0, TLS_1_2, SSL_2_0,
|
||||
"SSLv3",
|
||||
"TLS 1.0",
|
||||
"TLS 1.1",
|
||||
"TLS 1.2");
|
||||
ENUM_END(tls_version_names, TLS_1_2);
|
||||
|
||||
ENUM(tls_content_type_names, TLS_CHANGE_CIPHER_SPEC, TLS_APPLICATION_DATA,
|
||||
"ChangeCipherSpec",
|
||||
"Alert",
|
||||
"Handshake",
|
||||
"ApplicationData",
|
||||
);
|
||||
|
||||
ENUM_BEGIN(tls_handshake_type_names, TLS_HELLO_REQUEST, TLS_SERVER_HELLO,
|
||||
"HelloRequest",
|
||||
"ClientHello",
|
||||
"ServerHello");
|
||||
ENUM_NEXT(tls_handshake_type_names, TLS_CERTIFICATE, TLS_CLIENT_KEY_EXCHANGE, TLS_SERVER_HELLO,
|
||||
"Certificate",
|
||||
"ServerKeyExchange",
|
||||
"CertificateRequest",
|
||||
"ServerHelloDone",
|
||||
"CertificateVerify",
|
||||
"ClientKeyExchange");
|
||||
ENUM_NEXT(tls_handshake_type_names, TLS_FINISHED, TLS_FINISHED, TLS_CLIENT_KEY_EXCHANGE,
|
||||
"Finished");
|
||||
ENUM_END(tls_handshake_type_names, TLS_FINISHED);
|
||||
|
||||
|
||||
typedef struct private_tls_t private_tls_t;
|
||||
|
||||
/**
|
||||
* Private data of an tls_protection_t object.
|
||||
*/
|
||||
struct private_tls_t {
|
||||
|
||||
/**
|
||||
* Public tls_t interface.
|
||||
*/
|
||||
tls_t public;
|
||||
|
||||
/**
|
||||
* Role this TLS stack acts as.
|
||||
*/
|
||||
bool is_server;
|
||||
|
||||
/**
|
||||
* Server identity
|
||||
*/
|
||||
identification_t *server;
|
||||
|
||||
/**
|
||||
* Peer identity
|
||||
*/
|
||||
identification_t *peer;
|
||||
|
||||
/**
|
||||
* Negotiated TLS version
|
||||
*/
|
||||
tls_version_t version;
|
||||
|
||||
/**
|
||||
* TLS record protection layer
|
||||
*/
|
||||
tls_protection_t *protection;
|
||||
|
||||
/**
|
||||
* TLS record compression layer
|
||||
*/
|
||||
tls_compression_t *compression;
|
||||
|
||||
/**
|
||||
* TLS record fragmentation layer
|
||||
*/
|
||||
tls_fragmentation_t *fragmentation;
|
||||
|
||||
/**
|
||||
* TLS crypto helper context
|
||||
*/
|
||||
tls_crypto_t *crypto;
|
||||
|
||||
/**
|
||||
* TLS handshake protocol handler
|
||||
*/
|
||||
tls_handshake_t *handshake;
|
||||
};
|
||||
|
||||
METHOD(tls_t, process, status_t,
|
||||
private_tls_t *this, tls_content_type_t type, chunk_t data)
|
||||
{
|
||||
return this->protection->process(this->protection, type, data);
|
||||
}
|
||||
|
||||
METHOD(tls_t, build, status_t,
|
||||
private_tls_t *this, tls_content_type_t *type, chunk_t *data)
|
||||
{
|
||||
return this->protection->build(this->protection, type, data);
|
||||
}
|
||||
|
||||
METHOD(tls_t, is_server, bool,
|
||||
private_tls_t *this)
|
||||
{
|
||||
return this->is_server;
|
||||
}
|
||||
|
||||
METHOD(tls_t, get_version, tls_version_t,
|
||||
private_tls_t *this)
|
||||
{
|
||||
return this->version;
|
||||
}
|
||||
|
||||
METHOD(tls_t, set_version, void,
|
||||
private_tls_t *this, tls_version_t version)
|
||||
{
|
||||
this->version = version;
|
||||
}
|
||||
|
||||
METHOD(tls_t, is_complete, bool,
|
||||
private_tls_t *this)
|
||||
{
|
||||
return this->crypto->get_eap_msk(this->crypto).len != 0;
|
||||
}
|
||||
|
||||
METHOD(tls_t, get_eap_msk, chunk_t,
|
||||
private_tls_t *this)
|
||||
{
|
||||
return this->crypto->get_eap_msk(this->crypto);
|
||||
}
|
||||
|
||||
METHOD(tls_t, destroy, void,
|
||||
private_tls_t *this)
|
||||
{
|
||||
this->protection->destroy(this->protection);
|
||||
this->compression->destroy(this->compression);
|
||||
this->fragmentation->destroy(this->fragmentation);
|
||||
this->crypto->destroy(this->crypto);
|
||||
this->handshake->destroy(this->handshake);
|
||||
this->peer->destroy(this->peer);
|
||||
this->server->destroy(this->server);
|
||||
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
tls_t *tls_create(bool is_server, identification_t *server,
|
||||
identification_t *peer)
|
||||
{
|
||||
private_tls_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.process = _process,
|
||||
.build = _build,
|
||||
.is_server = _is_server,
|
||||
.get_version = _get_version,
|
||||
.set_version = _set_version,
|
||||
.is_complete = _is_complete,
|
||||
.get_eap_msk = _get_eap_msk,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.is_server = is_server,
|
||||
.version = TLS_1_2,
|
||||
.server = server->clone(server),
|
||||
.peer = peer->clone(peer),
|
||||
);
|
||||
|
||||
this->crypto = tls_crypto_create(&this->public);
|
||||
if (is_server)
|
||||
{
|
||||
this->handshake = &tls_server_create(&this->public, this->crypto,
|
||||
this->server, this->peer)->handshake;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->handshake = &tls_peer_create(&this->public, this->crypto,
|
||||
this->peer, this->server)->handshake;
|
||||
}
|
||||
this->fragmentation = tls_fragmentation_create(this->handshake);
|
||||
this->compression = tls_compression_create(this->fragmentation);
|
||||
this->protection = tls_protection_create(&this->public, this->compression);
|
||||
this->crypto->set_protection(this->crypto, this->protection);
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* 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 tlsgroup tls
|
||||
* @{ @ingroup eap_tls
|
||||
*
|
||||
* @defgroup tls tls
|
||||
* @{ @ingroup tls
|
||||
*/
|
||||
|
||||
#ifndef TLS_H_
|
||||
#define TLS_H_
|
||||
|
||||
typedef enum tls_version_t tls_version_t;
|
||||
typedef enum tls_content_type_t tls_content_type_t;
|
||||
typedef enum tls_handshake_type_t tls_handshake_type_t;
|
||||
typedef struct tls_t tls_t;
|
||||
|
||||
#include <library.h>
|
||||
|
||||
/**
|
||||
* TLS/SSL version numbers
|
||||
*/
|
||||
enum tls_version_t {
|
||||
SSL_2_0 = 0x0200,
|
||||
SSL_3_0 = 0x0300,
|
||||
TLS_1_0 = 0x0301,
|
||||
TLS_1_1 = 0x0302,
|
||||
TLS_1_2 = 0x0303,
|
||||
};
|
||||
|
||||
/**
|
||||
* Enum names for tls_version_t
|
||||
*/
|
||||
extern enum_name_t *tls_version_names;
|
||||
|
||||
/**
|
||||
* TLS higher level content type
|
||||
*/
|
||||
enum tls_content_type_t {
|
||||
TLS_CHANGE_CIPHER_SPEC = 20,
|
||||
TLS_ALERT = 21,
|
||||
TLS_HANDSHAKE = 22,
|
||||
TLS_APPLICATION_DATA = 23,
|
||||
};
|
||||
|
||||
/**
|
||||
* Enum names for tls_content_type_t
|
||||
*/
|
||||
extern enum_name_t *tls_content_type_names;
|
||||
|
||||
/**
|
||||
* TLS handshake subtype
|
||||
*/
|
||||
enum tls_handshake_type_t {
|
||||
TLS_HELLO_REQUEST = 0,
|
||||
TLS_CLIENT_HELLO = 1,
|
||||
TLS_SERVER_HELLO = 2,
|
||||
TLS_CERTIFICATE = 11,
|
||||
TLS_SERVER_KEY_EXCHANGE = 12,
|
||||
TLS_CERTIFICATE_REQUEST = 13,
|
||||
TLS_SERVER_HELLO_DONE = 14,
|
||||
TLS_CERTIFICATE_VERIFY = 15,
|
||||
TLS_CLIENT_KEY_EXCHANGE = 16,
|
||||
TLS_FINISHED = 20,
|
||||
};
|
||||
|
||||
/**
|
||||
* Enum names for tls_handshake_type_t
|
||||
*/
|
||||
extern enum_name_t *tls_handshake_type_names;
|
||||
|
||||
/**
|
||||
* A bottom-up driven TLS stack, suitable for EAP implementations.
|
||||
*/
|
||||
struct tls_t {
|
||||
|
||||
/**
|
||||
* Process a TLS record, pass it to upper layers.
|
||||
*
|
||||
* @param type type of the TLS record to process
|
||||
* @param data associated TLS record data
|
||||
* @return
|
||||
* - SUCCESS if TLS negotiation complete
|
||||
* - FAILED if TLS handshake failed
|
||||
* - NEED_MORE if more invocations to process/build needed
|
||||
*/
|
||||
status_t (*process)(tls_t *this, tls_content_type_t type, chunk_t data);
|
||||
|
||||
/**
|
||||
* Query upper layer for TLS record, build protected record.
|
||||
*
|
||||
* @param type type of the built TLS record
|
||||
* @param data allocated data of the built TLS record
|
||||
* @return
|
||||
* - SUCCESS if TLS negotiation complete
|
||||
* - FAILED if TLS handshake failed
|
||||
* - NEED_MORE if upper layers have more records to send
|
||||
* - INVALID_STATE if more input records required
|
||||
*/
|
||||
status_t (*build)(tls_t *this, tls_content_type_t *type, chunk_t *data);
|
||||
|
||||
/**
|
||||
* Check if TLS stack is acting as a server.
|
||||
*
|
||||
* @return TRUE if server, FALSE if peer
|
||||
*/
|
||||
bool (*is_server)(tls_t *this);
|
||||
|
||||
/**
|
||||
* Get the negotiated TLS/SSL version.
|
||||
*
|
||||
* @return negotiated TLS version
|
||||
*/
|
||||
tls_version_t (*get_version)(tls_t *this);
|
||||
|
||||
/**
|
||||
* Set the negotiated TLS/SSL version.
|
||||
*
|
||||
* @param version negotiated TLS version
|
||||
*/
|
||||
void (*set_version)(tls_t *this, tls_version_t version);
|
||||
|
||||
/**
|
||||
* Check if TLS negotiation completed successfully.
|
||||
*
|
||||
* @return TRUE if TLS negotation and authentication complete
|
||||
*/
|
||||
bool (*is_complete)(tls_t *this);
|
||||
|
||||
/**
|
||||
* Get the MSK for EAP-TLS.
|
||||
*
|
||||
* @return MSK, internal data
|
||||
*/
|
||||
chunk_t (*get_eap_msk)(tls_t *this);
|
||||
|
||||
/**
|
||||
* Destroy a tls_t.
|
||||
*/
|
||||
void (*destroy)(tls_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a tls instance.
|
||||
*
|
||||
* @param is_server TRUE to act as server, FALSE for client
|
||||
* @param server server identity
|
||||
* @param peer peer identity
|
||||
* @return TLS stack
|
||||
*/
|
||||
tls_t *tls_create(bool is_server, identification_t *server,
|
||||
identification_t *peer);
|
||||
|
||||
#endif /** TLS_H_ @}*/
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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_compression.h"
|
||||
|
||||
#include <daemon.h>
|
||||
|
||||
typedef struct private_tls_compression_t private_tls_compression_t;
|
||||
|
||||
/**
|
||||
* Private data of an tls_compression_t object.
|
||||
*/
|
||||
struct private_tls_compression_t {
|
||||
|
||||
/**
|
||||
* Public tls_compression_t interface.
|
||||
*/
|
||||
tls_compression_t public;
|
||||
|
||||
/**
|
||||
* Upper layer, TLS record fragmentation
|
||||
*/
|
||||
tls_fragmentation_t *fragmentation;
|
||||
};
|
||||
|
||||
METHOD(tls_compression_t, process, status_t,
|
||||
private_tls_compression_t *this, tls_content_type_t type, chunk_t data)
|
||||
{
|
||||
return this->fragmentation->process(this->fragmentation, type, data);
|
||||
}
|
||||
|
||||
METHOD(tls_compression_t, build, status_t,
|
||||
private_tls_compression_t *this, tls_content_type_t *type, chunk_t *data)
|
||||
{
|
||||
return this->fragmentation->build(this->fragmentation, type, data);
|
||||
}
|
||||
|
||||
METHOD(tls_compression_t, destroy, void,
|
||||
private_tls_compression_t *this)
|
||||
{
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
tls_compression_t *tls_compression_create(tls_fragmentation_t *fragmentation)
|
||||
{
|
||||
private_tls_compression_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.process = _process,
|
||||
.build = _build,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.fragmentation = fragmentation,
|
||||
);
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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_compression tls_compression
|
||||
* @{ @ingroup tls
|
||||
*/
|
||||
|
||||
#ifndef TLS_COMPRESSION_H_
|
||||
#define TLS_COMPRESSION_H_
|
||||
|
||||
typedef struct tls_compression_t tls_compression_t;
|
||||
|
||||
#include <library.h>
|
||||
|
||||
#include "tls.h"
|
||||
#include "tls_fragmentation.h"
|
||||
|
||||
/**
|
||||
* TLS record protocol compression layer.
|
||||
*/
|
||||
struct tls_compression_t {
|
||||
|
||||
/**
|
||||
* Process a compressed TLS record, pass it to upper layers.
|
||||
*
|
||||
* @param type type of the TLS record to process
|
||||
* @param data associated TLS record data
|
||||
* @return
|
||||
* - SUCCESS if TLS negotiation complete
|
||||
* - FAILED if TLS handshake failed
|
||||
* - NEED_MORE if more invocations to process/build needed
|
||||
*/
|
||||
status_t (*process)(tls_compression_t *this,
|
||||
tls_content_type_t type, chunk_t data);
|
||||
|
||||
/**
|
||||
* Query upper layer for TLS record, build compressed record.
|
||||
*
|
||||
* @param type type of the built TLS record
|
||||
* @param data allocated data of the built TLS record
|
||||
* @return
|
||||
* - SUCCESS if TLS negotiation complete
|
||||
* - FAILED if TLS handshake failed
|
||||
* - NEED_MORE if upper layers have more records to send
|
||||
* - INVALID_STATE if more input records required
|
||||
*/
|
||||
status_t (*build)(tls_compression_t *this,
|
||||
tls_content_type_t *type, chunk_t *data);
|
||||
|
||||
/**
|
||||
* Destroy a tls_compression_t.
|
||||
*/
|
||||
void (*destroy)(tls_compression_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a tls_compression instance.
|
||||
*
|
||||
* @param fragmentation fragmentation layer of TLS stack
|
||||
* @return TLS compression layer.
|
||||
*/
|
||||
tls_compression_t *tls_compression_create(tls_fragmentation_t *fragmentation);
|
||||
|
||||
#endif /** TLS_COMPRESSION_H_ @}*/
|
||||
@@ -0,0 +1,691 @@
|
||||
/*
|
||||
* 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_crypto.h"
|
||||
|
||||
#include <daemon.h>
|
||||
|
||||
typedef struct private_tls_crypto_t private_tls_crypto_t;
|
||||
|
||||
/**
|
||||
* Private data of an tls_crypto_t object.
|
||||
*/
|
||||
struct private_tls_crypto_t {
|
||||
|
||||
/**
|
||||
* Public tls_crypto_t interface.
|
||||
*/
|
||||
tls_crypto_t public;
|
||||
|
||||
/**
|
||||
* Protection layer
|
||||
*/
|
||||
tls_protection_t *protection;
|
||||
|
||||
/**
|
||||
* List of supported/acceptable cipher suites
|
||||
*/
|
||||
tls_cipher_suite_t *suites;
|
||||
|
||||
/**
|
||||
* Number of supported suites
|
||||
*/
|
||||
int suite_count;
|
||||
|
||||
/**
|
||||
* Selected cipher suite
|
||||
*/
|
||||
tls_cipher_suite_t suite;
|
||||
|
||||
/**
|
||||
* TLS context
|
||||
*/
|
||||
tls_t *tls;
|
||||
|
||||
/**
|
||||
* All handshake data concatentated
|
||||
*/
|
||||
chunk_t handshake;
|
||||
|
||||
/**
|
||||
* Connection state TLS PRF
|
||||
*/
|
||||
tls_prf_t *prf;
|
||||
|
||||
/**
|
||||
* Signer instance for inbound traffic
|
||||
*/
|
||||
signer_t *signer_in;
|
||||
|
||||
/**
|
||||
* Signer instance for outbound traffic
|
||||
*/
|
||||
signer_t *signer_out;
|
||||
|
||||
/**
|
||||
* Crypter instance for inbound traffic
|
||||
*/
|
||||
crypter_t *crypter_in;
|
||||
|
||||
/**
|
||||
* Crypter instance for outbound traffic
|
||||
*/
|
||||
crypter_t *crypter_out;
|
||||
|
||||
/**
|
||||
* IV for input decryption, if < TLSv1.2
|
||||
*/
|
||||
chunk_t iv_in;
|
||||
|
||||
/**
|
||||
* IV for output decryption, if < TLSv1.2
|
||||
*/
|
||||
chunk_t iv_out;
|
||||
|
||||
/**
|
||||
* EAP-TLS MSK
|
||||
*/
|
||||
chunk_t msk;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
tls_cipher_suite_t suite;
|
||||
hash_algorithm_t hash;
|
||||
pseudo_random_function_t prf;
|
||||
integrity_algorithm_t mac;
|
||||
encryption_algorithm_t encr;
|
||||
size_t encr_size;
|
||||
} suite_algs_t;
|
||||
|
||||
/**
|
||||
* Mapping suites to a set of algorithms
|
||||
*/
|
||||
static suite_algs_t suite_algs[] = {
|
||||
{ TLS_RSA_WITH_NULL_MD5,
|
||||
HASH_MD5,
|
||||
PRF_HMAC_MD5,
|
||||
AUTH_HMAC_MD5_128,
|
||||
ENCR_NULL, 0
|
||||
},
|
||||
{ TLS_RSA_WITH_NULL_SHA,
|
||||
HASH_SHA1,
|
||||
PRF_HMAC_SHA1,
|
||||
AUTH_HMAC_SHA1_160,
|
||||
ENCR_NULL, 0
|
||||
},
|
||||
{ TLS_RSA_WITH_NULL_SHA256,
|
||||
HASH_SHA256,
|
||||
PRF_HMAC_SHA2_256,
|
||||
AUTH_HMAC_SHA2_256_256,
|
||||
ENCR_NULL, 0
|
||||
},
|
||||
{ TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
HASH_SHA1,
|
||||
PRF_HMAC_SHA1,
|
||||
AUTH_HMAC_SHA1_160,
|
||||
ENCR_AES_CBC, 16
|
||||
},
|
||||
{ TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
HASH_SHA1,
|
||||
PRF_HMAC_SHA1,
|
||||
AUTH_HMAC_SHA1_160,
|
||||
ENCR_AES_CBC, 32
|
||||
},
|
||||
{ TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
HASH_SHA1,
|
||||
PRF_HMAC_SHA1,
|
||||
AUTH_HMAC_SHA1_160,
|
||||
ENCR_3DES, 0
|
||||
},
|
||||
{ TLS_RSA_WITH_AES_128_CBC_SHA256,
|
||||
HASH_SHA256,
|
||||
PRF_HMAC_SHA2_256,
|
||||
AUTH_HMAC_SHA2_256_256,
|
||||
ENCR_AES_CBC, 16
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Look up algoritms by a suite
|
||||
*/
|
||||
static suite_algs_t *find_suite(tls_cipher_suite_t suite)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < countof(suite_algs); i++)
|
||||
{
|
||||
if (suite_algs[i].suite == suite)
|
||||
{
|
||||
return &suite_algs[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the cipher suite list
|
||||
*/
|
||||
static void build_cipher_suite_list(private_tls_crypto_t *this)
|
||||
{
|
||||
encryption_algorithm_t encr;
|
||||
integrity_algorithm_t mac;
|
||||
enumerator_t *encrs, *macs;
|
||||
tls_cipher_suite_t supported[64], unique[64];
|
||||
int count = 0, i, j;
|
||||
|
||||
/* we assume that we support RSA, but no DHE yet */
|
||||
macs = lib->crypto->create_signer_enumerator(lib->crypto);
|
||||
while (macs->enumerate(macs, &mac))
|
||||
{
|
||||
switch (mac)
|
||||
{
|
||||
case AUTH_HMAC_SHA1_160:
|
||||
supported[count++] = TLS_RSA_WITH_NULL_SHA;
|
||||
break;
|
||||
case AUTH_HMAC_SHA2_256_256:
|
||||
supported[count++] = TLS_RSA_WITH_NULL_SHA256;
|
||||
break;
|
||||
case AUTH_HMAC_MD5_128:
|
||||
supported[count++] = TLS_RSA_WITH_NULL_MD5;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
encrs = lib->crypto->create_crypter_enumerator(lib->crypto);
|
||||
while (encrs->enumerate(encrs, &encr))
|
||||
{
|
||||
switch (encr)
|
||||
{
|
||||
case ENCR_AES_CBC:
|
||||
switch (mac)
|
||||
{
|
||||
case AUTH_HMAC_SHA1_160:
|
||||
supported[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
|
||||
supported[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
|
||||
break;
|
||||
case AUTH_HMAC_SHA2_256_256:
|
||||
supported[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256;
|
||||
supported[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ENCR_3DES:
|
||||
switch (mac)
|
||||
{
|
||||
case AUTH_HMAC_SHA1_160:
|
||||
supported[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
encrs->destroy(encrs);
|
||||
}
|
||||
macs->destroy(macs);
|
||||
|
||||
/* remove duplicates */
|
||||
this->suite_count = 0;
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
bool match = FALSE;
|
||||
|
||||
for (j = 0; j < this->suite_count; j++)
|
||||
{
|
||||
if (supported[i] == unique[j])
|
||||
{
|
||||
match = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!match)
|
||||
{
|
||||
unique[this->suite_count++] = supported[i];
|
||||
}
|
||||
}
|
||||
free(this->suites);
|
||||
this->suites = malloc(sizeof(tls_cipher_suite_t) * this->suite_count);
|
||||
memcpy(this->suites, unique, sizeof(tls_cipher_suite_t) * this->suite_count);
|
||||
}
|
||||
|
||||
METHOD(tls_crypto_t, get_cipher_suites, int,
|
||||
private_tls_crypto_t *this, tls_cipher_suite_t **suites)
|
||||
{
|
||||
*suites = this->suites;
|
||||
return this->suite_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create crypto primitives
|
||||
*/
|
||||
static bool create_ciphers(private_tls_crypto_t *this, tls_cipher_suite_t suite)
|
||||
{
|
||||
suite_algs_t *algs;
|
||||
|
||||
algs = find_suite(suite);
|
||||
if (!algs)
|
||||
{
|
||||
DBG1(DBG_IKE, "selected TLS suite not supported");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DESTROY_IF(this->prf);
|
||||
if (this->tls->get_version(this->tls) < TLS_1_2)
|
||||
{
|
||||
this->prf = tls_prf_create_10();
|
||||
}
|
||||
else
|
||||
{
|
||||
this->prf = tls_prf_create_12(algs->prf);
|
||||
}
|
||||
if (!this->prf)
|
||||
{
|
||||
DBG1(DBG_IKE, "selected TLS PRF not supported");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DESTROY_IF(this->signer_in);
|
||||
DESTROY_IF(this->signer_out);
|
||||
this->signer_in = lib->crypto->create_signer(lib->crypto, algs->mac);
|
||||
this->signer_out = lib->crypto->create_signer(lib->crypto, algs->mac);
|
||||
if (!this->signer_in || !this->signer_out)
|
||||
{
|
||||
DBG1(DBG_IKE, "selected TLS MAC %N not supported",
|
||||
integrity_algorithm_names, algs->mac);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DESTROY_IF(this->crypter_in);
|
||||
DESTROY_IF(this->crypter_out);
|
||||
if (algs->encr == ENCR_NULL)
|
||||
{
|
||||
this->crypter_in = this->crypter_out = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->crypter_in = lib->crypto->create_crypter(lib->crypto,
|
||||
algs->encr, algs->encr_size);
|
||||
this->crypter_out = lib->crypto->create_crypter(lib->crypto,
|
||||
algs->encr, algs->encr_size);
|
||||
if (!this->crypter_in || !this->crypter_out)
|
||||
{
|
||||
DBG1(DBG_IKE, "selected TLS crypter %N not supported",
|
||||
encryption_algorithm_names, algs->encr);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(tls_crypto_t, select_cipher_suite, tls_cipher_suite_t,
|
||||
private_tls_crypto_t *this, tls_cipher_suite_t *suites, int count)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < this->suite_count; i++)
|
||||
{
|
||||
for (j = 0; j < count; j++)
|
||||
{
|
||||
if (this->suites[i] == suites[j])
|
||||
{
|
||||
if (create_ciphers(this, this->suites[i]))
|
||||
{
|
||||
this->suite = this->suites[i];
|
||||
return this->suite;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
METHOD(tls_crypto_t, set_protection, void,
|
||||
private_tls_crypto_t *this, tls_protection_t *protection)
|
||||
{
|
||||
this->protection = protection;
|
||||
}
|
||||
|
||||
METHOD(tls_crypto_t, append_handshake, void,
|
||||
private_tls_crypto_t *this, tls_handshake_type_t type, chunk_t data)
|
||||
{
|
||||
u_int32_t header;
|
||||
|
||||
/* reconstruct handshake header */
|
||||
header = htonl(data.len | (type << 24));
|
||||
this->handshake = chunk_cat("mcc", this->handshake,
|
||||
chunk_from_thing(header), data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a hash of the stored handshake data
|
||||
*/
|
||||
static bool hash_handshake(private_tls_crypto_t *this, chunk_t *hash)
|
||||
{
|
||||
if (this->tls->get_version(this->tls) >= TLS_1_2)
|
||||
{
|
||||
hasher_t *hasher;
|
||||
suite_algs_t *alg;
|
||||
|
||||
alg = find_suite(this->suite);
|
||||
if (!alg)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
hasher = lib->crypto->create_hasher(lib->crypto, alg->hash);
|
||||
if (!hasher)
|
||||
{
|
||||
DBG1(DBG_IKE, "%N not supported", hash_algorithm_names, alg->hash);
|
||||
return FALSE;
|
||||
}
|
||||
hasher->allocate_hash(hasher, this->handshake, hash);
|
||||
hasher->destroy(hasher);
|
||||
}
|
||||
else
|
||||
{
|
||||
hasher_t *md5, *sha1;
|
||||
char buf[HASH_SIZE_MD5 + HASH_SIZE_SHA1];
|
||||
|
||||
md5 = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
|
||||
if (!md5)
|
||||
{
|
||||
DBG1(DBG_IKE, "%N not supported", hash_algorithm_names, HASH_MD5);
|
||||
return FALSE;
|
||||
}
|
||||
md5->get_hash(md5, this->handshake, buf);
|
||||
md5->destroy(md5);
|
||||
sha1 = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
|
||||
if (!sha1)
|
||||
{
|
||||
DBG1(DBG_IKE, "%N not supported", hash_algorithm_names, HASH_SHA1);
|
||||
return FALSE;
|
||||
}
|
||||
sha1->get_hash(sha1, this->handshake, buf + HASH_SIZE_MD5);
|
||||
sha1->destroy(sha1);
|
||||
|
||||
*hash = chunk_clone(chunk_from_thing(buf));
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(tls_crypto_t, sign_handshake, bool,
|
||||
private_tls_crypto_t *this, private_key_t *key, tls_writer_t *writer)
|
||||
{
|
||||
chunk_t sig, hash;
|
||||
|
||||
if (this->tls->get_version(this->tls) >= TLS_1_2)
|
||||
{
|
||||
/* TODO: use supported algorithms instead of fixed SHA1/RSA */
|
||||
if (!key->sign(key, SIGN_RSA_EMSA_PKCS1_SHA1, this->handshake, &sig))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
writer->write_uint8(writer, 2);
|
||||
writer->write_uint8(writer, 1);
|
||||
writer->write_data16(writer, sig);
|
||||
free(sig.ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!hash_handshake(this, &hash))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
if (!key->sign(key, SIGN_RSA_EMSA_PKCS1_NULL, hash, &sig))
|
||||
{
|
||||
free(hash.ptr);
|
||||
return FALSE;
|
||||
}
|
||||
writer->write_data16(writer, sig);
|
||||
free(hash.ptr);
|
||||
free(sig.ptr);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(tls_crypto_t, verify_handshake, bool,
|
||||
private_tls_crypto_t *this, public_key_t *key, tls_reader_t *reader)
|
||||
{
|
||||
if (this->tls->get_version(this->tls) >= TLS_1_2)
|
||||
{
|
||||
u_int8_t hash, alg;
|
||||
chunk_t sig;
|
||||
|
||||
if (!reader->read_uint8(reader, &hash) ||
|
||||
!reader->read_uint8(reader, &alg) ||
|
||||
!reader->read_data16(reader, &sig))
|
||||
{
|
||||
DBG1(DBG_IKE, "received invalid Certificate Verify");
|
||||
return FALSE;
|
||||
}
|
||||
/* TODO: map received hash/sig alg to signature scheme */
|
||||
if (hash != 2 || alg != 1 ||
|
||||
!key->verify(key, SIGN_RSA_EMSA_PKCS1_SHA1, this->handshake, sig))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
chunk_t sig, hash;
|
||||
|
||||
if (!reader->read_data16(reader, &sig))
|
||||
{
|
||||
DBG1(DBG_IKE, "received invalid Certificate Verify");
|
||||
return FALSE;
|
||||
}
|
||||
if (!hash_handshake(this, &hash))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
if (!key->verify(key, SIGN_RSA_EMSA_PKCS1_NULL, hash, sig))
|
||||
{
|
||||
free(hash.ptr);
|
||||
return FALSE;
|
||||
}
|
||||
free(hash.ptr);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(tls_crypto_t, calculate_finished, bool,
|
||||
private_tls_crypto_t *this, char *label, char out[12])
|
||||
{
|
||||
chunk_t seed;
|
||||
|
||||
if (!this->prf)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
if (!hash_handshake(this, &seed))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
this->prf->get_bytes(this->prf, label, seed, 12, out);
|
||||
free(seed.ptr);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(tls_crypto_t, derive_secrets, void,
|
||||
private_tls_crypto_t *this, chunk_t premaster,
|
||||
chunk_t client_random, chunk_t server_random)
|
||||
{
|
||||
char master[48];
|
||||
chunk_t seed, block, client_write, server_write;
|
||||
int mks, eks = 0, ivs = 0;
|
||||
|
||||
/* derive master secret */
|
||||
seed = chunk_cata("cc", client_random, server_random);
|
||||
this->prf->set_key(this->prf, premaster);
|
||||
this->prf->get_bytes(this->prf, "master secret", seed,
|
||||
sizeof(master), master);
|
||||
|
||||
this->prf->set_key(this->prf, chunk_from_thing(master));
|
||||
memset(master, 0, sizeof(master));
|
||||
|
||||
/* derive key block for key expansion */
|
||||
mks = this->signer_out->get_key_size(this->signer_out);
|
||||
if (this->crypter_out)
|
||||
{
|
||||
eks = this->crypter_out->get_key_size(this->crypter_out);
|
||||
if (this->tls->get_version(this->tls) < TLS_1_1)
|
||||
{
|
||||
ivs = this->crypter_out->get_block_size(this->crypter_out);
|
||||
}
|
||||
}
|
||||
seed = chunk_cata("cc", server_random, client_random);
|
||||
block = chunk_alloca((mks + eks + ivs) * 2);
|
||||
this->prf->get_bytes(this->prf, "key expansion", seed, block.len, block.ptr);
|
||||
|
||||
/* signer keys */
|
||||
client_write = chunk_create(block.ptr, mks);
|
||||
block = chunk_skip(block, mks);
|
||||
server_write = chunk_create(block.ptr, mks);
|
||||
block = chunk_skip(block, mks);
|
||||
if (this->tls->is_server(this->tls))
|
||||
{
|
||||
this->signer_in->set_key(this->signer_in, client_write);
|
||||
this->signer_out->set_key(this->signer_out, server_write);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->signer_out->set_key(this->signer_out, client_write);
|
||||
this->signer_in->set_key(this->signer_in, server_write);
|
||||
}
|
||||
|
||||
/* crypter keys, and IVs if < TLSv1.2 */
|
||||
if (this->crypter_out && this->crypter_in)
|
||||
{
|
||||
client_write = chunk_create(block.ptr, eks);
|
||||
block = chunk_skip(block, eks);
|
||||
server_write = chunk_create(block.ptr, eks);
|
||||
block = chunk_skip(block, eks);
|
||||
|
||||
if (this->tls->is_server(this->tls))
|
||||
{
|
||||
this->crypter_in->set_key(this->crypter_in, client_write);
|
||||
this->crypter_out->set_key(this->crypter_out, server_write);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->crypter_out->set_key(this->crypter_out, client_write);
|
||||
this->crypter_in->set_key(this->crypter_in, server_write);
|
||||
}
|
||||
if (ivs)
|
||||
{
|
||||
client_write = chunk_create(block.ptr, ivs);
|
||||
block = chunk_skip(block, ivs);
|
||||
server_write = chunk_create(block.ptr, ivs);
|
||||
block = chunk_skip(block, ivs);
|
||||
|
||||
if (this->tls->is_server(this->tls))
|
||||
{
|
||||
this->iv_in = chunk_clone(client_write);
|
||||
this->iv_out = chunk_clone(server_write);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->iv_out = chunk_clone(client_write);
|
||||
this->iv_in = chunk_clone(server_write);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
METHOD(tls_crypto_t, change_cipher, void,
|
||||
private_tls_crypto_t *this, bool inbound)
|
||||
{
|
||||
if (this->protection)
|
||||
{
|
||||
if (inbound)
|
||||
{
|
||||
this->protection->set_cipher(this->protection, TRUE,
|
||||
this->signer_in, this->crypter_in, this->iv_in);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->protection->set_cipher(this->protection, FALSE,
|
||||
this->signer_out, this->crypter_out, this->iv_out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
METHOD(tls_crypto_t, derive_eap_msk, void,
|
||||
private_tls_crypto_t *this, chunk_t client_random, chunk_t server_random)
|
||||
{
|
||||
chunk_t seed;
|
||||
|
||||
seed = chunk_cata("cc", client_random, server_random);
|
||||
free(this->msk.ptr);
|
||||
this->msk = chunk_alloc(64);
|
||||
this->prf->get_bytes(this->prf, "client EAP encryption", seed,
|
||||
this->msk.len, this->msk.ptr);
|
||||
}
|
||||
|
||||
METHOD(tls_crypto_t, get_eap_msk, chunk_t,
|
||||
private_tls_crypto_t *this)
|
||||
{
|
||||
return this->msk;
|
||||
}
|
||||
|
||||
METHOD(tls_crypto_t, destroy, void,
|
||||
private_tls_crypto_t *this)
|
||||
{
|
||||
DESTROY_IF(this->signer_in);
|
||||
DESTROY_IF(this->signer_out);
|
||||
DESTROY_IF(this->crypter_in);
|
||||
DESTROY_IF(this->crypter_out);
|
||||
free(this->iv_in.ptr);
|
||||
free(this->iv_out.ptr);
|
||||
free(this->handshake.ptr);
|
||||
free(this->msk.ptr);
|
||||
DESTROY_IF(this->prf);
|
||||
free(this->suites);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
tls_crypto_t *tls_crypto_create(tls_t *tls)
|
||||
{
|
||||
private_tls_crypto_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.get_cipher_suites = _get_cipher_suites,
|
||||
.select_cipher_suite = _select_cipher_suite,
|
||||
.set_protection = _set_protection,
|
||||
.append_handshake = _append_handshake,
|
||||
.sign_handshake = _sign_handshake,
|
||||
.verify_handshake = _verify_handshake,
|
||||
.calculate_finished = _calculate_finished,
|
||||
.derive_secrets = _derive_secrets,
|
||||
.change_cipher = _change_cipher,
|
||||
.derive_eap_msk = _derive_eap_msk,
|
||||
.get_eap_msk = _get_eap_msk,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.tls = tls,
|
||||
);
|
||||
|
||||
build_cipher_suite_list(this);
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* 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_crypto tls_crypto
|
||||
* @{ @ingroup tls
|
||||
*/
|
||||
|
||||
#ifndef TLS_CRYPTO_H_
|
||||
#define TLS_CRYPTO_H_
|
||||
|
||||
typedef struct tls_crypto_t tls_crypto_t;
|
||||
typedef enum tls_cipher_suite_t tls_cipher_suite_t;
|
||||
|
||||
#include "tls.h"
|
||||
#include "tls_prf.h"
|
||||
#include "tls_protection.h"
|
||||
|
||||
#include <credentials/keys/private_key.h>
|
||||
|
||||
/**
|
||||
* TLS cipher suites
|
||||
*/
|
||||
enum tls_cipher_suite_t {
|
||||
TLS_NULL_WITH_NULL_NULL = 0x00,
|
||||
TLS_RSA_WITH_NULL_MD5 = 0x01,
|
||||
TLS_RSA_WITH_NULL_SHA = 0x02,
|
||||
TLS_RSA_WITH_NULL_SHA256 = 0x3B,
|
||||
TLS_RSA_WITH_RC4_128_MD5 = 0x04,
|
||||
TLS_RSA_WITH_RC4_128_SHA = 0x05,
|
||||
TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x0A,
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA = 0x2F,
|
||||
TLS_RSA_WITH_AES_256_CBC_SHA = 0x35,
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x3C,
|
||||
TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x3D,
|
||||
TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0x0D,
|
||||
TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0x10,
|
||||
TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x13,
|
||||
TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x16,
|
||||
TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0x30,
|
||||
TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0x31,
|
||||
TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x32,
|
||||
TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x33,
|
||||
TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0x36,
|
||||
TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0x37,
|
||||
TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x38,
|
||||
TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x39,
|
||||
TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = 0x3E,
|
||||
TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = 0x3F,
|
||||
TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 0x40,
|
||||
TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x67,
|
||||
TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = 0x68,
|
||||
TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = 0x69,
|
||||
TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 0x6A,
|
||||
TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x6B,
|
||||
TLS_DH_ANON_WITH_RC4_128_MD5 = 0x18,
|
||||
TLS_DH_ANON_WITH_3DES_EDE_CBC_SHA = 0x1B,
|
||||
TLS_DH_ANON_WITH_AES_128_CBC_SHA = 0x34,
|
||||
TLS_DH_ANON_WITH_AES_256_CBC_SHA = 0x3A,
|
||||
TLS_DH_ANON_WITH_AES_128_CBC_SHA256 = 0x6C,
|
||||
TLS_DH_ANON_WITH_AES_256_CBC_SHA256 = 0x6D,
|
||||
};
|
||||
|
||||
/**
|
||||
* TLS crypto helper functions.
|
||||
*/
|
||||
struct tls_crypto_t {
|
||||
|
||||
/**
|
||||
* Get a list of supported TLS cipher suites.
|
||||
*
|
||||
* @param suites list of suites, points to internal data
|
||||
* @return number of suites returned
|
||||
*/
|
||||
int (*get_cipher_suites)(tls_crypto_t *this, tls_cipher_suite_t **suites);
|
||||
|
||||
/**
|
||||
* Select and store a cipher suite from a given list of candidates.
|
||||
*
|
||||
* @param suites list of candidates to select from
|
||||
* @param count number of suites
|
||||
* @return selected suite, 0 if none acceptable
|
||||
*/
|
||||
tls_cipher_suite_t (*select_cipher_suite)(tls_crypto_t *this,
|
||||
tls_cipher_suite_t *suites, int count);
|
||||
|
||||
/**
|
||||
* Set the protection layer of the TLS stack to control it.
|
||||
*
|
||||
* @param protection protection layer to work on
|
||||
*/
|
||||
void (*set_protection)(tls_crypto_t *this, tls_protection_t *protection);
|
||||
|
||||
/**
|
||||
* Store exchanged handshake data, used for cryptographic operations.
|
||||
*
|
||||
* @param type handshake sub type
|
||||
* @param data data to append to handshake buffer
|
||||
*/
|
||||
void (*append_handshake)(tls_crypto_t *this,
|
||||
tls_handshake_type_t type, chunk_t data);
|
||||
|
||||
/**
|
||||
* Create a signature of the handshake data using a given private key.
|
||||
*
|
||||
* @param key private key to use for signature
|
||||
* @param writer TLS writer to write signature to
|
||||
* @return TRUE if signature create successfully
|
||||
*/
|
||||
bool (*sign_handshake)(tls_crypto_t *this, private_key_t *key,
|
||||
tls_writer_t *writer);
|
||||
|
||||
/**
|
||||
* Verify the signature over handshake data using a given public key.
|
||||
*
|
||||
* @param key public key to verify signature with
|
||||
* @param reader TLS reader to read signature from
|
||||
* @return TRUE if signature valid
|
||||
*/
|
||||
bool (*verify_handshake)(tls_crypto_t *this, public_key_t *key,
|
||||
tls_reader_t *reader);
|
||||
|
||||
/**
|
||||
* Calculate the data of a TLS finished message.
|
||||
*
|
||||
* @param label ASCII label to use for calculation
|
||||
* @param out buffer to write finished data to
|
||||
* @return TRUE if calculation successful
|
||||
*/
|
||||
bool (*calculate_finished)(tls_crypto_t *this, char *label, char out[12]);
|
||||
|
||||
/**
|
||||
* Derive the master secret, MAC and encryption keys.
|
||||
*
|
||||
* @param premaster premaster secret
|
||||
* @param client_random random data from client hello
|
||||
* @param server_random random data from server hello
|
||||
*/
|
||||
void (*derive_secrets)(tls_crypto_t *this, chunk_t premaster,
|
||||
chunk_t client_random, chunk_t server_random);
|
||||
|
||||
/**
|
||||
* Change the cipher used at protection layer.
|
||||
*
|
||||
* @param inbound TRUE to change inbound cipher, FALSE for outbound
|
||||
*/
|
||||
void (*change_cipher)(tls_crypto_t *this, bool inbound);
|
||||
|
||||
/**
|
||||
* Derive the EAP-TLS MSK.
|
||||
*
|
||||
* @param client_random random data from client hello
|
||||
* @param server_random random data from server hello
|
||||
*/
|
||||
void (*derive_eap_msk)(tls_crypto_t *this,
|
||||
chunk_t client_random, chunk_t server_random);
|
||||
|
||||
/**
|
||||
* Get the MSK to use in EAP-TLS.
|
||||
*
|
||||
* @return MSK, points to internal data
|
||||
*/
|
||||
chunk_t (*get_eap_msk)(tls_crypto_t *this);
|
||||
|
||||
/**
|
||||
* Destroy a tls_crypto_t.
|
||||
*/
|
||||
void (*destroy)(tls_crypto_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a tls_crypto instance.
|
||||
*/
|
||||
tls_crypto_t *tls_crypto_create(tls_t *tls);
|
||||
|
||||
#endif /** TLS_CRYPTO_H_ @}*/
|
||||
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
* 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_fragmentation.h"
|
||||
|
||||
#include "tls_reader.h"
|
||||
|
||||
#include <daemon.h>
|
||||
|
||||
typedef struct private_tls_fragmentation_t private_tls_fragmentation_t;
|
||||
|
||||
/**
|
||||
* Private data of an tls_fragmentation_t object.
|
||||
*/
|
||||
struct private_tls_fragmentation_t {
|
||||
|
||||
/**
|
||||
* Public tls_fragmentation_t interface.
|
||||
*/
|
||||
tls_fragmentation_t public;
|
||||
|
||||
/**
|
||||
* Upper layer handshake protocol
|
||||
*/
|
||||
tls_handshake_t *handshake;
|
||||
|
||||
/**
|
||||
* Handshake input buffer
|
||||
*/
|
||||
chunk_t input;
|
||||
|
||||
/**
|
||||
* Position in input buffer
|
||||
*/
|
||||
size_t inpos;
|
||||
|
||||
/**
|
||||
* Currently processed handshake message type
|
||||
*/
|
||||
tls_handshake_type_t type;
|
||||
|
||||
/**
|
||||
* Handshake output buffer
|
||||
*/
|
||||
chunk_t output;
|
||||
};
|
||||
|
||||
/**
|
||||
* Maximum size of a TLS fragment
|
||||
*/
|
||||
#define MAX_TLS_FRAGMENT_LEN 16384
|
||||
|
||||
/**
|
||||
* Maximum size of a TLS handshake message we accept
|
||||
*/
|
||||
#define MAX_TLS_HANDSHAKE_LEN 65536
|
||||
|
||||
/**
|
||||
* Process TLS handshake protocol data
|
||||
*/
|
||||
static status_t process_handshake(private_tls_fragmentation_t *this,
|
||||
tls_reader_t *reader)
|
||||
{
|
||||
while (reader->remaining(reader))
|
||||
{
|
||||
tls_reader_t *msg;
|
||||
u_int8_t type;
|
||||
u_int32_t len;
|
||||
status_t status;
|
||||
chunk_t data;
|
||||
|
||||
if (reader->remaining(reader) > MAX_TLS_FRAGMENT_LEN)
|
||||
{
|
||||
DBG1(DBG_IKE, "TLS fragment has invalid length");
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
if (this->input.len == 0)
|
||||
{ /* new handshake message */
|
||||
if (!reader->read_uint8(reader, &type) ||
|
||||
!reader->read_uint24(reader, &len))
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
this->type = type;
|
||||
if (len > MAX_TLS_HANDSHAKE_LEN)
|
||||
{
|
||||
DBG1(DBG_IKE, "TLS handshake message exceeds maximum length");
|
||||
return FAILED;
|
||||
}
|
||||
chunk_free(&this->input);
|
||||
this->inpos = 0;
|
||||
if (len)
|
||||
{
|
||||
this->input = chunk_alloc(len);
|
||||
}
|
||||
}
|
||||
|
||||
len = min(this->input.len - this->inpos, reader->remaining(reader));
|
||||
if (!reader->read_data(reader, len, &data))
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
memcpy(this->input.ptr + this->inpos, data.ptr, len);
|
||||
this->inpos += len;
|
||||
|
||||
if (this->input.len == this->inpos)
|
||||
{ /* message completely defragmented, process */
|
||||
msg = tls_reader_create(this->input);
|
||||
status = this->handshake->process(this->handshake, this->type, msg);
|
||||
msg->destroy(msg);
|
||||
chunk_free(&this->input);
|
||||
if (status != NEED_MORE)
|
||||
{
|
||||
return status;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
METHOD(tls_fragmentation_t, process, status_t,
|
||||
private_tls_fragmentation_t *this, tls_content_type_t type, chunk_t data)
|
||||
{
|
||||
tls_reader_t *reader;
|
||||
status_t status;
|
||||
|
||||
reader = tls_reader_create(data);
|
||||
switch (type)
|
||||
{
|
||||
case TLS_CHANGE_CIPHER_SPEC:
|
||||
if (this->handshake->change_cipherspec(this->handshake))
|
||||
{
|
||||
status = NEED_MORE;
|
||||
break;
|
||||
}
|
||||
status = FAILED;
|
||||
break;
|
||||
case TLS_ALERT:
|
||||
/* TODO: handle Alert */
|
||||
status = FAILED;
|
||||
break;
|
||||
case TLS_HANDSHAKE:
|
||||
status = process_handshake(this, reader);
|
||||
break;
|
||||
case TLS_APPLICATION_DATA:
|
||||
/* skip application data */
|
||||
status = NEED_MORE;
|
||||
break;
|
||||
default:
|
||||
DBG1(DBG_IKE, "received unknown TLS content type %d, ignored", type);
|
||||
status = NEED_MORE;
|
||||
break;
|
||||
}
|
||||
reader->destroy(reader);
|
||||
return status;
|
||||
}
|
||||
|
||||
METHOD(tls_fragmentation_t, build, status_t,
|
||||
private_tls_fragmentation_t *this, tls_content_type_t *type, chunk_t *data)
|
||||
{
|
||||
tls_handshake_type_t hs_type;
|
||||
tls_writer_t *writer, *msg;
|
||||
status_t status;
|
||||
|
||||
if (this->handshake->cipherspec_changed(this->handshake))
|
||||
{
|
||||
*type = TLS_CHANGE_CIPHER_SPEC;
|
||||
*data = chunk_clone(chunk_from_chars(0x01));
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
if (!this->output.len)
|
||||
{
|
||||
msg = tls_writer_create(64);
|
||||
do
|
||||
{
|
||||
writer = tls_writer_create(64);
|
||||
status = this->handshake->build(this->handshake, &hs_type, writer);
|
||||
switch (status)
|
||||
{
|
||||
case NEED_MORE:
|
||||
msg->write_uint8(msg, hs_type);
|
||||
msg->write_data24(msg, writer->get_buf(writer));
|
||||
break;
|
||||
case INVALID_STATE:
|
||||
this->output = chunk_clone(msg->get_buf(msg));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
writer->destroy(writer);
|
||||
}
|
||||
while (status == NEED_MORE);
|
||||
|
||||
msg->destroy(msg);
|
||||
if (status != INVALID_STATE)
|
||||
{
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->output.len)
|
||||
{
|
||||
*type = TLS_HANDSHAKE;
|
||||
if (this->output.len <= MAX_TLS_FRAGMENT_LEN)
|
||||
{
|
||||
*data = this->output;
|
||||
this->output = chunk_empty;
|
||||
return NEED_MORE;
|
||||
}
|
||||
*data = chunk_create(this->output.ptr, MAX_TLS_FRAGMENT_LEN);
|
||||
this->output = chunk_clone(chunk_skip(this->output, MAX_TLS_FRAGMENT_LEN));
|
||||
return NEED_MORE;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
METHOD(tls_fragmentation_t, destroy, void,
|
||||
private_tls_fragmentation_t *this)
|
||||
{
|
||||
free(this->input.ptr);
|
||||
free(this->output.ptr);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
tls_fragmentation_t *tls_fragmentation_create(tls_handshake_t *handshake)
|
||||
{
|
||||
private_tls_fragmentation_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.process = _process,
|
||||
.build = _build,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.handshake = handshake,
|
||||
);
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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_fragmentation tls_fragmentation
|
||||
* @{ @ingroup tls
|
||||
*/
|
||||
|
||||
#ifndef TLS_FRAGMENTATION_H_
|
||||
#define TLS_FRAGMENTATION_H_
|
||||
|
||||
typedef struct tls_fragmentation_t tls_fragmentation_t;
|
||||
|
||||
#include <library.h>
|
||||
|
||||
#include "tls.h"
|
||||
#include "tls_handshake.h"
|
||||
|
||||
/**
|
||||
* TLS record protocol fragmentation layer.
|
||||
*/
|
||||
struct tls_fragmentation_t {
|
||||
|
||||
/**
|
||||
* Process a fragmented TLS record, pass it to upper layers.
|
||||
*
|
||||
* @param type type of the TLS record to process
|
||||
* @param data associated TLS record data
|
||||
* @return
|
||||
* - SUCCESS if TLS negotiation complete
|
||||
* - FAILED if TLS handshake failed
|
||||
* - NEED_MORE if more invocations to process/build needed
|
||||
*/
|
||||
status_t (*process)(tls_fragmentation_t *this,
|
||||
tls_content_type_t type, chunk_t data);
|
||||
|
||||
/**
|
||||
* Query upper layer for TLS messages, build fragmented records.
|
||||
*
|
||||
* @param type type of the built TLS record
|
||||
* @param data allocated data of the built TLS record
|
||||
* @return
|
||||
* - SUCCESS if TLS negotiation complete
|
||||
* - FAILED if TLS handshake failed
|
||||
* - NEED_MORE if upper layers have more records to send
|
||||
* - INVALID_STATE if more input records required
|
||||
*/
|
||||
status_t (*build)(tls_fragmentation_t *this,
|
||||
tls_content_type_t *type, chunk_t *data);
|
||||
|
||||
/**
|
||||
* Destroy a tls_fragmentation_t.
|
||||
*/
|
||||
void (*destroy)(tls_fragmentation_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a tls_fragmentation instance.
|
||||
*
|
||||
* @param handshake upper layer handshake protocol
|
||||
* @return TLS fragmentation layer.
|
||||
*/
|
||||
tls_fragmentation_t *tls_fragmentation_create(tls_handshake_t *handshake);
|
||||
|
||||
#endif /** TLS_FRAGMENTATION_H_ @}*/
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Martin Willi
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* 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_handshake tls_handshake
|
||||
* @{ @ingroup tls
|
||||
*/
|
||||
|
||||
#ifndef TLS_HANDSHAKE_H_
|
||||
#define TLS_HANDSHAKE_H_
|
||||
|
||||
typedef struct tls_handshake_t tls_handshake_t;
|
||||
|
||||
#include "tls.h"
|
||||
#include "tls_reader.h"
|
||||
#include "tls_writer.h"
|
||||
|
||||
/**
|
||||
* TLS handshake state machine interface.
|
||||
*/
|
||||
struct tls_handshake_t {
|
||||
|
||||
/**
|
||||
* Process received TLS handshake message.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
status_t (*process)(tls_handshake_t *this,
|
||||
tls_handshake_type_t type, tls_reader_t *reader);
|
||||
|
||||
/**
|
||||
* Build TLS handshake messages to send out.
|
||||
*
|
||||
* @param type type of created handshake message
|
||||
* @param writer TLS data buffer to write to
|
||||
* @return
|
||||
* - SUCCESS if handshake complete
|
||||
* - FAILED if handshake failed
|
||||
* - NEED_MORE if more messages ready for delivery
|
||||
* - INVALID_STATE if more input to process() required
|
||||
*/
|
||||
status_t (*build)(tls_handshake_t *this,
|
||||
tls_handshake_type_t *type, tls_writer_t *writer);
|
||||
|
||||
/**
|
||||
* Check if the cipher spec for outgoing messages has changed.
|
||||
*
|
||||
* @return TRUE if cipher spec changed
|
||||
*/
|
||||
bool (*cipherspec_changed)(tls_handshake_t *this);
|
||||
|
||||
/**
|
||||
* Change the cipher spec for incoming messages.
|
||||
*
|
||||
* @return TRUE if cipher spec changed
|
||||
*/
|
||||
bool (*change_cipherspec)(tls_handshake_t *this);
|
||||
|
||||
/**
|
||||
* Destroy a tls_handshake_t.
|
||||
*/
|
||||
void (*destroy)(tls_handshake_t *this);
|
||||
};
|
||||
|
||||
#endif /** TLS_HANDSHAKE_H_ @}*/
|
||||
@@ -0,0 +1,625 @@
|
||||
/*
|
||||
* 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_peer.h"
|
||||
|
||||
#include <daemon.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
typedef struct private_tls_peer_t private_tls_peer_t;
|
||||
|
||||
typedef enum {
|
||||
STATE_INIT,
|
||||
STATE_HELLO_SENT,
|
||||
STATE_HELLO_RECEIVED,
|
||||
STATE_HELLO_DONE,
|
||||
STATE_CERT_SENT,
|
||||
STATE_CERT_RECEIVED,
|
||||
STATE_CERTREQ_RECEIVED,
|
||||
STATE_KEY_EXCHANGE_SENT,
|
||||
STATE_VERIFY_SENT,
|
||||
STATE_CIPHERSPEC_CHANGED_OUT,
|
||||
STATE_FINISHED_SENT,
|
||||
STATE_CIPHERSPEC_CHANGED_IN,
|
||||
STATE_COMPLETE,
|
||||
} peer_state_t;
|
||||
|
||||
/**
|
||||
* Private data of an tls_peer_t object.
|
||||
*/
|
||||
struct private_tls_peer_t {
|
||||
|
||||
/**
|
||||
* Public tls_peer_t interface.
|
||||
*/
|
||||
tls_peer_t public;
|
||||
|
||||
/**
|
||||
* TLS stack
|
||||
*/
|
||||
tls_t *tls;
|
||||
|
||||
/**
|
||||
* TLS crypto context
|
||||
*/
|
||||
tls_crypto_t *crypto;
|
||||
|
||||
/**
|
||||
* Peer identity
|
||||
*/
|
||||
identification_t *peer;
|
||||
|
||||
/**
|
||||
* Server identity
|
||||
*/
|
||||
identification_t *server;
|
||||
|
||||
/**
|
||||
* State we are in
|
||||
*/
|
||||
peer_state_t state;
|
||||
|
||||
/**
|
||||
* Hello random data selected by client
|
||||
*/
|
||||
char client_random[32];
|
||||
|
||||
/**
|
||||
* Hello random data selected by server
|
||||
*/
|
||||
char server_random[32];
|
||||
|
||||
/**
|
||||
* Auth helper for peer authentication
|
||||
*/
|
||||
auth_cfg_t *peer_auth;
|
||||
|
||||
/**
|
||||
* Auth helper for server authentication
|
||||
*/
|
||||
auth_cfg_t *server_auth;
|
||||
|
||||
/**
|
||||
* Peer private key
|
||||
*/
|
||||
private_key_t *private;
|
||||
};
|
||||
|
||||
/**
|
||||
* Process a server hello message
|
||||
*/
|
||||
static status_t process_server_hello(private_tls_peer_t *this,
|
||||
tls_reader_t *reader)
|
||||
{
|
||||
u_int8_t compression;
|
||||
u_int16_t version, cipher;
|
||||
chunk_t random, session, ext = chunk_empty;
|
||||
tls_cipher_suite_t suite;
|
||||
|
||||
this->crypto->append_handshake(this->crypto,
|
||||
TLS_SERVER_HELLO, reader->peek(reader));
|
||||
|
||||
if (!reader->read_uint16(reader, &version) ||
|
||||
!reader->read_data(reader, sizeof(this->server_random), &random) ||
|
||||
!reader->read_data8(reader, &session) ||
|
||||
!reader->read_uint16(reader, &cipher) ||
|
||||
!reader->read_uint8(reader, &compression) ||
|
||||
(reader->remaining(reader) && !reader->read_data16(reader, &ext)))
|
||||
{
|
||||
DBG1(DBG_IKE, "received invalid ServerHello");
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
memcpy(this->server_random, random.ptr, sizeof(this->server_random));
|
||||
|
||||
if (version < this->tls->get_version(this->tls))
|
||||
{
|
||||
this->tls->set_version(this->tls, version);
|
||||
}
|
||||
suite = cipher;
|
||||
if (!this->crypto->select_cipher_suite(this->crypto, &suite, 1))
|
||||
{
|
||||
DBG1(DBG_IKE, "received cipher suite inacceptable");
|
||||
return FAILED;
|
||||
}
|
||||
this->state = STATE_HELLO_RECEIVED;
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a Certificate message
|
||||
*/
|
||||
static status_t process_certificate(private_tls_peer_t *this,
|
||||
tls_reader_t *reader)
|
||||
{
|
||||
certificate_t *cert;
|
||||
tls_reader_t *certs;
|
||||
chunk_t data;
|
||||
bool first = TRUE;
|
||||
|
||||
this->crypto->append_handshake(this->crypto,
|
||||
TLS_CERTIFICATE, reader->peek(reader));
|
||||
|
||||
if (!reader->read_data24(reader, &data))
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
certs = tls_reader_create(data);
|
||||
while (certs->remaining(certs))
|
||||
{
|
||||
if (!certs->read_data24(certs, &data))
|
||||
{
|
||||
certs->destroy(certs);
|
||||
return FAILED;
|
||||
}
|
||||
cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
|
||||
BUILD_BLOB_ASN1_DER, data, BUILD_END);
|
||||
if (cert)
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
this->server_auth->add(this->server_auth,
|
||||
AUTH_HELPER_SUBJECT_CERT, cert);
|
||||
DBG1(DBG_IKE, "received TLS server certificate '%Y'",
|
||||
cert->get_subject(cert));
|
||||
first = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG1(DBG_IKE, "received TLS intermediate certificate '%Y'",
|
||||
cert->get_subject(cert));
|
||||
this->server_auth->add(this->server_auth,
|
||||
AUTH_HELPER_IM_CERT, cert);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG1(DBG_IKE, "parsing TLS certificate failed, skipped");
|
||||
}
|
||||
}
|
||||
certs->destroy(certs);
|
||||
this->state = STATE_CERT_RECEIVED;
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a Certificate message
|
||||
*/
|
||||
static status_t process_certreq(private_tls_peer_t *this, tls_reader_t *reader)
|
||||
{
|
||||
chunk_t types, hashsig, data;
|
||||
tls_reader_t *authorities;
|
||||
identification_t *id;
|
||||
certificate_t *cert;
|
||||
|
||||
this->crypto->append_handshake(this->crypto,
|
||||
TLS_CERTIFICATE_REQUEST, reader->peek(reader));
|
||||
|
||||
if (!reader->read_data8(reader, &types))
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
if (this->tls->get_version(this->tls) >= TLS_1_2)
|
||||
{
|
||||
if (!reader->read_data16(reader, &hashsig))
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
/* TODO: store supported hashsig algorithms */
|
||||
}
|
||||
if (!reader->read_data16(reader, &data))
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
authorities = tls_reader_create(data);
|
||||
while (authorities->remaining(authorities))
|
||||
{
|
||||
if (!authorities->read_data16(authorities, &data))
|
||||
{
|
||||
authorities->destroy(authorities);
|
||||
return FAILED;
|
||||
}
|
||||
id = identification_create_from_encoding(ID_DER_ASN1_DN, data);
|
||||
cert = lib->credmgr->get_cert(lib->credmgr,
|
||||
CERT_X509, KEY_ANY, id, TRUE);
|
||||
if (cert)
|
||||
{
|
||||
DBG1(DBG_IKE, "received cert request for '%Y", id);
|
||||
this->peer_auth->add(this->peer_auth, AUTH_RULE_CA_CERT, cert);
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG1(DBG_IKE, "received cert request for unknown CA '%Y'", id);
|
||||
}
|
||||
id->destroy(id);
|
||||
}
|
||||
authorities->destroy(authorities);
|
||||
this->state = STATE_CERTREQ_RECEIVED;
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process Hello Done message
|
||||
*/
|
||||
static status_t process_hello_done(private_tls_peer_t *this,
|
||||
tls_reader_t *reader)
|
||||
{
|
||||
this->crypto->append_handshake(this->crypto,
|
||||
TLS_SERVER_HELLO_DONE, reader->peek(reader));
|
||||
this->state = STATE_HELLO_DONE;
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process finished message
|
||||
*/
|
||||
static status_t process_finished(private_tls_peer_t *this, tls_reader_t *reader)
|
||||
{
|
||||
chunk_t received;
|
||||
char buf[12];
|
||||
|
||||
if (!reader->read_data(reader, sizeof(buf), &received))
|
||||
{
|
||||
DBG1(DBG_IKE, "received server finished too short");
|
||||
return FAILED;
|
||||
}
|
||||
if (!this->crypto->calculate_finished(this->crypto, "server finished", buf))
|
||||
{
|
||||
DBG1(DBG_IKE, "calculating server finished failed");
|
||||
return FAILED;
|
||||
}
|
||||
if (!chunk_equals(received, chunk_from_thing(buf)))
|
||||
{
|
||||
DBG1(DBG_IKE, "received server finished invalid");
|
||||
return FAILED;
|
||||
}
|
||||
this->state = STATE_COMPLETE;
|
||||
this->crypto->derive_eap_msk(this->crypto,
|
||||
chunk_from_thing(this->client_random),
|
||||
chunk_from_thing(this->server_random));
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
METHOD(tls_handshake_t, process, status_t,
|
||||
private_tls_peer_t *this, tls_handshake_type_t type, tls_reader_t *reader)
|
||||
{
|
||||
tls_handshake_type_t expected;
|
||||
|
||||
switch (this->state)
|
||||
{
|
||||
case STATE_HELLO_SENT:
|
||||
if (type == TLS_SERVER_HELLO)
|
||||
{
|
||||
return process_server_hello(this, reader);
|
||||
}
|
||||
expected = TLS_SERVER_HELLO;
|
||||
break;
|
||||
case STATE_HELLO_RECEIVED:
|
||||
if (type == TLS_CERTIFICATE)
|
||||
{
|
||||
return process_certificate(this, reader);
|
||||
}
|
||||
expected = TLS_CERTIFICATE;
|
||||
break;
|
||||
case STATE_CERT_RECEIVED:
|
||||
if (type == TLS_CERTIFICATE_REQUEST)
|
||||
{
|
||||
return process_certreq(this, reader);
|
||||
}
|
||||
expected = TLS_CERTIFICATE_REQUEST;
|
||||
break;
|
||||
case STATE_CERTREQ_RECEIVED:
|
||||
if (type == TLS_SERVER_HELLO_DONE)
|
||||
{
|
||||
return process_hello_done(this, reader);
|
||||
}
|
||||
expected = TLS_SERVER_HELLO_DONE;
|
||||
break;
|
||||
case STATE_CIPHERSPEC_CHANGED_IN:
|
||||
if (type == TLS_FINISHED)
|
||||
{
|
||||
return process_finished(this, reader);
|
||||
}
|
||||
expected = TLS_FINISHED;
|
||||
break;
|
||||
default:
|
||||
DBG1(DBG_IKE, "TLS %N not expected in current state",
|
||||
tls_handshake_type_names, type);
|
||||
return FAILED;
|
||||
}
|
||||
DBG1(DBG_IKE, "TLS %N expected, but received %N",
|
||||
tls_handshake_type_names, expected, tls_handshake_type_names, type);
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a client hello
|
||||
*/
|
||||
static status_t send_client_hello(private_tls_peer_t *this,
|
||||
tls_handshake_type_t *type, tls_writer_t *writer)
|
||||
{
|
||||
tls_cipher_suite_t *suite;
|
||||
int count, i;
|
||||
rng_t *rng;
|
||||
|
||||
htoun32(&this->client_random, time(NULL));
|
||||
rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
|
||||
if (!rng)
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
rng->get_bytes(rng, sizeof(this->client_random) - 4, this->client_random + 4);
|
||||
rng->destroy(rng);
|
||||
|
||||
writer->write_uint16(writer, this->tls->get_version(this->tls));
|
||||
writer->write_data(writer, chunk_from_thing(this->client_random));
|
||||
/* session identifier => none */
|
||||
writer->write_data8(writer, chunk_empty);
|
||||
|
||||
count = this->crypto->get_cipher_suites(this->crypto, &suite);
|
||||
writer->write_uint16(writer, count * 2);
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
writer->write_uint16(writer, suite[i]);
|
||||
}
|
||||
/* NULL compression only */
|
||||
writer->write_uint8(writer, 1);
|
||||
writer->write_uint8(writer, 0);
|
||||
|
||||
*type = TLS_CLIENT_HELLO;
|
||||
this->state = STATE_HELLO_SENT;
|
||||
this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send Certificate
|
||||
*/
|
||||
static status_t send_certificate(private_tls_peer_t *this,
|
||||
tls_handshake_type_t *type, tls_writer_t *writer)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
certificate_t *cert;
|
||||
auth_rule_t rule;
|
||||
tls_writer_t *certs;
|
||||
chunk_t data;
|
||||
|
||||
this->private = lib->credmgr->get_private(lib->credmgr,
|
||||
KEY_ANY, this->peer, this->peer_auth);
|
||||
if (!this->private)
|
||||
{
|
||||
DBG1(DBG_IKE, "no TLS peer certificate found for '%Y'", this->peer);
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
/* generate certificate payload */
|
||||
certs = tls_writer_create(256);
|
||||
cert = this->peer_auth->get(this->peer_auth, AUTH_RULE_SUBJECT_CERT);
|
||||
if (cert)
|
||||
{
|
||||
if (cert->get_encoding(cert, CERT_ASN1_DER, &data))
|
||||
{
|
||||
DBG1(DBG_IKE, "sending TLS peer certificate '%Y'",
|
||||
cert->get_subject(cert));
|
||||
certs->write_data24(certs, data);
|
||||
free(data.ptr);
|
||||
}
|
||||
}
|
||||
enumerator = this->peer_auth->create_enumerator(this->peer_auth);
|
||||
while (enumerator->enumerate(enumerator, &rule, &cert))
|
||||
{
|
||||
if (rule == AUTH_RULE_IM_CERT)
|
||||
{
|
||||
if (cert->get_encoding(cert, CERT_ASN1_DER, &data))
|
||||
{
|
||||
DBG1(DBG_IKE, "sending TLS intermediate certificate '%Y'",
|
||||
cert->get_subject(cert));
|
||||
certs->write_data24(certs, data);
|
||||
free(data.ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
|
||||
writer->write_data24(writer, certs->get_buf(certs));
|
||||
certs->destroy(certs);
|
||||
|
||||
*type = TLS_CERTIFICATE;
|
||||
this->state = STATE_CERT_SENT;
|
||||
this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send client key exchange
|
||||
*/
|
||||
static status_t send_key_exchange(private_tls_peer_t *this,
|
||||
tls_handshake_type_t *type, tls_writer_t *writer)
|
||||
{
|
||||
public_key_t *public = NULL, *current;
|
||||
enumerator_t *enumerator;
|
||||
auth_cfg_t *auth;
|
||||
rng_t *rng;
|
||||
char premaster[48];
|
||||
chunk_t encrypted;
|
||||
|
||||
rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
|
||||
if (!rng)
|
||||
{
|
||||
DBG1(DBG_IKE, "no suitable RNG found for TLS premaster secret");
|
||||
return FAILED;
|
||||
}
|
||||
rng->get_bytes(rng, sizeof(premaster) - 2, premaster + 2);
|
||||
rng->destroy(rng);
|
||||
htoun16(premaster, TLS_1_2);
|
||||
|
||||
this->crypto->derive_secrets(this->crypto, chunk_from_thing(premaster),
|
||||
chunk_from_thing(this->client_random),
|
||||
chunk_from_thing(this->server_random));
|
||||
|
||||
enumerator = lib->credmgr->create_public_enumerator(lib->credmgr,
|
||||
KEY_ANY, this->server, this->server_auth);
|
||||
while (enumerator->enumerate(enumerator, ¤t, &auth))
|
||||
{
|
||||
public = current->get_ref(current);
|
||||
break;
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
|
||||
if (!public)
|
||||
{
|
||||
DBG1(DBG_IKE, "no TLS public key found for server '%Y'", this->server);
|
||||
return FAILED;
|
||||
}
|
||||
if (!public->encrypt(public, chunk_from_thing(premaster), &encrypted))
|
||||
{
|
||||
public->destroy(public);
|
||||
DBG1(DBG_IKE, "encrypting TLS premaster secret failed");
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
public->destroy(public);
|
||||
|
||||
writer->write_data16(writer, encrypted);
|
||||
free(encrypted.ptr);
|
||||
|
||||
*type = TLS_CLIENT_KEY_EXCHANGE;
|
||||
this->state = STATE_KEY_EXCHANGE_SENT;
|
||||
this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send certificate verify
|
||||
*/
|
||||
static status_t send_certificate_verify(private_tls_peer_t *this,
|
||||
tls_handshake_type_t *type, tls_writer_t *writer)
|
||||
{
|
||||
if (!this->private ||
|
||||
!this->crypto->sign_handshake(this->crypto, this->private, writer))
|
||||
{
|
||||
DBG1(DBG_IKE, "creating TLS Certificate Verify signature failed");
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
*type = TLS_CERTIFICATE_VERIFY;
|
||||
this->state = STATE_VERIFY_SENT;
|
||||
this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send Finished
|
||||
*/
|
||||
static status_t send_finished(private_tls_peer_t *this,
|
||||
tls_handshake_type_t *type, tls_writer_t *writer)
|
||||
{
|
||||
char buf[12];
|
||||
|
||||
if (!this->crypto->calculate_finished(this->crypto, "client finished", buf))
|
||||
{
|
||||
DBG1(DBG_IKE, "calculating client finished data failed");
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
writer->write_data(writer, chunk_from_thing(buf));
|
||||
|
||||
*type = TLS_FINISHED;
|
||||
this->state = STATE_FINISHED_SENT;
|
||||
this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
METHOD(tls_handshake_t, build, status_t,
|
||||
private_tls_peer_t *this, tls_handshake_type_t *type, tls_writer_t *writer)
|
||||
{
|
||||
switch (this->state)
|
||||
{
|
||||
case STATE_INIT:
|
||||
return send_client_hello(this, type, writer);
|
||||
case STATE_HELLO_DONE:
|
||||
return send_certificate(this, type, writer);
|
||||
case STATE_CERT_SENT:
|
||||
return send_key_exchange(this, type, writer);
|
||||
case STATE_KEY_EXCHANGE_SENT:
|
||||
return send_certificate_verify(this, type, writer);
|
||||
case STATE_CIPHERSPEC_CHANGED_OUT:
|
||||
return send_finished(this, type, writer);
|
||||
default:
|
||||
return INVALID_STATE;
|
||||
}
|
||||
}
|
||||
|
||||
METHOD(tls_handshake_t, cipherspec_changed, bool,
|
||||
private_tls_peer_t *this)
|
||||
{
|
||||
if (this->state == STATE_VERIFY_SENT)
|
||||
{
|
||||
this->crypto->change_cipher(this->crypto, FALSE);
|
||||
this->state = STATE_CIPHERSPEC_CHANGED_OUT;
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
METHOD(tls_handshake_t, change_cipherspec, bool,
|
||||
private_tls_peer_t *this)
|
||||
{
|
||||
if (this->state == STATE_FINISHED_SENT)
|
||||
{
|
||||
this->crypto->change_cipher(this->crypto, TRUE);
|
||||
this->state = STATE_CIPHERSPEC_CHANGED_IN;
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
METHOD(tls_handshake_t, destroy, void,
|
||||
private_tls_peer_t *this)
|
||||
{
|
||||
DESTROY_IF(this->private);
|
||||
this->peer_auth->destroy(this->peer_auth);
|
||||
this->server_auth->destroy(this->server_auth);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
tls_peer_t *tls_peer_create(tls_t *tls, tls_crypto_t *crypto,
|
||||
identification_t *peer, identification_t *server)
|
||||
{
|
||||
private_tls_peer_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public.handshake = {
|
||||
.process = _process,
|
||||
.build = _build,
|
||||
.cipherspec_changed = _cipherspec_changed,
|
||||
.change_cipherspec = _change_cipherspec,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.state = STATE_INIT,
|
||||
.tls = tls,
|
||||
.crypto = crypto,
|
||||
.peer = peer,
|
||||
.server = server,
|
||||
.peer_auth = auth_cfg_create(),
|
||||
.server_auth = auth_cfg_create(),
|
||||
);
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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_peer tls_peer
|
||||
* @{ @ingroup tls
|
||||
*/
|
||||
|
||||
#ifndef TLS_PEER_H_
|
||||
#define TLS_PEER_H_
|
||||
|
||||
typedef struct tls_peer_t tls_peer_t;
|
||||
|
||||
#include "tls_handshake.h"
|
||||
#include "tls_crypto.h"
|
||||
|
||||
#include <library.h>
|
||||
|
||||
/**
|
||||
* TLS handshake protocol handler as peer.
|
||||
*/
|
||||
struct tls_peer_t {
|
||||
|
||||
/**
|
||||
* Implements the TLS handshake protocol handler.
|
||||
*/
|
||||
tls_handshake_t handshake;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a tls_peer instance.
|
||||
*/
|
||||
tls_peer_t *tls_peer_create(tls_t *tls, tls_crypto_t *crypto,
|
||||
identification_t *peer, identification_t *server);
|
||||
|
||||
#endif /** TLS_PEER_H_ @}*/
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* 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_prf.h"
|
||||
|
||||
typedef struct private_tls_prf12_t private_tls_prf12_t;
|
||||
|
||||
/**
|
||||
* Private data of an tls_prf_t object.
|
||||
*/
|
||||
struct private_tls_prf12_t {
|
||||
|
||||
/**
|
||||
* Public tls_prf_t interface.
|
||||
*/
|
||||
tls_prf_t public;
|
||||
|
||||
/**
|
||||
* Underlying primitive PRF
|
||||
*/
|
||||
prf_t *prf;
|
||||
};
|
||||
|
||||
METHOD(tls_prf_t, set_key12, void,
|
||||
private_tls_prf12_t *this, chunk_t key)
|
||||
{
|
||||
this->prf->set_key(this->prf, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* The P_hash function as in TLS 1.0/1.2
|
||||
*/
|
||||
static void p_hash(prf_t *prf, char *label, chunk_t seed, size_t block_size,
|
||||
size_t bytes, char *out)
|
||||
{
|
||||
char buf[block_size], abuf[block_size];
|
||||
chunk_t a;
|
||||
|
||||
/* seed = label + seed */
|
||||
seed = chunk_cata("cc", chunk_create(label, strlen(label)), seed);
|
||||
/* A(0) = seed */
|
||||
a = seed;
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
/* A(i) = HMAC_hash(secret, A(i-1)) */
|
||||
prf->get_bytes(prf, a, abuf);
|
||||
a = chunk_from_thing(abuf);
|
||||
/* HMAC_hash(secret, A(i) + seed) */
|
||||
prf->get_bytes(prf, a, NULL);
|
||||
prf->get_bytes(prf, seed, buf);
|
||||
|
||||
if (bytes <= block_size)
|
||||
{
|
||||
memcpy(out, buf, bytes);
|
||||
break;
|
||||
}
|
||||
memcpy(out, buf, block_size);
|
||||
out += block_size;
|
||||
bytes -= block_size;
|
||||
}
|
||||
}
|
||||
|
||||
METHOD(tls_prf_t, get_bytes12, void,
|
||||
private_tls_prf12_t *this, char *label, chunk_t seed,
|
||||
size_t bytes, char *out)
|
||||
{
|
||||
p_hash(this->prf, label, seed, this->prf->get_block_size(this->prf),
|
||||
bytes, out);
|
||||
}
|
||||
|
||||
METHOD(tls_prf_t, destroy12, void,
|
||||
private_tls_prf12_t *this)
|
||||
{
|
||||
this->prf->destroy(this->prf);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
tls_prf_t *tls_prf_create_12(pseudo_random_function_t prf)
|
||||
{
|
||||
private_tls_prf12_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.set_key = _set_key12,
|
||||
.get_bytes = _get_bytes12,
|
||||
.destroy = _destroy12,
|
||||
},
|
||||
.prf = lib->crypto->create_prf(lib->crypto, prf),
|
||||
);
|
||||
if (!this->prf)
|
||||
{
|
||||
free(this);
|
||||
return NULL;
|
||||
}
|
||||
return &this->public;
|
||||
}
|
||||
|
||||
|
||||
typedef struct private_tls_prf10_t private_tls_prf10_t;
|
||||
|
||||
/**
|
||||
* Private data of an tls_prf_t object.
|
||||
*/
|
||||
struct private_tls_prf10_t {
|
||||
|
||||
/**
|
||||
* Public tls_prf_t interface.
|
||||
*/
|
||||
tls_prf_t public;
|
||||
|
||||
/**
|
||||
* Underlying MD5 PRF
|
||||
*/
|
||||
prf_t *md5;
|
||||
|
||||
/**
|
||||
* Underlying SHA1 PRF
|
||||
*/
|
||||
prf_t *sha1;
|
||||
};
|
||||
|
||||
METHOD(tls_prf_t, set_key10, void,
|
||||
private_tls_prf10_t *this, chunk_t key)
|
||||
{
|
||||
size_t len = key.len / 2 + key.len % 2;
|
||||
|
||||
this->md5->set_key(this->md5, chunk_create(key.ptr, len));
|
||||
this->sha1->set_key(this->sha1, chunk_create(key.ptr + key.len - len, len));
|
||||
}
|
||||
|
||||
METHOD(tls_prf_t, get_bytes10, void,
|
||||
private_tls_prf10_t *this, char *label, chunk_t seed,
|
||||
size_t bytes, char *out)
|
||||
{
|
||||
char buf[bytes];
|
||||
|
||||
p_hash(this->md5, label, seed, this->md5->get_block_size(this->md5),
|
||||
bytes, out);
|
||||
p_hash(this->sha1, label, seed, this->sha1->get_block_size(this->sha1),
|
||||
bytes, buf);
|
||||
memxor(out, buf, bytes);
|
||||
}
|
||||
|
||||
METHOD(tls_prf_t, destroy10, void,
|
||||
private_tls_prf10_t *this)
|
||||
{
|
||||
DESTROY_IF(this->md5);
|
||||
DESTROY_IF(this->sha1);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
tls_prf_t *tls_prf_create_10(pseudo_random_function_t prf)
|
||||
{
|
||||
private_tls_prf10_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.set_key = _set_key10,
|
||||
.get_bytes = _get_bytes10,
|
||||
.destroy = _destroy10,
|
||||
},
|
||||
.md5 = lib->crypto->create_prf(lib->crypto, PRF_HMAC_MD5),
|
||||
.sha1 = lib->crypto->create_prf(lib->crypto, PRF_HMAC_SHA1),
|
||||
);
|
||||
if (!this->md5 || !this->sha1)
|
||||
{
|
||||
destroy10(this);
|
||||
return NULL;
|
||||
}
|
||||
return &this->public;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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_prf tls_prf
|
||||
* @{ @ingroup tls
|
||||
*/
|
||||
|
||||
#ifndef TLS_PRF_H_
|
||||
#define TLS_PRF_H_
|
||||
|
||||
typedef struct tls_prf_t tls_prf_t;
|
||||
|
||||
#include <crypto/prfs/prf.h>
|
||||
|
||||
/**
|
||||
* The PRF function specified on TLS, based on HMAC.
|
||||
*/
|
||||
struct tls_prf_t {
|
||||
|
||||
/**
|
||||
* Set the key of the PRF function.
|
||||
*
|
||||
* @param key key to set
|
||||
*/
|
||||
void (*set_key)(tls_prf_t *this, chunk_t key);
|
||||
|
||||
/**
|
||||
* Generate a series of bytes using a label and a seed.
|
||||
*
|
||||
* @param label ASCII input label
|
||||
* @param seed seed input value
|
||||
* @param bytes number of bytes to get
|
||||
* @param out buffer receiving bytes
|
||||
*/
|
||||
void (*get_bytes)(tls_prf_t *this, char *label, chunk_t seed,
|
||||
size_t bytes, char *out);
|
||||
|
||||
/**
|
||||
* Destroy a tls_prf_t.
|
||||
*/
|
||||
void (*destroy)(tls_prf_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a tls_prf instance with specific algorithm as in TLS 1.2.
|
||||
*
|
||||
* @param hash underlying PRF function to use
|
||||
* @return TLS PRF algorithm
|
||||
*/
|
||||
tls_prf_t *tls_prf_create_12(pseudo_random_function_t prf);
|
||||
|
||||
/**
|
||||
* Create a tls_prf instance with XOred SHA1/MD5 as in TLS 1.0/1.1.
|
||||
*
|
||||
* @return TLS PRF algorithm
|
||||
*/
|
||||
tls_prf_t *tls_prf_create_10();
|
||||
|
||||
#endif /** TLS_PRF_H_ @}*/
|
||||
@@ -0,0 +1,311 @@
|
||||
/*
|
||||
* 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_protection.h"
|
||||
|
||||
#include <daemon.h>
|
||||
|
||||
typedef struct private_tls_protection_t private_tls_protection_t;
|
||||
|
||||
/**
|
||||
* Private data of an tls_protection_t object.
|
||||
*/
|
||||
struct private_tls_protection_t {
|
||||
|
||||
/**
|
||||
* Public tls_protection_t interface.
|
||||
*/
|
||||
tls_protection_t public;
|
||||
|
||||
/**
|
||||
* TLS context
|
||||
*/
|
||||
tls_t *tls;
|
||||
|
||||
/**
|
||||
* Upper layer, TLS record compression
|
||||
*/
|
||||
tls_compression_t *compression;
|
||||
|
||||
/**
|
||||
* RNG if we generate IVs ourself
|
||||
*/
|
||||
rng_t *rng;
|
||||
|
||||
/**
|
||||
* Sequence number of incoming records
|
||||
*/
|
||||
u_int32_t seq_in;
|
||||
|
||||
/**
|
||||
* Sequence number for outgoing records
|
||||
*/
|
||||
u_int32_t seq_out;
|
||||
|
||||
/**
|
||||
* Signer instance for inbound traffic
|
||||
*/
|
||||
signer_t *signer_in;
|
||||
|
||||
/**
|
||||
* Signer instance for outbound traffic
|
||||
*/
|
||||
signer_t *signer_out;
|
||||
|
||||
/**
|
||||
* Crypter instance for inbound traffic
|
||||
*/
|
||||
crypter_t *crypter_in;
|
||||
|
||||
/**
|
||||
* Crypter instance for outbound traffic
|
||||
*/
|
||||
crypter_t *crypter_out;
|
||||
|
||||
/**
|
||||
* Current IV for input decryption
|
||||
*/
|
||||
chunk_t iv_in;
|
||||
|
||||
/**
|
||||
* Current IV for output decryption
|
||||
*/
|
||||
chunk_t iv_out;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create the header to append to the record data to create the MAC
|
||||
*/
|
||||
static chunk_t sigheader(u_int32_t seq, u_int8_t type,
|
||||
u_int16_t version, u_int16_t length)
|
||||
{
|
||||
/* we only support 32 bit sequence numbers, but TLS uses 64 bit */
|
||||
u_int32_t seq_high = 0;
|
||||
|
||||
seq = htonl(seq);
|
||||
version = htons(version);
|
||||
length = htons(length);
|
||||
|
||||
return chunk_cat("ccccc", chunk_from_thing(seq_high),
|
||||
chunk_from_thing(seq), chunk_from_thing(type),
|
||||
chunk_from_thing(version), chunk_from_thing(length));
|
||||
}
|
||||
|
||||
METHOD(tls_protection_t, process, status_t,
|
||||
private_tls_protection_t *this, tls_content_type_t type, chunk_t data)
|
||||
{
|
||||
if (this->crypter_in)
|
||||
{
|
||||
chunk_t iv, next_iv = chunk_empty;
|
||||
u_int8_t bs, padding_length;
|
||||
|
||||
bs = this->crypter_in->get_block_size(this->crypter_in);
|
||||
if (data.len < bs || data.len % bs)
|
||||
{
|
||||
DBG1(DBG_IKE, "encrypted TLS record not multiple of block size");
|
||||
return FAILED;
|
||||
}
|
||||
if (this->iv_in.len)
|
||||
{ /* < TLSv1.1 uses IV from key derivation/last block */
|
||||
iv = this->iv_in;
|
||||
next_iv = chunk_clone(chunk_create(data.ptr + data.len - bs, bs));
|
||||
}
|
||||
else
|
||||
{ /* TLSv1.1 uses random IVs, prepended to record */
|
||||
iv = chunk_create(data.ptr, bs);
|
||||
data = chunk_skip(data, bs);
|
||||
if (data.len < bs)
|
||||
{
|
||||
DBG1(DBG_IKE, "TLS record too short to decrypt");
|
||||
return FAILED;
|
||||
}
|
||||
}
|
||||
this->crypter_in->decrypt(this->crypter_in, data, iv, NULL);
|
||||
|
||||
if (next_iv.len)
|
||||
{ /* next record IV is last ciphertext block of this record */
|
||||
memcpy(this->iv_in.ptr, next_iv.ptr, next_iv.len);
|
||||
free(next_iv.ptr);
|
||||
}
|
||||
|
||||
padding_length = data.ptr[data.len - 1];
|
||||
if (padding_length >= data.len)
|
||||
{
|
||||
DBG1(DBG_IKE, "invalid TLS record padding");
|
||||
return FAILED;
|
||||
}
|
||||
data.len -= padding_length + 1;
|
||||
}
|
||||
if (this->signer_in)
|
||||
{
|
||||
chunk_t mac, macdata, header;
|
||||
u_int8_t bs;
|
||||
|
||||
bs = this->signer_in->get_block_size(this->signer_in);
|
||||
if (data.len <= bs)
|
||||
{
|
||||
DBG1(DBG_IKE, "TLS record too short to verify MAC");
|
||||
return FAILED;
|
||||
}
|
||||
mac = chunk_skip(data, data.len - bs);
|
||||
data.len -= bs;
|
||||
|
||||
header = sigheader(this->seq_in, type,
|
||||
this->tls->get_version(this->tls), data.len);
|
||||
macdata = chunk_cat("mc", header, data);
|
||||
if (!this->signer_in->verify_signature(this->signer_in, macdata, mac))
|
||||
{
|
||||
DBG1(DBG_IKE, "TLS record MAC verification failed");
|
||||
free(macdata.ptr);
|
||||
return FAILED;
|
||||
}
|
||||
free(macdata.ptr);
|
||||
}
|
||||
|
||||
if (type == TLS_CHANGE_CIPHER_SPEC)
|
||||
{
|
||||
this->seq_in = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->seq_in++;
|
||||
}
|
||||
return this->compression->process(this->compression, type, data);
|
||||
}
|
||||
|
||||
METHOD(tls_protection_t, build, status_t,
|
||||
private_tls_protection_t *this, tls_content_type_t *type, chunk_t *data)
|
||||
{
|
||||
status_t status;
|
||||
|
||||
status = this->compression->build(this->compression, type, data);
|
||||
if (*type == TLS_CHANGE_CIPHER_SPEC)
|
||||
{
|
||||
this->seq_out = 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
if (status == NEED_MORE)
|
||||
{
|
||||
if (this->signer_out)
|
||||
{
|
||||
chunk_t mac, header;
|
||||
|
||||
header = sigheader(this->seq_out, *type,
|
||||
this->tls->get_version(this->tls), 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);
|
||||
if (this->crypter_out)
|
||||
{
|
||||
chunk_t padding, iv;
|
||||
u_int8_t bs, padding_length;
|
||||
|
||||
bs = this->crypter_out->get_block_size(this->crypter_out);
|
||||
padding_length = bs - ((data->len + mac.len + 1) % bs);
|
||||
|
||||
padding = chunk_alloca(padding_length);
|
||||
memset(padding.ptr, padding_length, padding.len);
|
||||
|
||||
if (this->iv_out.len)
|
||||
{ /* < TLSv1.1 uses IV from key derivation/last block */
|
||||
iv = this->iv_out;
|
||||
}
|
||||
else
|
||||
{ /* TLSv1.1 uses random IVs, prepended to record */
|
||||
if (!this->rng)
|
||||
{
|
||||
DBG1(DBG_IKE, "no RNG supported to generate TLS IV");
|
||||
free(data->ptr);
|
||||
return FAILED;
|
||||
}
|
||||
this->rng->allocate_bytes(this->rng, bs, &iv);
|
||||
}
|
||||
|
||||
*data = chunk_cat("mmcc", *data, mac, padding,
|
||||
chunk_from_thing(padding_length));
|
||||
/* encrypt inline */
|
||||
this->crypter_out->encrypt(this->crypter_out, *data, iv, NULL);
|
||||
|
||||
if (this->iv_out.len)
|
||||
{ /* next record IV is last ciphertext block of this record */
|
||||
memcpy(this->iv_out.ptr, data->ptr - this->iv_out.len,
|
||||
this->iv_out.len);
|
||||
}
|
||||
else
|
||||
{ /* prepend IV */
|
||||
*data = chunk_cat("mm", iv, *data);
|
||||
}
|
||||
}
|
||||
else
|
||||
{ /* NULL encryption */
|
||||
*data = chunk_cat("mm", *data, mac);
|
||||
}
|
||||
}
|
||||
}
|
||||
this->seq_out++;
|
||||
return status;
|
||||
}
|
||||
|
||||
METHOD(tls_protection_t, set_cipher, void,
|
||||
private_tls_protection_t *this, bool inbound, signer_t *signer,
|
||||
crypter_t *crypter, chunk_t iv)
|
||||
{
|
||||
if (inbound)
|
||||
{
|
||||
this->signer_in = signer;
|
||||
this->crypter_in = crypter;
|
||||
this->iv_in = iv;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->signer_out = signer;
|
||||
this->crypter_out = crypter;
|
||||
this->iv_out = iv;
|
||||
if (!iv.len)
|
||||
{ /* generate IVs if none given */
|
||||
this->rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
METHOD(tls_protection_t, destroy, void,
|
||||
private_tls_protection_t *this)
|
||||
{
|
||||
DESTROY_IF(this->rng);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
tls_protection_t *tls_protection_create(tls_t *tls,
|
||||
tls_compression_t *compression)
|
||||
{
|
||||
private_tls_protection_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.process = _process,
|
||||
.build = _build,
|
||||
.set_cipher = _set_cipher,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.tls = tls,
|
||||
.compression = compression,
|
||||
);
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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_protection tls_protection
|
||||
* @{ @ingroup tls
|
||||
*/
|
||||
|
||||
#ifndef TLS_PROTECTION_H_
|
||||
#define TLS_PROTECTION_H_
|
||||
|
||||
typedef struct tls_protection_t tls_protection_t;
|
||||
|
||||
#include <library.h>
|
||||
|
||||
#include "tls.h"
|
||||
#include "tls_compression.h"
|
||||
|
||||
/**
|
||||
* TLS record protocol protection layer.
|
||||
*/
|
||||
struct tls_protection_t {
|
||||
|
||||
/**
|
||||
* Process a protected TLS record, pass it to upper layers.
|
||||
*
|
||||
* @param type type of the TLS record to process
|
||||
* @param data associated TLS record data
|
||||
* @return
|
||||
* - SUCCESS if TLS negotiation complete
|
||||
* - FAILED if TLS handshake failed
|
||||
* - NEED_MORE if more invocations to process/build needed
|
||||
*/
|
||||
status_t (*process)(tls_protection_t *this,
|
||||
tls_content_type_t type, chunk_t data);
|
||||
|
||||
/**
|
||||
* Query upper layer for TLS record, build protected record.
|
||||
*
|
||||
* @param type type of the built TLS record
|
||||
* @param data allocated data of the built TLS record
|
||||
* @return
|
||||
* - SUCCESS if TLS negotiation complete
|
||||
* - FAILED if TLS handshake failed
|
||||
* - NEED_MORE if upper layers have more records to send
|
||||
* - INVALID_STATE if more input records required
|
||||
*/
|
||||
status_t (*build)(tls_protection_t *this,
|
||||
tls_content_type_t *type, chunk_t *data);
|
||||
|
||||
/**
|
||||
* Set a new cipher, including encryption and integrity algorithms.
|
||||
*
|
||||
* @param inbound TRUE to use cipher for inbound data, FALSE for outbound
|
||||
* @param signer new signer to use, gets owned by protection layer
|
||||
* @param crypter new crypter to use, gets owned by protection layer
|
||||
* @param iv initial IV for crypter, gets owned by protection layer
|
||||
*/
|
||||
void (*set_cipher)(tls_protection_t *this, bool inbound, signer_t *signer,
|
||||
crypter_t *crypter, chunk_t iv);
|
||||
|
||||
/**
|
||||
* Destroy a tls_protection_t.
|
||||
*/
|
||||
void (*destroy)(tls_protection_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a tls_protection instance.
|
||||
*
|
||||
* @param tls TLS context
|
||||
* @param compression compression layer of TLS stack
|
||||
* @return TLS protection layer.
|
||||
*/
|
||||
tls_protection_t *tls_protection_create(tls_t *tls,
|
||||
tls_compression_t *compression);
|
||||
|
||||
#endif /** TLS_PROTECTION_H_ @}*/
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Martin Willi
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* 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_reader.h"
|
||||
|
||||
#include <daemon.h>
|
||||
|
||||
typedef struct private_tls_reader_t private_tls_reader_t;
|
||||
|
||||
/**
|
||||
* Private data of an tls_reader_t object.
|
||||
*/
|
||||
struct private_tls_reader_t {
|
||||
|
||||
/**
|
||||
* Public tls_reader_t interface.
|
||||
*/
|
||||
tls_reader_t public;
|
||||
|
||||
/**
|
||||
* Remaining data to process
|
||||
*/
|
||||
chunk_t buf;
|
||||
};
|
||||
|
||||
METHOD(tls_reader_t, remaining, u_int32_t,
|
||||
private_tls_reader_t *this)
|
||||
{
|
||||
return this->buf.len;
|
||||
}
|
||||
|
||||
METHOD(tls_reader_t, peek, chunk_t,
|
||||
private_tls_reader_t *this)
|
||||
{
|
||||
return this->buf;
|
||||
}
|
||||
|
||||
METHOD(tls_reader_t, read_uint8, bool,
|
||||
private_tls_reader_t *this, u_int8_t *res)
|
||||
{
|
||||
if (this->buf.len < 1)
|
||||
{
|
||||
DBG1(DBG_IKE, "%d bytes insufficient to parse uint%d TLS data",
|
||||
this->buf.len, 8);
|
||||
return FALSE;
|
||||
}
|
||||
*res = this->buf.ptr[0];
|
||||
this->buf = chunk_skip(this->buf, 1);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(tls_reader_t, read_uint16, bool,
|
||||
private_tls_reader_t *this, u_int16_t *res)
|
||||
{
|
||||
if (this->buf.len < 2)
|
||||
{
|
||||
DBG1(DBG_IKE, "%d bytes insufficient to parse uint%d TLS data",
|
||||
this->buf.len, 16);
|
||||
return FALSE;
|
||||
}
|
||||
*res = untoh16(this->buf.ptr);
|
||||
this->buf = chunk_skip(this->buf, 2);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(tls_reader_t, read_uint24, bool,
|
||||
private_tls_reader_t *this, u_int32_t *res)
|
||||
{
|
||||
if (this->buf.len < 3)
|
||||
{
|
||||
DBG1(DBG_IKE, "%d bytes insufficient to parse uint%d TLS data",
|
||||
this->buf.len, 24);
|
||||
return FALSE;
|
||||
}
|
||||
*res = untoh32(this->buf.ptr) >> 8;
|
||||
this->buf = chunk_skip(this->buf, 3);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(tls_reader_t, read_uint32, bool,
|
||||
private_tls_reader_t *this, u_int32_t *res)
|
||||
{
|
||||
if (this->buf.len < 4)
|
||||
{
|
||||
DBG1(DBG_IKE, "%d bytes insufficient to parse uint%d TLS data",
|
||||
this->buf.len, 32);
|
||||
return FALSE;
|
||||
}
|
||||
*res = untoh32(this->buf.ptr);
|
||||
this->buf = chunk_skip(this->buf, 4);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(tls_reader_t, read_data, bool,
|
||||
private_tls_reader_t *this, u_int32_t len, chunk_t *res)
|
||||
{
|
||||
if (this->buf.len < len)
|
||||
{
|
||||
DBG1(DBG_IKE, "%d bytes insufficient to parse %d bytes TLS data",
|
||||
this->buf.len, len);
|
||||
return FALSE;
|
||||
}
|
||||
*res = chunk_create(this->buf.ptr, len);
|
||||
this->buf = chunk_skip(this->buf, len);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(tls_reader_t, read_data8, bool,
|
||||
private_tls_reader_t *this, chunk_t *res)
|
||||
{
|
||||
u_int8_t len;
|
||||
|
||||
if (!read_uint8(this, &len))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
return read_data(this, len, res);
|
||||
}
|
||||
|
||||
METHOD(tls_reader_t, read_data16, bool,
|
||||
private_tls_reader_t *this, chunk_t *res)
|
||||
{
|
||||
u_int16_t len;
|
||||
|
||||
if (!read_uint16(this, &len))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
return read_data(this, len, res);
|
||||
}
|
||||
|
||||
METHOD(tls_reader_t, read_data24, bool,
|
||||
private_tls_reader_t *this, chunk_t *res)
|
||||
{
|
||||
u_int32_t len;
|
||||
|
||||
if (!read_uint24(this, &len))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
return read_data(this, len, res);
|
||||
}
|
||||
|
||||
METHOD(tls_reader_t, read_data32, bool,
|
||||
private_tls_reader_t *this, chunk_t *res)
|
||||
{
|
||||
u_int32_t len;
|
||||
|
||||
if (!read_uint32(this, &len))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
return read_data(this, len, res);
|
||||
}
|
||||
|
||||
METHOD(tls_reader_t, destroy, void,
|
||||
private_tls_reader_t *this)
|
||||
{
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
tls_reader_t *tls_reader_create(chunk_t data)
|
||||
{
|
||||
private_tls_reader_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.remaining = _remaining,
|
||||
.peek = _peek,
|
||||
.read_uint8 = _read_uint8,
|
||||
.read_uint16 = _read_uint16,
|
||||
.read_uint24 = _read_uint24,
|
||||
.read_uint32 = _read_uint32,
|
||||
.read_data = _read_data,
|
||||
.read_data8 = _read_data8,
|
||||
.read_data16 = _read_data16,
|
||||
.read_data24 = _read_data24,
|
||||
.read_data32 = _read_data32,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.buf = data,
|
||||
);
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Martin Willi
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* 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_reader tls_reader
|
||||
* @{ @ingroup tls
|
||||
*/
|
||||
|
||||
#ifndef TLS_READER_H_
|
||||
#define TLS_READER_H_
|
||||
|
||||
typedef struct tls_reader_t tls_reader_t;
|
||||
|
||||
#include <library.h>
|
||||
|
||||
/**
|
||||
* TLS record parser.
|
||||
*/
|
||||
struct tls_reader_t {
|
||||
|
||||
/**
|
||||
* Get the number of remaining bytes.
|
||||
*
|
||||
* @return number of remaining bytes in buffer
|
||||
*/
|
||||
u_int32_t (*remaining)(tls_reader_t *this);
|
||||
|
||||
/**
|
||||
* Peek the remaining data, not consuming any bytes.
|
||||
*
|
||||
* @return remaining data
|
||||
*/
|
||||
chunk_t (*peek)(tls_reader_t *this);
|
||||
|
||||
/**
|
||||
* Read a 8-bit integer from the buffer, advance.
|
||||
*
|
||||
* @param res pointer to result
|
||||
* @return TRUE if integer read successfully
|
||||
*/
|
||||
bool (*read_uint8)(tls_reader_t *this, u_int8_t *res);
|
||||
|
||||
/**
|
||||
* Read a 16-bit integer from the buffer, advance.
|
||||
*
|
||||
* @param res pointer to result
|
||||
* @return TRUE if integer read successfully
|
||||
*/
|
||||
bool (*read_uint16)(tls_reader_t *this, u_int16_t *res);
|
||||
|
||||
/**
|
||||
* Read a 24-bit integer from the buffer, advance.
|
||||
*
|
||||
* @param res pointer to result
|
||||
* @return TRUE if integer read successfully
|
||||
*/
|
||||
bool (*read_uint24)(tls_reader_t *this, u_int32_t *res);
|
||||
|
||||
/**
|
||||
* Read a 32-bit integer from the buffer, advance.
|
||||
*
|
||||
* @param res pointer to result
|
||||
* @return TRUE if integer read successfully
|
||||
*/
|
||||
bool (*read_uint32)(tls_reader_t *this, u_int32_t *res);
|
||||
|
||||
/**
|
||||
* Read a chunk of len bytes, advance.
|
||||
*
|
||||
* @param len number of bytes to read
|
||||
* @param res pointer to result, not cloned
|
||||
* @return TRUE if data read successfully
|
||||
*/
|
||||
bool (*read_data)(tls_reader_t *this, u_int32_t len, chunk_t *res);
|
||||
|
||||
/**
|
||||
* Read a chunk of bytes with a 8-bit length header, advance.
|
||||
*
|
||||
* @param res pointer to result, not cloned
|
||||
* @return TRUE if data read successfully
|
||||
*/
|
||||
bool (*read_data8)(tls_reader_t *this, chunk_t *res);
|
||||
|
||||
/**
|
||||
* Read a chunk of bytes with a 16-bit length header, advance.
|
||||
*
|
||||
* @param res pointer to result, not cloned
|
||||
* @return TRUE if data read successfully
|
||||
*/
|
||||
bool (*read_data16)(tls_reader_t *this, chunk_t *res);
|
||||
|
||||
/**
|
||||
* Read a chunk of bytes with a 24-bit length header, advance.
|
||||
*
|
||||
* @param res pointer to result, not cloned
|
||||
* @return TRUE if data read successfully
|
||||
*/
|
||||
bool (*read_data24)(tls_reader_t *this, chunk_t *res);
|
||||
|
||||
/**
|
||||
* Read a chunk of bytes with a 32-bit length header, advance.
|
||||
*
|
||||
* @param res pointer to result, not cloned
|
||||
* @return TRUE if data read successfully
|
||||
*/
|
||||
bool (*read_data32)(tls_reader_t *this, chunk_t *res);
|
||||
|
||||
/**
|
||||
* Destroy a tls_reader_t.
|
||||
*/
|
||||
void (*destroy)(tls_reader_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a tls_reader instance.
|
||||
*/
|
||||
tls_reader_t *tls_reader_create(chunk_t data);
|
||||
|
||||
#endif /** tls_reader_H_ @}*/
|
||||
@@ -0,0 +1,607 @@
|
||||
/*
|
||||
* 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_server.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <daemon.h>
|
||||
|
||||
typedef struct private_tls_server_t private_tls_server_t;
|
||||
|
||||
|
||||
typedef enum {
|
||||
STATE_INIT,
|
||||
STATE_HELLO_RECEIVED,
|
||||
STATE_HELLO_SENT,
|
||||
STATE_CERT_SENT,
|
||||
STATE_CERTREQ_SENT,
|
||||
STATE_HELLO_DONE,
|
||||
STATE_CERT_RECEIVED,
|
||||
STATE_KEY_EXCHANGE_RECEIVED,
|
||||
STATE_CERT_VERIFY_RECEIVED,
|
||||
STATE_CIPHERSPEC_CHANGED_IN,
|
||||
STATE_FINISHED_RECEIVED,
|
||||
STATE_CIPHERSPEC_CHANGED_OUT,
|
||||
STATE_FINISHED_SENT,
|
||||
} server_state_t;
|
||||
|
||||
/**
|
||||
* Private data of an tls_server_t object.
|
||||
*/
|
||||
struct private_tls_server_t {
|
||||
|
||||
/**
|
||||
* Public tls_server_t interface.
|
||||
*/
|
||||
tls_server_t public;
|
||||
|
||||
/**
|
||||
* TLS stack
|
||||
*/
|
||||
tls_t *tls;
|
||||
|
||||
/**
|
||||
* TLS crypto context
|
||||
*/
|
||||
tls_crypto_t *crypto;
|
||||
|
||||
/**
|
||||
* Server identity
|
||||
*/
|
||||
identification_t *server;
|
||||
|
||||
/**
|
||||
* Peer identity
|
||||
*/
|
||||
identification_t *peer;
|
||||
|
||||
/**
|
||||
* State we are in
|
||||
*/
|
||||
server_state_t state;
|
||||
|
||||
/**
|
||||
* Hello random data selected by client
|
||||
*/
|
||||
char client_random[32];
|
||||
|
||||
/**
|
||||
* Hello random data selected by server
|
||||
*/
|
||||
char server_random[32];
|
||||
|
||||
/**
|
||||
* Auth helper for peer authentication
|
||||
*/
|
||||
auth_cfg_t *peer_auth;
|
||||
|
||||
/**
|
||||
* Auth helper for server authentication
|
||||
*/
|
||||
auth_cfg_t *server_auth;
|
||||
|
||||
/**
|
||||
* Peer private key
|
||||
*/
|
||||
private_key_t *private;
|
||||
|
||||
/**
|
||||
* Selected TLS cipher suite
|
||||
*/
|
||||
tls_cipher_suite_t suite;
|
||||
};
|
||||
|
||||
/**
|
||||
* Process client hello message
|
||||
*/
|
||||
static status_t process_client_hello(private_tls_server_t *this,
|
||||
tls_reader_t *reader)
|
||||
{
|
||||
u_int16_t version;
|
||||
chunk_t random, session, ciphers, compression, ext = chunk_empty;
|
||||
tls_cipher_suite_t *suites;
|
||||
int count, i;
|
||||
|
||||
this->crypto->append_handshake(this->crypto,
|
||||
TLS_CLIENT_HELLO, reader->peek(reader));
|
||||
|
||||
if (!reader->read_uint16(reader, &version) ||
|
||||
!reader->read_data(reader, sizeof(this->client_random), &random) ||
|
||||
!reader->read_data8(reader, &session) ||
|
||||
!reader->read_data16(reader, &ciphers) ||
|
||||
!reader->read_data8(reader, &compression) ||
|
||||
(reader->remaining(reader) && !reader->read_data16(reader, &ext)))
|
||||
{
|
||||
DBG1(DBG_IKE, "received invalid ClientHello");
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
memcpy(this->client_random, random.ptr, sizeof(this->client_random));
|
||||
|
||||
if (version < this->tls->get_version(this->tls))
|
||||
{
|
||||
this->tls->set_version(this->tls, version);
|
||||
}
|
||||
count = ciphers.len / sizeof(u_int16_t);
|
||||
suites = alloca(count * sizeof(tls_cipher_suite_t));
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
suites[i] = untoh16(&ciphers.ptr[i * sizeof(u_int16_t)]);
|
||||
}
|
||||
this->suite = this->crypto->select_cipher_suite(this->crypto, suites, count);
|
||||
if (!this->suite)
|
||||
{
|
||||
DBG1(DBG_IKE, "received cipher suite inacceptable");
|
||||
return FAILED;
|
||||
}
|
||||
this->state = STATE_HELLO_RECEIVED;
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process certificate
|
||||
*/
|
||||
static status_t process_certificate(private_tls_server_t *this,
|
||||
tls_reader_t *reader)
|
||||
{
|
||||
certificate_t *cert;
|
||||
tls_reader_t *certs;
|
||||
chunk_t data;
|
||||
bool first = TRUE;
|
||||
|
||||
this->crypto->append_handshake(this->crypto,
|
||||
TLS_CERTIFICATE, reader->peek(reader));
|
||||
|
||||
if (!reader->read_data24(reader, &data))
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
certs = tls_reader_create(data);
|
||||
while (certs->remaining(certs))
|
||||
{
|
||||
if (!certs->read_data24(certs, &data))
|
||||
{
|
||||
certs->destroy(certs);
|
||||
return FAILED;
|
||||
}
|
||||
cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
|
||||
BUILD_BLOB_ASN1_DER, data, BUILD_END);
|
||||
if (cert)
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
this->peer_auth->add(this->peer_auth,
|
||||
AUTH_HELPER_SUBJECT_CERT, cert);
|
||||
DBG1(DBG_IKE, "received TLS peer certificate '%Y'",
|
||||
cert->get_subject(cert));
|
||||
first = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG1(DBG_IKE, "received TLS intermediate certificate '%Y'",
|
||||
cert->get_subject(cert));
|
||||
this->peer_auth->add(this->peer_auth, AUTH_HELPER_IM_CERT, cert);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG1(DBG_IKE, "parsing TLS certificate failed, skipped");
|
||||
}
|
||||
}
|
||||
certs->destroy(certs);
|
||||
this->state = STATE_CERT_RECEIVED;
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process Client Key Exchange
|
||||
*/
|
||||
static status_t process_key_exchange(private_tls_server_t *this,
|
||||
tls_reader_t *reader)
|
||||
{
|
||||
chunk_t encrypted, premaster;
|
||||
|
||||
this->crypto->append_handshake(this->crypto,
|
||||
TLS_CLIENT_KEY_EXCHANGE, reader->peek(reader));
|
||||
|
||||
if (!reader->read_data16(reader, &encrypted))
|
||||
{
|
||||
DBG1(DBG_IKE, "received invalid Client Key Exchange");
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
if (!this->private ||
|
||||
!this->private->decrypt(this->private, encrypted, &premaster))
|
||||
{
|
||||
DBG1(DBG_IKE, "decrypting Client Key Exchange data failed");
|
||||
return FAILED;
|
||||
}
|
||||
this->crypto->derive_secrets(this->crypto, premaster,
|
||||
chunk_from_thing(this->client_random),
|
||||
chunk_from_thing(this->server_random));
|
||||
chunk_clear(&premaster);
|
||||
|
||||
this->state = STATE_KEY_EXCHANGE_RECEIVED;
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process Certificate verify
|
||||
*/
|
||||
static status_t process_cert_verify(private_tls_server_t *this,
|
||||
tls_reader_t *reader)
|
||||
{
|
||||
bool verified = FALSE;
|
||||
enumerator_t *enumerator;
|
||||
public_key_t *public;
|
||||
auth_cfg_t *auth;
|
||||
tls_reader_t *sig;
|
||||
|
||||
enumerator = lib->credmgr->create_public_enumerator(lib->credmgr,
|
||||
KEY_ANY, this->peer, this->peer_auth);
|
||||
while (enumerator->enumerate(enumerator, &public, &auth))
|
||||
{
|
||||
sig = tls_reader_create(reader->peek(reader));
|
||||
verified = this->crypto->verify_handshake(this->crypto, public, sig);
|
||||
sig->destroy(sig);
|
||||
if (verified)
|
||||
{
|
||||
break;
|
||||
}
|
||||
DBG1(DBG_IKE, "signature verification failed, trying another key");
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
|
||||
if (!verified)
|
||||
{
|
||||
DBG1(DBG_IKE, "no trusted certificate found for '%Y' to verify TLS peer",
|
||||
this->peer);
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
this->crypto->append_handshake(this->crypto,
|
||||
TLS_CERTIFICATE_VERIFY, reader->peek(reader));
|
||||
this->state = STATE_CERT_VERIFY_RECEIVED;
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process finished message
|
||||
*/
|
||||
static status_t process_finished(private_tls_server_t *this,
|
||||
tls_reader_t *reader)
|
||||
{
|
||||
chunk_t received;
|
||||
char buf[12];
|
||||
|
||||
if (!reader->read_data(reader, sizeof(buf), &received))
|
||||
{
|
||||
DBG1(DBG_IKE, "received client finished too short");
|
||||
return FAILED;
|
||||
}
|
||||
if (!this->crypto->calculate_finished(this->crypto, "client finished", buf))
|
||||
{
|
||||
DBG1(DBG_IKE, "calculating client finished failed");
|
||||
return FAILED;
|
||||
}
|
||||
if (!chunk_equals(received, chunk_from_thing(buf)))
|
||||
{
|
||||
DBG1(DBG_IKE, "received client finished invalid");
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
this->crypto->append_handshake(this->crypto, TLS_FINISHED, received);
|
||||
this->state = STATE_FINISHED_RECEIVED;
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
METHOD(tls_handshake_t, process, status_t,
|
||||
private_tls_server_t *this, tls_handshake_type_t type, tls_reader_t *reader)
|
||||
{
|
||||
tls_handshake_type_t expected;
|
||||
|
||||
switch (this->state)
|
||||
{
|
||||
case STATE_INIT:
|
||||
if (type == TLS_CLIENT_HELLO)
|
||||
{
|
||||
return process_client_hello(this, reader);
|
||||
}
|
||||
expected = TLS_CLIENT_HELLO;
|
||||
break;
|
||||
case STATE_HELLO_DONE:
|
||||
if (type == TLS_CERTIFICATE)
|
||||
{
|
||||
return process_certificate(this, reader);
|
||||
}
|
||||
expected = TLS_CERTIFICATE;
|
||||
break;
|
||||
case STATE_CERT_RECEIVED:
|
||||
if (type == TLS_CLIENT_KEY_EXCHANGE)
|
||||
{
|
||||
return process_key_exchange(this, reader);
|
||||
}
|
||||
expected = TLS_CLIENT_KEY_EXCHANGE;
|
||||
break;
|
||||
case STATE_KEY_EXCHANGE_RECEIVED:
|
||||
if (type == TLS_CERTIFICATE_VERIFY)
|
||||
{
|
||||
return process_cert_verify(this, reader);
|
||||
}
|
||||
expected = TLS_CERTIFICATE_VERIFY;
|
||||
break;
|
||||
case STATE_CIPHERSPEC_CHANGED_IN:
|
||||
if (type == TLS_FINISHED)
|
||||
{
|
||||
return process_finished(this, reader);
|
||||
}
|
||||
expected = TLS_FINISHED;
|
||||
break;
|
||||
default:
|
||||
DBG1(DBG_IKE, "TLS %N not expected in current state",
|
||||
tls_handshake_type_names, type);
|
||||
return FAILED;
|
||||
}
|
||||
DBG1(DBG_IKE, "TLS %N expected, but received %N",
|
||||
tls_handshake_type_names, expected, tls_handshake_type_names, type);
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send ServerHello message
|
||||
*/
|
||||
static status_t send_server_hello(private_tls_server_t *this,
|
||||
tls_handshake_type_t *type, tls_writer_t *writer)
|
||||
{
|
||||
rng_t *rng;
|
||||
|
||||
htoun32(&this->server_random, time(NULL));
|
||||
rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
|
||||
if (!rng)
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
rng->get_bytes(rng, sizeof(this->server_random) - 4, this->server_random + 4);
|
||||
rng->destroy(rng);
|
||||
|
||||
writer->write_uint16(writer, this->tls->get_version(this->tls));
|
||||
writer->write_data(writer, chunk_from_thing(this->server_random));
|
||||
/* session identifier => none, we don't support session resumption */
|
||||
writer->write_data8(writer, chunk_empty);
|
||||
/* add selected suite */
|
||||
writer->write_uint16(writer, this->suite);
|
||||
/* NULL compression only */
|
||||
writer->write_uint8(writer, 0);
|
||||
|
||||
*type = TLS_SERVER_HELLO;
|
||||
this->state = STATE_HELLO_SENT;
|
||||
this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send Certificate
|
||||
*/
|
||||
static status_t send_certificate(private_tls_server_t *this,
|
||||
tls_handshake_type_t *type, tls_writer_t *writer)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
certificate_t *cert;
|
||||
auth_rule_t rule;
|
||||
tls_writer_t *certs;
|
||||
chunk_t data;
|
||||
|
||||
this->private = lib->credmgr->get_private(lib->credmgr,
|
||||
KEY_ANY, this->server, this->server_auth);
|
||||
if (!this->private)
|
||||
{
|
||||
DBG1(DBG_IKE, "no TLS server certificate found for '%Y'", this->server);
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
/* generate certificate payload */
|
||||
certs = tls_writer_create(256);
|
||||
cert = this->server_auth->get(this->server_auth, AUTH_RULE_SUBJECT_CERT);
|
||||
if (cert)
|
||||
{
|
||||
if (cert->get_encoding(cert, CERT_ASN1_DER, &data))
|
||||
{
|
||||
DBG1(DBG_IKE, "sending TLS server certificate '%Y'",
|
||||
cert->get_subject(cert));
|
||||
certs->write_data24(certs, data);
|
||||
free(data.ptr);
|
||||
}
|
||||
}
|
||||
enumerator = this->server_auth->create_enumerator(this->server_auth);
|
||||
while (enumerator->enumerate(enumerator, &rule, &cert))
|
||||
{
|
||||
if (rule == AUTH_RULE_IM_CERT)
|
||||
{
|
||||
if (cert->get_encoding(cert, CERT_ASN1_DER, &data))
|
||||
{
|
||||
DBG1(DBG_IKE, "sending TLS intermediate certificate '%Y'",
|
||||
cert->get_subject(cert));
|
||||
certs->write_data24(certs, data);
|
||||
free(data.ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
|
||||
writer->write_data24(writer, certs->get_buf(certs));
|
||||
certs->destroy(certs);
|
||||
|
||||
*type = TLS_CERTIFICATE;
|
||||
this->state = STATE_CERT_SENT;
|
||||
this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send Certificate Request
|
||||
*/
|
||||
static status_t send_certificate_request(private_tls_server_t *this,
|
||||
tls_handshake_type_t *type, tls_writer_t *writer)
|
||||
{
|
||||
tls_writer_t *authorities;
|
||||
enumerator_t *enumerator;
|
||||
certificate_t *cert;
|
||||
identification_t *id;
|
||||
|
||||
/* currently only RSA signatures are supported */
|
||||
writer->write_data8(writer, chunk_from_chars(1));
|
||||
if (this->tls->get_version(this->tls) >= TLS_1_2)
|
||||
{
|
||||
/* enforce RSA with SHA1 signatures */
|
||||
writer->write_data16(writer, chunk_from_chars(2, 1));
|
||||
}
|
||||
|
||||
authorities = tls_writer_create(64);
|
||||
enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr,
|
||||
CERT_X509, KEY_RSA, NULL, TRUE);
|
||||
while (enumerator->enumerate(enumerator, &cert))
|
||||
{
|
||||
id = cert->get_subject(cert);
|
||||
authorities->write_data16(authorities, id->get_encoding(id));
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
writer->write_data16(writer, authorities->get_buf(authorities));
|
||||
authorities->destroy(authorities);
|
||||
|
||||
*type = TLS_CERTIFICATE_REQUEST;
|
||||
this->state = STATE_CERTREQ_SENT;
|
||||
this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send Hello Done
|
||||
*/
|
||||
static status_t send_hello_done(private_tls_server_t *this,
|
||||
tls_handshake_type_t *type, tls_writer_t *writer)
|
||||
{
|
||||
*type = TLS_SERVER_HELLO_DONE;
|
||||
this->state = STATE_HELLO_DONE;
|
||||
this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send Finished
|
||||
*/
|
||||
static status_t send_finished(private_tls_server_t *this,
|
||||
tls_handshake_type_t *type, tls_writer_t *writer)
|
||||
{
|
||||
char buf[12];
|
||||
|
||||
if (!this->crypto->calculate_finished(this->crypto, "server finished", buf))
|
||||
{
|
||||
DBG1(DBG_IKE, "calculating server finished data failed");
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
writer->write_data(writer, chunk_from_thing(buf));
|
||||
|
||||
*type = TLS_FINISHED;
|
||||
this->state = STATE_FINISHED_SENT;
|
||||
this->crypto->derive_eap_msk(this->crypto,
|
||||
chunk_from_thing(this->client_random),
|
||||
chunk_from_thing(this->server_random));
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
METHOD(tls_handshake_t, build, status_t,
|
||||
private_tls_server_t *this, tls_handshake_type_t *type, tls_writer_t *writer)
|
||||
{
|
||||
switch (this->state)
|
||||
{
|
||||
case STATE_HELLO_RECEIVED:
|
||||
return send_server_hello(this, type, writer);
|
||||
case STATE_HELLO_SENT:
|
||||
return send_certificate(this, type, writer);
|
||||
case STATE_CERT_SENT:
|
||||
return send_certificate_request(this, type, writer);
|
||||
case STATE_CERTREQ_SENT:
|
||||
return send_hello_done(this, type, writer);
|
||||
case STATE_CIPHERSPEC_CHANGED_OUT:
|
||||
return send_finished(this, type, writer);
|
||||
case STATE_FINISHED_SENT:
|
||||
return INVALID_STATE;
|
||||
default:
|
||||
return INVALID_STATE;
|
||||
}
|
||||
}
|
||||
|
||||
METHOD(tls_handshake_t, cipherspec_changed, bool,
|
||||
private_tls_server_t *this)
|
||||
{
|
||||
if (this->state == STATE_FINISHED_RECEIVED)
|
||||
{
|
||||
this->crypto->change_cipher(this->crypto, FALSE);
|
||||
this->state = STATE_CIPHERSPEC_CHANGED_OUT;
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
METHOD(tls_handshake_t, change_cipherspec, bool,
|
||||
private_tls_server_t *this)
|
||||
{
|
||||
if (this->state == STATE_CERT_VERIFY_RECEIVED)
|
||||
{
|
||||
this->crypto->change_cipher(this->crypto, TRUE);
|
||||
this->state = STATE_CIPHERSPEC_CHANGED_IN;
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
METHOD(tls_handshake_t, destroy, void,
|
||||
private_tls_server_t *this)
|
||||
{
|
||||
DESTROY_IF(this->private);
|
||||
this->peer_auth->destroy(this->peer_auth);
|
||||
this->server_auth->destroy(this->server_auth);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
tls_server_t *tls_server_create(tls_t *tls, tls_crypto_t *crypto,
|
||||
identification_t *server, identification_t *peer)
|
||||
{
|
||||
private_tls_server_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public.handshake = {
|
||||
.process = _process,
|
||||
.build = _build,
|
||||
.cipherspec_changed = _cipherspec_changed,
|
||||
.change_cipherspec = _change_cipherspec,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.tls = tls,
|
||||
.crypto = crypto,
|
||||
.server = server,
|
||||
.peer = peer,
|
||||
.state = STATE_INIT,
|
||||
.peer_auth = auth_cfg_create(),
|
||||
.server_auth = auth_cfg_create(),
|
||||
);
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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_server tls_server
|
||||
* @{ @ingroup tls
|
||||
*/
|
||||
|
||||
#ifndef TLS_SERVER_H_
|
||||
#define TLS_SERVER_H_
|
||||
|
||||
typedef struct tls_server_t tls_server_t;
|
||||
|
||||
#include "tls_handshake.h"
|
||||
#include "tls_crypto.h"
|
||||
|
||||
#include <library.h>
|
||||
|
||||
/**
|
||||
* TLS handshake protocol handler as peer.
|
||||
*/
|
||||
struct tls_server_t {
|
||||
|
||||
/**
|
||||
* Implements the TLS handshake protocol handler.
|
||||
*/
|
||||
tls_handshake_t handshake;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a tls_server instance.
|
||||
*/
|
||||
tls_server_t *tls_server_create(tls_t *tls, tls_crypto_t *crypto,
|
||||
identification_t *server, identification_t *peer);
|
||||
|
||||
#endif /** TLS_SERVER_H_ @}*/
|
||||
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Martin Willi
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* 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_writer.h"
|
||||
|
||||
typedef struct private_tls_writer_t private_tls_writer_t;
|
||||
|
||||
/**
|
||||
* Private data of an tls_writer_t object.
|
||||
*/
|
||||
struct private_tls_writer_t {
|
||||
|
||||
/**
|
||||
* Public tls_writer_t interface.
|
||||
*/
|
||||
tls_writer_t public;
|
||||
|
||||
/**
|
||||
* Allocated buffer
|
||||
*/
|
||||
chunk_t buf;
|
||||
|
||||
/**
|
||||
* Used bytes in buffer
|
||||
*/
|
||||
size_t used;
|
||||
|
||||
/**
|
||||
* Number of bytes to increase buffer size
|
||||
*/
|
||||
size_t increase;
|
||||
};
|
||||
|
||||
/**
|
||||
* Increase buffer size
|
||||
*/
|
||||
static void increase(private_tls_writer_t *this)
|
||||
{
|
||||
this->buf.len += this->increase;
|
||||
this->buf.ptr = realloc(this->buf.ptr, this->buf.len);
|
||||
}
|
||||
|
||||
METHOD(tls_writer_t, write_uint8, void,
|
||||
private_tls_writer_t *this, u_int8_t value)
|
||||
{
|
||||
if (this->used + 1 > this->buf.len)
|
||||
{
|
||||
increase(this);
|
||||
}
|
||||
this->buf.ptr[this->used] = value;
|
||||
this->used += 1;
|
||||
}
|
||||
|
||||
METHOD(tls_writer_t, write_uint16, void,
|
||||
private_tls_writer_t *this, u_int16_t value)
|
||||
{
|
||||
if (this->used + 2 > this->buf.len)
|
||||
{
|
||||
increase(this);
|
||||
}
|
||||
htoun16(this->buf.ptr + this->used, value);
|
||||
this->used += 2;
|
||||
}
|
||||
|
||||
METHOD(tls_writer_t, write_uint24, void,
|
||||
private_tls_writer_t *this, u_int32_t value)
|
||||
{
|
||||
if (this->used + 3 > this->buf.len)
|
||||
{
|
||||
increase(this);
|
||||
}
|
||||
value = htonl(value);
|
||||
memcpy(this->buf.ptr + this->used, ((char*)&value) + 1, 3);
|
||||
this->used += 3;
|
||||
}
|
||||
|
||||
METHOD(tls_writer_t, write_uint32, void,
|
||||
private_tls_writer_t *this, u_int32_t value)
|
||||
{
|
||||
if (this->used + 4 > this->buf.len)
|
||||
{
|
||||
increase(this);
|
||||
}
|
||||
htoun32(this->buf.ptr + this->used, value);
|
||||
this->used += 4;
|
||||
}
|
||||
|
||||
METHOD(tls_writer_t, write_data, void,
|
||||
private_tls_writer_t *this, chunk_t value)
|
||||
{
|
||||
while (this->used + value.len > this->buf.len)
|
||||
{
|
||||
increase(this);
|
||||
}
|
||||
memcpy(this->buf.ptr + this->used, value.ptr, value.len);
|
||||
this->used += value.len;
|
||||
}
|
||||
|
||||
METHOD(tls_writer_t, write_data8, void,
|
||||
private_tls_writer_t *this, chunk_t value)
|
||||
{
|
||||
write_uint8(this, value.len);
|
||||
write_data(this, value);
|
||||
}
|
||||
|
||||
METHOD(tls_writer_t, write_data16, void,
|
||||
private_tls_writer_t *this, chunk_t value)
|
||||
{
|
||||
write_uint16(this, value.len);
|
||||
write_data(this, value);
|
||||
}
|
||||
|
||||
METHOD(tls_writer_t, write_data24, void,
|
||||
private_tls_writer_t *this, chunk_t value)
|
||||
{
|
||||
write_uint24(this, value.len);
|
||||
write_data(this, value);
|
||||
}
|
||||
|
||||
METHOD(tls_writer_t, write_data32, void,
|
||||
private_tls_writer_t *this, chunk_t value)
|
||||
{
|
||||
write_uint32(this, value.len);
|
||||
write_data(this, value);
|
||||
}
|
||||
|
||||
METHOD(tls_writer_t, wrap8, void,
|
||||
private_tls_writer_t *this)
|
||||
{
|
||||
if (this->used + 1 > this->buf.len)
|
||||
{
|
||||
increase(this);
|
||||
}
|
||||
memmove(this->buf.ptr + 1, this->buf.ptr, 1);
|
||||
this->buf.ptr[0] = this->used;
|
||||
this->used += 1;
|
||||
}
|
||||
|
||||
METHOD(tls_writer_t, wrap16, void,
|
||||
private_tls_writer_t *this)
|
||||
{
|
||||
if (this->used + 2 > this->buf.len)
|
||||
{
|
||||
increase(this);
|
||||
}
|
||||
memmove(this->buf.ptr + 2, this->buf.ptr, 2);
|
||||
htoun16(this->buf.ptr, this->used);
|
||||
this->used += 2;
|
||||
}
|
||||
|
||||
METHOD(tls_writer_t, wrap24, void,
|
||||
private_tls_writer_t *this)
|
||||
{
|
||||
u_int32_t len;
|
||||
|
||||
if (this->used + 3 > this->buf.len)
|
||||
{
|
||||
increase(this);
|
||||
}
|
||||
memmove(this->buf.ptr + 3, this->buf.ptr, 3);
|
||||
|
||||
len = htonl(this->used);
|
||||
memcpy(this->buf.ptr, ((char*)&len) + 1, 3);
|
||||
this->used += 3;
|
||||
}
|
||||
|
||||
METHOD(tls_writer_t, wrap32, void,
|
||||
private_tls_writer_t *this)
|
||||
{
|
||||
if (this->used + 4 > this->buf.len)
|
||||
{
|
||||
increase(this);
|
||||
}
|
||||
memmove(this->buf.ptr + 4, this->buf.ptr, 4);
|
||||
htoun32(this->buf.ptr, this->used);
|
||||
this->used += 4;
|
||||
}
|
||||
|
||||
METHOD(tls_writer_t, get_buf, chunk_t,
|
||||
private_tls_writer_t *this)
|
||||
{
|
||||
return chunk_create(this->buf.ptr, this->used);
|
||||
}
|
||||
|
||||
METHOD(tls_writer_t, destroy, void,
|
||||
private_tls_writer_t *this)
|
||||
{
|
||||
free(this->buf.ptr);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
tls_writer_t *tls_writer_create(u_int32_t bufsize)
|
||||
{
|
||||
private_tls_writer_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.write_uint8 = _write_uint8,
|
||||
.write_uint16 = _write_uint16,
|
||||
.write_uint24 = _write_uint24,
|
||||
.write_uint32 = _write_uint32,
|
||||
.write_data = _write_data,
|
||||
.write_data8 = _write_data8,
|
||||
.write_data16 = _write_data16,
|
||||
.write_data24 = _write_data24,
|
||||
.write_data32 = _write_data32,
|
||||
.wrap8 = _wrap8,
|
||||
.wrap16 = _wrap16,
|
||||
.wrap24 = _wrap24,
|
||||
.wrap32 = _wrap32,
|
||||
.get_buf = _get_buf,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.increase = bufsize ?: 32,
|
||||
);
|
||||
if (bufsize)
|
||||
{
|
||||
this->buf = chunk_alloc(bufsize);
|
||||
}
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Martin Willi
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* 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_writer tls_writer
|
||||
* @{ @ingroup tls
|
||||
*/
|
||||
|
||||
#ifndef TLS_WRITER_H_
|
||||
#define TLS_WRITER_H_
|
||||
|
||||
typedef struct tls_writer_t tls_writer_t;
|
||||
|
||||
#include <library.h>
|
||||
|
||||
/**
|
||||
* TLS record generator.
|
||||
*/
|
||||
struct tls_writer_t {
|
||||
|
||||
/**
|
||||
* Append a 8-bit integer to the buffer.
|
||||
*
|
||||
* @param value value to append
|
||||
*/
|
||||
void (*write_uint8)(tls_writer_t *this, u_int8_t value);
|
||||
|
||||
/**
|
||||
* Append a 16-bit integer to the buffer.
|
||||
*
|
||||
* @param value value to append
|
||||
*/
|
||||
void (*write_uint16)(tls_writer_t *this, u_int16_t value);
|
||||
|
||||
/**
|
||||
* Append a 24-bit integer to the buffer.
|
||||
*
|
||||
* @param value value to append
|
||||
*/
|
||||
void (*write_uint24)(tls_writer_t *this, u_int32_t value);
|
||||
|
||||
/**
|
||||
* Append a 32-bit integer to the buffer.
|
||||
*
|
||||
* @param value value to append
|
||||
*/
|
||||
void (*write_uint32)(tls_writer_t *this, u_int32_t value);
|
||||
|
||||
/**
|
||||
* Append a chunk of data without a length header.
|
||||
*
|
||||
* @param value value to append
|
||||
*/
|
||||
void (*write_data)(tls_writer_t *this, chunk_t value);
|
||||
|
||||
/**
|
||||
* Append a chunk of data with a 16-bit length header.
|
||||
*
|
||||
* @param value value to append
|
||||
*/
|
||||
void (*write_data8)(tls_writer_t *this, chunk_t value);
|
||||
|
||||
/**
|
||||
* Append a chunk of data with a 8-bit length header.
|
||||
*
|
||||
* @param value value to append
|
||||
*/
|
||||
void (*write_data16)(tls_writer_t *this, chunk_t value);
|
||||
|
||||
/**
|
||||
* Append a chunk of data with a 24-bit length header.
|
||||
*
|
||||
* @param value value to append
|
||||
*/
|
||||
void (*write_data24)(tls_writer_t *this, chunk_t value);
|
||||
|
||||
/**
|
||||
* Append a chunk of data with a 32-bit length header.
|
||||
*
|
||||
* @param value value to append
|
||||
*/
|
||||
void (*write_data32)(tls_writer_t *this, chunk_t value);
|
||||
|
||||
/**
|
||||
* Prepend a 8-bit length header to existing data.
|
||||
*/
|
||||
void (*wrap8)(tls_writer_t *this);
|
||||
|
||||
/**
|
||||
* Prepend a 16-bit length header to existing data.
|
||||
*/
|
||||
void (*wrap16)(tls_writer_t *this);
|
||||
|
||||
/**
|
||||
* Prepend a 24-bit length header to existing data.
|
||||
*/
|
||||
void (*wrap24)(tls_writer_t *this);
|
||||
|
||||
/**
|
||||
* Prepend a 32-bit length header to existing data.
|
||||
*/
|
||||
void (*wrap32)(tls_writer_t *this);
|
||||
|
||||
/**
|
||||
* Get the encoded data buffer.
|
||||
*
|
||||
* @return chunk to internal buffer
|
||||
*/
|
||||
chunk_t (*get_buf)(tls_writer_t *this);
|
||||
|
||||
/**
|
||||
* Destroy a tls_writer_t.
|
||||
*/
|
||||
void (*destroy)(tls_writer_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a tls_writer instance.
|
||||
*
|
||||
* @param bufsize initially allocated buffer size
|
||||
*/
|
||||
tls_writer_t *tls_writer_create(u_int32_t bufsize);
|
||||
|
||||
#endif /** TLS_WRITER_H_ @}*/
|
||||
Reference in New Issue
Block a user