[xiph-commits] r15621 - icecast/trunk/icecast/src

karl at svn.xiph.org karl at svn.xiph.org
Tue Jan 13 17:18:23 PST 2009


Author: karl
Date: 2009-01-13 17:18:22 -0800 (Tue, 13 Jan 2009)
New Revision: 15621

Modified:
   icecast/trunk/icecast/src/admin.c
   icecast/trunk/icecast/src/auth.c
   icecast/trunk/icecast/src/auth.h
   icecast/trunk/icecast/src/auth_url.c
   icecast/trunk/icecast/src/client.c
   icecast/trunk/icecast/src/client.h
   icecast/trunk/icecast/src/connection.c
   icecast/trunk/icecast/src/connection.h
   icecast/trunk/icecast/src/source.h
Log:
Allow source client authentication via auth handler. Here the URL handler can
issue requests (using ithe stream_auth option) to allow external engines to
determine whether a client can stream or not. Admin requests using source auth
are able to use this mechanism however source clients using the icy protocol
cannot yet.



Modified: icecast/trunk/icecast/src/admin.c
===================================================================
--- icecast/trunk/icecast/src/admin.c	2009-01-12 21:36:29 UTC (rev 15620)
+++ icecast/trunk/icecast/src/admin.c	2009-01-14 01:18:22 UTC (rev 15621)
@@ -371,17 +371,22 @@
             return;
         }
         /* This is a mount request, handle it as such */
-        if (!connection_check_admin_pass(client->parser))
+        if (client->authenticated == 0 && !connection_check_admin_pass(client->parser))
         {
-            if (!connection_check_source_pass(client->parser, mount))
+            switch (client_check_source_auth (client, mount))
             {
-                INFO1("Bad or missing password on mount modification admin "
-                        "request (command: %s)", command_string);
-                client_send_401(client);
-                return;
+                case 0:
+                    break;
+                default:
+                    INFO1("Bad or missing password on mount modification admin "
+                            "request (command: %s)", command_string);
+                    client_send_401(client);
+                    /* fall through */
+                case 1:
+                    return;
             }
         }
-        
+
         avl_tree_rlock(global.source_tree);
         source = source_find_mount_raw(mount);
 

Modified: icecast/trunk/icecast/src/auth.c
===================================================================
--- icecast/trunk/icecast/src/auth.c	2009-01-12 21:36:29 UTC (rev 15620)
+++ icecast/trunk/icecast/src/auth.c	2009-01-14 01:18:22 UTC (rev 15621)
@@ -32,12 +32,13 @@
 #include "stats.h"
 #include "httpp/httpp.h"
 #include "fserve.h"
+#include "admin.h"
 
 #include "logging.h"
 #define CATMODULE "auth"
 
 
-static mutex_t auth_lock;
+static void auth_postprocess_source (auth_client *auth_user);
 
 
 static auth_client *auth_client_setup (const char *mount, client_t *client)
@@ -236,6 +237,25 @@
 }
 
 
+/* Called from auth thread to process any request for source client
+ * authentication. Only applies to source clients, not relays.
+ */
+static void stream_auth_callback (auth_t *auth, auth_client *auth_user)
+{
+    client_t *client = auth_user->client;
+
+    if (auth->stream_auth)
+        auth->stream_auth (auth_user);
+
+    auth_release (auth);
+    client->auth = NULL;
+    if (client->authenticated)
+        auth_postprocess_source (auth_user);
+    else
+        WARN1 ("Failed auth for source \"%s\"", auth_user->mount);
+}
+
+
 /* Callback from auth thread to handle a stream start event, this applies
  * to both source clients and relays.
  */
@@ -478,6 +498,30 @@
 }
 
 
+/* Decide whether we need to start a source or just process a source
+ * admin request.
+ */
+void auth_postprocess_source (auth_client *auth_user)
+{
+    client_t *client = auth_user->client;
+    const char *mount = auth_user->mount;
+    const char *req = httpp_getvar (client->parser, HTTPP_VAR_URI);
+
+    auth_user->client = NULL;
+    client->authenticated = 1;
+    if (strcmp (req, "/admin.cgi") == 0 || strncmp ("/admin/metadata", req, 15) == 0)
+    {
+        DEBUG2 ("metadata request (%s, %s)", req, mount);
+        admin_handle_request (client, "/admin/metadata");
+    }
+    else
+    {
+        DEBUG1 ("on mountpoint %s", mount);
+        source_startup (client, mount, 0);
+    }
+}
+
+
 /* Add a listener. Check for any mount information that states any
  * authentication to be used.
  */
@@ -654,6 +698,25 @@
 }
 
 
