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:
commit
fa86f2de73
@ -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;
|
||||
}
|
||||
|
141
src/cfgfile.c
141
src/cfgfile.c
@ -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;
|
||||
|
12
src/client.c
12
src/client.c
@ -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);
|
||||
}
|
||||
|
||||
|
160
src/connection.c
160
src/connection.c
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
306
src/tls.c
Normal file
306
src/tls.c
Normal 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
52
src/tls.h
Normal 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
|
@ -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" : ""),
|
||||
|
Loading…
x
Reference in New Issue
Block a user