2 * Copyright (C) 2010-2011 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 g_return_if_fail (server != NULL);
75 server->priv->port = port;
79 xdmcp_server_get_port (XDMCPServer *server)
81 g_return_val_if_fail (server != NULL, 0);
82 return server->priv->port;
86 xdmcp_server_set_hostname (XDMCPServer *server, const gchar *hostname)
88 g_return_if_fail (server != NULL);
90 g_free (server->priv->hostname);
91 server->priv->hostname = g_strdup (hostname);
95 xdmcp_server_get_hostname (XDMCPServer *server)
97 g_return_val_if_fail (server != NULL, NULL);
98 return server->priv->hostname;
102 xdmcp_server_set_status (XDMCPServer *server, const gchar *status)
104 g_return_if_fail (server != NULL);
106 g_free (server->priv->status);
107 server->priv->status = g_strdup (status);
111 xdmcp_server_get_status (XDMCPServer *server)
113 g_return_val_if_fail (server != NULL, NULL);
114 return server->priv->status;
118 xdmcp_server_set_authentication (XDMCPServer *server, const gchar *name, const guchar *data, gsize data_length)
120 g_return_if_fail (server != NULL);
122 g_free (server->priv->authentication_name);
123 server->priv->authentication_name = g_strdup (name);
124 g_free (server->priv->authentication_data);
125 server->priv->authentication_data = g_malloc (data_length);
126 server->priv->authentication_data_length = data_length;
127 memcpy (server->priv->authentication_data, data, data_length);
131 xdmcp_server_get_authentication_name (XDMCPServer *server)
133 g_return_val_if_fail (server != NULL, NULL);
134 return server->priv->authentication_name;
138 xdmcp_server_get_authentication_data (XDMCPServer *server)
140 g_return_val_if_fail (server != NULL, NULL);
141 return server->priv->authentication_data;
145 xdmcp_server_get_authentication_data_length (XDMCPServer *server)
147 g_return_val_if_fail (server != NULL, 0);
148 return server->priv->authentication_data_length;
152 xdmcp_server_set_authorization (XDMCPServer *server, const gchar *name, const guchar *data, gsize data_length)
154 g_return_if_fail (server != NULL);
156 g_free (server->priv->authorization_name);
157 server->priv->authorization_name = g_strdup (name);
158 g_free (server->priv->authorization_data);
159 server->priv->authorization_data = g_malloc (data_length);
160 server->priv->authorization_data_length = data_length;
161 memcpy (server->priv->authorization_data, data, data_length);
165 xdmcp_server_get_authorization_name (XDMCPServer *server)
167 g_return_val_if_fail (server != NULL, NULL);
168 return server->priv->authorization_name;
172 xdmcp_server_get_authorization_data (XDMCPServer *server)
174 g_return_val_if_fail (server != NULL, NULL);
175 return server->priv->authorization_data;
179 xdmcp_server_get_authorization_data_length (XDMCPServer *server)
181 g_return_val_if_fail (server != NULL, 0);
182 return server->priv->authorization_data_length;
186 session_timeout_cb (XDMCPSession *session)
188 g_debug ("Timing out unmanaged session %d", session->priv->id);
189 g_hash_table_remove (session->priv->server->priv->sessions, GINT_TO_POINTER ((gint) session->priv->id));
193 static XDMCPSession *
194 add_session (XDMCPServer *server)
196 XDMCPSession *session;
201 id = g_random_int () & 0xFFFFFFFF;
202 } while (g_hash_table_lookup (server->priv->sessions, GINT_TO_POINTER ((gint) id)));
204 session = xdmcp_session_new (id);
205 session->priv->server = server;
206 g_hash_table_insert (server->priv->sessions, GINT_TO_POINTER ((gint) id), g_object_ref (session));
207 session->priv->inactive_timeout = g_timeout_add (10, (GSourceFunc) session_timeout_cb, session);
212 static XDMCPSession *
213 get_session (XDMCPServer *server, guint16 id)
215 return g_hash_table_lookup (server->priv->sessions, GINT_TO_POINTER ((gint) id));
219 send_packet (GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
224 g_debug ("Send %s", xdmcp_packet_tostring (packet));
226 n_written = xdmcp_packet_encode (packet, data, 1024);
228 g_critical ("Failed to encode XDMCP packet");
231 GError *error = NULL;
233 if (g_socket_send_to (socket, address, (gchar *) data, n_written, NULL, &error) < 0)
234 g_warning ("Error sending packet: %s", error->message);
236 g_clear_error (&error);
241 handle_query (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
243 XDMCPPacket *response;
245 gboolean match_authentication = FALSE;
247 /* If no authentication requested and we are configured for none then allow */
248 if (packet->Query.authentication_names[0] == NULL && strcmp (server->priv->authentication_name, "") == 0)
249 match_authentication = TRUE;
251 for (i = packet->Query.authentication_names; *i; i++)
253 if (strcmp (*i, server->priv->authentication_name) == 0)
255 match_authentication = TRUE;
260 if (match_authentication)
262 response = xdmcp_packet_alloc (XDMCP_Willing);
263 response->Willing.authentication_name = g_strdup (server->priv->authentication_name);
264 response->Willing.hostname = g_strdup (server->priv->hostname);
265 response->Willing.status = g_strdup (server->priv->status);
269 response = xdmcp_packet_alloc (XDMCP_Unwilling);
270 response->Unwilling.hostname = g_strdup (server->priv->hostname);
271 if (strcmp (server->priv->authentication_name, "") != 0)
272 response->Unwilling.status = g_strdup_printf ("No matching authentication, server requires %s", server->priv->authentication_name);
274 response->Unwilling.status = g_strdup ("Server does not support authentication");
277 send_packet (socket, address, response);
279 xdmcp_packet_free (response);
283 handle_request (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
286 XDMCPPacket *response;
287 XDMCPSession *session;
288 guchar *authentication_data = NULL;
289 gsize authentication_data_length = 0;
290 gboolean match_authorization = FALSE;
291 guchar *authorization_data = NULL;
292 gsize authorization_data_length = 0;
293 guchar *session_authorization_data = NULL;
294 gsize session_authorization_data_length = 0;
296 GInetAddress *address4 = NULL, *address6 = NULL;
299 for (i = 0; i < packet->Request.n_connections; i++)
301 XDMCPConnection *connection;
303 connection = &packet->Request.connections[i];
304 switch (connection->type)
307 if (connection->address.length == 4)
308 address4 = g_inet_address_new_from_bytes (connection->address.data, G_SOCKET_FAMILY_IPV4);
310 /*case FamilyInternet6:
311 if (connection->address.length == 16)
312 address6 = g_inet_address_new_from_bytes (connection->address.data, G_SOCKET_FAMILY_IPV6);
316 if (!address4) // FIXME: && !address6)
318 response = xdmcp_packet_alloc (XDMCP_Decline);
319 response->Decline.status = g_strdup ("No valid address found");
320 response->Decline.authentication_name = g_strdup (packet->Request.authentication_name);
321 response->Decline.authentication_data.data = authentication_data;
322 response->Decline.authentication_data.length = authentication_data_length;
323 send_packet (socket, address, response);
324 xdmcp_packet_free (response);
328 /* Must be using our authentication scheme */
329 if (strcmp (packet->Request.authentication_name, server->priv->authentication_name) != 0)
331 response = xdmcp_packet_alloc (XDMCP_Decline);
332 if (strcmp (server->priv->authentication_name, "") == 0)
333 response->Decline.status = g_strdup ("Server does not support authentication");
335 response->Decline.status = g_strdup_printf ("Server only supports %s authentication", server->priv->authentication_name);
336 response->Decline.authentication_name = g_strdup ("");
337 send_packet (socket, address, response);
338 xdmcp_packet_free (response);
342 /* Perform requested authentication */
343 if (strcmp (server->priv->authentication_name, "XDM-AUTHENTICATION-1") == 0)
345 guchar input[8], key[8];
347 memset (input, 0, 8);
348 memcpy (input, packet->Request.authentication_data.data, packet->Request.authentication_data.length > 8 ? 8 : packet->Request.authentication_data.length);
352 memcpy (key, server->priv->authentication_data, server->priv->authentication_data_length > 8 ? 8 : server->priv->authentication_data_length);
354 /* Decode message from server */
355 authentication_data = g_malloc (sizeof (guchar) * 8);
356 authentication_data_length = 8;
358 XdmcpUnwrap (input, key, rho.data, authentication_data_length);
359 XdmcpIncrementKey (&rho);
360 XdmcpWrap (rho.data, key, authentication_data, authentication_data_length);
363 /* Check if they support our authorization */
364 for (j = packet->Request.authorization_names; *j; j++)
366 if (strcmp (*j, server->priv->authorization_name) == 0)
368 match_authorization = TRUE;
373 if (!match_authorization)
375 response = xdmcp_packet_alloc (XDMCP_Decline);
376 if (strcmp (server->priv->authorization_name, "") == 0)
377 response->Decline.status = g_strdup ("Server does not support authorization");
379 response->Decline.status = g_strdup_printf ("Server only supports %s authorization", server->priv->authorization_name);
380 response->Decline.authentication_name = g_strdup (packet->Request.authentication_name);
381 response->Decline.authentication_data.data = authentication_data;
382 response->Decline.authentication_data.length = authentication_data_length;
383 send_packet (socket, address, response);
384 xdmcp_packet_free (response);
388 /* Perform requested authorization */
389 if (strcmp (server->priv->authorization_name, "MIT-MAGIC-COOKIE-1") == 0)
391 XAuthorization *auth;
393 /* Data is the cookie */
394 auth = xauth_new_cookie (XAUTH_FAMILY_WILD, "", "");
395 authorization_data = xauth_copy_authorization_data (auth);
396 authorization_data_length = xauth_get_authorization_data_length (auth);
397 session_authorization_data = xauth_copy_authorization_data (auth);
398 session_authorization_data_length = xauth_get_authorization_data_length (auth);
400 g_object_unref (auth);
402 else if (strcmp (server->priv->authorization_name, "XDM-AUTHORIZATION-1") == 0)
405 guchar key[8], session_key[8];
409 memcpy (key, server->priv->authentication_data, server->priv->authentication_data_length > 8 ? 8 : server->priv->authentication_data_length);
411 /* Generate a private session key */
412 // FIXME: Pick a good DES key?
414 for (i = 1; i < 8; i++)
415 session_key[i] = g_random_int () & 0xFF;
417 /* Encrypt the session key and send it to the server */
418 authorization_data = g_malloc (8);
419 authorization_data_length = 8;
420 XdmcpWrap (session_key, key, authorization_data, authorization_data_length);
422 /* Authorization data is the number received from the client followed by the private session key */
423 session_authorization_data = g_malloc (16);
424 session_authorization_data_length = 16;
425 XdmcpDecrementKey (&rho);
426 memcpy (session_authorization_data, rho.data, 8);
427 memcpy (session_authorization_data + 8, session_key, 8);
430 session = add_session (server);
431 session->priv->address = address4;
432 session->priv->address6 = address6;
433 session->priv->authorization_name = g_strdup (server->priv->authorization_name);
434 session->priv->authorization_data = session_authorization_data;
435 session->priv->authorization_data_length = session_authorization_data_length;
437 response = xdmcp_packet_alloc (XDMCP_Accept);
438 response->Accept.session_id = xdmcp_session_get_id (session);
439 response->Accept.authentication_name = g_strdup (packet->Request.authentication_name);
440 response->Accept.authentication_data.data = authentication_data;
441 response->Accept.authentication_data.length = authentication_data_length;
442 response->Accept.authorization_name = g_strdup (server->priv->authorization_name);
443 response->Accept.authorization_data.data = authorization_data;
444 response->Accept.authorization_data.length = authorization_data_length;
445 send_packet (socket, address, response);
446 xdmcp_packet_free (response);
450 handle_manage (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
452 XDMCPSession *session;
454 session = get_session (server, packet->Manage.session_id);
459 /* Ignore duplicate requests */
460 if (session->priv->started)
462 if (session->priv->display_number != packet->Manage.display_number ||
463 strcmp (session->priv->display_class, packet->Manage.display_class) != 0)
464 g_warning ("Duplicate Manage received with different data");
468 session->priv->display_number = packet->Manage.display_number;
469 session->priv->display_class = g_strdup (packet->Manage.display_class);
471 g_signal_emit (server, signals[NEW_SESSION], 0, session, &result);
474 /* Cancel the inactive timer */
475 g_source_remove (session->priv->inactive_timeout);
477 session->priv->started = TRUE;
481 XDMCPPacket *response;
483 response = xdmcp_packet_alloc (XDMCP_Failed);
484 response->Failed.session_id = packet->Manage.session_id;
485 response->Failed.status = g_strdup_printf ("Failed to connect to display :%d", packet->Manage.display_number);
486 send_packet (socket, address, response);
487 xdmcp_packet_free (response);
492 XDMCPPacket *response;
494 response = xdmcp_packet_alloc (XDMCP_Refuse);
495 response->Refuse.session_id = packet->Manage.session_id;
496 send_packet (socket, address, response);
497 xdmcp_packet_free (response);
502 handle_keep_alive (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
504 XDMCPPacket *response;
505 XDMCPSession *session;
506 gboolean alive = FALSE;
508 session = get_session (server, packet->KeepAlive.session_id);
510 alive = TRUE; //xdmcp_session_get_alive (session);
512 response = xdmcp_packet_alloc (XDMCP_Alive);
513 response->Alive.session_running = alive;
514 response->Alive.session_id = alive ? packet->KeepAlive.session_id : 0;
515 send_packet (socket, address, response);
516 xdmcp_packet_free (response);
520 read_cb (GSocket *socket, GIOCondition condition, XDMCPServer *server)
522 GSocketAddress *address;
524 GError *error = NULL;
527 n_read = g_socket_receive_from (socket, &address, data, 1024, NULL, &error);
532 packet = xdmcp_packet_decode ((guchar *)data, n_read);
535 g_debug ("Got %s", xdmcp_packet_tostring (packet));
537 switch (packet->opcode)
539 case XDMCP_BroadcastQuery:
541 case XDMCP_IndirectQuery:
542 handle_query (server, socket, address, packet);
545 handle_request (server, socket, address, packet);
548 handle_manage (server, socket, address, packet);
550 case XDMCP_KeepAlive:
551 handle_keep_alive (server, socket, address, packet);
554 g_warning ("Got unexpected XDMCP packet %d", packet->opcode);
558 xdmcp_packet_free (packet);
562 g_warning ("Failed to read from XDMCP socket: %s", error->message);
564 g_clear_error (&error);
570 open_udp_socket (GSocketFamily family, guint port, GError **error)
573 GSocketAddress *address;
576 socket = g_socket_new (family, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, error);
580 address = g_inet_socket_address_new (g_inet_address_new_any (family), port);
581 result = g_socket_bind (socket, address, TRUE, error);
584 g_object_unref (socket);
592 xdmcp_server_start (XDMCPServer *server)
595 GError *error = NULL;
597 g_return_val_if_fail (server != NULL, FALSE);
599 server->priv->socket = open_udp_socket (G_SOCKET_FAMILY_IPV4, server->priv->port, &error);
600 if (server->priv->socket)
602 source = g_socket_create_source (server->priv->socket, G_IO_IN, NULL);
603 g_source_set_callback (source, (GSourceFunc) read_cb, server, NULL);
604 g_source_attach (source, NULL);
607 g_warning ("Failed to create IPv4 XDMCP socket: %s", error->message);
608 g_clear_error (&error);
609 server->priv->socket6 = open_udp_socket (G_SOCKET_FAMILY_IPV6, server->priv->port, &error);
610 if (server->priv->socket6)
612 source = g_socket_create_source (server->priv->socket6, G_IO_IN, NULL);
613 g_source_set_callback (source, (GSourceFunc) read_cb, server, NULL);
614 g_source_attach (source, NULL);
617 g_warning ("Failed to create IPv6 XDMCP socket: %s", error->message);
618 g_clear_error (&error);
620 if (!server->priv->socket && !server->priv->socket6)
627 xdmcp_server_init (XDMCPServer *server)
629 server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, XDMCP_SERVER_TYPE, XDMCPServerPrivate);
631 server->priv->port = XDM_UDP_PORT;
632 server->priv->hostname = g_strdup ("");
633 server->priv->status = g_strdup ("");
634 server->priv->sessions = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
635 server->priv->authentication_name = g_strdup ("");
636 server->priv->authorization_name = g_strdup ("");
640 xdmcp_server_finalize (GObject *object)
644 self = XDMCP_SERVER (object);
646 if (self->priv->socket)
647 g_object_unref (self->priv->socket);
648 if (self->priv->socket6)
649 g_object_unref (self->priv->socket6);
650 g_free (self->priv->hostname);
651 g_free (self->priv->status);
652 g_free (self->priv->authentication_name);
653 g_free (self->priv->authentication_data);
654 g_free (self->priv->authorization_name);
655 g_free (self->priv->authorization_data);
656 g_hash_table_unref (self->priv->sessions);
658 G_OBJECT_CLASS (xdmcp_server_parent_class)->finalize (object);
662 xdmcp_server_class_init (XDMCPServerClass *klass)
664 GObjectClass *object_class = G_OBJECT_CLASS (klass);
666 object_class->finalize = xdmcp_server_finalize;
668 g_type_class_add_private (klass, sizeof (XDMCPServerPrivate));
670 signals[NEW_SESSION] =
671 g_signal_new ("new-session",
672 G_TYPE_FROM_CLASS (klass),
674 G_STRUCT_OFFSET (XDMCPServerClass, new_session),
675 g_signal_accumulator_true_handled,
677 ldm_marshal_BOOLEAN__OBJECT,
678 G_TYPE_BOOLEAN, 1, XDMCP_SESSION_TYPE);