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"
23 #include "xauthority.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 /* XDM-AUTHENTICATION-1 key */
48 /* Active XDMCP sessions */
52 G_DEFINE_TYPE (XDMCPServer, xdmcp_server, G_TYPE_OBJECT);
54 /* Maximum number of milliseconds client will resend manage requests before giving up */
55 #define MANAGE_TIMEOUT 126000
58 xdmcp_server_new (void)
60 return g_object_new (XDMCP_SERVER_TYPE, NULL);
64 xdmcp_server_set_port (XDMCPServer *server, guint port)
66 g_return_if_fail (server != NULL);
67 server->priv->port = port;
71 xdmcp_server_get_port (XDMCPServer *server)
73 g_return_val_if_fail (server != NULL, 0);
74 return server->priv->port;
78 xdmcp_server_set_hostname (XDMCPServer *server, const gchar *hostname)
80 g_return_if_fail (server != NULL);
82 g_free (server->priv->hostname);
83 server->priv->hostname = g_strdup (hostname);
87 xdmcp_server_get_hostname (XDMCPServer *server)
89 g_return_val_if_fail (server != NULL, NULL);
90 return server->priv->hostname;
94 xdmcp_server_set_status (XDMCPServer *server, const gchar *status)
96 g_return_if_fail (server != NULL);
98 g_free (server->priv->status);
99 server->priv->status = g_strdup (status);
103 xdmcp_server_get_status (XDMCPServer *server)
105 g_return_val_if_fail (server != NULL, NULL);
106 return server->priv->status;
110 xdmcp_server_set_key (XDMCPServer *server, const gchar *key)
112 g_return_if_fail (server != NULL);
113 g_free (server->priv->key);
114 server->priv->key = g_strdup (key);
118 session_timeout_cb (XDMCPSession *session)
120 g_debug ("Timing out unmanaged session %d", session->priv->id);
121 g_hash_table_remove (session->priv->server->priv->sessions, GINT_TO_POINTER ((gint) session->priv->id));
125 static XDMCPSession *
126 add_session (XDMCPServer *server)
128 XDMCPSession *session;
133 id = g_random_int () & 0xFFFFFFFF;
134 } while (g_hash_table_lookup (server->priv->sessions, GINT_TO_POINTER ((gint) id)));
136 session = xdmcp_session_new (id);
137 session->priv->server = server;
138 g_hash_table_insert (server->priv->sessions, GINT_TO_POINTER ((gint) id), g_object_ref (session));
139 session->priv->inactive_timeout = g_timeout_add (MANAGE_TIMEOUT, (GSourceFunc) session_timeout_cb, session);
144 static XDMCPSession *
145 get_session (XDMCPServer *server, guint16 id)
147 return g_hash_table_lookup (server->priv->sessions, GINT_TO_POINTER ((gint) id));
151 send_packet (GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
156 g_debug ("Send %s", xdmcp_packet_tostring (packet));
158 n_written = xdmcp_packet_encode (packet, data, 1024);
160 g_critical ("Failed to encode XDMCP packet");
163 GError *error = NULL;
165 g_socket_send_to (socket, address, (gchar *) data, n_written, NULL, &error);
167 g_warning ("Error sending packet: %s", error->message);
168 g_clear_error (&error);
173 get_authentication_name (XDMCPServer *server)
175 if (server->priv->key)
176 return "XDM-AUTHENTICATION-1";
182 handle_query (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
184 XDMCPPacket *response;
186 gchar *authentication_name = NULL;
188 /* If no authentication requested and we are configured for none then allow */
189 if (packet->Query.authentication_names[0] == NULL && server->priv->key == NULL)
190 authentication_name = "";
192 for (i = packet->Query.authentication_names; *i; i++)
194 if (strcmp (*i, get_authentication_name (server)) == 0 && server->priv->key != NULL)
196 authentication_name = *i;
201 if (authentication_name)
203 response = xdmcp_packet_alloc (XDMCP_Willing);
204 response->Willing.authentication_name = g_strdup (authentication_name);
205 response->Willing.hostname = g_strdup (server->priv->hostname);
206 response->Willing.status = g_strdup (server->priv->status);
210 response = xdmcp_packet_alloc (XDMCP_Unwilling);
211 response->Unwilling.hostname = g_strdup (server->priv->hostname);
212 if (server->priv->key)
213 response->Unwilling.status = g_strdup_printf ("No matching authentication, server requires %s", get_authentication_name (server));
215 response->Unwilling.status = g_strdup ("Server does not support authentication");
218 send_packet (socket, address, response);
220 xdmcp_packet_free (response);
226 if (c >= '0' && c <= '9')
228 if (c >= 'a' && c <= 'f')
230 if (c >= 'A' && c <= 'F')
236 decode_key (const gchar *key, guint8 *data)
241 if (strncmp (key, "0x", 2) == 0 || strncmp (key, "0X", 2) == 0)
243 for (i = 0; i < 8; i++)
245 if (key[i*2] == '\0')
247 data[i] |= atox (key[i*2]) << 8;
248 if (key[i*2+1] == '\0')
250 data[i] |= atox (key[i*2+1]);
255 for (i = 1; i < 8 && key[i-1]; i++)
261 handle_request (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
264 XDMCPPacket *response;
265 XDMCPSession *session;
266 guint8 *authentication_data = NULL;
267 gsize authentication_data_length = 0;
268 gboolean match_authorization = FALSE;
269 gchar *authorization_name;
270 guint8 *authorization_data = NULL;
271 gsize authorization_data_length = 0;
272 guint8 *session_authorization_data = NULL;
273 gsize session_authorization_data_length = 0;
276 GInetAddress *xserver_address = NULL;
277 gchar *display_number;
280 /* Try and find an IPv6 address */
281 for (i = 0; i < packet->Request.n_connections; i++)
283 XDMCPConnection *connection = &packet->Request.connections[i];
284 if (connection->type == XAUTH_FAMILY_INTERNET6 && connection->address.length == 16)
286 family = connection->type;
287 xserver_address = g_inet_address_new_from_bytes (connection->address.data, G_SOCKET_FAMILY_IPV6);
289 /* We can't use link-local addresses, as we need to know what interface it is on */
290 if (g_inet_address_get_is_link_local (xserver_address))
292 g_object_unref (xserver_address);
293 xserver_address = NULL;
300 /* If no IPv6 address, then try and find an IPv4 one */
301 if (!xserver_address)
303 for (i = 0; i < packet->Request.n_connections; i++)
305 XDMCPConnection *connection = &packet->Request.connections[i];
306 if (connection->type == XAUTH_FAMILY_INTERNET && connection->address.length == 4)
308 family = connection->type;
309 xserver_address = g_inet_address_new_from_bytes (connection->address.data, G_SOCKET_FAMILY_IPV4);
315 /* Decline if haven't got an address we can connect on */
316 if (!xserver_address)
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, get_authentication_name (server)) != 0)
331 response = xdmcp_packet_alloc (XDMCP_Decline);
332 if (server->priv->key)
333 response->Decline.status = g_strdup_printf ("Server only supports %s authentication", get_authentication_name (server));
335 response->Decline.status = g_strdup ("Server does not support authentication");
336 response->Decline.authentication_name = g_strdup ("");
337 send_packet (socket, address, response);
338 xdmcp_packet_free (response);
342 /* Perform requested authentication */
343 if (server->priv->key)
345 guint8 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);
351 decode_key (server->priv->key, key);
353 /* Decode message from server */
354 authentication_data = g_malloc (sizeof (guint8) * 8);
355 authentication_data_length = 8;
357 XdmcpUnwrap (input, key, rho.data, authentication_data_length);
358 XdmcpIncrementKey (&rho);
359 XdmcpWrap (rho.data, key, authentication_data, authentication_data_length);
361 authorization_name = g_strdup ("XDM-AUTHORIZATION-1");
364 authorization_name = g_strdup ("MIT-MAGIC-COOKIE-1");
366 /* Check if they support our authorization */
367 for (j = packet->Request.authorization_names; *j; j++)
369 if (strcmp (*j, authorization_name) == 0)
371 match_authorization = TRUE;
376 /* Decline if don't support out authorization */
377 if (!match_authorization)
379 response = xdmcp_packet_alloc (XDMCP_Decline);
380 response->Decline.status = g_strdup_printf ("Server requires %s authorization", authorization_name);
381 g_free (authorization_name);
382 response->Decline.authentication_name = g_strdup (packet->Request.authentication_name);
383 response->Decline.authentication_data.data = authentication_data;
384 response->Decline.authentication_data.length = authentication_data_length;
385 send_packet (socket, address, response);
386 xdmcp_packet_free (response);
390 /* Perform requested authorization */
391 if (server->priv->key)
394 guint8 key[8], session_key[8];
397 decode_key (server->priv->key, key);
399 /* Generate a private session key */
400 // FIXME: Pick a good DES key?
402 for (i = 1; i < 8; i++)
403 session_key[i] = g_random_int () & 0xFF;
405 /* Encrypt the session key and send it to the server */
406 authorization_data = g_malloc (8);
407 authorization_data_length = 8;
408 XdmcpWrap (session_key, key, authorization_data, authorization_data_length);
410 /* Authorization data is the number received from the client followed by the private session key */
411 session_authorization_data = g_malloc (16);
412 session_authorization_data_length = 16;
413 XdmcpDecrementKey (&rho);
414 memcpy (session_authorization_data, rho.data, 8);
415 memcpy (session_authorization_data + 8, session_key, 8);
421 /* Data is the cookie */
422 auth = xauth_new_cookie (XAUTH_FAMILY_WILD, NULL, 0, "");
423 authorization_data = xauth_copy_authorization_data (auth);
424 authorization_data_length = xauth_get_authorization_data_length (auth);
425 session_authorization_data = xauth_copy_authorization_data (auth);
426 session_authorization_data_length = xauth_get_authorization_data_length (auth);
428 g_object_unref (auth);
431 session = add_session (server);
432 session->priv->address = xserver_address;
433 session->priv->display_number = packet->Request.display_number;
434 display_number = g_strdup_printf ("%d", packet->Request.display_number);
436 /* We need to check if this is the loopback address and set the authority
437 * for a local connection if this is so as XCB treats "127.0.0.1" as local
439 if (g_inet_address_get_is_loopback (xserver_address))
441 gchar hostname[1024];
442 gethostname (hostname, 1024);
444 session->priv->authority = xauth_new (XAUTH_FAMILY_LOCAL,
449 session_authorization_data,
450 session_authorization_data_length);
453 session->priv->authority = xauth_new (family,
454 g_inet_address_to_bytes (G_INET_ADDRESS (xserver_address)),
455 g_inet_address_get_native_size (G_INET_ADDRESS (xserver_address)),
458 session_authorization_data,
459 session_authorization_data_length);
460 g_free (display_number);
462 response = xdmcp_packet_alloc (XDMCP_Accept);
463 response->Accept.session_id = xdmcp_session_get_id (session);
464 response->Accept.authentication_name = g_strdup (packet->Request.authentication_name);
465 response->Accept.authentication_data.data = authentication_data;
466 response->Accept.authentication_data.length = authentication_data_length;
467 response->Accept.authorization_name = authorization_name;
468 response->Accept.authorization_data.data = authorization_data;
469 response->Accept.authorization_data.length = authorization_data_length;
470 send_packet (socket, address, response);
471 xdmcp_packet_free (response);
475 handle_manage (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
477 XDMCPSession *session;
480 session = get_session (server, packet->Manage.session_id);
483 XDMCPPacket *response;
485 response = xdmcp_packet_alloc (XDMCP_Refuse);
486 response->Refuse.session_id = packet->Manage.session_id;
487 send_packet (socket, address, response);
488 xdmcp_packet_free (response);
493 /* Ignore duplicate requests */
494 if (session->priv->started)
496 if (session->priv->display_number != packet->Manage.display_number ||
497 strcmp (session->priv->display_class, packet->Manage.display_class) != 0)
498 g_debug ("Ignoring duplicate Manage with different data");
502 /* Reject if has changed display number */
503 if (packet->Manage.display_number != session->priv->display_number)
505 XDMCPPacket *response;
507 g_debug ("Received Manage for display number %d, but Request was %d", packet->Manage.display_number, session->priv->display_number);
508 response = xdmcp_packet_alloc (XDMCP_Refuse);
509 response->Refuse.session_id = packet->Manage.session_id;
510 send_packet (socket, address, response);
511 xdmcp_packet_free (response);
514 session->priv->display_class = g_strdup (packet->Manage.display_class);
516 g_signal_emit (server, signals[NEW_SESSION], 0, session, &result);
519 /* Cancel the inactive timer */
520 g_source_remove (session->priv->inactive_timeout);
522 session->priv->started = TRUE;
526 XDMCPPacket *response;
528 response = xdmcp_packet_alloc (XDMCP_Failed);
529 response->Failed.session_id = packet->Manage.session_id;
530 response->Failed.status = g_strdup_printf ("Failed to connect to display :%d", packet->Manage.display_number);
531 send_packet (socket, address, response);
532 xdmcp_packet_free (response);
537 handle_keep_alive (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
539 XDMCPPacket *response;
540 XDMCPSession *session;
541 gboolean alive = FALSE;
543 session = get_session (server, packet->KeepAlive.session_id);
545 alive = TRUE; // FIXME: xdmcp_session_get_alive (session);
547 response = xdmcp_packet_alloc (XDMCP_Alive);
548 response->Alive.session_running = alive;
549 response->Alive.session_id = alive ? packet->KeepAlive.session_id : 0;
550 send_packet (socket, address, response);
551 xdmcp_packet_free (response);
555 read_cb (GSocket *socket, GIOCondition condition, XDMCPServer *server)
557 GSocketAddress *address;
559 GError *error = NULL;
562 n_read = g_socket_receive_from (socket, &address, data, 1024, NULL, &error);
564 g_warning ("Failed to read from XDMCP socket: %s", error->message);
565 g_clear_error (&error);
571 packet = xdmcp_packet_decode ((guint8 *)data, n_read);
574 g_debug ("Got %s", xdmcp_packet_tostring (packet));
576 switch (packet->opcode)
578 case XDMCP_BroadcastQuery:
580 case XDMCP_IndirectQuery:
581 handle_query (server, socket, address, packet);
584 handle_request (server, socket, address, packet);
587 handle_manage (server, socket, address, packet);
589 case XDMCP_KeepAlive:
590 handle_keep_alive (server, socket, address, packet);
593 g_warning ("Got unexpected XDMCP packet %d", packet->opcode);
597 xdmcp_packet_free (packet);
605 open_udp_socket (GSocketFamily family, guint port, GError **error)
608 GSocketAddress *address;
611 socket = g_socket_new (family, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, error);
615 address = g_inet_socket_address_new (g_inet_address_new_any (family), port);
616 result = g_socket_bind (socket, address, TRUE, error);
619 g_object_unref (socket);
627 xdmcp_server_start (XDMCPServer *server)
630 GError *error = NULL;
632 g_return_val_if_fail (server != NULL, FALSE);
634 server->priv->socket = open_udp_socket (G_SOCKET_FAMILY_IPV4, server->priv->port, &error);
636 g_warning ("Failed to create IPv4 XDMCP socket: %s", error->message);
637 g_clear_error (&error);
639 if (server->priv->socket)
641 source = g_socket_create_source (server->priv->socket, G_IO_IN, NULL);
642 g_source_set_callback (source, (GSourceFunc) read_cb, server, NULL);
643 g_source_attach (source, NULL);
646 server->priv->socket6 = open_udp_socket (G_SOCKET_FAMILY_IPV6, server->priv->port, &error);
648 g_warning ("Failed to create IPv6 XDMCP socket: %s", error->message);
649 g_clear_error (&error);
651 if (server->priv->socket6)
653 source = g_socket_create_source (server->priv->socket6, G_IO_IN, NULL);
654 g_source_set_callback (source, (GSourceFunc) read_cb, server, NULL);
655 g_source_attach (source, NULL);
658 if (!server->priv->socket && !server->priv->socket6)
665 xdmcp_server_init (XDMCPServer *server)
667 server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, XDMCP_SERVER_TYPE, XDMCPServerPrivate);
669 server->priv->port = XDM_UDP_PORT;
670 server->priv->hostname = g_strdup ("");
671 server->priv->status = g_strdup ("");
672 server->priv->sessions = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
676 xdmcp_server_finalize (GObject *object)
680 self = XDMCP_SERVER (object);
682 if (self->priv->socket)
683 g_object_unref (self->priv->socket);
684 if (self->priv->socket6)
685 g_object_unref (self->priv->socket6);
686 g_free (self->priv->hostname);
687 g_free (self->priv->status);
688 g_free (self->priv->key);
689 g_hash_table_unref (self->priv->sessions);
691 G_OBJECT_CLASS (xdmcp_server_parent_class)->finalize (object);
695 xdmcp_server_class_init (XDMCPServerClass *klass)
697 GObjectClass *object_class = G_OBJECT_CLASS (klass);
699 object_class->finalize = xdmcp_server_finalize;
701 g_type_class_add_private (klass, sizeof (XDMCPServerPrivate));
703 signals[NEW_SESSION] =
704 g_signal_new ("new-session",
705 G_TYPE_FROM_CLASS (klass),
707 G_STRUCT_OFFSET (XDMCPServerClass, new_session),
708 g_signal_accumulator_true_handled,
710 ldm_marshal_BOOLEAN__OBJECT,
711 G_TYPE_BOOLEAN, 1, XDMCP_SESSION_TYPE);