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

Wow. Mega patch!

This patch *replaces* the authentication system completly.

What is new:
 - <authentication> in mount section is now a container object.
 - <authentication> in root and mount section may hold any number of <role>-Tags.
 - <role> tags:
   Those tags define a 'role' and it's ACL rules.
   A role is a instance of an authentication module (see below).
   <role> takes the following options. All but type are optional.
   - authentication related:
     - type: Type of the authentication module (values: anonymous, static, legacy-password, url or htpasswd;
             symbolic constants in auth.h)
     - name: Name for the role. For later matching. (values: any string; default: (none))
     - method: This rule is only active on the given list of HTTP methods.
               (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: *)
   - ACL related:
     - allow-method: Allowed HTTP methods.
       (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: get)
     - deny-method: Rejected HTTP methods.
       (list of enum values: methods as recognized by httpp/ (e.g: get,post); default: *)
     - allow-admin: Allowed admin commands. (list of enum values: admin command; default: buildm3u)
     - deny-admin: Rejected admin commands. (list of enum values: admin command; default: *)
     - allow-web: Allowed web pages. (values: empty or *; default: *)
     - deny-web: Rejected web pages. (values: empty or *; default: (empty))
     - connections-per-user: maximum number of simultaneous connections per role and username.
       This is only active on active sources.  (values: unlimited or number of connections; default: unlimited)
     - connection-duration: maximum time of a connection. This is only active on active sources.
       (values: unlimited or number of secounds; default: unlimited)
   <role> takes <option> child tags. <option> tags contain a name and a value option.
   Meaning of <option> tags is up to the authentication module.
 - <role>s are considered to build a stack. If a role returns with AUTH_NOMATCH the next one will be tried.
 - <role>s are tested in this order: mount specific, default mount specific, global, internal fallback.
   Internal fallback is set to allow web/ access via GET, POST and HEAD (only GET supported by this time)
   and rejects all other requests.
 - New authentication module: anonymous
   This module matches all requests. No options taken.
 - New authentication module: static
   This module matches with a static username and password.
   It takes two <option>s. One with name="username" and one with name="password" to set username and password.
   This replaces old style <*-username> and <*-password> tags.
 - New authentication module: legacy-password
   This module matches with a statich password.
   It takes one <option> with name="password" to set password.
   This replaces old ICE and ICY (shoutcast compat mode) authentication.
 - Parsing <authentication> in <mount> with a type set in a special way to allow 100% backward compatibility.
 - Parsing of <source-password>, <admin-password>, <admin-user>, <relay-password> and <relay-user> in global
   <authentication> for 100% backward compatibility.
 - <alias> is now proccessed very early. This enables them to be used for all kinds of requests.

To Do List & What does not yet work:
 - type="url" auth: mount_add and mount_remove.
   This should be replaced by an unique feature I would call '<event>'.
 - Admin commands manageauth and manageauth.xsl are disabled as they need more review:
   This code needs to be ported to support multiple <role>s per <mount>.
 - url authentication module can not yet return AUTH_NOMATCH.
   This needs to be reviewed and discussed on how to handle this case best way.
 - Default config files needs to be updated to reflect the changes.
   As this is quite some political act it should be done in dicussion with the whole team
   and permission of the release manager.
 - Docs need to be updated to reflect the changes.

How does it work:
 Code has been changed so that authentification is done early for all clients.
 This allows accessing the ACL data (client->acl) from nearly everywhere in the code.

 After accept() and initial client setup the request is parsed. In the next step
 all <alias>es are resolved. After this the client is passed for authentication.
 After authentication it is passed to the corresponding subsystem depending on kind of request.

 All authentication instances have a thread running for doing the authentication.
 This thread works on a queue of clients.

Hints for testers:
 - Test with default config.
 - Test with diffrent authentication modules in <mount>.
 - Test shoutcast compatibility mode.
 - Test with new style <authentication> and any amount of <role> (zero to quite some).
 - Test <alias> lookup on all kinds of objects.
 - Test source level credential login into the admin interface.
 - Test shoucast style meta data updates.
 - Test playlist generation.

Thank you for reading this long commit message. Have fun reading the full patch!

svn path=/icecast/trunk/icecast/; revision=19358
This commit is contained in:
Philipp Schafft 2014-11-28 23:46:08 +00:00
parent 637af17f82
commit a642cac542
22 changed files with 2074 additions and 1384 deletions

View File

@ -9,7 +9,7 @@ bin_PROGRAMS = icecast
noinst_HEADERS = admin.h cfgfile.h logging.h sighandler.h connection.h \
global.h util.h slave.h source.h stats.h refbuf.h client.h \
compat.h fserve.h xslt.h yp.h event.h md5.h \
auth.h auth_htpasswd.h auth_url.h \
acl.h auth.h auth_htpasswd.h auth_url.h auth_anonymous.h auth_static.h \
format.h format_ogg.h format_mp3.h format_ebml.h \
format_vorbis.h format_theora.h format_flac.h format_speex.h format_midi.h \
format_kate.h format_skeleton.h format_opus.h
@ -17,7 +17,7 @@ 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 \
xslt.c fserve.c event.c admin.c md5.c \
format.c format_ogg.c format_mp3.c format_midi.c format_flac.c format_ebml.c \
auth.c auth_htpasswd.c format_kate.c format_skeleton.c format_opus.c
acl.c auth.c auth_htpasswd.c auth_anonymous.c auth_static.c format_kate.c format_skeleton.c format_opus.c
EXTRA_icecast_SOURCES = yp.c \
auth_url.c \
format_vorbis.c format_theora.c format_speex.c
@ -36,4 +36,3 @@ debug:
profile:
$(MAKE) all CFLAGS="@PROFILE@"

297
src/acl.c Normal file
View File

@ -0,0 +1,297 @@
/* Icecast
*
* This program is distributed under the GNU General Public License, version 2.
* A copy of this license is included with this source.
*
* Copyright 2014, Philipp Schafft <lion@lion.leolix.org>
*/
/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
#include <stdlib.h>
#include "acl.h"
#include "admin.h"
#include <stdio.h>
#define MAX_ADMIN_COMMANDS 32
/* define internal structure */
struct acl_tag {
/* reference counter */
size_t refcount;
/* allowed methods */
acl_policy_t method[httpp_req_unknown+1];
/* admin/ interface */
struct {
int command;
acl_policy_t policy;
} admin_commands[MAX_ADMIN_COMMANDS];
size_t admin_commands_len;
acl_policy_t admin_command_policy;
/* web/ interface */
acl_policy_t web_policy;
/* mount specific functons */
time_t max_connection_duration;
size_t max_connections_per_user;
};
/* some string util functions */
static inline void __skip_spaces(const char **str) {
register const char * p;
for (p = *str; *p == ' '; p++);
*str = p;
}
int acl_set_ANY_str(acl_t * acl, acl_policy_t policy, const char * str, int (*callback)(acl_t *, acl_policy_t, const char *)) {
const char * end;
size_t len;
char buf[64];
int ret;
if (!acl || !str || !callback || (policy != ACL_POLICY_ALLOW && policy != ACL_POLICY_DENY))
return -1;
do {
__skip_spaces(&str);
end = strstr(str, ",");
if (end) {
len = end - str;
} else {
len = strlen(str);
}
if (len > (sizeof(buf) - 1))
return -1;
memcpy(buf, str, len);
buf[len] = 0;
ret = callback(acl, policy, buf);
if (ret)
return ret;
str += len + 1;
} while (end);
return 0;
}
/* basic functions to work with ACLs */
acl_t * acl_new(void) {
acl_t * ret = calloc(1, sizeof(*ret));
if (!ret)
return NULL;
ret->refcount = 1;
acl_set_method_str(ret, ACL_POLICY_DENY, "*");
acl_set_method_str(ret, ACL_POLICY_ALLOW, "get");
acl_set_admin_str(ret, ACL_POLICY_DENY, "*");
acl_set_admin_str(ret, ACL_POLICY_ALLOW, "buildm3u");
acl_set_web_policy(ret, ACL_POLICY_ALLOW);
acl_set_max_connection_duration(ret, -1);
acl_set_max_connections_per_user(ret, 0);
return ret;
}
acl_t * acl_new_from_xml_node(xmlNodePtr node) {
acl_t * ret;
char * tmp;
xmlAttrPtr prop;
if (!node)
return NULL;
ret = acl_new();
if (!ret)
return NULL;
prop = node->properties;
while (prop) {
tmp = (char*)xmlGetProp(node, prop->name);
if (tmp) {
if (strcmp((const char*)prop->name, "allow-method") == 0) {
acl_set_method_str(ret, ACL_POLICY_ALLOW, tmp);
} else if (strcmp((const char*)prop->name, "deny-method") == 0) {
acl_set_method_str(ret, ACL_POLICY_DENY, tmp);
} else if (strcmp((const char*)prop->name, "allow-admin") == 0) {
acl_set_admin_str(ret, ACL_POLICY_ALLOW, tmp);
} else if (strcmp((const char*)prop->name, "deny-admin") == 0) {
acl_set_admin_str(ret, ACL_POLICY_DENY, tmp);
} else if (strcmp((const char*)prop->name, "allow-web") == 0) {
if (strstr(tmp, "*"))
acl_set_web_policy(ret, ACL_POLICY_ALLOW);
} else if (strcmp((const char*)prop->name, "deny-web") == 0) {
if (strstr(tmp, "*"))
acl_set_web_policy(ret, ACL_POLICY_DENY);
} else if (strcmp((const char*)prop->name, "connections-per-user") == 0) {
if (strcmp(tmp, "*") == 0 || strcmp(tmp, "unlimited") == 0) {
acl_set_max_connections_per_user(ret, 0);
} else {
acl_set_max_connections_per_user(ret, atoi(tmp));
}
} else if (strcmp((const char*)prop->name, "connection-duration") == 0) {
if (strcmp(tmp, "*") == 0 || strcmp(tmp, "unlimited") == 0) {
acl_set_max_connection_duration(ret, 0);
} else {
acl_set_max_connection_duration(ret, atoi(tmp));
}
}
xmlFree(tmp);
}
prop = prop->next;
}
return ret;
}
void acl_addref(acl_t * acl) {
if (!acl)
return;
acl->refcount++;
}
void acl_release(acl_t * acl) {
if (!acl)
return;
acl->refcount--;
if (acl->refcount)
return;
free(acl);
}
/* HTTP Method specific functions */
int acl_set_method_str__callback(acl_t * acl, acl_policy_t policy, const char * str) {
httpp_request_type_e method;
size_t i;
if (strcmp(str, "*") == 0) {
for (i = 0; i < (sizeof(acl->method)/sizeof(*acl->method)); i++)
acl->method[i] = policy;
} else {
method = httpp_str_to_method(str);
if (method == httpp_req_unknown)
return -1;
acl->method[method] = policy;
}
return 0;
}
acl_policy_t acl_test_method(acl_t * acl, httpp_request_type_e method) {
if (!acl || method < httpp_req_none || method > httpp_req_unknown)
return ACL_POLICY_ERROR;
return acl->method[method];
}
/* admin/ interface specific functions */
int acl_set_admin_str__callbck(acl_t * acl, acl_policy_t policy, const char * str) {
size_t read_i, write_i;
int command = admin_get_command(str);
if (command == ADMIN_COMMAND_ERROR)
return -1;
if (command == ADMIN_COMMAND_ANY) {
acl->admin_command_policy = policy;
for (read_i = write_i = 0; read_i < acl->admin_commands_len; read_i++) {
if (acl->admin_commands[read_i].policy == policy)
continue;
acl->admin_commands[write_i] = acl->admin_commands[read_i];
write_i++; /* no need to check bounds here as this loop can only compress the array */
}
acl->admin_commands_len = write_i;
return 0;
}
if (acl->admin_commands_len == MAX_ADMIN_COMMANDS)
return -1;
acl->admin_commands[acl->admin_commands_len].command = command;
acl->admin_commands[acl->admin_commands_len].policy = policy;
acl->admin_commands_len++;
return 0;
}
acl_policy_t acl_test_admin(acl_t * acl, int command) {
size_t i;
if (!acl)
return ACL_POLICY_ERROR;
for (i = 0; i < acl->admin_commands_len; i++)
if (acl->admin_commands[i].command == command)
return acl->admin_commands[i].policy;
return acl->admin_command_policy;
}
/* web/ interface specific functions */
int acl_set_web_policy(acl_t * acl, acl_policy_t policy) {
if (!acl || (policy != ACL_POLICY_ALLOW && policy != ACL_POLICY_DENY))
return -1;
acl->web_policy = policy;
return 0;
}
acl_policy_t acl_test_web(acl_t * acl) {
if (!acl)
return ACL_POLICY_ERROR;
return acl->web_policy;
}
/* mount specific functons */
int acl_set_max_connection_duration(acl_t * acl, time_t duration) {
if (!acl)
return -1;
acl->max_connection_duration = duration;
return 0;
}
time_t acl_get_max_connection_duration(acl_t * acl) {
if (!acl)
return -1;
return acl->max_connection_duration;
}
int acl_set_max_connections_per_user(acl_t * acl, size_t limit) {
if (!acl)
return -1;
acl->max_connections_per_user = limit;
return 0;
}
ssize_t acl_get_max_connections_per_user(acl_t * acl) {
if (!acl)
return -1;
return acl->max_connections_per_user;
}

63
src/acl.h Normal file
View File

@ -0,0 +1,63 @@
/* Icecast
*
* This program is distributed under the GNU General Public License, version 2.
* A copy of this license is included with this source.
*
* Copyright 2014, Philipp Schafft <lion@lion.leolix.org>
*/
#ifndef __ACL_H__
#define __ACL_H__
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include "httpp/httpp.h"
typedef enum acl_policy_tag {
/* Error on function call */
ACL_POLICY_ERROR = -1,
/* Client is allowed to do operation, go ahead! */
ACL_POLICY_ALLOW = 0,
/* Client is not allowed to do so, send error! */
ACL_POLICY_DENY = 1
} acl_policy_t;
struct acl_tag;
typedef struct acl_tag acl_t;
/* basic functions to work with ACLs */
acl_t * acl_new(void);
acl_t * acl_new_from_xml_node(xmlNodePtr node);
void acl_addref(acl_t * acl);
void acl_release(acl_t * acl);
/* special functions */
int acl_set_ANY_str(acl_t * acl, acl_policy_t policy, const char * str, int (*callback)(acl_t *, acl_policy_t, const char *));
/* HTTP Method specific functions */
int acl_set_method_str__callback(acl_t * acl, acl_policy_t policy, const char * str);
#define acl_set_method_str(acl,policy,str) acl_set_ANY_str((acl), (policy), (str), acl_set_method_str__callback)
acl_policy_t acl_test_method(acl_t * acl, httpp_request_type_e method);
/* admin/ interface specific functions */
int acl_set_admin_str__callbck(acl_t * acl, acl_policy_t policy, const char * str);
#define acl_set_admin_str(acl,policy,str) acl_set_ANY_str((acl), (policy), (str), acl_set_admin_str__callbck)
acl_policy_t acl_test_admin(acl_t * acl, int command);
/* web/ interface specific functions */
int acl_set_web_policy(acl_t * acl, acl_policy_t policy);
acl_policy_t acl_test_web(acl_t * acl);
/* mount specific functons */
int acl_set_max_connection_duration(acl_t * acl, time_t duration);
time_t acl_get_max_connection_duration(acl_t * acl);
int acl_set_max_connections_per_user(acl_t * acl, size_t limit);
ssize_t acl_get_max_connections_per_user(acl_t * acl);
#endif

View File

