From: Martin Willi Date: Fri, 5 Jul 2013 14:05:10 +0000 (+0200) Subject: tls-stream: add a new plugin providing TLS secured TCP streams X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5dec6e52024d44f7cf69029139cb32075fb1a8ad;p=thirdparty%2Fstrongswan.git tls-stream: add a new plugin providing TLS secured TCP streams --- diff --git a/configure.in b/configure.in index fbc771bf34..ae23cf10d0 100644 --- a/configure.in +++ b/configure.in @@ -109,6 +109,7 @@ ARG_ENABL_SET([curl], [enable CURL fetcher plugin to fetch files via l ARG_ENABL_SET([unbound], [enable UNBOUND resolver plugin to perform DNS queries via libunbound. Requires libldns and libunbound.]) ARG_ENABL_SET([soup], [enable soup fetcher plugin to fetch from HTTP via libsoup. Requires libsoup.]) ARG_ENABL_SET([ldap], [enable LDAP fetching plugin to fetch files via libldap. Requires openLDAP.]) +ARG_ENABL_SET([tls-stream], [enable libtls based TLS streams and services.]) ARG_DISBL_SET([aes], [disable AES software implementation plugin.]) ARG_DISBL_SET([des], [disable DES/3DES software implementation plugin.]) ARG_ENABL_SET([blowfish], [enable Blowfish software implementation plugin.]) @@ -326,7 +327,7 @@ if test x$eap_sim = xtrue; then simaka=true; fi -if test x$eap_tls = xtrue -o x$eap_ttls = xtrue -o x$eap_peap = xtrue; then +if test x$eap_tls = xtrue -o x$eap_ttls = xtrue -o x$eap_peap = xtrue -o x$tls_stream = xtrue; then tls=true; fi @@ -979,6 +980,7 @@ ADD_PLUGIN([curl], [s charon scepclient scripts nm cmd]) ADD_PLUGIN([soup], [s charon scripts nm cmd]) ADD_PLUGIN([unbound], [s charon scripts]) ADD_PLUGIN([ldap], [s charon scepclient scripts nm cmd]) +ADD_PLUGIN([tls-stream], [s charon]) ADD_PLUGIN([mysql], [s charon pool manager medsrv attest]) ADD_PLUGIN([sqlite], [s charon pool manager medsrv attest]) ADD_PLUGIN([pkcs11], [s charon pki nm cmd]) @@ -1115,6 +1117,7 @@ AM_CONDITIONAL(USE_CURL, test x$curl = xtrue) AM_CONDITIONAL(USE_UNBOUND, test x$unbound = xtrue) AM_CONDITIONAL(USE_SOUP, test x$soup = xtrue) AM_CONDITIONAL(USE_LDAP, test x$ldap = xtrue) +AM_CONDITIONAL(USE_TLS_STREAM, test x$tls_stream = xtrue) AM_CONDITIONAL(USE_AES, test x$aes = xtrue) AM_CONDITIONAL(USE_DES, test x$des = xtrue) AM_CONDITIONAL(USE_BLOWFISH, test x$blowfish = xtrue) @@ -1344,6 +1347,7 @@ AC_CONFIG_FILES([ src/libstrongswan/plugins/unbound/Makefile src/libstrongswan/plugins/soup/Makefile src/libstrongswan/plugins/ldap/Makefile + src/libstrongswan/plugins/tls_stream/Makefile src/libstrongswan/plugins/mysql/Makefile src/libstrongswan/plugins/sqlite/Makefile src/libstrongswan/plugins/padlock/Makefile diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index 8d2865d60b..e9cb00bf0f 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -82,7 +82,7 @@ endif library.lo : $(top_builddir)/config.status -libstrongswan_la_LIBADD = $(PTHREADLIB) $(DLLIB) $(BTLIB) $(SOCKLIB) $(RTLIB) $(BFDLIB) $(UNWINDLIB) +libstrongswan_la_LIBADD = $(PTHREADLIB) $(DLLIB) $(BTLIB) $(SOCKLIB) $(RTLIB) $(BFDLIB) $(UNWINDLIB) $(top_builddir)/src/libtls/libtls.la INCLUDES = -I$(top_srcdir)/src/libstrongswan AM_CFLAGS = \ @@ -375,6 +375,13 @@ if MONOLITHIC endif endif +if USE_TLS_STREAM + SUBDIRS += plugins/tls_stream +if MONOLITHIC + libstrongswan_la_LIBADD += plugins/tls_stream/libstrongswan-tls-stream.la +endif +endif + if USE_MYSQL SUBDIRS += plugins/mysql if MONOLITHIC diff --git a/src/libstrongswan/plugins/tls_stream/Makefile.am b/src/libstrongswan/plugins/tls_stream/Makefile.am new file mode 100644 index 0000000000..391f20a04c --- /dev/null +++ b/src/libstrongswan/plugins/tls_stream/Makefile.am @@ -0,0 +1,18 @@ + +INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/libtls + +AM_CFLAGS = -rdynamic + +if MONOLITHIC +noinst_LTLIBRARIES = libstrongswan-tls-stream.la +else +plugin_LTLIBRARIES = libstrongswan-tls-stream.la +libstrongswan_tls_stream_la_LIBADD = $(top_builddir)/src/libtls/libtls.la +endif + +libstrongswan_tls_stream_la_SOURCES = \ + tls_stream_plugin.h tls_stream_plugin.c \ + tls_stream.h tls_stream.c \ + tls_stream_service.h tls_stream_service.c + +libstrongswan_tls_stream_la_LDFLAGS = -module -avoid-version diff --git a/src/libstrongswan/plugins/tls_stream/tls_stream.c b/src/libstrongswan/plugins/tls_stream/tls_stream.c new file mode 100644 index 0000000000..f020f74925 --- /dev/null +++ b/src/libstrongswan/plugins/tls_stream/tls_stream.c @@ -0,0 +1,418 @@ +/* + * Copyright (C) 2013 Martin Willi + * Copyright (C) 2013 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 . + * + * 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. + */ + +#define _GNU_SOURCE +#include + +#include "tls_stream.h" + +#include + +#include +#include +#include + +typedef struct private_tls_stream_t private_tls_stream_t; + +/** + * Private data of an tls_stream_t object. + */ +struct private_tls_stream_t { + + /** + * Public tls_stream_t interface. + */ + stream_t public; + + /** + * Underlying TLS socket. + */ + tls_socket_t *tls; + + /** + * FD for encrypted data + */ + int fd; + + /** + * Callback if data is ready to read + */ + stream_cb_t read_cb; + + /** + * Data for read-ready callback + */ + void *read_data; + + /** + * Callback if write is non-blocking + */ + stream_cb_t write_cb; + + /** + * Data for write-ready callback + */ + void *write_data; +}; + +METHOD(stream_t, read_, ssize_t, + private_tls_stream_t *this, void *buf, size_t len, bool block) +{ + return this->tls->read(this->tls, buf, len, block); +} + +METHOD(stream_t, read_all, bool, + private_tls_stream_t *this, void *buf, size_t len) +{ + ssize_t ret; + + while (len) + { + ret = read_(this, buf, len, TRUE); + if (ret < 0) + { + return FALSE; + } + if (ret == 0) + { + errno = ECONNRESET; + return FALSE; + } + len -= ret; + buf += ret; + } + return TRUE; +} + +METHOD(stream_t, write_, ssize_t, + private_tls_stream_t *this, void *buf, size_t len, bool block) +{ + return this->tls->write(this->tls, buf, len); +} + +METHOD(stream_t, write_all, bool, + private_tls_stream_t *this, void *buf, size_t len) +{ + ssize_t ret; + + while (len) + { + ret = write_(this, buf, len, TRUE); + if (ret < 0) + { + return FALSE; + } + if (ret == 0) + { + errno = ECONNRESET; + return FALSE; + } + len -= ret; + buf += ret; + } + return TRUE; +} + +/** + * Remove a registered watcher + */ +static void remove_watcher(private_tls_stream_t *this) +{ + if (this->read_cb || this->write_cb) + { + lib->watcher->remove(lib->watcher, this->fd); + } +} + +/** + * Watcher callback + */ +static bool watch(private_tls_stream_t *this, int fd, watcher_event_t event) +{ + bool keep = FALSE; + stream_cb_t cb; + + switch (event) + { + case WATCHER_READ: + cb = this->read_cb; + this->read_cb = NULL; + keep = cb(this->read_data, &this->public); + if (keep) + { + this->read_cb = cb; + } + break; + case WATCHER_WRITE: + cb = this->write_cb; + this->write_cb = NULL; + keep = cb(this->write_data, &this->public); + if (keep) + { + this->write_cb = cb; + } + break; + case WATCHER_EXCEPT: + break; + } + return keep; +} + +/** + * Register watcher for stream callbacks + */ +static void add_watcher(private_tls_stream_t *this) +{ + watcher_event_t events = 0; + + if (this->read_cb) + { + events |= WATCHER_READ; + } + if (this->write_cb) + { + events |= WATCHER_WRITE; + } + if (events) + { + lib->watcher->add(lib->watcher, this->fd, events, + (watcher_cb_t)watch, this); + } +} + +METHOD(stream_t, on_read, void, + private_tls_stream_t *this, stream_cb_t cb, void *data) +{ + remove_watcher(this); + + this->read_cb = cb; + this->read_data = data; + + add_watcher(this); +} + +METHOD(stream_t, on_write, void, + private_tls_stream_t *this, stream_cb_t cb, void *data) +{ + remove_watcher(this); + + this->write_cb = cb; + this->write_data = data; + + add_watcher(this); +} + +#if defined(HAVE_FOPENCOOKIE) + +/** + * Read callback for fopencookie() + */ +static ssize_t cookie_read(private_tls_stream_t *this, char *buf, size_t len) +{ + return this->tls->read(this->tls, buf, len, TRUE); +} + +/** + * Write callback for fopencookie() + */ +static ssize_t cookie_write(private_tls_stream_t *this, char *buf, size_t len) +{ + return this->tls->write(this->tls, buf, len); +} + +METHOD(stream_t, get_file, FILE*, + private_tls_stream_t *this) +{ + static cookie_io_functions_t cookie_funcs = { + .read = (void*)cookie_read, + .write = (void*)cookie_write, + .seek = NULL, + .close = NULL, + }; + return fopencookie(this, "r+", cookie_funcs); +} + +#elif defined(HAVE_FUNOPEN) + +/** + * Read callback for funopen() + */ +static int fun_read(private_tls_stream_t *this, char *buf, int len) +{ + return this->tls->read(this->tls, buf, len, TRUE); +} + +/** + * Write callback for funopen() + */ +static int fun_write(private_tls_stream_t *this, char *buf, int len) +{ + return this->tls->write(this->tls, buf, len); +} + +METHOD(stream_t, get_file, FILE*, + private_tls_stream_t *this) +{ + return funopen(this, (void*)fun_read, (void*)fun_write, NULL, NULL); +} + +#else /* !HAVE_FOPENCOOKIE && !HAVE_FUNOPEN */ + +METHOD(stream_t, get_file, FILE*, + private_tls_stream_t *this) +{ + return NULL; +} + +#endif /* HAVE_FOPENCOOKIE/HAVE_FUNOPEN */ + +METHOD(stream_t, destroy, void, + private_tls_stream_t *this) +{ + this->tls->destroy(this->tls); + close(this->fd); + free(this); +} + +/** + * See header + */ +stream_t *tls_stream_create_from_fd(int fd, bool is_server, + identification_t *server, + tls_cache_t *cache) +{ + private_tls_stream_t *this; + + INIT(this, + .public = { + .read = _read_, + .read_all = _read_all, + .on_read = _on_read, + .write = _write_, + .write_all = _write_all, + .on_write = _on_write, + .get_file = _get_file, + .destroy = _destroy, + }, + .tls = tls_socket_create(is_server, server, NULL, fd, cache), + .fd = fd, + ); + + if (!this->tls) + { + free(this); + return NULL; + } + return &this->public; +} + +/** + * See header. + */ +int tls_stream_parse_uri(char *uri, struct sockaddr *addr, + identification_t **server) +{ + identification_t *id; + char *pos, buf[256]; + host_t *host; + u_long port; + int len; + + if (!strncaseeq(uri, "tcp+tls://", strlen("tcp+tls://"))) + { + return -1; + } + uri += strlen("tcp+tls://"); + pos = strrchr(uri, '@'); + if (!pos) + { + return -1; + } + id = identification_create_from_data(chunk_create(uri, pos - uri)); + uri = pos + 1; + pos = strrchr(uri, ':'); + if (!pos) + { + id->destroy(id); + return -1; + } + if (*uri == '[' && pos > uri && *(pos - 1) == ']') + { + /* IPv6 URI */ + snprintf(buf, sizeof(buf), "%.*s", (int)(pos - uri - 2), uri + 1); + } + else + { + snprintf(buf, sizeof(buf), "%.*s", (int)(pos - uri), uri); + } + port = strtoul(pos + 1, &pos, 10); + if (port == ULONG_MAX || *pos || port > 65535) + { + id->destroy(id); + return -1; + } + host = host_create_from_dns(buf, AF_UNSPEC, port); + if (!host) + { + id->destroy(id); + return -1; + } + len = *host->get_sockaddr_len(host); + memcpy(addr, host->get_sockaddr(host), len); + host->destroy(host); + *server = id; + return len; +} + +/** + * See header + */ +stream_t *tls_stream_create(char *uri) +{ + union { + struct sockaddr_in in; + struct sockaddr_in6 in6; + struct sockaddr sa; + } addr; + int fd, len; + identification_t *server; + stream_t *stream; + + len = tls_stream_parse_uri(uri, &addr.sa, &server); + if (len == -1) + { + DBG1(DBG_NET, "invalid stream URI: '%s'", uri); + return NULL; + } + fd = socket(addr.sa.sa_family, SOCK_STREAM, 0); + if (fd < 0) + { + DBG1(DBG_NET, "opening socket '%s' failed: %s", uri, strerror(errno)); + return NULL; + } + if (connect(fd, &addr.sa, len)) + { + DBG1(DBG_NET, "connecting to '%s' failed: %s", uri, strerror(errno)); + close(fd); + return NULL; + } + stream = tls_stream_create_from_fd(fd, FALSE, server, NULL); + server->destroy(server); + if (!stream) + { + close(fd); + } + return stream; +} diff --git a/src/libstrongswan/plugins/tls_stream/tls_stream.h b/src/libstrongswan/plugins/tls_stream/tls_stream.h new file mode 100644 index 0000000000..fad19e932f --- /dev/null +++ b/src/libstrongswan/plugins/tls_stream/tls_stream.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2013 Martin Willi + * Copyright (C) 2013 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 . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup tls_stream tls_stream + * @{ @ingroup tls + */ + +#ifndef TLS_STREAM_H_ +#define TLS_STREAM_H_ + +#include +#include + +/** + * Helper function to parse a tcp+tls:// URI. + * + * @param uri URI to parse + * @param addr sockaddr, large enough for URI address + * @param server pointer receiving allocated server identity + * @return len of created addr, -1 on error + */ +int tls_stream_parse_uri(char *uri, struct sockaddr *addr, + identification_t **server); + +/** + * Helper function to create a stream from an FD and a server identity. + * + * @param fd file descripter + * @param is_server TRUE to act as TLS server, FALSE for client + * @param server server identity, gets cloned + * @param cache shared TLS session cache, if any + * @return client stream, NULL on error + */ +stream_t *tls_stream_create_from_fd(int fd, bool is_server, + identification_t *server, + tls_cache_t *cache); + +/** + * Create a tls_stream instance. + * + * The following URIs are currently accepted by this constructor: + * - tcp+tls://serverid@address:port + * Server authenticates with a certificate for serverid, no client auth. + * + * @param uri URI to create a stream for + * @return stream instance, NULL on error + */ +stream_t *tls_stream_create(char *uri); + +#endif /** TLS_STREAM_H_ @}*/ diff --git a/src/libstrongswan/plugins/tls_stream/tls_stream_plugin.c b/src/libstrongswan/plugins/tls_stream/tls_stream_plugin.c new file mode 100644 index 0000000000..79d536823c --- /dev/null +++ b/src/libstrongswan/plugins/tls_stream/tls_stream_plugin.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2013 Martin Willi + * Copyright (C) 2013 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 . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "tls_stream_plugin.h" +#include "tls_stream.h" +#include "tls_stream_service.h" + +#include + +typedef struct private_tls_stream_plugin_t private_tls_stream_plugin_t; + +/** + * Private data of tls_stream_plugin + */ +struct private_tls_stream_plugin_t { + + /** + * public functions + */ + tls_stream_plugin_t public; +}; + +METHOD(plugin_t, get_name, char*, + private_tls_stream_plugin_t *this) +{ + return "tls-stream"; +} + +METHOD(plugin_t, get_features, int, + private_tls_stream_plugin_t *this, plugin_feature_t *features[]) +{ + static plugin_feature_t f[] = { + PLUGIN_REGISTER(STREAM, tls_stream_create), + PLUGIN_PROVIDE(STREAM, "tcp+tls://"), + PLUGIN_REGISTER(STREAM_SERVICE, tls_stream_service_create), + PLUGIN_PROVIDE(STREAM_SERVICE, "tcp+tls://"), + }; + *features = f; + return countof(f); +} + +METHOD(plugin_t, destroy, void, + private_tls_stream_plugin_t *this) +{ + free(this); +} + +/* + * see header file + */ +plugin_t *tls_stream_plugin_create() +{ + private_tls_stream_plugin_t *this; + + INIT(this, + .public = { + .plugin = { + .get_name = _get_name, + .get_features = _get_features, + .destroy = _destroy, + }, + }, + ); + + return &this->public.plugin; +} diff --git a/src/libstrongswan/plugins/tls_stream/tls_stream_plugin.h b/src/libstrongswan/plugins/tls_stream/tls_stream_plugin.h new file mode 100644 index 0000000000..9212fa091c --- /dev/null +++ b/src/libstrongswan/plugins/tls_stream/tls_stream_plugin.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2013 Martin Willi + * Copyright (C) 2013 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 . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup tls_stream tls_stream + * @ingroup plugins + * + * @defgroup tls_stream_plugin tls_stream_plugin + * @{ @ingroup tls_stream + */ + +#ifndef TLS_STREAM_PLUGIN_H_ +#define TLS_STREAM_PLUGIN_H_ + +#include + +typedef struct tls_stream_plugin_t tls_stream_plugin_t; + +/** + * Plugin providing TLS protected streams and stream services. + */ +struct tls_stream_plugin_t { + + /** + * Implements plugin interface. + */ + plugin_t plugin; +}; + +#endif /** TLS_STREAM_PLUGIN_H_ @}*/ diff --git a/src/libstrongswan/plugins/tls_stream/tls_stream_service.c b/src/libstrongswan/plugins/tls_stream/tls_stream_service.c new file mode 100644 index 0000000000..b28d883dff --- /dev/null +++ b/src/libstrongswan/plugins/tls_stream/tls_stream_service.c @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2013 Martin Willi + * Copyright (C) 2013 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 . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "tls_stream_service.h" +#include "tls_stream.h" + +#include +#include +#include +#include + +#include +#include + +typedef struct private_tls_stream_service_t private_tls_stream_service_t; + +/** + * Private data of an tls_stream_service_t object. + */ +struct private_tls_stream_service_t { + + /** + * Public tls_stream_service_t interface. + */ + stream_service_t public; + + /** + * Underlying socket + */ + int fd; + + /** + * Accept callback + */ + stream_service_cb_t cb; + + /** + * Accept callback data + */ + void *data; + + /** + * Job priority to invoke callback with + */ + job_priority_t prio; + + /** + * Maximum number of parallel callback invocations + */ + u_int cncrncy; + + /** + * Currently active jobs + */ + u_int active; + + /** + * mutex to lock active counter + */ + mutex_t *mutex; + + /** + * Condvar to wait for callback termination + */ + condvar_t *condvar; + + /** + * Server identity + */ + identification_t *server; + + /** + * TLS session cache for this service + */ + tls_cache_t *cache; +}; + +/** + * Data to pass to async accept job + */ +typedef struct { + /** callback function */ + stream_service_cb_t cb; + /** callback data */ + void *data; + /** accepted connection */ + int fd; + /** reference to stream service */ + private_tls_stream_service_t *this; +} async_data_t; + +/** + * Clean up accept data + */ +static void destroy_async_data(async_data_t *data) +{ + private_tls_stream_service_t *this = data->this; + + this->mutex->lock(this->mutex); + if (this->active-- == this->cncrncy) + { + /* leaving concurrency limit, restart accept()ing. */ + this->public.on_accept(&this->public, this->cb, this->data, + this->prio, this->cncrncy); + } + this->condvar->signal(this->condvar); + this->mutex->unlock(this->mutex); + + if (data->fd != -1) + { + close(data->fd); + } + free(data); +} + +/** + * Async processing of accepted connection + */ +static job_requeue_t accept_async(async_data_t *data) +{ + stream_t *stream; + + stream = tls_stream_create_from_fd(data->fd, TRUE, data->this->server, + data->this->cache); + if (stream) + { + /* FD is now owned by stream, don't close it during cleanup */ + data->fd = -1; + thread_cleanup_push((void*)stream->destroy, stream); + thread_cleanup_pop(!data->cb(data->data, stream)); + } + return JOB_REQUEUE_NONE; +} + +/** + * Watcher callback function + */ +static bool watch(private_tls_stream_service_t *this, + int fd, watcher_event_t event) +{ + async_data_t *data; + bool keep = TRUE; + + INIT(data, + .cb = this->cb, + .data = this->data, + .fd = accept(fd, NULL, NULL), + .this = this, + ); + + if (data->fd != -1) + { + this->mutex->lock(this->mutex); + if (++this->active == this->cncrncy) + { + /* concurrency limit reached, stop accept()ing new connections */ + keep = FALSE; + } + this->mutex->unlock(this->mutex); + + lib->processor->queue_job(lib->processor, + (job_t*)callback_job_create_with_prio((void*)accept_async, data, + (void*)destroy_async_data, (callback_job_cancel_t)return_false, + this->prio)); + } + else + { + free(data); + } + return keep; +} + +METHOD(stream_service_t, on_accept, void, + private_tls_stream_service_t *this, stream_service_cb_t cb, void *data, + job_priority_t prio, u_int cncrncy) +{ + this->mutex->lock(this->mutex); + + /* wait for all callbacks to return */ + this->mutex->lock(this->mutex); + while (this->active) + { + this->condvar->wait(this->condvar, this->mutex); + } + this->mutex->unlock(this->mutex); + + if (this->cb) + { + lib->watcher->remove(lib->watcher, this->fd); + } + + this->cb = cb; + this->data = data; + if (prio <= JOB_PRIO_MAX) + { + this->prio = prio; + } + this->cncrncy = cncrncy; + + if (this->cb) + { + lib->watcher->add(lib->watcher, this->fd, + WATCHER_READ, (watcher_cb_t)watch, this); + } + + this->mutex->unlock(this->mutex); +} + +METHOD(stream_service_t, destroy, void, + private_tls_stream_service_t *this) +{ + on_accept(this, NULL, NULL, this->prio, this->cncrncy); + close(this->fd); + this->mutex->destroy(this->mutex); + this->condvar->destroy(this->condvar); + this->server->destroy(this->server); + this->cache->destroy(this->cache); + free(this); +} + +/** + * See header + */ +stream_service_t *tls_stream_service_create_from_fd(int fd, + identification_t *server) +{ + private_tls_stream_service_t *this; + + INIT(this, + .public = { + .on_accept = _on_accept, + .destroy = _destroy, + }, + .fd = fd, + .prio = JOB_PRIO_MEDIUM, + .mutex = mutex_create(MUTEX_TYPE_RECURSIVE), + .condvar = condvar_create(CONDVAR_TYPE_DEFAULT), + .server = server, + .cache = tls_cache_create(25, 180), + ); + + return &this->public; +} + +/** + * See header + */ +stream_service_t *tls_stream_service_create(char *uri, int backlog) +{ + union { + struct sockaddr_in in; + struct sockaddr_in6 in6; + struct sockaddr sa; + } addr; + int fd, len, on = 1; + identification_t *server; + + len = tls_stream_parse_uri(uri, &addr.sa, &server); + if (len == -1) + { + DBG1(DBG_NET, "invalid stream URI: '%s'", uri); + return NULL; + } + fd = socket(addr.sa.sa_family, SOCK_STREAM, 0); + if (fd < 0) + { + DBG1(DBG_NET, "opening socket '%s' failed: %s", uri, strerror(errno)); + server->destroy(server); + return NULL; + } + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0) + { + DBG1(DBG_NET, "SO_REUSADDR on '%s' failed: %s", uri, strerror(errno)); + } + if (bind(fd, &addr.sa, len) < 0) + { + DBG1(DBG_NET, "binding socket '%s' failed: %s", uri, strerror(errno)); + server->destroy(server); + close(fd); + return NULL; + } + if (listen(fd, backlog) < 0) + { + DBG1(DBG_NET, "listen on socket '%s' failed: %s", uri, strerror(errno)); + server->destroy(server); + close(fd); + return NULL; + } + return tls_stream_service_create_from_fd(fd, server); +} diff --git a/src/libstrongswan/plugins/tls_stream/tls_stream_service.h b/src/libstrongswan/plugins/tls_stream/tls_stream_service.h new file mode 100644 index 0000000000..2610b9e0b1 --- /dev/null +++ b/src/libstrongswan/plugins/tls_stream/tls_stream_service.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2013 Martin Willi + * Copyright (C) 2013 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 . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup tls_stream_service tls_stream_service + * @{ @ingroup tls + */ + +#ifndef TLS_STREAM_SERVICE_H_ +#define TLS_STREAM_SERVICE_H_ + +#include + +/** + * Create a service instance for TLS secured TCP sockets. + * + * @param uri TLS socket specific URI, must start with "tcp+tls://" + * @param backlog size of the backlog queue, as passed to listen() + * @return stream_service instance, NULL on failure + */ +stream_service_t* tls_stream_service_create(char *uri, int backlog); + +#endif /** TLS_STREAM_SERVICE_H_ @}*/