Add a libpttls providing NEA PT-TLS / TNC IF-T for TLS transport layer

This commit is contained in:
Martin Willi 2013-01-15 17:38:10 +01:00
parent 435348f406
commit 18d56a1891
11 changed files with 1171 additions and 0 deletions

View File

@ -1147,6 +1147,7 @@ AM_CONDITIONAL(USE_LIBCHARON, test x$charon = xtrue -o x$conftest = xtrue -o x$n
AM_CONDITIONAL(USE_LIBIPSEC, test x$libipsec = xtrue)
AM_CONDITIONAL(USE_LIBTNCIF, test x$tnc_tnccs = xtrue -o x$imcv = xtrue)
AM_CONDITIONAL(USE_LIBTNCCS, test x$tnc_tnccs = xtrue)
AM_CONDITIONAL(USE_LIBPTTLS, test x$tnc_tnccs = xtrue)
AM_CONDITIONAL(USE_FILE_CONFIG, test x$stroke = xtrue)
AM_CONDITIONAL(USE_IPSEC_SCRIPT, test x$stroke = xtrue -o x$tools = xtrue -o x$conftest = xtrue)
AM_CONDITIONAL(USE_LIBCAP, test x$capabilities = xlibcap)
@ -1245,6 +1246,7 @@ AC_OUTPUT(
src/libradius/Makefile
src/libtncif/Makefile
src/libtnccs/Makefile
src/libpttls/Makefile
src/libpts/Makefile
src/libpts/plugins/imc_attestation/Makefile
src/libpts/plugins/imv_attestation/Makefile

View File

@ -32,6 +32,10 @@ if USE_LIBTNCCS
SUBDIRS += libtnccs
endif
if USE_LIBPTTLS
SUBDIRS += libpttls
endif
if USE_IMCV
SUBDIRS += libimcv
endif

9
src/libpttls/Makefile.am Normal file
View File

@ -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

120
src/libpttls/pt_tls.c Normal file
View File

@ -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;
}

79
src/libpttls/pt_tls.h Normal file
View File

@ -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_ @}*/

View File

@ -0,0 +1,316 @@
/*
* 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
*/
char *server;
/**
* Server port
*/
u_int16_t port;
/**
* 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)
{
identification_t *id;
host_t *server;
int fd;
server = host_create_from_dns(this->server, AF_UNSPEC, this->port);
if (!server)
{
return FALSE;
}
fd = socket(server->get_family(server), SOCK_STREAM, 0);
if (fd == -1)
{
DBG1(DBG_TNC, "opening PT-TLS socket failed: %s", strerror(errno));
server->destroy(server);
return FALSE;
}
if (connect(fd, server->get_sockaddr(server),
*server->get_sockaddr_len(server)) == -1)
{
DBG1(DBG_TNC, "connecting to PT-TLS server failed: %s", strerror(errno));
server->destroy(server);
close(fd);
return FALSE;
}
server->destroy(server);
id = identification_create_from_string(this->server);
this->tls = tls_socket_create(FALSE, id, NULL, fd, NULL);
id->destroy(id);
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);
}
free(this->server);
free(this);
}
/**
* See header
*/
pt_tls_client_t *pt_tls_client_create(char *server, u_int16_t port)
{
private_pt_tls_client_t *this;
INIT(this,
.public = {
.run_assessment = _run_assessment,
.destroy = _destroy,
},
.server = strdup(server),
.port = port,
);
return &this->public;
}

View File

@ -0,0 +1,56 @@
/*
* 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 <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 server server address to run assessments against
* @param port server TCP port to connect to
* @return PT-TLS context
*/
pt_tls_client_t *pt_tls_client_create(char *server, u_int16_t port);
#endif /** PT_TLS_CLIENT_H_ @}*/

View File

@ -0,0 +1,183 @@
/*
* 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 <networking/host.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;
};
/**
* Open listening server socket
*/
static bool open_socket(private_pt_tls_dispatcher_t *this,
char *server, u_int16_t port)
{
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;
}
host = host_create_from_dns(server, AF_UNSPEC, port);
if (!host)
{
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)
{
while (TRUE)
{
pt_tls_server_t *connection;
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;
}
connection = pt_tls_server_create(this->server, fd);
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(char *server, u_int16_t port)
{
private_pt_tls_dispatcher_t *this;
INIT(this,
.public = {
.dispatch = _dispatch,
.destroy = _destroy,
},
.server = identification_create_from_string(server),
.fd = -1,
);
if (!open_socket(this, server, port))
{
destroy(this);
return NULL;
}
return &this->public;
}

View File

@ -0,0 +1,55 @@
/*
* 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 <utils/utils.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.
*/
void (*dispatch)(pt_tls_dispatcher_t *this);
/**
* Destroy a pt_tls_dispatcher_t.
*/
void (*destroy)(pt_tls_dispatcher_t *this);
};
/**
* Create a pt_tls_dispatcher instance.
*
* @param server server address
* @param port server port to listen
* @return dispatcher service
*/
pt_tls_dispatcher_t *pt_tls_dispatcher_create(char *server, u_int16_t port);
#endif /** PT_TLS_DISPATCHER_H_ @}*/

View File

@ -0,0 +1,282 @@
/*
* 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>
#include <tnc/tnc.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;
this->tnccs = (tls_t*)tnc->tnccs->create_instance(tnc->tnccs,
TNCCS_2_0, TRUE);
if (!this->tnccs)
{
return FAILED;
}
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)
{
DESTROY_IF(this->tnccs);
this->tls->destroy(this->tls);
free(this);
}
/**
* See header
*/
pt_tls_server_t *pt_tls_server_create(identification_t *server, int fd)
{
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),
);
if (!this->tls)
{
free(this);
return NULL;
}
return &this->public;
}

View File

@ -0,0 +1,65 @@
/*
* 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>
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
* @return PT-TLS server
*/
pt_tls_server_t *pt_tls_server_create(identification_t *server, int fd);
#endif /** PT_TLS_SERVER_H_ @}*/