1
0
mirror of https://gitlab.xiph.org/xiph/icecast-server.git synced 2025-01-03 14:56:34 -05:00

Merge branch 'feature-auth-redirect'

This commit is contained in:
Philipp Schafft 2018-09-19 13:33:44 +00:00
commit b98aebe388
15 changed files with 585 additions and 103 deletions

View File

@ -470,7 +470,7 @@ void admin_send_response(xmlDocPtr doc,
config_release_config();
ICECAST_LOG_DEBUG("Sending XSLT (%s)", fullpath_xslt_template);
xslt_transform(doc, fullpath_xslt_template, client, 200);
xslt_transform(doc, fullpath_xslt_template, client, 200, NULL);
free(fullpath_xslt_template);
}
}

View File

@ -62,40 +62,43 @@ static unsigned long _next_auth_id(void) {
return id;
}
static const struct {
auth_result result;
const char *string;
} __auth_results[] = {
{.result = AUTH_UNDEFINED, .string = "undefined"},
{.result = AUTH_OK, .string = "ok"},
{.result = AUTH_FAILED, .string = "failed"},
{.result = AUTH_RELEASED, .string = "released"},
{.result = AUTH_FORBIDDEN, .string = "forbidden"},
{.result = AUTH_NOMATCH, .string = "no match"},
{.result = AUTH_USERADDED, .string = "user added"},
{.result = AUTH_USEREXISTS, .string = "user exists"},
{.result = AUTH_USERDELETED, .string = "user deleted"}
};
static const char *auth_result2str(auth_result res)
{
switch (res) {
case AUTH_UNDEFINED:
return "undefined";
break;
case AUTH_OK:
return "ok";
break;
case AUTH_FAILED:
return "failed";
break;
case AUTH_RELEASED:
return "released";
break;
case AUTH_FORBIDDEN:
return "forbidden";
break;
case AUTH_NOMATCH:
return "no match";
break;
case AUTH_USERADDED:
return "user added";
break;
case AUTH_USEREXISTS:
return "user exists";
break;
case AUTH_USERDELETED:
return "user deleted";
break;
default:
return "(unknown)";
break;
size_t i;
for (i = 0; i < (sizeof(__auth_results)/sizeof(*__auth_results)); i++) {
if (__auth_results[i].result == res)
return __auth_results[i].string;
}
return "(unknown)";
}
auth_result auth_str2result(const char *str)
{
size_t i;
for (i = 0; i < (sizeof(__auth_results)/sizeof(*__auth_results)); i++) {
if (strcasecmp(__auth_results[i].string, str) == 0)
return __auth_results[i].result;
}
return AUTH_FAILED;
}
static auth_client *auth_client_setup (client_t *client)
@ -227,9 +230,11 @@ void auth_addref (auth_t *authenticator) {
static void auth_client_free (auth_client *auth_user)
{
if (auth_user == NULL)
if (!auth_user)
return;
free (auth_user);
free(auth_user->alter_client_arg);
free(auth_user);
}
@ -295,6 +300,55 @@ static auth_result auth_remove_client(auth_t *auth, auth_client *auth_user)
return ret;
}
static inline int __handle_auth_client_alter(auth_t *auth, auth_client *auth_user)
{
client_t *client = auth_user->client;
const char *uuid = NULL;
const char *location = NULL;
int http_status = 0;
void client_send_redirect(client_t *client, const char *uuid, int status, const char *location);
switch (auth_user->alter_client_action) {
case AUTH_ALTER_NOOP:
return 0;
break;
case AUTH_ALTER_REWRITE:
free(client->uri);
client->uri = auth_user->alter_client_arg;
auth_user->alter_client_arg = NULL;
return 0;
break;
case AUTH_ALTER_REDIRECT:
/* fall through */
case AUTH_ALTER_REDIRECT_SEE_OTHER:
uuid = "be7fac90-54fb-4673-9e0d-d15d6a4963a2";
http_status = 303;
location = auth_user->alter_client_arg;
break;
case AUTH_ALTER_REDIRECT_TEMPORARY:
uuid = "4b08a03a-ecce-4981-badf-26b0bb6c9d9c";
http_status = 307;
location = auth_user->alter_client_arg;
break;
case AUTH_ALTER_REDIRECT_PERMANENT:
uuid = "36bf6815-95cb-4cc8-a7b0-6b4b0c82ac5d";
http_status = 308;
location = auth_user->alter_client_arg;
break;
case AUTH_ALTER_SEND_ERROR:
client_send_error_by_uuid(client, auth_user->alter_client_arg);
return 1;
break;
}
if (uuid && location && http_status) {
client_send_redirect(client, uuid, http_status, location);
return 1;
}
return -1;
}
static void __handle_auth_client (auth_t *auth, auth_client *auth_user) {
auth_result result;
@ -315,6 +369,11 @@ static void __handle_auth_client (auth_t *auth, auth_client *auth_user) {
auth_user->client->role = strdup(auth->role);
}
if (result != AUTH_NOMATCH) {
if (__handle_auth_client_alter(auth, auth_user) == 1)
return;
}
if (result == AUTH_NOMATCH && auth_user->on_no_match) {
auth_user->on_no_match(auth_user->client, auth_user->on_result, auth_user->userdata);
} else if (auth_user->on_result) {
@ -582,6 +641,52 @@ static inline int auth_get_authenticator__filter_method(auth_t *auth, xmlNodePtr
return 0;
}
static inline int auth_get_authenticator__permission_alter(auth_t *auth, xmlNodePtr node, const char *name, auth_matchtype_t matchtype)
{
char * tmp = (char*)xmlGetProp(node, XMLSTR(name));
if (tmp) {
char *cur = tmp;
while (cur) {
char *next = strstr(cur, ",");
auth_alter_t idx;
if (next) {
*next = 0;
next++;
for (; *next == ' '; next++);
}
if (strcmp(cur, "*") == 0) {
size_t i;
for (i = 0; i < (sizeof(auth->permission_alter)/sizeof(*(auth->permission_alter))); i++)
auth->permission_alter[i] = matchtype;
break;
}
idx = auth_str2alter(cur);
if (idx == AUTH_ALTER_NOOP) {
ICECAST_LOG_ERROR("Can not add unknown alter action \"%H\" to role's %s", cur, name);
return -1;
} else if (idx == AUTH_ALTER_REDIRECT) {
auth->permission_alter[AUTH_ALTER_REDIRECT] = matchtype;
auth->permission_alter[AUTH_ALTER_REDIRECT_SEE_OTHER] = matchtype;
auth->permission_alter[AUTH_ALTER_REDIRECT_TEMPORARY] = matchtype;
auth->permission_alter[AUTH_ALTER_REDIRECT_PERMANENT] = matchtype;
} else {
auth->permission_alter[idx] = matchtype;
}
cur = next;
}
free(tmp);
}
return 0;
}
auth_t *auth_get_authenticator(xmlNodePtr node)
{
auth_t *auth = calloc(1, sizeof(auth_t));
@ -609,6 +714,9 @@ auth_t *auth_get_authenticator(xmlNodePtr node)
auth->filter_admin[i].command = ADMIN_COMMAND_ERROR;
}
for (i = 0; i < (sizeof(auth->permission_alter)/sizeof(*(auth->permission_alter))); i++)
auth->permission_alter[i] = AUTH_MATCHTYPE_NOMATCH;
if (!auth->type) {
auth_release(auth);
return NULL;
@ -680,6 +788,9 @@ auth_t *auth_get_authenticator(xmlNodePtr node)
auth_get_authenticator__filter_admin(auth, node, &filter_admin_index, "match-admin", AUTH_MATCHTYPE_MATCH);
auth_get_authenticator__filter_admin(auth, node, &filter_admin_index, "nomatch-admin", AUTH_MATCHTYPE_NOMATCH);
auth_get_authenticator__permission_alter(auth, node, "may-alter", AUTH_MATCHTYPE_MATCH);
auth_get_authenticator__permission_alter(auth, node, "may-not-alter", AUTH_MATCHTYPE_NOMATCH);
/* BEFORE RELEASE 2.5.0 TODO: Migrate this to config_parse_options(). */
option = node->xmlChildrenNode;
while (option)
@ -743,6 +854,48 @@ auth_t *auth_get_authenticator(xmlNodePtr node)
return auth;
}
int auth_alter_client(auth_t *auth, auth_client *auth_user, auth_alter_t action, const char *arg)
{
if (!auth || !auth_user || !arg)
return -1;
if (action < 0 || action >= (sizeof(auth->permission_alter)/sizeof(*(auth->permission_alter))))
return -1;
if (auth->permission_alter[action] != AUTH_MATCHTYPE_MATCH)
return -1;
if (replace_string(&(auth_user->alter_client_arg), arg) != 0)
return -1;
auth_user->alter_client_action = action;
return 0;
}
auth_alter_t auth_str2alter(const char *str)
{
if (!str)
return AUTH_ALTER_NOOP;
if (strcasecmp(str, "noop") == 0) {
return AUTH_ALTER_NOOP;
} else if (strcasecmp(str, "rewrite") == 0) {
return AUTH_ALTER_REWRITE;
} else if (strcasecmp(str, "redirect") == 0) {
return AUTH_ALTER_REDIRECT;
} else if (strcasecmp(str, "redirect_see_other") == 0) {
return AUTH_ALTER_REDIRECT_SEE_OTHER;
} else if (strcasecmp(str, "redirect_temporary") == 0) {
return AUTH_ALTER_REDIRECT_TEMPORARY;
} else if (strcasecmp(str, "redirect_permanent") == 0) {
return AUTH_ALTER_REDIRECT_PERMANENT;
} else if (strcasecmp(str, "send_error") == 0) {
return AUTH_ALTER_SEND_ERROR;
} else {
return AUTH_ALTER_NOOP;
}
}
/* these are called at server start and termination */

View File

@ -65,6 +65,23 @@ typedef enum {
AUTH_MATCHTYPE_NOMATCH
} auth_matchtype_t;
typedef enum {
/* Used internally by auth system. */
AUTH_ALTER_NOOP = 0,
/* Internal rewrite of URI */
AUTH_ALTER_REWRITE,
/* Redirect to another location. */
AUTH_ALTER_REDIRECT,
/* See some other resource */
AUTH_ALTER_REDIRECT_SEE_OTHER,
/* This resource is currently located elsewhere */
AUTH_ALTER_REDIRECT_TEMPORARY,
/* This resource is now located at new location */
AUTH_ALTER_REDIRECT_PERMANENT,
/* Send an error report to the client */
AUTH_ALTER_SEND_ERROR
} auth_alter_t;
typedef struct auth_client_tag auth_client;
struct auth_client_tag {
client_t *client;
@ -72,6 +89,9 @@ struct auth_client_tag {
void (*on_no_match)(client_t *client, void (*on_result)(client_t *client, void *userdata, auth_result result), void *userdata);
void (*on_result)(client_t *client, void *userdata, auth_result result);
void *userdata;
void *authbackend_userdata;
auth_alter_t alter_client_action;
char *alter_client_arg;
auth_client *next;
};
@ -95,6 +115,9 @@ struct auth_tag
admin_command_id_t command;
} filter_admin[MAX_ADMIN_COMMANDS];
/* permissions */
auth_matchtype_t permission_alter[AUTH_ALTER_SEND_ERROR+1];
/* whether authenticate_client() and release_client() will return immediate.
* Setting this will result in no thread being started for this.
*/
@ -141,6 +164,8 @@ int auth_get_htpasswd_auth(auth_t *auth, config_options_t *options);
void auth_initialise(void);
void auth_shutdown(void);
auth_result auth_str2result(const char *str);
auth_t *auth_get_authenticator(xmlNodePtr node);
void auth_release(auth_t *authenticator);
void auth_addref(auth_t *authenticator);
@ -154,6 +179,9 @@ void auth_stack_add_client(auth_stack_t *stack,
auth_result result),
void *userdata);
int auth_alter_client(auth_t *auth, auth_client *auth_user, auth_alter_t action, const char *arg);
auth_alter_t auth_str2alter(const char *str);
void auth_stack_release(auth_stack_t *stack);
void auth_stack_addref(auth_stack_t *stack);
int auth_stack_next(auth_stack_t **stack); /* returns -1 on error, 0 on success, +1 if no next element is present */

View File

@ -20,6 +20,7 @@
#include "auth.h"
#include "cfgfile.h"
#include "client.h"
#include "util.h"
#include "logging.h"
#define CATMODULE "auth_static"
@ -27,6 +28,8 @@
typedef struct auth_static {
char *username;
char *password;
auth_alter_t action;
char *arg;
} auth_static_t;
static auth_result static_auth(auth_client *auth_user)
@ -45,19 +48,28 @@ static auth_result static_auth(auth_client *auth_user)
if (!client->password)
return AUTH_NOMATCH;
if (strcmp(auth_info->password, client->password) == 0)
return AUTH_OK;
if (strcmp(auth_info->password, client->password) != 0)
return AUTH_FAILED;
return AUTH_FAILED;
if (auth_info->action != AUTH_ALTER_NOOP) {
if (auth_alter_client(auth, auth_user, auth_info->action, auth_info->arg) != 0) {
ICECAST_LOG_ERROR("Can not alter client.");
}
}
return AUTH_OK;
}
static void clear_auth (auth_t *auth)
{
auth_static_t *auth_info = auth->state;
if (auth_info->username)
free(auth_info->username);
if (auth_info->password)
free(auth_info->password);
if (!auth_info)
return;
free(auth_info->username);
free(auth_info->password);
free(auth_info->arg);
free(auth_info);
auth->state = NULL;
}
@ -106,6 +118,15 @@ int auth_get_static_auth (auth_t *authenticator, config_options_t *options)
if (auth_info->password)
free(auth_info->password);
auth_info->password = strdup(options->value);
} else if (strcmp(options->name, "action") == 0) {
auth_info->action = auth_str2alter(options->value);
if (auth_info->action == AUTH_ALTER_NOOP) {
ICECAST_LOG_ERROR("Invalid action given.");
clear_auth(authenticator);
return -1;
}
} else if (strcmp(options->name, "argument") == 0) {
replace_string(&(auth_info->arg), options->value);
} else {
ICECAST_LOG_ERROR("Unknown option: %s", options->name);
}

