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);
55 xdmcp_server_new (void)
57 return g_object_new (XDMCP_SERVER_TYPE, NULL);
61 xdmcp_server_set_port (XDMCPServer *server, guint port)
63 g_return_if_fail (server != NULL);
64 server->priv->port = port;
68 xdmcp_server_get_port (XDMCPServer *server)
70 g_return_val_if_fail (server != NULL, 0);
71 return server->priv->port;
75 xdmcp_server_set_hostname (XDMCPServer *server, const gchar *hostname)
77 g_return_if_fail (server != NULL);
79 g_free (server->priv->hostname);
80 server->priv->hostname = g_strdup (hostname);
84 xdmcp_server_get_hostname (XDMCPServer *server)
86 g_return_val_if_fail (server != NULL, NULL);
87 return server->priv->hostname;
91 xdmcp_server_set_status (XDMCPServer *server, const gchar *status)
93 g_return_if_fail (server != NULL);
95 g_free (server->priv->status);
96 server->priv->status = g_strdup (status);
100 xdmcp_server_get_status (XDMCPServer *server)
102 g_return_val_if_fail (server != NULL, NULL);
103 return server->priv->status;
107 xdmcp_server_set_key (XDMCPServer *server, const gchar *key)
109 g_return_if_fail (server != NULL);
110 g_free (server->priv->key);
111 server->priv->key = g_strdup (key);
115 session_timeout_cb (XDMCPSession *session)
117 g_debug ("Timing out unmanaged session %d", session->priv->id);
118 g_hash_table_remove (session->priv->server->priv->sessions, GINT_TO_POINTER ((gint) session->priv->id));
122 static XDMCPSession *
123 add_session (XDMCPServer *server)
125 XDMCPSession *session;
130 id = g_random_int () & 0xFFFFFFFF;
131 } while (g_hash_table_lookup (server->priv->sessions, GINT_TO_POINTER ((gint) id)));
133 session = xdmcp_session_new (id);
134 session->priv->server = server;
135 g_hash_table_insert (server->priv->sessions, GINT_TO_POINTER ((gint) id), g_object_ref (session));
136 session->priv->inactive_timeout = g_timeout_add (10, (GSourceFunc) session_timeout_cb, session);
141 static XDMCPSession *
142 get_session (XDMCPServer *server, guint16 id)
144 return g_hash_table_lookup (server->priv->sessions, GINT_TO_POINTER ((gint) id));
148 send_packet (GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
153 g_debug ("Send %s", xdmcp_packet_tostring (packet));
155 n_written = xdmcp_packet_encode (packet, data, 1024);
157 g_critical ("Failed to encode XDMCP packet");
160 GError *error = NULL;
162 if (g_socket_send_to (socket, address, (gchar *) data, n_written, NULL, &error) < 0)
163 g_warning ("Error sending packet: %s", error->message);
165 g_clear_error (&error);
170 get_authentication_name (XDMCPServer *server)
172 if (server->priv->key)
173 return "XDM-AUTHENTICATION-1";
179 handle_query (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
181 XDMCPPacket *response;
183 gchar *authentication_name = NULL;
185 /* If no authentication requested and we are configured for none then allow */
186 if (packet->Query.authentication_names[0] == NULL && server->priv->key == NULL)
187 authentication_name = "";
189 for (i = packet->Query.authentication_names; *i; i++)
191 if (strcmp (*i, get_authentication_name (server)) == 0 && server->priv->key != NULL)
193 authentication_name = *i;
198 if (authentication_name)
200 response = xdmcp_packet_alloc (XDMCP_Willing);
201 response->Willing.authentication_name = g_strdup (authentication_name);
202 response->Willing.hostname = g_strdup (server->priv->hostname);
203 response->Willing.status = g_strdup (server->priv->status);
207 response = xdmcp_packet_alloc (XDMCP_Unwilling);
208 response->Unwilling.hostname = g_strdup (server->priv->hostname);
209 if (server->priv->key)
210 response->Unwilling.status = g_strdup_printf ("No matching authentication, server requires %s", get_authentication_name (server));
212 response->Unwilling.status = g_strdup ("Server does not support authentication");
215 send_packet (socket, address, response);
217 xdmcp_packet_free (response);
223 if (c >= '0' && c <= '9')
225 if (c >= 'a' && c <= 'f')
227 if (c >= 'A' && c <= 'F')
233 decode_key (const gchar *key, guint8 *data)
237 memset (data, 0, sizeof (data));
238 if (strncmp (key, "0x", 2) == 0 || strncmp (key, "0X", 2) == 0)
240 for (i = 0; i < 8; i++)
242 if (key[i*2] == '\0')
244 data[i] |= atox (key[i*2]) << 8;
245 if (key[i*2+1] == '\0')
247 data[i] |= atox (key[i*2+1]);
252 for (i = 1; i < 8 && key[i-1]; i++)
258 handle_request (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
261 XDMCPPacket *response;
262 XDMCPSession *session;
263 guint8 *authentication_data = NULL;
264 gsize authentication_data_length = 0;
265 gboolean match_authorization = FALSE;
266 gchar *authorization_name;
267 guint8 *authorization_data = NULL;
268 gsize authorization_data_length = 0;
269 guint8 *session_authorization_data = NULL;
270 gsize session_authorization_data_length = 0;
273 GInetAddress *xserver_address = NULL;
274 gchar *display_number;
277 /* Try and find an IPv6 address */
278 for (i = 0; i < packet->Request.n_connections; i++)
280 XDMCPConnection *connection = &packet->Request.connections[i];
281 if (connection->type == XAUTH_FAMILY_INTERNET6 && connection->address.length == 16)
283 family = connection->type;
284 xserver_address = g_inet_address_new_from_bytes (connection->address.data, G_SOCKET_FAMILY_IPV6);
286 /* We can't use link-local addresses, as we need to know what interface it is on */
287 if (g_inet_address_get_is_link_local (xserver_address))
289 g_object_unref (xserver_address);
290 xserver_address = NULL;
297 /* If no IPv6 address, then try and find an IPv4 one */
298 if (!xserver_address)
300 for (i = 0; i < packet->Request.n_connections; i++)
302 XDMCPConnection *connection = &packet->Request.connections[i];
303 if (connection->type == XAUTH_FAMILY_INTERNET && connection->address.length == 4)
305 family = connection->type;
306 xserver_address = g_inet_address_new_from_bytes (connection->address.data, G_SOCKET_FAMILY_IPV4);
312 /* Decline if haven't got an address we can connect on */
313 if (!xserver_address)
315 response = xdmcp_packet_alloc (XDMCP_Decline);
316 response->Decline.status = g_strdup ("No valid address found");
317 response->Decline.authentication_name = g_strdup (packet->Request.authentication_name);
318 response->Decline.authentication_data.data = authentication_data;
319 response->Decline.authentication_data.length = authentication_data_length;
320 send_packet (socket, address, response);
321 xdmcp_packet_free (response);
325 /* Must be using our authentication scheme */
326 if (strcmp (packet->Request.authentication_name, get_authentication_name (server)) != 0)
328 response = xdmcp_packet_alloc (XDMCP_Decline);
329 if (server->priv->key)
330 response->Decline.status = g_strdup_printf ("Server only supports %s authentication", get_authentication_name (server));
332 response->Decline.status = g_strdup ("Server does not support authentication");
333 response->Decline.authentication_name = g_strdup ("");
334 send_packet (socket, address, response);
335 xdmcp_packet_free (response);
339 /* Perform requested authentication */
340 if (server->priv->key)
342 guint8 input[8], key[8];
344 memset (input, 0, 8);
345 memcpy (input, packet->Request.authentication_data.data, packet->Request.authentication_data.length > 8 ? 8 : packet->Request.authentication_data.length);
348 decode_key (server->priv->key, key);
350 /* Decode message from server */
351 authentication_data = g_malloc (sizeof (guint8) * 8);
352 authentication_data_length = 8;
354 XdmcpUnwrap (input, key, rho.data, authentication_data_length);
355 XdmcpIncrementKey (&rho);
356 XdmcpWrap (rho.data, key, authentication_data, authentication_data_length);
358 authorization_name = g_strdup ("XDM-AUTHORIZATION-1");
361 authorization_name = g_strdup ("MIT-MAGIC-COOKIE-1");
363 /* Check if they support our authorization */
364 for (j = packet->Request.authorization_names; *j; j++)
366 if (strcmp (*j, authorization_name) == 0)
368 match_authorization = TRUE;
373 /* Decline if don't support out authorization */
374 if (!match_authorization)
376 response = xdmcp_packet_alloc (XDMCP_Decline);
377 response->Decline.status = g_strdup_printf ("Server requires %s authorization", authorization_name);
378 g_free (authorization_name);
379 response->Decline.authentication_name = g_strdup (packet->Request.authentication_name);
380 response->Decline.authentication_data.data = authentication_data;
381 response->Decline.authentication_data.length = authentication_data_length;
382 send_packet (socket, address, response);
383 xdmcp_packet_free (response);
387 /* Perform requested authorization */
388 if (server->priv->key)
391 guint8 key[8], session_key[8];
394 decode_key (server->priv->key, key);
396 /* Generate a private session key */
397 // FIXME: Pick a good DES key?
399 for (i = 1; i < 8; i++)
400 session_key[i] = g_random_int () & 0xFF;
402 /* Encrypt the session key and send it to the server */
403 authorization_data = g_malloc (8);
404 authorization_data_length = 8;
405 XdmcpWrap (session_key, key, authorization_data, authorization_data_length);
407 /* Authorization data is the number received from the client followed by the private session key */
408 session_authorization_data = g_malloc (16);
409 session_authorization_data_length = 16;
410 XdmcpDecrementKey (&rho);
411 memcpy (session_authorization_data, rho.data, 8);
412 memcpy (session_authorization_data + 8, session_key, 8);
418 /* Data is the cookie */
419 auth = xauth_new_cookie (XAUTH_FAMILY_WILD, NULL, 0, "");
420 authorization_data = xauth_copy_authorization_data (auth);
421 authorization_data_length = xauth_get_authorization_data_length (auth);
422 session_authorization_data = xauth_copy_authorization_data (auth);
423 session_authorization_data_length = xauth_get_authorization_data_length (auth);
425 g_object_unref (auth);
428 session = add_session (server);
429 session->priv->address = xserver_address;
430 session->priv->display_number = packet->Request.display_number;
431 display_number = g_strdup_printf ("%d", packet->Request.display_number);
433 /* We need to check if this is the loopback address and set the authority
434 * for a local connection if this is so as XCB treats "127.0.0.1" as local
436 if (g_inet_address_get_is_loopback (xserver_address))
438 gchar hostname[1024];
439 gethostname (hostname, 1024);
441 session->priv->authority = xauth_new (XAUTH_FAMILY_LOCAL,
446 session_authorization_data,
447 session_authorization_data_length);
450 session->priv->authority = xauth_new (family,
451 g_inet_address_to_bytes (G_INET_ADDRESS (xserver_address)),
452 g_inet_address_get_native_size (G_INET_ADDRESS (xserver_address)),
455 session_authorization_data,
456 session_authorization_data_length);
457 g_free (display_number);
459 response = xdmcp_packet_alloc (XDMCP_Accept);
460 response->Accept.session_id = xdmcp_session_get_id (session);
461 response->Accept.authentication_name = g_strdup (packet->Request.authentication_name);
462 response->Accept.authentication_data.data = authentication_data;
463 response->Accept.authentication_data.length = authentication_data_length;
464 response->Accept.authorization_name = authorization_name;
465 response->Accept.authorization_data.data = authorization_data;
466 response->Accept.authorization_data.length = authorization_data_length;
467 send_packet (socket, address, response);
468 xdmcp_packet_free (response);
472 handle_manage (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
474 XDMCPSession *session;
477 session = get_session (server, packet->Manage.session_id);
480 XDMCPPacket *response;
482 response = xdmcp_packet_alloc (XDMCP_Refuse);
483 response->Refuse.session_id = packet->Manage.session_id;
484 send_packet (socket, address, response);
485 xdmcp_packet_free (response);
490 /* Ignore duplicate requests */
491 if (session->priv->started)
493 if (session->priv->display_number != packet->Manage.display_number ||
494 strcmp (session->priv->display_class, packet->Manage.display_class) != 0)
495 g_debug ("Ignoring duplicate Manage with different data");
499 /* Reject if has changed display number */
500 if (packet->Manage.display_number != session->priv->display_number)
502 XDMCPPacket *response;
504 g_debug ("Received Manage for display number %d, but Request was %d", packet->Manage.display_number, session->priv->display_number);
505 response = xdmcp_packet_alloc (XDMCP_Refuse);
506 response->Refuse.session_id = packet->Manage.session_id;
507 send_packet (socket, address, response);
508 xdmcp_packet_free (response);
511 session->priv->display_class = g_strdup (packet->Manage.display_class);
513 g_signal_emit (server, signals[NEW_SESSION], 0, session, &result);
516 /* Cancel the inactive timer */
517 g_source_remove (session->priv->inactive_timeout);
519 session->priv->started = TRUE;
523 XDMCPPacket *response;
525 response = xdmcp_packet_alloc (XDMCP_Failed);
526 response->Failed.session_id = packet->Manage.session_id;
527 response->Failed.status = g_strdup_printf ("Failed to connect to display :%d", packet->Manage.display_number);
528 send_packet (socket, address, response);
529 xdmcp_packet_free (response);
534 handle_keep_alive (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
536 XDMCPPacket *response;
537 XDMCPSession *session;
538 gboolean alive = FALSE;
540 session = get_session (server, packet->KeepAlive.session_id);
542 alive = TRUE; // FIXME: xdmcp_session_get_alive (session);
544 response = xdmcp_packet_alloc (XDMCP_Alive);
545 response->Alive.session_running = alive;
546 response->Alive.session_id = alive ? packet->KeepAlive.session_id : 0;
547 send_packet (socket, address, response);
548 xdmcp_packet_free (response);
552 read_cb (GSocket *socket, GIOCondition condition, XDMCPServer *server)
554 GSocketAddress *address;
556 GError *error = NULL;
559 n_read = g_socket_receive_from (socket, &address, data, 1024, NULL, &error);
564 packet = xdmcp_packet_decode ((guint8 *)data, n_read);
567 g_debug ("Got %s", xdmcp_packet_tostring (packet));
569 switch (packet->opcode)
571 case XDMCP_BroadcastQuery:
573 case XDMCP_IndirectQuery:
574 handle_query (server, socket, address, packet);
577 handle_request (server, socket, address, packet);
580 handle_manage (server, socket, address, packet);
582 case XDMCP_KeepAlive:
583 handle_keep_alive (server, socket, address, packet);
586 g_warning ("Got unexpected XDMCP packet %d", packet->opcode);
590 xdmcp_packet_free (packet);
594 g_warning ("Failed to read from XDMCP socket: %s", error->message);
596 g_clear_error (&error);
602 open_udp_socket (GSocketFamily family, guint port, GError **error)
605 GSocketAddress *address;
608 socket = g_socket_new (family, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, error);
612 address = g_inet_socket_address_new (g_inet_address_new_any (family), port);
613 result = g_socket_bind (socket, address, TRUE, error);
616 g_object_unref (socket);
624 xdmcp_server_start (XDMCPServer *server)
627 GError *error = NULL;
629 g_return_val_if_fail (server != NULL, FALSE);
631 server->priv->socket = open_udp_socket (G_SOCKET_FAMILY_IPV4, server->priv->port, &error);
632 if (server->priv->socket)
634 source = g_socket_create_source (server->priv->socket, G_IO_IN, NULL);
635 g_source_set_callback (source, (GSourceFunc) read_cb, server, NULL);
636 g_source_attach (source, NULL);
639 g_warning ("Failed to create IPv4 XDMCP socket: %s", error->message);
640 g_clear_error (&error);
641 server->priv->socket6 = open_udp_socket (G_SOCKET_FAMILY_IPV6, server->priv->port, &error);
642 if (server->priv->socket6)
644 source = g_socket_create_source (server->priv->socket6, G_IO_IN, NULL);
645 g_source_set_callback (source, (GSourceFunc) read_cb, server, NULL);
646 g_source_attach (source, NULL);
649 g_warning ("Failed to create IPv6 XDMCP socket: %s", error->message);
650 g_clear_error (&error);
652 if (!server->priv->socket && !server->priv->socket6)
659 xdmcp_server_init (XDMCPServer *server)
661 server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, XDMCP_SERVER_TYPE, XDMCPServerPrivate);
663 server->priv->port = XDM_UDP_PORT;
664 server->priv->hostname = g_strdup ("");
665 server->priv->status = g_strdup ("");
666 server->priv->sessions = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
670 xdmcp_server_finalize (GObject *object)
674 self = XDMCP_SERVER (object);
676 if (self->priv->socket)
677 g_object_unref (self->priv->socket);
678 if (self->priv->socket6)
679 g_object_unref (self->priv->socket6);
680 g_free (self->priv->hostname);
681 g_free (self->priv->status);
682 g_free (self->priv->key);
683 g_hash_table_unref (self->priv->sessions);
685 G_OBJECT_CLASS (xdmcp_server_parent_class)->finalize (object);
689 xdmcp_server_class_init (XDMCPServerClass *klass)
691 GObjectClass *object_class = G_OBJECT_CLASS (klass);
693 object_class->finalize = xdmcp_server_finalize;
695 g_type_class_add_private (klass, sizeof (XDMCPServerPrivate));
697 signals[NEW_SESSION] =
698 g_signal_new ("new-session",
699 G_TYPE_FROM_CLASS (klass),
701 G_STRUCT_OFFSET (XDMCPServerClass, new_session),
702 g_signal_accumulator_true_handled,
704 ldm_marshal_BOOLEAN__OBJECT,
705 G_TYPE_BOOLEAN, 1, XDMCP_SESSION_TYPE);