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) ...@@ -371,17 +371,22 @@ void admin_handle_request(client_t *client, const char *uri)
return; return;
} }
/* This is a mount request, handle it as such */ /* 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 " case 0:
"request (command: %s)", command_string); break;
client_send_401(client); default:
return; 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); avl_tree_rlock(global.source_tree);
source = source_find_mount_raw(mount); source = source_find_mount_raw(mount);
......
...@@ -32,12 +32,13 @@ ...@@ -32,12 +32,13 @@
#include "stats.h" #include "stats.h"
#include "httpp/httpp.h" #include "httpp/httpp.h"
#include "fserve.h" #include "fserve.h"
#include "admin.h"
#include "logging.h" #include "logging.h"
#define CATMODULE "auth" #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) 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) ...@@ -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 /* Callback from auth thread to handle a stream start event, this applies
* to both source clients and relays. * to both source clients and relays.
*/ */
...@@ -478,6 +498,30 @@ int auth_postprocess_listener (auth_client *auth_user) ...@@ -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 /* Add a listener. Check for any mount information that states any
* authentication to be used. * authentication to be used.
*/ */
...@@ -654,6 +698,25 @@ auth_t *auth_get_authenticator (xmlNodePtr node) ...@@ -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 /* called when the stream starts, so that authentication engine can do any
* cleanup/initialisation. * cleanup/initialisation.
*/ */
...@@ -696,12 +759,10 @@ void auth_stream_end (mount_proxy *mountinfo, const char *mount) ...@@ -696,12 +759,10 @@ void auth_stream_end (mount_proxy *mountinfo, const char *mount)
void auth_initialise (void) void auth_initialise (void)
{ {
thread_mutex_create (&auth_lock);
} }
void auth_shutdown (void) void auth_shutdown (void)
{ {
thread_mutex_destroy (&auth_lock);
INFO0 ("Auth shutdown"); INFO0 ("Auth shutdown");
} }
...@@ -55,6 +55,9 @@ typedef struct auth_tag ...@@ -55,6 +55,9 @@ typedef struct auth_tag
auth_result (*authenticate)(auth_client *aclient); auth_result (*authenticate)(auth_client *aclient);
auth_result (*release_listener)(auth_client *auth_user); 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 */ /* auth handler for source startup, no client passed as it may disappear */
void (*stream_start)(auth_client *auth_user); void (*stream_start)(auth_client *auth_user);
...@@ -92,12 +95,15 @@ void auth_shutdown (void); ...@@ -92,12 +95,15 @@ void auth_shutdown (void);
auth_t *auth_get_authenticator (xmlNodePtr node); auth_t *auth_get_authenticator (xmlNodePtr node);
void auth_release (auth_t *authenticator); 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); 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); 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 /* called from auth thread, after the client has successfully authenticated
* and requires adding to source or fserve. */ * and requires adding to source or fserve. */
int auth_postprocess_listener (auth_client *auth_user); int auth_postprocess_listener (auth_client *auth_user);
......
...@@ -46,6 +46,15 @@ ...@@ -46,6 +46,15 @@
* *
* action=mount_add&mount=/live&server=myserver.com&port=8000 * action=mount_add&mount=/live&server=myserver.com&port=8000
* action=mount_remove&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 #ifdef HAVE_CONFIG_H
...@@ -80,6 +89,7 @@ typedef struct { ...@@ -80,6 +89,7 @@ typedef struct {
char *removeurl; char *removeurl;
char *stream_start; char *stream_start;
char *stream_end; char *stream_end;
char *stream_auth;
char *username; char *username;
char *password; char *password;
char *auth_header; char *auth_header;
...@@ -447,6 +457,52 @@ static void url_stream_end (auth_client *auth_user) ...@@ -447,6 +457,52 @@ static void url_stream_end (auth_client *auth_user)
return; 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) 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) ...@@ -516,6 +572,12 @@ int auth_get_url_auth (auth_t *authenticator, config_options_t *options)
free (url_info->stream_end); free (url_info->stream_end);
url_info->stream_end = strdup (options->value); 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")) if(!strcmp(options->name, "auth_header"))
{ {
free (url_info->auth_header); free (url_info->auth_header);
......
...@@ -122,6 +122,36 @@ void client_destroy(client_t *client) ...@@ -122,6 +122,36 @@ void client_destroy(client_t *client)
free(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 */ /* helper function for reading data from a client */
int client_read_bytes (client_t *client, void *buf, unsigned len) int client_read_bytes (client_t *client, void *buf, unsigned len)
......
...@@ -77,5 +77,6 @@ void client_send_400(client_t *client, char *message); ...@@ -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_send_bytes (client_t *client, const void *buf, unsigned len);
int client_read_bytes (client_t *client, 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); void client_set_queue (client_t *client, refbuf_t *refbuf);
int client_check_source_auth (client_t *client, const char *mount);
#endif /* __CLIENT_H__ */ #endif /* __CLIENT_H__ */
...@@ -968,29 +968,17 @@ int connection_check_relay_pass(http_parser_t *parser) ...@@ -968,29 +968,17 @@ int connection_check_relay_pass(http_parser_t *parser)
return ret; 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 ret;
int ice_login = config->ice_login;
const char *protocol; 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) { if(!pass) {
WARN0("No source password set, rejecting source"); WARN0("No source password set, rejecting source");
config_release_config(); return -1;
return 0;
} }
protocol = httpp_getvar(parser, HTTPP_VAR_PROTOCOL); protocol = httpp_getvar(parser, HTTPP_VAR_PROTOCOL);
...@@ -999,22 +987,24 @@ int connection_check_source_pass(http_parser_t *parser, const char *mount) ...@@ -999,22 +987,24 @@ int connection_check_source_pass(http_parser_t *parser, const char *mount)
} }
else { else {
ret = _check_pass_http(parser, user, pass); ret = _check_pass_http(parser, user, pass);
if(!ret && ice_login) if (!ret)
{ {
ret = _check_pass_ice(parser, pass); ice_config_t *config = config_get_config_unlocked();
if(ret) if (config->ice_login)
WARN0("Source is using deprecated icecast login"); {
ret = _check_pass_ice(parser, pass);
if(ret)
WARN0("Source is using deprecated icecast login");
}
} }
} }
config_release_config();
return ret; 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); INFO1("Source logging in at mountpoint \"%s\"", uri);
if (uri[0] != '/') if (uri[0] != '/')
...@@ -1023,24 +1013,30 @@ static void _handle_source_request (client_t *client, char *uri, int auth_style) ...@@ -1023,24 +1013,30 @@ static void _handle_source_request (client_t *client, char *uri, int auth_style)
client_send_401 (client); client_send_401 (client);
return; return;
} }
if (auth_style == ICECAST_SOURCE_AUTH) { switch (client_check_source_auth (client, uri))
if (connection_check_source_pass (client->parser, uri) == 0) {
{ case 0: /* authenticated from config file */
/* We commonly get this if the source client is using the wrong source_startup (client, uri, ICECAST_SOURCE_AUTH);
* protocol: attempt to diagnose this and return an error break;
*/
/* TODO: Do what the above comment says */ case 1: /* auth pending */
break;
default: /* failed */
INFO1("Source (%s) attempted to login with invalid or missing password", uri); INFO1("Source (%s) attempted to login with invalid or missing password", uri);
client_send_401(client); 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); source = source_reserve (uri);
if (source) if (source)
{ {
if (auth_style == SHOUTCAST_SOURCE_AUTH) {
source->shoutcast_compat = 1;
}
source->client = client; source->client = client;
source->parser = client->parser; source->parser = client->parser;
source->con = client->con; source->con = client->con;
...@@ -1048,6 +1044,13 @@ static void _handle_source_request (client_t *client, char *uri, int auth_style) ...@@ -1048,6 +1044,13 @@ static void _handle_source_request (client_t *client, char *uri, int auth_style)
{ {
source_clear_source (source); source_clear_source (source);
source_free_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 else
{ {
...@@ -1241,7 +1244,7 @@ static void _handle_shoutcast_compatible (client_queue_t *node) ...@@ -1241,7 +1244,7 @@ static void _handle_shoutcast_compatible (client_queue_t *node)
memmove (ptr, ptr + node->stream_offset, client->refbuf->len); memmove (ptr, ptr + node->stream_offset, client->refbuf->len);
} }
client->parser = parser; client->parser = parser;
_handle_source_request (client, shoutcast_mount, SHOUTCAST_SOURCE_AUTH); source_startup (client, shoutcast_mount, SHOUTCAST_SOURCE_AUTH);
} }
else { else {
httpp_destroy (parser); httpp_destroy (parser);
...@@ -1322,7 +1325,7 @@ static void _handle_connection(void) ...@@ -1322,7 +1325,7 @@ static void _handle_connection(void)
} }
if (parser->req_type == httpp_req_source) { 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) { else if (parser->req_type == httpp_req_stats) {
_handle_stats_request (client, uri); _handle_stats_request (client, uri);
......
...@@ -60,7 +60,7 @@ void connection_close(connection_t *con); ...@@ -60,7 +60,7 @@ void connection_close(connection_t *con);
connection_t *connection_create (sock_t sock, sock_t serversock, char *ip); connection_t *connection_create (sock_t sock, sock_t serversock, char *ip);
int connection_complete_source (struct source_tag *source, int response); 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_relay_pass(http_parser_t *parser);
int connection_check_admin_pass(http_parser_t *parser); int connection_check_admin_pass(http_parser_t *parser);
......
...@@ -80,6 +80,7 @@ typedef struct source_tag ...@@ -80,6 +80,7 @@ typedef struct source_tag
source_t *source_reserve (const char *mount); source_t *source_reserve (const char *mount);
void *source_client_thread (void *arg); 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_client_callback (client_t *client, void *source);
void source_update_settings (ice_config_t *config, source_t *source, mount_proxy *mountinfo); void source_update_settings (ice_config_t *config, source_t *source, mount_proxy *mountinfo);
void source_clear_source (source_t *source); 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