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 session->priv->inactive_timeout = 0;
121 g_debug ("Timing out unmanaged session %d", session->priv->id);
122 g_hash_table_remove (session->priv->server->priv->sessions, GINT_TO_POINTER ((gint) session->priv->id));
126 static XDMCPSession *
127 add_session (XDMCPServer *server)
129 XDMCPSession *session;
134 id = g_random_int () & 0xFFFFFFFF;
135 } while (g_hash_table_lookup (server->priv->sessions, GINT_TO_POINTER ((gint) id)));
137 session = xdmcp_session_new (id);
138 session->priv->server = server;
139 g_hash_table_insert (server->priv->sessions, GINT_TO_POINTER ((gint) id), g_object_ref (session));
140 session->priv->inactive_timeout = g_timeout_add (MANAGE_TIMEOUT, (GSourceFunc) session_timeout_cb, session);
145 static XDMCPSession *
146 get_session (XDMCPServer *server, guint16 id)
148 return g_hash_table_lookup (server->priv->sessions, GINT_TO_POINTER ((gint) id));
152 send_packet (GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
157 g_debug ("Send %s", xdmcp_packet_tostring (packet));
159 n_written = xdmcp_packet_encode (packet, data, 1024);
161 g_critical ("Failed to encode XDMCP packet");
164 GError *error = NULL;
166 g_socket_send_to (socket, address, (gchar *) data, n_written, NULL, &error);
168 g_warning ("Error sending packet: %s", error->message);
169 g_clear_error (&error);
174 get_authentication_name (XDMCPServer *server)
176 if (server->priv->key)
177 return "XDM-AUTHENTICATION-1";
183 handle_query (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
185 XDMCPPacket *response;
187 gchar *authentication_name = NULL;
189 /* If no authentication requested and we are configured for none then allow */
190 if (packet->Query.authentication_names[0] == NULL && server->priv->key == NULL)
191 authentication_name = "";
193 for (i = packet->Query.authentication_names; *i; i++)
195 if (strcmp (*i, get_authentication_name (server)) == 0 && server->priv->key != NULL)
197 authentication_name = *i;
202 if (authentication_name)
204 response = xdmcp_packet_alloc (XDMCP_Willing);
205 response->Willing.authentication_name = g_strdup (authentication_name);
206 response->Willing.hostname = g_strdup (server->priv->hostname);
207 response->Willing.status = g_strdup (server->priv->status);
211 response = xdmcp_packet_alloc (XDMCP_Unwilling);
212 response->Unwilling.hostname = g_strdup (server->priv->hostname);
213 if (server->priv->key)
214 response->Unwilling.status = g_strdup_printf ("No matching authentication, server requires %s", get_authentication_name (server));
216 response->Unwilling.status = g_strdup ("Server does not support authentication");
219 send_packet (socket, address, response);
221 xdmcp_packet_free (response);
227 if (c >= '0' && c <= '9')
229 if (c >= 'a' && c <= 'f')
231 if (c >= 'A' && c <= 'F')
237 decode_key (const gchar *key, guint8 *data)
242 if (strncmp (key, "0x", 2) == 0 || strncmp (key, "0X", 2) == 0)
244 for (i = 0; i < 8; i++)
246 if (key[i*2] == '\0')
248 data[i] |= atox (key[i*2]) << 8;
249 if (key[i*2+1] == '\0')
251 data[i] |= atox (key[i*2+1]);
256 for (i = 1; i < 8 && key[i-1]; i++)
262 handle_request (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
265 XDMCPPacket *response;
266 XDMCPSession *session;
267 guint8 *authentication_data = NULL;
268 gsize authentication_data_length = 0;
269 gboolean match_authorization = FALSE;
270 gchar *authorization_name;
271 guint8 *authorization_data = NULL;
272 gsize authorization_data_length = 0;
273 guint8 *session_authorization_data = NULL;
274 gsize session_authorization_data_length = 0;
277 GInetAddress *x_server_address = NULL;
278 gchar *display_number;
281 /* Try and find an IPv6 address */
282 for (i = 0; i < packet->Request.n_connections; i++)
284 XDMCPConnection *connection = &packet->Request.connections[i];
285 if (connection->type == XAUTH_FAMILY_INTERNET6 && connection->address.length == 16)
287 family = connection->type;
288 x_server_address = g_inet_address_new_from_bytes (connection->address.data, G_SOCKET_FAMILY_IPV6);
290 /* We can't use link-local addresses, as we need to know what interface it is on */
291 if (g_inet_address_get_is_link_local (x_server_address))
293 g_object_unref (x_server_address);
294 x_server_address = NULL;
301 /* If no IPv6 address, then try and find an IPv4 one */
302 if (!x_server_address)
304 for (i = 0; i < packet->Request.n_connections; i++)
306 XDMCPConnection *connection = &packet->Request.connections[i];
307 if (connection->type == XAUTH_FAMILY_INTERNET && connection->address.length == 4)
309 family = connection->type;
310 x_server_address = g_inet_address_new_from_bytes (connection->address.data, G_SOCKET_FAMILY_IPV4);
316 /* Decline if haven't got an address we can connect on */
317 if (!x_server_address)
319 response = xdmcp_packet_alloc (XDMCP_Decline);
320 response->Decline.status = g_strdup ("No valid address found");
321 response->Decline.authentication_name = g_strdup (packet->Request.authentication_name);
322 response->Decline.authentication_data.data = authentication_data;
323 response->Decline.authentication_data.length = authentication_data_length;
324 send_packet (socket, address, response);
325 xdmcp_packet_free (response);
329 /* Must be using our authentication scheme */
330 if (strcmp (packet->Request.authentication_name, get_authentication_name (server)) != 0)
332 response = xdmcp_packet_alloc (XDMCP_Decline);
333 if (server->priv->key)
334 response->Decline.status = g_strdup_printf ("Server only supports %s authentication", get_authentication_name (server));
336 response->Decline.status = g_strdup ("Server does not support authentication");
337 response->Decline.authentication_name = g_strdup ("");
338 send_packet (socket, address, response);
339 xdmcp_packet_free (response);
343 /* Perform requested authentication */
344 if (server->priv->key)
346 guint8 input[8], key[8];
348 memset (input, 0, 8);
349 memcpy (input, packet->Request.authentication_data.data, packet->Request.authentication_data.length > 8 ? 8 : packet->Request.authentication_data.length);
352 decode_key (server->priv->key, key);
354 /* Decode message from server */
355 authentication_data = g_malloc (sizeof (guint8) * 8);
356 authentication_data_length = 8;
358 XdmcpUnwrap (input, key, rho.data, authentication_data_length);
359 XdmcpIncrementKey (&rho);
360 XdmcpWrap (rho.data, key, authentication_data, authentication_data_length);
362 authorization_name = g_strdup ("XDM-AUTHORIZATION-1");
365 authorization_name = g_strdup ("MIT-MAGIC-COOKIE-1");
367 /* Check if they support our authorization */
368 for (j = packet->Request.authorization_names; *j; j++)
370 if (strcmp (*j, authorization_name) == 0)
372 match_authorization = TRUE;
377 /* Decline if don't support out authorization */
378 if (!match_authorization)
380 response = xdmcp_packet_alloc (XDMCP_Decline);
381 response->Decline.status = g_strdup_printf ("Server requires %s authorization", authorization_name);
382 g_free (authorization_name);
383 response->Decline.authentication_name = g_strdup (packet->Request.authentication_name);
384 response->Decline.authentication_data.data = authentication_data;
385 response->Decline.authentication_data.length = authentication_data_length;
386 send_packet (socket, address, response);
387 xdmcp_packet_free (response);
391 /* Perform requested authorization */
392 if (server->priv->key)
395 guint8 key[8], session_key[8];
398 decode_key (server->priv->key, key);
400 /* Generate a private session key */
401 // FIXME: Pick a good DES key?
403 for (i = 1; i < 8; i++)
404 session_key[i] = g_random_int () & 0xFF;
406 /* Encrypt the session key and send it to the server */
407 authorization_data = g_malloc (8);
408 authorization_data_length = 8;
409 XdmcpWrap (session_key, key, authorization_data, authorization_data_length);
411 /* Authorization data is the number received from the client followed by the private session key */
412 session_authorization_data = g_malloc (16);
413 session_authorization_data_length = 16;
414 XdmcpDecrementKey (&rho);
415 memcpy (session_authorization_data, rho.data, 8);
416 memcpy (session_authorization_data + 8, session_key, 8);
422 /* Data is the cookie */
423 auth = x_authority_new_cookie (XAUTH_FAMILY_WILD, NULL, 0, "");
424 authorization_data = x_authority_copy_authorization_data (auth);
425 authorization_data_length = x_authority_get_authorization_data_length (auth);
426 session_authorization_data = x_authority_copy_authorization_data (auth);
427 session_authorization_data_length = x_authority_get_authorization_data_length (auth);
429 g_object_unref (auth);
432 session = add_session (server);
433 session->priv->address = x_server_address;
434 session->priv->display_number = packet->Request.display_number;
435 display_number = g_strdup_printf ("%d", packet->Request.display_number);
437 /* We need to check if this is the loopback address and set the authority
438 * for a local connection if this is so as XCB treats "127.0.0.1" as local
440 if (g_inet_address_get_is_loopback (x_server_address))
442 gchar hostname[1024];
443 gethostname (hostname, 1024);
445 session->priv->authority = x_authority_new (XAUTH_FAMILY_LOCAL,
450 session_authorization_data,
451 session_authorization_data_length);
454 session->priv->authority = x_authority_new (family,
455 g_inet_address_to_bytes (G_INET_ADDRESS (x_server_address)),
456 g_inet_address_get_native_size (G_INET_ADDRESS (x_server_address)),
459 session_authorization_data,
460 session_authorization_data_length);
461 g_free (display_number);
463 response = xdmcp_packet_alloc (XDMCP_Accept);
464 response->Accept.session_id = xdmcp_session_get_id (session);
465 response->Accept.authentication_name = g_strdup (packet->Request.authentication_name);
466 response->Accept.authentication_data.data = authentication_data;
467 response->Accept.authentication_data.length = authentication_data_length;
468 response->Accept.authorization_name = authorization_name;
469 response->Accept.authorization_data.data = authorization_data;
470 response->Accept.authorization_data.length = authorization_data_length;
471 send_packet (socket, address, response);
472 xdmcp_packet_free (response);
476 handle_manage (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
478 XDMCPSession *session;
481 session = get_session (server, packet->Manage.session_id);
484 XDMCPPacket *response;
486 response = xdmcp_packet_alloc (XDMCP_Refuse);
487 response->Refuse.session_id = packet->Manage.session_id;
488 send_packet (socket, address, response);
489 xdmcp_packet_free (response);
494 /* Ignore duplicate requests */
495 if (session->priv->started)
497 if (session->priv->display_number != packet->Manage.display_number ||
498 strcmp (session->priv->display_class, packet->Manage.display_class) != 0)
499 g_debug ("Ignoring duplicate Manage with different data");
503 /* Reject if has changed display number */
504 if (packet->Manage.display_number != session->priv->display_number)
506 XDMCPPacket *response;
508 g_debug ("Received Manage for display number %d, but Request was %d", packet->Manage.display_number, session->priv->display_number);
509 response = xdmcp_packet_alloc (XDMCP_Refuse);
510 response->Refuse.session_id = packet->Manage.session_id;
511 send_packet (socket, address, response);
512 xdmcp_packet_free (response);
515 session->priv->display_class = g_strdup (packet->Manage.display_class);
517 g_signal_emit (server, signals[NEW_SESSION], 0, session, &result);
520 /* Cancel the inactive timer */
521 if (session->priv->inactive_timeout)
522 g_source_remove (session->priv->inactive_timeout);
524 session->priv->started = TRUE;
528 XDMCPPacket *response;
530 response = xdmcp_packet_alloc (XDMCP_Failed);
531 response->Failed.session_id = packet->Manage.session_id;
532 response->Failed.status = g_strdup_printf ("Failed to connect to display :%d", packet->Manage.display_number);
533 send_packet (socket, address, response);
534 xdmcp_packet_free (response);
539 handle_keep_alive (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
541 XDMCPPacket *response;
542 XDMCPSession *session;
543 gboolean alive = FALSE;
545 session = get_session (server, packet->KeepAlive.session_id);
547 alive = TRUE; // FIXME: xdmcp_session_get_alive (session);
549 response = xdmcp_packet_alloc (XDMCP_Alive);
550 response->Alive.session_running = alive;
551 response->Alive.session_id = alive ? packet->KeepAlive.session_id : 0;
552 send_packet (socket, address, response);
553 xdmcp_packet_free (response);
557 read_cb (GSocket *socket, GIOCondition condition, XDMCPServer *server)
559 GSocketAddress *address;
561 GError *error = NULL;
564 n_read = g_socket_receive_from (socket, &address, data, 1024, NULL, &error);
566 g_warning ("Failed to read from XDMCP socket: %s", error->message);
567 g_clear_error (&error);
573 packet = xdmcp_packet_decode ((guint8 *)data, n_read);
576 g_debug ("Got %s", xdmcp_packet_tostring (packet));
578 switch (packet->opcode)
580 case XDMCP_BroadcastQuery:
582 case XDMCP_IndirectQuery:
583 handle_query (server, socket, address, packet);
586 handle_request (server, socket, address, packet);
589 handle_manage (server, socket, address, packet);
591 case XDMCP_KeepAlive:
592 handle_keep_alive (server, socket, address, packet);
595 g_warning ("Got unexpected XDMCP packet %d", packet->opcode);
599 xdmcp_packet_free (packet);
607 open_udp_socket (GSocketFamily family, guint port, GError **error)
610 GSocketAddress *address;
613 socket = g_socket_new (family, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, error);
617 address = g_inet_socket_address_new (g_inet_address_new_any (family), port);
618 result = g_socket_bind (socket, address, TRUE, error);
621 g_object_unref (socket);
629 xdmcp_server_start (XDMCPServer *server)
632 GError *error = NULL;
634 g_return_val_if_fail (server != NULL, FALSE);
636 server->priv->socket = open_udp_socket (G_SOCKET_FAMILY_IPV4, server->priv->port, &error);
638 g_warning ("Failed to create IPv4 XDMCP socket: %s", error->message);
639 g_clear_error (&error);
641 if (server->priv->socket)
643 source = g_socket_create_source (server->priv->socket, G_IO_IN, NULL);
644 g_source_set_callback (source, (GSourceFunc) read_cb, server, NULL);
645 g_source_attach (source, NULL);
648 server->priv->socket6 = open_udp_socket (G_SOCKET_FAMILY_IPV6, server->priv->port, &error);
650 g_warning ("Failed to create IPv6 XDMCP socket: %s", error->message);
651 g_clear_error (&error);
653 if (server->priv->socket6)
655 source = g_socket_create_source (server->priv->socket6, G_IO_IN, NULL);
656 g_source_set_callback (source, (GSourceFunc) read_cb, server, NULL);
657 g_source_attach (source, NULL);
660 if (!server->priv->socket && !server->priv->socket6)
667 xdmcp_server_init (XDMCPServer *server)
669 server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, XDMCP_SERVER_TYPE, XDMCPServerPrivate);
671 server->priv->port = XDM_UDP_PORT;
672 server->priv->hostname = g_strdup ("");
673 server->priv->status = g_strdup ("");
674 server->priv->sessions = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
678 xdmcp_server_finalize (GObject *object)
682 self = XDMCP_SERVER (object);
684 if (self->priv->socket)
685 g_object_unref (self->priv->socket);
686 if (self->priv->socket6)
687 g_object_unref (self->priv->socket6);
688 g_free (self->priv->hostname);
689 g_free (self->priv->status);
690 g_free (self->priv->key);
691 g_hash_table_unref (self->priv->sessions);
693 G_OBJECT_CLASS (xdmcp_server_parent_class)->finalize (object);
697 xdmcp_server_class_init (XDMCPServerClass *klass)
699 GObjectClass *object_class = G_OBJECT_CLASS (klass);
701 object_class->finalize = xdmcp_server_finalize;
703 g_type_class_add_private (klass, sizeof (XDMCPServerPrivate));
705 signals[NEW_SESSION] =
706 g_signal_new (XDMCP_SERVER_SIGNAL_NEW_SESSION,
707 G_TYPE_FROM_CLASS (klass),
709 G_STRUCT_OFFSET (XDMCPServerClass, new_session),
710 g_signal_accumulator_true_handled,
713 G_TYPE_BOOLEAN, 1, XDMCP_SESSION_TYPE);