From 4a5eefee70576a946720d606af8d79c4359e3459 Mon Sep 17 00:00:00 2001 From: "C. S" Date: Fri, 9 Nov 2018 18:28:12 -0500 Subject: [PATCH] Add shoutcast user auth and mount designation --- src/admin.c | 29 ++++++++++++++++++++++++----- src/auth.c | 2 +- src/connection.c | 38 ++++++++++++++++++++++++++++++++------ src/util.c | 22 +++++++++++++++++++--- src/util.h | 1 + 5 files changed, 77 insertions(+), 15 deletions(-) diff --git a/src/admin.c b/src/admin.c index 4d7bf36e..8f2cb297 100644 --- a/src/admin.c +++ b/src/admin.c @@ -326,17 +326,36 @@ int admin_handle_request (client_t *client, const char *uri) if (pass == NULL) return client_send_400 (client, "missing pass parameter"); uri++; + + char *pass_copy = strdup(pass); + char *login_end = pass_copy + strlen(pass_copy); + char *login_pass = strchr(pass_copy, ':'); + char *login_mount = strchr(pass_copy, '@'); + + if(login_pass) *login_pass++ = '\0'; + if(login_mount) *login_mount++ = '\0'; + + client->username = strdup(login_pass && (!login_mount || (login_mount && login_mount > pass_copy +1)) ? pass_copy : "source"); + client->password = strdup(login_pass && login_pass < login_end ? login_pass : pass_copy); + if (mount == NULL) { - if (client->server_conn && client->server_conn->shoutcast_mount) - httpp_set_query_param (client->parser, "mount", + if (login_mount && login_mount < login_end) + { + if(*login_mount != '/') + *--login_mount = '/'; + httpp_set_query_param (client->parser, "mount", login_mount); + } + else if (client->server_conn && client->server_conn->shoutcast_mount) + { + httpp_set_query_param (client->parser, "mount", client->server_conn->shoutcast_mount); + } mount = httpp_get_query_param (client->parser, "mount"); } + free(pass_copy); httpp_setvar (client->parser, HTTPP_VAR_PROTOCOL, "ICY"); - httpp_setvar (client->parser, HTTPP_VAR_ICYPASSWORD, pass); - client->username = strdup ("source"); - client->password = strdup (pass); + httpp_setvar (client->parser, HTTPP_VAR_ICYPASSWORD, client->password); } else uri += 7; diff --git a/src/auth.c b/src/auth.c index 631aefa2..8767ab71 100644 --- a/src/auth.c +++ b/src/auth.c @@ -964,7 +964,7 @@ int auth_check_source (client_t *client, const char *mount) ret = -1; if (mountinfo->password) pass = mountinfo->password; - if (mountinfo->username && client->server_conn->shoutcast_compat == 0) + if (mountinfo->username) user = mountinfo->username; } if (connection_check_pass (client->parser, user, pass) > 0) diff --git a/src/connection.c b/src/connection.c index 072814b3..bb65c4e4 100644 --- a/src/connection.c +++ b/src/connection.c @@ -1153,14 +1153,40 @@ static int shoutcast_source_client (client_t *client) return 0; refbuf->data [len] = '\0'; - snprintf (header, sizeof(header), "source:%s", refbuf->data); - esc_header = util_base64_encode (header); - len += 1 + strspn (refbuf->data+len+1, "\r\n"); + + char *login_end = refbuf->data + strlen(refbuf->data); + char *login_pass = strchr(refbuf->data, ':'); + char *login_mount = strchr(refbuf->data, '@'); + + if(login_pass) *login_pass++ = '\0'; + if(login_mount) *login_mount++ = '\0'; + + snprintf (header, sizeof(header), "%s:%s", + login_pass && (!login_mount || (login_mount && login_mount > refbuf->data +1)) ? refbuf->data : "source", + login_pass && login_pass < login_end ? login_pass : refbuf->data + ); + + if(login_mount && login_mount < login_end) + { + if(*login_mount != '/') + *--login_mount = '/'; + + // Reject mounts with "unsafe" characters. Behavior is unpredictable/undesirable + if (!util_url_safe(login_mount +1)) { + INFO1("rejected mount '%s'", login_mount); + break; + } + } else { + login_mount = client->server_conn->shoutcast_mount; + } + r = refbuf_new (PER_CLIENT_REFBUF_SIZE); + esc_header = util_base64_encode (header); snprintf (r->data, PER_CLIENT_REFBUF_SIZE, - "SOURCE %s HTTP/1.0\r\n" "Authorization: Basic %s\r\n%s", - client->server_conn->shoutcast_mount, esc_header, refbuf->data+len); + "SOURCE %s HTTP/1.0\r\n" "Authorization: Basic %s\r\n%s", + login_mount, esc_header, refbuf->data+len); + r->len = strlen (r->data); free (esc_header); client->respcode = 200; @@ -1171,7 +1197,7 @@ static int shoutcast_source_client (client_t *client) client->refbuf = resp; refbuf_release (refbuf); client->shared_data = NULL; - INFO1 ("emulation on %s", client->server_conn->shoutcast_mount); + INFO1 ("emulation on %s", login_mount); } format_generic_write_to_client (client); if (client->pos == client->refbuf->len) diff --git a/src/util.c b/src/util.c index 46b7d8e1..abc7aea7 100644 --- a/src/util.c +++ b/src/util.c @@ -281,12 +281,12 @@ static char hexchars[16] = { static char safechars[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -297,6 +297,22 @@ static char safechars[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; +_Bool util_url_safe (const char *src) +{ + int i, len; + unsigned char *source; + + len = strlen(src); + source = (unsigned char *)src; + + for(i=0; i < len; i++) { + if(!safechars[source[i]]) { + return 0; + } + } + return 1; +} + char *util_url_escape (const char *src) { int len, i, j=0; diff --git a/src/util.h b/src/util.h index b5f3d18a..9428b64f 100644 --- a/src/util.h +++ b/src/util.h @@ -34,6 +34,7 @@ char *util_base64_encode(const char *data); char *util_base64_decode(const char *input); char *util_bin_to_hex(unsigned char *data, int len); +_Bool util_url_safe (const char *src); char *util_url_unescape(const char *src); char *util_url_escape(const char *src);