Commit 15b3a5f8 authored by Karl Heyes's avatar Karl Heyes

Initial auth merge. Add an auth thread (multiple threads can be done later)

which can be used to handle authentication mechanisms without taking locks
for long periods.  Non-authenticated mountpoints bypass the auth thread.

The lookup/checking of the source_t is done after the authentication succeeds
so the fallback mechanism does not affect which authenticator is used. This
can be extended to allow us to authenticate in webroot as well. XML re-read
changes will take effect immediately for new listeners but existing listeners
will use the original auth_t (refcounted) when they exit.

htpasswd access has been seperated out from auth.c, and implements an AVL
tree for a faster username lookup.  The htpasswd file timestamp is checked
just in case there are changes made externally

svn path=/icecast/trunk/icecast/; revision=9713
parent 33cf86f5
......@@ -7,14 +7,16 @@ SUBDIRS = avl thread httpp net log timing
bin_PROGRAMS = icecast
noinst_HEADERS = admin.h cfgfile.h os.h logging.h sighandler.h connection.h \
global.h util.h slave.h source.h stats.h refbuf.h client.h format.h \
compat.h format_mp3.h fserve.h xslt.h yp.h event.h md5.h \
auth.h format_ogg.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 \
format.h format_ogg.h format_mp3.h \
format_vorbis.h format_theora.h format_flac.h format_speex.h format_midi.h
icecast_SOURCES = cfgfile.c main.c logging.c sighandler.c connection.c global.c \
util.c slave.c source.c stats.c refbuf.c client.c \
xslt.c fserve.c event.c admin.c auth.c md5.c \
format.c format_ogg.c format_mp3.c format_midi.c format_flac.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 \
auth.c auth_htpasswd.c
EXTRA_icecast_SOURCES = yp.c \
format_vorbis.c format_theora.c format_speex.c
......
......@@ -229,6 +229,9 @@ xmlDocPtr admin_build_sourcelist (const char *mount)
if (source->running || source->on_demand)
{
ice_config_t *config;
mount_proxy *mountinfo;
srcnode = xmlNewChild(xmlnode, NULL, "source", NULL);
xmlSetProp(srcnode, "mount", source->mount);
......@@ -237,6 +240,16 @@ xmlDocPtr admin_build_sourcelist (const char *mount)
source->fallback_mount:"");
snprintf (buf, sizeof(buf), "%lu", source->listeners);
xmlNewChild(srcnode, NULL, "listeners", buf);
config = config_get_config();
mountinfo = config_find_mount (config, source->mount);
if (mountinfo && mountinfo->auth)
{
xmlNewChild(srcnode, NULL, "authenticator",
mountinfo->auth->type);
}
config_release_config();
if (source->running)
{
snprintf (buf, sizeof(buf), "%lu",
......@@ -245,10 +258,6 @@ xmlDocPtr admin_build_sourcelist (const char *mount)
xmlNewChild (srcnode, NULL, "content-type",
source->format->contenttype);
}
if (source->authenticator) {
xmlNewChild(srcnode, NULL, "authenticator",
source->authenticator->type);
}
}
node = avl_get_next(node);
}
......@@ -707,12 +716,21 @@ static void command_manageauth(client_t *client, source_t *source,
char *password = NULL;
char *message = NULL;
int ret = AUTH_OK;
ice_config_t *config = config_get_config ();
mount_proxy *mountinfo = config_find_mount (config, source->mount);
if((COMMAND_OPTIONAL(client, "action", action))) {
if (mountinfo == NULL || mountinfo->auth == NULL)
{
WARN1 ("manage auth request for %s but no facility available", source->mount);
config_release_config ();
client_send_404 (client, "no such auth facility");
return;
}
if (!strcmp(action, "add")) {
COMMAND_REQUIRE(client, "username", username);
COMMAND_REQUIRE(client, "password", password);
ret = auth_adduser(source, username, password);
ret = mountinfo->auth->adduser(mountinfo->auth, username, password);
if (ret == AUTH_FAILED) {
message = strdup("User add failed - check the icecast error log");
}
......@@ -725,7 +743,7 @@ static void command_manageauth(client_t *client, source_t *source,
}
if (!strcmp(action, "delete")) {
COMMAND_REQUIRE(client, "username", username);
ret = auth_deleteuser(source, username);
ret = mountinfo->auth->deleteuser(mountinfo->auth, username);
if (ret == AUTH_FAILED) {
message = strdup("User delete failed - check the icecast error log");
}
......@@ -747,7 +765,10 @@ static void command_manageauth(client_t *client, source_t *source,
xmlDocSetRootElement(doc, node);
auth_get_userlist(source, srcnode);
if (mountinfo && mountinfo->auth && mountinfo->auth->listuser)
mountinfo->auth->listuser (mountinfo->auth, srcnode);
config_release_config ();
admin_send_response(doc, client, response,
MANAGEAUTH_TRANSFORMED_REQUEST);
......
This diff is collapsed.
......@@ -17,14 +17,19 @@
#include <config.h>
#endif
struct source_tag;
struct auth_tag;
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include "source.h"
#include "cfgfile.h"
#include "client.h"
#include "thread/thread.h"
typedef enum
{
AUTH_UNDEFINED,
AUTH_OK,
AUTH_FAILED,
AUTH_FORBIDDEN,
......@@ -33,23 +38,57 @@ typedef enum
AUTH_USERDELETED,
} auth_result;
typedef struct auth_client_tag
{
char *mount;
client_t *client;
void (*process)(struct auth_client_tag *auth_user);
struct auth_client_tag *next;
} auth_client;
typedef struct auth_tag
{
char *mount;
/* Authenticate using the given username and password */
auth_result (*authenticate)(struct auth_tag *self,
source_t *source, char *username, char *password);
auth_result (*authenticate)(auth_client *aclient);
auth_result (*release_client)(auth_client *auth_user);
/* callbacks to specific auth for notifying auth server on source
* startup or shutdown
*/
void (*stream_start)(auth_client *auth_user);
void (*stream_end)(auth_client *auth_user);
void (*free)(struct auth_tag *self);
auth_result (*adduser)(struct auth_tag *auth, const char *username, const char *password);
auth_result (*deleteuser)(struct auth_tag *auth, const char *username);
auth_result (*listuser)(struct auth_tag *auth, xmlNodePtr srcnode);
int refcount;
int allow_duplicate_users;
void *state;
char *type;
} auth_t;
auth_result auth_check_client(source_t *source, client_t *client);
void add_client (const char *mount, client_t *client);
int release_client (client_t *client);
void auth_initialise ();
auth_t *auth_get_authenticator (xmlNodePtr node);
void auth_release (auth_t *authenticator);
/* call to send a url request when source starts */
void auth_stream_start (struct _mount_proxy *mountinfo, const char *mount);
/* call to send a url request when source ends */
void auth_stream_end (struct _mount_proxy *mountinfo, const char *mount);
auth_t *auth_get_authenticator(char *type, config_options_t *options);
void *auth_clear(auth_t *authenticator);
int auth_get_userlist(source_t *source, xmlNodePtr srcnode);
int auth_adduser(source_t *source, char *username, char *password);
int auth_deleteuser(source_t *source, char *username);
/* called from auth thread, after the client has successfully authenticated
* and requires adding to source or fserve. */
int auth_postprocess_client (auth_client *auth_user);
#endif
......
This diff is collapsed.
/* Icecast
*
* This program is distributed under the GNU General Public License, version 2.
* A copy of this license is included with this source.
*
* Copyright 2000-2004, Jack Moffitt <jack@xiph.org,
* Michael Smith <msmith@xiph.org>,
* oddsock <oddsock@xiph.org>,
* Karl Heyes <karl@xiph.org>
* and others (see AUTHORS for details).
*/
#ifndef __AUTH_HTPASSWD_H__
#define __AUTH_HTPASSWD_H__
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
void auth_get_htpasswd_auth (auth_t *auth, config_options_t *options);
#endif
......@@ -522,8 +522,6 @@ 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;
xmlNodePtr option;
config_options_t *last_option;
while(current) {
last = current;
......@@ -601,35 +599,7 @@ static void _parse_mount(xmlDocPtr doc, xmlNodePtr node,
if(tmp) xmlFree(tmp);
}
else if (strcmp(node->name, "authentication") == 0) {
mount->auth_type = xmlGetProp(node, "type");
option = node->xmlChildrenNode;
last_option = NULL;
while(option != NULL) {
if(strcmp(option->name, "option") == 0) {
config_options_t *opt = malloc(sizeof(config_options_t));
opt->name = xmlGetProp(option, "name");
if(!opt->name) {
free(opt);
option = option->next;
continue;
}
opt->value = xmlGetProp(option, "value");
if(!opt->value) {
free(opt->name);
free(opt);
option = option->next;
continue;
}
opt->next = NULL;
if(last_option)
last_option->next = opt;
else
mount->auth_options = opt;
last_option = opt;
}
option = option->next;
}
mount->auth = auth_get_authenticator (node);
}
else if (strcmp(node->name, "on-connect") == 0) {
mount->on_connect = (char *)xmlNodeListGetString(
......
......@@ -20,9 +20,11 @@
#define MAX_YP_DIRECTORIES 25
struct _mount_proxy;
#include "thread/thread.h"
#include "avl/avl.h"
#include "auth.h"
#include "global.h"
typedef struct ice_config_dir_tag
......@@ -63,6 +65,7 @@ typedef struct _mount_proxy {
int mp3_meta_interval; /* outgoing per-stream metadata interval */
char *auth_type; /* Authentication type */
struct auth_tag *auth;
char *cluster_password;
config_options_t *auth_options; /* Options for this type */
char *on_connect;
......
......@@ -72,6 +72,10 @@ void client_destroy(client_t *client)
{
if (client == NULL)
return;
if (release_client (client))
return;
/* write log entry if ip is set (some things don't set it, like outgoing
* slave requests
*/
......
......@@ -32,6 +32,9 @@ typedef struct _client_tag
/* http response code for this client */
int respcode;
/* auth completed, 0 not yet, 1 passed */
int authenticated;
/* is client getting intro data */
long intro_offset;
......@@ -41,9 +44,15 @@ typedef struct _client_tag
/* position in first buffer */
unsigned long pos;
/* 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;
......
......@@ -758,7 +758,6 @@ static void _handle_stats_request (client_t *client, char *uri)
static void _handle_get_request (client_t *client, char *passed_uri)
{
source_t *source;
int fileserve;
int port;
int i;
......@@ -766,7 +765,6 @@ static void _handle_get_request (client_t *client, char *passed_uri)
int serverport = 0;
aliases *alias;
ice_config_t *config;
int ret;
char *uri = passed_uri;
config = config_get_config();
......@@ -831,95 +829,14 @@ static void _handle_get_request (client_t *client, char *passed_uri)
return;
}
avl_tree_rlock(global.source_tree);
source = source_find_mount(uri);
if (source) {
DEBUG0("Source found for client");
/* The source may not be the requested source - it might have gone
* via one or more fallbacks. We only reject it for no-mount if it's
* the originally requested source
*/
if(strcmp(uri, source->mount) == 0 && source->no_mount) {
avl_tree_unlock(global.source_tree);
client_send_404(client, "This mount is unavailable.");
if (uri != passed_uri) free (uri);
return;
}
if (source->running == 0 && source->on_demand == 0)
{
avl_tree_unlock(global.source_tree);
DEBUG0("inactive source, client dropped");
client_send_404(client, "This mount is unavailable.");
if (uri != passed_uri) free (uri);
return;
}
sock_set_blocking(client->con->sock, SOCK_NONBLOCK);
sock_set_nodelay(client->con->sock);
/* Check for any required authentication first */
if(source->authenticator != NULL) {
ret = auth_check_client(source, client);
if(ret != AUTH_OK) {
avl_tree_unlock(global.source_tree);
if (ret == AUTH_FORBIDDEN) {
INFO1("Client attempted to log multiple times to source "
"(\"%s\")", uri);
client_send_403(client);
}
else {
/* If not FORBIDDEN, default to 401 */
INFO1("Client attempted to log in to source (\"%s\")with "
"incorrect or missing password", uri);
client_send_401(client);
}
if (uri != passed_uri) free (uri);
return;
}
}
client->write_to_client = format_generic_write_to_client;
client->check_buffer = format_check_http_buffer;
client->refbuf = refbuf_new (PER_CLIENT_REFBUF_SIZE);
global_lock();
/* Early-out for per-source max listeners. This gets checked again
* by the source itself, later. This route gives a useful message to
* the client, also.
*/
if (source->max_listeners != -1 &&
source->listeners >= (unsigned long)source->max_listeners)
{
global_unlock();
avl_tree_unlock(global.source_tree);
client_send_404(client,
"Too many clients on this mountpoint. Try again later.");
if (uri != passed_uri) free (uri);
return;
}
global_unlock();
sock_set_blocking(client->con->sock, SOCK_NONBLOCK);
sock_set_nodelay(client->con->sock);
client->write_to_client = format_generic_write_to_client;
client->check_buffer = format_check_http_buffer;
client->refbuf = refbuf_new (PER_CLIENT_REFBUF_SIZE);
avl_tree_wlock(source->pending_tree);
avl_insert(source->pending_tree, (void *)client);
avl_tree_unlock(source->pending_tree);
stats_event_inc (NULL, "listener_connections");
if (source->running == 0 && source->on_demand)
{
/* enable on-demand relay to start, wake up the slave thread */
DEBUG0("kicking off on-demand relay");
source->on_demand_req = 1;
slave_rescan ();
}
}
avl_tree_unlock(global.source_tree);
if (!source) {
DEBUG0("Source not found for client");
client_send_404(client, "The source you requested could not be found.");
}
add_client (uri, client);
if (uri != passed_uri) free (uri);
}
......
......@@ -505,6 +505,7 @@ int main(int argc, char **argv)
/* Do this after logging init */
slave_initialize();
auth_initialise ();
_server_proc();
......
......@@ -245,7 +245,6 @@ void source_clear_source (source_t *source)
source->queue_size = 0;
source->queue_size_limit = 0;
source->listeners = 0;
source->no_mount = 0;
source->shoutcast_compat = 0;
source->max_listeners = -1;
source->hidden = 0;
......@@ -614,6 +613,7 @@ static void source_init (source_t *source)
{
if (mountinfo->on_connect)
source_run_script (mountinfo->on_connect, source->mount);
auth_stream_start (mountinfo, source->mount);
}
config_release_config();
......@@ -817,6 +817,7 @@ static void source_shutdown (source_t *source)
{
if (mountinfo->on_disconnect)
source_run_script (mountinfo->on_disconnect, source->mount);
auth_stream_end (mountinfo, source->mount);
}
config_release_config();
......@@ -930,7 +931,6 @@ static void source_apply_mount (source_t *source, mount_proxy *mountinfo)
{
source->max_listeners = mountinfo->max_listeners;
source->fallback_override = mountinfo->fallback_override;
source->no_mount = mountinfo->no_mount;
source->hidden = mountinfo->hidden;
}
......@@ -1062,6 +1062,11 @@ 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);
else
stats_event (source->mount, "authenticator", NULL);
if (mountinfo && mountinfo->fallback_mount)
{
char *mount = source->fallback_mount;
......@@ -1071,13 +1076,6 @@ static void source_apply_mount (source_t *source, mount_proxy *mountinfo)
else
source->fallback_mount = NULL;
if (mountinfo && mountinfo->auth_type != NULL && source->authenticator == NULL)
{
source->authenticator = auth_get_authenticator(
mountinfo->auth_type, mountinfo->auth_options);
stats_event(source->mount, "authenticator", mountinfo->auth_type);
}
if (mountinfo && mountinfo->dumpfile)
{
char *filename = source->dumpfilename;
......
......@@ -20,8 +20,6 @@
#include <stdio.h>
struct auth_tag;
typedef struct source_tag
{
client_t *client;
......@@ -55,9 +53,7 @@ typedef struct source_tag
unsigned long listeners;
long max_listeners;
int yp_public;
struct auth_tag *authenticator;
int fallback_override;
int no_mount;
int shoutcast_compat;
/* per source burst handling for connecting clients */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment