2
* Copyright (C) 2010 Robert Ancell.
3
* Author: Robert Ancell <robert.ancell@canonical.com>
5
* This program is free software: you can redistribute it and/or modify it under
6
* the terms of the GNU General Public License as published by the Free Software
7
* Foundation, either version 3 of the License, or (at your option) any later
8
* version. See http://www.gnu.org/copyleft/gpl.html the full text of the
16
#include <X11/Xdmcp.h>
18
#include "ldm-marshal.h"
20
#include "xdmcp-server.h"
21
#include "xdmcp-protocol.h"
22
#include "xdmcp-session-private.h"
29
static guint signals[LAST_SIGNAL] = { 0 };
31
struct XDMCPServerPrivate
33
/* Port to listen on */
36
/* Listening sockets */
37
GSocket *socket, *socket6;
39
/* Hostname to report to client */
42
/* Status to report to clients */
45
/* Auhentication scheme to use */
46
gchar *authentication_name;
48
/* Auhentication data */
49
guchar *authentication_data;
50
gsize authentication_data_length;
52
/* Authorization scheme to use */
53
gchar *authorization_name;
55
/* Authorization data */
56
guchar *authorization_data;
57
gsize authorization_data_length;
59
/* Active XDMCP sessions */
63
G_DEFINE_TYPE (XDMCPServer, xdmcp_server, G_TYPE_OBJECT);
66
xdmcp_server_new (void)
68
return g_object_new (XDMCP_SERVER_TYPE, NULL);
72
xdmcp_server_set_port (XDMCPServer *server, guint port)
74
server->priv->port = port;
78
xdmcp_server_get_port (XDMCPServer *server)
80
return server->priv->port;
84
xdmcp_server_set_hostname (XDMCPServer *server, const gchar *hostname)
86
g_free (server->priv->hostname);
87
server->priv->hostname = g_strdup (hostname);
91
xdmcp_server_get_hostname (XDMCPServer *server)
93
return server->priv->hostname;
97
xdmcp_server_set_status (XDMCPServer *server, const gchar *status)
99
g_free (server->priv->status);
100
server->priv->status = g_strdup (status);
104
xdmcp_server_get_status (XDMCPServer *server)
106
return server->priv->status;
110
xdmcp_server_set_authentication (XDMCPServer *server, const gchar *name, const guchar *data, gsize data_length)
112
g_free (server->priv->authentication_name);
113
server->priv->authentication_name = g_strdup (name);
114
g_free (server->priv->authentication_data);
115
server->priv->authentication_data = g_malloc (data_length);
116
server->priv->authentication_data_length = data_length;
117
memcpy (server->priv->authentication_data, data, data_length);
121
xdmcp_server_get_authentication_name (XDMCPServer *server)
123
return server->priv->authentication_name;
127
xdmcp_server_get_authentication_data (XDMCPServer *server)
129
return server->priv->authentication_data;
133
xdmcp_server_get_authentication_data_length (XDMCPServer *server)
135
return server->priv->authentication_data_length;
139
xdmcp_server_set_authorization (XDMCPServer *server, const gchar *name, const guchar *data, gsize data_length)
141
g_free (server->priv->authorization_name);
142
server->priv->authorization_name = g_strdup (name);
143
g_free (server->priv->authorization_data);
144
server->priv->authorization_data = g_malloc (data_length);
145
server->priv->authorization_data_length = data_length;
146
memcpy (server->priv->authorization_data, data, data_length);
150
xdmcp_server_get_authorization_name (XDMCPServer *server)
152
return server->priv->authorization_name;
156
xdmcp_server_get_authorization_data (XDMCPServer *server)
158
return server->priv->authorization_data;
162
xdmcp_server_get_authorization_data_length (XDMCPServer *server)
164
return server->priv->authorization_data_length;
168
session_timeout_cb (XDMCPSession *session)
170
g_debug ("Timing out unmanaged session %d", session->priv->id);
171
g_hash_table_remove (session->priv->server->priv->sessions, GINT_TO_POINTER ((gint) session->priv->id));
175
static XDMCPSession *
176
add_session (XDMCPServer *server)
178
XDMCPSession *session;
183
id = g_random_int () & 0xFFFFFFFF;
184
} while (g_hash_table_lookup (server->priv->sessions, GINT_TO_POINTER ((gint) id)));
186
session = xdmcp_session_new (id);
187
session->priv->server = server;
188
g_hash_table_insert (server->priv->sessions, GINT_TO_POINTER ((gint) id), g_object_ref (session));
189
session->priv->inactive_timeout = g_timeout_add (10, (GSourceFunc) session_timeout_cb, session);
194
static XDMCPSession *
195
get_session (XDMCPServer *server, guint16 id)
197
return g_hash_table_lookup (server->priv->sessions, GINT_TO_POINTER ((gint) id));
201
send_packet (GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
206
g_debug ("Send %s", xdmcp_packet_tostring (packet));
208
n_written = xdmcp_packet_encode (packet, data, 1024);
210
g_critical ("Failed to encode XDMCP packet");
213
GError *error = NULL;
215
if (g_socket_send_to (socket, address, (gchar *) data, n_written, NULL, &error) < 0)
216
g_warning ("Error sending packet: %s", error->message);
218
g_clear_error (&error);
223
handle_query (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
225
XDMCPPacket *response;
227
gboolean match_authentication = FALSE;
229
/* If no authentication requested and we are configured for none then allow */
230
if (packet->Query.authentication_names[0] == NULL && strcmp (server->priv->authentication_name, "") == 0)
231
match_authentication = TRUE;
233
for (i = packet->Query.authentication_names; *i; i++)
235
if (strcmp (*i, server->priv->authentication_name) == 0)
237
match_authentication = TRUE;
242
if (match_authentication)
244
response = xdmcp_packet_alloc (XDMCP_Willing);
245
response->Willing.authentication_name = g_strdup (server->priv->authentication_name);
246
response->Willing.hostname = g_strdup (server->priv->hostname);
247
response->Willing.status = g_strdup (server->priv->status);
251
response = xdmcp_packet_alloc (XDMCP_Unwilling);
252
response->Willing.hostname = g_strdup (server->priv->hostname);
253
response->Willing.status = g_strdup (server->priv->status);
256
send_packet (socket, address, response);
258
xdmcp_packet_free (response);
262
handle_request (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
265
XDMCPPacket *response;
266
XDMCPSession *session;
267
guchar *authentication_data = NULL;
268
gsize authentication_data_length = 0;
269
gboolean match_authorization = FALSE;
270
guchar *authorization_data = NULL;
271
gsize authorization_data_length = 0;
272
guchar *session_authorization_data = NULL;
273
gsize session_authorization_data_length = 0;
275
GInetAddress *address4 = NULL, *address6 = NULL;
278
/* Must be using our authentication scheme */
279
if (strcmp (packet->Request.authentication_name, server->priv->authentication_name) != 0)
281
response = xdmcp_packet_alloc (XDMCP_Decline);
282
if (strcmp (server->priv->authentication_name, "") == 0)
283
response->Decline.status = g_strdup ("Server does not support authentication");
285
response->Decline.status = g_strdup_printf ("Server only supports %s authentication", server->priv->authentication_name);
286
response->Decline.authentication_name = g_strdup ("");
287
send_packet (socket, address, response);
288
xdmcp_packet_free (response);
292
/* Perform requested authentication */
293
if (strcmp (server->priv->authentication_name, "XDM-AUTHENTICATION-1") == 0)
295
guchar input[8], key[8];
297
memset (input, 0, 8);
298
memcpy (input, packet->Request.authentication_data.data, packet->Request.authentication_data.length > 8 ? 8 : packet->Request.authentication_data.length);
302
memcpy (key, server->priv->authentication_data, server->priv->authentication_data_length > 8 ? 8 : server->priv->authentication_data_length);
304
/* Decode message from server */
305
authentication_data = g_malloc (sizeof (guchar) * 8);
306
authentication_data_length = 8;
308
XdmcpUnwrap (input, key, rho.data, authentication_data_length);
309
XdmcpIncrementKey (&rho);
310
XdmcpWrap (rho.data, key, authentication_data, authentication_data_length);
313
/* Check if they support our authorization */
314
for (j = packet->Request.authorization_names; *j; j++)
316
if (strcmp (*j, server->priv->authorization_name) == 0)
318
match_authorization = TRUE;
323
if (!match_authorization)
325
response = xdmcp_packet_alloc (XDMCP_Decline);
326
if (strcmp (server->priv->authorization_name, "") == 0)
327
response->Decline.status = g_strdup ("Server does not support authorization");
329
response->Decline.status = g_strdup_printf ("Server only supports %s authorization", server->priv->authorization_name);
330
response->Decline.authentication_name = g_strdup (packet->Request.authentication_name);
331
response->Decline.authentication_data.data = authentication_data;
332
response->Decline.authentication_data.length = authentication_data_length;
333
send_packet (socket, address, response);
334
xdmcp_packet_free (response);
338
/* Perform requested authorization */
339
if (strcmp (server->priv->authorization_name, "MIT-MAGIC-COOKIE-1") == 0)
341
/* Data is the cookie */
342
authorization_data = g_malloc (sizeof (guchar) * server->priv->authorization_data_length);
343
memcpy (authorization_data, server->priv->authorization_data, server->priv->authorization_data_length);
344
authorization_data_length = server->priv->authorization_data_length;
346
else if (strcmp (server->priv->authorization_name, "XDM-AUTHORIZATION-1") == 0)
349
guchar key[8], session_key[8];
353
memcpy (key, server->priv->authentication_data, server->priv->authentication_data_length > 8 ? 8 : server->priv->authentication_data_length);
355
/* Generate a private session key */
356
// FIXME: Pick a good DES key?
358
for (i = 1; i < 8; i++)
359
session_key[i] = g_random_int () & 0xFF;
361
/* Encrypt the session key and send it to the server */
362
authorization_data = g_malloc (8);
363
authorization_data_length = 8;
364
XdmcpWrap (session_key, key, authorization_data, authorization_data_length);
366
/* Authorization data is the number received from the client followed by the private session key */
367
session_authorization_data = g_malloc (16);
368
session_authorization_data_length = 16;
369
XdmcpDecrementKey (&rho);
370
memcpy (session_authorization_data, rho.data, 8);
371
memcpy (session_authorization_data + 8, session_key, 8);
374
for (i = 0; i < packet->Request.n_connections; i++)
376
XDMCPConnection *connection;
378
connection = &packet->Request.connections[i];
379
switch (connection->type)
382
if (connection->address.length == 4)
383
address4 = g_inet_address_new_from_bytes (connection->address.data, G_SOCKET_FAMILY_IPV4);
385
case FamilyInternet6:
386
if (connection->address.length == 16)
387
address6 = g_inet_address_new_from_bytes (connection->address.data, G_SOCKET_FAMILY_IPV6);
392
if (!address4) // FIXME: && !address6)
394
response = xdmcp_packet_alloc (XDMCP_Decline);
395
response->Decline.status = g_strdup ("No valid address found");
396
response->Decline.authentication_name = g_strdup (packet->Request.authentication_name);
397
response->Decline.authentication_data.data = authentication_data;
398
response->Decline.authentication_data.length = authentication_data_length;
399
send_packet (socket, address, response);
400
xdmcp_packet_free (response);
404
session = add_session (server);
405
session->priv->address = address4;
406
session->priv->address6 = address6;
407
session->priv->authorization_name = g_strdup (server->priv->authorization_name);
408
session->priv->authorization_data = session_authorization_data;
409
session->priv->authorization_data_length = session_authorization_data_length;
411
response = xdmcp_packet_alloc (XDMCP_Accept);
412
response->Accept.session_id = xdmcp_session_get_id (session);
413
response->Accept.authentication_name = g_strdup (packet->Request.authentication_name);
414
response->Accept.authentication_data.data = authentication_data;
415
response->Accept.authentication_data.length = authentication_data_length;
416
response->Accept.authorization_name = g_strdup (server->priv->authorization_name);
417
response->Accept.authorization_data.data = authorization_data;
418
response->Accept.authorization_data.length = authorization_data_length;
419
send_packet (socket, address, response);
420
xdmcp_packet_free (response);
424
handle_manage (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
426
XDMCPSession *session;
428
session = get_session (server, packet->Manage.session_id);
431
gchar *display_address;
434
/* Ignore duplicate requests */
435
if (session->priv->started)
437
if (session->priv->display_number != packet->Manage.display_number ||
438
strcmp (session->priv->display_class, packet->Manage.display_class) != 0)
439
g_warning ("Duplicate Manage received with different data");
443
session->priv->display_number = packet->Manage.display_number;
444
session->priv->display_class = g_strdup (packet->Manage.display_class);
446
g_signal_emit (server, signals[NEW_SESSION], 0, session, &result);
449
/* Cancel the inactive timer */
450
g_source_remove (session->priv->inactive_timeout);
452
session->priv->started = TRUE;
456
XDMCPPacket *response;
458
response = xdmcp_packet_alloc (XDMCP_Failed);
459
response->Failed.session_id = packet->Manage.session_id;
460
response->Failed.status = g_strdup_printf ("Failed to connect to display %s", display_address);
461
send_packet (socket, address, response);
462
xdmcp_packet_free (response);
465
g_free (display_address);
469
XDMCPPacket *response;
471
response = xdmcp_packet_alloc (XDMCP_Refuse);
472
response->Refuse.session_id = packet->Manage.session_id;
473
send_packet (socket, address, response);
474
xdmcp_packet_free (response);
479
handle_keep_alive (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
481
XDMCPPacket *response;
482
XDMCPSession *session;
483
gboolean alive = FALSE;
485
session = get_session (server, packet->KeepAlive.session_id);
487
alive = TRUE; //xdmcp_session_get_alive (session);
489
response = xdmcp_packet_alloc (XDMCP_Alive);
490
response->Alive.session_running = alive;
491
response->Alive.session_id = alive ? packet->KeepAlive.session_id : 0;
492
send_packet (socket, address, response);
493
xdmcp_packet_free (response);
497
read_cb (GSocket *socket, GIOCondition condition, XDMCPServer *server)
499
GSocketAddress *address;
501
GError *error = NULL;
504
n_read = g_socket_receive_from (socket, &address, data, 1024, NULL, &error);
509
packet = xdmcp_packet_decode ((guchar *)data, n_read);
512
g_debug ("Got %s", xdmcp_packet_tostring (packet));
514
switch (packet->opcode)
516
case XDMCP_BroadcastQuery:
518
case XDMCP_IndirectQuery:
519
handle_query (server, socket, address, packet);
522
handle_request (server, socket, address, packet);
525
handle_manage (server, socket, address, packet);
527
case XDMCP_KeepAlive:
528
handle_keep_alive (server, socket, address, packet);
531
g_warning ("Got unexpected XDMCP packet %d", packet->opcode);
535
xdmcp_packet_free (packet);
539
g_warning ("Failed to read from XDMCP socket: %s", error->message);
541
g_clear_error (&error);
547
open_udp_socket (GSocketFamily family, guint port, GError **error)
550
GSocketAddress *address;
553
socket = g_socket_new (family, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, error);
557
address = g_inet_socket_address_new (g_inet_address_new_any (family), port);
558
result = g_socket_bind (socket, address, TRUE, error);
561
g_object_unref (socket);
569
xdmcp_server_start (XDMCPServer *server)
572
GError *error = NULL;
574
server->priv->socket = open_udp_socket (G_SOCKET_FAMILY_IPV4, server->priv->port, &error);
575
if (server->priv->socket)
577
source = g_socket_create_source (server->priv->socket, G_IO_IN, NULL);
578
g_source_set_callback (source, (GSourceFunc) read_cb, server, NULL);
579
g_source_attach (source, NULL);
582
g_warning ("Failed to create IPv4 XDMCP socket: %s", error->message);
583
g_clear_error (&error);
584
server->priv->socket6 = open_udp_socket (G_SOCKET_FAMILY_IPV6, server->priv->port, &error);
585
if (server->priv->socket6)
587
source = g_socket_create_source (server->priv->socket6, G_IO_IN, NULL);
588
g_source_set_callback (source, (GSourceFunc) read_cb, server, NULL);
589
g_source_attach (source, NULL);
592
g_warning ("Failed to create IPv6 XDMCP socket: %s", error->message);
593
g_clear_error (&error);
595
if (!server->priv->socket && !server->priv->socket6)
602
xdmcp_server_init (XDMCPServer *server)
604
server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, XDMCP_SERVER_TYPE, XDMCPServerPrivate);
606
server->priv->port = XDM_UDP_PORT;
607
server->priv->hostname = g_strdup ("");
608
server->priv->status = g_strdup ("");
609
server->priv->sessions = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
610
server->priv->authentication_name = g_strdup ("");
611
server->priv->authorization_name = g_strdup ("");
615
xdmcp_server_finalize (GObject *object)
619
self = XDMCP_SERVER (object);
621
if (self->priv->socket)
622
g_object_unref (self->priv->socket);
623
if (self->priv->socket6)
624
g_object_unref (self->priv->socket6);
625
g_free (self->priv->hostname);
626
g_free (self->priv->status);
627
g_free (self->priv->authentication_name);
628
g_free (self->priv->authentication_data);
629
g_free (self->priv->authorization_name);
630
g_free (self->priv->authorization_data);
631
g_hash_table_unref (self->priv->sessions);
635
xdmcp_server_class_init (XDMCPServerClass *klass)
637
GObjectClass *object_class = G_OBJECT_CLASS (klass);
639
object_class->finalize = xdmcp_server_finalize;
641
g_type_class_add_private (klass, sizeof (XDMCPServerPrivate));
643
signals[NEW_SESSION] =
644
g_signal_new ("new-session",
645
G_TYPE_FROM_CLASS (klass),
647
G_STRUCT_OFFSET (XDMCPServerClass, new_session),
648
g_signal_accumulator_true_handled,
650
ldm_marshal_BOOLEAN__OBJECT,
651
G_TYPE_BOOLEAN, 1, XDMCP_SESSION_TYPE);