Commit 913ec348 authored by Michael Smith's avatar Michael Smith

HTTP Basic source login support. The old "ice-password" method is still

available, but is deprecated and turned off by default.

svn path=/trunk/icecast/; revision=3837
parent def074c8
......@@ -55,3 +55,12 @@ void client_send_404(client_t *client, char *message) {
client_destroy(client);
}
void client_send_401(client_t *client) {
int bytes = sock_write(client->con->sock,
"HTTP/1.0 401 Authentication Required\r\n"
"WWW-Authenticate: Basic realm=\"Icecast2 Server\"\r\n"
"\r\n"
"You need to authenticate\r\n");
if(bytes > 0) client->con->sent_bytes = bytes;
client_destroy(client);
}
......@@ -25,5 +25,6 @@ typedef struct _client_tag
client_t *client_create(connection_t *con, http_parser_t *parser);
void client_destroy(client_t *client);
void client_send_404(client_t *client, char *message);
void client_send_401(client_t *client);
#endif /* __CLIENT_H__ */
......@@ -14,6 +14,7 @@
#define CONFIG_DEFAULT_HEADER_TIMEOUT 15
#define CONFIG_DEFAULT_SOURCE_TIMEOUT 10
#define CONFIG_DEFAULT_SOURCE_PASSWORD "changeme"
#define CONFIG_DEFAULT_ICE_LOGIN 0
#define CONFIG_DEFAULT_TOUCH_FREQ 5
#define CONFIG_DEFAULT_HOSTNAME "localhost"
#define CONFIG_DEFAULT_PORT 8888
......@@ -156,6 +157,7 @@ static void _set_defaults(void)
_configuration.header_timeout = CONFIG_DEFAULT_HEADER_TIMEOUT;
_configuration.source_timeout = CONFIG_DEFAULT_SOURCE_TIMEOUT;
_configuration.source_password = CONFIG_DEFAULT_SOURCE_PASSWORD;
_configuration.ice_login = CONFIG_DEFAULT_ICE_LOGIN;
_configuration.touch_freq = CONFIG_DEFAULT_TOUCH_FREQ;
_configuration.dir_list = NULL;
_configuration.hostname = CONFIG_DEFAULT_HOSTNAME;
......@@ -200,6 +202,10 @@ static void _parse_root(xmlDocPtr doc, xmlNodePtr node)
if (_configuration.source_password && _configuration.source_password != CONFIG_DEFAULT_SOURCE_PASSWORD) xmlFree(_configuration.source_password);
_configuration.source_password = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
}
} else if (strcmp(node->name, "icelogin") == 0) {
tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
_configuration.ice_login = atoi(tmp);
if (tmp) xmlFree(tmp);
} else if (strcmp(node->name, "hostname") == 0) {
if (_configuration.hostname && _configuration.hostname != CONFIG_DEFAULT_HOSTNAME) xmlFree(_configuration.hostname);
_configuration.hostname = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
......
......@@ -24,6 +24,7 @@ typedef struct ice_config_tag
int client_timeout;
int header_timeout;
int source_timeout;
int ice_login;
char *source_password;
......
......@@ -286,9 +286,11 @@ static connection_t *_get_connection(void)
return con;
}
int connection_create_source(connection_t *con, http_parser_t *parser, char *mount) {
int connection_create_source(client_t *client, connection_t *con, http_parser_t *parser, char *mount) {
source_t *source;
char *contenttype;
int bytes;
/* check to make sure this source wouldn't
** be over the limit
*/
......@@ -311,12 +313,16 @@ int connection_create_source(connection_t *con, http_parser_t *parser, char *mou
WARN1("Content-type \"%s\" not supported, dropping source", contenttype);
goto fail;
} else {
source = source_create(con, parser, mount, format);
source = source_create(client, con, parser, mount, format);
}
} else {
WARN0("No content-type header, cannot handle source");
goto fail;
}
bytes = sock_write(client->con->sock,
"HTTP/1.0 200 OK\r\n\r\n");
if(bytes > 0) client->con->sent_bytes = bytes;
source->shutdown_rwlock = &_source_shutdown_rwlock;
sock_set_blocking(con->sock, SOCK_NONBLOCK);
thread_create("Source Thread", source_main, (void *)source, THREAD_DETACHED);
......@@ -331,7 +337,46 @@ fail:
return 0;
}
static int _check_source_pass(http_parser_t *parser)
static int _check_source_pass_http(http_parser_t *parser)
{
/* This will look something like "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" */
char *header = httpp_getvar(parser, "authorization");
char *userpass, *tmp;
char *username, *password;
char *correctpass;
correctpass = config_get_config()->source_password;
if(!correctpass)
correctpass = "";
if(header == NULL)
return 0;
if(strncmp(header, "Basic ", 6))
return 0;
userpass = util_base64_decode(header+6);
if(userpass == NULL)
return 0;
tmp = strchr(userpass, ':');
if(!tmp) {
free(userpass);
return 0;
}
*tmp = 0;
username = userpass;
password = tmp+1;
if(strcmp(username, "source") || strcmp(password, correctpass)) {
free(userpass);
return 0;
}
return 1;
}
static int _check_source_pass_ice(http_parser_t *parser)
{
char *password, *correctpass;
......@@ -348,16 +393,27 @@ static int _check_source_pass(http_parser_t *parser)
return 1;
}
static int _check_source_pass(http_parser_t *parser)
{
if(config_get_config()->ice_login)
return _check_source_pass_ice(parser);
else
return _check_source_pass_http(parser);
}
static void _handle_source_request(connection_t *con,
http_parser_t *parser, char *uri)
{
client_t *client;
client = client_create(con, parser);
INFO1("Source logging in at mountpoint \"%s\"", uri);
stats_event_inc(NULL, "source_connections");
if (!_check_source_pass(parser)) {
INFO1("Source (%s) attempted to login with bad password", uri);
connection_close(con);
httpp_destroy(parser);
client_send_401(client);
return;
}
......@@ -368,16 +424,14 @@ static void _handle_source_request(connection_t *con,
avl_tree_rlock(global.source_tree);
if (source_find_mount(uri) != NULL) {
INFO1("Source tried to log in as %s, but mountpoint is already used", uri);
connection_close(con);
httpp_destroy(parser);
client_send_404(client, "Mountpoint in use");
avl_tree_unlock(global.source_tree);
return;
}
avl_tree_unlock(global.source_tree);
if (!connection_create_source(con, parser, uri)) {
connection_close(con);
httpp_destroy(parser);
if (!connection_create_source(client, con, parser, uri)) {
client_destroy(client);
}
}
......@@ -432,6 +486,11 @@ static void _handle_get_request(connection_t *con,
*/
/* TODO: add GUID-xxxxxx */
if (strcmp(uri, "/stats.xml") == 0) {
if (!_check_source_pass(parser)) {
INFO1("Source (%s) attempted to login with bad password", uri);
client_send_401(client);
return;
}
DEBUG0("Stats request, sending xml stats");
stats_sendxml(client);
client_destroy(client);
......@@ -491,7 +550,7 @@ static void _handle_get_request(connection_t *con,
if (strcmp(uri, "/allstreams.txt") == 0) {
if (!_check_source_pass(parser)) {
INFO0("Client attempted to fetch allstreams.txt with bad password");
client_send_404(client, "Bad ice-password");
client_send_401(client);
} else {
avl_node *node;
source_t *s;
......
......@@ -7,6 +7,8 @@
#include "thread.h"
#include "sock.h"
struct _client_tag;
typedef struct connection_tag
{
unsigned long id;
......@@ -26,8 +28,8 @@ void connection_shutdown(void);
void connection_accept_loop(void);
void connection_close(connection_t *con);
connection_t *create_connection(sock_t sock, char *ip);
int connection_create_source(connection_t *con, http_parser_t *parser,
char *mount);
int connection_create_source(struct _client_tag *client, connection_t *con,
http_parser_t *parser, char *mount);
extern rwlock_t _source_shutdown_rwlock;
......
......@@ -5,6 +5,7 @@
#include "connection.h"
#include "refbuf.h"
#include "format.h"
#include "client.h"
#include "source.h"
#include "global.h"
......
......@@ -70,6 +70,7 @@ static void *_slave_thread(void *arg) {
char header[4096];
connection_t *con;
http_parser_t *parser;
client_t *client;
int interval = config_get_config()->master_update_interval;
while (_initialized) {
......@@ -85,6 +86,7 @@ static void *_slave_thread(void *arg) {
WARN0("Relay slave failed to contact master server to fetch stream list");
continue;
}
// FIXME: This is now broken...
sock_write(mastersock, "GET /allstreams.txt HTTP/1.0\r\nice-password: %s\r\n\r\n", config_get_config()->source_password);
while (sock_read_line(mastersock, buf, sizeof(buf))) {
buf[strlen(buf)] = 0;
......@@ -119,10 +121,10 @@ static void *_slave_thread(void *arg) {
continue;
}
if (!connection_create_source(con, parser,
client = client_create(con, parser);
if (!connection_create_source(client, con, parser,
httpp_getvar(parser, HTTPP_VAR_URI))) {
connection_close(con);
httpp_destroy(parser);
client_destroy(client);
}
continue;
......
......@@ -39,11 +39,12 @@ static int _compare_clients(void *compare_arg, void *a, void *b);
static int _remove_client(void *key);
static int _free_client(void *key);
source_t *source_create(connection_t *con, http_parser_t *parser, const char *mount, format_type_t type)
source_t *source_create(client_t *client, connection_t *con, http_parser_t *parser, const char *mount, format_type_t type)
{
source_t *src;
src = (source_t *)malloc(sizeof(source_t));
src->client = client;
src->mount = (char *)strdup(mount);
src->format = format_get_plugin(type, src->mount);
src->con = con;
......@@ -94,8 +95,7 @@ int source_free_source(void *key)
source_t *source = (source_t *)key;
free(source->mount);
connection_close(source->con);
httpp_destroy(source->parser);
client_destroy(source->client);
avl_tree_free(source->pending_tree, _free_client);
avl_tree_free(source->client_tree, _free_client);
source->format->free_plugin(source->format);
......
......@@ -3,6 +3,7 @@
typedef struct source_tag
{
client_t *client;
connection_t *con;
http_parser_t *parser;
......@@ -15,7 +16,7 @@ typedef struct source_tag
rwlock_t *shutdown_rwlock;
} source_t;
source_t *source_create(connection_t *con, http_parser_t *parser, const char *mount, format_type_t type);
source_t *source_create(client_t *client, connection_t *con, http_parser_t *parser, const char *mount, format_type_t type);
source_t *source_find_mount(const char *mount);
int source_compare_sources(void *arg, void *a, void *b);
int source_free_source(void *key);
......
......@@ -268,3 +268,102 @@ char *util_normalise_uri(char *uri) {
}
}
static char base64table[64] = {
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
};
/* This isn't efficient, but it doesn't need to be */
char *util_base64_encode(char *data)
{
int len = strlen(data);
char *out = malloc(len*4/3 + 4);
char *result = out;
int chunk;
while(len > 0) {
chunk = (len >3)?3:len;
*out++ = base64table[(*data & 0xFC)>>2];
*out++ = base64table[((*data & 0x03)<<4) | ((*(data+1) & 0xF0) >> 4)];
switch(chunk) {
case 3:
*out++ = base64table[((*(data+1) & 0x0F)<<2) | ((*(data+2) & 0xC0)>>6)];
*out++ = base64table[(*(data+2)) & 0x3F];
break;
case 2:
*out++ = base64table[((*(data+1) & 0x0F)<<2)];
*out++ = '=';
break;
case 1:
*out++ = '=';
*out++ = '=';
break;
}
data += chunk;
len -= chunk;
}
return result;
}
static int base64chartoval(char input)
{
if(input >= 'A' && input <= 'Z')
return input - 'A';
else if(input >= 'a' && input <= 'z')
return input - 'a' + 26;
else if(input >= '0' && input <= '9')
return input - '0' + 52;
else if(input == '+')
return 62;
else if(input == '/')
return 63;
else if(input == '=')
return -1;
else
return -2;
}
char *util_base64_decode(char *input)
{
int len = strlen(input);
char *out = malloc(len*3/4 + 5);
char *result = out;
signed char vals[4];
while(len > 0) {
if(len < 4)
{
free(result);
return NULL; /* Invalid Base64 data */
}
vals[0] = base64chartoval(*input++);
vals[1] = base64chartoval(*input++);
vals[2] = base64chartoval(*input++);
vals[3] = base64chartoval(*input++);
if(vals[0] < 0 || vals[1] < 0 || vals[2] < -1 || vals[3] < -1) {
continue;
}
*out++ = vals[0]<<2 | vals[1]>>4;
if(vals[2] >= 0)
*out++ = ((vals[1]&0x0F)<<4) | (vals[2]>>2);
else
*out++ = 0;
if(vals[3] >= 0)
*out++ = ((vals[2]&0x03)<<6) | (vals[3]);
else
*out++ = 0;
len -= 4;
}
*out = 0;
return result;
}
......@@ -11,5 +11,7 @@ char *util_get_extension(char *path);
char *util_get_path_from_uri(char *uri);
char *util_get_path_from_normalised_uri(char *uri);
char *util_normalise_uri(char *uri);
char *util_base64_encode(char *data);
char *util_base64_decode(char *input);
#endif /* __UTIL_H__ */
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