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, XDMCPPacket *packet)
204 XDMCPPacket *response;
206 gchar *authentication_name = NULL;
208 /* If no authentication requested and we are configured for none then allow */
209 if (packet->Query.authentication_names[0] == NULL && server->priv->key == NULL)
210 authentication_name = "";
212 for (i = packet->Query.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);
246 if (c >= '0' && c <= '9')
248 if (c >= 'a' && c <= 'f')
250 if (c >= 'A' && c <= 'F')
256 decode_key (const gchar *key, guint8 *data)
261 if (strncmp (key, "0x", 2) == 0 || strncmp (key, "0X", 2) == 0)
263 for (i = 0; i < 8; i++)
265 if (key[i*2] == '\0')
267 data[i] |= atox (key[i*2]) << 8;
268 if (key[i*2+1] == '\0')
270 data[i] |= atox (key[i*2+1]);
275 for (i = 1; i < 8 && key[i-1]; i++)
280 static GInetAddress *
281 connection_to_address (XDMCPConnection *connection)
283 switch (connection->type)
285 case XAUTH_FAMILY_INTERNET:
286 if (connection->address.length == 4)
287 return g_inet_address_new_from_bytes (connection->address.data, G_SOCKET_FAMILY_IPV4);
290 case XAUTH_FAMILY_INTERNET6:
291 if (connection->address.length == 16)
292 return g_inet_address_new_from_bytes (connection->address.data, G_SOCKET_FAMILY_IPV6);
301 find_address (GInetAddress **addresses, gsize length, GSocketFamily family)
305 for (i = 0; i < length; i++)
307 GInetAddress *address = addresses[i];
308 if (address && g_inet_address_get_family (address) == family)
315 static XDMCPConnection *
316 choose_connection (XDMCPPacket *packet, GInetAddress *source_address)
318 GInetAddress **addresses;
319 gsize addresses_length, i;
322 addresses_length = packet->Request.n_connections;
323 addresses = malloc (sizeof (GInetAddress *) * addresses_length);
324 for (i = 0; i < addresses_length; i++)
325 addresses[i] = connection_to_address (&packet->Request.connections[i]);
327 /* Use the address the request came in on as this is the least likely to have firewall / routing issues */
328 for (i = 0; i < addresses_length && index < 0; i++)
329 if (g_inet_address_equal (source_address, addresses[i]))
332 /* Otherwise try and find an address that matches the incoming type */
334 index = find_address (addresses, addresses_length, g_inet_address_get_family (source_address));
336 /* Otherwise use the first available */
337 if (index < 0 && addresses_length > 0)
340 for (i = 0; i < addresses_length; i++)
341 g_object_unref (addresses[i]);
344 return &packet->Request.connections[index];
348 handle_request (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
350 XDMCPPacket *response;
351 XDMCPSession *session;
352 guint8 *authentication_data = NULL;
353 gsize authentication_data_length = 0;
354 gboolean match_authorization = FALSE;
355 gchar *authorization_name;
356 guint8 *authorization_data = NULL;
357 gsize authorization_data_length = 0;
358 guint8 *session_authorization_data = NULL;
359 gsize session_authorization_data_length = 0;
361 XDMCPConnection *connection;
362 gchar *display_number;
365 /* Choose an address to connect back on */
366 connection = choose_connection (packet, g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (address)));
368 /* Decline if haven't got an address we can connect on */
371 response = xdmcp_packet_alloc (XDMCP_Decline);
372 response->Decline.status = g_strdup ("No valid address found");
373 response->Decline.authentication_name = g_strdup (packet->Request.authentication_name);
374 response->Decline.authentication_data.data = authentication_data;
375 response->Decline.authentication_data.length = authentication_data_length;
376 send_packet (socket, address, response);
377 xdmcp_packet_free (response);
381 /* Must be using our authentication scheme */
382 if (strcmp (packet->Request.authentication_name, get_authentication_name (server)) != 0)
384 response = xdmcp_packet_alloc (XDMCP_Decline);
385 if (server->priv->key)
386 response->Decline.status = g_strdup_printf ("Server only supports %s authentication", get_authentication_name (server));
388 response->Decline.status = g_strdup ("Server does not support authentication");
389 response->Decline.authentication_name = g_strdup ("");
390 send_packet (socket, address, response);
391 xdmcp_packet_free (response);
395 /* Perform requested authentication */
396 if (server->priv->key)
398 guint8 input[8], key[8];
400 memset (input, 0, 8);
401 memcpy (input, packet->Request.authentication_data.data, packet->Request.authentication_data.length > 8 ? 8 : packet->Request.authentication_data.length);
404 decode_key (server->priv->key, key);
406 /* Decode message from server */
407 authentication_data = g_malloc (sizeof (guint8) * 8);
408 authentication_data_length = 8;
410 XdmcpUnwrap (input, key, rho.data, authentication_data_length);
411 XdmcpIncrementKey (&rho);
412 XdmcpWrap (rho.data, key, authentication_data, authentication_data_length);
414 authorization_name = g_strdup ("XDM-AUTHORIZATION-1");
417 authorization_name = g_strdup ("MIT-MAGIC-COOKIE-1");
419 /* Check if they support our authorization */
420 for (j = packet->Request.authorization_names; *j; j++)
422 if (strcmp (*j, authorization_name) == 0)
424 match_authorization = TRUE;
429 /* Decline if don't support out authorization */
430 if (!match_authorization)
432 response = xdmcp_packet_alloc (XDMCP_Decline);
433 response->Decline.status = g_strdup_printf ("Server requires %s authorization", authorization_name);
434 g_free (authorization_name);
435 response->Decline.authentication_name = g_strdup (packet->Request.authentication_name);
436 response->Decline.authentication_data.data = authentication_data;
437 response->Decline.authentication_data.length = authentication_data_length;
438 send_packet (socket, address, response);
439 xdmcp_packet_free (response);
443 /* Perform requested authorization */
444 if (server->priv->key)
447 guint8 key[8], session_key[8];
450 decode_key (server->priv->key, key);
452 /* Generate a private session key */
453 // FIXME: Pick a good DES key?
455 for (i = 1; i < 8; i++)
456 session_key[i] = g_random_int () & 0xFF;
458 /* Encrypt the session key and send it to the server */
459 authorization_data = g_malloc (8);
460 authorization_data_length = 8;
461 XdmcpWrap (session_key, key, authorization_data, authorization_data_length);
463 /* Authorization data is the number received from the client followed by the private session key */
464 session_authorization_data = g_malloc (16);
465 session_authorization_data_length = 16;
466 XdmcpDecrementKey (&rho);
467 memcpy (session_authorization_data, rho.data, 8);
468 memcpy (session_authorization_data + 8, session_key, 8);
474 /* Data is the cookie */
475 auth = x_authority_new_cookie (XAUTH_FAMILY_WILD, NULL, 0, "");
476 authorization_data = x_authority_copy_authorization_data (auth);
477 authorization_data_length = x_authority_get_authorization_data_length (auth);
478 session_authorization_data = x_authority_copy_authorization_data (auth);
479 session_authorization_data_length = x_authority_get_authorization_data_length (auth);
481 g_object_unref (auth);
484 session = add_session (server);
485 session->priv->address = connection_to_address (connection);
486 session->priv->display_number = packet->Request.display_number;
487 display_number = g_strdup_printf ("%d", packet->Request.display_number);
489 /* We need to check if this is the loopback address and set the authority
490 * for a local connection if this is so as XCB treats "127.0.0.1" as local
492 if (g_inet_address_get_is_loopback (session->priv->address))
494 gchar hostname[1024];
495 gethostname (hostname, 1024);
497 session->priv->authority = x_authority_new (XAUTH_FAMILY_LOCAL,
502 session_authorization_data,
503 session_authorization_data_length);
506 session->priv->authority = x_authority_new (connection->type,
507 connection->address.data,
508 connection->address.length,
511 session_authorization_data,
512 session_authorization_data_length);
513 g_free (display_number);
515 response = xdmcp_packet_alloc (XDMCP_Accept);
516 response->Accept.session_id = xdmcp_session_get_id (session);
517 response->Accept.authentication_name = g_strdup (packet->Request.authentication_name);
518 response->Accept.authentication_data.data = authentication_data;
519 response->Accept.authentication_data.length = authentication_data_length;
520 response->Accept.authorization_name = authorization_name;
521 response->Accept.authorization_data.data = authorization_data;
522 response->Accept.authorization_data.length = authorization_data_length;
523 send_packet (socket, address, response);
524 xdmcp_packet_free (response);
528 handle_manage (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
530 XDMCPSession *session;
533 session = get_session (server, packet->Manage.session_id);
536 XDMCPPacket *response;
538 response = xdmcp_packet_alloc (XDMCP_Refuse);
539 response->Refuse.session_id = packet->Manage.session_id;
540 send_packet (socket, address, response);
541 xdmcp_packet_free (response);
546 /* Ignore duplicate requests */
547 if (session->priv->started)
549 if (session->priv->display_number != packet->Manage.display_number ||
550 strcmp (session->priv->display_class, packet->Manage.display_class) != 0)
551 g_debug ("Ignoring duplicate Manage with different data");
555 /* Reject if has changed display number */
556 if (packet->Manage.display_number != session->priv->display_number)
558 XDMCPPacket *response;
560 g_debug ("Received Manage for display number %d, but Request was %d", packet->Manage.display_number, session->priv->display_number);
561 response = xdmcp_packet_alloc (XDMCP_Refuse);
562 response->Refuse.session_id = packet->Manage.session_id;
563 send_packet (socket, address, response);
564 xdmcp_packet_free (response);
567 session->priv->display_class = g_strdup (packet->Manage.display_class);
569 g_signal_emit (server, signals[NEW_SESSION], 0, session, &result);
572 /* Cancel the inactive timer */
573 if (session->priv->inactive_timeout)
574 g_source_remove (session->priv->inactive_timeout);
576 session->priv->started = TRUE;
580 XDMCPPacket *response;
582 response = xdmcp_packet_alloc (XDMCP_Failed);
583 response->Failed.session_id = packet->Manage.session_id;
584 response->Failed.status = g_strdup_printf ("Failed to connect to display :%d", packet->Manage.display_number);
585 send_packet (socket, address, response);
586 xdmcp_packet_free (response);
591 handle_keep_alive (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
593 XDMCPPacket *response;
594 XDMCPSession *session;
595 gboolean alive = FALSE;
597 session = get_session (server, packet->KeepAlive.session_id);
599 alive = TRUE; // FIXME: xdmcp_session_get_alive (session);
601 response = xdmcp_packet_alloc (XDMCP_Alive);
602 response->Alive.session_running = alive;
603 response->Alive.session_id = alive ? packet->KeepAlive.session_id : 0;
604 send_packet (socket, address, response);
605 xdmcp_packet_free (response);
609 read_cb (GSocket *socket, GIOCondition condition, XDMCPServer *server)
611 GSocketAddress *address;
613 GError *error = NULL;
616 n_read = g_socket_receive_from (socket, &address, data, 1024, NULL, &error);
618 g_warning ("Failed to read from XDMCP socket: %s", error->message);
619 g_clear_error (&error);
625 packet = xdmcp_packet_decode ((guint8 *)data, n_read);
628 g_debug ("Got %s", xdmcp_packet_tostring (packet));
630 switch (packet->opcode)
632 case XDMCP_BroadcastQuery:
634 case XDMCP_IndirectQuery:
635 handle_query (server, socket, address, packet);
638 handle_request (server, socket, address, packet);
641 handle_manage (server, socket, address, packet);
643 case XDMCP_KeepAlive:
644 handle_keep_alive (server, socket, address, packet);
647 g_warning ("Got unexpected XDMCP packet %d", packet->opcode);
651 xdmcp_packet_free (packet);
659 open_udp_socket (GSocketFamily family, guint port, const gchar *listen_address, GError **error)
662 GSocketAddress *address;
665 socket = g_socket_new (family, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, error);
673 addresses = g_resolver_lookup_by_name (g_resolver_get_default (), listen_address, NULL, error);
676 g_object_unref (socket);
679 address = g_inet_socket_address_new (addresses->data, port);
680 g_resolver_free_addresses (addresses);
683 address = g_inet_socket_address_new (g_inet_address_new_any (family), port);
684 result = g_socket_bind (socket, address, TRUE, error);
687 g_object_unref (socket);
695 xdmcp_server_start (XDMCPServer *server)
698 GError *error = NULL;
700 g_return_val_if_fail (server != NULL, FALSE);
702 server->priv->socket = open_udp_socket (G_SOCKET_FAMILY_IPV4, server->priv->port, server->priv->listen_address, &error);
704 g_warning ("Failed to create IPv4 XDMCP socket: %s", error->message);
705 g_clear_error (&error);
707 if (server->priv->socket)
709 source = g_socket_create_source (server->priv->socket, G_IO_IN, NULL);
710 g_source_set_callback (source, (GSourceFunc) read_cb, server, NULL);
711 g_source_attach (source, NULL);
714 server->priv->socket6 = open_udp_socket (G_SOCKET_FAMILY_IPV6, server->priv->port, server->priv->listen_address, &error);
716 g_warning ("Failed to create IPv6 XDMCP socket: %s", error->message);
717 g_clear_error (&error);
719 if (server->priv->socket6)
721 source = g_socket_create_source (server->priv->socket6, G_IO_IN, NULL);
722 g_source_set_callback (source, (GSourceFunc) read_cb, server, NULL);
723 g_source_attach (source, NULL);
726 if (!server->priv->socket && !server->priv->socket6)
733 xdmcp_server_init (XDMCPServer *server)
735 server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, XDMCP_SERVER_TYPE, XDMCPServerPrivate);
737 server->priv->port = XDM_UDP_PORT;
738 server->priv->hostname = g_strdup ("");
739 server->priv->status = g_strdup ("");
740 server->priv->sessions = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
744 xdmcp_server_finalize (GObject *object)
746 XDMCPServer *self = XDMCP_SERVER (object);
748 g_clear_object (&self->priv->socket);
749 g_clear_object (&self->priv->socket6);
750 g_free (self->priv->listen_address);
751 g_free (self->priv->hostname);
752 g_free (self->priv->status);
753 g_free (self->priv->key);
754 g_hash_table_unref (self->priv->sessions);
756 G_OBJECT_CLASS (xdmcp_server_parent_class)->finalize (object);
760 xdmcp_server_class_init (XDMCPServerClass *klass)
762 GObjectClass *object_class = G_OBJECT_CLASS (klass);
764 object_class->finalize = xdmcp_server_finalize;
766 g_type_class_add_private (klass, sizeof (XDMCPServerPrivate));
768 signals[NEW_SESSION] =
769 g_signal_new (XDMCP_SERVER_SIGNAL_NEW_SESSION,
770 G_TYPE_FROM_CLASS (klass),
772 G_STRUCT_OFFSET (XDMCPServerClass, new_session),
773 g_signal_accumulator_true_handled,
776 G_TYPE_BOOLEAN, 1, XDMCP_SESSION_TYPE);