diff options
Diffstat (limited to 'src/libpttls')
-rw-r--r-- | src/libpttls/Makefile.am | 10 | ||||
-rw-r--r-- | src/libpttls/pt_tls.c | 120 | ||||
-rw-r--r-- | src/libpttls/pt_tls.h | 79 | ||||
-rw-r--r-- | src/libpttls/pt_tls_client.c | 304 | ||||
-rw-r--r-- | src/libpttls/pt_tls_client.h | 59 | ||||
-rw-r--r-- | src/libpttls/pt_tls_dispatcher.c | 198 | ||||
-rw-r--r-- | src/libpttls/pt_tls_dispatcher.h | 72 | ||||
-rw-r--r-- | src/libpttls/pt_tls_server.c | 277 | ||||
-rw-r--r-- | src/libpttls/pt_tls_server.h | 69 |
9 files changed, 1188 insertions, 0 deletions
diff --git a/src/libpttls/Makefile.am b/src/libpttls/Makefile.am new file mode 100644 index 000000000..d19af0365 --- /dev/null +++ b/src/libpttls/Makefile.am @@ -0,0 +1,10 @@ + +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_LIBADD = $(top_builddir)/src/libtls/libtls.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 diff --git a/src/libpttls/pt_tls.c b/src/libpttls/pt_tls.c new file mode 100644 index 000000000..0fee343b8 --- /dev/null +++ b/src/libpttls/pt_tls.c @@ -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; +} diff --git a/src/libpttls/pt_tls.h b/src/libpttls/pt_tls.h new file mode 100644 index 000000000..8b2422540 --- /dev/null +++ b/src/libpttls/pt_tls.h @@ -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_ @}*/ diff --git a/src/libpttls/pt_tls_client.c b/src/libpttls/pt_tls_client.c new file mode 100644 index 000000000..948d92982 --- /dev/null +++ b/src/libpttls/pt_tls_client.c @@ -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; +} diff --git a/src/libpttls/pt_tls_client.h b/src/libpttls/pt_tls_client.h new file mode 100644 index 000000000..2e0553641 --- /dev/null +++ b/src/libpttls/pt_tls_client.h @@ -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_ @}*/ diff --git a/src/libpttls/pt_tls_dispatcher.c b/src/libpttls/pt_tls_dispatcher.c new file mode 100644 index 000000000..813580cd0 --- /dev/null +++ b/src/libpttls/pt_tls_dispatcher.c @@ -0,0 +1,198 @@ +/* + * 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; + + /** + * Peer identity + */ + identification_t *peer; + + /** + * TNCCS protocol handler constructor + */ + pt_tls_tnccs_constructor_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, + pt_tls_tnccs_constructor_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(this->server, this->peer); + 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); + this->peer->destroy(this->peer); + 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, + /* we currently don't authenticate the peer, use %any identity */ + .peer = identification_create_from_encoding(ID_ANY, chunk_empty), + .fd = -1, + ); + + if (!open_socket(this, address)) + { + address->destroy(address); + destroy(this); + return NULL; + } + address->destroy(address); + + return &this->public; +} diff --git a/src/libpttls/pt_tls_dispatcher.h b/src/libpttls/pt_tls_dispatcher.h new file mode 100644 index 000000000..3c6560baa --- /dev/null +++ b/src/libpttls/pt_tls_dispatcher.h @@ -0,0 +1,72 @@ +/* + * 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; + +/** + * Constructor callback to create TNCCS to use within PT-TLS. + * + * @param server server identity + * @param peer peer identity + */ +typedef tnccs_t* (pt_tls_tnccs_constructor_t)(identification_t *server, + identification_t *peer); + +/** + * 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, + pt_tls_tnccs_constructor_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_ @}*/ diff --git a/src/libpttls/pt_tls_server.c b/src/libpttls/pt_tls_server.c new file mode 100644 index 000000000..b525acb6f --- /dev/null +++ b/src/libpttls/pt_tls_server.c @@ -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; +} diff --git a/src/libpttls/pt_tls_server.h b/src/libpttls/pt_tls_server.h new file mode 100644 index 000000000..244111b43 --- /dev/null +++ b/src/libpttls/pt_tls_server.h @@ -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_ @}*/ |