Commit e6dfee63 authored by Karl Heyes's avatar Karl Heyes

Allow source client authentication via auth handler. Here the URL handler can

issue requests (using ithe stream_auth option) to allow external engines to
determine whether a client can stream or not. Admin requests using source auth
are able to use this mechanism however source clients using the icy protocol
cannot yet.


svn path=/icecast/trunk/icecast/; revision=15621
parent d49fd42e
......@@ -371,17 +371,22 @@ void admin_handle_request(client_t *client, const char *uri)
return;
}
/* This is a mount request, handle it as such */
if (!connection_check_admin_pass(client->parser))
if (client->authenticated == 0 && !connection_check_admin_pass(client->parser))
{
if (!connection_check_source_pass(client->parser, mount))
switch (client_check_source_auth (client, mount))
{
INFO1("Bad or missing password on mount modification admin "
"request (command: %s)", command_string);
client_send_401(client);
return;
case 0:
break;
default:
INFO1("Bad or missing password on mount modification admin "
"request (command: %s)", command_string);
client_send_401(client);
/* fall through */
case 1:
return;
}
}
avl_tree_rlock(global.source_tree);
source = source_find_mount_raw(mount);
......
......@@ -32,12 +32,13 @@
#include "stats.h"
#include "httpp/httpp.h"
#include "fserve.h"
#include "admin.h"
#include "logging.h"
#define CATMODULE "auth"
static mutex_t auth_lock;
static void auth_postprocess_source (auth_client *auth_user);
static auth_client *auth_client_setup (const char *mount, client_t *client)
......@@ -236,6 +237,25 @@ static void auth_remove_listener (auth_t *auth, auth_client *auth_user)
}
/* Called from auth thread to process any request for source client
* authentication. Only applies to source clients, not relays.
*/
static void stream_auth_callback (auth_t *auth, auth_client *auth_user)
{
client_t *client = auth_user->client;
if (auth->stream_auth)
auth->stream_auth (auth_user);
auth_release (auth);
client->auth = NULL;
if (client->authenticated)
auth_postprocess_source (auth_user);
else
WARN1 ("Failed auth for source \"%s\"", auth_user->mount);
}
/* Callback from auth thread to handle a stream start event, this applies
* to both source clients and relays.
*/
......@@ -478,6 +498,30 @@ int auth_postprocess_listener (auth_client *auth_user)
}
/* Decide whether we need to start a source or just process a source
* admin request.
*/
void auth_postprocess_source (auth_client *auth_user)
{
client_t *client = auth_user->client;
const char *mount = auth_user->mount;
const char *req = httpp_getvar (client->parser, HTTPP_VAR_URI);
auth_user->client = NULL;
client->authenticated = 1;
if (strcmp (req, "/admin.cgi") == 0 || strncmp ("/admin/metadata", req, 15) == 0)
{
DEBUG2 ("metadata request (%s, %s)", req, mount);
admin_handle_request (client, "/admin/metadata");
}
else
{
DEBUG1 ("on mountpoint %s", mount);
source_startup (client, mount, 0);
}
}
/* Add a listener. Check for any mount information that states any
* authentication to be used.
*/
......@@ -654,6 +698,25 @@ auth_t *auth_get_authenticator (xmlNodePtr node)
}
/* Called when a source client connects and requires authentication via the
* authenticator. This is called for both source clients and admin requests
* that work on a specified mountpoint.
*/
int auth_stream_authenticate (client_t *client, const char *mount, mount_proxy *mountinfo)
{
if (mountinfo && mountinfo->auth && mountinfo->auth->stream_auth)
{
auth_client *auth_user = auth_client_setup (mount, client);
auth_user->process = stream_auth_callback;
INFO1 ("request source auth for \"%s\"", mount);
queue_auth_client (auth_user, mountinfo);
return 1;
}
return 0;
}
/* called when the stream starts, so that authentication engine can do any
* cleanup/initialisation.
*/
......@@ -696,12 +759,10 @@ void auth_stream_end (mount_proxy *mountinfo, const char *mount)
void auth_initialise (void)
{
thread_mutex_create (&auth_lock);
}
void auth_shutdown (void)
{
thread_mutex_destroy (&auth_lock);
INFO0 ("Auth shutdown");
}
......@@ -55,6 +55,9 @@ typedef struct auth_tag
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);
......@@ -92,12 +95,15 @@ void auth_shutdown (void);
auth_t *auth_get_authenticator (xmlNodePtr node);
void auth_release (auth_t *authenticator);
/* call to send a url request when source starts */
/* call to trigger an event when a stream starts */
void auth_stream_start (struct _mount_proxy *mountinfo, const char *mount);
/* call to send a url request when source ends */
/* call to trigger an event when a stream ends */
void auth_stream_end (struct _mount_proxy *mountinfo, const char *mount);
/* 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);
......
......@@ -46,6 +46,15 @@
*
* 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
*
* action=stream_auth&mount=/stream&ip=IP&server=SERVER&port=8000&user=fred&pass=pass
*
* As admin requests can come in for a stream (eg metadata update) these requests
* can be issued while stream is active. For these &admin=1 is added to the POST
* details.
*/
#ifdef HAVE_CONFIG_H
......@@ -80,6 +89,7 @@ typedef struct {
char *removeurl;
char *stream_start;
char *stream_end;
char *stream_auth;
char *username;
char *password;
char *auth_header;
......@@ -447,6 +457,52 @@ static void url_stream_end (auth_client *auth_user)
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))
WARN2 ("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)
{
......@@ -516,6 +572,12 @@ int auth_get_url_auth (auth_t *authenticator, config_options_t *options)
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);
......
......@@ -122,6 +122,36 @@ void client_destroy(client_t *client)
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);
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)
......
......@@ -77,5 +77,6 @@ void client_send_400(client_t *client, char *message);
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__ */
......@@ -968,29 +968,17 @@ int connection_check_relay_pass(http_parser_t *parser)
return ret;
}
int connection_check_source_pass(http_parser_t *parser, const char *mount)
/* return 0 for failed, 1 for ok
*/
int connection_check_pass (http_parser_t *parser, const char *user, const char *pass)
{
ice_config_t *config = config_get_config();
char *pass = config->source_password;
char *user = "source";
int ret;
int ice_login = config->ice_login;
const char *protocol;
mount_proxy *mountinfo = config_find_mount (config, mount);
if (mountinfo)
{
if (mountinfo->password)
pass = mountinfo->password;
if (mountinfo->username)
user = mountinfo->username;
}
if(!pass) {
WARN0("No source password set, rejecting source");
config_release_config();
return 0;
return -1;
}
protocol = httpp_getvar(parser, HTTPP_VAR_PROTOCOL);
......@@ -999,22 +987,24 @@ int connection_check_source_pass(http_parser_t *parser, const char *mount)
}
else {
ret = _check_pass_http(parser, user, pass);
if(!ret && ice_login)
if (!ret)
{
ret = _check_pass_ice(parser, pass);
if(ret)
WARN0("Source is using deprecated icecast login");
ice_config_t *config = config_get_config_unlocked();
if (config->ice_login)
{
ret = _check_pass_ice(parser, pass);
if(ret)
WARN0("Source is using deprecated icecast login");
}
}
}
config_release_config();
return ret;
}
static void _handle_source_request (client_t *client, char *uri, int auth_style)
/* only called for native icecast source clients */
static void _handle_source_request (client_t *client, const char *uri)
{
source_t *source;
INFO1("Source logging in at mountpoint \"%s\"", uri);
if (uri[0] != '/')
......@@ -1023,24 +1013,30 @@ static void _handle_source_request (client_t *client, char *uri, int auth_style)
client_send_401 (client);
return;
}
if (auth_style == ICECAST_SOURCE_AUTH) {
if (connection_check_source_pass (client->parser, uri) == 0)
{
/* We commonly get this if the source client is using the wrong
* protocol: attempt to diagnose this and return an error
*/
/* TODO: Do what the above comment says */
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 */
INFO1("Source (%s) attempted to login with invalid or missing password", uri);
client_send_401(client);
return;
}
break;
}
}
void source_startup (client_t *client, const char *uri, int auth_style)
{
source_t *source;
source = source_reserve (uri);
if (source)
{
if (auth_style == SHOUTCAST_SOURCE_AUTH) {
source->shoutcast_compat = 1;
}
source->client = client;
source->parser = client->parser;
source->con = client->con;
......@@ -1048,6 +1044,13 @@ static void _handle_source_request (client_t *client, char *uri, int auth_style)
{
source_clear_source (source);
source_free_source (source);
return;
}
client->respcode = 200;
if (auth_style == SHOUTCAST_SOURCE_AUTH)
{
source->shoutcast_compat = 1;
source_client_callback (client, source);
}
else
{
......@@ -1241,7 +1244,7 @@ static void _handle_shoutcast_compatible (client_queue_t *node)
memmove (ptr, ptr + node->stream_offset, client->refbuf->len);
}
client->parser = parser;
_handle_source_request (client, shoutcast_mount, SHOUTCAST_SOURCE_AUTH);
source_startup (client, shoutcast_mount, SHOUTCAST_SOURCE_AUTH);
}
else {
httpp_destroy (parser);
......@@ -1322,7 +1325,7 @@ static void _handle_connection(void)
}
if (parser->req_type == httpp_req_source) {
_handle_source_request (client, uri, ICECAST_SOURCE_AUTH);
_handle_source_request (client, uri);
}
else if (parser->req_type == httpp_req_stats) {
_handle_stats_request (client, uri);
......
......@@ -60,7 +60,7 @@ 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_source_pass(http_parser_t *parser, const char *mount);
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);
......
......@@ -80,6 +80,7 @@ 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);
......
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