broadway: Add support for password authentication

This commit is contained in:
Alexander Larsson 2013-03-28 22:57:33 +01:00
parent 1148a375e5
commit 8289f4cdc9
7 changed files with 202 additions and 50 deletions

View File

@ -54,6 +54,15 @@ Start your applications like this:
BROADWAY_DISPLAY=:5 gtk3-demo
</programlisting>
You can add password protection for your session by creating a file in
<filename>$XDG_CONFIG_HOME/broadway.passwd</filename> or <filename>$HOME/.config/broadway.passwd</filename>
with a crypt(3) style password hash.
A simple way to generate it is with openssl:
<programlisting>
openssl passwd -1 > ~/.config/broadway.passwd
</programlisting>
</para>
</refsect1>

View File

@ -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)

View File

@ -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)
{

View File

@ -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,

View File

@ -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 {

View File

@ -2,6 +2,8 @@
#include "broadway-output.h"
#define _XOPEN_SOURCE /* for crypt */
#include <glib.h>
#include <glib/gprintf.h>
#include "gdktypes.h"
@ -9,6 +11,7 @@
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <crypt.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
@ -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

View File

@ -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<br>";
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);
}