1
0
mirror of https://gitlab.xiph.org/xiph/icecast-server.git synced 2024-09-22 04:15:54 -04:00

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
This commit is contained in:
Karl Heyes 2005-08-07 23:01:04 +00:00
parent 33cf86f527
commit 15b3a5f853
14 changed files with 1013 additions and 572 deletions

View File

@ -7,14 +7,16 @@ SUBDIRS = avl thread httpp net log timing
bin_PROGRAMS = icecast bin_PROGRAMS = icecast
noinst_HEADERS = admin.h cfgfile.h os.h logging.h sighandler.h connection.h \ 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 \ global.h util.h slave.h source.h stats.h refbuf.h client.h \
compat.h format_mp3.h fserve.h xslt.h yp.h event.h md5.h \ compat.h fserve.h xslt.h yp.h event.h md5.h \
auth.h format_ogg.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 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 \ 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 \ util.c slave.c source.c stats.c refbuf.c client.c \
xslt.c fserve.c event.c admin.c auth.c md5.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.c format_ogg.c format_mp3.c format_midi.c format_flac.c \
auth.c auth_htpasswd.c
EXTRA_icecast_SOURCES = yp.c \ EXTRA_icecast_SOURCES = yp.c \
format_vorbis.c format_theora.c format_speex.c format_vorbis.c format_theora.c format_speex.c

View File