+/* Called when a source client connects and requires authentication via the
+ * authenticator. This is called for both source clients and admin requests
+ * that work on a specified mountpoint.
+ */
+int auth_stream_authenticate (client_t *client, const char *mount, mount_proxy *mountinfo)
+{
+    if (mountinfo && mountinfo->auth && mountinfo->auth->stream_auth)
+    {
+        auth_client *auth_user = auth_client_setup (mount, client);
+
+        auth_user->process = stream_auth_callback;
+        INFO1 ("request source auth for \"%s\"", mount);
+        queue_auth_client (auth_user, mountinfo);
+        return 1;
+    }
+    return 0;
+}
+
+
 /* called when the stream starts, so that authentication engine can do any
  * cleanup/initialisation.
  */
@@ -696,12 +759,10 @@
 
 void auth_initialise (void)
 {
-    thread_mutex_create (&auth_lock);
 }
 
 void auth_shutdown (void)
 {
-    thread_mutex_destroy (&auth_lock);
     INFO0 ("Auth shutdown");
 }
 

Modified: icecast/trunk/icecast/src/auth.h
===================================================================
--- icecast/trunk/icecast/src/auth.h	2009-01-12 21:36:29 UTC (rev 15620)
+++ icecast/trunk/icecast/src/auth.h	2009-01-14 01:18:22 UTC (rev 15621)
@@ -55,6 +55,9 @@
     auth_result (*authenticate)(auth_client *aclient);
     auth_result (*release_listener)(auth_client *auth_user);
 
+    /* auth handler for authenicating a connecting source client */
+    void (*stream_auth)(auth_client *auth_user);
+
     /* auth handler for source startup, no client passed as it may disappear */
     void (*stream_start)(auth_client *auth_user);
 
@@ -92,12 +95,15 @@
 auth_t  *auth_get_authenticator (xmlNodePtr node);
 void    auth_release (auth_t *authenticator);
 
-/* call to send a url request when source starts */
+/* call to trigger an event when a stream starts */
 void auth_stream_start (struct _mount_proxy *mountinfo, const char *mount);
 
-/* call to send a url request when source ends */
+/* call to trigger an event when a stream ends */
 void auth_stream_end (struct _mount_proxy *mountinfo, const char *mount);
 
+/* call to trigger an event to authenticate a source client */
+int auth_stream_authenticate (client_t *client, const char *mount, struct _mount_proxy *mountinfo);
+
 /* called from auth thread, after the client has successfully authenticated
  * and requires adding to source or fserve. */
 int auth_postprocess_listener (auth_client *auth_user);

Modified: icecast/trunk/icecast/src/auth_url.c
===================================================================
--- icecast/trunk/icecast/src/auth_url.c	2009-01-12 21:36:29 UTC (rev 15620)
+++ icecast/trunk/icecast/src/auth_url.c	2009-01-14 01:18:22 UTC (rev 15621)
@@ -46,6 +46,15 @@
  *
  * action=mount_add&mount=/live&server=myserver.com&port=8000
  * action=mount_remove&mount=/live&server=myserver.com&port=8000
+ *
+ * On source client connection, a request can be made to trigger a URL request
+ * to verify the details externally. Post info is
+ *
+ * action=stream_auth&mount=/stream&ip=IP&server=SERVER&port=8000&user=fred&pass=pass
+ *
+ * As admin requests can come in for a stream (eg metadata update) these requests
+ * can be issued while stream is active. For these &admin=1 is added to the POST
+ * details.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -80,6 +89,7 @@
     char *removeurl;
     char *stream_start;
     char *stream_end;
+    char *stream_auth;
     char *username;
     char *password;
     char *auth_header;
@@ -447,7 +457,53 @@
     return;
 }
 
+static void url_stream_auth (auth_client *auth_user)
+{
+    ice_config_t *config;
+    int port;
+    client_t *client = auth_user->client;
+    auth_url *url = client->auth->state;
+    char *mount, *host, *user, *pass, *ipaddr, *admin="";
+    char post [4096];
 
+    if (strchr (url->stream_auth, '@') == NULL)
+    {
+        if (url->userpwd)
+            curl_easy_setopt (url->handle, CURLOPT_USERPWD, url->userpwd);
+        else
+            curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
+    }
+    else
+        curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
+    curl_easy_setopt (url->handle, CURLOPT_URL, url->stream_auth);
+    curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post);
+    curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user);
+    if (strcmp (auth_user->mount, httpp_getvar (client->parser, HTTPP_VAR_URI)) != 0)
+        admin = "&admin=1";
+    mount = util_url_escape (auth_user->mount);
+    config = config_get_config ();
+    host = util_url_escape (config->hostname);
+    port = config->port;
+    config_release_config ();
+    user = util_url_escape (client->username);
+    pass = util_url_escape (client->password);
+    ipaddr = util_url_escape (client->con->ip);
+
+    snprintf (post, sizeof (post),
+            "action=stream_auth&mount=%s&ip=%s&server=%s&port=%d&user=%s&pass=%s%s",
+            mount, ipaddr, host, port, user, pass, admin);
+    free (ipaddr);
+    free (user);
+    free (pass);
+    free (mount);
+    free (host);
+
+    client->authenticated = 0;
+    if (curl_easy_perform (url->handle))
+        WARN2 ("auth to server %s failed with %s", url->stream_auth, url->errormsg);
+}
+
+
 static auth_result auth_url_adduser(auth_t *auth, const char *username, const char *password)
 {
     return AUTH_FAILED;
@@ -516,6 +572,12 @@
             free (url_info->stream_end);
             url_info->stream_end = strdup (options->value);
         }