@ -8,6 +8,7 @@
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@xiph.org>
* and others (see AUTHORS for details).
* Copyright 2012-2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
#ifdef HAVE_CONFIG_H
@ -116,70 +117,73 @@
#define DEFAULT_TRANSFORMED_REQUEST ""
#define BUILDM3U_RAW_REQUEST "buildm3u"
int admin_get_command(const char *command)
{
if(!strcmp(command, FALLBACK_RAW_REQUEST))
return COMMAND_RAW_FALLBACK;
else if(!strcmp(command, FALLBACK_TRANSFORMED_REQUEST))
return COMMAND_TRANSFORMED_FALLBACK;
else if(!strcmp(command, METADATA_RAW_REQUEST))
return COMMAND_RAW_METADATA_UPDATE;
else if(!strcmp(command, METADATA_TRANSFORMED_REQUEST))
return COMMAND_TRANSFORMED_METADATA_UPDATE;
else if(!strcmp(command, SHOUTCAST_METADATA_REQUEST))
return COMMAND_SHOUTCAST_METADATA_UPDATE;
else if(!strcmp(command, LISTCLIENTS_RAW_REQUEST))
return COMMAND_RAW_SHOW_LISTENERS;
else if(!strcmp(command, LISTCLIENTS_TRANSFORMED_REQUEST))
return COMMAND_TRANSFORMED_SHOW_LISTENERS;
else if(!strcmp(command, STATS_RAW_REQUEST))
return COMMAND_RAW_STATS;
else if(!strcmp(command, STATS_TRANSFORMED_REQUEST))
return COMMAND_TRANSFORMED_STATS;
else if(!strcmp(command, "stats.xml")) /* The old way */
return COMMAND_RAW_STATS;
else if(!strcmp(command, QUEUE_RELOAD_RAW_REQUEST))
return COMMAND_RAW_QUEUE_RELOAD;
else if(!strcmp(command, QUEUE_RELOAD_TRANSFORMED_REQUEST))
return COMMAND_TRANSFORMED_QUEUE_RELOAD;
else if(!strcmp(command, LISTMOUNTS_RAW_REQUEST))
return COMMAND_RAW_LIST_MOUNTS;
else if(!strcmp(command, LISTMOUNTS_TRANSFORMED_REQUEST))
return COMMAND_TRANSFORMED_LIST_MOUNTS;
else if(!strcmp(command, STREAMLIST_RAW_REQUEST))
return COMMAND_RAW_LISTSTREAM;
else if(!strcmp(command, STREAMLIST_PLAINTEXT_REQUEST))
return COMMAND_PLAINTEXT_LISTSTREAM;
else if(!strcmp(command, MOVECLIENTS_RAW_REQUEST))
return COMMAND_RAW_MOVE_CLIENTS;
else if(!strcmp(command, MOVECLIENTS_TRANSFORMED_REQUEST))
return COMMAND_TRANSFORMED_MOVE_CLIENTS;
else if(!strcmp(command, KILLCLIENT_RAW_REQUEST))
return COMMAND_RAW_KILL_CLIENT;
else if(!strcmp(command, KILLCLIENT_TRANSFORMED_REQUEST))
return COMMAND_TRANSFORMED_KILL_CLIENT;
else if(!strcmp(command, KILLSOURCE_RAW_REQUEST))
return COMMAND_RAW_KILL_SOURCE;
else if(!strcmp(command, KILLSOURCE_TRANSFORMED_REQUEST))
return COMMAND_TRANSFORMED_KILL_SOURCE;
else if(!strcmp(command, MANAGEAUTH_RAW_REQUEST))
return COMMAND_RAW_MANAGEAUTH;
else if(!strcmp(command, MANAGEAUTH_TRANSFORMED_REQUEST))
return COMMAND_TRANSFORMED_MANAGEAUTH;
else if(!strcmp(command, UPDATEMETADATA_RAW_REQUEST))
return COMMAND_RAW_UPDATEMETADATA;
else if(!strcmp(command, UPDATEMETADATA_TRANSFORMED_REQUEST))
return COMMAND_TRANSFORMED_UPDATEMETADATA;
else if(!strcmp(command, BUILDM3U_RAW_REQUEST))
return COMMAND_BUILDM3U;
else if(!strcmp(command, DEFAULT_TRANSFORMED_REQUEST))
return COMMAND_TRANSFORMED_STATS;
else if(!strcmp(command, DEFAULT_RAW_REQUEST))
return COMMAND_TRANSFORMED_STATS;
else if(!strcmp(command, "*")) /* for ACL framework */
return COMMAND_ANY;
else
return COMMAND_ERROR;
typedef struct admin_command_tag {
const int id;
const char *name;
const int type;
const int format;
} admin_command_t;
/*
COMMAND_TRANSFORMED_METADATA_UPDATE -> METADATA_TRANSFORMED_REQUEST
COMMAND_TRANSFORMED_UPDATEMETADATA -> UPDATEMETADATA_TRANSFORMED_REQUEST
*/
static const admin_command_t commands[] = {
{COMMAND_RAW_FALLBACK, FALLBACK_RAW_REQUEST, ADMINTYPE_MOUNT, RAW},
{COMMAND_TRANSFORMED_FALLBACK, FALLBACK_TRANSFORMED_REQUEST, ADMINTYPE_MOUNT, TRANSFORMED},
{COMMAND_RAW_METADATA_UPDATE, METADATA_RAW_REQUEST, ADMINTYPE_MOUNT, RAW},
{COMMAND_SHOUTCAST_METADATA_UPDATE, SHOUTCAST_METADATA_REQUEST, ADMINTYPE_MOUNT, TRANSFORMED},
{COMMAND_TRANSFORMED_METADATA_UPDATE, METADATA_TRANSFORMED_REQUEST, ADMINTYPE_MOUNT, TRANSFORMED},
{COMMAND_RAW_SHOW_LISTENERS, LISTCLIENTS_RAW_REQUEST, ADMINTYPE_MOUNT, RAW},
{COMMAND_TRANSFORMED_SHOW_LISTENERS, LISTCLIENTS_TRANSFORMED_REQUEST, ADMINTYPE_MOUNT, TRANSFORMED},
{COMMAND_RAW_STATS, STATS_RAW_REQUEST, ADMINTYPE_HYBRID, RAW},
{COMMAND_TRANSFORMED_STATS, STATS_TRANSFORMED_REQUEST, ADMINTYPE_HYBRID, TRANSFORMED},
{COMMAND_RAW_STATS, "stats.xml", ADMINTYPE_HYBRID, RAW}, /* The old way */
{COMMAND_RAW_QUEUE_RELOAD, QUEUE_RELOAD_RAW_REQUEST, ADMINTYPE_GENERAL, RAW},
{COMMAND_TRANSFORMED_QUEUE_RELOAD, QUEUE_RELOAD_TRANSFORMED_REQUEST, ADMINTYPE_GENERAL, TRANSFORMED},
{COMMAND_RAW_LIST_MOUNTS, LISTMOUNTS_RAW_REQUEST, ADMINTYPE_GENERAL, RAW},
{COMMAND_TRANSFORMED_LIST_MOUNTS, LISTMOUNTS_TRANSFORMED_REQUEST, ADMINTYPE_GENERAL, TRANSFORMED},
{COMMAND_RAW_LISTSTREAM, STREAMLIST_RAW_REQUEST, ADMINTYPE_GENERAL, RAW},
{COMMAND_PLAINTEXT_LISTSTREAM, STREAMLIST_PLAINTEXT_REQUEST, ADMINTYPE_GENERAL, PLAINTEXT},
{COMMAND_TRANSFORMED_LISTSTREAM, STREAMLIST_TRANSFORMED_REQUEST, ADMINTYPE_GENERAL, TRANSFORMED},
{COMMAND_RAW_MOVE_CLIENTS, MOVECLIENTS_RAW_REQUEST, ADMINTYPE_MOUNT, RAW},
{COMMAND_TRANSFORMED_MOVE_CLIENTS, MOVECLIENTS_TRANSFORMED_REQUEST, ADMINTYPE_HYBRID, TRANSFORMED},
{COMMAND_RAW_KILL_CLIENT, KILLCLIENT_RAW_REQUEST, ADMINTYPE_MOUNT, RAW},
{COMMAND_TRANSFORMED_KILL_CLIENT, KILLCLIENT_TRANSFORMED_REQUEST, ADMINTYPE_MOUNT, TRANSFORMED},
{COMMAND_RAW_KILL_SOURCE, KILLSOURCE_RAW_REQUEST, ADMINTYPE_MOUNT, RAW},
{COMMAND_TRANSFORMED_KILL_SOURCE, KILLSOURCE_TRANSFORMED_REQUEST, ADMINTYPE_MOUNT, TRANSFORMED},
{COMMAND_RAW_MANAGEAUTH, MANAGEAUTH_RAW_REQUEST, ADMINTYPE_MOUNT, RAW},
{COMMAND_TRANSFORMED_MANAGEAUTH, MANAGEAUTH_TRANSFORMED_REQUEST, ADMINTYPE_MOUNT, TRANSFORMED},
{COMMAND_RAW_UPDATEMETADATA, UPDATEMETADATA_RAW_REQUEST, ADMINTYPE_MOUNT, RAW},
{COMMAND_TRANSFORMED_UPDATEMETADATA, UPDATEMETADATA_TRANSFORMED_REQUEST, ADMINTYPE_MOUNT, TRANSFORMED},
{COMMAND_BUILDM3U, BUILDM3U_RAW_REQUEST, ADMINTYPE_MOUNT, RAW},
{COMMAND_TRANSFORMED_STATS, DEFAULT_TRANSFORMED_REQUEST, ADMINTYPE_HYBRID, TRANSFORMED},
{COMMAND_TRANSFORMED_STATS, DEFAULT_RAW_REQUEST, ADMINTYPE_HYBRID, TRANSFORMED},
{COMMAND_ANY, "*", ADMINTYPE_GENERAL, TRANSFORMED} /* for ACL framework */
};
int admin_get_command(const char *command) {
size_t i;
for (i = 0; i < (sizeof(commands)/sizeof(*commands)); i++)
if (strcmp(commands[i].name, command) == 0)
return commands[i].id;
return COMMAND_ERROR;
}
int admin_get_command_type(int command) {
size_t i;
if (command == ADMIN_COMMAND_ERROR || command == COMMAND_ANY)
return ADMINTYPE_ERROR;
for (i = 0; i < (sizeof(commands)/sizeof(*commands)); i++)
if (commands[i].id == command)
return commands[i].type;
return ADMINTYPE_ERROR;
}
static void command_fallback(client_t *client, source_t *source, int response);
@ -201,9 +205,8 @@ static void command_kill_source(client_t *client, source_t *source,
int response);
static void command_updatemetadata(client_t *client, source_t *source,
int response);
static void admin_handle_mount_request(client_t *client, source_t *source,
int command);
static void admin_handle_general_request(client_t *client, int command);
static void admin_handle_mount_request(client_t *client, source_t *source);
static void admin_handle_general_request(client_t *client);
/* build an XML doc containing information about currently running sources.
* If a mountpoint is passed then that source will not be added to the XML
@ -238,6 +241,7 @@ xmlDocPtr admin_build_sourcelist (const char *mount)
{
ice_config_t *config;
mount_proxy *mountinfo;
acl_t *acl = NULL;
srcnode = xmlNewChild(xmlnode, NULL, XMLSTR("source"), NULL);
xmlSetProp(srcnode, XMLSTR("mount"), XMLSTR(source->mount));
@ -250,11 +254,14 @@ xmlDocPtr admin_build_sourcelist (const char *mount)
config = config_get_config();
mountinfo = config_find_mount (config, source->mount, MOUNT_TYPE_NORMAL);
if (mountinfo && mountinfo->auth)
{
xmlNewChild(srcnode, NULL, XMLSTR("authenticator"),
XMLSTR(mountinfo->auth->type));
if (mountinfo)
acl = auth_stack_get_anonymous_acl(mountinfo->authstack);
if (!acl)
auth_stack_get_anonymous_acl(config->authstack);
if (acl && acl_test_web(acl) == ACL_POLICY_DENY) {
xmlNewChild(srcnode, NULL, XMLSTR("authenticator"), XMLSTR("(dummy)"));
}
acl_release(acl);
config_release_config();
if (source->running)
@ -359,7 +366,6 @@ void admin_send_response (xmlDocPtr doc, client_t *client,
void admin_handle_request(client_t *client, const char *uri)
{
const char *mount, *command_string;
int command;
ICECAST_LOG_DEBUG("Admin request (%s)", uri);
if (!((strcmp(uri, "/admin.cgi") == 0) ||
@ -377,40 +383,23 @@ void admin_handle_request(client_t *client, const char *uri)
}
ICECAST_LOG_DEBUG("Got command (%s)", command_string);
command = admin_get_command(command_string);
if(command <= 0) {
if (client->admin_command <= 0) {
ICECAST_LOG_ERROR("Error parsing command string or unrecognised command: %s",
command_string);
client_send_error(client, 400, 0, "Unrecognised command");
return;
}
if (command == COMMAND_SHOUTCAST_METADATA_UPDATE) {
ice_config_t *config;
const char *sc_mount;
const char *pass = httpp_get_query_param (client->parser, "pass");
listener_t *listener;
if (pass == NULL)
{
client_send_error(client, 400, 0, "missing pass parameter");
if (acl_test_admin(client->acl, client->admin_command) != ACL_POLICY_ALLOW) {
if (client->admin_command == COMMAND_RAW_METADATA_UPDATE &&
(acl_test_method(client->acl, httpp_req_source) == ACL_POLICY_ALLOW ||
acl_test_method(client->acl, httpp_req_put) == ACL_POLICY_ALLOW)) {
ICECAST_LOG_DEBUG("Granted right to call COMMAND_RAW_METADATA_UPDATE to client because it is allowed to do SOURCE or PUT.");
} else {
client_send_error(client, 401, 1, "You need to authenticate\r\n");
return;
}
global_lock();
config = config_get_config ();
sc_mount = config->shoutcast_mount;
listener = config_get_listen_sock (config, client->con);
if (listener && listener->shoutcast_mount)
sc_mount = listener->shoutcast_mount;
httpp_set_query_param (client->parser, "mount", sc_mount);
httpp_setvar (client->parser, HTTPP_VAR_PROTOCOL, "ICY");
httpp_setvar (client->parser, HTTPP_VAR_ICYPASSWORD, pass);
config_release_config ();
global_unlock();
}
mount = httpp_get_query_param(client->parser, "mount");
@ -419,28 +408,12 @@ void admin_handle_request(client_t *client, const char *uri)
source_t *source;
/* this request does not require auth but can apply to files on webroot */
if (command == COMMAND_BUILDM3U)
{
command_buildm3u (client, mount);
if (client->admin_command == COMMAND_BUILDM3U) {
command_buildm3u(client, mount);
return;
}
/* This is a mount request, handle it as such */
if (client->authenticated == 0 && !connection_check_admin_pass(client->parser))
{
switch (client_check_source_auth (client, mount))
{
case 0:
break;
default:
ICECAST_LOG_INFO("Bad or missing password on mount modification admin "
"request (command: %s)", command_string);
client_send_error(client, 401, 1, "You need to authenticate\r\n");
/* fall through */
case 1:
return;
}
}
/* This is a mount request, handle it as such */
avl_tree_rlock(global.source_tree);
source = source_find_mount_raw(mount);
@ -461,7 +434,7 @@ void admin_handle_request(client_t *client, const char *uri)
client_send_error(client, 400, 0, "Source is not available");
return;
}
if (command == COMMAND_SHOUTCAST_METADATA_UPDATE &&
if (client->admin_command == COMMAND_SHOUTCAST_METADATA_UPDATE &&
source->shoutcast_compat == 0)
{
avl_tree_unlock (global.source_tree);
@ -472,39 +445,18 @@ void admin_handle_request(client_t *client, const char *uri)
}
ICECAST_LOG_INFO("Received admin command %s on mount \"%s\"",
command_string, mount);
admin_handle_mount_request(client, source, command);
admin_handle_mount_request(client, source);
avl_tree_unlock(global.source_tree);
}
}
else {
if (command == COMMAND_PLAINTEXT_LISTSTREAM) {
/* this request is used by a slave relay to retrieve
mounts from the master, so handle this request
validating against the relay password */
if(!connection_check_relay_pass(client->parser)) {
ICECAST_LOG_INFO("Bad or missing password on admin command "
"request (command: %s)", command_string);
client_send_error(client, 401, 1, "You need to authenticate\r\n");
return;
}
}
else {
if(!connection_check_admin_pass(client->parser)) {
ICECAST_LOG_INFO("Bad or missing password on admin command "
"request (command: %s)", command_string);
client_send_error(client, 401, 1, "You need to authenticate\r\n");
return;
}
}
admin_handle_general_request(client, command);
admin_handle_general_request(client);
}
}
static void admin_handle_general_request(client_t *client, int command)
static void admin_handle_general_request(client_t *client)
{
switch(command) {
switch(client->admin_command) {
case COMMAND_RAW_STATS:
command_stats(client, NULL, RAW);
break;
@ -542,10 +494,8 @@ static void admin_handle_general_request(client_t *client, int command)
}
}
static void admin_handle_mount_request(client_t *client, source_t *source,
int command)
{
switch(command) {
static void admin_handle_mount_request(client_t *client, source_t *source) {
switch(client->admin_command) {
case COMMAND_RAW_STATS:
command_stats(client, source->mount, RAW);
break;
@ -836,14 +786,22 @@ static void command_manageauth(client_t *client, source_t *source,
int ret = AUTH_OK;
ice_config_t *config = config_get_config ();
mount_proxy *mountinfo = config_find_mount (config, source->mount, MOUNT_TYPE_NORMAL);
auth_t *auth;
do
{
#if 0
if (mountinfo == NULL || mountinfo->auth == NULL)
{
ICECAST_LOG_WARN("manage auth request for %s but no facility available", source->mount);
break;
}
auth = mountinfo->auth;
#else
ICECAST_LOG_WARN("manage auth request for %s but no facility available", source->mount);
break;
#endif
COMMAND_OPTIONAL(client, "action", action);
COMMAND_OPTIONAL (client, "username", username);
@ -860,7 +818,7 @@ static void command_manageauth(client_t *client, source_t *source,
ICECAST_LOG_WARN("manage auth request add for %s but no user/pass", source->mount);
break;
}
ret = mountinfo->auth->adduser(mountinfo->auth, username, password);
ret = auth->adduser(auth, username, password);
if (ret == AUTH_FAILED) {
message = strdup("User add failed - check the icecast error log");
}
@ -878,7 +836,7 @@ static void command_manageauth(client_t *client, source_t *source,
ICECAST_LOG_WARN("manage auth request delete for %s but no username", source->mount);
break;
}
ret = mountinfo->auth->deleteuser(mountinfo->auth, username);
ret = auth->deleteuser(auth, username);
if (ret == AUTH_FAILED) {
message = strdup("User delete failed - check the icecast error log");
}
@ -899,8 +857,8 @@ static void command_manageauth(client_t *client, source_t *source,
xmlDocSetRootElement(doc, node);
if (mountinfo && mountinfo->auth && mountinfo->auth->listuser)
mountinfo->auth->listuser (mountinfo->auth, srcnode);
if (auth && auth->listuser)
auth->listuser(auth, srcnode);
config_release_config ();
@ -1011,6 +969,10 @@ static void command_metadata(client_t *client, source_t *source,
ICECAST_LOG_DEBUG("Got metadata update request");
if (source->parser->req_type == httpp_req_put) {
ICECAST_LOG_ERROR("Got legacy SOURCE-style metadata update command on source connected with PUT at mountpoint %s", source->mount);
}
COMMAND_REQUIRE(client, "mode", action);
COMMAND_OPTIONAL(client, "song", song);
COMMAND_OPTIONAL(client, "title", title);
@ -1029,7 +991,7 @@ static void command_metadata(client_t *client, source_t *source,
plugin = source->format;
if (source->client && strcmp (client->con->ip, source->client->con->ip) != 0)
if (response == RAW && connection_check_admin_pass (client->parser) == 0)
if (response == RAW && acl_test_admin(client->acl, COMMAND_RAW_METADATA_UPDATE) != ACL_POLICY_ALLOW)
same_ip = 0;
if (same_ip && plugin && plugin->set_tag)
@ -1078,6 +1040,10 @@ static void command_shoutcast_metadata(client_t *client, source_t *source)
ICECAST_LOG_DEBUG("Got shoutcast metadata update request");
if (source->parser->req_type == httpp_req_put) {
ICECAST_LOG_ERROR("Got legacy shoutcast-style metadata update command on source connected with PUT at mountpoint %s", source->mount);
}
COMMAND_REQUIRE(client, "mode", action);
COMMAND_REQUIRE(client, "song", value);
@ -1087,7 +1053,7 @@ static void command_shoutcast_metadata(client_t *client, source_t *source)
return;
}
if (source->client && strcmp (client->con->ip, source->client->con->ip) != 0)
if (connection_check_admin_pass (client->parser) == 0)
if (acl_test_admin(client->acl, COMMAND_RAW_METADATA_UPDATE) != ACL_POLICY_ALLOW)
same_ip = 0;
if (same_ip && source->format && source->format->set_tag)

View File

@ -20,6 +20,13 @@
#include "client.h"
#include "source.h"
/* types */
#define ADMINTYPE_ERROR (-1)
#define ADMINTYPE_GENERAL 1
#define ADMINTYPE_MOUNT 2
#define ADMINTYPE_HYBRID (ADMINTYPE_GENERAL|ADMINTYPE_MOUNT)
/* formats */
#define RAW 1
#define TRANSFORMED 2
#define PLAINTEXT 3
@ -35,5 +42,6 @@ void admin_send_response(xmlDocPtr doc, client_t *client,
void admin_add_listeners_to_mount(source_t *source, xmlNodePtr parent);
int admin_get_command(const char *command);
int admin_get_command_type(int command);
#endif /* __ADMIN_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -8,6 +8,7 @@
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@xiph.org>
* and others (see AUTHORS for details).
* Copyright 2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
#ifndef __AUTH_H__
@ -27,6 +28,13 @@ struct auth_tag;
#include "client.h"
#include "thread/thread.h"
/* implemented */
#define AUTH_TYPE_ANONYMOUS "anonymous"
#define AUTH_TYPE_STATIC "static"
#define AUTH_TYPE_LEGACY_PASSWORD "legacy-password"
#define AUTH_TYPE_URL "url"
#define AUTH_TYPE_HTPASSWD "htpasswd"
typedef enum
{
/* XXX: ??? */
@ -35,6 +43,8 @@ typedef enum
AUTH_OK,
/* user authed failed */
AUTH_FAILED,
/* session got terminated */
AUTH_RELEASED,
/* XXX: ??? */
AUTH_FORBIDDEN,
/* No match for given username or other identifier found */
@ -47,9 +57,11 @@ typedef enum
typedef struct auth_client_tag
{
char *mount;
client_t *client;
void (*process)(struct auth_tag *auth, struct auth_client_tag *auth_user);
client_t *client;
auth_result (*process)(struct auth_tag *auth, struct auth_client_tag *auth_user);
void (*on_no_match)(client_t *client, void (*on_result)(client_t *client, void *userdata, auth_result result), void *userdata);
void (*on_result)(client_t *client, void *userdata, auth_result result);
void *userdata;
struct auth_client_tag *next;
} auth_client;
@ -58,18 +70,12 @@ typedef struct auth_tag
{
char *mount;
/* filters */
int method[httpp_req_unknown+1];
/* Authenticate using the given username and password */
auth_result (*authenticate)(auth_client *aclient);
auth_result (*release_listener)(auth_client *auth_user);
/* auth handler for authenicating a connecting source client */
void (*stream_auth)(auth_client *auth_user);
/* auth handler for source startup, no client passed as it may disappear */
void (*stream_start)(auth_client *auth_user);
/* auth handler for source exit, no client passed as it may disappear */
void (*stream_end)(auth_client *auth_user);
auth_result (*authenticate_client)(auth_client *aclient);
auth_result (*release_client)(auth_client *auth_user);
/* auth state-specific free call */
void (*free)(struct auth_tag *self);
@ -80,8 +86,7 @@ typedef struct auth_tag
mutex_t lock;
int running;
int refcount;
int allow_duplicate_users;
size_t refcount;
thread_type *thread;
@ -91,30 +96,33 @@ typedef struct auth_tag
void *state;
char *type;
char *unique_tag;
/* acl to set on succsessful auth */
acl_t *acl;
/* role name for later matching, may be NULL if no role name was given in config */
char *role;
} auth_t;
void auth_add_listener (const char *mount, client_t *client);
int auth_release_listener (client_t *client);
typedef struct auth_stack_tag auth_stack_t;
void auth_initialise (void);
void auth_shutdown (void);
auth_t *auth_get_authenticator (xmlNodePtr node);
void auth_release (auth_t *authenticator);
void auth_addref (auth_t *authenticator);
/* call to trigger an event when a stream starts */
void auth_stream_start (struct _mount_proxy *mountinfo, const char *mount);
int auth_release_client(client_t *client);
/* call to trigger an event when a stream ends */
void auth_stream_end (struct _mount_proxy *mountinfo, const char *mount);
void auth_stack_add_client(auth_stack_t *stack, client_t *client, void (*on_result)(client_t *client, void *userdata, auth_result result), void *userdata);
/* call to trigger an event to authenticate a source client */
int auth_stream_authenticate (client_t *client, const char *mount, struct _mount_proxy *mountinfo);
/* called from auth thread, after the client has successfully authenticated
* and requires adding to source or fserve. */
int auth_postprocess_listener (auth_client *auth_user);
void auth_stack_release(auth_stack_t *stack);
void auth_stack_addref(auth_stack_t *stack);
int auth_stack_next(auth_stack_t **stack); /* returns -1 on error, 0 on success, +1 if no next element is present */
int auth_stack_push(auth_stack_t **stack, auth_t *auth);
int auth_stack_append(auth_stack_t *stack, auth_stack_t *tail);
auth_t *auth_stack_get(auth_stack_t *stack);
acl_t *auth_stack_get_anonymous_acl(auth_stack_t *stack);
#endif

30
src/auth_anonymous.c Normal file
View File

@ -0,0 +1,30 @@
/* Icecast
*
* This program is distributed under the GNU General Public License, version 2.
* A copy of this license is included with this source.
*
* Copyright 2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
/**
* Client authentication functions
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "auth.h"
#include "client.h"
#include "logging.h"
#define CATMODULE "auth_anonymous"
static auth_result anonymous_auth (auth_client *auth_user) {
return AUTH_OK;
}
int auth_get_anonymous_auth (auth_t *authenticator, config_options_t *options) {
authenticator->authenticate_client = anonymous_auth;
return 0;
}

18
src/auth_anonymous.h Normal file
View File

@ -0,0 +1,18 @@
/* Icecast
*
* This program is distributed under the GNU General Public License, version 2.
* A copy of this license is included with this source.
*
* Copyright 2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
#ifndef __AUTH_ANONYMOUS_H__
#define __AUTH_ANONYMOUS_H__
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
int auth_get_anonymous_auth (auth_t *auth, config_options_t *options);
#endif

View File

@ -8,6 +8,7 @@
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@xiph.org>
* and others (see AUTHORS for details).
* Copyright 2012-2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
/**
@ -184,13 +185,12 @@ static auth_result htpasswd_auth (auth_client *auth_user)
htpasswd_user entry;
void *result;
if (client->username == NULL || client->password == NULL)
return AUTH_FAILED;
if (!client->username || !client->password)
return AUTH_NOMATCH;
if (htpasswd->filename == NULL)
{
if (!htpasswd->filename) {
ICECAST_LOG_ERROR("No filename given in options for authenticator.");
return AUTH_FAILED;
return AUTH_NOMATCH;
}
htpasswd_recheckfile (htpasswd);
@ -214,15 +214,14 @@ static auth_result htpasswd_auth (auth_client *auth_user)
}
ICECAST_LOG_DEBUG("no such username: %s", client->username);
thread_rwlock_unlock (&htpasswd->file_rwlock);
return AUTH_FAILED;
return AUTH_NOMATCH;
}
int auth_get_htpasswd_auth (auth_t *authenticator, config_options_t *options)
{
htpasswd_auth_state *state;
authenticator->authenticate = htpasswd_auth;
authenticator->authenticate_client = htpasswd_auth;
authenticator->free = htpasswd_clear;
authenticator->adduser = htpasswd_adduser;
authenticator->deleteuser = htpasswd_deleteuser;
@ -408,4 +407,3 @@ static auth_result htpasswd_userlist(auth_t *auth, xmlNodePtr srcnode)
return AUTH_OK;
}

102
src/auth_static.c Normal file
View File

@ -0,0 +1,102 @@
/* Icecast
*
* This program is distributed under the GNU General Public License, version 2.
* A copy of this license is included with this source.
*
* Copyright 2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
/**
* Client authentication functions
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "auth.h"
#include "client.h"
#include "logging.h"
#define CATMODULE "auth_static"
typedef struct auth_static {
char *username;
char *password;
} auth_static_t;
static auth_result static_auth (auth_client *auth_user) {
client_t *client = auth_user->client;
auth_t *auth = client->auth;
auth_static_t *auth_info = auth->state;
if (auth_info->username) {
if (!client->username)
return AUTH_NOMATCH;
if (strcmp(auth_info->username, client->username) != 0)
return AUTH_NOMATCH;
}
if (!client->password)
return AUTH_NOMATCH;
if (strcmp(auth_info->password, client->password) == 0)
return AUTH_OK;
return AUTH_FAILED;
}
static void clear_auth (auth_t *auth) {
auth_static_t *auth_info = auth->state;
if (auth_info->username) free(auth_info->username);
if (auth_info->password) free(auth_info->password);
free(auth_info);
auth->state = NULL;
}
int auth_get_static_auth (auth_t *authenticator, config_options_t *options) {
auth_static_t *auth_info;
int need_user;
if (strcmp(authenticator->type, AUTH_TYPE_STATIC) == 0) {
need_user = 1;
} else if (strcmp(authenticator->type, AUTH_TYPE_LEGACY_PASSWORD) == 0) {
need_user = 0;
} else {
ICECAST_LOG_ERROR("Type is not known.");
return -1;
}
auth_info = calloc(1, sizeof(auth_static_t));
if (!auth_info)
return -1;
authenticator->authenticate_client = static_auth;
authenticator->free = clear_auth;
authenticator->state = auth_info;
while (options) {
if (strcmp(options->name, "username") == 0) {
if (auth_info->username) free(auth_info->username);
auth_info->username = strdup(options->value);
} else if (strcmp(options->name, "password") == 0) {
if (auth_info->password) free(auth_info->password);
auth_info->password = strdup(options->value);
} else {
ICECAST_LOG_ERROR("Unknown option: %s", options->name);
}
options = options->next;
}
if (need_user && !auth_info->username) {
ICECAST_LOG_ERROR("No Username given but needed.");
clear_auth(authenticator);
return -1;
} else if (!auth_info->password) {
ICECAST_LOG_ERROR("No password given but needed.");
clear_auth(authenticator);
return -1;
}
return 0;
}

18
src/auth_static.h Normal file
View File

@ -0,0 +1,18 @@
/* Icecast
*
* This program is distributed under the GNU General Public License, version 2.
* A copy of this license is included with this source.
*
* Copyright 2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
#ifndef __AUTH_STATIC_H__
#define __AUTH_STATIC_H__
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
int auth_get_static_auth (auth_t *auth, config_options_t *options);
#endif

View File

@ -8,7 +8,7 @@
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@xiph.org>
* and others (see AUTHORS for details).
* Copyright 2011-2012, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
* Copyright 2011-2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
/*
@ -41,13 +41,6 @@
* encoded) and duration is the amount of time in seconds. user and pass
* setting can be blank
*
* On stream start and end, another url can be issued to help clear any user
* info stored at the auth server. Useful for abnormal outage/termination
* cases.
*
* action=mount_add&mount=/live&server=myserver.com&port=8000
* action=mount_remove&mount=/live&server=myserver.com&port=8000
*
* On source client connection, a request can be made to trigger a URL request
* to verify the details externally. Post info is
*
@ -90,9 +83,8 @@ typedef struct {
char *prefix_headers; // prefix for passed headers.
char *addurl;
char *removeurl;
char *stream_start;
char *stream_end;
char *stream_auth;
char *addaction;
char *removeaction;
char *username;
char *password;
char *auth_header;
@ -102,6 +94,7 @@ typedef struct {
char *userpwd;
CURL *handle;
char errormsg [CURL_ERROR_SIZE];
auth_result result;
} auth_url;
@ -112,19 +105,19 @@ static void auth_url_clear(auth_t *self)
ICECAST_LOG_INFO("Doing auth URL cleanup");
url = self->state;
self->state = NULL;
curl_easy_cleanup (url->handle);
free (url->username);
free (url->password);
free (url->pass_headers);
free (url->prefix_headers);
free (url->removeurl);
free (url->addurl);
free (url->stream_start);
free (url->stream_end);
free (url->auth_header);
free (url->timelimit_header);
free (url->userpwd);
free (url);
curl_easy_cleanup(url->handle);
free(url->username);
free(url->password);
free(url->pass_headers);
free(url->prefix_headers);
free(url->removeurl);
free(url->addurl);
free(url->addaction);
free(url->removeaction);
free(url->auth_header);
free(url->timelimit_header);
free(url->userpwd);
free(url);
}
@ -149,7 +142,7 @@ static size_t handle_returned_header (void *ptr, size_t size, size_t nmemb, void
auth_t *auth = client->auth;
auth_url *url = auth->state;
if (strncasecmp (ptr, url->auth_header, url->auth_header_len) == 0)
client->authenticated = 1;
url->result = AUTH_OK;
if (strncasecmp (ptr, url->timelimit_header, url->timelimit_header_len) == 0)
{
unsigned int limit = 0;
@ -178,7 +171,7 @@ static size_t handle_returned_data (void *ptr, size_t size, size_t nmemb, void *
}
static auth_result url_remove_listener (auth_client *auth_user)
static auth_result url_remove_client (auth_client *auth_user)
{
client_t *client = auth_user->client;
auth_t *auth = client->auth;
@ -224,8 +217,9 @@ static auth_result url_remove_listener (auth_client *auth_user)
ipaddr = util_url_escape (client->con->ip);
snprintf (post, sizeof (post),
"action=listener_remove&server=%s&port=%d&client=%lu&mount=%s"
"action=%s&server=%s&port=%d&client=%lu&mount=%s"
"&user=%s&pass=%s&duration=%lu&ip=%s&agent=%s",
url->removeaction, /* already escaped */
server, port, client->con->id, mount, username,
password, (long unsigned)duration, ipaddr, user_agent);
free (server);
@ -271,7 +265,7 @@ static auth_result url_remove_listener (auth_client *auth_user)
}
static auth_result url_add_listener (auth_client *auth_user)
static auth_result url_add_client (auth_client *auth_user)
{
client_t *client = auth_user->client;
auth_t *auth = client->auth;
@ -320,8 +314,9 @@ static auth_result url_add_listener (auth_client *auth_user)
ipaddr = util_url_escape (client->con->ip);
post_offset = snprintf (post, sizeof (post),
"action=listener_add&server=%s&port=%d&client=%lu&mount=%s"
"action=%s&server=%s&port=%d&client=%lu&mount=%s"
"&user=%s&pass=%s&ip=%s&agent=%s",
url->addaction, /* already escaped */
server, port, client->con->id, mount, username,
password, ipaddr, user_agent);
free (server);
@ -388,6 +383,7 @@ static auth_result url_add_listener (auth_client *auth_user)
curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user);
url->errormsg[0] = '\0';
url->result = AUTH_FAILED;
res = curl_easy_perform (url->handle);
free (userpwd);
@ -398,166 +394,12 @@ static auth_result url_add_listener (auth_client *auth_user)
return AUTH_FAILED;
}
/* we received a response, lets see what it is */
if (client->authenticated)
return AUTH_OK;
ICECAST_LOG_INFO("client auth (%s) failed with \"%s\"", url->addurl, url->errormsg);
return AUTH_FAILED;
if (url->result == AUTH_FAILED) {
ICECAST_LOG_INFO("client auth (%s) failed with \"%s\"", url->addurl, url->errormsg);
}
return url->result;
}
/* called by auth thread when a source starts, there is no client_t in
* this case
*/
static void url_stream_start (auth_client *auth_user)
{
char *mount, *server;
ice_config_t *config = config_get_config ();
mount_proxy *mountinfo = config_find_mount (config, auth_user->mount, MOUNT_TYPE_NORMAL);
auth_t *auth = mountinfo->auth;
auth_url *url = auth->state;
char *stream_start_url;
int port;
char post [4096];
if (url->stream_start == NULL)
{
config_release_config ();
return;
}
server = util_url_escape (config->hostname);
port = config->port;
stream_start_url = strdup (url->stream_start);
/* we don't want this auth disappearing from under us while
* the connection is in progress */
mountinfo->auth->refcount++;
config_release_config ();
mount = util_url_escape (auth_user->mount);
snprintf (post, sizeof (post),
"action=mount_add&mount=%s&server=%s&port=%d", mount, server, port);
free (server);
free (mount);
if (strchr (url->stream_start, '@') == NULL)
{
if (url->userpwd)
curl_easy_setopt (url->handle, CURLOPT_USERPWD, url->userpwd);
else
curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
}
else
curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
curl_easy_setopt (url->handle, CURLOPT_URL, stream_start_url);
curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post);
curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user);
if (curl_easy_perform (url->handle))
ICECAST_LOG_WARN("auth to server %s failed with %s", stream_start_url, url->errormsg);
auth_release (auth);
free (stream_start_url);
return;
}
static void url_stream_end (auth_client *auth_user)
{
char *mount, *server;
ice_config_t *config = config_get_config ();
mount_proxy *mountinfo = config_find_mount (config, auth_user->mount, MOUNT_TYPE_NORMAL);
auth_t *auth = mountinfo->auth;
auth_url *url = auth->state;
char *stream_end_url;
int port;
char post [4096];
if (url->stream_end == NULL)
{
config_release_config ();
return;
}
server = util_url_escape (config->hostname);
port = config->port;
stream_end_url = strdup (url->stream_end);
/* we don't want this auth disappearing from under us while
* the connection is in progress */
mountinfo->auth->refcount++;
config_release_config ();
mount = util_url_escape (auth_user->mount);
snprintf (post, sizeof (post),
"action=mount_remove&mount=%s&server=%s&port=%d", mount, server, port);
free (server);
free (mount);
if (strchr (url->stream_end, '@') == NULL)
{
if (url->userpwd)
curl_easy_setopt (url->handle, CURLOPT_USERPWD, url->userpwd);
else
curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
}
else
curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
curl_easy_setopt (url->handle, CURLOPT_URL, url->stream_end);
curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post);
curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user);
if (curl_easy_perform (url->handle))
ICECAST_LOG_WARN("auth to server %s failed with %s", stream_end_url, url->errormsg);
auth_release (auth);
free (stream_end_url);
return;
}
static void url_stream_auth (auth_client *auth_user)
{
ice_config_t *config;
int port;
client_t *client = auth_user->client;
auth_url *url = client->auth->state;
char *mount, *host, *user, *pass, *ipaddr, *admin="";
char post [4096];
if (strchr (url->stream_auth, '@') == NULL)
{
if (url->userpwd)
curl_easy_setopt (url->handle, CURLOPT_USERPWD, url->userpwd);
else
curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
}
else
curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
curl_easy_setopt (url->handle, CURLOPT_URL, url->stream_auth);
curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post);
curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user);
if (strcmp (auth_user->mount, httpp_getvar (client->parser, HTTPP_VAR_URI)) != 0)
admin = "&admin=1";
mount = util_url_escape (auth_user->mount);
config = config_get_config ();
host = util_url_escape (config->hostname);
port = config->port;
config_release_config ();
user = util_url_escape (client->username);
pass = util_url_escape (client->password);
ipaddr = util_url_escape (client->con->ip);
snprintf (post, sizeof (post),
"action=stream_auth&mount=%s&ip=%s&server=%s&port=%d&user=%s&pass=%s%s",
mount, ipaddr, host, port, user, pass, admin);
free (ipaddr);
free (user);
free (pass);
free (mount);
free (host);
client->authenticated = 0;
if (curl_easy_perform (url->handle))
ICECAST_LOG_WARN("auth to server %s failed with %s", url->stream_auth, url->errormsg);
}
static auth_result auth_url_adduser(auth_t *auth, const char *username, const char *password)
{
return AUTH_FAILED;
@ -576,6 +418,8 @@ static auth_result auth_url_listuser (auth_t *auth, xmlNodePtr srcnode)
int auth_get_url_auth (auth_t *authenticator, config_options_t *options)
{
auth_url *url_info;
const char * addaction = "listener_add";
const char * removeaction = "listener_remove";
authenticator->free = auth_url_clear;
authenticator->adduser = auth_url_adduser;
@ -590,76 +434,54 @@ int auth_get_url_auth (auth_t *authenticator, config_options_t *options)
url_info->timelimit_header = strdup ("icecast-auth-timelimit:");
/* force auth thread to call function. this makes sure the auth_t is attached to client */
authenticator->authenticate = url_add_listener;
authenticator->authenticate_client = url_add_client;
while(options) {
if(!strcmp(options->name, "username"))
{
free (url_info->username);
url_info->username = strdup (options->value);
}
if(!strcmp(options->name, "password"))
{
free (url_info->password);
url_info->password = strdup (options->value);
}
if(!strcmp(options->name, "headers"))
{
free (url_info->pass_headers);
url_info->pass_headers = strdup (options->value);
}
if(!strcmp(options->name, "header_prefix"))
{
free (url_info->prefix_headers);
url_info->prefix_headers = strdup (options->value);
}
if(!strcmp(options->name, "listener_add"))
{
free (url_info->addurl);
url_info->addurl = strdup (options->value);
}
if(!strcmp(options->name, "listener_remove"))
{
authenticator->release_listener = url_remove_listener;
free (url_info->removeurl);
url_info->removeurl = strdup (options->value);
}
if(!strcmp(options->name, "mount_add"))
{
authenticator->stream_start = url_stream_start;
free (url_info->stream_start);
url_info->stream_start = strdup (options->value);
}
if(!strcmp(options->name, "mount_remove"))
{
authenticator->stream_end = url_stream_end;
free (url_info->stream_end);
url_info->stream_end = strdup (options->value);
}
if(!strcmp(options->name, "stream_auth"))
{
authenticator->stream_auth = url_stream_auth;
free (url_info->stream_auth);
url_info->stream_auth = strdup (options->value);
}
if(!strcmp(options->name, "auth_header"))
{
free (url_info->auth_header);
url_info->auth_header = strdup (options->value);
}
if (strcmp(options->name, "timelimit_header") == 0)
{
free (url_info->timelimit_header);
url_info->timelimit_header = strdup (options->value);
if(strcmp(options->name, "username") == 0) {
free(url_info->username);
url_info->username = strdup(options->value);
} else if(strcmp(options->name, "password") == 0) {
free(url_info->password);
url_info->password = strdup(options->value);
} else if(strcmp(options->name, "headers") == 0) {
free(url_info->pass_headers);
url_info->pass_headers = strdup(options->value);
} else if(strcmp(options->name, "header_prefix") == 0) {
free(url_info->prefix_headers);
url_info->prefix_headers = strdup(options->value);
} else if(strcmp(options->name, "client_add") == 0) {
free(url_info->addurl);
url_info->addurl = strdup(options->value);
} else if(strcmp(options->name, "client_remove") == 0) {
authenticator->release_client = url_remove_client;
free(url_info->removeurl);
url_info->removeurl = strdup(options->value);
} else if(strcmp(options->name, "action_add") == 0) {
addaction = options->value;
} else if(strcmp(options->name, "action_remove") == 0) {
removeaction = options->value;
} else if(strcmp(options->name, "auth_header") == 0) {
free(url_info->auth_header);
url_info->auth_header = strdup(options->value);
} else if (strcmp(options->name, "timelimit_header") == 0) {
free(url_info->timelimit_header);
url_info->timelimit_header = strdup(options->value);
} else {
ICECAST_LOG_ERROR("Unknown option: %s", options->name);
}
options = options->next;
}
url_info->addaction = util_url_escape(addaction);
url_info->removeaction = util_url_escape(removeaction);
url_info->handle = curl_easy_init ();
if (url_info->handle == NULL)
{
auth_url_clear (authenticator);
return -1;
}
if (url_info->auth_header)
url_info->auth_header_len = strlen (url_info->auth_header);
if (url_info->timelimit_header)
@ -686,4 +508,3 @@ int auth_get_url_auth (auth_t *authenticator, config_options_t *options)
ICECAST_LOG_INFO("URL based authentication setup");
return 0;
}

View File

@ -32,6 +32,7 @@
#include "client.h"
#include "logging.h"
#include "util.h"
#include "auth.h"
#define CATMODULE "CONFIG"
#define CONFIG_DEFAULT_LOCATION "Earth"
@ -46,7 +47,6 @@
#define CONFIG_DEFAULT_SOURCE_TIMEOUT 10
#define CONFIG_DEFAULT_MASTER_USERNAME "relay"
#define CONFIG_DEFAULT_SHOUTCAST_MOUNT "/stream"
#define CONFIG_DEFAULT_ICE_LOGIN 0
#define CONFIG_DEFAULT_FILESERVE 1
#define CONFIG_DEFAULT_TOUCH_FREQ 5
#define CONFIG_DEFAULT_HOSTNAME "localhost"
@ -89,7 +89,7 @@ static void _parse_paths(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
static void _parse_logging(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
static void _parse_security(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
static void _parse_authentication(xmlDocPtr doc, xmlNodePtr node,
ice_config_t *c);
ice_config_t *c, char **source_password);
static void _parse_http_headers(xmlDocPtr doc, xmlNodePtr node,
ice_config_http_header_t **http_headers);
static void _parse_relay(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
@ -155,6 +155,112 @@ static inline int __parse_loglevel(const char *str) {
return atoi(str);
}
static void __append_old_style_auth(auth_stack_t **stack, const char *name, const char *type, const char *username, const char *password, const char *method, const char *allow_method, int allow_web, const char *allow_admin) {
xmlNodePtr role, user, pass;
auth_t *auth;
if (!type)
return;
role = xmlNewNode(NULL, XMLSTR("role"));
xmlSetProp(role, XMLSTR("type"), XMLSTR(type));
xmlSetProp(role, XMLSTR("deny-method"), XMLSTR("*"));
if (allow_method)
xmlSetProp(role, XMLSTR("allow-method"), XMLSTR(allow_method));
if (name)
xmlSetProp(role, XMLSTR("name"), XMLSTR(name));
if (method)
xmlSetProp(role, XMLSTR("method"), XMLSTR(method));
if (allow_web) {
xmlSetProp(role, XMLSTR("allow-web"), XMLSTR("*"));
} else {
xmlSetProp(role, XMLSTR("deny-web"), XMLSTR("*"));
}
if (allow_admin && strcmp(allow_admin, "*") == 0) {
xmlSetProp(role, XMLSTR("allow-admin"), XMLSTR("*"));
} else {
xmlSetProp(role, XMLSTR("deny-admin"), XMLSTR("*"));
if (allow_admin)
xmlSetProp(role, XMLSTR("allow-admin"), XMLSTR(allow_admin));
}
if (username) {
user = xmlNewChild(role, NULL, XMLSTR("option"), NULL);
xmlSetProp(user, XMLSTR("name"), XMLSTR("username"));
xmlSetProp(user, XMLSTR("value"), XMLSTR(username));
}
if (password) {
pass = xmlNewChild(role, NULL, XMLSTR("option"), NULL);
xmlSetProp(pass, XMLSTR("name"), XMLSTR("password"));
xmlSetProp(pass, XMLSTR("value"), XMLSTR(password));
}
auth = auth_get_authenticator(role);
auth_stack_push(stack, auth);
auth_release(auth);
xmlFreeNode(role);
}
static void __append_option_tag (xmlNodePtr parent, const char *name, const char *value) {
xmlNodePtr node;
if (!name || !value)
return;
node = xmlNewChild(parent, NULL, XMLSTR("option"), NULL);
xmlSetProp(node, XMLSTR("name"), XMLSTR(name));
xmlSetProp(node, XMLSTR("value"), XMLSTR(value));
}
static void __append_old_style_urlauth(auth_stack_t **stack, const char *client_add, const char *client_remove, const char *action_add, const char *action_remove, const char *username, const char *password, int is_source, const char *auth_header, const char *timelimit_header, const char *headers, const char *header_prefix) {
xmlNodePtr role;
auth_t *auth;
if (!stack || !client_add || !!client_remove)
return;
role = xmlNewNode(NULL, XMLSTR("role"));
xmlSetProp(role, XMLSTR("type"), XMLSTR("url"));
if (is_source) {
xmlSetProp(role, XMLSTR("method"), XMLSTR("source,put"));
xmlSetProp(role, XMLSTR("deny-method"), XMLSTR("*"));
xmlSetProp(role, XMLSTR("allow-method"), XMLSTR("source,put"));
xmlSetProp(role, XMLSTR("allow-web"), XMLSTR("*"));
xmlSetProp(role, XMLSTR("allow-admin"), XMLSTR("*"));
} else {
xmlSetProp(role, XMLSTR("method"), XMLSTR("get,post,head"));
xmlSetProp(role, XMLSTR("deny-method"), XMLSTR("*"));
xmlSetProp(role, XMLSTR("allow-method"), XMLSTR("get,post,head"));
xmlSetProp(role, XMLSTR("allow-web"), XMLSTR("*"));
xmlSetProp(role, XMLSTR("deny-admin"), XMLSTR("*"));
}
__append_option_tag(role, "client_add", client_add);
__append_option_tag(role, "client_remove", client_remove);
__append_option_tag(role, "action_add", action_add);
__append_option_tag(role, "action_remove", action_remove);
__append_option_tag(role, "username", username);
__append_option_tag(role, "password", password);
__append_option_tag(role, "auth_header", auth_header);
__append_option_tag(role, "timelimit_header", timelimit_header);
__append_option_tag(role, "headers", headers);
__append_option_tag(role, "header_prefix", header_prefix);
auth = auth_get_authenticator(role);
auth_stack_push(stack, auth);
auth_release(auth);
xmlFreeNode(role);
}
static void config_clear_http_header(ice_config_http_header_t *header) {
ice_config_http_header_t *old;
@ -209,11 +315,7 @@ static inline ice_config_http_header_t * config_copy_http_header(ice_config_http
static void config_clear_mount (mount_proxy *mount)
{
config_options_t *option;
if (mount->mountname) xmlFree (mount->mountname);
if (mount->username) xmlFree (mount->username);
if (mount->password) xmlFree (mount->password);
if (mount->dumpfile) xmlFree (mount->dumpfile);
if (mount->intro_filename) xmlFree (mount->intro_filename);
if (mount->on_connect) xmlFree (mount->on_connect);
@ -227,18 +329,8 @@ static void config_clear_mount (mount_proxy *mount)
if (mount->type) xmlFree (mount->type);
if (mount->charset) xmlFree (mount->charset);
if (mount->cluster_password) xmlFree (mount->cluster_password);
if (mount->authstack) auth_stack_release(mount->authstack);
if (mount->auth_type) xmlFree (mount->auth_type);
option = mount->auth_options;
while (option)
{
config_options_t *nextopt = option->next;
if (option->name) xmlFree (option->name);
if (option->value) xmlFree (option->value);
free (option);
option = nextopt;
}
auth_release (mount->auth);
config_clear_http_header(mount->http_headers);
free (mount);
}
@ -271,15 +363,6 @@ void config_clear(ice_config_t *c)
xmlFree (c->server_id);
if (c->location) xmlFree(c->location);
if (c->admin) xmlFree(c->admin);
if (c->source_password) xmlFree(c->source_password);
if (c->admin_username)
xmlFree(c->admin_username);
if (c->admin_password)
xmlFree(c->admin_password);
if (c->relay_username)
xmlFree(c->relay_username);
if (c->relay_password)
xmlFree(c->relay_password);
if (c->hostname) xmlFree(c->hostname);
if (c->base_dir) xmlFree(c->base_dir);
if (c->log_dir) xmlFree(c->log_dir);
@ -295,6 +378,7 @@ void config_clear(ice_config_t *c)
if (c->access_log) xmlFree(c->access_log);
if (c->error_log) xmlFree(c->error_log);
if (c->shoutcast_mount) xmlFree(c->shoutcast_mount);
if (c->authstack) auth_stack_release(c->authstack);
if (c->master_server) xmlFree(c->master_server);
if (c->master_username) xmlFree(c->master_username);
if (c->master_password) xmlFree(c->master_password);
@ -446,9 +530,7 @@ static void _set_defaults(ice_config_t *configuration)
configuration->client_timeout = CONFIG_DEFAULT_CLIENT_TIMEOUT;
configuration->header_timeout = CONFIG_DEFAULT_HEADER_TIMEOUT;
configuration->source_timeout = CONFIG_DEFAULT_SOURCE_TIMEOUT;
configuration->source_password = NULL;
configuration->shoutcast_mount = (char *)xmlCharStrdup (CONFIG_DEFAULT_SHOUTCAST_MOUNT);
configuration->ice_login = CONFIG_DEFAULT_ICE_LOGIN;
configuration->fileserve = CONFIG_DEFAULT_FILESERVE;
configuration->touch_interval = CONFIG_DEFAULT_TOUCH_FREQ;
configuration->on_demand = 0;
@ -475,8 +557,6 @@ static void _set_defaults(ice_config_t *configuration)
configuration->user = NULL;
configuration->group = NULL;
configuration->num_yp_directories = 0;
configuration->relay_username = (char *)xmlCharStrdup (CONFIG_DEFAULT_MASTER_USERNAME);
configuration->relay_password = NULL;
/* default to a typical prebuffer size used by clients */
configuration->burst_size = CONFIG_DEFAULT_BURST_SIZE;
}
@ -523,6 +603,7 @@ static void _parse_root(xmlDocPtr doc, xmlNodePtr node,
ice_config_t *configuration)
{
char *tmp;
char *source_password = NULL;
configuration->listen_sock = calloc (1, sizeof (*configuration->listen_sock));
configuration->listen_sock->port = 8000;
@ -543,16 +624,15 @@ static void _parse_root(xmlDocPtr doc, xmlNodePtr node,
configuration->server_id = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
ICECAST_LOG_WARN("Warning, server version string override detected. This may lead to unexpected client software behavior.");
} else if(xmlStrcmp (node->name, XMLSTR("authentication")) == 0) {
_parse_authentication(doc, node->xmlChildrenNode, configuration);
_parse_authentication(doc, node->xmlChildrenNode, configuration, &source_password);
} else if (xmlStrcmp (node->name, XMLSTR("source-password")) == 0) {
/* TODO: This is the backwards-compatibility location */
ICECAST_LOG_WARN("<source-password> defined outside <authentication>. This is deprecated and will be removed in version 2.5.");
if (configuration->source_password) xmlFree(configuration->source_password);
configuration->source_password = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
if (source_password)
xmlFree(source_password);
source_password = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
} else if (xmlStrcmp (node->name, XMLSTR("icelogin")) == 0) {
tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
configuration->ice_login = util_str_to_bool(tmp);
if (tmp) xmlFree(tmp);
ICECAST_LOG_ERROR("<icelogin> has been removed.");
} else if (xmlStrcmp (node->name, XMLSTR("fileserve")) == 0) {
tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
configuration->fileserve = util_str_to_bool(tmp);
@ -621,6 +701,29 @@ static void _parse_root(xmlDocPtr doc, xmlNodePtr node,
}
} while ((node = node->next));
/* global source password is set.
* We need to set it on default mount.
* If default mount has a authstack not NULL we don't need to do anything.
*/
if (source_password) {
mount_proxy *mount = config_find_mount(configuration, NULL, MOUNT_TYPE_DEFAULT);
if (!mount) {
/* create a default mount here */
xmlNodePtr node;
node = xmlNewNode(NULL, XMLSTR("mount"));
xmlSetProp(node, XMLSTR("type"), XMLSTR("default"));
_parse_mount(doc, node, configuration);
xmlFreeNode(node);
mount = config_find_mount(configuration, NULL, MOUNT_TYPE_DEFAULT);
}
if (mount) {
if (!mount->authstack)
__append_old_style_auth(&mount->authstack, "legacy-global-source", AUTH_TYPE_STATIC, "source", source_password, NULL, "source,put,get", 0, "*");
} else {
ICECAST_LOG_ERROR("Can not find nor create default mount but global lagency source password set. Bad.");
}
}
/* drop the first listening socket details if more than one is defined, as we only
* have port or listen-socket not both */
if (configuration->listen_sock_count > 1)
@ -698,6 +801,158 @@ static void _parse_limits(xmlDocPtr doc, xmlNodePtr node,
} while ((node = node->next));
}
static void _parse_mount_oldstyle_authentication(mount_proxy *mount, xmlNodePtr node, auth_stack_t **authstack) {
int allow_duplicate_users = 1;
auth_t *auth;
char *type;
char *name;
char *value;
xmlNodePtr child;
child = node->xmlChildrenNode;
while (child) {
if (xmlStrcmp(node->name, XMLSTR("option")) == 0) {
name = (char *)xmlGetProp(node, XMLSTR("name"));
value = (char *)xmlGetProp(node, XMLSTR("value"));
if (name && value) {
if (strcmp(name, "allow_duplicate_users") == 0) {
allow_duplicate_users = util_str_to_bool(value);
}
}
if (name)
xmlFree(name);
if (value)
xmlFree(value);
}
child = child->next;
}
type = (char *)xmlGetProp(node, XMLSTR("type"));
if (strcmp(type, AUTH_TYPE_HTPASSWD) == 0) {
if (!allow_duplicate_users)
xmlSetProp(node, XMLSTR("connections-per-user"), XMLSTR("0"));
auth = auth_get_authenticator(node);
if (auth) {
auth_stack_push(authstack, auth);
auth_release(auth);
}
__append_old_style_auth(authstack, NULL, AUTH_TYPE_ANONYMOUS, NULL, NULL, "source,put", NULL, 0, NULL);
} else if (strcmp(type, AUTH_TYPE_URL) == 0) {
/* This block is super fun! Attention! Super fun ahead! Ladies and Gentlemans take care and watch your children! */
/* Stuff that was of help:
* $ sed 's/^.*name="\([^"]*\)".*$/ const char *\1 = NULL;/'
* $ sed 's/^.*name="\([^"]*\)".*$/ if (\1)\n xmlFree(\1);/'
* $ sed 's/^.*name="\([^"]*\)".*$/ } else if (strcmp(name, "\1") == 0) {\n \1 = value;\n value = NULL;/'
*/
/* urls */
char *mount_add = NULL;
char *mount_remove = NULL;
char *listener_add = NULL;
char *listener_remove = NULL;
char *stream_auth = NULL;
/* request credentials */
char *username = NULL;
char *password = NULL;
/* general options */
char *auth_header = NULL;
char *timelimit_header = NULL;
char *headers = NULL;
char *header_prefix = NULL;
child = node->xmlChildrenNode;
while (child) {
if (xmlStrcmp(node->name, XMLSTR("option")) == 0) {
name = (char *)xmlGetProp(node, XMLSTR("name"));
value = (char *)xmlGetProp(node, XMLSTR("value"));
if (name && value) {
if (strcmp(name, "mount_add") == 0) {
mount_add = value;
value = NULL;
} else if (strcmp(name, "mount_add") == 0) {
mount_add = value;
value = NULL;
} else if (strcmp(name, "mount_remove") == 0) {
mount_remove = value;
value = NULL;
} else if (strcmp(name, "listener_add") == 0) {
listener_add = value;
value = NULL;
} else if (strcmp(name, "listener_remove") == 0) {
listener_remove = value;
value = NULL;
} else if (strcmp(name, "username") == 0) {
username = value;
value = NULL;
} else if (strcmp(name, "password") == 0) {
password = value;
value = NULL;
} else if (strcmp(name, "auth_header") == 0) {
auth_header = value;
value = NULL;
} else if (strcmp(name, "timelimit_header") == 0) {
timelimit_header = value;
value = NULL;
} else if (strcmp(name, "headers") == 0) {
headers = value;
value = NULL;
} else if (strcmp(name, "header_prefix") == 0) {
header_prefix = value;
value = NULL;
} else if (strcmp(name, "stream_auth") == 0) {
stream_auth = value;
value = NULL;
}
}
if (name)
xmlFree(name);
if (value)
xmlFree(value);
}
child = child->next;
}
/* TODO: FIXME: XXX: implement support for mount_add and mount_remove (using only username and password) here. */
__append_old_style_urlauth(authstack, listener_add, listener_remove, "listener_add", "listener_remove", username, password, 0, auth_header, timelimit_header, headers, header_prefix);
__append_old_style_urlauth(authstack, stream_auth, NULL, "stream_auth", NULL, username, password, 1, auth_header, timelimit_header, headers, header_prefix);
if (listener_add)
__append_old_style_auth(authstack, NULL, AUTH_TYPE_ANONYMOUS, NULL, NULL, "get,put,head", NULL, 0, NULL);
if (stream_auth)
__append_old_style_auth(authstack, NULL, AUTH_TYPE_ANONYMOUS, NULL, NULL, "source,put", NULL, 0, NULL);
if (mount_add)
xmlFree(mount_add);
if (mount_remove)
xmlFree(mount_remove);
if (listener_add)
xmlFree(listener_add);
if (listener_remove)
xmlFree(listener_remove);
if (username)
xmlFree(username);
if (password)
xmlFree(password);
if (auth_header)
xmlFree(auth_header);
if (timelimit_header)
xmlFree(timelimit_header);
if (headers)
xmlFree(headers);
if (header_prefix)
xmlFree(header_prefix);
if (stream_auth)
xmlFree(stream_auth);
} else {
ICECAST_LOG_ERROR("Unknown authentication type in legacy mode. Disable anonymous login listener and global login for source.");
__append_old_style_auth(authstack, NULL, AUTH_TYPE_ANONYMOUS, NULL, NULL, NULL, NULL, 0, NULL);
}
xmlFree(type);
}
static void _parse_mount(xmlDocPtr doc, xmlNodePtr node,
ice_config_t *configuration)
{
@ -705,6 +960,9 @@ static void _parse_mount(xmlDocPtr doc, xmlNodePtr node,
mount_proxy *mount = calloc(1, sizeof(mount_proxy));
mount_proxy *current = configuration->mounts;
mount_proxy *last=NULL;
char *username = NULL;
char *password = NULL;
auth_stack_t *authstack = NULL;
/* default <mount> settings */
mount->mounttype = MOUNT_TYPE_NORMAL;
@ -740,11 +998,11 @@ static void _parse_mount(xmlDocPtr doc, xmlNodePtr node,
mount->mountname = (char *)xmlNodeListGetString (doc, node->xmlChildrenNode, 1);
}
else if (xmlStrcmp (node->name, XMLSTR("username")) == 0) {
mount->username = (char *)xmlNodeListGetString(
username = (char *)xmlNodeListGetString(
doc, node->xmlChildrenNode, 1);
}
else if (xmlStrcmp (node->name, XMLSTR("password")) == 0) {
mount->password = (char *)xmlNodeListGetString(
password = (char *)xmlNodeListGetString(
doc, node->xmlChildrenNode, 1);
}
else if (xmlStrcmp (node->name, XMLSTR("dump-file")) == 0) {
@ -800,7 +1058,23 @@ static void _parse_mount(xmlDocPtr doc, xmlNodePtr node,
if(tmp) xmlFree(tmp);
}
else if (xmlStrcmp (node->name, XMLSTR("authentication")) == 0) {
mount->auth = auth_get_authenticator (node);
tmp = (char *)xmlGetProp(node, XMLSTR("type"));
if (tmp) {
xmlFree(tmp);
_parse_mount_oldstyle_authentication(mount, node, &authstack);
} else {
xmlNodePtr child = node->xmlChildrenNode;
do {
if (child == NULL) break;
if (xmlIsBlankNode(child)) continue;
if (xmlStrcmp (child->name, XMLSTR("role")) == 0) {
auth_t *auth = auth_get_authenticator(child);
auth_stack_push(&authstack, auth);
auth_release(auth);
}
} while ((child = child->next));
}
}
else if (xmlStrcmp (node->name, XMLSTR("on-connect")) == 0) {
mount->on_connect = (char *)xmlNodeListGetString(
@ -864,6 +1138,25 @@ static void _parse_mount(xmlDocPtr doc, xmlNodePtr node,
}
} while ((node = node->next));
if (password) {
auth_stack_t *old_style = NULL;
__append_old_style_auth(&old_style, "legacy-mount-source", AUTH_TYPE_STATIC, username ? username : "source", password, NULL, "source,put,get", 0, "*");
if (authstack) {
auth_stack_append(old_style, authstack);
auth_stack_release(authstack);
}
authstack = old_style;
}
if (username)
xmlFree(username);
if (password)
xmlFree(password);
if (mount->authstack)
auth_stack_release(mount->authstack);
auth_stack_addref(mount->authstack = authstack);
/* make sure we have at least the mountpoint name */
if (mount->mountname == NULL && mount->mounttype != MOUNT_TYPE_DEFAULT)
{
@ -874,11 +1167,18 @@ static void _parse_mount(xmlDocPtr doc, xmlNodePtr node,
{
ICECAST_LOG_WARN("Default mount %s has mount-name set. This is not supported. Behavior may not be consistent.", mount->mountname);
}
if (mount->auth && mount->mountname) {
mount->auth->mount = strdup ((char *)mount->mountname);
} else if (mount->auth && mount->mounttype == MOUNT_TYPE_DEFAULT ) {
mount->auth->mount = strdup ("(default mount)");
while (authstack) {
auth_t *auth = auth_stack_get(authstack);
if (mount->mountname) {
auth->mount = strdup((char *)mount->mountname);
} else if (mount->mounttype == MOUNT_TYPE_DEFAULT ) {
auth->mount = strdup("(default mount)");
}
auth_release(auth);
auth_stack_next(&authstack);
}
while(current) {
last = current;
current = current->next;
@ -1107,8 +1407,13 @@ static void _parse_listen_socket(xmlDocPtr doc, xmlNodePtr node,
}
static void _parse_authentication(xmlDocPtr doc, xmlNodePtr node,
ice_config_t *configuration)
ice_config_t *configuration, char **source_password)
{
char *admin_password = NULL, *admin_username = NULL;
char *relay_password = NULL, *relay_username = (char*)xmlCharStrdup(CONFIG_DEFAULT_MASTER_USERNAME);
auth_stack_t *old_style = NULL;
auth_stack_t *new_style = NULL;
do {
if (node == NULL) break;
if (xmlIsBlankNode(node)) continue;
@ -1118,33 +1423,59 @@ static void _parse_authentication(xmlDocPtr doc, xmlNodePtr node,
ICECAST_LOG_ERROR("Mount level source password defined within global <authentication> section.");
}
else {
if (configuration->source_password)
xmlFree(configuration->source_password);
configuration->source_password =
if (*source_password) xmlFree(*source_password);
*source_password =
(char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
}
} else if (xmlStrcmp (node->name, XMLSTR("admin-password")) == 0) {
if(configuration->admin_password)
xmlFree(configuration->admin_password);
configuration->admin_password =
(char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
if(admin_password) xmlFree(admin_password);
admin_password = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
} else if (xmlStrcmp (node->name, XMLSTR("admin-user")) == 0) {
if(configuration->admin_username)
xmlFree(configuration->admin_username);
configuration->admin_username =
(char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
if(admin_username) xmlFree(admin_username);
admin_username = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
} else if (xmlStrcmp (node->name, XMLSTR("relay-password")) == 0) {
if(configuration->relay_password)
xmlFree(configuration->relay_password);
configuration->relay_password =
(char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
if(relay_password) xmlFree(relay_password);
relay_password = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
} else if (xmlStrcmp (node->name, XMLSTR("relay-user")) == 0) {
if(configuration->relay_username)
xmlFree(configuration->relay_username);
configuration->relay_username =
(char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
if(relay_username) xmlFree(relay_username);
relay_username = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
} else if (xmlStrcmp (node->name, XMLSTR("role")) == 0) {
auth_t *auth = auth_get_authenticator(node);
auth_stack_push(&new_style, auth);
auth_release(auth);
}
} while ((node = node->next));
if (admin_password && admin_username)
__append_old_style_auth(&old_style, "legacy-admin", AUTH_TYPE_STATIC, admin_username, admin_password, NULL, "get,post,head", 1, "*");
if (relay_password && relay_username)
__append_old_style_auth(&old_style, "legacy-relay", AUTH_TYPE_STATIC, relay_username, relay_password, NULL, "get", 1, "streamlist.txt");
if (admin_password)
xmlFree(admin_password);
if (admin_username)
xmlFree(admin_username);
if (relay_password)
xmlFree(relay_password);
if (relay_username)
xmlFree(relay_username);
if (old_style && new_style) {
auth_stack_append(old_style, new_style);
auth_stack_release(new_style);
} else if (new_style) {
old_style = new_style;
}
/* default unauthed anonymous account */
__append_old_style_auth(&old_style, "anonymous", AUTH_TYPE_ANONYMOUS, NULL, NULL, NULL, "get,post,head", 1, NULL);
if (!old_style)
ICECAST_LOG_ERROR("BAD. old_style=NULL");
if (configuration->authstack)
auth_stack_release(configuration->authstack);
configuration->authstack = old_style;
}
static void _parse_directory(xmlDocPtr doc, xmlNodePtr node,
@ -1409,10 +1740,6 @@ static void merge_mounts(mount_proxy * dst, mount_proxy * src) {
if (!dst || !src)
return;
if (!dst->username)
dst->username = (char*)xmlStrdup((xmlChar*)src->username);
if (!dst->password)
dst->password = (char*)xmlStrdup((xmlChar*)src->password);
if (!dst->dumpfile)
dst->dumpfile = (char*)xmlStrdup((xmlChar*)src->dumpfile);
if (!dst->intro_filename)
@ -1439,12 +1766,8 @@ static void merge_mounts(mount_proxy * dst, mount_proxy * src) {
dst->charset = (char*)xmlStrdup((xmlChar*)src->charset);
if (dst->mp3_meta_interval == -1)
dst->mp3_meta_interval = src->mp3_meta_interval;
if (!dst->auth_type)
dst->auth_type = (char*)xmlStrdup((xmlChar*)src->auth_type);
// TODO: dst->auth
if (!dst->cluster_password)
dst->cluster_password = (char*)xmlStrdup((xmlChar*)src->cluster_password);
// TODO: dst->auth_options
if (!dst->on_connect)
dst->on_connect = (char*)xmlStrdup((xmlChar*)src->on_connect);
if (!dst->on_disconnect)
@ -1494,8 +1817,7 @@ static inline void _merge_mounts_all(ice_config_t *c) {
}
/* return the mount details that match the supplied mountpoint */
mount_proxy *config_find_mount (ice_config_t *config, const char *mount, mount_type type)
{
mount_proxy *config_find_mount (ice_config_t *config, const char *mount, mount_type type) {
mount_proxy *mountinfo = config->mounts;
for (; mountinfo; mountinfo = mountinfo->next)
@ -1503,19 +1825,27 @@ mount_proxy *config_find_mount (ice_config_t *config, const char *mount, mount_t
if (mountinfo->mounttype != type)
continue;
if (mount == NULL || mountinfo->mountname == NULL)
if (!mount && !mountinfo->mountname)
break;
if (mountinfo->mounttype == MOUNT_TYPE_NORMAL && strcmp (mountinfo->mountname, mount) == 0)
break;
if (mountinfo->mounttype == MOUNT_TYPE_NORMAL) {
if (!mount || !mountinfo->mountname)
continue;
if (strcmp(mountinfo->mountname, mount) == 0)
break;
} else if (mountinfo->mounttype == MOUNT_TYPE_DEFAULT) {
if (!mountinfo->mountname)
break;
#ifndef _WIN32
if (fnmatch(mountinfo->mountname, mount, FNM_PATHNAME) == 0)
break;
if (fnmatch(mountinfo->mountname, mount, FNM_PATHNAME) == 0)
break;
#else
if (strcmp(mountinfo->mountname, mount) == 0)
break;
if (strcmp(mountinfo->mountname, mount) == 0)
break;
#endif
}
}
/* retry with default mount */
@ -1548,4 +1878,3 @@ listener_t *config_get_listen_sock (ice_config_t *config, connection_t *con)
}
return listener;
}

View File

@ -26,7 +26,6 @@ struct _mount_proxy;
#include "thread/thread.h"
#include "avl/avl.h"
#include "auth.h"
#include "global.h"
#include "connection.h"
@ -74,8 +73,8 @@ typedef struct _mount_proxy {
mount_type mounttype; /* The type of the mount point */
char *username; /* Username and password for this mountpoint. If unset, */
char *password; /* falls back to global source password */
//char *username; /* Username and password for this mountpoint. If unset, */
//char *password; /* falls back to global source password */
char *dumpfile; /* Filename to dump this stream to (will be appended). NULL
to not dump. */
@ -100,10 +99,8 @@ typedef struct _mount_proxy {
ice_config_http_header_t *http_headers; /* additional HTTP headers */
char *auth_type; /* Authentication type */
struct auth_tag *auth;
char *cluster_password;
config_options_t *auth_options; /* Options for this type */
struct auth_stack_tag *authstack;
char *on_connect;
char *on_disconnect;
unsigned int max_listener_duration;
@ -152,16 +149,11 @@ typedef struct ice_config_tag {
int client_timeout;
int header_timeout;
int source_timeout;
int ice_login;
int fileserve;
int on_demand; /* global setting for all relays */
char *shoutcast_mount;
char *source_password;
char *admin_username;
char *admin_password;
char *relay_username;
char *relay_password;
struct auth_stack_tag *authstack;
int touch_interval;
ice_config_dir_t *dir_list;

View File

@ -8,7 +8,7 @@
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@xiph.org>
* and others (see AUTHORS for details).
* Copyright 2011-2012, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
* Copyright 2011-2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
/* client.c
@ -36,10 +36,14 @@
#include "fserve.h"
#include "client.h"
#include "auth.h"
#include "logging.h"
#include "util.h"
/* for ADMIN_COMMAND_ERROR */
#include "admin.h"
#ifdef _WIN32
#define snprintf _snprintf
#endif
@ -76,6 +80,8 @@ int client_create (client_t **c_ptr, connection_t *con, http_parser_t *parser)
stats_event_args (NULL, "clients", "%d", global.clients);
client->con = con;
client->parser = parser;
client->protocol = ICECAST_PROTOCOL_HTTP;
client->admin_command = ADMIN_COMMAND_ERROR;
client->refbuf = refbuf_new (PER_CLIENT_REFBUF_SIZE);
client->refbuf->len = 0; /* force reader code to ignore buffer contents */
client->pos = 0;
@ -87,6 +93,7 @@ int client_create (client_t **c_ptr, connection_t *con, http_parser_t *parser)
void client_destroy(client_t *client)
{
ICECAST_LOG_DEBUG("Called to destory client %p", client);
if (client == NULL)
return;
@ -98,7 +105,7 @@ void client_destroy(client_t *client)
client->refbuf = NULL;
}
if (auth_release_listener (client))
if (auth_release_client(client))
return;
/* write log entry if ip is set (some things don't set it, like outgoing
@ -123,41 +130,12 @@ void client_destroy(client_t *client)
free(client->username);
free(client->password);
free(client->role);
acl_release(client->acl);
free(client);
}
/* return -1 for failed, 0 for authenticated, 1 for pending
*/
int client_check_source_auth (client_t *client, const char *mount)
{
ice_config_t *config = config_get_config();
char *pass = config->source_password;
char *user = "source";
int ret = -1;
mount_proxy *mountinfo = config_find_mount (config, mount, MOUNT_TYPE_NORMAL);
do
{
if (mountinfo)
{
ret = 1;
if (auth_stream_authenticate (client, mount, mountinfo) > 0)
break;
ret = -1;
if (mountinfo->password)
pass = mountinfo->password;
if (mountinfo->username)
user = mountinfo->username;
}
if (connection_check_pass (client->parser, user, pass) > 0)
ret = 0;
} while (0);
config_release_config();
return ret;
}
/* helper function for reading data from a client */
int client_read_bytes (client_t *client, void *buf, unsigned len)
{
@ -259,4 +237,3 @@ void client_set_queue (client_t *client, refbuf_t *refbuf)
if (to_release)
refbuf_release (to_release);
}

View File

@ -8,7 +8,7 @@
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@xiph.org>
* and others (see AUTHORS for details).
* Copyright 2011-2012, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
* Copyright 2011-2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
/* client.h
@ -21,8 +21,14 @@
#include "connection.h"
#include "refbuf.h"
#include "acl.h"
#include "httpp/httpp.h"
typedef enum _protocol_tag {
ICECAST_PROTOCOL_HTTP = 0,
ICECAST_PROTOCOL_SHOUTCAST
} protocol_t;
typedef struct _client_tag
{
/* the client's connection */
@ -30,11 +36,29 @@ typedef struct _client_tag
/* the client's http headers */
http_parser_t *parser;
/* protocol client uses */
protocol_t protocol;
/* http response code for this client */
int respcode;
/* auth completed, 0 not yet, 1 passed */
int authenticated;
/* admin command if any. ADMIN_COMMAND_ERROR if not an admin command. */
int admin_command;
/* authentication instances we still need to go thru */
struct auth_stack_tag *authstack;
/* Client username */
char *username;
/* Client password */
char *password;
/* Client role */
char *role;
/* active ACL, set as soon as the client is authenticated */
acl_t *acl;
/* is client getting intro data */
long intro_offset;
@ -48,12 +72,6 @@ typedef struct _client_tag
/* auth used for this client */
struct auth_tag *auth;
/* Client username, if authenticated */
char *username;
/* Client password, if authenticated */
char *password;
/* Format-handler-specific data for this client */
void *format_data;
@ -75,6 +93,5 @@ void client_send_100(client_t *client);
int client_send_bytes (client_t *client, const void *buf, unsigned len);
int client_read_bytes (client_t *client, void *buf, unsigned len);
void client_set_queue (client_t *client, refbuf_t *refbuf);
int client_check_source_auth (client_t *client, const char *mount);
#endif /* __CLIENT_H__ */

View File

@ -8,8 +8,8 @@
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@xiph.org>
* and others (see AUTHORS for details).
* Copyright 2011, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
* Dave 'justdave' Miller <justdave@mozilla.com>.
* Copyright 2011, Dave 'justdave' Miller <justdave@mozilla.com>,
* Copyright 2011-2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
@ -80,8 +80,6 @@
Icecast auth style uses HTTP and Basic Authorization.
*/
#define SHOUTCAST_SOURCE_AUTH 1
#define ICECAST_SOURCE_AUTH 0
typedef struct client_queue_tag {
client_t *client;
@ -903,177 +901,7 @@ int connection_complete_source (source_t *source, int response)
return -1;
}
static int _check_pass_http(http_parser_t *parser,
const char *correctuser, const char *correctpass)
{
/* This will look something like "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" */
const char *header = httpp_getvar(parser, "authorization");
char *userpass, *tmp;
char *username, *password;
if(header == NULL)
return 0;
if(strncmp(header, "Basic ", 6))
return 0;
userpass = util_base64_decode(header+6);
if(userpass == NULL) {
ICECAST_LOG_WARN("Base64 decode of Authorization header \"%s\" failed",
header+6);
return 0;
}
tmp = strchr(userpass, ':');
if(!tmp) {
free(userpass);
return 0;
}
*tmp = 0;
username = userpass;
password = tmp+1;
if(strcmp(username, correctuser) || strcmp(password, correctpass)) {
free(userpass);
return 0;
}
free(userpass);
return 1;
}
static int _check_pass_icy(http_parser_t *parser, const char *correctpass)
{
const char *password;
password = httpp_getvar(parser, HTTPP_VAR_ICYPASSWORD);
if(!password)
return 0;
if (strcmp(password, correctpass))
return 0;
else
return 1;
}
static int _check_pass_ice(http_parser_t *parser, const char *correctpass)
{
const char *password;
password = httpp_getvar(parser, "ice-password");
if(!password)
password = "";
if (strcmp(password, correctpass))
return 0;
else
return 1;
}
int connection_check_admin_pass(http_parser_t *parser)
{
int ret;
ice_config_t *config = config_get_config();
char *pass = config->admin_password;
char *user = config->admin_username;
const char *protocol;
if(!pass || !user) {
config_release_config();
return 0;
}
protocol = httpp_getvar (parser, HTTPP_VAR_PROTOCOL);
if (protocol && strcmp (protocol, "ICY") == 0)
ret = _check_pass_icy (parser, pass);
else
ret = _check_pass_http (parser, user, pass);
config_release_config();
return ret;
}
int connection_check_relay_pass(http_parser_t *parser)
{
int ret;
ice_config_t *config = config_get_config();
char *pass = config->relay_password;
char *user = config->relay_username;
if(!pass || !user) {
config_release_config();
return 0;
}
ret = _check_pass_http(parser, user, pass);
config_release_config();
return ret;
}
/* return 0 for failed, 1 for ok
*/
int connection_check_pass (http_parser_t *parser, const char *user, const char *pass)
{
int ret;
const char *protocol;
if(!pass) {
ICECAST_LOG_WARN("No source password set, rejecting source");
return -1;
}
protocol = httpp_getvar(parser, HTTPP_VAR_PROTOCOL);
if(protocol != NULL && !strcmp(protocol, "ICY")) {
ret = _check_pass_icy(parser, pass);
}
else {
ret = _check_pass_http(parser, user, pass);
if (!ret)
{
ice_config_t *config = config_get_config_unlocked();
if (config->ice_login)
{
ret = _check_pass_ice(parser, pass);
if(ret)
ICECAST_LOG_WARN("Source is using deprecated icecast login");
}
}
}
return ret;
}
/* only called for native icecast source clients */
static void _handle_source_request (client_t *client, const char *uri)
{
ICECAST_LOG_INFO("Source logging in at mountpoint \"%s\" from %s",
uri, client->con->ip);
if (uri[0] != '/')
{
ICECAST_LOG_WARN("source mountpoint not starting with /");
client_send_error(client, 401, 1, "You need to authenticate\r\n");
return;
}
switch (client_check_source_auth (client, uri))
{
case 0: /* authenticated from config file */
source_startup (client, uri, ICECAST_SOURCE_AUTH);
break;
case 1: /* auth pending */
break;
default: /* failed */
ICECAST_LOG_INFO("Source (%s) attempted to login with invalid or missing password", uri);
client_send_error(client, 401, 1, "You need to authenticate\r\n");
break;
}
}
void source_startup (client_t *client, const char *uri, int auth_style)
static inline void source_startup (client_t *client, const char *uri)
{
source_t *source;
source = source_reserve (uri);
@ -1090,13 +918,14 @@ void source_startup (client_t *client, const char *uri, int auth_style)
return;
}
client->respcode = 200;
if (auth_style == SHOUTCAST_SOURCE_AUTH)
{
if (client->protocol == ICECAST_PROTOCOL_SHOUTCAST) {
client->respcode = 200;
/* send this non-blocking but if there is only a partial write
* then leave to header timeout */
sock_write (client->con->sock, "OK2\r\nicy-caps:11\r\n\r\n");
source->shoutcast_compat = 1;
source_client_callback (client, source);
}
else
{
} else {
refbuf_t *ok = refbuf_new (PER_CLIENT_REFBUF_SIZE);
client->respcode = 200;
snprintf (ok->data, PER_CLIENT_REFBUF_SIZE,
@ -1115,18 +944,27 @@ void source_startup (client_t *client, const char *uri, int auth_style)
}
}
/* only called for native icecast source clients */
static void _handle_source_request (client_t *client, const char *uri)
{
ICECAST_LOG_INFO("Source logging in at mountpoint \"%s\" from %s",
uri, client->con->ip);
if (uri[0] != '/')
{
ICECAST_LOG_WARN("source mountpoint not starting with /");
client_send_error(client, 400, 1, "source mountpoint not starting with /");
return;
}
source_startup(client, uri);
}
static void _handle_stats_request (client_t *client, char *uri)
{
stats_event_inc(NULL, "stats_connections");
if (connection_check_admin_pass (client->parser) == 0)
{
client_send_error(client, 401, 1, "You need to authenticate\r\n");
ICECAST_LOG_ERROR("Bad password for stats connection");
return;
}
client->respcode = 200;
snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE,
"HTTP/1.0 200 OK\r\n\r\n");
@ -1134,73 +972,173 @@ static void _handle_stats_request (client_t *client, char *uri)
fserve_add_client_callback (client, stats_callback, NULL);
}
static void _handle_get_request (client_t *client, char *passed_uri)
/* if 0 is returned then the client should not be touched, however if -1
* is returned then the caller is responsible for handling the client
*/
static int __add_listener_to_source (source_t *source, client_t *client)
{
char *serverhost = NULL;
int serverport = 0;
aliases *alias;
ice_config_t *config;
char *uri = passed_uri;
listener_t *listen_sock;
const char *http_host = httpp_getvar(client->parser, "host");
char *vhost;
char *vhost_colon;
size_t loop = 10;
config = config_get_config();
listen_sock = config_get_listen_sock (config, client->con);
if (listen_sock)
do
{
serverhost = listen_sock->bind_address;
serverport = listen_sock->port;
ICECAST_LOG_DEBUG("max on %s is %ld (cur %lu)", source->mount,
source->max_listeners, source->listeners);
if (source->max_listeners == -1)
break;
if (source->listeners < (unsigned long)source->max_listeners)
break;
if (loop && source->fallback_when_full && source->fallback_mount)
{
source_t *next = source_find_mount (source->fallback_mount);
if (!next) {
ICECAST_LOG_ERROR("Fallback '%s' for full source '%s' not found",
source->mount, source->fallback_mount);
return -1;
}
ICECAST_LOG_INFO("stream full trying %s", next->mount);
source = next;
loop--;
continue;
}
/* now we fail the client */
return -1;
} while (1);
client->write_to_client = format_generic_write_to_client;
client->check_buffer = format_check_http_buffer;
client->refbuf->len = PER_CLIENT_REFBUF_SIZE;
memset (client->refbuf->data, 0, PER_CLIENT_REFBUF_SIZE);
/* lets add the client to the active list */
avl_tree_wlock (source->pending_tree);
avl_insert (source->pending_tree, client);
avl_tree_unlock (source->pending_tree);
if (source->running == 0 && source->on_demand)
{
/* enable on-demand relay to start, wake up the slave thread */
ICECAST_LOG_DEBUG("kicking off on-demand relay");
source->on_demand_req = 1;
}
alias = config->aliases;
ICECAST_LOG_DEBUG("Added client to %s", source->mount);
return 0;
}
/* count the number of clients on a mount with same username and same role as the given one */
static inline ssize_t __count_user_role_on_mount (source_t *source, client_t *client) {
ssize_t ret = 0;
avl_node *node;
avl_tree_rlock(source->client_tree);
node = avl_get_first(source->client_tree);
while (node) {
client_t *existing_client = (client_t *)node->key;
if (existing_client->username && client->username &&
strcmp(existing_client->username, client->username) == 0 &&
existing_client->role && client->role &&
strcmp(existing_client->role, client->role) == 0)
ret++;
node = avl_get_next(node);
}
avl_tree_unlock(source->client_tree);
avl_tree_rlock(source->pending_tree);
node = avl_get_first(source->pending_tree);
while (node)
{
client_t *existing_client = (client_t *)node->key;
if (existing_client->username && client->username &&
strcmp(existing_client->username, client->username) == 0 &&
existing_client->role && client->role &&
strcmp(existing_client->role, client->role) == 0)
ret++;
node = avl_get_next(node);
}
avl_tree_unlock(source->pending_tree);
return ret;
}
static void _handle_get_request (client_t *client, char *uri) {
source_t *source = NULL;
ICECAST_LOG_DEBUG("Got client %p with URI %H", client, uri);
/* there are several types of HTTP GET clients
** media clients, which are looking for a source (eg, URI = /stream.ogg)
** stats clients, which are looking for /admin/stats.xml
** and directory server authorizers, which are looking for /GUID-xxxxxxxx
** (where xxxxxx is the GUID in question) - this isn't implemented yet.
** we need to handle the latter two before the former, as the latter two
** aren't subject to the limits.
*/
/* TODO: add GUID-xxxxxx */
/* Handle aliases */
if (http_host) {
vhost = strdup(http_host);
if (vhost) {
vhost_colon = strstr(vhost, ":");
if (vhost_colon)
*vhost_colon = 0;
}
}
while (alias) {
if(strcmp(uri, alias->source) == 0 &&
(alias->port == -1 || alias->port == serverport) &&
(alias->bind_address == NULL || (serverhost != NULL && strcmp(alias->bind_address, serverhost) == 0)) &&
(alias->vhost == NULL || (vhost != NULL && strcmp(alias->vhost, vhost) == 0)) ) {
uri = strdup (alias->destination);
ICECAST_LOG_DEBUG("alias has made %s into %s", passed_uri, uri);
break;
}
alias = alias->next;
}
if (vhost)
free(vhost);
config_release_config();
* media clients, which are looking for a source (eg, URI = /stream.ogg),
* stats clients, which are looking for /admin/stats.xml and
* fserve clients, which are looking for static files.
*/
stats_event_inc(NULL, "client_connections");
/* Dispatch all admin requests */
if ((strcmp(uri, "/admin.cgi") == 0) ||
(strncmp(uri, "/admin/", 7) == 0)) {
ICECAST_LOG_DEBUG("Client %p requesting admin interface.", client);
admin_handle_request(client, uri);
if (uri != passed_uri) free (uri);
return;
}
auth_add_listener (uri, client);
if (uri != passed_uri) free (uri);
/* this is a web/ request. let's check if we are allowed to do that. */
if (acl_test_web(client->acl) != ACL_POLICY_ALLOW) {
/* doesn't seem so, sad client :( */
if (client->protocol == ICECAST_PROTOCOL_SHOUTCAST) {
client_destroy(client);
} else {
client_send_error(client, 401, 1, "You need to authenticate\r\n");
}
return;
}
if (util_check_valid_extension(uri) == XSLT_CONTENT) {
/* If the file exists, then transform it, otherwise, write a 404 */
ICECAST_LOG_DEBUG("Stats request, sending XSL transformed stats");
stats_transform_xslt(client, uri);
return;
}
avl_tree_rlock(global.source_tree);
/* let's see if this is a source or just a random fserve file */
source = source_find_mount(uri);
if (source) {
/* true mount */
int in_error = 0;
ssize_t max_connections_per_user = acl_get_max_connections_per_user(client->acl);
/* check for duplicate_logins */
if (max_connections_per_user > 0) { /* -1 = not set (-> default=unlimited), 0 = unlimited */
if (max_connections_per_user <= __count_user_role_on_mount(source, client)) {
client_send_error(client, 403, 1, "Reached limit of concurrent connections on those credentials");
in_error = 1;
}
}
/* Set max listening duration in case not already set. */
if (!in_error && client->con->discon_time == 0) {
time_t connection_duration = acl_get_max_connection_duration(client->acl);
if (connection_duration == -1) {
ice_config_t *config = config_get_config();
mount_proxy *mount = config_find_mount(config, source->mount, MOUNT_TYPE_NORMAL);
if (mount && mount->max_listener_duration)
connection_duration = mount->max_listener_duration;
config_release_config();
}
if (connection_duration > 0) /* -1 = not set (-> default=unlimited), 0 = unlimited */
client->con->discon_time = connection_duration + time(NULL);
}
if (!in_error && __add_listener_to_source(source, client) == -1) {
client_send_error(client, 403, 1, "Rejecting client for whatever reason");
}
avl_tree_unlock(global.source_tree);
} else {
/* file */
avl_tree_unlock(global.source_tree);
fserve_client_create(client, uri);
}
}
static void _handle_shoutcast_compatible (client_queue_t *node)
@ -1208,30 +1146,13 @@ static void _handle_shoutcast_compatible (client_queue_t *node)
char *http_compliant;
int http_compliant_len = 0;
http_parser_t *parser;
ice_config_t *config = config_get_config ();
char *shoutcast_mount;
const char *shoutcast_mount;
client_t *client = node->client;
if (node->shoutcast_mount)
shoutcast_mount = node->shoutcast_mount;
else
shoutcast_mount = config->shoutcast_mount;
ice_config_t *config;
if (node->shoutcast == 1)
{
char *source_password, *ptr, *headers;
mount_proxy *mountinfo = config_find_mount (config, shoutcast_mount, MOUNT_TYPE_NORMAL);
if (mountinfo && mountinfo->password)
source_password = strdup (mountinfo->password);
else
{
if (config->source_password)
source_password = strdup (config->source_password);
else
source_password = NULL;
}
config_release_config();
char *ptr, *headers;
/* Get rid of trailing \r\n or \n after password */
ptr = strstr (client->refbuf->data, "\r\r\n");
@ -1253,45 +1174,36 @@ static void _handle_shoutcast_compatible (client_queue_t *node)
if (ptr == NULL)
{
client_destroy (client);
free (source_password);
free (node->shoutcast_mount);
free (node);
return;
}
*ptr = '\0';
if (source_password && strcmp (client->refbuf->data, source_password) == 0)
{
client->respcode = 200;
/* send this non-blocking but if there is only a partial write
* then leave to header timeout */
sock_write (client->con->sock, "OK2\r\nicy-caps:11\r\n\r\n");
node->offset -= (headers - client->refbuf->data);
memmove (client->refbuf->data, headers, node->offset+1);
node->shoutcast = 2;
/* we've checked the password, now send it back for reading headers */
_add_request_queue (node);
free (source_password);
return;
}
else
ICECAST_LOG_INFO("password does not match \"%s\"", client->refbuf->data);
client_destroy (client);
free (source_password);
free (node->shoutcast_mount);
free (node);
client->password = strdup(client->refbuf->data);
node->offset -= (headers - client->refbuf->data);
memmove (client->refbuf->data, headers, node->offset+1);
node->shoutcast = 2;
/* we've checked the password, now send it back for reading headers */
_add_request_queue (node);
return;
}
/* actually make a copy as we are dropping the config lock */
shoutcast_mount = strdup (shoutcast_mount);
config_release_config();
/* Here we create a valid HTTP request based of the information
that was passed in via the non-HTTP style protocol above. This
means we can use some of our existing code to handle this case */
http_compliant_len = 20 + strlen (shoutcast_mount) + node->offset;
config = config_get_config();
if (node->shoutcast_mount) {
shoutcast_mount = node->shoutcast_mount;
} else {
shoutcast_mount = config->shoutcast_mount;
}
http_compliant_len = 20 + strlen(shoutcast_mount) + node->offset;
http_compliant = (char *)calloc(1, http_compliant_len);
snprintf (http_compliant, http_compliant_len,
snprintf(http_compliant, http_compliant_len,
"SOURCE %s HTTP/1.0\r\n%s", shoutcast_mount, client->refbuf->data);
config_release_config();
parser = httpp_create_parser();
httpp_initialize(parser, NULL);
if (httpp_parse (parser, http_compliant, strlen(http_compliant)))
@ -1306,19 +1218,222 @@ static void _handle_shoutcast_compatible (client_queue_t *node)
memmove (ptr, ptr + node->stream_offset, client->refbuf->len);
}
client->parser = parser;
source_startup (client, shoutcast_mount, SHOUTCAST_SOURCE_AUTH);
client->protocol = ICECAST_PROTOCOL_SHOUTCAST;
node->shoutcast = 0;
return;
}
else {
httpp_destroy (parser);
client_destroy (client);
}
free (http_compliant);
free (shoutcast_mount);
free (node->shoutcast_mount);
free (node);
return;
}
/* Handle <alias> lookups here.
*/
static int _handle_aliases(client_t *client, char **uri) {
const char *http_host = httpp_getvar(client->parser, "host");
char *serverhost = NULL;
int serverport = 0;
char *vhost;
char *vhost_colon;
char *new_uri = NULL;
ice_config_t *config;
listener_t *listen_sock;
aliases *alias;
if (http_host) {
vhost = strdup(http_host);
if (vhost) {
vhost_colon = strstr(vhost, ":");
if (vhost_colon)
*vhost_colon = 0;
}
}
config = config_get_config();
listen_sock = config_get_listen_sock (config, client->con);
if (listen_sock)
{
serverhost = listen_sock->bind_address;
serverport = listen_sock->port;
}
alias = config->aliases;
while (alias) {
if(strcmp(*uri, alias->source) == 0 &&
(alias->port == -1 || alias->port == serverport) &&
(alias->bind_address == NULL || (serverhost != NULL && strcmp(alias->bind_address, serverhost) == 0)) &&
(alias->vhost == NULL || (vhost != NULL && strcmp(alias->vhost, vhost) == 0)) ) {
new_uri = strdup(alias->destination);
ICECAST_LOG_DEBUG("alias has made %s into %s", *uri, new_uri);
break;
}
alias = alias->next;
}
config_release_config();
if (new_uri) {
free(*uri);
*uri = new_uri;
}
if (vhost)
free(vhost);
return 0;
}
/* Handle any client that passed the authing process.
*/
static void _handle_authed_client(client_t *client, void *uri, auth_result result) {
auth_stack_release(client->authstack);
client->authstack = NULL;
if (result != AUTH_OK) {
ICECAST_LOG_ERROR("Client not authenticated at all! (uri is: %H)", uri);
client_send_error(client, 401, 1, "You need to authenticate\r\n");
free(uri);
return;
}
if (acl_test_method(client->acl, client->parser->req_type) != ACL_POLICY_ALLOW) {
ICECAST_LOG_ERROR("Client (role=%s, username=%s) not allowed to use this request method on %H", client->role, client->username, uri);
client_send_error(client, 401, 1, "You need to authenticate\r\n");
free(uri);
return;
}
switch (client->parser->req_type) {
case httpp_req_source:
case httpp_req_put:
_handle_source_request(client, uri);
break;
case httpp_req_stats:
_handle_stats_request(client, uri);
break;
case httpp_req_get:
_handle_get_request(client, uri);
break;
default:
ICECAST_LOG_ERROR("Wrong request type from client");
client_send_error(client, 400, 0, "unknown request");
break;
}
free(uri);
}
/* Handle clients that still need to authenticate.
*/
static void _handle_authentication_global(client_t *client, void *uri, auth_result result) {
ice_config_t *config;
auth_stack_release(client->authstack);
client->authstack = NULL;
if (result != AUTH_NOMATCH) {
_handle_authed_client(client, uri, result);
return;
}
config = config_get_config();
auth_stack_add_client(config->authstack, client, _handle_authed_client, uri);
config_release_config();
}
static inline mount_proxy * __find_non_admin_mount(ice_config_t *config, const char *name, mount_type type) {
if (strcmp(name, "/admin.cgi") == 0 || strncmp(name, "/admin/", 7) == 0)
return NULL;
return config_find_mount(config, name, type);
}
static void _handle_authentication_mount_generic(client_t *client, void *uri, mount_type type, void (*callback)(client_t*, void*, auth_result)) {
ice_config_t *config;
mount_proxy *mountproxy;
auth_stack_t *stack = NULL;
config = config_get_config();
mountproxy = __find_non_admin_mount(config, uri, type);
if (!mountproxy) {
int command_type = admin_get_command_type(client->admin_command);
if (command_type == ADMINTYPE_MOUNT || command_type == ADMINTYPE_HYBRID) {
const char *mount = httpp_get_query_param(client->parser, "mount");
if (mount)
mountproxy = __find_non_admin_mount(config, mount, type);
}
}
if (mountproxy && mountproxy->mounttype == type)
stack = mountproxy->authstack;
auth_stack_addref(stack);
config_release_config();
if (stack) {
auth_stack_add_client(stack, client, callback, uri);
auth_stack_release(stack);
} else {
callback(client, uri, AUTH_NOMATCH);
}
}
static void _handle_authentication_mount_default(client_t *client, void *uri, auth_result result) {
auth_stack_release(client->authstack);
client->authstack = NULL;
if (result != AUTH_NOMATCH) {
_handle_authed_client(client, uri, result);
return;
}
_handle_authentication_mount_generic(client, uri, MOUNT_TYPE_DEFAULT, _handle_authentication_global);
}
static void _handle_authentication_mount_normal(client_t *client, char *uri) {
_handle_authentication_mount_generic(client, uri, MOUNT_TYPE_NORMAL, _handle_authentication_mount_default);
}
static void _handle_authentication(client_t *client, char *uri) {
_handle_authentication_mount_normal(client, uri);
}
static void __prepare_shoutcast_admin_cgi_request(client_t *client) {
ice_config_t *config;
const char *sc_mount;
const char *pass = httpp_get_query_param(client->parser, "pass");
listener_t *listener;
if (pass == NULL) {
ICECAST_LOG_ERROR("missing pass parameter");
return;
}
if (client->password) {
ICECAST_LOG_INFO("Client already has password set");
return;
}
global_lock();
config = config_get_config();
sc_mount = config->shoutcast_mount;
listener = config_get_listen_sock(config, client->con);
if (listener && listener->shoutcast_mount)
sc_mount = listener->shoutcast_mount;
httpp_set_query_param(client->parser, "mount", sc_mount);
httpp_setvar(client->parser, HTTPP_VAR_PROTOCOL, "ICY");
client->password = strdup(pass);
config_release_config();
global_unlock();
}
/* Connection thread. Here we take clients off the connection queue and check
* the contents provided. We set up the parser then hand off to the specific
@ -1336,19 +1451,26 @@ static void _handle_connection(void)
if (node)
{
client_t *client = node->client;
int already_parsed = 0;
/* Check for special shoutcast compatability processing */
if (node->shoutcast)
{
_handle_shoutcast_compatible (node);
continue;
if (node->shoutcast)
continue;
}
/* process normal HTTP headers */
parser = httpp_create_parser();
httpp_initialize(parser, NULL);
client->parser = parser;
if (httpp_parse (parser, client->refbuf->data, node->offset))
if (client->parser) {
already_parsed = 1;
parser = client->parser;
} else {
parser = httpp_create_parser();
httpp_initialize(parser, NULL);
client->parser = parser;
}
if (already_parsed || httpp_parse (parser, client->refbuf->data, node->offset))
{
char *uri;
@ -1380,27 +1502,28 @@ static void _handle_connection(void)
uri = util_normalise_uri(rawuri);
if (uri == NULL)
{
if (!uri) {
client_destroy (client);
continue;
}
if (parser->req_type == httpp_req_source || parser->req_type == httpp_req_put) {
_handle_source_request (client, uri);
}
else if (parser->req_type == httpp_req_stats) {
_handle_stats_request (client, uri);
}
else if (parser->req_type == httpp_req_get) {
_handle_get_request (client, uri);
}
else {
ICECAST_LOG_ERROR("Wrong request type from client");
client_send_error(client, 400, 0, "unknown request");
if (_handle_aliases(client, &uri) != 0) {
client_destroy (client);
continue;
}
free(uri);
if (strcmp(uri, "/admin.cgi") == 0) {
client->admin_command = admin_get_command(uri + 1);
__prepare_shoutcast_admin_cgi_request(client);
if (!client->password) {
client_send_error(client, 400, 0, "missing pass parameter");
continue;
}
} else if (strncmp("/admin/", uri, 7) == 0) {
client->admin_command = admin_get_command(uri + 7);
}
_handle_authentication(client, uri);
}
else
{

View File

@ -8,6 +8,7 @@
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@xiph.org>
* and others (see AUTHORS for details).
* Copyright 2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
#ifndef __CONNECTION_H__
@ -60,10 +61,6 @@ 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);
int connection_check_pass (http_parser_t *parser, const char *user, const char *pass);
int connection_check_relay_pass(http_parser_t *parser);
int connection_check_admin_pass(http_parser_t *parser);
extern rwlock_t _source_shutdown_rwlock;
#endif /* __CONNECTION_H__ */

View File

@ -8,12 +8,14 @@
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@xiph.org>
* and others (see AUTHORS for details).
* Copyright 2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
#ifndef __LOGGING_H__
#define __LOGGING_H__
#include "cfgfile.h"
#include "client.h"
#include "log/log.h"
/* declare the global log descriptors */
@ -76,4 +78,3 @@ void restart_logging (ice_config_t *config);
void log_parse_failure (void *ctx, const char *fmt, ...);
#endif /* __LOGGING_H__ */

View File

@ -8,6 +8,7 @@
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@xiph.org>
* and others (see AUTHORS for details).
* Copyright 2012-2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
@ -677,7 +678,8 @@ static void source_init (source_t *source)
{
if (mountinfo->on_connect)
source_run_script (mountinfo->on_connect, source, mountinfo, "source-connect");
auth_stream_start (mountinfo, source->mount);
/* TODO: replace with <event> */
/* auth_stream_start (mountinfo, source->mount); */
}
config_release_config();
@ -885,7 +887,8 @@ static void source_shutdown (source_t *source)
{
if (mountinfo->on_disconnect)
source_run_script (mountinfo->on_disconnect, source, mountinfo, "source-disconnect");
auth_stream_end (mountinfo, source->mount);
/* TODO: replace with <event> */
/* auth_stream_end (mountinfo, source->mount); */
}
config_release_config();
@ -988,11 +991,12 @@ static void _parse_audio_info (source_t *source, const char *s)
/* Apply the mountinfo details to the source */
static void source_apply_mount (source_t *source, mount_proxy *mountinfo)
static void source_apply_mount (ice_config_t *config, source_t *source, mount_proxy *mountinfo)
{
const char *str;
int val;
http_parser_t *parser = NULL;
acl_t *acl = NULL;
ICECAST_LOG_DEBUG("Applying mount information for \"%s\"", source->mount);
avl_tree_rlock (source->client_tree);
@ -1141,10 +1145,15 @@ static void source_apply_mount (source_t *source, mount_proxy *mountinfo)
if (mountinfo && mountinfo->subtype)
stats_event (source->mount, "subtype", mountinfo->subtype);
if (mountinfo && mountinfo->auth)
stats_event (source->mount, "authenticator", mountinfo->auth->type);
if (mountinfo)
acl = auth_stack_get_anonymous_acl(mountinfo->authstack);
if (!acl)
auth_stack_get_anonymous_acl(config->authstack);
if (acl && acl_test_web(acl) == ACL_POLICY_DENY)
stats_event (source->mount, "authenticator", "(dummy)");
else
stats_event (source->mount, "authenticator", NULL);
acl_release(acl);
if (mountinfo && mountinfo->fallback_mount)
{
@ -1228,7 +1237,7 @@ void source_update_settings (ice_config_t *config, source_t *source, mount_proxy
stats_event_args (source->mount, "listenurl", "http://%s:%d%s",
config->hostname, config->port, source->mount);
source_apply_mount (source, mountinfo);
source_apply_mount (config, source, mountinfo);
if (source->fallback_mount)
ICECAST_LOG_DEBUG("fallback %s", source->fallback_mount);
@ -1533,4 +1542,3 @@ void source_recheck_mounts (int update_all)
avl_tree_unlock (global.source_tree);
config_release_config();
}

View File

@ -8,6 +8,7 @@
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@xiph.org>
* and others (see AUTHORS for details).
* Copyright 2012-2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
#ifndef __SOURCE_H__
@ -82,7 +83,6 @@ typedef struct source_tag
source_t *source_reserve (const char *mount);
void *source_client_thread (void *arg);
void source_startup (client_t *client, const char *uri, int auth_style);
void source_client_callback (client_t *client, void *source);
void source_update_settings (ice_config_t *config, source_t *source, mount_proxy *mountinfo);
void source_clear_source (source_t *source);
@ -99,5 +99,3 @@ void source_recheck_mounts (int update_all);
extern mutex_t move_clients_mutex;
#endif