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 socket_address_to_string (GSocketAddress *address)
173 gchar *inet_text, *text;
175 inet_text = g_inet_address_to_string (g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (address)));
176 text = g_strdup_printf ("%s:%d", inet_text, g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (address)));
183 send_packet (GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
185 gchar *address_string;
189 address_string = socket_address_to_string (address);
190 g_debug ("Send %s to %s", xdmcp_packet_tostring (packet), address_string);
191 g_free (address_string);
193 n_written = xdmcp_packet_encode (packet, data, 1024);
195 g_critical ("Failed to encode XDMCP packet");
198 GError *error = NULL;
200 g_socket_send_to (socket, address, (gchar *) data, n_written, NULL, &error);
202 g_warning ("Error sending packet: %s", error->message);
203 g_clear_error (&error);
208 get_authentication_name (XDMCPServer *server)
210 if (server->priv->key)
211 return "XDM-AUTHENTICATION-1";
217 handle_query (XDMCPServer *server, GSocket *socket, GSocketAddress *address, gchar **authentication_names)
219 XDMCPPacket *response;
221 gchar *authentication_name = NULL;
223 /* If no authentication requested and we are configured for none then allow */
224 if (authentication_names[0] == NULL && server->priv->key == NULL)
225 authentication_name = "";
227 for (i = authentication_names; *i; i++)
229 if (strcmp (*i, get_authentication_name (server)) == 0 && server->priv->key != NULL)
231 authentication_name = *i;
236 if (authentication_name)
238 response = xdmcp_packet_alloc (XDMCP_Willing);
239 response->Willing.authentication_name = g_strdup (authentication_name);
240 response->Willing.hostname = g_strdup (server->priv->hostname);
241 response->Willing.status = g_strdup (server->priv->status);
245 response = xdmcp_packet_alloc (XDMCP_Unwilling);
246 response->Unwilling.hostname = g_strdup (server->priv->hostname);
247 if (server->priv->key)
248 response->Unwilling.status = g_strdup_printf ("No matching authentication, server requires %s", get_authentication_name (server));
250 response->Unwilling.status = g_strdup ("No matching authentication");
253 send_packet (socket, address, response);
255 xdmcp_packet_free (response);
259 handle_forward_query (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
261 GSocketFamily family;
262 GInetAddress *client_inet_address;
263 GSocketAddress *client_address;
267 family = g_socket_get_family (socket);
270 case G_SOCKET_FAMILY_IPV4:
271 if (packet->ForwardQuery.client_address.length != 4)
273 g_warning ("Ignoring IPv4 XDMCP ForwardQuery with client address of length %d", packet->ForwardQuery.client_address.length);
277 case G_SOCKET_FAMILY_IPV6:
278 if (packet->ForwardQuery.client_address.length != 16)
280 g_warning ("Ignoring IPv6 XDMCP ForwardQuery with client address of length %d", packet->ForwardQuery.client_address.length);
285 g_warning ("Unknown socket family %d", family);
289 for (i = 0; i < packet->ForwardQuery.client_port.length; i++)
290 port = port << 8 | packet->ForwardQuery.client_port.data[i];
292 client_inet_address = g_inet_address_new_from_bytes (packet->ForwardQuery.client_address.data, family);
293 client_address = g_inet_socket_address_new (client_inet_address, port);
294 g_object_unref (client_inet_address);
296 handle_query (server, socket, client_address, packet->ForwardQuery.authentication_names);
298 g_object_unref (client_address);
304 if (c >= '0' && c <= '9')
306 if (c >= 'a' && c <= 'f')
308 if (c >= 'A' && c <= 'F')
314 decode_key (const gchar *key, guint8 *data)
319 if (strncmp (key, "0x", 2) == 0 || strncmp (key, "0X", 2) == 0)
321 for (i = 0; i < 8; i++)
323 if (key[i*2] == '\0')
325 data[i] |= atox (key[i*2]) << 8;
326 if (key[i*2+1] == '\0')
328 data[i] |= atox (key[i*2+1]);
333 for (i = 1; i < 8 && key[i-1]; i++)
338 static GInetAddress *
339 connection_to_address (XDMCPConnection *connection)
341 switch (connection->type)
343 case XAUTH_FAMILY_INTERNET:
344 if (connection->address.length == 4)
345 return g_inet_address_new_from_bytes (connection->address.data, G_SOCKET_FAMILY_IPV4);
348 case XAUTH_FAMILY_INTERNET6:
349 if (connection->address.length == 16)
350 return g_inet_address_new_from_bytes (connection->address.data, G_SOCKET_FAMILY_IPV6);
359 find_address (GInetAddress **addresses, gsize length, GSocketFamily family)
363 for (i = 0; i < length; i++)
365 GInetAddress *address = addresses[i];
366 if (address && g_inet_address_get_family (address) == family)
373 static XDMCPConnection *
374 choose_connection (XDMCPPacket *packet, GInetAddress *source_address)
376 GInetAddress **addresses;
377 gsize addresses_length, i;
380 addresses_length = packet->Request.n_connections;
381 if (addresses_length == 0)
384 addresses = malloc (sizeof (GInetAddress *) * addresses_length);
385 for (i = 0; i < addresses_length; i++)
386 addresses[i] = connection_to_address (&packet->Request.connections[i]);
388 /* Use the address the request came in on as this is the least likely to have firewall / routing issues */
389 for (i = 0; i < addresses_length && index < 0; i++)
390 if (g_inet_address_equal (source_address, addresses[i]))
393 /* Otherwise try and find an address that matches the incoming type */
395 index = find_address (addresses, addresses_length, g_inet_address_get_family (source_address));
397 /* Otherwise use the first available */
401 for (i = 0; i < addresses_length; i++)
402 g_object_unref (addresses[i]);
405 return &packet->Request.connections[index];
409 has_string (gchar **list, const gchar *text)
413 for (i = list; *i; i++)
414 if (strcmp (*i, text) == 0)
421 handle_request (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
423 XDMCPPacket *response;
424 XDMCPSession *session;
425 gchar *authentication_name = NULL, *decline_status = NULL, *authorization_name, *display_number;
426 guint8 *authentication_data = NULL, *authorization_data = NULL, *session_authorization_data = NULL;
427 gsize authentication_data_length = 0, authorization_data_length = 0, session_authorization_data_length = 0;
428 XDMCPConnection *connection;
431 /* Check authentication */
432 if (strcmp (packet->Request.authentication_name, "") == 0)
434 if (!server->priv->key)
436 if (!has_string (packet->Request.authorization_names, "MIT-MAGIC-COOKIE-1"))
437 decline_status = g_strdup ("No matching authorization, server requires MIT-MAGIC-COOKIE-1");
440 decline_status = g_strdup ("No matching authentication, server requires XDM-AUTHENTICATION-1");
442 else if (strcmp (packet->Request.authentication_name, "XDM-AUTHENTICATION-1") == 0 && server->priv->key)
444 if (packet->Request.authentication_data.length == 8)
446 guint8 input[8], key[8];
448 memcpy (input, packet->Request.authentication_data.data, packet->Request.authentication_data.length);
451 decode_key (server->priv->key, key);
453 /* Decode message from server */
454 authentication_name = g_strdup ("XDM-AUTHENTICATION-1");
455 authentication_data = g_malloc (sizeof (guint8) * 8);
456 authentication_data_length = 8;
458 XdmcpUnwrap (input, key, rho.data, authentication_data_length);
459 XdmcpIncrementKey (&rho);
460 XdmcpWrap (rho.data, key, authentication_data, authentication_data_length);
462 if (!has_string (packet->Request.authorization_names, "XDM-AUTHORIZATION-1"))
463 decline_status = g_strdup ("No matching authorization, server requires XDM-AUTHORIZATION-1");
466 decline_status = g_strdup ("Invalid XDM-AUTHENTICATION-1 data provided");
470 if (strcmp (packet->Request.authentication_name, "") == 0)
471 decline_status = g_strdup_printf ("No matching authentication, server does not support unauthenticated connections");
472 else if (server->priv->key)
473 decline_status = g_strdup ("No matching authentication, server requires XDM-AUTHENTICATION-1");
475 decline_status = g_strdup ("No matching authentication, server only supports unauthenticated connections");
478 /* Choose an address to connect back on */
479 connection = choose_connection (packet, g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (address)));
480 if (!connection && !decline_status)
481 decline_status = g_strdup ("No valid address found");
483 if (!authentication_name)
484 authentication_name = g_strdup ("");
486 /* Decline if request was not valid */
489 response = xdmcp_packet_alloc (XDMCP_Decline);
490 response->Decline.status = decline_status;
491 response->Decline.authentication_name = authentication_name;
492 response->Decline.authentication_data.data = authentication_data;
493 response->Decline.authentication_data.length = authentication_data_length;
494 send_packet (socket, address, response);
495 xdmcp_packet_free (response);
499 /* Generate authorization data */
500 if (server->priv->key)
503 guint8 key[8], session_key[8];
506 decode_key (server->priv->key, key);
508 /* Generate a private session key */
509 // FIXME: Pick a good DES key?
511 for (i = 1; i < 8; i++)
512 session_key[i] = g_random_int () & 0xFF;
514 /* Encrypt the session key and send it to the server */
515 authorization_data = g_malloc (8);
516 authorization_data_length = 8;
517 XdmcpWrap (session_key, key, authorization_data, authorization_data_length);
519 /* Authorization data is the number received from the client followed by the private session key */
520 authorization_name = g_strdup ("XDM-AUTHORIZATION-1");
521 session_authorization_data = g_malloc (16);
522 session_authorization_data_length = 16;
523 XdmcpDecrementKey (&rho);
524 memcpy (session_authorization_data, rho.data, 8);
525 memcpy (session_authorization_data + 8, session_key, 8);
531 /* Data is the cookie */
532 auth = x_authority_new_cookie (XAUTH_FAMILY_WILD, NULL, 0, "");
533 authorization_data = x_authority_copy_authorization_data (auth);
534 authorization_data_length = x_authority_get_authorization_data_length (auth);
535 authorization_name = g_strdup ("MIT-MAGIC-COOKIE-1");
536 session_authorization_data = x_authority_copy_authorization_data (auth);
537 session_authorization_data_length = x_authority_get_authorization_data_length (auth);
539 g_object_unref (auth);
542 session = add_session (server);
543 session->priv->address = connection_to_address (connection);
544 session->priv->display_number = packet->Request.display_number;
545 display_number = g_strdup_printf ("%d", packet->Request.display_number);
547 /* We need to check if this is the loopback address and set the authority
548 * for a local connection if this is so as XCB treats "127.0.0.1" as local
550 if (g_inet_address_get_is_loopback (session->priv->address))
552 gchar hostname[1024];
553 gethostname (hostname, 1024);
555 session->priv->authority = x_authority_new (XAUTH_FAMILY_LOCAL,
560 session_authorization_data,
561 session_authorization_data_length);
564 session->priv->authority = x_authority_new (connection->type,
565 connection->address.data,
566 connection->address.length,
569 session_authorization_data,
570 session_authorization_data_length);
571 g_free (display_number);
573 response = xdmcp_packet_alloc (XDMCP_Accept);
574 response->Accept.session_id = xdmcp_session_get_id (session);
575 response->Accept.authentication_name = authentication_name;
576 response->Accept.authentication_data.data = authentication_data;
577 response->Accept.authentication_data.length = authentication_data_length;
578 response->Accept.authorization_name = authorization_name;
579 response->Accept.authorization_data.data = authorization_data;
580 response->Accept.authorization_data.length = authorization_data_length;
581 send_packet (socket, address, response);
582 xdmcp_packet_free (response);
586 handle_manage (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
588 XDMCPSession *session;
591 session = get_session (server, packet->Manage.session_id);
594 XDMCPPacket *response;
596 response = xdmcp_packet_alloc (XDMCP_Refuse);
597 response->Refuse.session_id = packet->Manage.session_id;
598 send_packet (socket, address, response);
599 xdmcp_packet_free (response);
604 /* Ignore duplicate requests */
605 if (session->priv->started)
607 if (session->priv->display_number != packet->Manage.display_number ||
608 strcmp (session->priv->display_class, packet->Manage.display_class) != 0)
609 g_debug ("Ignoring duplicate Manage with different data");
613 /* Reject if has changed display number */
614 if (packet->Manage.display_number != session->priv->display_number)
616 XDMCPPacket *response;
618 g_debug ("Received Manage for display number %d, but Request was %d", packet->Manage.display_number, session->priv->display_number);
619 response = xdmcp_packet_alloc (XDMCP_Refuse);
620 response->Refuse.session_id = packet->Manage.session_id;
621 send_packet (socket, address, response);
622 xdmcp_packet_free (response);
625 session->priv->display_class = g_strdup (packet->Manage.display_class);
627 g_signal_emit (server, signals[NEW_SESSION], 0, session, &result);
630 /* Cancel the inactive timer */
631 if (session->priv->inactive_timeout)
632 g_source_remove (session->priv->inactive_timeout);
634 session->priv->started = TRUE;
638 XDMCPPacket *response;
640 response = xdmcp_packet_alloc (XDMCP_Failed);
641 response->Failed.session_id = packet->Manage.session_id;
642 response->Failed.status = g_strdup_printf ("Failed to connect to display :%d", packet->Manage.display_number);
643 send_packet (socket, address, response);
644 xdmcp_packet_free (response);
649 handle_keep_alive (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
651 XDMCPPacket *response;
652 XDMCPSession *session;
653 gboolean alive = FALSE;
655 session = get_session (server, packet->KeepAlive.session_id);
657 alive = TRUE; // FIXME: xdmcp_session_get_alive (session);
659 response = xdmcp_packet_alloc (XDMCP_Alive);
660 response->Alive.session_running = alive;
661 response->Alive.session_id = alive ? packet->KeepAlive.session_id : 0;
662 send_packet (socket, address, response);
663 xdmcp_packet_free (response);
667 read_cb (GSocket *socket, GIOCondition condition, XDMCPServer *server)
669 GSocketAddress *address;
671 GError *error = NULL;
674 n_read = g_socket_receive_from (socket, &address, data, 1024, NULL, &error);
676 g_warning ("Failed to read from XDMCP socket: %s", error->message);
677 g_clear_error (&error);
683 packet = xdmcp_packet_decode ((guint8 *)data, n_read);
686 gchar *packet_string, *address_string;
688 packet_string = xdmcp_packet_tostring (packet);
689 address_string = socket_address_to_string (address);
690 g_debug ("Got %s from %s", packet_string, address_string);
691 g_free (packet_string);
692 g_free (address_string);
694 switch (packet->opcode)
696 case XDMCP_BroadcastQuery:
698 case XDMCP_IndirectQuery:
699 handle_query (server, socket, address, packet->Query.authentication_names);
701 case XDMCP_ForwardQuery:
702 handle_forward_query (server, socket, address, packet);
705 handle_request (server, socket, address, packet);
708 handle_manage (server, socket, address, packet);
710 case XDMCP_KeepAlive:
711 handle_keep_alive (server, socket, address, packet);
714 g_warning ("Got unexpected XDMCP packet %d", packet->opcode);
718 xdmcp_packet_free (packet);
726 open_udp_socket (GSocketFamily family, guint port, const gchar *listen_address, GError **error)
729 GSocketAddress *address;
732 socket = g_socket_new (family, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, error);
740 addresses = g_resolver_lookup_by_name (g_resolver_get_default (), listen_address, NULL, error);
743 g_object_unref (socket);
746 address = g_inet_socket_address_new (addresses->data, port);
747 g_resolver_free_addresses (addresses);
750 address = g_inet_socket_address_new (g_inet_address_new_any (family), port);
751 result = g_socket_bind (socket, address, TRUE, error);
754 g_object_unref (socket);
762 xdmcp_server_start (XDMCPServer *server)
765 GError *error = NULL;
767 g_return_val_if_fail (server != NULL, FALSE);
769 server->priv->socket = open_udp_socket (G_SOCKET_FAMILY_IPV4, server->priv->port, server->priv->listen_address, &error);
771 g_warning ("Failed to create IPv4 XDMCP socket: %s", error->message);
772 g_clear_error (&error);
774 if (server->priv->socket)
776 source = g_socket_create_source (server->priv->socket, G_IO_IN, NULL);
777 g_source_set_callback (source, (GSourceFunc) read_cb, server, NULL);
778 g_source_attach (source, NULL);
781 server->priv->socket6 = open_udp_socket (G_SOCKET_FAMILY_IPV6, server->priv->port, server->priv->listen_address, &error);
783 g_warning ("Failed to create IPv6 XDMCP socket: %s", error->message);
784 g_clear_error (&error);
786 if (server->priv->socket6)
788 source = g_socket_create_source (server->priv->socket6, G_IO_IN, NULL);
789 g_source_set_callback (source, (GSourceFunc) read_cb, server, NULL);
790 g_source_attach (source, NULL);
793 if (!server->priv->socket && !server->priv->socket6)
800 xdmcp_server_init (XDMCPServer *server)
802 server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, XDMCP_SERVER_TYPE, XDMCPServerPrivate);
804 server->priv->port = XDM_UDP_PORT;
805 server->priv->hostname = g_strdup ("");
806 server->priv->status = g_strdup ("");
807 server->priv->sessions = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
811 xdmcp_server_finalize (GObject *object)
813 XDMCPServer *self = XDMCP_SERVER (object);
815 g_clear_object (&self->priv->socket);
816 g_clear_object (&self->priv->socket6);
817 g_free (self->priv->listen_address);
818 g_free (self->priv->hostname);
819 g_free (self->priv->status);
820 g_free (self->priv->key);
821 g_hash_table_unref (self->priv->sessions);
823 G_OBJECT_CLASS (xdmcp_server_parent_class)->finalize (object);
827 xdmcp_server_class_init (XDMCPServerClass *klass)
829 GObjectClass *object_class = G_OBJECT_CLASS (klass);
831 object_class->finalize = xdmcp_server_finalize;
833 g_type_class_add_private (klass, sizeof (XDMCPServerPrivate));
835 signals[NEW_SESSION] =
836 g_signal_new (XDMCP_SERVER_SIGNAL_NEW_SESSION,
837 G_TYPE_FROM_CLASS (klass),
839 G_STRUCT_OFFSET (XDMCPServerClass, new_session),
840 g_signal_accumulator_true_handled,
843 G_TYPE_BOOLEAN, 1, XDMCP_SESSION_TYPE);