From 4d7a60d588f8586c5dd679b28bc1263a51dbe7c7 Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Fri, 14 Sep 2018 13:39:50 +0000 Subject: [PATCH] Feature: Added basic support for auth backends to manipulate the client --- src/admin.c | 2 +- src/auth.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++-- src/auth.h | 21 ++++++++++++++ src/client.c | 45 ++++++++++++++++++++--------- src/client.h | 4 ++- src/connection.c | 5 ++++ src/errors.c | 14 +++++++++ src/errors.h | 1 + src/stats.c | 2 +- src/xslt.c | 19 ++++++++++--- src/xslt.h | 2 +- 11 files changed, 166 insertions(+), 23 deletions(-) diff --git a/src/admin.c b/src/admin.c index 0b8dd023..fcf40bb6 100644 --- a/src/admin.c +++ b/src/admin.c @@ -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); } } diff --git a/src/auth.c b/src/auth.c index f063b701..fcc81b75 100644 --- a/src/auth.c +++ b/src/auth.c @@ -227,9 +227,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 +297,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 +366,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) { @@ -743,6 +799,20 @@ 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; + + /* TODO: check if auth backend has the permission for this operation */ + + if (replace_string(&(auth_user->alter_client_arg), arg) != 0) + return -1; + + auth_user->alter_client_action = action; + + return 0; +} /* these are called at server start and termination */ diff --git a/src/auth.h b/src/auth.h index 1b877b53..3129b12b 100644 --- a/src/auth.h +++ b/src/auth.h @@ -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,8 @@ 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; + auth_alter_t alter_client_action; + char *alter_client_arg; auth_client *next; }; @@ -154,6 +173,8 @@ 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); + 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 */ diff --git a/src/client.c b/src/client.c index 5338568d..3a0ad6f2 100644 --- a/src/client.c +++ b/src/client.c @@ -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); } diff --git a/src/client.h b/src/client.h index 9a15e567..89996809 100644 --- a/src/client.h +++ b/src/client.h @@ -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); diff --git a/src/connection.c b/src/connection.c index 63feffe6..b96bb651 100644 --- a/src/connection.c +++ b/src/connection.c @@ -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) { diff --git a/src/errors.c b/src/errors.c index d553a523..899ed7ce 100644 --- a/src/errors.c +++ b/src/errors.c @@ -10,6 +10,8 @@ #include #endif +#include + #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; +} diff --git a/src/errors.h b/src/errors.h index babfed91..1a6828c5 100644 --- a/src/errors.h +++ b/src/errors.h @@ -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__ */ diff --git a/src/stats.c b/src/stats.c index 61871caf..715f22c3 100644 --- a/src/stats.c +++ b/src/stats.c @@ -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); diff --git a/src/xslt.c b/src/xslt.c index b1c4508a..aa4e1ec5 100644 --- a/src/xslt.c +++ b/src/xslt.c @@ -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); diff --git a/src/xslt.h b/src/xslt.h index c5d68e10..2fe5fd53 100644 --- a/src/xslt.h +++ b/src/xslt.h @@ -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);