Added authentication for webservice connection.
authorSuperBFG7 <daniel@despite.ch>
Mon, 23 Aug 2021 15:23:48 +0000 (17:23 +0200)
committerSuperBFG7 <daniel@despite.ch>
Mon, 23 Aug 2021 15:23:48 +0000 (17:23 +0200)
htdocs/js/mpd.js
src/http_server.c
src/mpd_client.c
src/mpd_client.h

index 663745a..5e02bc1 100644 (file)
@@ -30,6 +30,7 @@ var MAX_ELEMENTS_PER_PAGE = 512;
 var isTouch = Modernizr.touch ? 1 : 0;
 var filter = '';
 var scrobbler = '';
+var wss_auth_token = '';
 
 var app = $.sammy(function () {
     function runBrowse() {
@@ -38,7 +39,8 @@ var app = $.sammy(function () {
         $('#breadcrump').addClass('hide');
         $('#filter').addClass('hide');
         $('#salamisandwich').removeClass('hide').find('tr:gt(0)').remove();
-        socket.send('MPD_API_GET_QUEUE,' + pagination);
+        if (wss_auth_token !== '')
+            socket.send('MPD_API_GET_QUEUE,' + pagination);
 
         $('#panel-heading').text('Queue');
         $('#panel-heading-info').empty();
@@ -249,6 +251,25 @@ $(document).ready(function () {
     );
 });
 
+function webSocketAuthenticate() {
+    var u = document.URL.split('#');
+    var separator;
+
+    if (/\/$/.test(u[0])) {
+        separator = '';
+    } else {
+        separator = '/';
+    }
+
+    $.ajax({
+        url: u[0] + separator + 'wss-auth',
+        success: function (data) {
+            wss_auth_token = data;
+            socket.send('MPD_API_AUTHORIZE,' + wss_auth_token);
+        },
+    });
+}
+
 function webSocketConnect() {
     if (typeof MozWebSocket != 'undefined') {
         socket = new MozWebSocket(get_appropriate_ws_url());
@@ -267,9 +288,8 @@ function webSocketConnect() {
                 .show();
 
             app.run();
-            /* emit initial request for output names */
-            socket.send('MPD_API_GET_OUTPUTS');
-            socket.send('MPD_API_GET_CHANNELS');
+
+            if (wss_auth_token === '') webSocketAuthenticate();
         };
 
         socket.onmessage = function got_packet(msg) {
@@ -904,6 +924,15 @@ function webSocketConnect() {
                         $('#mpd_password_set').removeClass('hide');
                     break;
 
+                case 'authorized':
+                    if (obj.data === 'true') {
+                        /* emit initial request for output names */
+                        socket.send('MPD_API_GET_OUTPUTS');
+                        socket.send('MPD_API_GET_CHANNELS');
+                    } else webSocketAuthenticate();
+
+                    break;
+
                 case 'error':
                     $('.top-right')
                         .notify({
@@ -918,6 +947,7 @@ function webSocketConnect() {
 
         socket.onclose = function () {
             console.log('disconnected');
+            wss_auth_token = '';
             $('.top-right')
                 .notify({
                     message: {
index 1552db6..e059955 100644 (file)
 
 #include "http_server.h"
 
+#include <openssl/rand.h>
 #include <string.h>
 
+#include "mpd_client.h"
+
 int callback_http(struct mg_connection *c) {
     const struct embedded_file *req_file;
 
@@ -35,6 +38,22 @@ int callback_http(struct mg_connection *c) {
         return MG_TRUE;
     }
 
+    if (!strcmp(c->uri, "/wss-auth")) {
+        unsigned char salt[WSS_AUTH_TOKEN_SIZE + 1];
+
+        RAND_bytes(salt, WSS_AUTH_TOKEN_SIZE);
+        for (int i = 0; i <= WSS_AUTH_TOKEN_SIZE; i++) salt[i] = salt[i] % 26 + 65;
+        salt[WSS_AUTH_TOKEN_SIZE] = 0;
+        if (mpd.wss_auth_token)
+            free(mpd.wss_auth_token);
+        mpd.wss_auth_token = strdup((char *)salt);
+
+        mg_send_header(c, "Content-Type", "text/plain");
+        mg_send_data(c, salt, WSS_AUTH_TOKEN_SIZE);
+
+        return MG_TRUE;
+    }
+
     mg_send_status(c, 404);
     mg_printf_data(c, "Not Found");
     return MG_TRUE;
index 4aa6565..ac594bc 100644 (file)
@@ -58,6 +58,18 @@ int callback_mpd(struct mg_connection *c) {
     int int_buf;
     char *p_charbuf = NULL, *token;
 
+    if (!c->connection_param)
+        c->connection_param = calloc(1, sizeof(struct t_mpd_client_session));
+
+    struct t_mpd_client_session *s = (struct t_mpd_client_session *)c->connection_param;
+
+    if (!s->authorized && (cmd_id != MPD_API_AUTHORIZE)) {
+        n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"error\",\"data\":\"not authorized\"}");
+        mg_websocket_write(c, 1, mpd.buf, n);
+
+        return MG_TRUE;
+    }
+
     if (cmd_id == -1)
         return MG_TRUE;
 
@@ -66,6 +78,25 @@ int callback_mpd(struct mg_connection *c) {
         return MG_TRUE;
 
     switch (cmd_id) {
+        case MPD_API_AUTHORIZE:
+            p_charbuf = strdup(c->content);
+            if (strcmp(strtok(p_charbuf, ","), "MPD_API_AUTHORIZE"))
+                goto out_authorize;
+
+            if ((token = strtok(NULL, ",")) == NULL)
+                goto out_authorize;
+
+            free(p_charbuf);
+            p_charbuf = strdup(c->content);
+            s->auth_token = strdup(get_arg1(p_charbuf));
+            if (!strcmp(mpd.wss_auth_token, s->auth_token))
+                s->authorized = 1;
+
+            n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"authorized\", \"data\":\"%s\"}",
+                         s->authorized ? "true" : "false");
+        out_authorize:
+            free(p_charbuf);
+            break;
         case MPD_API_UPDATE_DB:
             mpd_run_update(mpd.conn, NULL);
             break;
@@ -332,8 +363,13 @@ int callback_mpd(struct mg_connection *c) {
 
 int mpd_close_handler(struct mg_connection *c) {
     /* Cleanup session data */
-    if (c->connection_param)
+    if (c->connection_param) {
+        struct t_mpd_client_session *s = (struct t_mpd_client_session *)c->connection_param;
+        if (s->auth_token)
+            free(s->auth_token);
         free(c->connection_param);
+    }
+
     return 0;
 }
 
@@ -343,6 +379,14 @@ static int mpd_notify_callback(struct mg_connection *c, enum mg_event ev) {
     if (!c->is_websocket)
         return MG_TRUE;
 
+    if (!c->connection_param)
+        return MG_TRUE;
+
+    struct t_mpd_client_session *s = (struct t_mpd_client_session *)c->connection_param;
+
+    if (!s->authorized)
+        return MG_TRUE;
+
     if (c->callback_param) {
         /* error message? */
         n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"error\",\"data\":\"%s\"}",
@@ -352,11 +396,6 @@ static int mpd_notify_callback(struct mg_connection *c, enum mg_event ev) {
         return MG_TRUE;
     }
 
-    if (!c->connection_param)
-        c->connection_param = calloc(1, sizeof(struct t_mpd_client_session));
-
-    struct t_mpd_client_session *s = (struct t_mpd_client_session *)c->connection_param;
-
     if (mpd.conn_state != MPD_CONNECTED) {
         n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"disconnected\"}");
         mg_websocket_write(c, 1, mpd.buf, n);
@@ -444,6 +483,11 @@ void mpd_poll(struct mg_server *s) {
                 c->callback_param = NULL;
                 mpd_notify_callback(c, MG_POLL);
             }
+            mpd.buf_size = mpd_put_channels(mpd.buf);
+            for (struct mg_connection *c = mg_next(s, NULL); c != NULL; c = mg_next(s, c)) {
+                c->callback_param = NULL;
+                mpd_notify_callback(c, MG_POLL);
+            }
             break;
     }
 }
index fe6e9f7..ca9c6e3 100644 (file)
@@ -34,6 +34,8 @@
 #define MAX_SIZE 1024 * 100
 #define MAX_ELEMENTS_PER_PAGE 512
 
+#define WSS_AUTH_TOKEN_SIZE 50
+
 #define GEN_ENUM(X) X,
 #define GEN_STR(X) #X,
 #define MPD_CMDS(X)             \
@@ -68,7 +70,8 @@
     X(MPD_API_TOGGLE_CONSUME)   \
     X(MPD_API_TOGGLE_SINGLE)    \
     X(MPD_API_TOGGLE_CROSSFADE) \
-    X(MPD_API_TOGGLE_REPEAT)
+    X(MPD_API_TOGGLE_REPEAT)    \
+    X(MPD_API_AUTHORIZE)
 
 enum mpd_cmd_ids { MPD_CMDS(GEN_ENUM) };
 
@@ -86,6 +89,7 @@ struct t_mpd {
     char host[128];
     char *password;
     char *gpass;
+    char *wss_auth_token;
 
     struct mpd_connection *conn;
     enum mpd_conn_states conn_state;
@@ -103,6 +107,8 @@ extern struct t_mpd mpd;
 struct t_mpd_client_session {
     int song_id;
     unsigned queue_version;
+    int authorized;
+    char *auth_token;
 };
 
 void mpd_poll(struct mg_server *s);