From 8289f4cdc9a8733379c4d451fa9030b03515fb01 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 28 Mar 2013 22:57:33 +0100 Subject: [PATCH] broadway: Add support for password authentication --- docs/reference/gtk/broadwayd.xml | 9 ++ gdk/broadway/Makefile.am | 2 +- gdk/broadway/broadway-output.c | 25 ++++++ gdk/broadway/broadway-output.h | 5 ++ gdk/broadway/broadway-protocol.h | 3 + gdk/broadway/broadway-server.c | 138 ++++++++++++++++++++++--------- gdk/broadway/broadway.js | 70 +++++++++++++--- 7 files changed, 202 insertions(+), 50 deletions(-) diff --git a/docs/reference/gtk/broadwayd.xml b/docs/reference/gtk/broadwayd.xml index be41c1aff8..14f7064c0c 100644 --- a/docs/reference/gtk/broadwayd.xml +++ b/docs/reference/gtk/broadwayd.xml @@ -54,6 +54,15 @@ Start your applications like this: BROADWAY_DISPLAY=:5 gtk3-demo +You can add password protection for your session by creating a file in +$XDG_CONFIG_HOME/broadway.passwd or $HOME/.config/broadway.passwd +with a crypt(3) style password hash. + +A simple way to generate it is with openssl: + +openssl passwd -1 > ~/.config/broadway.passwd + + diff --git a/gdk/broadway/Makefile.am b/gdk/broadway/Makefile.am index a062240370..dd4b189550 100644 --- a/gdk/broadway/Makefile.am +++ b/gdk/broadway/Makefile.am @@ -83,7 +83,7 @@ broadwayd_SOURCES = \ broadway-output.h \ broadway-output.c -broadwayd_LDADD = $(GDK_DEP_LIBS) -lrt +broadwayd_LDADD = $(GDK_DEP_LIBS) -lrt -lcrypt MAINTAINERCLEANFILES = $(broadway_built_sources) EXTRA_DIST += $(broadway_built_sources) diff --git a/gdk/broadway/broadway-output.c b/gdk/broadway/broadway-output.c index 3e1fb1ec3e..774cca3e82 100644 --- a/gdk/broadway/broadway-output.c +++ b/gdk/broadway/broadway-output.c @@ -317,6 +317,13 @@ broadway_output_get_next_serial (BroadwayOutput *output) return output->serial; } +void +broadway_output_set_next_serial (BroadwayOutput *output, + guint32 serial) +{ + output->serial = serial; +} + /************************************************************************ * Core rendering operations * @@ -471,6 +478,24 @@ broadway_output_new_surface(BroadwayOutput *output, append_bool (output, is_temp); } +void +broadway_output_request_auth (BroadwayOutput *output) +{ + write_header (output, BROADWAY_OP_REQUEST_AUTH); +} + +void +broadway_output_auth_ok (BroadwayOutput *output) +{ + write_header (output, BROADWAY_OP_AUTH_OK); +} + +void +broadway_output_disconnected (BroadwayOutput *output) +{ + write_header (output, BROADWAY_OP_DISCONNECTED); +} + void broadway_output_show_surface(BroadwayOutput *output, int id) { diff --git a/gdk/broadway/broadway-output.h b/gdk/broadway/broadway-output.h index 02436f4189..ea57c7e5f4 100644 --- a/gdk/broadway/broadway-output.h +++ b/gdk/broadway/broadway-output.h @@ -23,6 +23,8 @@ BroadwayOutput *broadway_output_new (GOutputStream *out, void broadway_output_free (BroadwayOutput *output); int broadway_output_flush (BroadwayOutput *output); int broadway_output_has_error (BroadwayOutput *output); +void broadway_output_set_next_serial (BroadwayOutput *output, + guint32 serial); guint32 broadway_output_get_next_serial (BroadwayOutput *output); void broadway_output_new_surface (BroadwayOutput *output, int id, @@ -31,6 +33,9 @@ void broadway_output_new_surface (BroadwayOutput *output, int w, int h, gboolean is_temp); +void broadway_output_request_auth (BroadwayOutput *output); +void broadway_output_auth_ok (BroadwayOutput *output); +void broadway_output_disconnected (BroadwayOutput *output); void broadway_output_show_surface (BroadwayOutput *output, int id); void broadway_output_hide_surface (BroadwayOutput *output, diff --git a/gdk/broadway/broadway-protocol.h b/gdk/broadway/broadway-protocol.h index c098fdf8de..fe7a8a4fe5 100644 --- a/gdk/broadway/broadway-protocol.h +++ b/gdk/broadway/broadway-protocol.h @@ -36,6 +36,9 @@ typedef enum { BROADWAY_OP_SET_TRANSIENT_FOR = 'p', BROADWAY_OP_PUT_RGB = 'i', BROADWAY_OP_FLUSH = 'f', + BROADWAY_OP_REQUEST_AUTH = 'l', + BROADWAY_OP_AUTH_OK = 'L', + BROADWAY_OP_DISCONNECTED = 'D', } BroadwayOpType; typedef struct { diff --git a/gdk/broadway/broadway-server.c b/gdk/broadway/broadway-server.c index 77dd894714..5b6bb96eef 100644 --- a/gdk/broadway/broadway-server.c +++ b/gdk/broadway/broadway-server.c @@ -2,6 +2,8 @@ #include "broadway-output.h" +#define _XOPEN_SOURCE /* for crypt */ + #include #include #include "gdktypes.h" @@ -9,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +22,7 @@ typedef struct BroadwayWindow BroadwayWindow; struct _BroadwayServer { GObject parent_instance; + char *password; char *address; int port; GSocketService *service; @@ -69,6 +73,7 @@ typedef struct HttpRequest { struct BroadwayInput { BroadwayServer *server; + BroadwayOutput *output; GSocketConnection *connection; GByteArray *buffer; GSource *source; @@ -76,6 +81,7 @@ struct BroadwayInput { gint64 time_base; gboolean proto_v7_plus; gboolean binary; + gboolean active; }; struct BroadwayWindow { @@ -100,6 +106,8 @@ static void broadway_server_init (BroadwayServer *server) { BroadwayWindow *root; + char *passwd_file; + char *password, *p; server->service = g_socket_service_new (); server->pointer_grab_window_id = -1; @@ -108,6 +116,22 @@ broadway_server_init (BroadwayServer *server) server->id_ht = g_hash_table_new (NULL, NULL); server->id_counter = 0; + passwd_file = g_build_filename (g_get_user_config_dir (), + "broadway.passwd", NULL); + + if (g_file_get_contents (passwd_file, + &password, NULL, NULL)) + { + p = strchr (password, '\n'); + if (p) + *p = 0; + g_strstrip (password); + if (strlen (password) > 3) + server->password = password; + else + g_free (password); + } + root = g_new0 (BroadwayWindow, 1); root->id = server->id_counter++; root->width = 1024; @@ -139,7 +163,7 @@ broadway_server_class_init (BroadwayServerClass * class) object_class->finalize = broadway_server_finalize; } -static void start_output (HttpRequest *request, gboolean proto_v7_plus, gboolean binary); +static void start (BroadwayInput *input); static void http_request_free (HttpRequest *request) @@ -349,6 +373,14 @@ update_future_pointer_info (BroadwayServer *server, BroadwayInputPointerMsg *dat server->future_mouse_in_toplevel = data->mouse_window_id; } +static gboolean +verify_password (BroadwayServer *server, const char *password) +{ + char *hash; + hash = crypt (password, server->password); + return strcmp (hash, server->password) == 0; +} + static void parse_input_message (BroadwayInput *input, const char *message) { @@ -357,6 +389,22 @@ parse_input_message (BroadwayInput *input, const char *message) char *p; gint64 time_; + if (!input->active) + { + /* The input has not been activated yet, handle auth/start */ + + if (message[0] != 'l' || + !verify_password (server, message+1)) + { + broadway_output_request_auth (input->output); + broadway_output_flush (input->output); + } + else + start (input); + + return; + } + memset (&msg, 0, sizeof (msg)); p = (char *)message; @@ -570,7 +618,7 @@ parse_input (BroadwayInput *input) } break; case BROADWAY_WS_CNX_PING: - broadway_output_pong (server->output); + broadway_output_pong (input->output); break; case BROADWAY_WS_CNX_PONG: break; /* we never send pings, but tolerate pongs */ @@ -596,7 +644,8 @@ parse_input (BroadwayInput *input) if (buf[0] != 0) { - server->input = NULL; + if (server->input == input) + server->input = NULL; broadway_input_free (input); return; } @@ -613,7 +662,8 @@ parse_input (BroadwayInput *input) if (len > 0 && buf[0] != 0) { - server->input = NULL; + if (server->input == input) + server->input = NULL; broadway_input_free (input); break; } @@ -640,19 +690,16 @@ queue_process_input_at_idle (BroadwayServer *server) } static void -broadway_server_read_all_input_nonblocking (BroadwayServer *server) +broadway_server_read_all_input_nonblocking (BroadwayInput *input) { GInputStream *in; gssize res; guint8 buffer[1024]; GError *error; - BroadwayInput *input; - if (server->input == NULL) + if (input == NULL) return; - input = server->input; - in = g_io_stream_get_input_stream (G_IO_STREAM (input->connection)); error = NULL; @@ -668,7 +715,8 @@ broadway_server_read_all_input_nonblocking (BroadwayServer *server) return; } - server->input = NULL; + if (input->server->input == input) + input->server->input = NULL; broadway_input_free (input); if (res < 0) { @@ -686,7 +734,7 @@ broadway_server_read_all_input_nonblocking (BroadwayServer *server) static void broadway_server_consume_all_input (BroadwayServer *server) { - broadway_server_read_all_input_nonblocking (server); + broadway_server_read_all_input_nonblocking (server->input); /* Since we're parsing input but not processing the resulting messages we might not get a readable callback on the stream, so queue an idle to @@ -701,9 +749,10 @@ input_data_cb (GObject *stream, { BroadwayServer *server = input->server; - broadway_server_read_all_input_nonblocking (server); + broadway_server_read_all_input_nonblocking (input); - process_input_messages (server); + if (input->active) + process_input_messages (server); return TRUE; } @@ -878,15 +927,14 @@ start_input (HttpRequest *request, gboolean binary) gsize len; GChecksum *checksum; char *origin, *host; - BroadwayServer *server; BroadwayInput *input; const void *data_buffer; gsize data_buffer_size; GInputStream *in; char *key_v7; gboolean proto_v7_plus; - - server = request->server; + GSocket *socket; + int flag = 1; #ifdef DEBUG_WEBSOCKETS g_print ("incoming request:\n%s\n", request->request->str); @@ -1033,15 +1081,11 @@ start_input (HttpRequest *request, gboolean binary) proto_v7_plus = FALSE; } - - if (server->input != NULL) - { - broadway_input_free (server->input); - server->input = NULL; - } + socket = g_socket_connection_get_socket (request->connection); + setsockopt (g_socket_get_fd (socket), IPPROTO_TCP, + TCP_NODELAY, (char *) &flag, sizeof(int)); input = g_new0 (BroadwayInput, 1); - input->server = request->server; input->connection = g_object_ref (request->connection); input->proto_v7_plus = proto_v7_plus; @@ -1051,9 +1095,9 @@ start_input (HttpRequest *request, gboolean binary) input->buffer = g_byte_array_sized_new (data_buffer_size); g_byte_array_append (input->buffer, data_buffer, data_buffer_size); - server->input = input; - - start_output (request, proto_v7_plus, binary); + input->output = + broadway_output_new (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)), + 0, proto_v7_plus, binary); /* This will free and close the data input stream, but we got all the buffered content already */ http_request_free (request); @@ -1063,35 +1107,53 @@ start_input (HttpRequest *request, gboolean binary) g_source_set_callback (input->source, (GSourceFunc)input_data_cb, input, NULL); g_source_attach (input->source, NULL); + if (input->server->password) + { + broadway_output_request_auth (input->output); + broadway_output_flush (input->output); + } + else + start (input); + /* Process any data in the pipe already */ parse_input (input); - process_input_messages (server); g_strfreev (lines); } static void -start_output (HttpRequest *request, gboolean proto_v7_plus, gboolean binary) +start (BroadwayInput *input) { - GSocket *socket; BroadwayServer *server; - int flag = 1; - socket = g_socket_connection_get_socket (request->connection); - setsockopt(g_socket_get_fd (socket), IPPROTO_TCP, - TCP_NODELAY, (char *) &flag, sizeof(int)); + input->active = TRUE; - server = BROADWAY_SERVER (request->server); + server = BROADWAY_SERVER (input->server); + + if (server->output) + { + broadway_output_disconnected (server->output); + broadway_output_flush (server->output); + } + + if (server->input != NULL) + { + broadway_input_free (server->input); + server->input = NULL; + } + + server->input = input; if (server->output) { server->saved_serial = broadway_output_get_next_serial (server->output); broadway_output_free (server->output); } + server->output = input->output; - server->output = - broadway_output_new (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)), - server->saved_serial, proto_v7_plus, binary); + broadway_output_set_next_serial (server->output, server->saved_serial); + broadway_output_auth_ok (server->output); + broadway_output_flush (server->output); broadway_server_resync_windows (server); @@ -1099,6 +1161,8 @@ start_output (HttpRequest *request, gboolean proto_v7_plus, gboolean binary) broadway_output_grab_pointer (server->output, server->pointer_grab_window_id, server->pointer_grab_owner_events); + + process_input_messages (server); } static void diff --git a/gdk/broadway/broadway.js b/gdk/broadway/broadway.js index fb4cffed24..3f51e65692 100644 --- a/gdk/broadway/broadway.js +++ b/gdk/broadway/broadway.js @@ -537,6 +537,21 @@ function handleCommands(cmd) var command = cmd.get_char(); lastSerial = cmd.get_32(); switch (command) { + case 'l': + login (); + break; + + case 'L': + if (loginDiv != null) + loginDiv.parentNode.removeChild(loginDiv); + start (); + break; + + case 'D': + alert ("disconnected"); + inputSocket = null; + break; + case 's': // create new surface id = cmd.get_16(); x = cmd.get_16s(); @@ -2625,6 +2640,47 @@ function newWS(loc) { return ws; } +function start() +{ + setupDocument(document); + + var w, h; + w = window.innerWidth; + h = window.innerHeight; + window.onresize = function(ev) { + var w, h; + w = window.innerWidth; + h = window.innerHeight; + sendInput ("d", [w, h]); + }; + sendInput ("d", [w, h]); +} + +var loginDiv = null; +function login() +{ + if (loginDiv == null) { + var div = document.createElement('div'); + document.body.appendChild(div); + div.innerHTML = "Please enter password
"; + div.style.marginTop = "40px"; + div.style.textAlign = "center"; + + var input = document.createElement("input"); + input.setAttribute("type", "password"); + div.appendChild(input); + input.onkeyup = function(e) { + if (e.keyCode === 13) { + inputSocket.send ("l" + input.value); + } + } + input.focus (); + loginDiv = div; + } else { + alert ("Wrong password"); + } +} + function connect() { var url = window.location.toString(); @@ -2646,23 +2702,13 @@ function connect() ws.onopen = function() { inputSocket = ws; - var w, h; - w = window.innerWidth; - h = window.innerHeight; - window.onresize = function(ev) { - var w, h; - w = window.innerWidth; - h = window.innerHeight; - sendInput ("d", [w, h]); - }; - sendInput ("d", [w, h]); }; ws.onclose = function() { + if (inputSocket != null) + alert ("disconnected"); inputSocket = null; }; ws.onmessage = function(event) { handleMessage(event.data); }; - - setupDocument(document); }