Commit fa86f2de authored by Philipp Schafft's avatar Philipp Schafft 🦁

Merge branch 'ph3-update-TLS'

parents b7087c38 cd9c8420
......@@ -16,7 +16,7 @@ noinst_HEADERS = admin.h cfgfile.h logging.h sighandler.h connection.h \
format_kate.h format_skeleton.h format_opus.h
icecast_SOURCES = cfgfile.c main.c logging.c sighandler.c connection.c global.c \
util.c slave.c source.c stats.c refbuf.c client.c playlist.c \
xslt.c fserve.c admin.c md5.c matchfile.c \
xslt.c fserve.c admin.c md5.c matchfile.c tls.c \
format.c format_ogg.c format_mp3.c format_midi.c format_flac.c format_ebml.c \
format_kate.c format_skeleton.c format_opus.c \
event.c event_log.c event_exec.c \
......
......@@ -695,11 +695,7 @@ static inline xmlNodePtr __add_listener(client_t *client,
if (client->role)
xmlNewTextChild(node, NULL, XMLSTR("role"), XMLSTR(client->role));
#ifdef HAVE_OPENSSL
xmlNewTextChild(node, NULL, XMLSTR("tls"), XMLSTR(client->con->ssl ? "true" : "false"));
#else
xmlNewTextChild(node, NULL, XMLSTR("tls"), XMLSTR("false"));
#endif
xmlNewTextChild(node, NULL, XMLSTR("tls"), XMLSTR(client->con->tls ? "true" : "false"));
return node;
}
......
......@@ -34,11 +34,13 @@
#include "util.h"
#include "auth.h"
#include "event.h"
#include "tls.h"
/* for config_reread_config() */
#include "yp.h"
#include "fserve.h"
#include "stats.h"
#include "connection.h"
#define CATMODULE "CONFIG"
#define CONFIG_DEFAULT_LOCATION "Earth"
......@@ -233,6 +235,60 @@ static inline int __parse_public(const char *str)
return util_str_to_bool(str);
}
/* This converts TLS mode strings to (tlsmode_t).
* In older versions of Icecast2 this was just a bool.
* So we need to handle boolean values as well.
* See also: util_str_to_bool().
*/
static tlsmode_t str_to_tlsmode(const char *str) {
/* consider NULL and empty strings as auto mode */
if (!str || !*str)
return ICECAST_TLSMODE_AUTO;
if (strcasecmp(str, "disabled") == 0) {
return ICECAST_TLSMODE_DISABLED;
} else if (strcasecmp(str, "auto") == 0) {
return ICECAST_TLSMODE_AUTO;
} else if (strcasecmp(str, "auto_no_plain") == 0) {
return ICECAST_TLSMODE_AUTO_NO_PLAIN;
} else if (strcasecmp(str, "rfc2817") == 0) {
return ICECAST_TLSMODE_RFC2817;
} else if (strcasecmp(str, "rfc2818") == 0 ||
/* boolean-style values */
strcasecmp(str, "true") == 0 ||
strcasecmp(str, "yes") == 0 ||
strcasecmp(str, "on") == 0 ) {
return ICECAST_TLSMODE_RFC2818;
}
/* old style numbers: consider everyting non-zero RFC2818 */
if (atoi(str))
return ICECAST_TLSMODE_RFC2818;
/* we default to auto mode */
return ICECAST_TLSMODE_AUTO;
}
/* This checks for the TLS implementation of a node */
static int __check_node_impl(xmlNodePtr node, const char *def)
{
char *impl;
int res;
impl = (char *)xmlGetProp(node, XMLSTR("implementation"));
if (!impl)
impl = (char *)xmlGetProp(node, XMLSTR("impl"));
if (!impl)
impl = (char *)xmlStrdup(XMLSTR(def));
res = tls_check_impl(impl);
xmlFree(impl);
return res;
}
static void __append_old_style_auth(auth_stack_t **stack,
const char *name,
const char *type,
......@@ -532,8 +588,6 @@ void config_clear(ice_config_t *c)
if (c->webroot_dir) xmlFree(c->webroot_dir);
if (c->adminroot_dir) xmlFree(c->adminroot_dir);
if (c->null_device) xmlFree(c->null_device);
if (c->cert_file) xmlFree(c->cert_file);
if (c->cipher_list) xmlFree(c->cipher_list);
if (c->pidfile) xmlFree(c->pidfile);
if (c->banfile) xmlFree(c->banfile);
if (c->allowfile) xmlFree(c->allowfile);
......@@ -549,6 +603,10 @@ void config_clear(ice_config_t *c)
if (c->group) xmlFree(c->group);
if (c->mimetypes_fn) xmlFree(c->mimetypes_fn);
if (c->tls_context.cert_file) xmlFree(c->tls_context.cert_file);
if (c->tls_context.key_file) xmlFree(c->tls_context.key_file);
if (c->tls_context.cipher_list) xmlFree(c->tls_context.cipher_list);
event_registration_release(c->event);
while ((c->listen_sock = config_clear_listener(c->listen_sock)));
......@@ -636,6 +694,7 @@ void config_reread_config(void)
config_set_config(&new_config);
config = config_get_config_unlocked();
restart_logging(config);
connection_reread_config(config);
yp_recheck_config(config);
fserve_recheck_mime_types(config);
stats_global(config);
......@@ -766,8 +825,6 @@ static void _set_defaults(ice_config_t *configuration)
->base_dir = (char *) xmlCharStrdup(CONFIG_DEFAULT_BASE_DIR);
configuration
->log_dir = (char *) xmlCharStrdup(CONFIG_DEFAULT_LOG_DIR);
configuration
->cipher_list = (char *) xmlCharStrdup(CONFIG_DEFAULT_CIPHER_LIST);
configuration
->null_device = (char *) xmlCharStrdup(CONFIG_DEFAULT_NULL_FILE);
configuration
......@@ -795,6 +852,8 @@ static void _set_defaults(ice_config_t *configuration)
/* default to a typical prebuffer size used by clients */
configuration
->burst_size = CONFIG_DEFAULT_BURST_SIZE;
configuration->tls_context
.cipher_list = (char *) xmlCharStrdup(CONFIG_DEFAULT_CIPHER_LIST);
}
static inline void __check_hostname(ice_config_t *configuration)
......@@ -1676,7 +1735,7 @@ static void _parse_listen_socket(xmlDocPtr doc,
} else if (xmlStrcmp(node->name, XMLSTR("tls")) == 0 ||
xmlStrcmp(node->name, XMLSTR("ssl")) == 0) {
tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
listener->ssl = util_str_to_bool(tmp);
listener->tls = str_to_tlsmode(tmp);
if(tmp)
xmlFree(tmp);
} else if (xmlStrcmp(node->name, XMLSTR("shoutcast-compat")) == 0) {
......@@ -1882,14 +1941,24 @@ static void _parse_paths(xmlDocPtr doc,
configuration->allowfile = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
} else if (xmlStrcmp(node->name, XMLSTR("tls-certificate")) == 0 ||
xmlStrcmp(node->name, XMLSTR("ssl-certificate")) == 0) {
if (configuration->cert_file)
xmlFree(configuration->cert_file);
configuration->cert_file = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
if (__check_node_impl(node, "generic") != 0) {
ICECAST_LOG_WARN("Node %s uses unsupported implementation.", node->name);
continue;
}
if (configuration->tls_context.cert_file)
xmlFree(configuration->tls_context.cert_file);
configuration->tls_context.cert_file = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
} else if (xmlStrcmp(node->name, XMLSTR("tls-allowed-ciphers")) == 0 ||
xmlStrcmp(node->name, XMLSTR("ssl-allowed-ciphers")) == 0) {
if (configuration->cipher_list)
xmlFree(configuration->cipher_list);
configuration->cipher_list = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
if (__check_node_impl(node, "openssl") != 0) {
ICECAST_LOG_WARN("Node %s uses unsupported implementation.", node->name);
continue;
}
if (configuration->tls_context.cipher_list)
xmlFree(configuration->tls_context.cipher_list);
configuration->tls_context.cipher_list = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
} else if (xmlStrcmp(node->name, XMLSTR("webroot")) == 0) {
if (!(temp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1))) {
ICECAST_LOG_WARN("<webroot> setting must not be empty.");
......@@ -2002,6 +2071,54 @@ static void _parse_logging(xmlDocPtr doc,
} while ((node = node->next));
}
static void _parse_tls_context(xmlDocPtr doc,
xmlNodePtr node,
ice_config_t *configuration)
{
config_tls_context_t *context = &configuration->tls_context;
node = node->xmlChildrenNode;
do {
if (node == NULL)
break;
if (xmlIsBlankNode(node))
continue;
if (xmlStrcmp(node->name, XMLSTR("tls-certificate")) == 0) {
if (__check_node_impl(node, "generic") != 0) {
ICECAST_LOG_WARN("Node %s uses unsupported implementation.", node->name);
continue;
}
if (context->cert_file)
xmlFree(context->cert_file);
context->cert_file = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
} else if (xmlStrcmp(node->name, XMLSTR("tls-key")) == 0) {
if (__check_node_impl(node, "generic") != 0) {
ICECAST_LOG_WARN("Node %s uses unsupported implementation.", node->name);
continue;
}
if (context->key_file)
xmlFree(context->key_file);
context->key_file = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
} else if (xmlStrcmp(node->name, XMLSTR("tls-allowed-ciphers")) == 0) {
if (__check_node_impl(node, "openssl") != 0) {
ICECAST_LOG_WARN("Node %s uses unsupported implementation.", node->name);
continue;
}
if (context->cipher_list)
xmlFree(context->cipher_list);
context->cipher_list = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
} else {
ICECAST_LOG_ERROR("Unknown config tag: %s", node->name);
}
} while ((node = node->next));
}
static void _parse_security(xmlDocPtr doc,
xmlNodePtr node,
ice_config_t *configuration)
......@@ -2020,6 +2137,8 @@ static void _parse_security(xmlDocPtr doc,
configuration->chroot = util_str_to_bool(tmp);
if (tmp)
xmlFree(tmp);
} else if (xmlStrcmp(node->name, XMLSTR("tls-context")) == 0) {
_parse_tls_context(doc, node, configuration);
} else if (xmlStrcmp(node->name, XMLSTR("changeowner")) == 0) {
configuration->chuid = 1;
oldnode = node;
......
......@@ -172,9 +172,15 @@ typedef struct _listener_t {
char *bind_address;
int shoutcast_compat;
char *shoutcast_mount;
int ssl;
tlsmode_t tls;
} listener_t;
typedef struct _config_tls_context {
char *cert_file;
char *key_file;
char *cipher_list;
} config_tls_context_t;
typedef struct ice_config_tag {
char *config_filename;
......@@ -229,8 +235,6 @@ typedef struct ice_config_tag {
char *null_device;
char *banfile;
char *allowfile;
char *cert_file;
char *cipher_list;
char *webroot_dir;
char *adminroot_dir;
aliases *aliases;
......@@ -242,6 +246,8 @@ typedef struct ice_config_tag {
int logsize;
int logarchive;
config_tls_context_t tls_context;
int chroot;
int chuid;
char *user;
......
......@@ -105,26 +105,24 @@ static inline void client_reuseconnection(client_t *client) {
client->con->sock = -1; /* TODO: do not use magic */
/* handle to keep the TLS connection */
#ifdef HAVE_OPENSSL
if (client->con->ssl) {
if (client->con->tls) {
/* AHhhggrr.. That pain....
* stealing SSL state...
* stealing TLS state...
*/
con->ssl = client->con->ssl;
con->tls = client->con->tls;
con->read = client->con->read;
con->send = client->con->send;
client->con->ssl = NULL;
client->con->tls = NULL;
client->con->read = NULL;
client->con->send = NULL;
}
#endif
client->reuse = ICECAST_REUSE_CLOSE;
client_destroy(client);
if (reuse == ICECAST_REUSE_UPGRADETLS)
connection_uses_ssl(con);
connection_uses_tls(con);
connection_queue(con);
}
......
This diff is collapsed.
......@@ -16,10 +16,8 @@
#include <sys/types.h>
#include <time.h>
#ifdef HAVE_OPENSSL
#include <openssl/ssl.h>
#include <openssl/err.h>
#endif
#include "tls.h"
#include "compat.h"
#include "common/httpp/httpp.h"
......@@ -30,6 +28,19 @@ struct _client_tag;
struct source_tag;
struct ice_config_tag;
typedef enum _tlsmode_tag {
/* no TLS is used at all */
ICECAST_TLSMODE_DISABLED = 0,
/* TLS mode is to be detected */
ICECAST_TLSMODE_AUTO,
/* Like ICECAST_TLSMODE_AUTO but enforces TLS */
ICECAST_TLSMODE_AUTO_NO_PLAIN,
/* TLS via HTTP Upgrade:-header [RFC2817] */
ICECAST_TLSMODE_RFC2817,
/* TLS for transport layer like HTTPS [RFC2818] does */
ICECAST_TLSMODE_RFC2818
} tlsmode_t;
typedef struct connection_tag
{
unsigned long id;
......@@ -42,9 +53,8 @@ typedef struct connection_tag
sock_t serversock;
int error;
#ifdef HAVE_OPENSSL
SSL *ssl; /* SSL handler */
#endif
tlsmode_t tlsmode;
tls_t *tls;
int (*send)(struct connection_tag *handle, const void *buf, size_t len);
int (*read)(struct connection_tag *handle, void *buf, size_t len);
......@@ -53,13 +63,14 @@ typedef struct connection_tag
void connection_initialize(void);
void connection_shutdown(void);
void connection_reread_config(struct ice_config_tag *config);
void connection_accept_loop(void);
int connection_setup_sockets(struct ice_config_tag *config);
void connection_close(connection_t *con);
connection_t *connection_create(sock_t sock, sock_t serversock, char *ip);
int connection_complete_source(struct source_tag *source, int response);
void connection_queue(connection_t *con);
void connection_uses_ssl(connection_t *con);
void connection_uses_tls(connection_t *con);
ssize_t connection_read_bytes(connection_t *con, void *buf, size_t len);
......
......@@ -123,6 +123,7 @@ void initialize_subsystems(void)
sock_initialize();
resolver_initialize();
config_initialize();
tls_initialize();
connection_initialize();
global_initialize();
refbuf_initialize();
......@@ -145,6 +146,7 @@ void shutdown_subsystems(void)
global_shutdown();
connection_shutdown();
tls_shutdown();
config_shutdown();
resolver_shutdown();
sock_shutdown();
......
......@@ -514,10 +514,8 @@ static refbuf_t *get_next_buffer (source_t *source)
}
source->last_read = current;
refbuf = source->format->get_buffer (source);
#ifdef HAVE_OPENSSL
if (source->client->con->ssl && (SSL_get_shutdown(source->client->con->ssl) & SSL_RECEIVED_SHUTDOWN))
if (source->client->con->tls && tls_got_shutdown(source->client->con->tls) > 1)
source->client->con->error = 1;
#endif
if (source->client->con && source->client->con->error)
{
ICECAST_LOG_INFO("End of Stream %s", source->mount);
......
/* Icecast
*
* This program is distributed under the GNU General Public License, version 2.
* A copy of this license is included with this source.
*
* Copyright 2016, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
/**
* TLS support functions
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <strings.h>
#include "tls.h"
#include "logging.h"
#define CATMODULE "tls"
/* Check for a specific implementation. Returns 0 if supported, 1 if unsupported and -1 on error. */
int tls_check_impl(const char *impl)
{
#ifdef HAVE_OPENSSL
if (!strcasecmp(impl, "openssl"))
return 0;
#endif
#ifdef ICECAST_CAP_TLS
if (!strcasecmp(impl, "generic"))
return 0;
#endif
return 1;
}
#ifdef HAVE_OPENSSL
struct tls_ctx_tag {
size_t refc;
SSL_CTX *ctx;
};
struct tls_tag {
size_t refc;
SSL *ssl;
tls_ctx_t *ctx;
};
void tls_initialize(void)
{
SSL_load_error_strings(); /* readable error messages */
SSL_library_init(); /* initialize library */
}
void tls_shutdown(void)
{
}
tls_ctx_t *tls_ctx_new(const char *cert_file, const char *key_file, const char *cipher_list)
{
tls_ctx_t *ctx;
SSL_METHOD *method;
long ssl_opts;
if (!cert_file || !key_file || !cipher_list)
return NULL;
ctx = calloc(1, sizeof(*ctx));
if (!ctx)
return NULL;
method = SSLv23_server_method();
ctx->refc = 1;
ctx->ctx = SSL_CTX_new(method);
ssl_opts = SSL_CTX_get_options(ctx->ctx);
#ifdef SSL_OP_NO_COMPRESSION
SSL_CTX_set_options(ctx->ctx, ssl_opts|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_COMPRESSION);
#else
SSL_CTX_set_options(ctx->ctx, ssl_opts|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3);
#endif
do {
if (SSL_CTX_use_certificate_chain_file(ctx->ctx, cert_file) <= 0) {
ICECAST_LOG_WARN("Invalid cert file %s", cert_file);
break;
}
if (SSL_CTX_use_PrivateKey_file(ctx->ctx, key_file, SSL_FILETYPE_PEM) <= 0) {
ICECAST_LOG_WARN("Invalid private key file %s", key_file);
break;
}
if (!SSL_CTX_check_private_key(ctx->ctx)) {
ICECAST_LOG_ERROR("Invalid %s - Private key does not match cert public key", key_file);
break;
}
if (SSL_CTX_set_cipher_list(ctx->ctx, cipher_list) <= 0) {
ICECAST_LOG_WARN("Invalid cipher list: %s", cipher_list);
}
ICECAST_LOG_INFO("Certificate found at %s", cert_file);
ICECAST_LOG_INFO("Using ciphers %s", cipher_list);
return ctx;
} while (0);
ICECAST_LOG_INFO("Can not setup TLS.");
tls_ctx_unref(ctx);
return NULL;
}
void tls_ctx_ref(tls_ctx_t *ctx)
{
if (!ctx)
return;
ctx->refc++;
}
void tls_ctx_unref(tls_ctx_t *ctx)
{
if (!ctx)
return;
ctx->refc--;
if (ctx->refc)
return;
if (ctx->ctx)
SSL_CTX_free(ctx->ctx);
free(ctx);
}
tls_t *tls_new(tls_ctx_t *ctx)
{
tls_t *tls;
SSL *ssl;
if (!ctx)
return NULL;
ssl = SSL_new(ctx->ctx);
if (!ssl)
return NULL;
tls = calloc(1, sizeof(*tls));
if (!tls) {
SSL_free(ssl);
return NULL;
}
tls_ctx_ref(ctx);
tls->refc = 1;
tls->ssl = ssl;
tls->ctx = ctx;
return tls;
}
void tls_ref(tls_t *tls)
{
if (!tls)
return;
tls->refc++;
}
void tls_unref(tls_t *tls)
{
if (!tls)
return;
tls->refc--;
if (tls->refc)
return;
SSL_shutdown(tls->ssl);
SSL_free(tls->ssl);
if (tls->ctx)
tls_ctx_unref(tls->ctx);
free(tls);
}
void tls_set_incoming(tls_t *tls)
{
if (!tls)
return;
SSL_set_accept_state(tls->ssl);
}
void tls_set_socket(tls_t *tls, sock_t sock)
{
if (!tls)
return;
SSL_set_fd(tls->ssl, sock);
}
int tls_want_io(tls_t *tls)
{
int what;
if (!tls)
return -1;
what = SSL_want(tls->ssl);
switch (what) {
case SSL_WRITING:
case SSL_READING:
return 1;
break;
case SSL_NOTHING:
default:
return 0;
break;
}
}
int tls_got_shutdown(tls_t *tls)
{
if (!tls)
return -1;
if (SSL_get_shutdown(tls->ssl) & SSL_RECEIVED_SHUTDOWN) {
return 1;
} else {
return 0;
}
}
ssize_t tls_read(tls_t *tls, void *buffer, size_t len)
{
if (!tls)
return -1;
return SSL_read(tls->ssl, buffer, len);
}
ssize_t tls_write(tls_t *tls, const void *buffer, size_t len)
{
if (!tls)
return -1;
return SSL_write(tls->ssl, buffer, len);
}
#else
void tls_initialize(void)
{
}
void tls_shutdown(void)
{
}
tls_ctx_t *tls_ctx_new(const char *cert_file, const char *key_file, const char *cipher_list)
{
return NULL;
}
void tls_ctx_ref(tls_ctx_t *ctx)
{
}
void tls_ctx_unref(tls_ctx_t *ctx)
{
}
tls_t *tls_new(tls_ctx_t *ctx)
{
return NULL;
}
void tls_ref(tls_t *tls)
{
}
void tls_unref(tls_t *tls)
{
}
void tls_set_incoming(tls_t *tls)
{
}
void tls_set_socket(tls_t *tls, sock_t sock)
{
}
int tls_want_io(tls_t *tls)
{
return -1;
}
int tls_got_shutdown(tls_t *tls)
{
return -1;
}
ssize_t tls_read(tls_t *tls, void *buffer, size_t len)
{
return -1;
}
ssize_t tls_write(tls_t *tls, const void *buffer, size_t len)
{
return -1;
}
#endif
/* Icecast
*
* This program is distributed under the GNU General Public License, version 2.
* A copy of this license is included with this source.
*
* Copyright 2016, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
#ifndef __TLS_H__
#define __TLS_H__
#ifdef HAVE_OPENSSL
#include <openssl/ssl.h>
#include <openssl/err.h>
#endif
#include "common/net/sock.h"
/* Do we have TLS Support? */
#if defined(HAVE_OPENSSL)
#define ICECAST_CAP_TLS
#endif
typedef struct tls_ctx_tag tls_ctx_t;
typedef struct tls_tag tls_t;
/* Check for a specific implementation. Returns 0 if supported, 1 if unsupported and -1 on error. */
int tls_check_impl(const char *impl);
void tls_initialize(void);
void tls_shutdown(void);
tls_ctx_t *tls_ctx_new(const char *cert_file, const char *key_file, const char *cipher_list);
void tls_ctx_ref(tls_ctx_t *ctx);
void tls_ctx_unref(tls_ctx_t *ctx);
tls_t *tls_new(tls_ctx_t *ctx);
void tls_ref(tls_t *tls);
void tls_unref(tls_t *tls);
void tls_set_incoming(tls_t *tls);