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) { ...@@ -55,3 +55,12 @@ void client_send_404(client_t *client, char *message) {
client_destroy(client); 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 ...@@ -25,5 +25,6 @@ typedef struct _client_tag
client_t *client_create(connection_t *con, http_parser_t *parser); client_t *client_create(connection_t *con, http_parser_t *parser);
void client_destroy(client_t *client); void client_destroy(client_t *client);
void client_send_404(client_t *client, char *message); void client_send_404(client_t *client, char *message);
void client_send_401(client_t *client);
#endif /* __CLIENT_H__ */ #endif /* __CLIENT_H__ */
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#define CONFIG_DEFAULT_HEADER_TIMEOUT 15 #define CONFIG_DEFAULT_HEADER_TIMEOUT 15
#define CONFIG_DEFAULT_SOURCE_TIMEOUT 10 #define CONFIG_DEFAULT_SOURCE_TIMEOUT 10
#define CONFIG_DEFAULT_SOURCE_PASSWORD "changeme" #define CONFIG_DEFAULT_SOURCE_PASSWORD "changeme"
#define CONFIG_DEFAULT_ICE_LOGIN 0
#define CONFIG_DEFAULT_TOUCH_FREQ 5 #define CONFIG_DEFAULT_TOUCH_FREQ 5
#define CONFIG_DEFAULT_HOSTNAME "localhost" #define CONFIG_DEFAULT_HOSTNAME "localhost"
#define CONFIG_DEFAULT_PORT 8888 #define CONFIG_DEFAULT_PORT 8888
...@@ -156,6 +157,7 @@ static void _set_defaults(void) ...@@ -156,6 +157,7 @@ static void _set_defaults(void)
_configuration.header_timeout = CONFIG_DEFAULT_HEADER_TIMEOUT; _configuration.header_timeout = CONFIG_DEFAULT_HEADER_TIMEOUT;
_configuration.source_timeout = CONFIG_DEFAULT_SOURCE_TIMEOUT; _configuration.source_timeout = CONFIG_DEFAULT_SOURCE_TIMEOUT;
_configuration.source_password = CONFIG_DEFAULT_SOURCE_PASSWORD; _configuration.source_password = CONFIG_DEFAULT_SOURCE_PASSWORD;
_configuration.ice_login = CONFIG_DEFAULT_ICE_LOGIN;
_configuration.touch_freq = CONFIG_DEFAULT_TOUCH_FREQ; _configuration.touch_freq = CONFIG_DEFAULT_TOUCH_FREQ;
_configuration.dir_list = NULL; _configuration.dir_list = NULL;
_configuration.hostname = CONFIG_DEFAULT_HOSTNAME; _configuration.hostname = CONFIG_DEFAULT_HOSTNAME;
...@@ -200,6 +202,10 @@ static void _parse_root(xmlDocPtr doc, xmlNodePtr node) ...@@ -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); if (_configuration.source_password && _configuration.source_password != CONFIG_DEFAULT_SOURCE_PASSWORD) xmlFree(_configuration.source_password);
_configuration.source_password = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1); _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) { } else if (strcmp(node->name, "hostname") == 0) {
if (_configuration.hostname && _configuration.hostname != CONFIG_DEFAULT_HOSTNAME) xmlFree(_configuration.hostname); if (_configuration.hostname && _configuration.hostname != CONFIG_DEFAULT_HOSTNAME) xmlFree(_configuration.hostname);
_configuration.hostname = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1); _configuration.hostname = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
......
...@@ -24,6 +24,7 @@ typedef struct ice_config_tag ...@@ -24,6 +24,7 @@ typedef struct ice_config_tag
int client_timeout; int client_timeout;
int header_timeout; int header_timeout;
int source_timeout; int source_timeout;
int ice_login;
char *source_password; char *source_password;
......
...@@ -286,9 +286,11 @@ static connection_t *_get_connection(void) ...@@ -286,9 +286,11 @@ static connection_t *_get_connection(void)
return con; 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; source_t *source;
char *contenttype; char *contenttype;
int bytes;
/* check to make sure this source wouldn't /* check to make sure this source wouldn't
** be over the limit ** be over the limit
*/ */
...@@ -311,12 +313,16 @@ int connection_create_source(connection_t *con, http_parser_t *parser, char *mou ...@@ -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); WARN1("Content-type \"%s\" not supported, dropping source", contenttype);
goto fail; goto fail;
} else { } else {
source = source_create(con, parser, mount, format); source = source_create(client, con, parser, mount, format);
} }
} else { } else {
WARN0("No content-type header, cannot handle source"); WARN0("No content-type header, cannot handle source");
goto fail; 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; source->shutdown_rwlock = &_source_shutdown_rwlock;
sock_set_blocking(con->sock, SOCK_NONBLOCK); sock_set_blocking(con->sock, SOCK_NONBLOCK);
thread_create("Source Thread", source_main, (void *)source, THREAD_DETACHED); thread_create("Source Thread", source_main, (void *)source, THREAD_DETACHED);
...@@ -331,7 +337,46 @@ fail: ...@@ -331,7 +337,46 @@ fail:
return 0; 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; char *password, *correctpass;
...@@ -348,16 +393,27 @@ static int _check_source_pass(http_parser_t *parser) ...@@ -348,16 +393,27 @@ static int _check_source_pass(http_parser_t *parser)
return 1; 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, static void _handle_source_request(connection_t *con,
http_parser_t *parser, char *uri) http_parser_t *parser, char *uri)
{ {
client_t *client;
client = client_create(con, parser);
INFO1("Source logging in at mountpoint \"%s\"", uri); INFO1("Source logging in at mountpoint \"%s\"", uri);
stats_event_inc(NULL, "source_connections"); stats_event_inc(NULL, "source_connections");
if (!_check_source_pass(parser)) { if (!_check_source_pass(parser)) {
INFO1("Source (%s) attempted to login with bad password", uri); INFO1("Source (%s) attempted to login with bad password", uri);
connection_close(con); client_send_401(client);
httpp_destroy(parser);
return; return;
} }
...@@ -368,16 +424,14 @@ static void _handle_source_request(connection_t *con, ...@@ -368,16 +424,14 @@ static void _handle_source_request(connection_t *con,
avl_tree_rlock(global.source_tree); avl_tree_rlock(global.source_tree);
if (source_find_mount(uri) != NULL) { if (source_find_mount(uri) != NULL) {
INFO1("Source tried to log in as %s, but mountpoint is already used", uri); INFO1("Source tried to log in as %s, but mountpoint is already used", uri);
connection_close(con); client_send_404(client, "Mountpoint in use");
httpp_destroy(parser);
avl_tree_unlock(global.source_tree); avl_tree_unlock(global.source_tree);
return; return;
} }
avl_tree_unlock(global.source_tree); avl_tree_unlock(global.source_tree);
if (!connection_create_source(con, parser, uri)) { if (!connection_create_source(client, con, parser, uri)) {
connection_close(con); client_destroy(client);
httpp_destroy(parser);
} }
} }
...@@ -432,6 +486,11 @@ static void _handle_get_request(connection_t *con, ...@@ -432,6 +486,11 @@ static void _handle_get_request(connection_t *con,
*/ */
/* TODO: add GUID-xxxxxx */ /* TODO: add GUID-xxxxxx */
if (strcmp(uri, "/stats.xml") == 0) { 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"); DEBUG0("Stats request, sending xml stats");
stats_sendxml(client); stats_sendxml(client);
client_destroy(client); client_destroy(client);
...@@ -491,7 +550,7 @@ static void _handle_get_request(connection_t *con, ...@@ -491,7 +550,7 @@ static void _handle_get_request(connection_t *con,
if (strcmp(uri, "/allstreams.txt") == 0) { if (strcmp(uri, "/allstreams.txt") == 0) {
if (!_check_source_pass(parser)) { if (!_check_source_pass(parser)) {
INFO0("Client attempted to fetch allstreams.txt with bad password"); INFO0("Client attempted to fetch allstreams.txt with bad password");
client_send_404(client, "Bad ice-password"); client_send_401(client);
} else { } else {
avl_node *node; avl_node *node;
source_t *s; source_t *s;
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
#include "thread.h" #include "thread.h"
#include "sock.h" #include "sock.h"
struct _client_tag;
typedef struct connection_tag typedef struct connection_tag
{ {
unsigned long id; unsigned long id;
...@@ -26,8 +28,8 @@ void connection_shutdown(void); ...@@ -26,8 +28,8 @@ void connection_shutdown(void);
void connection_accept_loop(void); void connection_accept_loop(void);
void connection_close(connection_t *con); void connection_close(connection_t *con);
connection_t *create_connection(sock_t sock, char *ip); connection_t *create_connection(sock_t sock, char *ip);
int connection_create_source(connection_t *con, http_parser_t *parser, int connection_create_source(struct _client_tag *client, connection_t *con,
char *mount); http_parser_t *parser, char *mount);
extern rwlock_t _source_shutdown_rwlock; extern rwlock_t _source_shutdown_rwlock;
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "connection.h" #include "connection.h"
#include "refbuf.h" #include "refbuf.h"
#include "format.h" #include "format.h"
#include "client.h"
#include "source.h" #include "source.h"
#include "global.h" #include "global.h"
......
...@@ -70,6 +70,7 @@ static void *_slave_thread(void *arg) { ...@@ -70,6 +70,7 @@ static void *_slave_thread(void *arg) {
char header[4096]; char header[4096];
connection_t *con; connection_t *con;
http_parser_t *parser; http_parser_t *parser;
client_t *client;
int interval = config_get_config()->master_update_interval; int interval = config_get_config()->master_update_interval;
while (_initialized) { while (_initialized) {
...@@ -85,6 +86,7 @@ static void *_slave_thread(void *arg) { ...@@ -85,6 +86,7 @@ static void *_slave_thread(void *arg) {
WARN0("Relay slave failed to contact master server to fetch stream list"); WARN0("Relay slave failed to contact master server to fetch stream list");
continue; 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); 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))) { while (sock_read_line(mastersock, buf, sizeof(buf))) {
buf[strlen(buf)] = 0; buf[strlen(buf)] = 0;
...@@ -119,10 +121,10 @@ static void *_slave_thread(void *arg) { ...@@ -119,10 +121,10 @@ static void *_slave_thread(void *arg) {
continue; 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))) { httpp_getvar(parser, HTTPP_VAR_URI))) {
connection_close(con); client_destroy(client);
httpp_destroy(parser);
} }
continue; continue;
......
...@@ -39,11 +39,12 @@ static int _compare_clients(void *compare_arg, void *a, void *b); ...@@ -39,11 +39,12 @@ static int _compare_clients(void *compare_arg, void *a, void *b);
static int _remove_client(void *key); static int _remove_client(void *key);
static int _free_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; source_t *src;
src = (source_t *)malloc(sizeof(source_t)); src = (source_t *)malloc(sizeof(source_t));
src->client = client;
src->mount = (char *)strdup(mount); src->mount = (char *)strdup(mount);
src->format = format_get_plugin(type, src->mount); src->format = format_get_plugin(type, src->mount);
src->con = con; src->con = con;
...@@ -94,8 +95,7 @@ int source_free_source(void *key) ...@@ -94,8 +95,7 @@ int source_free_source(void *key)
source_t *source = (source_t *)key; source_t *source = (source_t *)key;
free(source->mount); free(source->mount);
connection_close(source->con); client_destroy(source->client);
httpp_destroy(source->parser);
avl_tree_free(source->pending_tree, _free_client); avl_tree_free(source->pending_tree, _free_client);
avl_tree_free(source->client_tree, _free_client); avl_tree_free(source->client_tree, _free_client);
source->format->free_plugin(source->format); source->format->free_plugin(source->format);
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
typedef struct source_tag typedef struct source_tag
{ {
client_t *client;
connection_t *con; connection_t *con;
http_parser_t *parser; http_parser_t *parser;
...@@ -15,7 +16,7 @@ typedef struct source_tag ...@@ -15,7 +16,7 @@ typedef struct source_tag
rwlock_t *shutdown_rwlock; rwlock_t *shutdown_rwlock;
} source_t; } 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); source_t *source_find_mount(const char *mount);
int source_compare_sources(void *arg, void *a, void *b); int source_compare_sources(void *arg, void *a, void *b);
int source_free_source(void *key); int source_free_source(void *key);
......
...@@ -268,3 +268,102 @@ char *util_normalise_uri(char *uri) { ...@@ -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); ...@@ -11,5 +11,7 @@ char *util_get_extension(char *path);
char *util_get_path_from_uri(char *uri); char *util_get_path_from_uri(char *uri);
char *util_get_path_from_normalised_uri(char *uri); char *util_get_path_from_normalised_uri(char *uri);
char *util_normalise_uri(char *uri); char *util_normalise_uri(char *uri);
char *util_base64_encode(char *data);
char *util_base64_decode(char *input);
#endif /* __UTIL_H__ */ #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