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 /* Listening sockets */
36 GSocket *socket, *socket6;
38 /* Hostname to report to client */
41 /* Status to report to clients */
44 /* XDM-AUTHENTICATION-1 key */
47 /* Active XDMCP sessions */
51 G_DEFINE_TYPE (XDMCPServer, xdmcp_server, G_TYPE_OBJECT);
53 /* Maximum number of milliseconds client will resend manage requests before giving up */
54 #define MANAGE_TIMEOUT 126000
57 xdmcp_server_new (void)
59 return g_object_new (XDMCP_SERVER_TYPE, NULL);
63 xdmcp_server_set_port (XDMCPServer *server, guint port)
65 g_return_if_fail (server != NULL);
66 server->priv->port = port;
70 xdmcp_server_get_port (XDMCPServer *server)
72 g_return_val_if_fail (server != NULL, 0);
73 return server->priv->port;
77 xdmcp_server_set_hostname (XDMCPServer *server, const gchar *hostname)
79 g_return_if_fail (server != NULL);
81 g_free (server->priv->hostname);
82 server->priv->hostname = g_strdup (hostname);
86 xdmcp_server_get_hostname (XDMCPServer *server)
88 g_return_val_if_fail (server != NULL, NULL);
89 return server->priv->hostname;
93 xdmcp_server_set_status (XDMCPServer *server, const gchar *status)
95 g_return_if_fail (server != NULL);
97 g_free (server->priv->status);
98 server->priv->status = g_strdup (status);
102 xdmcp_server_get_status (XDMCPServer *server)
104 g_return_val_if_fail (server != NULL, NULL);
105 return server->priv->status;
109 xdmcp_server_set_key (XDMCPServer *server, const gchar *key)
111 g_return_if_fail (server != NULL);
112 g_free (server->priv->key);
113 server->priv->key = g_strdup (key);
117 session_timeout_cb (XDMCPSession *session)
119 g_debug ("Timing out unmanaged session %d", session->priv->id);
120 g_hash_table_remove (session->priv->server->priv->sessions, GINT_TO_POINTER ((gint) session->priv->id));
124 static XDMCPSession *
125 add_session (XDMCPServer *server)
127 XDMCPSession *session;
132 id = g_random_int () & 0xFFFFFFFF;
133 } while (g_hash_table_lookup (server->priv->sessions, GINT_TO_POINTER ((gint) id)));
135 session = xdmcp_session_new (id);
136 session->priv->server = server;
137 g_hash_table_insert (server->priv->sessions, GINT_TO_POINTER ((gint) id), g_object_ref (session));
138 session->priv->inactive_timeout = g_timeout_add (MANAGE_TIMEOUT, (GSourceFunc) session_timeout_cb, session);
143 static XDMCPSession *
144 get_session (XDMCPServer *server, guint16 id)
146 return g_hash_table_lookup (server->priv->sessions, GINT_TO_POINTER ((gint) id));
150 send_packet (GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
155 g_debug ("Send %s", xdmcp_packet_tostring (packet));
157 n_written = xdmcp_packet_encode (packet, data, 1024);
159 g_critical ("Failed to encode XDMCP packet");
162 GError *error = NULL;
164 g_socket_send_to (socket, address, (gchar *) data, n_written, NULL, &error);
166 g_warning ("Error sending packet: %s", error->message);
167 g_clear_error (&error);
172 get_authentication_name (XDMCPServer *server)
174 if (server->priv->key)
175 return "XDM-AUTHENTICATION-1";
181 handle_query (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
183 XDMCPPacket *response;
185 gchar *authentication_name = NULL;
187 /* If no authentication requested and we are configured for none then allow */
188 if (packet->Query.authentication_names[0] == NULL && server->priv->key == NULL)
189 authentication_name = "";
191 for (i = packet->Query.authentication_names; *i; i++)
193 if (strcmp (*i, get_authentication_name (server)) == 0 && server->priv->key != NULL)
195 authentication_name = *i;
200 if (authentication_name)
202 response = xdmcp_packet_alloc (XDMCP_Willing);
203 response->Willing.authentication_name = g_strdup (authentication_name);
204 response->Willing.hostname = g_strdup (server->priv->hostname);
205 response->Willing.status = g_strdup (server->priv->status);
209 response = xdmcp_packet_alloc (XDMCP_Unwilling);
210 response->Unwilling.hostname = g_strdup (server->priv->hostname);
211 if (server->priv->key)
212 response->Unwilling.status = g_strdup_printf ("No matching authentication, server requires %s", get_authentication_name (server));
214 response->Unwilling.status = g_strdup ("Server does not support authentication");
217 send_packet (socket, address, response);
219 xdmcp_packet_free (response);
225 if (c >= '0' && c <= '9')
227 if (c >= 'a' && c <= 'f')
229 if (c >= 'A' && c <= 'F')
235 decode_key (const gchar *key, guint8 *data)
240 if (strncmp (key, "0x", 2) == 0 || strncmp (key, "0X", 2) == 0)
242 for (i = 0; i < 8; i++)
244 if (key[i*2] == '\0')
246 data[i] |= atox (key[i*2]) << 8;
247 if (key[i*2+1] == '\0')
249 data[i] |= atox (key[i*2+1]);
254 for (i = 1; i < 8 && key[i-1]; i++)
260 handle_request (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
263 XDMCPPacket *response;
264 XDMCPSession *session;
265 guint8 *authentication_data = NULL;
266 gsize authentication_data_length = 0;
267 gboolean match_authorization = FALSE;
268 gchar *authorization_name;
269 guint8 *authorization_data = NULL;
270 gsize authorization_data_length = 0;
271 guint8 *session_authorization_data = NULL;
272 gsize session_authorization_data_length = 0;
275 GInetAddress *x_server_address = NULL;
276 gchar *display_number;
279 /* Try and find an IPv6 address */
280 for (i = 0; i < packet->Request.n_connections; i++)
282 XDMCPConnection *connection = &packet->Request.connections[i];
283 if (connection->type == XAUTH_FAMILY_INTERNET6 && connection->address.length == 16)
285 family = connection->type;
286 x_server_address = g_inet_address_new_from_bytes (connection->address.data, G_SOCKET_FAMILY_IPV6);
288 /* We can't use link-local addresses, as we need to know what interface it is on */
289 if (g_inet_address_get_is_link_local (x_server_address))
291 g_object_unref (x_server_address);
292 x_server_address = NULL;
299 /* If no IPv6 address, then try and find an IPv4 one */
300 if (!x_server_address)
302 for (i = 0; i < packet->Request.n_connections; i++)
304 XDMCPConnection *connection = &packet->Request.connections[i];
305 if (connection->type == XAUTH_FAMILY_INTERNET && connection->address.length == 4)
307 family = connection->type;
308 x_server_address = g_inet_address_new_from_bytes (connection->address.data, G_SOCKET_FAMILY_IPV4);
314 /* Decline if haven't got an address we can connect on */
315 if (!x_server_address)
317 response = xdmcp_packet_alloc (XDMCP_Decline);
318 response->Decline.status = g_strdup ("No valid address found");
319 response->Decline.authentication_name = g_strdup (packet->Request.authentication_name);
320 response->Decline.authentication_data.data = authentication_data;
321 response->Decline.authentication_data.length = authentication_data_length;
322 send_packet (socket, address, response);
323 xdmcp_packet_free (response);
327 /* Must be using our authentication scheme */
328 if (strcmp (packet->Request.authentication_name, get_authentication_name (server)) != 0)
330 response = xdmcp_packet_alloc (XDMCP_Decline);
331 if (server->priv->key)
332 response->Decline.status = g_strdup_printf ("Server only supports %s authentication", get_authentication_name (server));
334 response->Decline.status = g_strdup ("Server does not support authentication");
335 response->Decline.authentication_name = g_strdup ("");
336 send_packet (socket, address, response);
337 xdmcp_packet_free (response);
341 /* Perform requested authentication */
342 if (server->priv->key)
344 guint8 input[8], key[8];
346 memset (input, 0, 8);
347 memcpy (input, packet->Request.authentication_data.data, packet->Request.authentication_data.length > 8 ? 8 : packet->Request.authentication_data.length);
350 decode_key (server->priv->key, key);
352 /* Decode message from server */
353 authentication_data = g_malloc (sizeof (guint8) * 8);
354 authentication_data_length = 8;
356 XdmcpUnwrap (input, key, rho.data, authentication_data_length);
357 XdmcpIncrementKey (&rho);
358 XdmcpWrap (rho.data, key, authentication_data, authentication_data_length);
360 authorization_name = g_strdup ("XDM-AUTHORIZATION-1");
363 authorization_name = g_strdup ("MIT-MAGIC-COOKIE-1");
365 /* Check if they support our authorization */
366 for (j = packet->Request.authorization_names; *j; j++)
368 if (strcmp (*j, authorization_name) == 0)
370 match_authorization = TRUE;
375 /* Decline if don't support out authorization */
376 if (!match_authorization)
378 response = xdmcp_packet_alloc (XDMCP_Decline);
379 response->Decline.status = g_strdup_printf ("Server requires %s authorization", authorization_name);
380 g_free (authorization_name);
381 response->Decline.authentication_name = g_strdup (packet->Request.authentication_name);
382 response->Decline.authentication_data.data = authentication_data;
383 response->Decline.authentication_data.length = authentication_data_length;
384 send_packet (socket, address, response);
385 xdmcp_packet_free (response);
389 /* Perform requested authorization */
390 if (server->priv->key)
393 guint8 key[8], session_key[8];
396 decode_key (server->priv->key, key);
398 /* Generate a private session key */
399 // FIXME: Pick a good DES key?
401 for (i = 1; i < 8; i++)
402 session_key[i] = g_random_int () & 0xFF;
404 /* Encrypt the session key and send it to the server */
405 authorization_data = g_malloc (8);
406 authorization_data_length = 8;
407 XdmcpWrap (session_key, key, authorization_data, authorization_data_length);
409 /* Authorization data is the number received from the client followed by the private session key */
410 session_authorization_data = g_malloc (16);
411 session_authorization_data_length = 16;
412 XdmcpDecrementKey (&rho);
413 memcpy (session_authorization_data, rho.data, 8);
414 memcpy (session_authorization_data + 8, session_key, 8);
420 /* Data is the cookie */
421 auth = x_authority_new_cookie (XAUTH_FAMILY_WILD, NULL, 0, "");
422 authorization_data = x_authority_copy_authorization_data (auth);
423 authorization_data_length = x_authority_get_authorization_data_length (auth);
424 session_authorization_data = x_authority_copy_authorization_data (auth);
425 session_authorization_data_length = x_authority_get_authorization_data_length (auth);
427 g_object_unref (auth);
430 session = add_session (server);
431 session->priv->address = x_server_address;
432 session->priv->display_number = packet->Request.display_number;
433 display_number = g_strdup_printf ("%d", packet->Request.display_number);
435 /* We need to check if this is the loopback address and set the authority
436 * for a local connection if this is so as XCB treats "127.0.0.1" as local
438 if (g_inet_address_get_is_loopback (x_server_address))
440 gchar hostname[1024];
441 gethostname (hostname, 1024);
443 session->priv->authority = x_authority_new (XAUTH_FAMILY_LOCAL,
448 session_authorization_data,
449 session_authorization_data_length);
452 session->priv->authority = x_authority_new (family,
453 g_inet_address_to_bytes (G_INET_ADDRESS (x_server_address)),
454 g_inet_address_get_native_size (G_INET_ADDRESS (x_server_address)),
457 session_authorization_data,
458 session_authorization_data_length);
459 g_free (display_number);
461 response = xdmcp_packet_alloc (XDMCP_Accept);
462 response->Accept.session_id = xdmcp_session_get_id (session);
463 response->Accept.authentication_name = g_strdup (packet->Request.authentication_name);
464 response->Accept.authentication_data.data = authentication_data;
465 response->Accept.authentication_data.length = authentication_data_length;
466 response->Accept.authorization_name = authorization_name;
467 response->Accept.authorization_data.data = authorization_data;
468 response->Accept.authorization_data.length = authorization_data_length;
469 send_packet (socket, address, response);
470 xdmcp_packet_free (response);
474 handle_manage (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
476 XDMCPSession *session;
479 session = get_session (server, packet->Manage.session_id);
482 XDMCPPacket *response;
484 response = xdmcp_packet_alloc (XDMCP_Refuse);
485 response->Refuse.session_id = packet->Manage.session_id;
486 send_packet (socket, address, response);
487 xdmcp_packet_free (response);
492 /* Ignore duplicate requests */
493 if (session->priv->started)
495 if (session->priv->display_number != packet->Manage.display_number ||
496 strcmp (session->priv->display_class, packet->Manage.display_class) != 0)
497 g_debug ("Ignoring duplicate Manage with different data");
501 /* Reject if has changed display number */
502 if (packet->Manage.display_number != session->priv->display_number)
504 XDMCPPacket *response;
506 g_debug ("Received Manage for display number %d, but Request was %d", packet->Manage.display_number, session->priv->display_number);
507 response = xdmcp_packet_alloc (XDMCP_Refuse);
508 response->Refuse.session_id = packet->Manage.session_id;
509 send_packet (socket, address, response);
510 xdmcp_packet_free (response);
513 session->priv->display_class = g_strdup (packet->Manage.display_class);
515 g_signal_emit (server, signals[NEW_SESSION], 0, session, &result);
518 /* Cancel the inactive timer */
519 g_source_remove (session->priv->inactive_timeout);
521 session->priv->started = TRUE;
525 XDMCPPacket *response;
527 response = xdmcp_packet_alloc (XDMCP_Failed);
528 response->Failed.session_id = packet->Manage.session_id;
529 response->Failed.status = g_strdup_printf ("Failed to connect to display :%d", packet->Manage.display_number);
530 send_packet (socket, address, response);
531 xdmcp_packet_free (response);
536 handle_keep_alive (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
538 XDMCPPacket *response;
539 XDMCPSession *session;
540 gboolean alive = FALSE;
542 session = get_session (server, packet->KeepAlive.session_id);
544 alive = TRUE; // FIXME: xdmcp_session_get_alive (session);
546 response = xdmcp_packet_alloc (XDMCP_Alive);
547 response->Alive.session_running = alive;
548 response->Alive.session_id = alive ? packet->KeepAlive.session_id : 0;
549 send_packet (socket, address, response);
550 xdmcp_packet_free (response);
554 read_cb (GSocket *socket, GIOCondition condition, XDMCPServer *server)
556 GSocketAddress *address;
558 GError *error = NULL;
561 n_read = g_socket_receive_from (socket, &address, data, 1024, NULL, &error);
563 g_warning ("Failed to read from XDMCP socket: %s", error->message);
564 g_clear_error (&error);
570 packet = xdmcp_packet_decode ((guint8 *)data, n_read);
573 g_debug ("Got %s", xdmcp_packet_tostring (packet));
575 switch (packet->opcode)
577 case XDMCP_BroadcastQuery:
579 case XDMCP_IndirectQuery:
580 handle_query (server, socket, address, packet);
583 handle_request (server, socket, address, packet);
586 handle_manage (server, socket, address, packet);
588 case XDMCP_KeepAlive:
589 handle_keep_alive (server, socket, address, packet);
592 g_warning ("Got unexpected XDMCP packet %d", packet->opcode);
596 xdmcp_packet_free (packet);
604 open_udp_socket (GSocketFamily family, guint port, GError **error)
607 GSocketAddress *address;
610 socket = g_socket_new (family, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, error);
614 address = g_inet_socket_address_new (g_inet_address_new_any (family), port);
615 result = g_socket_bind (socket, address, TRUE, error);
618 g_object_unref (socket);
626 xdmcp_server_start (XDMCPServer *server)
629 GError *error = NULL;
631 g_return_val_if_fail (server != NULL, FALSE);
633 server->priv->socket = open_udp_socket (G_SOCKET_FAMILY_IPV4, server->priv->port, &error);
635 g_warning ("Failed to create IPv4 XDMCP socket: %s", error->message);
636 g_clear_error (&error);
638 if (server->priv->socket)
640 source = g_socket_create_source (server->priv->socket, G_IO_IN, NULL);
641 g_source_set_callback (source, (GSourceFunc) read_cb, server, NULL);
642 g_source_attach (source, NULL);
645 server->priv->socket6 = open_udp_socket (G_SOCKET_FAMILY_IPV6, server->priv->port, &error);
647 g_warning ("Failed to create IPv6 XDMCP socket: %s", error->message);
648 g_clear_error (&error);
650 if (server->priv->socket6)
652 source = g_socket_create_source (server->priv->socket6, G_IO_IN, NULL);
653 g_source_set_callback (source, (GSourceFunc) read_cb, server, NULL);
654 g_source_attach (source, NULL);
657 if (!server->priv->socket && !server->priv->socket6)
664 xdmcp_server_init (XDMCPServer *server)
666 server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, XDMCP_SERVER_TYPE, XDMCPServerPrivate);
668 server->priv->port = XDM_UDP_PORT;
669 server->priv->hostname = g_strdup ("");
670 server->priv->status = g_strdup ("");
671 server->priv->sessions = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
675 xdmcp_server_finalize (GObject *object)
679 self = XDMCP_SERVER (object);
681 if (self->priv->socket)
682 g_object_unref (self->priv->socket);
683 if (self->priv->socket6)
684 g_object_unref (self->priv->socket6);
685 g_free (self->priv->hostname);
686 g_free (self->priv->status);
687 g_free (self->priv->key);
688 g_hash_table_unref (self->priv->sessions);
690 G_OBJECT_CLASS (xdmcp_server_parent_class)->finalize (object);
694 xdmcp_server_class_init (XDMCPServerClass *klass)
696 GObjectClass *object_class = G_OBJECT_CLASS (klass);
698 object_class->finalize = xdmcp_server_finalize;
700 g_type_class_add_private (klass, sizeof (XDMCPServerPrivate));
702 signals[NEW_SESSION] =
703 g_signal_new ("new-session",
704 G_TYPE_FROM_CLASS (klass),
706 G_STRUCT_OFFSET (XDMCPServerClass, new_session),
707 g_signal_accumulator_true_handled,
710 G_TYPE_BOOLEAN, 1, XDMCP_SESSION_TYPE);