From eebb340a1a917ed36e72e87a4bc9d9517ff5e4b5 Mon Sep 17 00:00:00 2001 From: Karl Heyes Date: Sun, 7 Aug 2005 23:29:12 +0000 Subject: [PATCH] merge URL listener auth svn path=/icecast/trunk/icecast/; revision=9714 --- configure.in | 23 +-- src/Makefile.am | 3 +- src/auth.c | 8 + src/auth_url.c | 421 ++++++++++++++++++++++++++++++++++++++++++++++++ src/auth_url.h | 24 +++ 5 files changed, 468 insertions(+), 11 deletions(-) create mode 100644 src/auth_url.c create mode 100644 src/auth_url.h diff --git a/configure.in b/configure.in index 09fa79ed..7e25c4fb 100644 --- a/configure.in +++ b/configure.in @@ -106,24 +106,27 @@ XIPH_VAR_APPEND([XIPH_CFLAGS],[$PTHREAD_CFLAGS]) XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$PTHREAD_CPPFLAGS]) XIPH_VAR_PREPEND([XIPH_LIBS],[$PTHREAD_LIBS]) -dnl -- YP support -- -AC_ARG_ENABLE([yp], - AC_HELP_STRING([--disable-yp],[disable YP directory support]), - enable_yp="$enableval", - enable_yp="yes") -if test "x$enable_yp" = "xyes" -then XIPH_PATH_CURL([ AC_CHECK_DECL([CURLOPT_NOSIGNAL], - [ AC_DEFINE([USE_YP], 1, [Define to compile in YP support code]) - ICECAST_OPTIONAL="$ICECAST_OPTIONAL yp.o" + [ AC_DEFINE([HAVE_AUTH_URL], 1, [Define to compile in auth URL support code]) + ICECAST_OPTIONAL="$ICECAST_OPTIONAL auth_url.o" + enable_curl="yes" XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$CURL_CFLAGS]) XIPH_VAR_PREPEND([XIPH_LIBS],[$CURL_LIBS]) ], [ AC_MSG_NOTICE([Your curl dev files are too old (7.10 or above required), YP disabled]) ], [#include ]) - ],[ AC_MSG_NOTICE([libcurl not found, YP disabled]) + ],[ AC_MSG_NOTICE([libcurl not found]) ]) +dnl -- YP support -- +AC_ARG_ENABLE([yp], + AC_HELP_STRING([--disable-yp],[disable YP directory support]), + enable_yp="$enableval", + enable_yp="yes") +if test "x$enable_yp" = "xyes" -a "x$enable_curl" = xyes +then + AC_DEFINE([USE_YP], 1, [Define to compile in YP support code]) + ICECAST_OPTIONAL="$ICECAST_OPTIONAL yp.o" else AC_MSG_NOTICE([YP support disabled]) fi diff --git a/src/Makefile.am b/src/Makefile.am index 21212c8f..1b41e1a6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -9,7 +9,7 @@ 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 \ compat.h fserve.h xslt.h yp.h event.h md5.h \ - auth.h auth_htpasswd.h \ + auth.h auth_htpasswd.h auth_url.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 \ @@ -18,6 +18,7 @@ icecast_SOURCES = cfgfile.c main.c logging.c sighandler.c connection.c global.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 \ + auth_url.c \ format_vorbis.c format_theora.c format_speex.c icecast_DEPENDENCIES = @ICECAST_OPTIONAL@ net/libicenet.la thread/libicethread.la \ diff --git a/src/auth.c b/src/auth.c index 49ad70a7..ed3477d4 100644 --- a/src/auth.c +++ b/src/auth.c @@ -25,6 +25,7 @@ #include "auth.h" #include "auth_htpasswd.h" +#include "auth_url.h" #include "source.h" #include "client.h" #include "cfgfile.h" @@ -423,6 +424,13 @@ static void get_authenticator (auth_t *auth, config_options_t *options) do { DEBUG1 ("type is %s", auth->type); +#ifdef HAVE_AUTH_URL + if (strcmp (auth->type, "url") == 0) + { + auth_get_url_auth (auth, options); + break; + } +#endif if (strcmp (auth->type, "htpasswd") == 0) { auth_get_htpasswd_auth (auth, options); diff --git a/src/auth_url.c b/src/auth_url.c new file mode 100644 index 00000000..d60847cb --- /dev/null +++ b/src/auth_url.c @@ -0,0 +1,421 @@ +/* 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 , + * Michael Smith , + * oddsock , + * Karl Heyes + * and others (see AUTHORS for details). + */ + +/* + * Client authentication via URL functions + * + * authenticate user via a URL, this is done via libcurl so https can also + * be handled. The request will have POST information about the request in + * the form of + * + * action=auth&client=1&server=host&port=8000&mount=/live&user=fred&pass=mypass&ip=127.0.0.1&agent="" + * + * For a user to be accecpted the following HTTP header needs + * to be returned (the actual string can be specified in the xml file) + * + * icecast-auth-user: 1 + * + * On client disconnection another request can be sent to a URL with the POST + * information of + * + * action=remove&server=host&port=8000&client=1&mount=/live&user=fred&pass=mypass&duration=3600 + * + * client refers to the icecast client identification number. mount refers + * to the mountpoint (beginning with / and may contain query parameters eg ?& + * 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=start&mount=/live&server=myserver.com&port=8000 + * action=end&mount=/live&server=myserver.com&port=8000 + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#ifndef _WIN32 +#include +#include +#else +#define snprintf _snprintf +#define strncasecmp strnicmp +#endif + +#include + +#include "auth.h" +#include "source.h" +#include "client.h" +#include "cfgfile.h" +#include "httpp/httpp.h" + +#include "logging.h" +#define CATMODULE "auth_url" + +typedef struct { + char *addurl; + char *removeurl; + char *stream_start; + char *stream_end; + char *username; + char *password; + char *auth_header; + int auth_header_len; + CURL *handle; + char errormsg [CURL_ERROR_SIZE]; +} auth_url; + + +static void auth_url_clear(auth_t *self) +{ + auth_url *url = self->state; + curl_easy_cleanup (url->handle); + free (url->username); + free (url->password); + free (url->removeurl); + free (url->addurl); + free (url->stream_start); + free (url->stream_end); + free (url->auth_header); + free (url); +} + + +static int handle_returned_header (void *ptr, size_t size, size_t nmemb, void *stream) +{ + auth_client *auth_user = stream; + unsigned bytes = size * nmemb; + client_t *client = auth_user->client; + + if (client) + { + auth_t *auth = client->auth; + auth_url *url = auth->state; + if (strncasecmp (ptr, url->auth_header, url->auth_header_len) == 0) + client->authenticated = 1; + if (strncasecmp (ptr, "icecast-auth-message: ", 22) == 0) + { + char *eol; + snprintf (url->errormsg, sizeof (url->errormsg), "%s", (char*)ptr+22); + eol = strchr (url->errormsg, '\r'); + if (eol == NULL) + eol = strchr (url->errormsg, '\n'); + if (eol) + *eol = '\0'; + } + } + + return (int)bytes; +} + +/* capture returned data, but don't do anything with it */ +static int handle_returned_data (void *ptr, size_t size, size_t nmemb, void *stream) +{ + return (int)(size*nmemb); +} + + +static auth_result url_remove_client (auth_client *auth_user) +{ + client_t *client = auth_user->client; + auth_t *auth = client->auth; + auth_url *url = auth->state; + time_t duration = time(NULL) - client->con->con_time; + char *username, *password, *mount, *server; + ice_config_t *config; + int port; + char post [4096]; + + config = config_get_config (); + server = util_url_escape (config->hostname); + port = config->port; + config_release_config (); + + if (client->username) + username = util_url_escape (client->username); + else + username = strdup (""); + + if (client->password) + password = util_url_escape (client->password); + else + password = strdup (""); + + /* get the full uri (with query params if available) */ + mount = httpp_getvar (client->parser, HTTPP_VAR_RAWURI); + if (mount == NULL) + mount = httpp_getvar (client->parser, HTTPP_VAR_URI); + mount = util_url_escape (mount); + + snprintf (post, sizeof (post), + "action=remove&server=%s&port=%d&client=%lu&mount=%s" + "&user=%s&pass=%s&duration=%lu", + server, port, client->con->id, mount, username, + password, (long unsigned)duration); + free (server); + free (mount); + free (username); + free (password); + + curl_easy_setopt (url->handle, CURLOPT_URL, url->removeurl); + curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post); + curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user); + + if (curl_easy_perform (url->handle)) + WARN2 ("auth to server %s failed with %s", url->removeurl, url->errormsg); + + return AUTH_OK; +} + + +static auth_result url_add_client (auth_client *auth_user) +{ + client_t *client = auth_user->client; + auth_t *auth = client->auth; + auth_url *url = auth->state; + int res = 0, port; + char *agent, *user_agent, *username, *password; + char *mount, *ipaddr, *server; + ice_config_t *config; + char post [4096]; + + if (url->addurl == NULL) + return AUTH_OK; + + config = config_get_config (); + server = util_url_escape (config->hostname); + port = config->port; + config_release_config (); + agent = httpp_getvar (client->parser, "user-agent"); + if (agent == NULL) + agent = "-"; + user_agent = util_url_escape (agent); + if (client->username) + username = util_url_escape (client->username); + else + username = strdup (""); + if (client->password) + password = util_url_escape (client->password); + else + password = strdup (""); + + /* get the full uri (with query params if available) */ + mount = httpp_getvar (client->parser, HTTPP_VAR_RAWURI); + if (mount == NULL) + mount = httpp_getvar (client->parser, HTTPP_VAR_URI); + mount = util_url_escape (mount); + ipaddr = util_url_escape (client->con->ip); + + snprintf (post, sizeof (post), + "action=auth&server=%s&port=%d&client=%lu&mount=%s" + "&user=%s&pass=%s&ip=%s&agent=%s", + server, port, client->con->id, mount, username, + password, ipaddr, user_agent); + free (server); + free (mount); + free (user_agent); + free (username); + free (password); + free (ipaddr); + + curl_easy_setopt (url->handle, CURLOPT_URL, url->addurl); + curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post); + curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user); + url->errormsg[0] = '\0'; + + res = curl_easy_perform (url->handle); + + if (res) + { + WARN2 ("auth to server %s failed with %s", url->addurl, url->errormsg); + return AUTH_FAILED; + } + /* we received a response, lets see what it is */ + if (client->authenticated) + return AUTH_OK; + INFO2 ("client auth (%s) failed with \"%s\"", url->addurl, url->errormsg); + return AUTH_FAILED; +} + + +/* 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); + 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=start&mount=%s&server=%s&port=%d", mount, server, port); + free (server); + free (mount); + + 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)) + WARN2 ("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); + 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=end&mount=%s&server=%s&port=%d", mount, server, port); + free (server); + free (mount); + + curl_easy_setopt (url->handle, CURLOPT_URL, stream_end_url); + curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post); + curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user); + + if (curl_easy_perform (url->handle)) + WARN2 ("auth to server %s failed with %s", stream_end_url, url->errormsg); + + auth_release (auth); + free (stream_end_url); + return; +} + + +static auth_result auth_url_adduser(auth_t *auth, const char *username, const char *password) +{ + return AUTH_FAILED; +} + +static auth_result auth_url_deleteuser (auth_t *auth, const char *username) +{ + return AUTH_FAILED; +} + +static auth_result auth_url_listuser (auth_t *auth, xmlNodePtr srcnode) +{ + return AUTH_FAILED; +} + +int auth_get_url_auth (auth_t *authenticator, config_options_t *options) +{ + auth_url *url_info; + + authenticator->authenticate = url_add_client; + authenticator->release_client = url_remove_client; + + authenticator->free = auth_url_clear; + authenticator->adduser = auth_url_adduser; + authenticator->deleteuser = auth_url_deleteuser; + authenticator->listuser = auth_url_listuser; + + authenticator->stream_start = url_stream_start; + authenticator->stream_end = url_stream_end; + + url_info = calloc(1, sizeof(auth_url)); + url_info->auth_header = strdup ("icecast-auth-user: 1\r\n"); + + while(options) { + if(!strcmp(options->name, "username")) + url_info->username = strdup (options->value); + if(!strcmp(options->name, "password")) + url_info->password = strdup (options->value); + if(!strcmp(options->name, "add")) + url_info->addurl = strdup (options->value); + if(!strcmp(options->name, "remove")) + url_info->removeurl = strdup (options->value); + if(!strcmp(options->name, "start")) + url_info->stream_start = strdup (options->value); + if(!strcmp(options->name, "end")) + url_info->stream_end = strdup (options->value); + if(!strcmp(options->name, "auth_header")) + { + free (url_info->auth_header); + url_info->auth_header = strdup (options->value); + } + options = options->next; + } + url_info->handle = curl_easy_init (); + if (url_info->handle == NULL) + { + free (url_info); + return -1; + } + if (url_info->auth_header) + url_info->auth_header_len = strlen (url_info->auth_header); + + curl_easy_setopt (url_info->handle, CURLOPT_USERAGENT, ICECAST_VERSION_STRING); + curl_easy_setopt (url_info->handle, CURLOPT_HEADERFUNCTION, handle_returned_header); + curl_easy_setopt (url_info->handle, CURLOPT_WRITEFUNCTION, handle_returned_data); + curl_easy_setopt (url_info->handle, CURLOPT_WRITEDATA, url_info->handle); + curl_easy_setopt (url_info->handle, CURLOPT_NOSIGNAL, 1L); + curl_easy_setopt (url_info->handle, CURLOPT_TIMEOUT, 15L); + curl_easy_setopt (url_info->handle, CURLOPT_ERRORBUFFER, &url_info->errormsg[0]); + + authenticator->state = url_info; + INFO0("URL based authentication setup"); + return 0; +} + diff --git a/src/auth_url.h b/src/auth_url.h new file mode 100644 index 00000000..35878f36 --- /dev/null +++ b/src/auth_url.h @@ -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 , + * oddsock , + * Karl Heyes + * and others (see AUTHORS for details). + */ + +#ifndef __AUTH_URL_H__ +#define __AUTH_URL_H__ + +#ifdef HAVE_CONFIG_H +#include +#endif + +int *auth_get_url_auth (auth_t *authenticator, config_options_t *options); + +#endif + +