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>
19 #include "xdmcp-server.h"
20 #include "xdmcp-protocol.h"
21 #include "xdmcp-session-private.h"
22 #include "x-authority.h"
28 static guint signals[LAST_SIGNAL] = { 0 };
30 struct XDMCPServerPrivate
32 /* Port to listen on */
35 /* Address to listen on */
36 gchar *listen_address;
38 /* Listening sockets */
39 GSocket *socket, *socket6;
41 /* Hostname to report to client */
44 /* Status to report to clients */
47 /* XDM-AUTHENTICATION-1 key */
50 /* Active XDMCP sessions */
54 G_DEFINE_TYPE (XDMCPServer, xdmcp_server, G_TYPE_OBJECT);
56 /* Maximum number of milliseconds client will resend manage requests before giving up */
57 #define MANAGE_TIMEOUT 126000
60 xdmcp_server_new (void)
62 return g_object_new (XDMCP_SERVER_TYPE, NULL);
66 xdmcp_server_set_port (XDMCPServer *server, guint port)
68 g_return_if_fail (server != NULL);
69 server->priv->port = port;
73 xdmcp_server_get_port (XDMCPServer *server)
75 g_return_val_if_fail (server != NULL, 0);
76 return server->priv->port;
80 xdmcp_server_set_listen_address (XDMCPServer *server, const gchar *listen_address)
82 g_return_if_fail (server != NULL);
84 g_free (server->priv->listen_address);
85 server->priv->listen_address = g_strdup (listen_address);
89 xdmcp_server_get_listen_address (XDMCPServer *server)
91 g_return_val_if_fail (server != NULL, NULL);
92 return server->priv->listen_address;
96 xdmcp_server_set_hostname (XDMCPServer *server, const gchar *hostname)
98 g_return_if_fail (server != NULL);
100 g_free (server->priv->hostname);
101 server->priv->hostname = g_strdup (hostname);
105 xdmcp_server_get_hostname (XDMCPServer *server)
107 g_return_val_if_fail (server != NULL, NULL);
108 return server->priv->hostname;
112 xdmcp_server_set_status (XDMCPServer *server, const gchar *status)
114 g_return_if_fail (server != NULL);
116 g_free (server->priv->status);
117 server->priv->status = g_strdup (status);
121 xdmcp_server_get_status (XDMCPServer *server)
123 g_return_val_if_fail (server != NULL, NULL);
124 return server->priv->status;
128 xdmcp_server_set_key (XDMCPServer *server, const gchar *key)
130 g_return_if_fail (server != NULL);
131 g_free (server->priv->key);
132 server->priv->key = g_strdup (key);
136 session_timeout_cb (XDMCPSession *session)
138 session->priv->inactive_timeout = 0;
140 g_debug ("Timing out unmanaged session %d", session->priv->id);
141 g_hash_table_remove (session->priv->server->priv->sessions, GINT_TO_POINTER ((gint) session->priv->id));
145 static XDMCPSession *
146 add_session (XDMCPServer *server)
148 XDMCPSession *session;
153 id = g_random_int () & 0xFFFFFFFF;
154 } while (g_hash_table_lookup (server->priv->sessions, GINT_TO_POINTER ((gint) id)));
156 session = xdmcp_session_new (id);
157 session->priv->server = server;
158 g_hash_table_insert (server->priv->sessions, GINT_TO_POINTER ((gint) id), g_object_ref (session));
159 session->priv->inactive_timeout = g_timeout_add (MANAGE_TIMEOUT, (GSourceFunc) session_timeout_cb, session);
164 static XDMCPSession *
165 get_session (XDMCPServer *server, guint16 id)
167 return g_hash_table_lookup (server->priv->sessions, GINT_TO_POINTER ((gint) id));
171 send_packet (GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
176 g_debug ("Send %s", xdmcp_packet_tostring (packet));
178 n_written = xdmcp_packet_encode (packet, data, 1024);
180 g_critical ("Failed to encode XDMCP packet");
183 GError *error = NULL;
185 g_socket_send_to (socket, address, (gchar *) data, n_written, NULL, &error);
187 g_warning ("Error sending packet: %s", error->message);
188 g_clear_error (&error);
193 get_authentication_name (XDMCPServer *server)
195 if (server->priv->key)
196 return "XDM-AUTHENTICATION-1";
202 handle_query (XDMCPServer *server, GSocket *socket, GSocketAddress *address, gchar **authentication_names)
204 XDMCPPacket *response;
206 gchar *authentication_name = NULL;
208 /* If no authentication requested and we are configured for none then allow */
209 if (authentication_names[0] == NULL && server->priv->key == NULL)
210 authentication_name = "";
212 for (i = authentication_names; *i; i++)
214 if (strcmp (*i, get_authentication_name (server)) == 0 && server->priv->key != NULL)
216 authentication_name = *i;
221 if (authentication_name)
223 response = xdmcp_packet_alloc (XDMCP_Willing);
224 response->Willing.authentication_name = g_strdup (authentication_name);
225 response->Willing.hostname = g_strdup (server->priv->hostname);
226 response->Willing.status = g_strdup (server->priv->status);
230 response = xdmcp_packet_alloc (XDMCP_Unwilling);
231 response->Unwilling.hostname = g_strdup (server->priv->hostname);
232 if (server->priv->key)
233 response->Unwilling.status = g_strdup_printf ("No matching authentication, server requires %s", get_authentication_name (server));
235 response->Unwilling.status = g_strdup ("Server does not support authentication");
238 send_packet (socket, address, response);
240 xdmcp_packet_free (response);
244 handle_forward_query (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
246 GSocketFamily family;
247 GInetAddress *client_inet_address;
248 GSocketAddress *client_address;
252 family = g_socket_get_family (socket);
255 case G_SOCKET_FAMILY_IPV4:
256 if (packet->ForwardQuery.client_address.length != 4)
258 g_warning ("Ignoring IPv4 XDMCP ForwardQuery with client address of length %d", packet->ForwardQuery.client_address.length);
262 case G_SOCKET_FAMILY_IPV6:
263 if (packet->ForwardQuery.client_address.length != 16)
265 g_warning ("Ignoring IPv6 XDMCP ForwardQuery with client address of length %d", packet->ForwardQuery.client_address.length);
270 g_warning ("Unknown socket family %d", family);
274 for (i = 0; i < packet->ForwardQuery.client_port.length; i++)
275 port = port << 8 | packet->ForwardQuery.client_port.data[i];
277 client_inet_address = g_inet_address_new_from_bytes (packet->ForwardQuery.client_address.data, family);
278 client_address = g_inet_socket_address_new (client_inet_address, port);
279 g_object_unref (client_inet_address);
281 handle_query (server, socket, client_address, packet->ForwardQuery.authentication_names);
283 g_object_unref (client_address);
289 if (c >= '0' && c <= '9')
291 if (c >= 'a' && c <= 'f')
293 if (c >= 'A' && c <= 'F')
299 decode_key (const gchar *key, guint8 *data)
304 if (strncmp (key, "0x", 2) == 0 || strncmp (key, "0X", 2) == 0)
306 for (i = 0; i < 8; i++)
308 if (key[i*2] == '\0')
310 data[i] |= atox (key[i*2]) << 8;
311 if (key[i*2+1] == '\0')
313 data[i] |= atox (key[i*2+1]);
318 for (i = 1; i < 8 && key[i-1]; i++)
323 static GInetAddress *
324 connection_to_address (XDMCPConnection *connection)
326 switch (connection->type)
328 case XAUTH_FAMILY_INTERNET:
329 if (connection->address.length == 4)
330 return g_inet_address_new_from_bytes (connection->address.data, G_SOCKET_FAMILY_IPV4);
333 case XAUTH_FAMILY_INTERNET6:
334 if (connection->address.length == 16)
335 return g_inet_address_new_from_bytes (connection->address.data, G_SOCKET_FAMILY_IPV6);
344 find_address (GInetAddress **addresses, gsize length, GSocketFamily family)
348 for (i = 0; i < length; i++)
350 GInetAddress *address = addresses[i];
351 if (address && g_inet_address_get_family (address) == family)
358 static XDMCPConnection *
359 choose_connection (XDMCPPacket *packet, GInetAddress *source_address)
361 GInetAddress **addresses;
362 gsize addresses_length, i;
365 addresses_length = packet->Request.n_connections;
366 addresses = malloc (sizeof (GInetAddress *) * addresses_length);
367 for (i = 0; i < addresses_length; i++)
368 addresses[i] = connection_to_address (&packet->Request.connections[i]);
370 /* Use the address the request came in on as this is the least likely to have firewall / routing issues */
371 for (i = 0; i < addresses_length && index < 0; i++)
372 if (g_inet_address_equal (source_address, addresses[i]))
375 /* Otherwise try and find an address that matches the incoming type */
377 index = find_address (addresses, addresses_length, g_inet_address_get_family (source_address));
379 /* Otherwise use the first available */
380 if (index < 0 && addresses_length > 0)
383 for (i = 0; i < addresses_length; i++)
384 g_object_unref (addresses[i]);
387 return &packet->Request.connections[index];
391 handle_request (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
393 XDMCPPacket *response;
394 XDMCPSession *session;
395 guint8 *authentication_data = NULL;
396 gsize authentication_data_length = 0;
397 gboolean match_authorization = FALSE;
398 gchar *authorization_name;
399 guint8 *authorization_data = NULL;
400 gsize authorization_data_length = 0;
401 guint8 *session_authorization_data = NULL;
402 gsize session_authorization_data_length = 0;
404 XDMCPConnection *connection;
405 gchar *display_number;
408 /* Choose an address to connect back on */
409 connection = choose_connection (packet, g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (address)));
411 /* Decline if haven't got an address we can connect on */
414 response = xdmcp_packet_alloc (XDMCP_Decline);
415 response->Decline.status = g_strdup ("No valid address found");
416 response->Decline.authentication_name = g_strdup (packet->Request.authentication_name);
417 response->Decline.authentication_data.data = authentication_data;
418 response->Decline.authentication_data.length = authentication_data_length;
419 send_packet (socket, address, response);
420 xdmcp_packet_free (response);
424 /* Must be using our authentication scheme */
425 if (strcmp (packet->Request.authentication_name, get_authentication_name (server)) != 0)
427 response = xdmcp_packet_alloc (XDMCP_Decline);
428 if (server->priv->key)
429 response->Decline.status = g_strdup_printf ("Server only supports %s authentication", get_authentication_name (server));
431 response->Decline.status = g_strdup ("Server does not support authentication");
432 response->Decline.authentication_name = g_strdup ("");
433 send_packet (socket, address, response);
434 xdmcp_packet_free (response);
438 /* Perform requested authentication */
439 if (server->priv->key)
441 guint8 input[8], key[8];
443 memset (input, 0, 8);
444 memcpy (input, packet->Request.authentication_data.data, packet->Request.authentication_data.length > 8 ? 8 : packet->Request.authentication_data.length);
447 decode_key (server->priv->key, key);
449 /* Decode message from server */
450 authentication_data = g_malloc (sizeof (guint8) * 8);
451 authentication_data_length = 8;
453 XdmcpUnwrap (input, key, rho.data, authentication_data_length);
454 XdmcpIncrementKey (&rho);
455 XdmcpWrap (rho.data, key, authentication_data, authentication_data_length);
457 authorization_name = g_strdup ("XDM-AUTHORIZATION-1");
460 authorization_name = g_strdup ("MIT-MAGIC-COOKIE-1");
462 /* Check if they support our authorization */
463 for (j = packet->Request.authorization_names; *j; j++)
465 if (strcmp (*j, authorization_name) == 0)
467 match_authorization = TRUE;
472 /* Decline if don't support out authorization */
473 if (!match_authorization)
475 response = xdmcp_packet_alloc (XDMCP_Decline);
476 response->Decline.status = g_strdup_printf ("Server requires %s authorization", authorization_name);
477 g_free (authorization_name);
478 response->Decline.authentication_name = g_strdup (packet->Request.authentication_name);
479 response->Decline.authentication_data.data = authentication_data;
480 response->Decline.authentication_data.length = authentication_data_length;
481 send_packet (socket, address, response);
482 xdmcp_packet_free (response);
486 /* Perform requested authorization */
487 if (server->priv->key)
490 guint8 key[8], session_key[8];
493 decode_key (server->priv->key, key);
495 /* Generate a private session key */
496 // FIXME: Pick a good DES key?
498 for (i = 1; i < 8; i++)
499 session_key[i] = g_random_int () & 0xFF;
501 /* Encrypt the session key and send it to the server */
502 authorization_data = g_malloc (8);
503 authorization_data_length = 8;
504 XdmcpWrap (session_key, key, authorization_data, authorization_data_length);
506 /* Authorization data is the number received from the client followed by the private session key */
507 session_authorization_data = g_malloc (16);
508 session_authorization_data_length = 16;
509 XdmcpDecrementKey (&rho);
510 memcpy (session_authorization_data, rho.data, 8);
511 memcpy (session_authorization_data + 8, session_key, 8);
517 /* Data is the cookie */
518 auth = x_authority_new_cookie (XAUTH_FAMILY_WILD, NULL, 0, "");
519 authorization_data = x_authority_copy_authorization_data (auth);
520 authorization_data_length = x_authority_get_authorization_data_length (auth);
521 session_authorization_data = x_authority_copy_authorization_data (auth);
522 session_authorization_data_length = x_authority_get_authorization_data_length (auth);
524 g_object_unref (auth);
527 session = add_session (server);
528 session->priv->address = connection_to_address (connection);
529 session->priv->display_number = packet->Request.display_number;
530 display_number = g_strdup_printf ("%d", packet->Request.display_number);
532 /* We need to check if this is the loopback address and set the authority
533 * for a local connection if this is so as XCB treats "127.0.0.1" as local
535 if (g_inet_address_get_is_loopback (session->priv->address))
537 gchar hostname[1024];
538 gethostname (hostname, 1024);
540 session->priv->authority = x_authority_new (XAUTH_FAMILY_LOCAL,
545 session_authorization_data,
546 session_authorization_data_length);
549 session->priv->authority = x_authority_new (connection->type,
550 connection->address.data,
551 connection->address.length,
554 session_authorization_data,
555 session_authorization_data_length);
556 g_free (display_number);
558 response = xdmcp_packet_alloc (XDMCP_Accept);
559 response->Accept.session_id = xdmcp_session_get_id (session);
560 response->Accept.authentication_name = g_strdup (packet->Request.authentication_name);
561 response->Accept.authentication_data.data = authentication_data;
562 response->Accept.authentication_data.length = authentication_data_length;
563 response->Accept.authorization_name = authorization_name;
564 response->Accept.authorization_data.data = authorization_data;
565 response->Accept.authorization_data.length = authorization_data_length;
566 send_packet (socket, address, response);
567 xdmcp_packet_free (response);
571 handle_manage (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
573 XDMCPSession *session;
576 session = get_session (server, packet->Manage.session_id);
579 XDMCPPacket *response;
581 response = xdmcp_packet_alloc (XDMCP_Refuse);
582 response->Refuse.session_id = packet->Manage.session_id;
583 send_packet (socket, address, response);
584 xdmcp_packet_free (response);
589 /* Ignore duplicate requests */
590 if (session->priv->started)
592 if (session->priv->display_number != packet->Manage.display_number ||
593 strcmp (session->priv->display_class, packet->Manage.display_class) != 0)
594 g_debug ("Ignoring duplicate Manage with different data");
598 /* Reject if has changed display number */
599 if (packet->Manage.display_number != session->priv->display_number)
601 XDMCPPacket *response;
603 g_debug ("Received Manage for display number %d, but Request was %d", packet->Manage.display_number, session->priv->display_number);
604 response = xdmcp_packet_alloc (XDMCP_Refuse);
605 response->Refuse.session_id = packet->Manage.session_id;
606 send_packet (socket, address, response);
607 xdmcp_packet_free (response);
610 session->priv->display_class = g_strdup (packet->Manage.display_class);
612 g_signal_emit (server, signals[NEW_SESSION], 0, session, &result);
615 /* Cancel the inactive timer */
616 if (session->priv->inactive_timeout)
617 g_source_remove (session->priv->inactive_timeout);
619 session->priv->started = TRUE;
623 XDMCPPacket *response;
625 response = xdmcp_packet_alloc (XDMCP_Failed);
626 response->Failed.session_id = packet->Manage.session_id;
627 response->Failed.status = g_strdup_printf ("Failed to connect to display :%d", packet->Manage.display_number);
628 send_packet (socket, address, response);
629 xdmcp_packet_free (response);
634 handle_keep_alive (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
636 XDMCPPacket *response;
637 XDMCPSession *session;
638 gboolean alive = FALSE;
640 session = get_session (server, packet->KeepAlive.session_id);
642 alive = TRUE; // FIXME: xdmcp_session_get_alive (session);
644 response = xdmcp_packet_alloc (XDMCP_Alive);
645 response->Alive.session_running = alive;
646 response->Alive.session_id = alive ? packet->KeepAlive.session_id : 0;
647 send_packet (socket, address, response);
648 xdmcp_packet_free (response);
652 read_cb (GSocket *socket, GIOCondition condition, XDMCPServer *server)
654 GSocketAddress *address;
656 GError *error = NULL;
659 n_read = g_socket_receive_from (socket, &address, data, 1024, NULL, &error);
661 g_warning ("Failed to read from XDMCP socket: %s", error->message);
662 g_clear_error (&error);
668 packet = xdmcp_packet_decode ((guint8 *)data, n_read);
671 gchar *packet_string;
673 packet_string = xdmcp_packet_tostring (packet);
674 g_debug ("Got %s", packet_string);
675 g_free (packet_string);
677 switch (packet->opcode)
679 case XDMCP_BroadcastQuery:
681 case XDMCP_IndirectQuery:
682 handle_query (server, socket, address, packet->Query.authentication_names);
684 case XDMCP_ForwardQuery:
685 handle_forward_query (server, socket, address, packet);
687 handle_request (server, socket, address, packet);
690 handle_manage (server, socket, address, packet);
692 case XDMCP_KeepAlive:
693 handle_keep_alive (server, socket, address, packet);
696 g_warning ("Got unexpected XDMCP packet %d", packet->opcode);
700 xdmcp_packet_free (packet);
708 open_udp_socket (GSocketFamily family, guint port, const gchar *listen_address, GError **error)
711 GSocketAddress *address;
714 socket = g_socket_new (family, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, error);
722 addresses = g_resolver_lookup_by_name (g_resolver_get_default (), listen_address, NULL, error);
725 g_object_unref (socket);
728 address = g_inet_socket_address_new (addresses->data, port);
729 g_resolver_free_addresses (addresses);
732 address = g_inet_socket_address_new (g_inet_address_new_any (family), port);
733 result = g_socket_bind (socket, address, TRUE, error);
736 g_object_unref (socket);
744 xdmcp_server_start (XDMCPServer *server)
747 GError *error = NULL;
749 g_return_val_if_fail (server != NULL, FALSE);
751 server->priv->socket = open_udp_socket (G_SOCKET_FAMILY_IPV4, server->priv->port, server->priv->listen_address, &error);
753 g_warning ("Failed to create IPv4 XDMCP socket: %s", error->message);
754 g_clear_error (&error);
756 if (server->priv->socket)
758 source = g_socket_create_source (server->priv->socket, G_IO_IN, NULL);
759 g_source_set_callback (source, (GSourceFunc) read_cb, server, NULL);
760 g_source_attach (source, NULL);
763 server->priv->socket6 = open_udp_socket (G_SOCKET_FAMILY_IPV6, server->priv->port, server->priv->listen_address, &error);
765 g_warning ("Failed to create IPv6 XDMCP socket: %s", error->message);
766 g_clear_error (&error);
768 if (server->priv->socket6)
770 source = g_socket_create_source (server->priv->socket6, G_IO_IN, NULL);
771 g_source_set_callback (source, (GSourceFunc) read_cb, server, NULL);
772 g_source_attach (source, NULL);
775 if (!server->priv->socket && !server->priv->socket6)
782 xdmcp_server_init (XDMCPServer *server)
784 server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, XDMCP_SERVER_TYPE, XDMCPServerPrivate);
786 server->priv->port = XDM_UDP_PORT;
787 server->priv->hostname = g_strdup ("");
788 server->priv->status = g_strdup ("");
789 server->priv->sessions = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
793 xdmcp_server_finalize (GObject *object)
795 XDMCPServer *self = XDMCP_SERVER (object);
797 g_clear_object (&self->priv->socket);
798 g_clear_object (&self->priv->socket6);
799 g_free (self->priv->listen_address);
800 g_free (self->priv->hostname);
801 g_free (self->priv->status);
802 g_free (self->priv->key);
803 g_hash_table_unref (self->priv->sessions);
805 G_OBJECT_CLASS (xdmcp_server_parent_class)->finalize (object);
809 xdmcp_server_class_init (XDMCPServerClass *klass)
811 GObjectClass *object_class = G_OBJECT_CLASS (klass);
813 object_class->finalize = xdmcp_server_finalize;
815 g_type_class_add_private (klass, sizeof (XDMCPServerPrivate));
817 signals[NEW_SESSION] =
818 g_signal_new (XDMCP_SERVER_SIGNAL_NEW_SESSION,
819 G_TYPE_FROM_CLASS (klass),
821 G_STRUCT_OFFSET (XDMCPServerClass, new_session),
822 g_signal_accumulator_true_handled,
825 G_TYPE_BOOLEAN, 1, XDMCP_SESSION_TYPE);