View File

@ -67,6 +67,7 @@
# define strncasecmp strnicmp
#endif
#include "util.h"
#include "curl.h"
#include "auth.h"
#include "source.h"
@ -78,6 +79,16 @@
#include "logging.h"
#define CATMODULE "auth_url"
/* Default headers */
#define DEFAULT_HEADER_OLD_RESULT "icecast-auth-user: 1\r\n"
#define DEFAULT_HEADER_OLD_TIMELIMIT "icecast-auth-timelimit:"
#define DEFAULT_HEADER_OLD_MESSAGE "icecast-auth-message"
#define DEFAULT_HEADER_NEW_RESULT "x-icecast-auth-result"
#define DEFAULT_HEADER_NEW_TIMELIMIT "x-icecast-auth-timelimit"
#define DEFAULT_HEADER_NEW_MESSAGE "x-icecast-auth-message"
#define DEFAULT_HEADER_NEW_ALTER_ACTION "x-icecast-auth-alter-action"
#define DEFAULT_HEADER_NEW_ALTER_ARGUMENT "x-icecast-auth-alter-argument"
typedef struct {
char *pass_headers; // headers passed from client to addurl.
char *prefix_headers; // prefix for passed headers.
@ -87,16 +98,37 @@ typedef struct {
char *removeaction;
char *username;
char *password;
/* old style */
char *auth_header;
int auth_header_len;
size_t auth_header_len;
char *timelimit_header;
int timelimit_header_len;
size_t timelimit_header_len;
/* new style */
char *header_auth;
char *header_timelimit;
char *header_message;
char *header_alter_action;
char *header_alter_argument;
char *userpwd;
CURL *handle;
char errormsg[CURL_ERROR_SIZE];
auth_result result;
} auth_url;
typedef struct {
char *all_headers;
size_t all_headers_len;
http_parser_t *parser;
} auth_user_url_t;
static inline const char * __str_or_default(const char *str, const char *def)
{
if (str)
return str;
return def;
}
static void auth_url_clear(auth_t *self)
{
@ -116,42 +148,181 @@ static void auth_url_clear(auth_t *self)
free(url->removeaction);
free(url->auth_header);
free(url->timelimit_header);
free(url->header_auth);
free(url->header_timelimit);
free(url->header_message);
free(url->header_alter_action);
free(url->header_alter_argument);
free(url->userpwd);
free(url);
}
static void auth_user_url_clear(auth_client *auth_user)
{
auth_user_url_t *au_url = auth_user->authbackend_userdata;
if (!au_url)
return;
free(au_url->all_headers);
if (au_url->parser)
httpp_destroy(au_url->parser);
free(au_url);
auth_user->authbackend_userdata = NULL;
}
static void handle_returned_header__complete(auth_client *auth_user)
{
auth_user_url_t *au_url = auth_user->authbackend_userdata;
const char *tmp;
const char *action;
const char *argument;
auth_url *url = auth_user->client->auth->state;
if (!au_url)
return;
if (au_url->parser)
return;
au_url->parser = httpp_create_parser();
httpp_initialize(au_url->parser, NULL);
if (!httpp_parse_response(au_url->parser, au_url->all_headers, au_url->all_headers_len, NULL)) {
ICECAST_LOG_ERROR("Can not parse auth backend reply.");
return;
}
tmp = httpp_getvar(au_url->parser, HTTPP_VAR_ERROR_CODE);
if (tmp[0] == '2') {
ICECAST_LOG_DEBUG("Got final status: %#H", tmp);
} else {
ICECAST_LOG_DEBUG("Got non-final status: %#H", tmp);
httpp_destroy(au_url->parser);
au_url->parser = NULL;
au_url->all_headers_len = 0;
return;
}
if (url->header_auth) {
tmp = httpp_getvar(au_url->parser, url->header_auth);
if (tmp) {
url->result = auth_str2result(tmp);
}
}
if (url->header_timelimit) {
tmp = httpp_getvar(au_url->parser, url->header_timelimit);
if (tmp) {
long long int ret;
char *endptr;
errno = 0;
ret = strtoll(tmp, &endptr, 0);
if (endptr != tmp && errno == 0) {
auth_user->client->con->discon_time = time(NULL) + (time_t)ret;
} else {
ICECAST_LOG_ERROR("Auth backend returned invalid new style timelimit header: % #H", tmp);
}
}
}
action = httpp_getvar(au_url->parser, __str_or_default(url->header_alter_action, DEFAULT_HEADER_NEW_ALTER_ACTION));
argument = httpp_getvar(au_url->parser, __str_or_default(url->header_alter_argument, DEFAULT_HEADER_NEW_ALTER_ARGUMENT));
if (action && argument) {
if (auth_alter_client(auth_user->client->auth, auth_user, auth_str2alter(action), argument) != 0) {
ICECAST_LOG_ERROR("Auth backend returned invalid alter action/argument.");
}
} else if (action || argument) {
ICECAST_LOG_ERROR("Auth backend returned incomplete alter action/argument.");
}
if (url->header_message) {
tmp = httpp_getvar(au_url->parser, url->header_message);
} else {
tmp = httpp_getvar(au_url->parser, DEFAULT_HEADER_NEW_MESSAGE);
if (!tmp)
tmp = httpp_getvar(au_url->parser, DEFAULT_HEADER_OLD_MESSAGE);
}
if (tmp) {
snprintf(url->errormsg, sizeof(url->errormsg), "%s", tmp);
}
}
static size_t handle_returned_header(void *ptr,
size_t size,
size_t nmemb,
void *stream)
{
size_t len = size * nmemb;
auth_client *auth_user = stream;
unsigned bytes = size * nmemb;
client_t *client = auth_user->client;
auth_t *auth;
auth_url *url;
if (client) {
auth_t *auth = client->auth;
auth_url *url = auth->state;
if (strncasecmp(ptr, url->auth_header, url->auth_header_len) == 0)
url->result = AUTH_OK;
if (strncasecmp(ptr, url->timelimit_header,
url->timelimit_header_len) == 0) {
unsigned int limit = 0;
sscanf ((char *)ptr+url->timelimit_header_len, "%u\r\n", &limit);
client->con->discon_time = time(NULL) + limit;
if (!client)
return len;
auth = client->auth;
url = auth->state;
if (!auth_user->authbackend_userdata) {
auth_user->authbackend_userdata = calloc(1, sizeof(auth_user_url_t));
}
if (auth_user->authbackend_userdata) {
auth_user_url_t *au_url = auth_user->authbackend_userdata;
char *n = realloc(au_url->all_headers, au_url->all_headers_len + len);
if (n) {
au_url->all_headers = n;
memcpy(n + au_url->all_headers_len, ptr, len);
au_url->all_headers_len += len;
} else {
ICECAST_LOG_ERROR("Can not allocate buffer for auth backend reply headers. BAD.");
}
if (strncasecmp (ptr, "icecast-auth-message: ", 22) == 0) {
char *eol;
snprintf(url->errormsg, sizeof(url->errormsg), "%s", (char*)ptr+22);
eol = strchr(url->errormsg, '\r');
if (eol == NULL)
eol = strchr(url->errormsg, '\n');
if (eol)
*eol = '\0';
} else {
ICECAST_LOG_ERROR("Can not allocate authbackend_userdata. BAD.");
}
ICECAST_LOG_DEBUG("Got header: %* #H", (int)(size * nmemb + 2), ptr);
if (url->auth_header && len >= url->auth_header_len && strncasecmp(ptr, url->auth_header, url->auth_header_len) == 0) {
url->result = AUTH_OK;
}
if (url->timelimit_header && len > url->timelimit_header_len && strncasecmp(ptr, url->timelimit_header, url->timelimit_header_len) == 0) {
const char *input = ptr;
unsigned int limit = 0;
if (len >= 2 && input[len - 2] == '\r' && input[len - 1] == '\n') {
input += url->timelimit_header_len;
if (sscanf(input, "%u\r\n", &limit) == 1) {
client->con->discon_time = time(NULL) + limit;
} else {
ICECAST_LOG_ERROR("Auth backend returned invalid timeline header: Can not parse limit");
}
} else {
ICECAST_LOG_ERROR("Auth backend returned invalid timelimit header.");
}
}
return (int)bytes;
if (len == 1) {
const char *c = ptr;
if (c[0] == '\r' || c[0] == '\n') {
handle_returned_header__complete(auth_user);
}
} else if (len == 2) {
const char *c = ptr;
if ((c[0] == '\r' || c[0] == '\n') && (c[1] == '\r' || c[1] == '\n')) {
handle_returned_header__complete(auth_user);
}
}
return len;
}
static auth_result url_remove_client(auth_client *auth_user)
@ -250,6 +421,7 @@ static auth_result url_remove_client(auth_client *auth_user)
url->removeurl, url->errormsg);
free(userpwd);
auth_user_url_clear(auth_user);
return AUTH_OK;
}
@ -382,6 +554,7 @@ static auth_result url_add_client(auth_client *auth_user)
res = curl_easy_perform(url->handle);
free(userpwd);
auth_user_url_clear(auth_user);
if (res) {
ICECAST_LOG_WARN("auth to server %s failed with %s",
@ -427,43 +600,46 @@ int auth_get_url_auth(auth_t *authenticator, config_options_t *options)
url_info = calloc(1, sizeof(auth_url));
authenticator->state = url_info;
/* default headers */
url_info->auth_header = strdup("icecast-auth-user: 1\r\n");
url_info->timelimit_header = strdup("icecast-auth-timelimit:");
/* force auth thread to call function. this makes sure the auth_t is attached to client */
authenticator->authenticate_client = url_add_client;
while(options) {
if(strcmp(options->name, "username") == 0) {
free(url_info->username);
url_info->username = strdup(options->value);
replace_string(&(url_info->username), options->value);
} else if(strcmp(options->name, "password") == 0) {
free(url_info->password);
url_info->password = strdup(options->value);
replace_string(&(url_info->password), options->value);
} else if(strcmp(options->name, "headers") == 0) {
free(url_info->pass_headers);
url_info->pass_headers = strdup(options->value);
replace_string(&(url_info->pass_headers), options->value);
} else if(strcmp(options->name, "header_prefix") == 0) {
free(url_info->prefix_headers);
url_info->prefix_headers = strdup(options->value);
replace_string(&(url_info->prefix_headers), options->value);
} else if(strcmp(options->name, "client_add") == 0) {
free(url_info->addurl);
url_info->addurl = strdup(options->value);
replace_string(&(url_info->addurl), options->value);
} else if(strcmp(options->name, "client_remove") == 0) {
authenticator->release_client = url_remove_client;
free(url_info->removeurl);
url_info->removeurl = strdup(options->value);
replace_string(&(url_info->removeurl), options->value);
} else if(strcmp(options->name, "action_add") == 0) {
addaction = options->value;
} else if(strcmp(options->name, "action_remove") == 0) {
removeaction = options->value;
} else if(strcmp(options->name, "auth_header") == 0) {
free(url_info->auth_header);
url_info->auth_header = strdup(options->value);
replace_string(&(url_info->auth_header), options->value);
} else if (strcmp(options->name, "timelimit_header") == 0) {
free(url_info->timelimit_header);
url_info->timelimit_header = strdup(options->value);
replace_string(&(url_info->timelimit_header), options->value);
} else if (strcmp(options->name, "header_auth") == 0) {
replace_string(&(url_info->header_auth), options->value);
util_strtolower(url_info->header_message);
} else if (strcmp(options->name, "header_timelimit") == 0) {
replace_string(&(url_info->header_timelimit), options->value);
util_strtolower(url_info->header_message);
} else if (strcmp(options->name, "header_message") == 0) {
replace_string(&(url_info->header_message), options->value);
util_strtolower(url_info->header_message);
} else if (strcmp(options->name, "header_alter_action") == 0) {
replace_string(&(url_info->header_alter_action), options->value);
util_strtolower(url_info->header_alter_action);
} else if (strcmp(options->name, "header_alter_argument") == 0) {
replace_string(&(url_info->header_alter_argument), options->value);
util_strtolower(url_info->header_alter_argument);
} else {
ICECAST_LOG_ERROR("Unknown option: %s", options->name);
}
@ -479,6 +655,22 @@ int auth_get_url_auth(auth_t *authenticator, config_options_t *options)
return -1;
}
/* default headers */
if (url_info->auth_header) {
ICECAST_LOG_WARN("You use old style auth option \"auth_header\". Please switch to new style option \"header_auth\".");
} else if (!url_info->header_auth && !url_info->auth_header) {
ICECAST_LOG_WARN("You do not have enabled old or new style auth option for auth status header. I will enable both. Please set \"header_auth\".");
url_info->auth_header = strdup(DEFAULT_HEADER_OLD_RESULT);
url_info->header_auth = strdup(DEFAULT_HEADER_NEW_RESULT);
}
if (url_info->timelimit_header) {
ICECAST_LOG_WARN("You use old style auth option \"timelimit_header\". Please switch to new style option \"header_timelimit\".");
} else if (!url_info->header_timelimit && !url_info->timelimit_header) {
ICECAST_LOG_WARN("You do not have enabled old or new style auth option for auth timelimit header. I will enable both. Please set \"header_timelimit\".");
url_info->timelimit_header = strdup(DEFAULT_HEADER_OLD_TIMELIMIT);
url_info->timelimit_header = strdup(DEFAULT_HEADER_NEW_TIMELIMIT);
}
if (url_info->auth_header)
url_info->auth_header_len = strlen (url_info->auth_header);
if (url_info->timelimit_header)

View File

@ -302,7 +302,7 @@ int client_read_bytes(client_t *client, void *buf, unsigned len)
return bytes;
}
static inline void _client_send_error(client_t *client, const icecast_error_t *error)
static inline void _client_send_report(client_t *client, const char *uuid, const char *message, int http_status, const char *location)
{
reportxml_t *report;
admin_format_t admin_format;
@ -325,17 +325,15 @@ static inline void _client_send_error(client_t *client, const icecast_error_t *e
break;
}
report = client_get_reportxml(uuid, NULL, message);
report = client_get_reportxml(error->uuid, NULL, error->message);
client_send_reportxml(client, report, DOCUMENT_DOMAIN_ADMIN, xslt, admin_format, error->http_status);
client_send_reportxml(client, report, DOCUMENT_DOMAIN_ADMIN, xslt, admin_format, http_status, location);
refobject_unref(report);
}
void client_send_error_by_id(client_t *client, icecast_error_id_t id)
void client_send_error_by_error(client_t *client, const icecast_error_t *error)
{
const icecast_error_t *error = error_get_by_id(id);
if (!error) {
client_send_500(client, "Unknown error ID");
@ -347,7 +345,15 @@ void client_send_error_by_id(client_t *client, icecast_error_id_t id)
return;
}
_client_send_error(client, error);
_client_send_report(client, error->uuid, error->message, error->http_status, NULL);
}
void client_send_error_by_uuid(client_t *client, const char *uuid)
{
client_send_error_by_error(client, error_get_by_uuid(uuid));
}
void client_send_error_by_id(client_t *client, icecast_error_id_t id)
{
client_send_error_by_error(client, error_get_by_id(id));
}
void client_send_101(client_t *client, reuse_t reuse)
@ -458,8 +464,13 @@ static inline void client_send_500(client_t *client, const char *message)
client_destroy(client);
}
void client_send_redirect(client_t *client, const char *uuid, int status, const char *location)
{
_client_send_report(client, uuid, "Redirecting", status, location);
}
/* this function sends a reportxml file to the client in the prefered format. */
void client_send_reportxml(client_t *client, reportxml_t *report, document_domain_t domain, const char *xsl, admin_format_t admin_format_hint, int status)
void client_send_reportxml(client_t *client, reportxml_t *report, document_domain_t domain, const char *xsl, admin_format_t admin_format_hint, int status, const char *location)
{
admin_format_t admin_format;
xmlDocPtr doc;
@ -514,13 +525,18 @@ void client_send_reportxml(client_t *client, reportxml_t *report, document_domai
if (admin_format == ADMIN_FORMAT_RAW) {
xmlChar *buff = NULL;
size_t location_length = 0;
int len = 0;
size_t buf_len;
ssize_t ret;
xmlDocDumpMemory(doc, &buff, &len);
buf_len = len + 1024;
if (location) {
location_length = strlen(location);
}
buf_len = len + location_length + 1024;
if (buf_len < 4096)
buf_len = 4096;
@ -536,9 +552,9 @@ void client_send_reportxml(client_t *client, reportxml_t *report, document_domai
client_send_error_by_id(client, ICECAST_ERROR_GEN_HEADER_GEN_FAILED);
xmlFree(buff);
return;
} else if (buf_len < (size_t)(len + ret + 64)) {
} else if (buf_len < (size_t)(len + location_length + ret + 128)) {
void *new_data;
buf_len = ret + len + 64;
buf_len = ret + len + 128;
new_data = realloc(client->refbuf->data, buf_len);
if (new_data) {
ICECAST_LOG_DEBUG("Client buffer reallocation succeeded.");
@ -563,7 +579,10 @@ void client_send_reportxml(client_t *client, reportxml_t *report, document_domai
}
/* FIXME: in this section we hope no function will ever return -1 */
ret += snprintf (client->refbuf->data + ret, buf_len - ret, "Content-Length: %d\r\n\r\n%s", xmlStrlen(buff), buff);
if (location) {
ret += snprintf(client->refbuf->data + ret, buf_len - ret, "Location: %s\r\n", location);
}
ret += snprintf(client->refbuf->data + ret, buf_len - ret, "Content-Length: %d\r\n\r\n%s", xmlStrlen(buff), buff);
client->refbuf->len = ret;
xmlFree(buff);
@ -598,7 +617,7 @@ void client_send_reportxml(client_t *client, reportxml_t *report, document_domai
ICECAST_LOG_DEBUG("Sending XSLT (%s)", fullpath_xslt_template);
fastevent_emit(FASTEVENT_TYPE_CLIENT_SEND_RESPONSE, FASTEVENT_FLAG_MODIFICATION_ALLOWED, FASTEVENT_DATATYPE_CLIENT, client);
xslt_transform(doc, fullpath_xslt_template, client, status);
xslt_transform(doc, fullpath_xslt_template, client, status, location);
free(fullpath_xslt_template);
}

View File

@ -143,10 +143,12 @@ int client_create (client_t **c_ptr, connection_t *con, http_parser_t *parser);
void client_complete(client_t *client);
void client_destroy(client_t *client);
void client_send_error_by_id(client_t *client, icecast_error_id_t id);
void client_send_error_by_uuid(client_t *client, const char *uuid);
void client_send_101(client_t *client, reuse_t reuse);
void client_send_204(client_t *client);
void client_send_426(client_t *client, reuse_t reuse);
void client_send_reportxml(client_t *client, reportxml_t *report, document_domain_t domain, const char *xsl, admin_format_t admin_format_hint, int status);
void client_send_redirect(client_t *client, const char *uuid, int status, const char *location);
void client_send_reportxml(client_t *client, reportxml_t *report, document_domain_t domain, const char *xsl, admin_format_t admin_format_hint, int status, const char *location);
reportxml_t *client_get_reportxml(const char *state_definition, const char *state_akindof, const char *state_text);
admin_format_t client_get_admin_format_by_content_negotiation(client_t *client);
int client_send_bytes (client_t *client, const void *buf, unsigned len);

View File

@ -105,6 +105,7 @@ static matchfile_t *banned_ip, *allowed_ip;
rwlock_t _source_shutdown_rwlock;
static int _update_admin_command(client_t *client);
static void _handle_connection(void);
static void get_tls_certificate(ice_config_t *config);
@ -1277,6 +1278,10 @@ static void _handle_authed_client(client_t *client, void *userdata, auth_result
auth_stack_release(client->authstack);
client->authstack = NULL;
/* Update admin parameters just in case auth changed our URI */
if (_update_admin_command(client) == -1)
return;
fastevent_emit(FASTEVENT_TYPE_CLIENT_AUTHED, FASTEVENT_FLAG_MODIFICATION_ALLOWED, FASTEVENT_DATATYPE_CLIENT, client);
if (result != AUTH_OK) {

View File

@ -10,6 +10,8 @@
#include <config.h>
#endif
#include <strings.h>
#include "errors.h"
#include "logging.h"
#define CATMODULE "errors"
@ -150,3 +152,15 @@ const icecast_error_t * error_get_by_id(icecast_error_id_t id) {
return NULL;
}
const icecast_error_t * error_get_by_uuid(const char *uuid)
{
size_t i;
for (i = 0; i < (sizeof(__errors)/sizeof(*__errors)); i++) {
if (strcasecmp(__errors[i].uuid, uuid) == 0) {
return &(__errors[i]);
}
}
return NULL;
}

View File

@ -62,5 +62,6 @@ struct icecast_error_tag {
typedef struct icecast_error_tag icecast_error_t;
const icecast_error_t * error_get_by_id(icecast_error_id_t id);
const icecast_error_t * error_get_by_uuid(const char *uuid);
#endif /* __ERRORS_H__ */

View File

@ -1030,7 +1030,7 @@ void stats_transform_xslt(client_t *client)
doc = stats_get_xml(0, mount, client);
xslt_transform(doc, xslpath, client, 200);
xslt_transform(doc, xslpath, client, 200, NULL);
xmlFreeDoc(doc);
free(xslpath);

View File

@ -17,6 +17,7 @@
#endif
#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
@ -1429,3 +1430,35 @@ int get_line(FILE *file, char *buf, size_t siz)
}
return 0;
}
int replace_string(char **dst, const char *src)
{
char *n;
if (!dst)
return -1;
if (src) {
n = strdup(src);
if (!n)
return -1;
} else {
n = NULL;
}
free(*dst);
*dst = n;
return 0;
}
int util_strtolower(char *str)
{
if (!str)
return -1;
for (; *str; str++)
*str = tolower(*str);
return 0;
}

View File

@ -128,4 +128,7 @@ struct tm *localtime_r(const time_t *timep, struct tm *result);
char *util_conv_string (const char *string, const char *in_charset, const char *out_charset);
int get_line(FILE *file, char *buf, size_t siz);
int replace_string(char **dst, const char *src);
int util_strtolower(char *str);
#endif /* __UTIL_H__ */

View File

@ -320,7 +320,7 @@ static inline void _send_error(client_t *client, icecast_error_id_t id, int old_
client_send_error_by_id(client, id);
}
void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client, int status)
void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client, int status, const char *location)
{
xmlDocPtr res;
xsltStylesheetPtr cur;
@ -374,7 +374,14 @@ void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client, in
ssize_t ret;
int failed = 0;
refbuf_t *refbuf;
size_t location_length = 0;
ssize_t full_len = strlen(mediatype) + (ssize_t)len + (ssize_t)1024;
if (location) {
location_length = strlen(location);
full_len += location_length;
}
if (full_len < 4096)
full_len = 4096;
refbuf = refbuf_new (full_len);
@ -386,9 +393,9 @@ void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client, in
ICECAST_LOG_ERROR("Dropping client as we can not build response headers.");
_send_error(client, ICECAST_ERROR_GEN_HEADER_GEN_FAILED, status);
} else {
if ( full_len < (ret + (ssize_t)len + (ssize_t)64) ) {
if ( full_len < (ret + (ssize_t)len + (ssize_t)128) ) {
void *new_data;
full_len = ret + (ssize_t)len + (ssize_t)64;
full_len = ret + (ssize_t)len + (ssize_t)128;
new_data = realloc(refbuf->data, full_len);
if (new_data) {
ICECAST_LOG_DEBUG("Client buffer reallocation succeeded.");
@ -408,7 +415,11 @@ void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client, in
}
if (!failed) {
snprintf(refbuf->data + ret, full_len - ret, "Content-Length: %d\r\n\r\n%s", len, string);
/* FIXME: in this section we hope no function will ever return -1 */
if (location) {
ret += snprintf(refbuf->data + ret, full_len - ret, "Location: %s\r\n", location);
}
ret += snprintf(refbuf->data + ret, full_len - ret, "Content-Length: %d\r\n\r\n%s", len, string);
client->respcode = status;
client_set_queue (client, NULL);

View File

@ -16,7 +16,7 @@
#include "icecasttypes.h"
void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client, int status);
void xslt_transform(xmlDocPtr doc, const char *xslfilename, client_t *client, int status, const char *location);
void xslt_initialize(void);
void xslt_shutdown(void);
void xslt_clear_cache(void);