1
0
mirror of https://gitlab.xiph.org/xiph/icecast-server.git synced 2025-02-02 15:07:36 -05:00

Merge branch 'ph3-update-TLS'

This commit is contained in:
Philipp Schafft 2017-07-23 13:22:21 +00:00
commit fa86f2de73
12 changed files with 607 additions and 122 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -59,6 +59,7 @@
#include "admin.h"
#include "auth.h"
#include "matchfile.h"
#include "tls.h"
#define CATMODULE "connection"
@ -97,10 +98,8 @@ static int _initialized = 0;
static volatile client_queue_t *_req_queue = NULL, **_req_queue_tail = &_req_queue;
static volatile client_queue_t *_con_queue = NULL, **_con_queue_tail = &_con_queue;
static int ssl_ok;
#ifdef HAVE_OPENSSL
static SSL_CTX *ssl_ctx;
#endif
static int tls_ok;
static tls_ctx_t *tls_ctx;
/* filtering client connection based on IP */
static matchfile_t *banned_ip, *allowed_ip;
@ -108,6 +107,7 @@ static matchfile_t *banned_ip, *allowed_ip;
rwlock_t _source_shutdown_rwlock;
static void _handle_connection(void);
static void get_tls_certificate(ice_config_t *config);
void connection_initialize(void)
{
@ -131,9 +131,7 @@ void connection_shutdown(void)
if (!_initialized)
return;
#ifdef HAVE_OPENSSL
SSL_CTX_free (ssl_ctx);
#endif
tls_ctx_unref(tls_ctx);
matchfile_release(banned_ip);
matchfile_release(allowed_ip);
@ -145,6 +143,11 @@ void connection_shutdown(void)
_initialized = 0;
}
void connection_reread_config(struct ice_config_tag *config)
{
get_tls_certificate(config);
}
static unsigned long _next_connection_id(void)
{
unsigned long id;
@ -157,80 +160,50 @@ static unsigned long _next_connection_id(void)
}
#ifdef HAVE_OPENSSL
static void get_ssl_certificate(ice_config_t *config)
#ifdef ICECAST_CAP_TLS
static void get_tls_certificate(ice_config_t *config)
{
SSL_METHOD *method;
long ssl_opts;
config->tls_ok = ssl_ok = 0;
const char *keyfile;
SSL_load_error_strings(); /* readable error messages */
SSL_library_init(); /* initialize library */
config->tls_ok = tls_ok = 0;
method = SSLv23_server_method();
ssl_ctx = SSL_CTX_new(method);
ssl_opts = SSL_CTX_get_options(ssl_ctx);
#ifdef SSL_OP_NO_COMPRESSION
SSL_CTX_set_options(ssl_ctx, ssl_opts|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_COMPRESSION);
#else
SSL_CTX_set_options(ssl_ctx, ssl_opts|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3);
#endif
keyfile = config->tls_context.key_file;
if (!keyfile)
keyfile = config->tls_context.cert_file;
do {
if (config->cert_file == NULL)
break;
if (SSL_CTX_use_certificate_chain_file (ssl_ctx, config->cert_file) <= 0) {
ICECAST_LOG_WARN("Invalid cert file %s", config->cert_file);
break;
}
if (SSL_CTX_use_PrivateKey_file (ssl_ctx, config->cert_file, SSL_FILETYPE_PEM) <= 0) {
ICECAST_LOG_WARN("Invalid private key file %s", config->cert_file);
break;
}
if (!SSL_CTX_check_private_key (ssl_ctx)) {
ICECAST_LOG_ERROR("Invalid %s - Private key does not match cert public key", config->cert_file);
break;
}
if (SSL_CTX_set_cipher_list(ssl_ctx, config->cipher_list) <= 0) {
ICECAST_LOG_WARN("Invalid cipher list: %s", config->cipher_list);
}
config->tls_ok = ssl_ok = 1;
ICECAST_LOG_INFO("Certificate found at %s", config->cert_file);
ICECAST_LOG_INFO("Using ciphers %s", config->cipher_list);
tls_ctx_unref(tls_ctx);
tls_ctx = tls_ctx_new(config->tls_context.cert_file, keyfile, config->tls_context.cipher_list);
if (!tls_ctx) {
ICECAST_LOG_INFO("No TLS capability on any configured ports");
return;
} while (0);
ICECAST_LOG_INFO("No TLS capability on any configured ports");
}
config->tls_ok = tls_ok = 1;
}
/* handlers for reading and writing a connection_t when there is ssl
/* handlers for reading and writing a connection_t when there is TLS
* configured on the listening port
*/
static int connection_read_ssl(connection_t *con, void *buf, size_t len)
static int connection_read_tls(connection_t *con, void *buf, size_t len)
{
int bytes = SSL_read(con->ssl, buf, len);
ssize_t bytes = tls_read(con->tls, buf, len);
if (bytes < 0) {
switch (SSL_get_error(con->ssl, bytes)) {
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
if (tls_want_io(con->tls) > 0)
return -1;
}
con->error = 1;
}
return bytes;
}
static int connection_send_ssl(connection_t *con, const void *buf, size_t len)
static int connection_send_tls(connection_t *con, const void *buf, size_t len)
{
int bytes = SSL_write (con->ssl, buf, len);
ssize_t bytes = tls_write(con->tls, buf, len);
if (bytes < 0) {
switch (SSL_get_error(con->ssl, bytes)){
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
return -1;
}
if (tls_want_io(con->tls) > 0)
return -1;
con->error = 1;
} else {
con->sent_bytes += bytes;
@ -239,14 +212,14 @@ static int connection_send_ssl(connection_t *con, const void *buf, size_t len)
}
#else
/* SSL not compiled in, so at least log it */
static void get_ssl_certificate(ice_config_t *config)
/* TLS not compiled in, so at least log it */
static void get_tls_certificate(ice_config_t *config)
{
ssl_ok = 0;
tls_ok = 0;
ICECAST_LOG_INFO("No TLS capability. "
"Rebuild Icecast with openSSL support to enable this.");
"Rebuild Icecast with OpenSSL support to enable this.");
}
#endif /* HAVE_OPENSSL */
#endif /* ICECAST_CAP_TLS */
/* handlers (default) for reading and writing a connection_t, no encrpytion
@ -284,6 +257,7 @@ connection_t *connection_create (sock_t sock, sock_t serversock, char *ip)
con->con_time = time(NULL);
con->id = _next_connection_id();
con->ip = ip;
con->tlsmode = ICECAST_TLSMODE_AUTO;
con->read = connection_read;
con->send = connection_send;
}
@ -291,19 +265,20 @@ connection_t *connection_create (sock_t sock, sock_t serversock, char *ip)
return con;
}
/* prepare connection for interacting over a SSL connection
/* prepare connection for interacting over a TLS connection
*/
void connection_uses_ssl(connection_t *con)
void connection_uses_tls(connection_t *con)
{
#ifdef HAVE_OPENSSL
if (con->ssl)
#ifdef ICECAST_CAP_TLS
if (con->tls)
return;
con->read = connection_read_ssl;
con->send = connection_send_ssl;
con->ssl = SSL_new(ssl_ctx);
SSL_set_accept_state(con->ssl);
SSL_set_fd(con->ssl, con->sock);
con->tlsmode = ICECAST_TLSMODE_RFC2818;
con->read = connection_read_tls;
con->send = connection_send_tls;
con->tls = tls_new(tls_ctx);
tls_set_incoming(con->tls);
tls_set_socket(con->tls, con->sock);
#endif
}
@ -462,8 +437,12 @@ static client_queue_t *_get_connection(void)
static void process_request_queue (void)
{
client_queue_t **node_ref = (client_queue_t **)&_req_queue;
ice_config_t *config = config_get_config();
int timeout = config->header_timeout;
ice_config_t *config;
int timeout;
char peak;
config = config_get_config();
timeout = config->header_timeout;
config_release_config();
while (*node_ref) {
@ -472,6 +451,14 @@ static void process_request_queue (void)
int len = PER_CLIENT_REFBUF_SIZE - 1 - node->offset;
char *buf = client->refbuf->data + node->offset;
if (client->con->tlsmode == ICECAST_TLSMODE_AUTO || client->con->tlsmode == ICECAST_TLSMODE_AUTO_NO_PLAIN) {
if (recv(client->con->sock, &peak, 1, MSG_PEEK) == 1) {
if (peak == 0x16) { /* TLS Record Protocol Content type 0x16 == Handshake */
connection_uses_tls(client->con);
}
}
}
if (len > 0) {
if (client->con->con_time + timeout <= time(NULL)) {
len = 0;
@ -568,8 +555,9 @@ static client_queue_t *create_client_node(client_t *client)
if (listener) {
if (listener->shoutcast_compat)
node->shoutcast = 1;
if (listener->ssl && ssl_ok)
connection_uses_ssl(client->con);
client->con->tlsmode = listener->tls;
if (listener->tls == ICECAST_TLSMODE_RFC2818 && tls_ok)
connection_uses_tls(client->con);
if (listener->shoutcast_mount)
node->shoutcast_mount = strdup(listener->shoutcast_mount);
}
@ -621,7 +609,7 @@ void connection_accept_loop(void)
int duration = 300;
config = config_get_config();
get_ssl_certificate(config);
get_tls_certificate(config);
config_release_config();
while (global.running == ICECAST_RUNNING) {
@ -1358,8 +1346,16 @@ static void _handle_connection(void)
upgrade = httpp_getvar(parser, "upgrade");
connection = httpp_getvar(parser, "connection");
if (upgrade && connection && strstr(upgrade, "TLS/1.0") != NULL && strcasecmp(connection, "upgrade") == 0) {
client_send_101(client, ICECAST_REUSE_UPGRADETLS);
if (upgrade && connection && strcasecmp(connection, "upgrade") == 0) {
if (client->con->tlsmode == ICECAST_TLSMODE_DISABLED || strstr(upgrade, "TLS/1.0") == NULL) {
client_send_error(client, 400, 1, "Can not upgrade protocol");
continue;
} else {
client_send_101(client, ICECAST_REUSE_UPGRADETLS);
continue;
}
} else if (client->con->tlsmode != ICECAST_TLSMODE_DISABLED && client->con->tlsmode != ICECAST_TLSMODE_AUTO && !client->con->tls) {
client_send_426(client, ICECAST_REUSE_UPGRADETLS);
continue;
}
@ -1495,8 +1491,6 @@ void connection_close(connection_t *con)
sock_close(con->sock);
if (con->ip)
free(con->ip);
#ifdef HAVE_OPENSSL
if (con->ssl) { SSL_shutdown(con->ssl); SSL_free(con->ssl); }
#endif
tls_unref(con->tls);
free(con);
}

View File

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

View File

@ -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();

View File

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

306
src/tls.c Normal file
View File

@ -0,0 +1,306 @@
/* 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

52
src/tls.h Normal file
View File

@ -0,0 +1,52 @@
/* 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);
void tls_set_socket(tls_t *tls, sock_t sock);
int tls_want_io(tls_t *tls);
int tls_got_shutdown(tls_t *tls);
ssize_t tls_read(tls_t *tls, void *buffer, size_t len);
ssize_t tls_write(tls_t *tls, const void *buffer, size_t len);
#endif

View File

@ -676,6 +676,7 @@ ssize_t util_http_build_header(char * out, size_t len, ssize_t offset,
ssize_t ret;
char * extra_headers;
const char *connection_header = "Close";
const char *upgrade_header = "";
if (!out)
return -1;
@ -686,6 +687,8 @@ ssize_t util_http_build_header(char * out, size_t len, ssize_t offset,
case ICECAST_REUSE_KEEPALIVE: connection_header = "Keep-Alive"; break;
case ICECAST_REUSE_UPGRADETLS: connection_header = "Upgrade"; break;
}
if (client->con->tlsmode != ICECAST_TLSMODE_DISABLED)
upgrade_header = "Upgrade: TLS/1.0\r\n";
}
if (offset == -1)
@ -758,7 +761,7 @@ ssize_t util_http_build_header(char * out, size_t len, ssize_t offset,
connection_header,
(client && client->admin_command == ADMIN_COMMAND_ERROR ?
"GET, SOURCE" : "GET"),
(config->tls_ok ? "Upgrade: TLS/1.0\r\n" : ""),
upgrade_header,
currenttime_buffer,
contenttype_buffer,
(status == 401 ? "WWW-Authenticate: Basic realm=\"Icecast2 Server\"\r\n" : ""),