Merge branch 'pt-tls'
This commit is contained in:
@@ -32,6 +32,10 @@ if USE_LIBTNCCS
|
||||
SUBDIRS += libtnccs
|
||||
endif
|
||||
|
||||
if USE_LIBPTTLS
|
||||
SUBDIRS += libpttls
|
||||
endif
|
||||
|
||||
if USE_IMCV
|
||||
SUBDIRS += libimcv
|
||||
endif
|
||||
|
||||
@@ -30,8 +30,6 @@ METHOD(plugin_t, get_features, int,
|
||||
static plugin_feature_t f[] = {
|
||||
PLUGIN_CALLBACK(tnccs_method_register, tnccs_11_create),
|
||||
PLUGIN_PROVIDE(CUSTOM, "tnccs-1.1"),
|
||||
PLUGIN_DEPENDS(EAP_SERVER, EAP_TNC),
|
||||
PLUGIN_DEPENDS(EAP_PEER, EAP_TNC),
|
||||
PLUGIN_DEPENDS(CUSTOM, "tnccs-manager"),
|
||||
};
|
||||
*features = f;
|
||||
@@ -61,4 +59,3 @@ plugin_t *tnccs_11_plugin_create()
|
||||
|
||||
return &this->plugin;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,8 +30,6 @@ METHOD(plugin_t, get_features, int,
|
||||
static plugin_feature_t f[] = {
|
||||
PLUGIN_CALLBACK(tnccs_method_register, tnccs_20_create),
|
||||
PLUGIN_PROVIDE(CUSTOM, "tnccs-2.0"),
|
||||
PLUGIN_DEPENDS(EAP_SERVER, EAP_TNC),
|
||||
PLUGIN_DEPENDS(EAP_PEER, EAP_TNC),
|
||||
PLUGIN_DEPENDS(CUSTOM, "tnccs-manager"),
|
||||
};
|
||||
*features = f;
|
||||
@@ -61,4 +59,3 @@ plugin_t *tnccs_20_plugin_create()
|
||||
|
||||
return &this->plugin;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,8 +32,6 @@ METHOD(plugin_t, get_features, int,
|
||||
PLUGIN_PROVIDE(CUSTOM, "tnccs-dynamic"),
|
||||
PLUGIN_DEPENDS(CUSTOM, "tnccs-1.1"),
|
||||
PLUGIN_DEPENDS(CUSTOM, "tnccs-2.0"),
|
||||
PLUGIN_DEPENDS(EAP_SERVER, EAP_TNC),
|
||||
PLUGIN_DEPENDS(EAP_PEER, EAP_TNC),
|
||||
};
|
||||
*features = f;
|
||||
return countof(f);
|
||||
@@ -62,4 +60,3 @@ plugin_t *tnccs_dynamic_plugin_create()
|
||||
|
||||
return &this->plugin;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/libtls \
|
||||
-I$(top_srcdir)/src/libtncif -I$(top_srcdir)/src/libtnccs
|
||||
|
||||
ipseclib_LTLIBRARIES = libpttls.la
|
||||
libpttls_la_SOURCES = pt_tls.c pt_tls.h \
|
||||
pt_tls_client.c pt_tls_client.h \
|
||||
pt_tls_server.c pt_tls_server.h \
|
||||
pt_tls_dispatcher.c pt_tls_dispatcher.h
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Martin Willi
|
||||
* Copyright (C) 2012 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 "pt_tls.h"
|
||||
|
||||
#include <utils/debug.h>
|
||||
|
||||
/*
|
||||
* PT-TNC Message format:
|
||||
* 1 2 3
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Reserved | Message Type Vendor ID |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Message Type |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Message Length |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Message Identifier |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Message Value (e.g. PB-TNC Batch) . . . |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
|
||||
/**
|
||||
* Read a chunk of data from TLS, returning a reader for it
|
||||
*/
|
||||
static bio_reader_t* read_tls(tls_socket_t *tls, size_t len)
|
||||
{
|
||||
ssize_t got, total = 0;
|
||||
char *buf;
|
||||
|
||||
buf = malloc(len);
|
||||
while (total < len)
|
||||
{
|
||||
got = tls->read(tls, buf + total, len - total, TRUE);
|
||||
if (got <= 0)
|
||||
{
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
total += got;
|
||||
}
|
||||
return bio_reader_create_own(chunk_create(buf, len));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a PT-TLS message, return header data
|
||||
*/
|
||||
bio_reader_t* pt_tls_read(tls_socket_t *tls, u_int32_t *vendor,
|
||||
u_int32_t *type, u_int32_t *identifier)
|
||||
{
|
||||
bio_reader_t *reader;
|
||||
u_int32_t len;
|
||||
u_int8_t reserved;
|
||||
|
||||
reader = read_tls(tls, PT_TLS_HEADER_LEN);
|
||||
if (!reader)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
if (!reader->read_uint8(reader, &reserved) ||
|
||||
!reader->read_uint24(reader, vendor) ||
|
||||
!reader->read_uint32(reader, type) ||
|
||||
!reader->read_uint32(reader, &len) ||
|
||||
!reader->read_uint32(reader, identifier))
|
||||
{
|
||||
reader->destroy(reader);
|
||||
return NULL;
|
||||
}
|
||||
reader->destroy(reader);
|
||||
|
||||
if (len < PT_TLS_HEADER_LEN)
|
||||
{
|
||||
DBG1(DBG_TNC, "received short PT-TLS header (%d bytes)", len);
|
||||
return NULL;
|
||||
}
|
||||
return read_tls(tls, len - PT_TLS_HEADER_LEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepend a PT-TLS header to a writer, send data, destroy writer
|
||||
*/
|
||||
bool pt_tls_write(tls_socket_t *tls, bio_writer_t *writer,
|
||||
pt_tls_message_type_t type, u_int32_t identifier)
|
||||
{
|
||||
bio_writer_t *header;
|
||||
ssize_t len;
|
||||
chunk_t data;
|
||||
|
||||
data = writer->get_buf(writer);
|
||||
len = PT_TLS_HEADER_LEN + data.len;
|
||||
header = bio_writer_create(len);
|
||||
header->write_uint8(header, 0);
|
||||
header->write_uint24(header, 0);
|
||||
header->write_uint32(header, type);
|
||||
header->write_uint32(header, len);
|
||||
header->write_uint32(header, identifier);
|
||||
|
||||
header->write_data(header, data);
|
||||
writer->destroy(writer);
|
||||
|
||||
data = header->get_buf(header);
|
||||
len = tls->write(tls, data.ptr, data.len);
|
||||
header->destroy(header);
|
||||
|
||||
return len == data.len;
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Martin Willi
|
||||
* Copyright (C) 2012 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 pt_tls pt_tls
|
||||
* @{ @ingroup pt_tls
|
||||
*/
|
||||
|
||||
#ifndef PT_TLS_H_
|
||||
#define PT_TLS_H_
|
||||
|
||||
#include <bio/bio_reader.h>
|
||||
#include <bio/bio_writer.h>
|
||||
#include <tls_socket.h>
|
||||
|
||||
/**
|
||||
* PT-TLS version we support
|
||||
*/
|
||||
#define PT_TLS_VERSION 1
|
||||
|
||||
/**
|
||||
* Length of a PT-TLS header
|
||||
*/
|
||||
#define PT_TLS_HEADER_LEN 16
|
||||
|
||||
typedef enum pt_tls_message_type_t pt_tls_message_type_t;
|
||||
|
||||
/**
|
||||
* Message types, as defined by NEA PT-TLS
|
||||
*/
|
||||
enum pt_tls_message_type_t {
|
||||
PT_TLS_EXPERIMENTAL = 0,
|
||||
PT_TLS_VERSION_REQUEST = 1,
|
||||
PT_TLS_VERSION_RESPONSE = 2,
|
||||
PT_TLS_SASL_MECHS = 3,
|
||||
PT_TLS_SASL_MECH_SELECTION = 4,
|
||||
PT_TLS_SASL_AUTH_DATA = 5,
|
||||
PT_TLS_SASL_RESULT = 6,
|
||||
PT_TLS_PB_TNC_BATCH = 7,
|
||||
PT_TLS_ERROR = 8,
|
||||
};
|
||||
|
||||
/**
|
||||
* Read a PT-TLS message, create reader over Message Value.
|
||||
*
|
||||
* @param tls TLS socket to read from
|
||||
* @param vendor receives Message Type Vendor ID from header
|
||||
* @param type receives Message Type from header
|
||||
* @param identifier receives Message Identifer
|
||||
* @return reader over message value, NULL on error
|
||||
*/
|
||||
bio_reader_t* pt_tls_read(tls_socket_t *tls, u_int32_t *vendor,
|
||||
u_int32_t *type, u_int32_t *identifier);
|
||||
|
||||
/**
|
||||
* Prepend a PT-TLS header to a writer, send data, destroy writer.
|
||||
*
|
||||
* @param tls TLS socket to write to
|
||||
* @param writer prepared Message value to write
|
||||
* @param type Message Type to write
|
||||
* @param identifier Message Identifier to write
|
||||
* @return TRUE if data written successfully
|
||||
*/
|
||||
bool pt_tls_write(tls_socket_t *tls, bio_writer_t *writer,
|
||||
pt_tls_message_type_t type, u_int32_t identifier);
|
||||
|
||||
#endif /** PT_TLS_H_ @}*/
|
||||
@@ -0,0 +1,304 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Martin Willi
|
||||
* Copyright (C) 2012 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 "pt_tls_client.h"
|
||||
#include "pt_tls.h"
|
||||
|
||||
#include <tls_socket.h>
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
typedef struct private_pt_tls_client_t private_pt_tls_client_t;
|
||||
|
||||
/**
|
||||
* Private data of an pt_tls_client_t object.
|
||||
*/
|
||||
struct private_pt_tls_client_t {
|
||||
|
||||
/**
|
||||
* Public pt_tls_client_t interface.
|
||||
*/
|
||||
pt_tls_client_t public;
|
||||
|
||||
/**
|
||||
* TLS secured socket used by PT-TLS
|
||||
*/
|
||||
tls_socket_t *tls;
|
||||
|
||||
/**
|
||||
* Server address/port
|
||||
*/
|
||||
host_t *address;
|
||||
|
||||
/**
|
||||
* Server identity
|
||||
*/
|
||||
identification_t *id;
|
||||
|
||||
/**
|
||||
* Current PT-TLS message identifier
|
||||
*/
|
||||
u_int32_t identifier;
|
||||
};
|
||||
|
||||
/**
|
||||
* Establish TLS secured TCP connection to TNC server
|
||||
*/
|
||||
static bool make_connection(private_pt_tls_client_t *this)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = socket(this->address->get_family(this->address), SOCK_STREAM, 0);
|
||||
if (fd == -1)
|
||||
{
|
||||
DBG1(DBG_TNC, "opening PT-TLS socket failed: %s", strerror(errno));
|
||||
return FALSE;
|
||||
}
|
||||
if (connect(fd, this->address->get_sockaddr(this->address),
|
||||
*this->address->get_sockaddr_len(this->address)) == -1)
|
||||
{
|
||||
DBG1(DBG_TNC, "connecting to PT-TLS server failed: %s", strerror(errno));
|
||||
close(fd);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
this->tls = tls_socket_create(FALSE, this->id, NULL, fd, NULL);
|
||||
if (!this->tls)
|
||||
{
|
||||
close(fd);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Negotiate PT-TLS version
|
||||
*/
|
||||
static bool negotiate_version(private_pt_tls_client_t *this)
|
||||
{
|
||||
bio_writer_t *writer;
|
||||
bio_reader_t *reader;
|
||||
u_int32_t type, vendor, identifier, reserved;
|
||||
u_int8_t version;
|
||||
|
||||
DBG1(DBG_TNC, "sending offer for PT-TLS version %d", PT_TLS_VERSION);
|
||||
|
||||
writer = bio_writer_create(4);
|
||||
writer->write_uint8(writer, 0);
|
||||
writer->write_uint8(writer, PT_TLS_VERSION);
|
||||
writer->write_uint8(writer, PT_TLS_VERSION);
|
||||
writer->write_uint8(writer, PT_TLS_VERSION);
|
||||
if (!pt_tls_write(this->tls, writer, PT_TLS_VERSION_REQUEST,
|
||||
this->identifier++))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
reader = pt_tls_read(this->tls, &vendor, &type, &identifier);
|
||||
if (!reader)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
if (vendor != 0 || type != PT_TLS_VERSION_RESPONSE ||
|
||||
!reader->read_uint24(reader, &reserved) ||
|
||||
!reader->read_uint8(reader, &version) ||
|
||||
version != PT_TLS_VERSION)
|
||||
{
|
||||
DBG1(DBG_TNC, "PT-TLS version negotiation failed");
|
||||
reader->destroy(reader);
|
||||
return FALSE;
|
||||
}
|
||||
reader->destroy(reader);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate session using SASL
|
||||
*/
|
||||
static bool authenticate(private_pt_tls_client_t *this)
|
||||
{
|
||||
bio_reader_t *reader;
|
||||
u_int32_t type, vendor, identifier;
|
||||
|
||||
reader = pt_tls_read(this->tls, &vendor, &type, &identifier);
|
||||
if (!reader)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
if (vendor != 0 || type != PT_TLS_SASL_MECHS)
|
||||
{
|
||||
DBG1(DBG_TNC, "PT-TLS authentication failed");
|
||||
reader->destroy(reader);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (reader->remaining(reader))
|
||||
{ /* mechanism list not empty, FAIL until we support it */
|
||||
reader->destroy(reader);
|
||||
return FALSE;
|
||||
}
|
||||
DBG1(DBG_TNC, "PT-TLS authentication complete");
|
||||
reader->destroy(reader);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform assessment
|
||||
*/
|
||||
static bool assess(private_pt_tls_client_t *this, tls_t *tnccs)
|
||||
{
|
||||
while (TRUE)
|
||||
{
|
||||
bio_writer_t *writer;
|
||||
bio_reader_t *reader;
|
||||
u_int32_t vendor, type, identifier;
|
||||
chunk_t data;
|
||||
|
||||
writer = bio_writer_create(32);
|
||||
while (TRUE)
|
||||
{
|
||||
char buf[2048];
|
||||
size_t buflen, msglen;
|
||||
|
||||
buflen = sizeof(buf);
|
||||
switch (tnccs->build(tnccs, buf, &buflen, &msglen))
|
||||
{
|
||||
case SUCCESS:
|
||||
writer->destroy(writer);
|
||||
return tnccs->is_complete(tnccs);
|
||||
case FAILED:
|
||||
default:
|
||||
writer->destroy(writer);
|
||||
return FALSE;
|
||||
case INVALID_STATE:
|
||||
writer->destroy(writer);
|
||||
break;
|
||||
case NEED_MORE:
|
||||
writer->write_data(writer, chunk_create(buf, buflen));
|
||||
continue;
|
||||
case ALREADY_DONE:
|
||||
writer->write_data(writer, chunk_create(buf, buflen));
|
||||
if (!pt_tls_write(this->tls, writer, PT_TLS_PB_TNC_BATCH,
|
||||
this->identifier++))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
writer = bio_writer_create(32);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
reader = pt_tls_read(this->tls, &vendor, &type, &identifier);
|
||||
if (!reader)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
if (vendor == 0)
|
||||
{
|
||||
if (type == PT_TLS_ERROR)
|
||||
{
|
||||
DBG1(DBG_TNC, "received PT-TLS error");
|
||||
reader->destroy(reader);
|
||||
return FALSE;
|
||||
}
|
||||
if (type != PT_TLS_PB_TNC_BATCH)
|
||||
{
|
||||
DBG1(DBG_TNC, "unexpected PT-TLS message: %d", type);
|
||||
reader->destroy(reader);
|
||||
return FALSE;
|
||||
}
|
||||
data = reader->peek(reader);
|
||||
switch (tnccs->process(tnccs, data.ptr, data.len))
|
||||
{
|
||||
case SUCCESS:
|
||||
reader->destroy(reader);
|
||||
return tnccs->is_complete(tnccs);
|
||||
case FAILED:
|
||||
default:
|
||||
reader->destroy(reader);
|
||||
return FALSE;
|
||||
case NEED_MORE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG1(DBG_TNC, "ignoring vendor specific PT-TLS message");
|
||||
}
|
||||
reader->destroy(reader);
|
||||
}
|
||||
}
|
||||
|
||||
METHOD(pt_tls_client_t, run_assessment, status_t,
|
||||
private_pt_tls_client_t *this, tnccs_t *tnccs)
|
||||
{
|
||||
if (!this->tls)
|
||||
{
|
||||
if (!make_connection(this))
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
}
|
||||
if (!negotiate_version(this))
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
if (!authenticate(this))
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
if (!assess(this, (tls_t*)tnccs))
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
METHOD(pt_tls_client_t, destroy, void,
|
||||
private_pt_tls_client_t *this)
|
||||
{
|
||||
if (this->tls)
|
||||
{
|
||||
close(this->tls->get_fd(this->tls));
|
||||
this->tls->destroy(this->tls);
|
||||
}
|
||||
this->address->destroy(this->address);
|
||||
this->id->destroy(this->id);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
pt_tls_client_t *pt_tls_client_create(host_t *address, identification_t *id)
|
||||
{
|
||||
private_pt_tls_client_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.run_assessment = _run_assessment,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.address = address,
|
||||
.id = id,
|
||||
);
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Martin Willi
|
||||
* Copyright (C) 2012 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 pt_tls_client pt_tls_client
|
||||
* @{ @ingroup pt_tls
|
||||
*/
|
||||
|
||||
#ifndef PT_TLS_CLIENT_H_
|
||||
#define PT_TLS_CLIENT_H_
|
||||
|
||||
#include <networking/host.h>
|
||||
#include <utils/identification.h>
|
||||
|
||||
#include <tnc/tnccs/tnccs.h>
|
||||
|
||||
typedef struct pt_tls_client_t pt_tls_client_t;
|
||||
|
||||
/**
|
||||
* IF-T for TLS aka PT-TLS transport client.
|
||||
*/
|
||||
struct pt_tls_client_t {
|
||||
|
||||
/**
|
||||
* Perform an assessment.
|
||||
*
|
||||
* @param tnccs upper layer TNC client used for assessment
|
||||
* @return status of assessment
|
||||
*/
|
||||
status_t (*run_assessment)(pt_tls_client_t *this, tnccs_t *tnccs);
|
||||
|
||||
/**
|
||||
* Destroy a pt_tls_client_t.
|
||||
*/
|
||||
void (*destroy)(pt_tls_client_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a pt_tls_client instance.
|
||||
*
|
||||
* @param address address/port to run assessments against, gets owned
|
||||
* @param id server identity to use for authentication, gets owned
|
||||
* @return PT-TLS context
|
||||
*/
|
||||
pt_tls_client_t *pt_tls_client_create(host_t *address, identification_t *id);
|
||||
|
||||
#endif /** PT_TLS_CLIENT_H_ @}*/
|
||||
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Martin Willi
|
||||
* Copyright (C) 2012 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 "pt_tls_dispatcher.h"
|
||||
#include "pt_tls_server.h"
|
||||
|
||||
#include <threading/thread.h>
|
||||
#include <utils/debug.h>
|
||||
#include <processing/jobs/callback_job.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
typedef struct private_pt_tls_dispatcher_t private_pt_tls_dispatcher_t;
|
||||
|
||||
/**
|
||||
* Private data of an pt_tls_dispatcher_t object.
|
||||
*/
|
||||
struct private_pt_tls_dispatcher_t {
|
||||
|
||||
/**
|
||||
* Public pt_tls_dispatcher_t interface.
|
||||
*/
|
||||
pt_tls_dispatcher_t public;
|
||||
|
||||
/**
|
||||
* Listening socket
|
||||
*/
|
||||
int fd;
|
||||
|
||||
/**
|
||||
* Server identity
|
||||
*/
|
||||
identification_t *server;
|
||||
|
||||
/**
|
||||
* TNCCS protocol handler constructor
|
||||
*/
|
||||
tnccs_t*(*create)();
|
||||
};
|
||||
|
||||
/**
|
||||
* Open listening server socket
|
||||
*/
|
||||
static bool open_socket(private_pt_tls_dispatcher_t *this, host_t *host)
|
||||
{
|
||||
this->fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (this->fd == -1)
|
||||
{
|
||||
DBG1(DBG_TNC, "opening PT-TLS socket failed: %s", strerror(errno));
|
||||
return FALSE;
|
||||
}
|
||||
if (bind(this->fd, host->get_sockaddr(host),
|
||||
*host->get_sockaddr_len(host)) == -1)
|
||||
{
|
||||
DBG1(DBG_TNC, "binding to PT-TLS socket failed: %s", strerror(errno));
|
||||
return FALSE;
|
||||
}
|
||||
if (listen(this->fd, 5) == -1)
|
||||
{
|
||||
DBG1(DBG_TNC, "listen on PT-TLS socket failed: %s", strerror(errno));
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a single PT-TLS client connection
|
||||
*/
|
||||
static job_requeue_t handle(pt_tls_server_t *connection)
|
||||
{
|
||||
while (TRUE)
|
||||
{
|
||||
switch (connection->handle(connection))
|
||||
{
|
||||
case NEED_MORE:
|
||||
continue;
|
||||
case FAILED:
|
||||
case SUCCESS:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return JOB_REQUEUE_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up connection state
|
||||
*/
|
||||
static void cleanup(pt_tls_server_t *connection)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = connection->get_fd(connection);
|
||||
connection->destroy(connection);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
METHOD(pt_tls_dispatcher_t, dispatch, void,
|
||||
private_pt_tls_dispatcher_t *this, tnccs_t*(*create)())
|
||||
{
|
||||
while (TRUE)
|
||||
{
|
||||
pt_tls_server_t *connection;
|
||||
tnccs_t *tnccs;
|
||||
bool old;
|
||||
int fd;
|
||||
|
||||
old = thread_cancelability(TRUE);
|
||||
fd = accept(this->fd, NULL, NULL);
|
||||
thread_cancelability(old);
|
||||
if (fd == -1)
|
||||
{
|
||||
DBG1(DBG_TNC, "accepting PT-TLS failed: %s", strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
tnccs = create();
|
||||
if (!tnccs)
|
||||
{
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
connection = pt_tls_server_create(this->server, fd, tnccs);
|
||||
if (!connection)
|
||||
{
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
lib->processor->queue_job(lib->processor,
|
||||
(job_t*)callback_job_create_with_prio((callback_job_cb_t)handle,
|
||||
connection, (void*)cleanup,
|
||||
(callback_job_cancel_t)return_false,
|
||||
JOB_PRIO_CRITICAL));
|
||||
}
|
||||
}
|
||||
|
||||
METHOD(pt_tls_dispatcher_t, destroy, void,
|
||||
private_pt_tls_dispatcher_t *this)
|
||||
{
|
||||
if (this->fd != -1)
|
||||
{
|
||||
close(this->fd);
|
||||
}
|
||||
this->server->destroy(this->server);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
pt_tls_dispatcher_t *pt_tls_dispatcher_create(host_t *address,
|
||||
identification_t *id)
|
||||
{
|
||||
private_pt_tls_dispatcher_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.dispatch = _dispatch,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.server = id,
|
||||
.fd = -1,
|
||||
);
|
||||
|
||||
if (!open_socket(this, address))
|
||||
{
|
||||
address->destroy(address);
|
||||
destroy(this);
|
||||
return NULL;
|
||||
}
|
||||
address->destroy(address);
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Martin Willi
|
||||
* Copyright (C) 2012 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 pt_tls_dispatcher pt_tls_dispatcher
|
||||
* @{ @ingroup pt_tls
|
||||
*/
|
||||
|
||||
#ifndef PT_TLS_DISPATCHER_H_
|
||||
#define PT_TLS_DISPATCHER_H_
|
||||
|
||||
#include <networking/host.h>
|
||||
#include <utils/identification.h>
|
||||
|
||||
#include <tnc/tnccs/tnccs.h>
|
||||
|
||||
typedef struct pt_tls_dispatcher_t pt_tls_dispatcher_t;
|
||||
|
||||
/**
|
||||
* PT-TLS dispatcher service, handles PT-TLS connections as a server.
|
||||
*/
|
||||
struct pt_tls_dispatcher_t {
|
||||
|
||||
/**
|
||||
* Dispatch and handle PT-TLS connections.
|
||||
*
|
||||
* This call is blocking and a thread cancellation point. The passed
|
||||
* constructor gets called for each dispatched connection.
|
||||
*
|
||||
* @param create TNCCS constructor function to use
|
||||
*/
|
||||
void (*dispatch)(pt_tls_dispatcher_t *this, tnccs_t*(*create)());
|
||||
|
||||
/**
|
||||
* Destroy a pt_tls_dispatcher_t.
|
||||
*/
|
||||
void (*destroy)(pt_tls_dispatcher_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a pt_tls_dispatcher instance.
|
||||
*
|
||||
* @param address server address with port to listen on, gets owned
|
||||
* @param id TLS server identity, gets owned
|
||||
* @return dispatcher service
|
||||
*/
|
||||
pt_tls_dispatcher_t *pt_tls_dispatcher_create(host_t *address,
|
||||
identification_t *id);
|
||||
|
||||
#endif /** PT_TLS_DISPATCHER_H_ @}*/
|
||||
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Martin Willi
|
||||
* Copyright (C) 2012 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 "pt_tls_server.h"
|
||||
#include "pt_tls.h"
|
||||
|
||||
#include <utils/debug.h>
|
||||
|
||||
typedef struct private_pt_tls_server_t private_pt_tls_server_t;
|
||||
|
||||
/**
|
||||
* Private data of an pt_tls_server_t object.
|
||||
*/
|
||||
struct private_pt_tls_server_t {
|
||||
|
||||
/**
|
||||
* Public pt_tls_server_t interface.
|
||||
*/
|
||||
pt_tls_server_t public;
|
||||
|
||||
/**
|
||||
* TLS protected socket
|
||||
*/
|
||||
tls_socket_t *tls;
|
||||
|
||||
enum {
|
||||
/* expecting version negotiation */
|
||||
PT_TLS_SERVER_VERSION,
|
||||
/* expecting an SASL exchange */
|
||||
PT_TLS_SERVER_AUTH,
|
||||
/* expecting TNCCS exchange */
|
||||
PT_TLS_SERVER_TNCCS,
|
||||
/* terminating state */
|
||||
PT_TLS_SERVER_END,
|
||||
} state;
|
||||
|
||||
/**
|
||||
* Message Identifier
|
||||
*/
|
||||
u_int32_t identifier;
|
||||
|
||||
/**
|
||||
* TNCCS protocol handler, implemented as tls_t
|
||||
*/
|
||||
tls_t *tnccs;
|
||||
};
|
||||
|
||||
/**
|
||||
* Negotiate PT-TLS version
|
||||
*/
|
||||
static bool negotiate_version(private_pt_tls_server_t *this)
|
||||
{
|
||||
bio_reader_t *reader;
|
||||
bio_writer_t *writer;
|
||||
u_int32_t vendor, type, identifier;
|
||||
u_int8_t reserved, vmin, vmax, vpref;
|
||||
|
||||
reader = pt_tls_read(this->tls, &vendor, &type, &identifier);
|
||||
if (!reader)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
if (vendor != 0 || type != PT_TLS_VERSION_REQUEST ||
|
||||
!reader->read_uint8(reader, &reserved) ||
|
||||
!reader->read_uint8(reader, &vmin) ||
|
||||
!reader->read_uint8(reader, &vmax) ||
|
||||
!reader->read_uint8(reader, &vpref))
|
||||
{
|
||||
DBG1(DBG_TNC, "PT-TLS version negotiation failed");
|
||||
reader->destroy(reader);
|
||||
return FALSE;
|
||||
}
|
||||
reader->destroy(reader);
|
||||
|
||||
if (vmin > PT_TLS_VERSION || vmax < PT_TLS_VERSION)
|
||||
{
|
||||
/* TODO: send error */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
writer = bio_writer_create(4);
|
||||
writer->write_uint24(writer, 0);
|
||||
writer->write_uint8(writer, PT_TLS_VERSION);
|
||||
|
||||
return pt_tls_write(this->tls, writer, PT_TLS_VERSION_RESPONSE,
|
||||
this->identifier++);
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticated PT-TLS session with SASL
|
||||
*/
|
||||
static bool authenticate(private_pt_tls_server_t *this)
|
||||
{
|
||||
bio_writer_t *writer;
|
||||
|
||||
/* send empty SASL mechanims list to skip authentication */
|
||||
writer = bio_writer_create(0);
|
||||
return pt_tls_write(this->tls, writer, PT_TLS_SASL_MECHS,
|
||||
this->identifier++);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform assessment
|
||||
*/
|
||||
static bool assess(private_pt_tls_server_t *this, tls_t *tnccs)
|
||||
{
|
||||
while (TRUE)
|
||||
{
|
||||
bio_writer_t *writer;
|
||||
bio_reader_t *reader;
|
||||
u_int32_t vendor, type, identifier;
|
||||
chunk_t data;
|
||||
|
||||
writer = bio_writer_create(32);
|
||||
while (TRUE)
|
||||
{
|
||||
char buf[2048];
|
||||
size_t buflen, msglen;
|
||||
|
||||
buflen = sizeof(buf);
|
||||
switch (tnccs->build(tnccs, buf, &buflen, &msglen))
|
||||
{
|
||||
case SUCCESS:
|
||||
writer->destroy(writer);
|
||||
return tnccs->is_complete(tnccs);
|
||||
case FAILED:
|
||||
default:
|
||||
writer->destroy(writer);
|
||||
return FALSE;
|
||||
case INVALID_STATE:
|
||||
writer->destroy(writer);
|
||||
break;
|
||||
case NEED_MORE:
|
||||
writer->write_data(writer, chunk_create(buf, buflen));
|
||||
continue;
|
||||
case ALREADY_DONE:
|
||||
writer->write_data(writer, chunk_create(buf, buflen));
|
||||
if (!pt_tls_write(this->tls, writer, PT_TLS_PB_TNC_BATCH,
|
||||
this->identifier++))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
writer = bio_writer_create(32);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
reader = pt_tls_read(this->tls, &vendor, &type, &identifier);
|
||||
if (!reader)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
if (vendor == 0)
|
||||
{
|
||||
if (type == PT_TLS_ERROR)
|
||||
{
|
||||
DBG1(DBG_TNC, "received PT-TLS error");
|
||||
reader->destroy(reader);
|
||||
return FALSE;
|
||||
}
|
||||
if (type != PT_TLS_PB_TNC_BATCH)
|
||||
{
|
||||
DBG1(DBG_TNC, "unexpected PT-TLS message: %d", type);
|
||||
reader->destroy(reader);
|
||||
return FALSE;
|
||||
}
|
||||
data = reader->peek(reader);
|
||||
switch (tnccs->process(tnccs, data.ptr, data.len))
|
||||
{
|
||||
case SUCCESS:
|
||||
reader->destroy(reader);
|
||||
return tnccs->is_complete(tnccs);
|
||||
case FAILED:
|
||||
default:
|
||||
reader->destroy(reader);
|
||||
return FALSE;
|
||||
case NEED_MORE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG1(DBG_TNC, "ignoring vendor specific PT-TLS message");
|
||||
}
|
||||
reader->destroy(reader);
|
||||
}
|
||||
}
|
||||
|
||||
METHOD(pt_tls_server_t, handle, status_t,
|
||||
private_pt_tls_server_t *this)
|
||||
{
|
||||
switch (this->state)
|
||||
{
|
||||
case PT_TLS_SERVER_VERSION:
|
||||
if (!negotiate_version(this))
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
DBG1(DBG_TNC, "negotiated PT-TLS version %d", PT_TLS_VERSION);
|
||||
this->state = PT_TLS_SERVER_AUTH;
|
||||
break;
|
||||
case PT_TLS_SERVER_AUTH:
|
||||
DBG1(DBG_TNC, "sending empty mechanism list to skip SASL");
|
||||
if (!authenticate(this))
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
this->state = PT_TLS_SERVER_TNCCS;
|
||||
break;
|
||||
case PT_TLS_SERVER_TNCCS:
|
||||
if (!assess(this, (tls_t*)this->tnccs))
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
this->state = PT_TLS_SERVER_END;
|
||||
return SUCCESS;
|
||||
default:
|
||||
return FAILED;
|
||||
}
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
METHOD(pt_tls_server_t, get_fd, int,
|
||||
private_pt_tls_server_t *this)
|
||||
{
|
||||
return this->tls->get_fd(this->tls);
|
||||
}
|
||||
|
||||
METHOD(pt_tls_server_t, destroy, void,
|
||||
private_pt_tls_server_t *this)
|
||||
{
|
||||
this->tnccs->destroy(this->tnccs);
|
||||
this->tls->destroy(this->tls);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
pt_tls_server_t *pt_tls_server_create(identification_t *server, int fd,
|
||||
tnccs_t *tnccs)
|
||||
{
|
||||
private_pt_tls_server_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.handle = _handle,
|
||||
.get_fd = _get_fd,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.state = PT_TLS_SERVER_VERSION,
|
||||
.tls = tls_socket_create(TRUE, server, NULL, fd, NULL),
|
||||
.tnccs = (tls_t*)tnccs,
|
||||
);
|
||||
|
||||
if (!this->tls)
|
||||
{
|
||||
this->tnccs->destroy(this->tnccs);
|
||||
free(this);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Martin Willi
|
||||
* Copyright (C) 2012 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 pt_tls_server pt_tls_server
|
||||
* @{ @ingroup pt_tls
|
||||
*/
|
||||
|
||||
#ifndef PT_TLS_SERVER_H_
|
||||
#define PT_TLS_SERVER_H_
|
||||
|
||||
#include <utils/identification.h>
|
||||
|
||||
#include <tnc/tnccs/tnccs.h>
|
||||
|
||||
typedef struct pt_tls_server_t pt_tls_server_t;
|
||||
|
||||
/**
|
||||
* IF-T for TLS aka PT-TLS transport server.
|
||||
*/
|
||||
struct pt_tls_server_t {
|
||||
|
||||
/**
|
||||
* Handle assessment data read from socket.
|
||||
*
|
||||
* @return
|
||||
* - NEED_MORE if more exchanges required,
|
||||
* - SUCCESS if assessment complete
|
||||
* - FAILED if assessment failed
|
||||
*/
|
||||
status_t (*handle)(pt_tls_server_t *this);
|
||||
|
||||
/**
|
||||
* Get the underlying client connection socket.
|
||||
*
|
||||
* @return socket fd, suitable to select()
|
||||
*/
|
||||
int (*get_fd)(pt_tls_server_t *this);
|
||||
|
||||
/**
|
||||
* Destroy a pt_tls_server_t.
|
||||
*/
|
||||
void (*destroy)(pt_tls_server_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a pt_tls_server connection instance.
|
||||
*
|
||||
* @param server TLS server identity
|
||||
* @param fd client connection socket
|
||||
* @param tnccs inner TNCCS protocol handler to use for this connection
|
||||
* @return PT-TLS server
|
||||
*/
|
||||
pt_tls_server_t *pt_tls_server_create(identification_t *server, int fd,
|
||||
tnccs_t *tnccs);
|
||||
|
||||
#endif /** PT_TLS_SERVER_H_ @}*/
|
||||
@@ -36,6 +36,11 @@ struct private_bio_reader_t {
|
||||
* Remaining data to process
|
||||
*/
|
||||
chunk_t buf;
|
||||
|
||||
/**
|
||||
* Optional data to free during destruction
|
||||
*/
|
||||
chunk_t cleanup;
|
||||
};
|
||||
|
||||
METHOD(bio_reader_t, remaining, u_int32_t,
|
||||
@@ -302,6 +307,7 @@ METHOD(bio_reader_t, read_data32, bool,
|
||||
METHOD(bio_reader_t, destroy, void,
|
||||
private_bio_reader_t *this)
|
||||
{
|
||||
free(this->cleanup.ptr);
|
||||
free(this);
|
||||
}
|
||||
|
||||
@@ -339,3 +345,17 @@ bio_reader_t *bio_reader_create(chunk_t data)
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
bio_reader_t *bio_reader_create_own(chunk_t data)
|
||||
{
|
||||
private_bio_reader_t *this;
|
||||
|
||||
this = (private_bio_reader_t*)bio_reader_create(data);
|
||||
|
||||
this->cleanup = data;
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
|
||||
@@ -187,7 +187,18 @@ struct bio_reader_t {
|
||||
|
||||
/**
|
||||
* Create a bio_reader instance.
|
||||
*
|
||||
* @param data data buffer, must survive lifetime of reader
|
||||
* @return reader
|
||||
*/
|
||||
bio_reader_t *bio_reader_create(chunk_t data);
|
||||
|
||||
#endif /** bio_reader_H_ @}*/
|
||||
/**
|
||||
* Create a bio_reader instance owning buffer.
|
||||
*
|
||||
* @param data data buffer, gets freed with destroy()
|
||||
* @return reader
|
||||
*/
|
||||
bio_reader_t *bio_reader_create_own(chunk_t data);
|
||||
|
||||
#endif /** BIO_READER_H_ @}*/
|
||||
|
||||
@@ -190,6 +190,11 @@ static inline void chunk_clear(chunk_t *chunk)
|
||||
*/
|
||||
#define chunk_from_thing(thing) chunk_create((char*)&(thing), sizeof(thing))
|
||||
|
||||
/**
|
||||
* Initialize a chunk from a static string, not containing 0-terminator
|
||||
*/
|
||||
#define chunk_from_str(str) chunk_create(str, strlen(str))
|
||||
|
||||
/**
|
||||
* Allocate a chunk on the heap
|
||||
*/
|
||||
|
||||
@@ -223,7 +223,7 @@ static status_t process_application(private_tls_fragmentation_t *this,
|
||||
continue;
|
||||
case SUCCESS:
|
||||
this->application_finished = TRUE;
|
||||
return SUCCESS;
|
||||
/* FALL */
|
||||
case FAILED:
|
||||
default:
|
||||
this->alert->add(this->alert, TLS_FATAL, TLS_CLOSE_NOTIFY);
|
||||
@@ -368,7 +368,7 @@ static status_t build_application(private_tls_fragmentation_t *this)
|
||||
break;
|
||||
case SUCCESS:
|
||||
this->application_finished = TRUE;
|
||||
break;
|
||||
/* FALL */
|
||||
case FAILED:
|
||||
default:
|
||||
this->alert->add(this->alert, TLS_FATAL, TLS_CLOSE_NOTIFY);
|
||||
@@ -391,6 +391,10 @@ METHOD(tls_fragmentation_t, build, status_t,
|
||||
this->state = ALERT_SENT;
|
||||
return INVALID_STATE;
|
||||
case ALERT_SENT:
|
||||
if (this->application_finished)
|
||||
{
|
||||
return SUCCESS;
|
||||
}
|
||||
return FAILED;
|
||||
case ALERT_NONE:
|
||||
break;
|
||||
|
||||
+184
-73
@@ -42,14 +42,39 @@ struct private_tls_application_t {
|
||||
tls_application_t application;
|
||||
|
||||
/**
|
||||
* Chunk of data to send
|
||||
* Output buffer to write to
|
||||
*/
|
||||
chunk_t out;
|
||||
|
||||
/**
|
||||
* Chunk of data received
|
||||
* Number of bytes written to out
|
||||
*/
|
||||
size_t out_done;
|
||||
|
||||
/**
|
||||
* Input buffer to read to
|
||||
*/
|
||||
chunk_t in;
|
||||
|
||||
/**
|
||||
* Number of bytes read to in
|
||||
*/
|
||||
size_t in_done;
|
||||
|
||||
/**
|
||||
* Cached input data
|
||||
*/
|
||||
chunk_t cache;
|
||||
|
||||
/**
|
||||
* Bytes cosnumed in cache
|
||||
*/
|
||||
size_t cache_done;
|
||||
|
||||
/**
|
||||
* Close TLS connection?
|
||||
*/
|
||||
bool close;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -82,22 +107,44 @@ METHOD(tls_application_t, process, status_t,
|
||||
private_tls_application_t *this, bio_reader_t *reader)
|
||||
{
|
||||
chunk_t data;
|
||||
size_t len;
|
||||
|
||||
if (!reader->read_data(reader, reader->remaining(reader), &data))
|
||||
if (this->close)
|
||||
{
|
||||
return FAILED;
|
||||
return SUCCESS;
|
||||
}
|
||||
len = min(reader->remaining(reader), this->in.len - this->in_done);
|
||||
if (len)
|
||||
{ /* copy to read buffer as much as fits in */
|
||||
if (!reader->read_data(reader, len, &data))
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
memcpy(this->in.ptr + this->in_done, data.ptr, data.len);
|
||||
this->in_done += data.len;
|
||||
}
|
||||
else
|
||||
{ /* read buffer is full, cache for next read */
|
||||
if (!reader->read_data(reader, reader->remaining(reader), &data))
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
this->cache = chunk_cat("mc", this->cache, data);
|
||||
}
|
||||
this->in = chunk_cat("mc", this->in, data);
|
||||
return NEED_MORE;
|
||||
}
|
||||
|
||||
METHOD(tls_application_t, build, status_t,
|
||||
private_tls_application_t *this, bio_writer_t *writer)
|
||||
{
|
||||
if (this->out.len)
|
||||
if (this->close)
|
||||
{
|
||||
return SUCCESS;
|
||||
}
|
||||
if (this->out.len > this->out_done)
|
||||
{
|
||||
writer->write_data(writer, this->out);
|
||||
this->out = chunk_empty;
|
||||
this->out_done = this->out.len;
|
||||
return NEED_MORE;
|
||||
}
|
||||
return INVALID_STATE;
|
||||
@@ -106,11 +153,11 @@ METHOD(tls_application_t, build, status_t,
|
||||
/**
|
||||
* TLS data exchange loop
|
||||
*/
|
||||
static bool exchange(private_tls_socket_t *this, bool wr)
|
||||
static bool exchange(private_tls_socket_t *this, bool wr, bool block)
|
||||
{
|
||||
char buf[CRYPTO_BUF_SIZE], *pos;
|
||||
ssize_t len, out;
|
||||
int round = 0;
|
||||
int round = 0, flags;
|
||||
|
||||
for (round = 0; TRUE; round++)
|
||||
{
|
||||
@@ -137,6 +184,8 @@ static bool exchange(private_tls_socket_t *this, bool wr)
|
||||
continue;
|
||||
case INVALID_STATE:
|
||||
break;
|
||||
case SUCCESS:
|
||||
return TRUE;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
@@ -144,55 +193,97 @@ static bool exchange(private_tls_socket_t *this, bool wr)
|
||||
}
|
||||
if (wr)
|
||||
{
|
||||
if (this->app.out.len == 0)
|
||||
if (this->app.out_done == this->app.out.len)
|
||||
{ /* all data written */
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this->app.in.len)
|
||||
{ /* some data received */
|
||||
return TRUE;
|
||||
}
|
||||
if (round > 0)
|
||||
{ /* did some handshaking, return empty chunk to not block */
|
||||
if (this->app.in_done == this->app.in.len)
|
||||
{ /* buffer fully received */
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
len = read(this->fd, buf, sizeof(buf));
|
||||
if (len <= 0)
|
||||
|
||||
flags = 0;
|
||||
if (this->app.out_done == this->app.out.len)
|
||||
{
|
||||
if (!block || this->app.in_done)
|
||||
{
|
||||
flags |= MSG_DONTWAIT;
|
||||
}
|
||||
}
|
||||
len = recv(this->fd, buf, sizeof(buf), flags);
|
||||
if (len < 0)
|
||||
{
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
||||
{
|
||||
if (this->app.in_done == 0)
|
||||
{
|
||||
/* reading, nothing got yet, and call would block */
|
||||
errno = EWOULDBLOCK;
|
||||
this->app.in_done = -1;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
if (this->tls->process(this->tls, buf, len) != NEED_MORE)
|
||||
if (len == 0)
|
||||
{ /* EOF */
|
||||
return TRUE;
|
||||
}
|
||||
switch (this->tls->process(this->tls, buf, len))
|
||||
{
|
||||
return FALSE;
|
||||
case NEED_MORE:
|
||||
break;
|
||||
case SUCCESS:
|
||||
return TRUE;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
METHOD(tls_socket_t, read_, bool,
|
||||
private_tls_socket_t *this, chunk_t *buf)
|
||||
METHOD(tls_socket_t, read_, ssize_t,
|
||||
private_tls_socket_t *this, void *buf, size_t len, bool block)
|
||||
{
|
||||
if (exchange(this, FALSE))
|
||||
if (this->app.cache.len)
|
||||
{
|
||||
*buf = this->app.in;
|
||||
this->app.in = chunk_empty;
|
||||
return TRUE;
|
||||
size_t cache;
|
||||
|
||||
cache = min(len, this->app.cache.len - this->app.cache_done);
|
||||
memcpy(buf, this->app.cache.ptr + this->app.cache_done, cache);
|
||||
|
||||
this->app.cache_done += cache;
|
||||
if (this->app.cache_done == this->app.cache.len)
|
||||
{
|
||||
chunk_free(&this->app.cache);
|
||||
this->app.cache_done = 0;
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
return FALSE;
|
||||
this->app.in.ptr = buf;
|
||||
this->app.in.len = len;
|
||||
this->app.in_done = 0;
|
||||
if (exchange(this, FALSE, block))
|
||||
{
|
||||
return this->app.in_done;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
METHOD(tls_socket_t, write_, bool,
|
||||
private_tls_socket_t *this, chunk_t buf)
|
||||
METHOD(tls_socket_t, write_, ssize_t,
|
||||
private_tls_socket_t *this, void *buf, size_t len)
|
||||
{
|
||||
this->app.out = buf;
|
||||
if (exchange(this, TRUE))
|
||||
this->app.out.ptr = buf;
|
||||
this->app.out.len = len;
|
||||
this->app.out_done = 0;
|
||||
if (exchange(this, TRUE, FALSE))
|
||||
{
|
||||
return TRUE;
|
||||
return this->app.out_done;
|
||||
}
|
||||
return FALSE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
METHOD(tls_socket_t, splice, bool,
|
||||
@@ -200,68 +291,85 @@ METHOD(tls_socket_t, splice, bool,
|
||||
{
|
||||
char buf[PLAIN_BUF_SIZE], *pos;
|
||||
fd_set set;
|
||||
chunk_t data;
|
||||
ssize_t len;
|
||||
bool old;
|
||||
ssize_t in, out;
|
||||
bool old, plain_eof = FALSE, crypto_eof = FALSE;
|
||||
|
||||
while (TRUE)
|
||||
while (!plain_eof && !crypto_eof)
|
||||
{
|
||||
FD_ZERO(&set);
|
||||
FD_SET(rfd, &set);
|
||||
FD_SET(this->fd, &set);
|
||||
|
||||
old = thread_cancelability(TRUE);
|
||||
len = select(max(rfd, this->fd) + 1, &set, NULL, NULL, NULL);
|
||||
in = select(max(rfd, this->fd) + 1, &set, NULL, NULL, NULL);
|
||||
thread_cancelability(old);
|
||||
if (len == -1)
|
||||
if (in == -1)
|
||||
{
|
||||
DBG1(DBG_TLS, "TLS select error: %s", strerror(errno));
|
||||
return FALSE;
|
||||
}
|
||||
if (FD_ISSET(this->fd, &set))
|
||||
while (!plain_eof && FD_ISSET(this->fd, &set))
|
||||
{
|
||||
if (!read_(this, &data))
|
||||
in = read_(this, buf, sizeof(buf), FALSE);
|
||||
switch (in)
|
||||
{
|
||||
DBG2(DBG_TLS, "TLS read error/disconnect");
|
||||
return TRUE;
|
||||
case 0:
|
||||
plain_eof = TRUE;
|
||||
break;
|
||||
case -1:
|
||||
if (errno != EWOULDBLOCK)
|
||||
{
|
||||
DBG1(DBG_TLS, "TLS read error: %s", strerror(errno));
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pos = buf;
|
||||
while (in)
|
||||
{
|
||||
out = write(wfd, pos, in);
|
||||
if (out == -1)
|
||||
{
|
||||
DBG1(DBG_TLS, "TLS plain write error: %s",
|
||||
strerror(errno));
|
||||
return FALSE;
|
||||
}
|
||||
in -= out;
|
||||
pos += out;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
pos = data.ptr;
|
||||
while (data.len)
|
||||
{
|
||||
len = write(wfd, pos, data.len);
|
||||
if (len == -1)
|
||||
{
|
||||
free(data.ptr);
|
||||
DBG1(DBG_TLS, "TLS plain write error: %s", strerror(errno));
|
||||
return FALSE;
|
||||
}
|
||||
data.len -= len;
|
||||
pos += len;
|
||||
}
|
||||
free(data.ptr);
|
||||
break;
|
||||
}
|
||||
if (FD_ISSET(rfd, &set))
|
||||
if (!crypto_eof && FD_ISSET(rfd, &set))
|
||||
{
|
||||
len = read(rfd, buf, sizeof(buf));
|
||||
if (len > 0)
|
||||
in = read(rfd, buf, sizeof(buf));
|
||||
switch (in)
|
||||
{
|
||||
if (!write_(this, chunk_create(buf, len)))
|
||||
{
|
||||
DBG1(DBG_TLS, "TLS write error");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (len < 0)
|
||||
{
|
||||
case 0:
|
||||
crypto_eof = TRUE;
|
||||
break;
|
||||
case -1:
|
||||
DBG1(DBG_TLS, "TLS plain read error: %s", strerror(errno));
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
default:
|
||||
pos = buf;
|
||||
while (in)
|
||||
{
|
||||
out = write_(this, pos, in);
|
||||
if (out == -1)
|
||||
{
|
||||
DBG1(DBG_TLS, "TLS write error");
|
||||
return FALSE;
|
||||
}
|
||||
in -= out;
|
||||
pos += out;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(tls_socket_t, get_fd, int,
|
||||
@@ -273,8 +381,11 @@ METHOD(tls_socket_t, get_fd, int,
|
||||
METHOD(tls_socket_t, destroy, void,
|
||||
private_tls_socket_t *this)
|
||||
{
|
||||
/* send a TLS close notify if not done yet */
|
||||
this->app.close = TRUE;
|
||||
write_(this, NULL, 0);
|
||||
free(this->app.cache.ptr);
|
||||
this->tls->destroy(this->tls);
|
||||
free(this->app.in.ptr);
|
||||
free(this);
|
||||
}
|
||||
|
||||
|
||||
+13
-10
@@ -35,24 +35,27 @@ typedef struct tls_socket_t tls_socket_t;
|
||||
struct tls_socket_t {
|
||||
|
||||
/**
|
||||
* Read data from secured socket, return allocated chunk.
|
||||
* Read data from secured socket.
|
||||
*
|
||||
* This call is blocking, you may use select() on the underlying socket to
|
||||
* wait for data. If the there was non-application data available, the
|
||||
* read function can return an empty chunk.
|
||||
* wait for data. If "block" is FALSE and no application data is available,
|
||||
* the function returns -1 and sets errno to EWOULDBLOCK.
|
||||
*
|
||||
* @param data pointer to allocate received data
|
||||
* @return TRUE if data received successfully
|
||||
* @param buf buffer to write received data to
|
||||
* @param len size of buffer
|
||||
* @param block TRUE to block this call, FALSE to fail if it would block
|
||||
* @return number of bytes read, 0 on EOF, -1 on error
|
||||
*/
|
||||
bool (*read)(tls_socket_t *this, chunk_t *data);
|
||||
ssize_t (*read)(tls_socket_t *this, void *buf, size_t len, bool block);
|
||||
|
||||
/**
|
||||
* Write a chunk of data over the secured socket.
|
||||
* Write data over the secured socket.
|
||||
*
|
||||
* @param data data to send
|
||||
* @return TRUE if data sent successfully
|
||||
* @param buf data to send
|
||||
* @param len number of bytes to write from buf
|
||||
* @return number of bytes written, -1 on error
|
||||
*/
|
||||
bool (*write)(tls_socket_t *this, chunk_t data);
|
||||
ssize_t (*write)(tls_socket_t *this, void *buf, size_t len);
|
||||
|
||||
/**
|
||||
* Read/write plain data from file descriptor.
|
||||
|
||||
Reference in New Issue
Block a user