+        if(!strcmp(options->name, "stream_auth"))
+        {
+            authenticator->stream_auth = url_stream_auth;
+            free (url_info->stream_auth);
+            url_info->stream_auth = strdup (options->value);
+        }
         if(!strcmp(options->name, "auth_header"))
         {
             free (url_info->auth_header);

Modified: icecast/trunk/icecast/src/client.c
===================================================================
--- icecast/trunk/icecast/src/client.c	2009-01-12 21:36:29 UTC (rev 15620)
+++ icecast/trunk/icecast/src/client.c	2009-01-14 01:18:22 UTC (rev 15621)
@@ -122,7 +122,37 @@
     free(client);
 }
 
+/* return -1 for failed, 0 for authenticated, 1 for pending
+ */
+int client_check_source_auth (client_t *client, const char *mount)
+{
+    ice_config_t *config = config_get_config();
+    char *pass = config->source_password;
+    char *user = "source";
+    int ret = -1;
+    mount_proxy *mountinfo = config_find_mount (config, mount);
 
+    do
+    {
+        if (mountinfo)
+        {
+            ret = 1;
+            if (auth_stream_authenticate (client, mount, mountinfo) > 0)
+                break;
+            ret = -1;
+            if (mountinfo->password)
+                pass = mountinfo->password;
+            if (mountinfo->username)
+                user = mountinfo->username;
+        }
+        if (connection_check_pass (client->parser, user, pass) > 0)
+            ret = 0;
+    } while (0);
+    config_release_config();
+    return ret;
+}
+
+
 /* helper function for reading data from a client */
 int client_read_bytes (client_t *client, void *buf, unsigned len)
 {

Modified: icecast/trunk/icecast/src/client.h
===================================================================
--- icecast/trunk/icecast/src/client.h	2009-01-12 21:36:29 UTC (rev 15620)
+++ icecast/trunk/icecast/src/client.h	2009-01-14 01:18:22 UTC (rev 15621)
@@ -77,5 +77,6 @@
 int client_send_bytes (client_t *client, const void *buf, unsigned len);
 int client_read_bytes (client_t *client, void *buf, unsigned len);
 void client_set_queue (client_t *client, refbuf_t *refbuf);
+int client_check_source_auth (client_t *client, const char *mount);
 
 #endif  /* __CLIENT_H__ */

Modified: icecast/trunk/icecast/src/connection.c
===================================================================
--- icecast/trunk/icecast/src/connection.c	2009-01-12 21:36:29 UTC (rev 15620)
+++ icecast/trunk/icecast/src/connection.c	2009-01-14 01:18:22 UTC (rev 15621)
@@ -968,29 +968,17 @@
     return ret;
 }
 
-int connection_check_source_pass(http_parser_t *parser, const char *mount)
+
+/* return 0 for failed, 1 for ok
+ */
+int connection_check_pass (http_parser_t *parser, const char *user, const char *pass)
 {
-    ice_config_t *config = config_get_config();
-    char *pass = config->source_password;
-    char *user = "source";
     int ret;
-    int ice_login = config->ice_login;
     const char *protocol;
 
-    mount_proxy *mountinfo = config_find_mount (config, mount);
-
-    if (mountinfo)
-    {
-        if (mountinfo->password)
-            pass = mountinfo->password;
-        if (mountinfo->username)
-            user = mountinfo->username;
-    }
-
     if(!pass) {
         WARN0("No source password set, rejecting source");
-        config_release_config();
-        return 0;
+        return -1;
     }
 
     protocol = httpp_getvar(parser, HTTPP_VAR_PROTOCOL);
@@ -999,22 +987,24 @@
     }
     else {
         ret = _check_pass_http(parser, user, pass);
-        if(!ret && ice_login)
+        if (!ret)
         {
-            ret = _check_pass_ice(parser, pass);
-            if(ret)
-                WARN0("Source is using deprecated icecast login");
+            ice_config_t *config = config_get_config_unlocked();
+            if (config->ice_login)
+            {
+                ret = _check_pass_ice(parser, pass);
+                if(ret)
+                    WARN0("Source is using deprecated icecast login");
+            }
         }
     }
