OSUOSL/Nero are experiencing Internet connectivity problems. This affects us as we're hosted with OSUOSL. We apologize for the inconvenience.

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