@ -229,6 +229,9 @@ xmlDocPtr admin_build_sourcelist (const char *mount)
if (source->running || source->on_demand) if (source->running || source->on_demand)
{ {
ice_config_t *config;
mount_proxy *mountinfo;
srcnode = xmlNewChild(xmlnode, NULL, "source", NULL); srcnode = xmlNewChild(xmlnode, NULL, "source", NULL);
xmlSetProp(srcnode, "mount", source->mount); xmlSetProp(srcnode, "mount", source->mount);
@ -237,6 +240,16 @@ xmlDocPtr admin_build_sourcelist (const char *mount)
source->fallback_mount:""); source->fallback_mount:"");
snprintf (buf, sizeof(buf), "%lu", source->listeners); snprintf (buf, sizeof(buf), "%lu", source->listeners);
xmlNewChild(srcnode, NULL, "listeners", buf); 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) if (source->running)
{ {
snprintf (buf, sizeof(buf), "%lu", snprintf (buf, sizeof(buf), "%lu",
@ -245,10 +258,6 @@ xmlDocPtr admin_build_sourcelist (const char *mount)
xmlNewChild (srcnode, NULL, "content-type", xmlNewChild (srcnode, NULL, "content-type",
source->format->contenttype); source->format->contenttype);
} }
if (source->authenticator) {
xmlNewChild(srcnode, NULL, "authenticator",
source->authenticator->type);
}
} }
node = avl_get_next(node); node = avl_get_next(node);
} }
@ -707,12 +716,21 @@ static void command_manageauth(client_t *client, source_t *source,
char *password = NULL; char *password = NULL;
char *message = NULL; char *message = NULL;
int ret = AUTH_OK; 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((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")) { if (!strcmp(action, "add")) {
COMMAND_REQUIRE(client, "username", username); COMMAND_REQUIRE(client, "username", username);
COMMAND_REQUIRE(client, "password", password); COMMAND_REQUIRE(client, "password", password);
ret = auth_adduser(source, username, password); ret = mountinfo->auth->adduser(mountinfo->auth, username, password);
if (ret == AUTH_FAILED) { if (ret == AUTH_FAILED) {
message = strdup("User add failed - check the icecast error log"); 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")) { if (!strcmp(action, "delete")) {
COMMAND_REQUIRE(client, "username", username); COMMAND_REQUIRE(client, "username", username);
ret = auth_deleteuser(source, username); ret = mountinfo->auth->deleteuser(mountinfo->auth, username);
if (ret == AUTH_FAILED) { if (ret == AUTH_FAILED) {
message = strdup("User delete failed - check the icecast error log"); 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); 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, admin_send_response(doc, client, response,
MANAGEAUTH_TRANSFORMED_REQUEST); MANAGEAUTH_TRANSFORMED_REQUEST);

View File

@ -22,487 +22,534 @@
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "auth.h" #include "auth.h"
#include "auth_htpasswd.h"
#include "source.h" #include "source.h"
#include "client.h" #include "client.h"
#include "cfgfile.h" #include "cfgfile.h"
#include "stats.h"
#include "httpp/httpp.h" #include "httpp/httpp.h"
#include "md5.h"
#include "logging.h" #include "logging.h"
#define CATMODULE "auth" #define CATMODULE "auth"
#ifdef _WIN32
#define snprintf _snprintf
#endif
int auth_is_listener_connected(source_t *source, char *username)
static volatile auth_client *clients_to_auth;
static volatile unsigned int auth_pending_count;
static volatile int auth_running;
static mutex_t auth_lock;
static thread_type *auth_thread;
static void auth_client_setup (mount_proxy *mountinfo, client_t *client)
{ {
client_t *client; /* This will look something like "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" */
avl_node *client_node; char *header = httpp_getvar(client->parser, "authorization");
char *userpass, *tmp;
char *username, *password;
avl_tree_rlock(source->client_tree); do
{
if (header == NULL)
break;
client_node = avl_get_first(source->client_tree); if (strncmp(header, "Basic ", 6) == 0)
while(client_node) { {
client = (client_t *)client_node->key; userpass = util_base64_decode (header+6);
if (client->username) { if (userpass == NULL)
if (!strcmp(client->username, username)) { {
avl_tree_unlock(source->client_tree); WARN1("Base64 decode of Authorization header \"%s\" failed",
return 1; header+6);
break;
} }
tmp = strchr(userpass, ':');
if (tmp == NULL)
{
free (userpass);
break;
}
*tmp = 0;
username = userpass;
password = tmp+1;
client->username = strdup (username);
client->password = strdup (password);
free (userpass);
break;
} }
client_node = avl_get_next(client_node); INFO1 ("unhandled authorization header: %s", header);
}
avl_tree_unlock(source->client_tree); } while (0);
return 0;
client->auth = mountinfo->auth;
client->auth->refcount++;
} }
auth_result auth_check_client(source_t *source, client_t *client)
static void queue_auth_client (auth_client *auth_user)
{ {
auth_t *authenticator = source->authenticator; thread_mutex_lock (&auth_lock);
auth_result result; auth_user->next = (auth_client *)clients_to_auth;
clients_to_auth = auth_user;
auth_pending_count++;
thread_mutex_unlock (&auth_lock);
}
if(authenticator) {
/* This will look something like "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" */ /* release the auth. It is referred to by multiple structures so this is
char *header = httpp_getvar(client->parser, "authorization"); * refcounted and only actual freed after the last use
char *userpass, *tmp; */
char *username, *password; void auth_release (auth_t *authenticator)
{
if(header == NULL) if (authenticator == NULL)
return AUTH_FAILED; return;
if(strncmp(header, "Basic ", 6)) { authenticator->refcount--;
INFO0("Authorization not using Basic"); if (authenticator->refcount)
return 0; return;
if (authenticator->free)
authenticator->free (authenticator);
free (authenticator->type);
free (authenticator);
}
void auth_client_free (auth_client *auth_user)
{
if (auth_user == NULL)
return;
if (auth_user->client)
{
client_t *client = auth_user->client;
if (client->respcode)
client_destroy (client);
else
client_send_401 (client);
auth_user->client = NULL;
}
free (auth_user->mount);
free (auth_user);
}
/* wrapper function for auth thread to authenticate new listener
* connection details
*/
static void auth_new_listener (auth_client *auth_user)
{
client_t *client = auth_user->client;
if (client->auth->authenticate)
{
if (client->auth->authenticate (auth_user) != AUTH_OK)
return;
}
if (auth_postprocess_client (auth_user) < 0)
INFO1 ("client %lu failed", client->con->id);
}
/* wrapper function are auth thread to authenticate new listener
* connections
*/
static void auth_remove_listener (auth_client *auth_user)
{
client_t *client = auth_user->client;
if (client->auth->release_client)
client->auth->release_client (auth_user);
auth_release (client->auth);
client->auth = NULL;
return;
}
/* The auth thread main loop. */
static void *auth_run_thread (void *arg)
{
INFO0 ("Authentication thread started");
while (1)
{
if (clients_to_auth)
{
auth_client *auth_user;
thread_mutex_lock (&auth_lock);
auth_user = (auth_client*)clients_to_auth;
clients_to_auth = auth_user->next;
auth_pending_count--;
thread_mutex_unlock (&auth_lock);
auth_user->next = NULL;
if (auth_user->process)
auth_user->process (auth_user);
else
ERROR0 ("client auth process not set");
auth_client_free (auth_user);
continue;
} }
/* is there a request to shutdown */
userpass = util_base64_decode(header+6); if (auth_running == 0)
if(userpass == NULL) { break;
WARN1("Base64 decode of Authorization header \"%s\" failed", thread_sleep (150000);
header+6); }
return AUTH_FAILED; INFO0 ("Authenication thread shutting down");
return NULL;
}
/* Check whether this client is currently on this mount, the client may be
* on either the active or pending lists.
* return 1 if ok to add or 0 to prevent
*/
static int check_duplicate_logins (source_t *source, client_t *client)
{
auth_t *auth = client->auth;
/* allow multiple authenticated relays */
if (client->username == NULL)
return 1;
if (auth && auth->allow_duplicate_users == 0)
{
avl_node *node;
avl_tree_rlock (source->client_tree);
node = avl_get_first (source->client_tree);
while (node)
{
client_t *client = (client_t *)node->key;
if (client->username && strcmp (client->username, client->username) == 0)
{
avl_tree_unlock (source->client_tree);
return 0;
}
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 *client = (client_t *)node->key;
if (client->username && strcmp (client->username, client->username) == 0)
{
avl_tree_unlock (source->pending_tree);
return 0;
}
node = avl_get_next (node);
} }
avl_tree_unlock (source->pending_tree);
tmp = strchr(userpass, ':'); }
if(!tmp) { return 1;
free(userpass); }
return AUTH_FAILED;
/* 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_client_to_source (source_t *source, client_t *client)
{
do
{
DEBUG3 ("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;
/* now we fail the client */
return -1;
} while (1);
/* 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);
stats_event_inc (NULL, "listener_connections");
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);
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 ();
}
DEBUG1 ("Added client to %s", source->mount);
return 0;
}
/* Add listener to the pending lists of either the source or fserve thread.
* This can be run from the connection or auth thread context
*/
static int add_authenticated_client (const char *mount, mount_proxy *mountinfo, client_t *client)
{
int ret = 0;
source_t *source = NULL;
avl_tree_rlock (global.source_tree);
source = source_find_mount (mount);
if (source)
{
if (client->auth && check_duplicate_logins (source, client) == 0)
{
avl_tree_unlock (global.source_tree);
return -1;
} }
ret = add_client_to_source (source, client);
avl_tree_unlock (global.source_tree);
if (ret == 0)
DEBUG0 ("client authenticated, passed to source");
}
return ret;
}
*tmp = 0;
username = userpass;
password = tmp+1;
result = authenticator->authenticate( int auth_postprocess_client (auth_client *auth_user)
authenticator, source, username, password); {
int ret;
ice_config_t *config = config_get_config();
if(result == AUTH_OK) mount_proxy *mountinfo = config_find_mount (config, auth_user->mount);
client->username = strdup(username); auth_user->client->authenticated = 1;
free(userpass); ret = add_authenticated_client (auth_user->mount, mountinfo, auth_user->client);
config_release_config();
return result; if (ret < 0)
client_send_401 (auth_user->client);
auth_user->client = NULL;
return ret;
}
/* Add a listener. Check for any mount information that states any
* authentication to be used.
*/
void add_client (const char *mount, client_t *client)
{
mount_proxy *mountinfo;
ice_config_t *config = config_get_config();
mountinfo = config_find_mount (config, mount);
if (mountinfo && mountinfo->no_mount)
{
config_release_config ();
client_send_404 (client, "mountpoint unavailable");
return;
}
if (mountinfo && mountinfo->auth)
{
auth_client *auth_user;
if (auth_pending_count > 30)
{
config_release_config ();
WARN0 ("too many clients awaiting authentication");
client_send_404 (client, "busy, please try again later");
return;
}
auth_client_setup (mountinfo, client);
config_release_config ();
if (client->auth == NULL)
{
client_send_401 (client);
return;
}
auth_user = calloc (1, sizeof (auth_client));
if (auth_user == NULL)
{
client_send_401 (client);
return;
}
auth_user->mount = strdup (mount);
auth_user->process = auth_new_listener;
auth_user->client = client;
INFO0 ("adding client for authentication");
queue_auth_client (auth_user);
} }
else else
return AUTH_FAILED; {
} int ret = add_authenticated_client (mount, mountinfo, client);
config_release_config ();
static auth_t *auth_get_htpasswd_auth(config_options_t *options); if (ret < 0)
client_send_404 (client, "stream full");
auth_t *auth_get_authenticator(char *type, config_options_t *options)
{
auth_t *auth = NULL;
if(!strcmp(type, "htpasswd")) {
auth = auth_get_htpasswd_auth(options);
auth->type = strdup(type);
} }
else {
ERROR1("Unrecognised authenticator type: \"%s\"", type);
return NULL;
}
if(!auth)
ERROR1("Couldn't configure authenticator of type \"%s\"", type);
return auth;
} }
typedef struct {
char *filename;
int allow_duplicate_users;
rwlock_t file_rwlock;
} htpasswd_auth_state;
static void htpasswd_clear(auth_t *self) { /* determine whether we need to process this client further. This
htpasswd_auth_state *state = self->state; * involves any auth exit, typically for external auth servers.
free(state->filename); */
thread_rwlock_destroy(&state->file_rwlock); int release_client (client_t *client)
free(state);
free(self->type);
free(self);
}
static int get_line(FILE *file, char *buf, int len)
{ {
if(fgets(buf, len, file)) { if (client->auth)
int len = strlen(buf); {
if(len > 0 && buf[len-1] == '\n') { auth_client *auth_user = calloc (1, sizeof (auth_client));
buf[--len] = 0; if (auth_user == NULL)
if(len > 0 && buf[len-1] == '\r') return 0;
buf[--len] = 0;
} auth_user->mount = strdup (httpp_getvar (client->parser, HTTPP_VAR_URI));
auth_user->process = auth_remove_listener;
auth_user->client = client;
queue_auth_client (auth_user);
return 1; return 1;
} }
return 0; return 0;
} }
/* md5 hash */
static char *get_hash(char *data, int len) static void get_authenticator (auth_t *auth, config_options_t *options)
{ {
struct MD5Context context; do
unsigned char digest[16]; {
DEBUG1 ("type is %s", auth->type);
MD5Init(&context); if (strcmp (auth->type, "htpasswd") == 0)
{
MD5Update(&context, data, len); auth_get_htpasswd_auth (auth, options);
MD5Final(digest, &context);
return util_bin_to_hex(digest, 16);
}
#define MAX_LINE_LEN 512
/* Not efficient; opens and scans the entire file for every request */
static auth_result htpasswd_auth(auth_t *auth, source_t *source, char *username, char *password)
{
htpasswd_auth_state *state = auth->state;
FILE *passwdfile = NULL;
char line[MAX_LINE_LEN];
char *sep;
thread_rwlock_rlock(&state->file_rwlock);
if (!state->allow_duplicate_users) {
if (auth_is_listener_connected(source, username)) {
thread_rwlock_unlock(&state->file_rwlock);
return AUTH_FORBIDDEN;
}
}
passwdfile = fopen(state->filename, "rb");
if(passwdfile == NULL) {
WARN2("Failed to open authentication database \"%s\": %s",
state->filename, strerror(errno));
thread_rwlock_unlock(&state->file_rwlock);
return AUTH_FAILED;
}
while(get_line(passwdfile, line, MAX_LINE_LEN)) {
if(!line[0] || line[0] == '#')
continue;
sep = strchr(line, ':');
if(sep == NULL) {
DEBUG0("No separator in line");
continue;
}
*sep = 0;
if(!strcmp(username, line)) {
/* Found our user, now: does the hash of password match hash? */
char *hash = sep+1;
char *hashed_password = get_hash(password, strlen(password));
if(!strcmp(hash, hashed_password)) {
fclose(passwdfile);
free(hashed_password);
thread_rwlock_unlock(&state->file_rwlock);
return AUTH_OK;
}
free(hashed_password);
/* We don't keep searching through the file */
break;
}
}
fclose(passwdfile);
thread_rwlock_unlock(&state->file_rwlock);
return AUTH_FAILED;
}
static auth_t *auth_get_htpasswd_auth(config_options_t *options)
{
auth_t *authenticator = calloc(1, sizeof(auth_t));
htpasswd_auth_state *state;
authenticator->authenticate = htpasswd_auth;
authenticator->free = htpasswd_clear;
state = calloc(1, sizeof(htpasswd_auth_state));
state->allow_duplicate_users = 1;
while(options) {
if(!strcmp(options->name, "filename"))
state->filename = strdup(options->value);
if(!strcmp(options->name, "allow_duplicate_users"))
state->allow_duplicate_users = atoi(options->value);
options = options->next;
}
if(!state->filename) {
free(state);
free(authenticator);
ERROR0("No filename given in options for authenticator.");
return NULL;
}
authenticator->state = state;
DEBUG1("Configured htpasswd authentication using password file %s",
state->filename);
thread_rwlock_create(&state->file_rwlock);
return authenticator;
}
int auth_htpasswd_existing_user(auth_t *auth, char *username)
{
FILE *passwdfile;
htpasswd_auth_state *state;
int ret = AUTH_OK;
char line[MAX_LINE_LEN];
char *sep;
state = auth->state;
passwdfile = fopen(state->filename, "rb");
if(passwdfile == NULL) {
WARN2("Failed to open authentication database \"%s\": %s",
state->filename, strerror(errno));
return AUTH_FAILED;
}
while(get_line(passwdfile, line, MAX_LINE_LEN)) {
if(!line[0] || line[0] == '#')
continue;
sep = strchr(line, ':');
if(sep == NULL) {
DEBUG0("No separator in line");
continue;
}
*sep = 0;
if (!strcmp(username, line)) {
/* We found the user, break out of the loop */
ret = AUTH_USEREXISTS;
break; break;
} }
}
ERROR1("Unrecognised authenticator type: \"%s\"", auth->type);
return;
} while (0);
fclose(passwdfile); auth->refcount = 1;
return ret; while (options)
}
int auth_htpasswd_adduser(auth_t *auth, char *username, char *password)
{
FILE *passwdfile;
char *hashed_password = NULL;
htpasswd_auth_state *state;
if (auth_htpasswd_existing_user(auth, username) == AUTH_USEREXISTS) {
return AUTH_USEREXISTS;
}
state = auth->state;
passwdfile = fopen(state->filename, "ab");
if(passwdfile == NULL) {
WARN2("Failed to open authentication database \"%s\": %s",
state->filename, strerror(errno));
return AUTH_FAILED;
}
hashed_password = get_hash(password, strlen(password));
if (hashed_password) {
fprintf(passwdfile, "%s:%s\n", username, hashed_password);
free(hashed_password);
}
fclose(passwdfile);
return AUTH_USERADDED;
}
int auth_adduser(source_t *source, char *username, char *password)
{
int ret = 0;
htpasswd_auth_state *state;
if (source->authenticator) {
if (!strcmp(source->authenticator->type, "htpasswd")) {
state = source->authenticator->state;
thread_rwlock_wlock(&state->file_rwlock);
ret = auth_htpasswd_adduser(source->authenticator, username, password);
thread_rwlock_unlock(&state->file_rwlock);
}
}
return ret;
}
int auth_htpasswd_deleteuser(auth_t *auth, char *username)
{
FILE *passwdfile;
FILE *tmp_passwdfile;
htpasswd_auth_state *state;
char line[MAX_LINE_LEN];
char *sep;
char *tmpfile = NULL;
int tmpfile_len = 0;
struct stat file_info;
state = auth->state;
passwdfile = fopen(state->filename, "rb");
if(passwdfile == NULL) {
WARN2("Failed to open authentication database \"%s\": %s",
state->filename, strerror(errno));
return AUTH_FAILED;
}
tmpfile_len = strlen(state->filename) + 5;
tmpfile = calloc(1, tmpfile_len);
snprintf (tmpfile, tmpfile_len, "%s.tmp", state->filename);
if (stat (tmpfile, &file_info) == 0)
{ {
WARN1 ("temp file \"%s\" exists, rejecting operation", tmpfile); if (strcmp(options->name, "allow_duplicate_users") == 0)
free (tmpfile); auth->allow_duplicate_users = atoi (options->value);
fclose (passwdfile); options = options->next;
return AUTH_FAILED;
} }
tmp_passwdfile = fopen(tmpfile, "wb");
if(tmp_passwdfile == NULL) {
WARN2("Failed to open temporary authentication database \"%s\": %s",
tmpfile, strerror(errno));
fclose(passwdfile);
free(tmpfile);
return AUTH_FAILED;
}
while(get_line(passwdfile, line, MAX_LINE_LEN)) {
if(!line[0] || line[0] == '#')
continue;
sep = strchr(line, ':');
if(sep == NULL) {
DEBUG0("No separator in line");
continue;
}
*sep = 0;
if (strcmp(username, line)) {
/* We did not match on the user, so copy it to the temp file */
/* and put the : back in */
*sep = ':';
fprintf(tmp_passwdfile, "%s\n", line);
}
}
fclose(tmp_passwdfile);
fclose(passwdfile);
/* Now move the contents of the tmp file to the original */
#ifdef _WIN32
/* Windows won't let us rename a file if the destination file
exists...so, lets remove the original first */
if (remove(state->filename) != 0) {
ERROR3("Problem moving temp authentication file to original \"%s\" - \"%s\": %s",
tmpfile, state->filename, strerror(errno));
}
else {
#endif
if (rename(tmpfile, state->filename) != 0) {
ERROR3("Problem moving temp authentication file to original \"%s\" - \"%s\": %s",
tmpfile, state->filename, strerror(errno));
}
#ifdef _WIN32
}
#endif
free(tmpfile);
return AUTH_USERDELETED;
} }
int auth_deleteuser(source_t *source, char *username)
auth_t *auth_get_authenticator (xmlNodePtr node)
{ {
htpasswd_auth_state *state; auth_t *auth = calloc (1, sizeof (auth_t));
config_options_t *options = NULL, **next_option = &options;
xmlNodePtr option;
int ret = 0; if (auth == NULL)
if (source->authenticator) { return NULL;
if (!strcmp(source->authenticator->type, "htpasswd")) {
state = source->authenticator->state; option = node->xmlChildrenNode;
thread_rwlock_wlock(&state->file_rwlock); while (option)
ret = auth_htpasswd_deleteuser(source->authenticator, username); {
thread_rwlock_unlock(&state->file_rwlock); xmlNodePtr current = option;
option = option->next;
if (strcmp (current->name, "option") == 0)
{
config_options_t *opt = calloc (1, sizeof (config_options_t));
opt->name = xmlGetProp (current, "name");
if (opt->name == NULL)
{
free(opt);
continue;
}
opt->value = xmlGetProp (current, "value");
if (opt->value == NULL)
{
xmlFree (opt->name);
free (opt);
continue;
}
*next_option = opt;
next_option = &opt->next;
} }
else
if (strcmp (current->name, "text") != 0)
WARN1 ("unknown auth setting (%s)", current->name);
} }
return ret; auth->type = xmlGetProp (node, "type");
get_authenticator (auth, options);
while (options)
{
config_options_t *opt = options;
options = opt->next;
xmlFree (opt->name);
xmlFree (opt->value);
free (opt);
}
return auth;
} }
int auth_get_htpasswd_userlist(auth_t *auth, xmlNodePtr srcnode)
/* called when the stream starts, so that authentication engine can do any
* cleanup/initialisation.
*/
void auth_stream_start (mount_proxy *mountinfo, const char *mount)
{ {
htpasswd_auth_state *state; if (mountinfo && mountinfo->auth && mountinfo->auth->stream_start)
FILE *passwdfile; {
char line[MAX_LINE_LEN]; auth_client *auth_user = calloc (1, sizeof (auth_client));
char *sep; if (auth_user)
char *passwd; {
xmlNodePtr newnode; auth_user->mount = strdup (mount);
auth_user->process = mountinfo->auth->stream_start;
state = auth->state; queue_auth_client (auth_user);
passwdfile = fopen(state->filename, "rb");
if(passwdfile == NULL) {
WARN2("Failed to open authentication database \"%s\": %s",
state->filename, strerror(errno));
return AUTH_FAILED;
}
while(get_line(passwdfile, line, MAX_LINE_LEN)) {
if(!line[0] || line[0] == '#')
continue;
sep = strchr(line, ':');
if(sep == NULL) {
DEBUG0("No separator in line");
continue;
} }
*sep = 0;
newnode = xmlNewChild(srcnode, NULL, "User", NULL);
xmlNewChild(newnode, NULL, "username", line);
passwd = sep+1;
xmlNewChild(newnode, NULL, "password", passwd);
} }
fclose(passwdfile);
return AUTH_OK;
} }
int auth_get_userlist(source_t *source, xmlNodePtr srcnode)
/* Called when the stream ends so that the authentication engine can do
* any authentication cleanup
*/
void auth_stream_end (mount_proxy *mountinfo, const char *mount)
{ {
int ret = 0; if (mountinfo && mountinfo->auth && mountinfo->auth->stream_end)
htpasswd_auth_state *state; {
auth_client *auth_user = calloc (1, sizeof (auth_client));
if (auth_user)
{
auth_user->mount = strdup (mount);
auth_user->process = mountinfo->auth->stream_end;
if (source->authenticator) { queue_auth_client (auth_user);
if (!strcmp(source->authenticator->type, "htpasswd")) {
state = source->authenticator->state;
thread_rwlock_rlock(&state->file_rwlock);
ret = auth_get_htpasswd_userlist(source->authenticator, srcnode);
thread_rwlock_unlock(&state->file_rwlock);
} }
} }
return ret; }
/* these are called at server start and termination */
void auth_initialise ()
{
clients_to_auth = NULL;
auth_pending_count = 0;
auth_running = 1;
thread_mutex_create (&auth_lock);
auth_thread = thread_create ("auth thread", auth_run_thread, NULL, THREAD_ATTACHED);
}
void auth_shutdown ()
{
if (auth_thread)
{
auth_running = 0;
thread_join (auth_thread);
INFO0 ("Auth thread has terminated");
}
} }

View File

@ -17,14 +17,19 @@
#include <config.h> #include <config.h>
#endif #endif
struct source_tag;
struct auth_tag;
#include <libxml/xmlmemory.h> #include <libxml/xmlmemory.h>
#include <libxml/parser.h> #include <libxml/parser.h>
#include <libxml/tree.h> #include <libxml/tree.h>
#include "source.h" #include "cfgfile.h"
#include "client.h" #include "client.h"
#include "thread/thread.h"
typedef enum typedef enum
{ {
AUTH_UNDEFINED,
AUTH_OK, AUTH_OK,
AUTH_FAILED, AUTH_FAILED,
AUTH_FORBIDDEN, AUTH_FORBIDDEN,
@ -33,23 +38,57 @@ typedef enum
AUTH_USERDELETED, AUTH_USERDELETED,
} auth_result; } 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 typedef struct auth_tag
{ {
char *mount;
/* Authenticate using the given username and password */ /* Authenticate using the given username and password */
auth_result (*authenticate)(struct auth_tag *self, auth_result (*authenticate)(auth_client *aclient);
source_t *source, char *username, char *password); 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); 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; void *state;
char *type; char *type;
} auth_t; } 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);
auth_t *auth_get_authenticator(char *type, config_options_t *options); void auth_initialise ();
void *auth_clear(auth_t *authenticator); auth_t *auth_get_authenticator (xmlNodePtr node);
int auth_get_userlist(source_t *source, xmlNodePtr srcnode); void auth_release (auth_t *authenticator);
int auth_adduser(source_t *source, char *username, char *password);
int auth_deleteuser(source_t *source, char *username); /* 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);
/* 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 #endif

410
src/auth_htpasswd.c Normal file
View File

@ -0,0 +1,410 @@
/* 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).
*/
/**
* Client authentication functions
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "auth.h"
#include "source.h"
#include "client.h"
#include "cfgfile.h"
#include "httpp/httpp.h"
#include "md5.h"
#include "logging.h"
#define CATMODULE "auth_htpasswd"
#ifdef WIN32
#define snprintf _snprintf
#endif
static auth_result htpasswd_adduser (auth_t *auth, const char *username, const char *password);
static auth_result htpasswd_deleteuser(auth_t *auth, const char *username);
static auth_result htpasswd_userlist(auth_t *auth, xmlNodePtr srcnode);
static int _free_user (void *key);
typedef struct
{
char *name;
char *pass;
} htpasswd_user;
typedef struct {
char *filename;
rwlock_t file_rwlock;
avl_tree *users;
time_t mtime;
} htpasswd_auth_state;
static void htpasswd_clear(auth_t *self) {
htpasswd_auth_state *state = self->state;
free(state->filename);
if (state->users)
avl_tree_free (state->users, _free_user);
thread_rwlock_destroy(&state->file_rwlock);
free(state);
}
static int get_line(FILE *file, char *buf, int len)
{
if(fgets(buf, len, file)) {
int len = strlen(buf);
if(len > 0 && buf[len-1] == '\n') {
buf[--len] = 0;
if(len > 0 && buf[len-1] == '\r')
buf[--len] = 0;
}
return 1;
}
return 0;
}
/* md5 hash */
static char *get_hash(const char *data, int len)
{
struct MD5Context context;
unsigned char digest[16];
MD5Init(&context);
MD5Update(&context, data, len);
MD5Final(digest, &context);
return util_bin_to_hex(digest, 16);
}
#define MAX_LINE_LEN 512
static int compare_users (void *arg, void *a, void *b)
{
htpasswd_user *user1 = (htpasswd_user *)a;
htpasswd_user *user2 = (htpasswd_user *)b;
return strcmp (user1->name, user2->name);
}
static int _free_user (void *key)
{
htpasswd_user *user = (htpasswd_user *)key;
free (user->name); /* ->pass is part of same buffer */
free (user);
return 1;
}
static void htpasswd_recheckfile (htpasswd_auth_state *htpasswd)
{
FILE *passwdfile;
avl_tree *new_users;
int num = 0;
struct stat file_stat;
char *sep;
char line [MAX_LINE_LEN];
if (stat (htpasswd->filename, &file_stat) < 0)
{
WARN1 ("failed to check status of %s", htpasswd->filename);
return;
}
if (file_stat.st_mtime == htpasswd->mtime)
{
/* common case, no update to file */
return;
}
INFO1 ("re-reading htpasswd file \"%s\"", htpasswd->filename);
passwdfile = fopen (htpasswd->filename, "rb");
if (passwdfile == NULL)
{
WARN2("Failed to open authentication database \"%s\": %s",
htpasswd->filename, strerror(errno));
return;
}
htpasswd->mtime = file_stat.st_mtime;
new_users = avl_tree_new (compare_users, NULL);
while (get_line(passwdfile, line, MAX_LINE_LEN))
{
int len;
htpasswd_user *entry;
num++;
if(!line[0] || line[0] == '#')
continue;
sep = strrchr (line, ':');
if (sep == NULL)
{
WARN2("No separator on line %d (%s)", num, htpasswd->filename);
continue;
}
entry = calloc (1, sizeof (htpasswd_user));
len = strlen (line) + 1;
entry->name = malloc (len);
*sep = 0;
memcpy (entry->name, line, len);
entry->pass = entry->name + (sep-line) + 1;
avl_insert (new_users, entry);
}
fclose (passwdfile);
thread_rwlock_wlock (&htpasswd->file_rwlock);
if (htpasswd->users)
avl_tree_free (htpasswd->users, _free_user);
htpasswd->users = new_users;
thread_rwlock_unlock (&htpasswd->file_rwlock);
}
static auth_result htpasswd_auth (auth_client *auth_user)
{
auth_t *auth = auth_user->client->auth;
htpasswd_auth_state *htpasswd = auth->state;
client_t *client = auth_user->client;
htpasswd_user entry;
void *result;
if (client->username == NULL || client->password == NULL)
return AUTH_FAILED;
htpasswd_recheckfile (htpasswd);
thread_rwlock_rlock (&htpasswd->file_rwlock);
entry.name = client->username;
if (avl_get_by_key (htpasswd->users, &entry, &result) == 0)
{
htpasswd_user *found = result;
char *hashed_pw;
thread_rwlock_unlock (&htpasswd->file_rwlock);
hashed_pw = get_hash (client->password, strlen (client->password));
if (strcmp (found->pass, hashed_pw) == 0)
{
free (hashed_pw);
return AUTH_OK;
}
free (hashed_pw);
DEBUG0 ("incorrect password for client");
return AUTH_FAILED;
}
DEBUG1 ("no such username: %s", client->username);
thread_rwlock_unlock (&htpasswd->file_rwlock);
return AUTH_FAILED;
}
int auth_get_htpasswd_auth (auth_t *authenticator, config_options_t *options)
{
htpasswd_auth_state *state;
authenticator->authenticate = htpasswd_auth;
authenticator->free = htpasswd_clear;
authenticator->adduser = htpasswd_adduser;
authenticator->deleteuser = htpasswd_deleteuser;
authenticator->listuser = htpasswd_userlist;
state = calloc(1, sizeof(htpasswd_auth_state));
while(options) {
if(!strcmp(options->name, "filename"))
state->filename = strdup(options->value);
options = options->next;
}
if(!state->filename) {
free(state);
ERROR0("No filename given in options for authenticator.");
return -1;
}
authenticator->state = state;
DEBUG1("Configured htpasswd authentication using password file %s",
state->filename);
thread_rwlock_create(&state->file_rwlock);
htpasswd_recheckfile (state);
return 0;
}
static auth_result htpasswd_adduser (auth_t *auth, const char *username, const char *password)
{
FILE *passwdfile;
char *hashed_password = NULL;
htpasswd_auth_state *state = auth->state;
htpasswd_user entry;
void *result;
htpasswd_recheckfile (state);
thread_rwlock_wlock (&state->file_rwlock);
entry.name = (char*)username;
if (avl_get_by_key (state->users, &entry, &result) == 0)
{
thread_rwlock_unlock (&state->file_rwlock);
return AUTH_USEREXISTS;
}
passwdfile = fopen(state->filename, "ab");
if (passwdfile == NULL)
{
thread_rwlock_unlock (&state->file_rwlock);
WARN2("Failed to open authentication database \"%s\": %s",
state->filename, strerror(errno));
return AUTH_FAILED;
}
hashed_password = get_hash(password, strlen(password));
if (hashed_password) {
fprintf(passwdfile, "%s:%s\n", username, hashed_password);
free(hashed_password);
}
fclose(passwdfile);
thread_rwlock_unlock (&state->file_rwlock);
return AUTH_USERADDED;
}
static auth_result htpasswd_deleteuser(auth_t *auth, const char *username)
{
FILE *passwdfile;
FILE *tmp_passwdfile;
htpasswd_auth_state *state;
char line[MAX_LINE_LEN];
char *sep;
char *tmpfile = NULL;
int tmpfile_len = 0;
struct stat file_info;
state = auth->state;
thread_rwlock_wlock (&state->file_rwlock);
passwdfile = fopen(state->filename, "rb");
if(passwdfile == NULL) {
WARN2("Failed to open authentication database \"%s\": %s",
state->filename, strerror(errno));
thread_rwlock_unlock (&state->file_rwlock);
return AUTH_FAILED;
}
tmpfile_len = strlen(state->filename) + 6;
tmpfile = calloc(1, tmpfile_len);
snprintf (tmpfile, tmpfile_len, "%s.tmp", state->filename);
if (stat (tmpfile, &file_info) == 0)
{
WARN1 ("temp file \"%s\" exists, rejecting operation", tmpfile);
free (tmpfile);
fclose (passwdfile);
thread_rwlock_unlock (&state->file_rwlock);
return AUTH_FAILED;
}
tmp_passwdfile = fopen(tmpfile, "wb");
if(tmp_passwdfile == NULL) {
WARN2("Failed to open temporary authentication database \"%s\": %s",
tmpfile, strerror(errno));
fclose(passwdfile);
free(tmpfile);
thread_rwlock_unlock (&state->file_rwlock);
return AUTH_FAILED;
}
while(get_line(passwdfile, line, MAX_LINE_LEN)) {
if(!line[0] || line[0] == '#')
continue;
sep = strchr(line, ':');
if(sep == NULL) {
DEBUG0("No separator in line");
continue;
}
*sep = 0;
if (strcmp(username, line)) {
/* We did not match on the user, so copy it to the temp file */
/* and put the : back in */
*sep = ':';
fprintf(tmp_passwdfile, "%s\n", line);
}
}
fclose(tmp_passwdfile);
fclose(passwdfile);
/* Now move the contents of the tmp file to the original */
/* Windows won't let us rename a file if the destination file
exists...so, lets remove the original first */
if (remove(state->filename) != 0) {
ERROR3("Problem moving temp authentication file to original \"%s\" - \"%s\": %s",
tmpfile, state->filename, strerror(errno));
}
else {
if (rename(tmpfile, state->filename) != 0) {
ERROR3("Problem moving temp authentication file to original \"%s\" - \"%s\": %s",
tmpfile, state->filename, strerror(errno));
}
}
free(tmpfile);
thread_rwlock_unlock (&state->file_rwlock);
htpasswd_recheckfile (state);
return AUTH_USERDELETED;
}
static auth_result htpasswd_userlist(auth_t *auth, xmlNodePtr srcnode)
{
htpasswd_auth_state *state;
xmlNodePtr newnode;
avl_node *node;
state = auth->state;
htpasswd_recheckfile (state);
thread_rwlock_rlock (&state->file_rwlock);
node = avl_get_first (state->users);
while (node)
{
htpasswd_user *user = (htpasswd_user *)node->key;
newnode = xmlNewChild (srcnode, NULL, "User", NULL);
xmlNewChild(newnode, NULL, "username", user->name);
xmlNewChild(newnode, NULL, "password", user->pass);
node = avl_get_next (node);
}
thread_rwlock_unlock (&state->file_rwlock);
return AUTH_OK;
}

24
src/auth_htpasswd.h Normal file
View File

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

View File

@ -522,8 +522,6 @@ static void _parse_mount(xmlDocPtr doc, xmlNodePtr node,
mount_proxy *mount = calloc(1, sizeof(mount_proxy)); mount_proxy *mount = calloc(1, sizeof(mount_proxy));
mount_proxy *current = configuration->mounts; mount_proxy *current = configuration->mounts;
mount_proxy *last=NULL; mount_proxy *last=NULL;
xmlNodePtr option;
config_options_t *last_option;
while(current) { while(current) {
last = current; last = current;
@ -601,35 +599,7 @@ static void _parse_mount(xmlDocPtr doc, xmlNodePtr node,
if(tmp) xmlFree(tmp); if(tmp) xmlFree(tmp);
} }
else if (strcmp(node->name, "authentication") == 0) { else if (strcmp(node->name, "authentication") == 0) {
mount->auth_type = xmlGetProp(node, "type"); mount->auth = auth_get_authenticator (node);
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;
}
} }
else if (strcmp(node->name, "on-connect") == 0) { else if (strcmp(node->name, "on-connect") == 0) {
mount->on_connect = (char *)xmlNodeListGetString( mount->on_connect = (char *)xmlNodeListGetString(

View File

@ -20,9 +20,11 @@
#define MAX_YP_DIRECTORIES 25 #define MAX_YP_DIRECTORIES 25
struct _mount_proxy;
#include "thread/thread.h" #include "thread/thread.h"
#include "avl/avl.h" #include "avl/avl.h"
#include "auth.h"
#include "global.h" #include "global.h"
typedef struct ice_config_dir_tag typedef struct ice_config_dir_tag
@ -63,6 +65,7 @@ typedef struct _mount_proxy {
int mp3_meta_interval; /* outgoing per-stream metadata interval */ int mp3_meta_interval; /* outgoing per-stream metadata interval */
char *auth_type; /* Authentication type */ char *auth_type; /* Authentication type */
struct auth_tag *auth;
char *cluster_password; char *cluster_password;
config_options_t *auth_options; /* Options for this type */ config_options_t *auth_options; /* Options for this type */
char *on_connect; char *on_connect;

View File

@ -72,6 +72,10 @@ void client_destroy(client_t *client)
{ {
if (client == NULL) if (client == NULL)
return; return;
if (release_client (client))
return;
/* write log entry if ip is set (some things don't set it, like outgoing /* write log entry if ip is set (some things don't set it, like outgoing
* slave requests * slave requests
*/ */

View File

@ -32,6 +32,9 @@ typedef struct _client_tag
/* http response code for this client */ /* http response code for this client */
int respcode; int respcode;
/* auth completed, 0 not yet, 1 passed */
int authenticated;
/* is client getting intro data */ /* is client getting intro data */
long intro_offset; long intro_offset;
@ -41,9 +44,15 @@ typedef struct _client_tag
/* position in first buffer */ /* position in first buffer */
unsigned long pos; unsigned long pos;
/* auth used for this client */
struct auth_tag *auth;
/* Client username, if authenticated */ /* Client username, if authenticated */
char *username; char *username;
/* Client password, if authenticated */
char *password;
/* Format-handler-specific data for this client */ /* Format-handler-specific data for this client */
void *format_data; void *format_data;

View File

@ -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) static void _handle_get_request (client_t *client, char *passed_uri)
{ {
source_t *source;
int fileserve; int fileserve;
int port; int port;
int i; int i;
@ -766,7 +765,6 @@ static void _handle_get_request (client_t *client, char *passed_uri)
int serverport = 0; int serverport = 0;
aliases *alias; aliases *alias;
ice_config_t *config; ice_config_t *config;
int ret;
char *uri = passed_uri; char *uri = passed_uri;
config = config_get_config(); config = config_get_config();
@ -831,95 +829,14 @@ static void _handle_get_request (client_t *client, char *passed_uri)
return; return;
} }
avl_tree_rlock(global.source_tree); sock_set_blocking(client->con->sock, SOCK_NONBLOCK);
source = source_find_mount(uri); sock_set_nodelay(client->con->sock);
if (source) {
DEBUG0("Source found for client");
/* The source may not be the requested source - it might have gone client->write_to_client = format_generic_write_to_client;
* via one or more fallbacks. We only reject it for no-mount if it's client->check_buffer = format_check_http_buffer;
* the originally requested source client->refbuf = refbuf_new (PER_CLIENT_REFBUF_SIZE);
*/
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;
}
/* Check for any required authentication first */ add_client (uri, client);
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;
}
}
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.");
}
if (uri != passed_uri) free (uri); if (uri != passed_uri) free (uri);
} }

View File

@ -505,6 +505,7 @@ int main(int argc, char **argv)
/* Do this after logging init */ /* Do this after logging init */
slave_initialize(); slave_initialize();
auth_initialise ();
_server_proc(); _server_proc();

View File

@ -245,7 +245,6 @@ void source_clear_source (source_t *source)
source->queue_size = 0; source->queue_size = 0;
source->queue_size_limit = 0; source->queue_size_limit = 0;
source->listeners = 0; source->listeners = 0;
source->no_mount = 0;
source->shoutcast_compat = 0; source->shoutcast_compat = 0;
source->max_listeners = -1; source->max_listeners = -1;
source->hidden = 0; source->hidden = 0;
@ -614,6 +613,7 @@ static void source_init (source_t *source)
{ {
if (mountinfo->on_connect) if (mountinfo->on_connect)
source_run_script (mountinfo->on_connect, source->mount); source_run_script (mountinfo->on_connect, source->mount);
auth_stream_start (mountinfo, source->mount);
} }
config_release_config(); config_release_config();
@ -817,6 +817,7 @@ static void source_shutdown (source_t *source)
{ {
if (mountinfo->on_disconnect) if (mountinfo->on_disconnect)
source_run_script (mountinfo->on_disconnect, source->mount); source_run_script (mountinfo->on_disconnect, source->mount);
auth_stream_end (mountinfo, source->mount);
} }
config_release_config(); 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->max_listeners = mountinfo->max_listeners;
source->fallback_override = mountinfo->fallback_override; source->fallback_override = mountinfo->fallback_override;
source->no_mount = mountinfo->no_mount;
source->hidden = mountinfo->hidden; source->hidden = mountinfo->hidden;
} }
@ -1062,6 +1062,11 @@ static void source_apply_mount (source_t *source, mount_proxy *mountinfo)
if (mountinfo && mountinfo->subtype) if (mountinfo && mountinfo->subtype)
stats_event (source->mount, "subtype", 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) if (mountinfo && mountinfo->fallback_mount)
{ {
char *mount = source->fallback_mount; char *mount = source->fallback_mount;
@ -1071,13 +1076,6 @@ static void source_apply_mount (source_t *source, mount_proxy *mountinfo)
else else
source->fallback_mount = NULL; 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) if (mountinfo && mountinfo->dumpfile)
{ {
char *filename = source->dumpfilename; char *filename = source->dumpfilename;

View File

@ -20,8 +20,6 @@
#include <stdio.h> #include <stdio.h>
struct auth_tag;
typedef struct source_tag typedef struct source_tag
{ {
client_t *client; client_t *client;
@ -55,9 +53,7 @@ typedef struct source_tag
unsigned long listeners; unsigned long listeners;
long max_listeners; long max_listeners;
int yp_public; int yp_public;
struct auth_tag *authenticator;
int fallback_override; int fallback_override;
int no_mount;
int shoutcast_compat; int shoutcast_compat;
/* per source burst handling for connecting clients */ /* per source burst handling for connecting clients */