-    config_release_config();
     return ret;
 }
 
 
-static void _handle_source_request (client_t *client, char *uri, int auth_style)
+/* only called for native icecast source clients */
+static void _handle_source_request (client_t *client, const char *uri)
 {
-    source_t *source;
-
     INFO1("Source logging in at mountpoint \"%s\"", uri);
 
     if (uri[0] != '/')
@@ -1023,24 +1013,30 @@
         client_send_401 (client);
         return;
     }
-    if (auth_style == ICECAST_SOURCE_AUTH) {
-        if (connection_check_source_pass (client->parser, uri) == 0)
-        {
-            /* We commonly get this if the source client is using the wrong
-             * protocol: attempt to diagnose this and return an error
-             */
-            /* TODO: Do what the above comment says */
+    switch (client_check_source_auth (client, uri))
+    {
+        case 0: /* authenticated from config file */
+            source_startup (client, uri, ICECAST_SOURCE_AUTH);
+            break;
+
+        case 1: /* auth pending */
+            break;
+
+        default: /* failed */
             INFO1("Source (%s) attempted to login with invalid or missing password", uri);
             client_send_401(client);
-            return;
-        }
+            break;
     }
+}
+
+
+void source_startup (client_t *client, const char *uri, int auth_style)
+{
+    source_t *source;
     source = source_reserve (uri);
+
     if (source)
     {
-        if (auth_style == SHOUTCAST_SOURCE_AUTH) {
-            source->shoutcast_compat = 1;
-        }
         source->client = client;
         source->parser = client->parser;
         source->con = client->con;
@@ -1048,7 +1044,14 @@
         {
             source_clear_source (source);
             source_free_source (source);
+            return;
         }
+        client->respcode = 200;
+        if (auth_style == SHOUTCAST_SOURCE_AUTH)
+        {
+            source->shoutcast_compat = 1;
+            source_client_callback (client, source);
+        }
         else
         {
             refbuf_t *ok = refbuf_new (PER_CLIENT_REFBUF_SIZE);
@@ -1241,7 +1244,7 @@
             memmove (ptr, ptr + node->stream_offset, client->refbuf->len);
         }
         client->parser = parser;
-        _handle_source_request (client, shoutcast_mount, SHOUTCAST_SOURCE_AUTH);
+        source_startup (client, shoutcast_mount, SHOUTCAST_SOURCE_AUTH);
     }
     else {
         httpp_destroy (parser);
@@ -1322,7 +1325,7 @@
                 }
 
                 if (parser->req_type == httpp_req_source) {
-                    _handle_source_request (client, uri, ICECAST_SOURCE_AUTH);
+                    _handle_source_request (client, uri);
                 }
                 else if (parser->req_type == httpp_req_stats) {
                     _handle_stats_request (client, uri);

Modified: icecast/trunk/icecast/src/connection.h
===================================================================
--- icecast/trunk/icecast/src/connection.h	2009-01-12 21:36:29 UTC (rev 15620)
+++ icecast/trunk/icecast/src/connection.h	2009-01-14 01:18:22 UTC (rev 15621)
@@ -60,7 +60,7 @@
 connection_t *connection_create (sock_t sock, sock_t serversock, char *ip);
 int connection_complete_source (struct source_tag *source, int response);
 
-int connection_check_source_pass(http_parser_t *parser, const char *mount);
+int connection_check_pass (http_parser_t *parser, const char *user, const char *pass);
 int connection_check_relay_pass(http_parser_t *parser);
 int connection_check_admin_pass(http_parser_t *parser);
 

Modified: icecast/trunk/icecast/src/source.h
===================================================================
--- icecast/trunk/icecast/src/source.h	2009-01-12 21:36:29 UTC (rev 15620)
+++ icecast/trunk/icecast/src/source.h	2009-01-14 01:18:22 UTC (rev 15621)
@@ -80,6 +80,7 @@
 
 source_t *source_reserve (const char *mount);
 void *source_client_thread (void *arg);
+void source_startup (client_t *client, const char *uri, int auth_style);
 void source_client_callback (client_t *client, void *source);
 void source_update_settings (ice_config_t *config, source_t *source, mount_proxy *mountinfo);
 void source_clear_source (source_t *source);



More information about the commits mailing list