Add a libpttls providing NEA PT-TLS / TNC IF-T for TLS transport layer
authorMartin Willi <martin@revosec.ch>
Tue, 15 Jan 2013 16:38:10 +0000 (17:38 +0100)
committerMartin Willi <martin@revosec.ch>
Wed, 16 Jan 2013 12:53:32 +0000 (13:53 +0100)
configure.in
src/Makefile.am
src/libpttls/Makefile.am [new file with mode: 0644]
src/libpttls/pt_tls.c [new file with mode: 0644]
src/libpttls/pt_tls.h [new file with mode: 0644]
src/libpttls/pt_tls_client.c [new file with mode: 0644]
src/libpttls/pt_tls_client.h [new file with mode: 0644]
src/libpttls/pt_tls_dispatcher.c [new file with mode: 0644]
src/libpttls/pt_tls_dispatcher.h [new file with mode: 0644]
src/libpttls/pt_tls_server.c [new file with mode: 0644]
src/libpttls/pt_tls_server.h [new file with mode: 0644]

index a3c7590..d2b49f3 100644 (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
index e4c0374..e71f73d 100644 (file)
@@ -32,6 +32,10 @@ if USE_LIBTNCCS
   SUBDIRS += libtnccs
 endif
 
+if USE_LIBPTTLS
+  SUBDIRS += libpttls
+endif
+
 if USE_IMCV
   SUBDIRS += libimcv
 endif
diff --git a/src/libpttls/Makefile.am b/src/libpttls/Makefile.am
new file mode 100644 (file)
index 0000000..e79f455
--- /dev/null
@@ -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
diff --git a/src/libpttls/pt_tls.c b/src/libpttls/pt_tls.c
new file mode 100644 (file)
index 0000000..0fee343
--- /dev/null
@@ -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 (file)
index 0000000..8b24225
--- /dev/null
@@ -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 (file)
index 0000000..59e5cd9
--- /dev/null
@@ -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;
+}
diff --git a/src/libpttls/pt_tls_client.h b/src/libpttls/pt_tls_client.h
new file mode 100644 (file)
index 0000000..36ab8c0
--- /dev/null
@@ -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_ @}*/
diff --git a/src/libpttls/pt_tls_dispatcher.c b/src/libpttls/pt_tls_dispatcher.c
new file mode 100644 (file)
index 0000000..f89e1a5
--- /dev/null
@@ -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;
+}
diff --git a/src/libpttls/pt_tls_dispatcher.h b/src/libpttls/pt_tls_dispatcher.h
new file mode 100644 (file)
index 0000000..c6f3864
--- /dev/null
@@ -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_ @}*/
diff --git a/src/libpttls/pt_tls_server.c b/src/libpttls/pt_tls_server.c
new file mode 100644 (file)
index 0000000..2260d72
--- /dev/null
@@ -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;
+}
diff --git a/src/libpttls/pt_tls_server.h b/src/libpttls/pt_tls_server.h
new file mode 100644 (file)
index 0000000..0d952c4
--- /dev/null
@@ -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_ @}*/