mirror of
https://gitlab.xiph.org/xiph/icecast-server.git
synced 2025-02-02 15:07:36 -05:00
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
This commit is contained in:
parent
d49fd42eec
commit
e6dfee632c
19
src/admin.c
19
src/admin.c
@ -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);
|
||||
|
||||
|
67
src/auth.c
67
src/auth.c
@ -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");
|
||||
}
|
||||
|
||||
|
10
src/auth.h
10
src/auth.h
@ -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);
|
||||
|
30
src/client.c
30
src/client.c
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user