]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/xdmcp-server.c
Refactored code to replace hardcoded signal identification strings by constants.
[sojka/lightdm.git] / src / xdmcp-server.c
1 /*
2  * Copyright (C) 2010-2011 Robert Ancell.
3  * Author: Robert Ancell <robert.ancell@canonical.com>
4  *
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
9  * license.
10  */
11
12 #include <stdlib.h>
13 #include <string.h>
14 #include <X11/X.h>
15 #define HASXDMAUTH
16 #include <X11/Xdmcp.h>
17 #include <gio/gio.h>
18
19 #include "xdmcp-server.h"
20 #include "xdmcp-protocol.h"
21 #include "xdmcp-session-private.h"
22 #include "x-authority.h"
23
24 enum {
25     NEW_SESSION,
26     LAST_SIGNAL
27 };
28 static guint signals[LAST_SIGNAL] = { 0 };
29
30 struct XDMCPServerPrivate
31 {
32     /* Port to listen on */
33     guint port;
34
35     /* Listening sockets */
36     GSocket *socket, *socket6;
37
38     /* Hostname to report to client */
39     gchar *hostname;
40
41     /* Status to report to clients */
42     gchar *status;
43
44     /* XDM-AUTHENTICATION-1 key */
45     gchar *key;
46
47     /* Active XDMCP sessions */
48     GHashTable *sessions;
49 };
50
51 G_DEFINE_TYPE (XDMCPServer, xdmcp_server, G_TYPE_OBJECT);
52
53 /* Maximum number of milliseconds client will resend manage requests before giving up */
54 #define MANAGE_TIMEOUT 126000
55
56 XDMCPServer *
57 xdmcp_server_new (void)
58 {
59     return g_object_new (XDMCP_SERVER_TYPE, NULL);
60 }
61
62 void
63 xdmcp_server_set_port (XDMCPServer *server, guint port)
64 {
65     g_return_if_fail (server != NULL);
66     server->priv->port = port;
67 }
68
69 guint
70 xdmcp_server_get_port (XDMCPServer *server)
71 {
72     g_return_val_if_fail (server != NULL, 0);
73     return server->priv->port;
74 }
75
76 void
77 xdmcp_server_set_hostname (XDMCPServer *server, const gchar *hostname)
78 {
79     g_return_if_fail (server != NULL);
80
81     g_free (server->priv->hostname);
82     server->priv->hostname = g_strdup (hostname);
83 }
84
85 const gchar *
86 xdmcp_server_get_hostname (XDMCPServer *server)
87 {
88     g_return_val_if_fail (server != NULL, NULL);
89     return server->priv->hostname;
90 }
91
92 void
93 xdmcp_server_set_status (XDMCPServer *server, const gchar *status)
94 {
95     g_return_if_fail (server != NULL);
96
97     g_free (server->priv->status);
98     server->priv->status = g_strdup (status);
99 }
100
101 const gchar *
102 xdmcp_server_get_status (XDMCPServer *server)
103 {
104     g_return_val_if_fail (server != NULL, NULL);
105     return server->priv->status;
106 }
107
108 void
109 xdmcp_server_set_key (XDMCPServer *server, const gchar *key)
110 {
111     g_return_if_fail (server != NULL);
112     g_free (server->priv->key);
113     server->priv->key = g_strdup (key);
114 }
115
116 static gboolean
117 session_timeout_cb (XDMCPSession *session)
118 {
119     session->priv->inactive_timeout = 0;
120
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));
123     return FALSE;
124 }
125
126 static XDMCPSession *
127 add_session (XDMCPServer *server)
128 {
129     XDMCPSession *session;
130     guint16 id;
131
132     do
133     {
134         id = g_random_int () & 0xFFFFFFFF;
135     } while (g_hash_table_lookup (server->priv->sessions, GINT_TO_POINTER ((gint) id)));
136
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);
141
142     return session;
143 }
144
145 static XDMCPSession *
146 get_session (XDMCPServer *server, guint16 id)
147 {
148     return g_hash_table_lookup (server->priv->sessions, GINT_TO_POINTER ((gint) id));
149 }
150
151 static void
152 send_packet (GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
153 {
154     guint8 data[1024];
155     gssize n_written;
156
157     g_debug ("Send %s", xdmcp_packet_tostring (packet));
158
159     n_written = xdmcp_packet_encode (packet, data, 1024);
160     if (n_written < 0)
161       g_critical ("Failed to encode XDMCP packet");
162     else
163     {
164         GError *error = NULL;
165
166         g_socket_send_to (socket, address, (gchar *) data, n_written, NULL, &error);
167         if (error)
168             g_warning ("Error sending packet: %s", error->message);
169         g_clear_error (&error);
170     }
171 }
172
173 static const gchar *
174 get_authentication_name (XDMCPServer *server)
175 {
176     if (server->priv->key)
177         return "XDM-AUTHENTICATION-1";
178     else
179         return "";
180 }
181
182 static void
183 handle_query (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
184 {
185     XDMCPPacket *response;
186     gchar **i;
187     gchar *authentication_name = NULL;
188
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 = "";
192
193     for (i = packet->Query.authentication_names; *i; i++)
194     {
195         if (strcmp (*i, get_authentication_name (server)) == 0 && server->priv->key != NULL)
196         {
197             authentication_name = *i;
198             break;
199         }
200     }
201
202     if (authentication_name)
203     {
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);
208     }
209     else
210     {
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));
215         else
216             response->Unwilling.status = g_strdup ("Server does not support authentication");
217     }
218
219     send_packet (socket, address, response);
220
221     xdmcp_packet_free (response);
222 }
223
224 static guint8
225 atox (char c)
226 {
227     if (c >= '0' && c <= '9')
228         return c - '0';
229     if (c >= 'a' && c <= 'f')
230         return c - 'a' + 10;
231     if (c >= 'A' && c <= 'F')
232         return c - 'A' + 10;
233     return 0;
234 }
235
236 static void
237 decode_key (const gchar *key, guint8 *data)
238 {
239     gint i;
240
241     memset (data, 0, 8);
242     if (strncmp (key, "0x", 2) == 0 || strncmp (key, "0X", 2) == 0)
243     {
244         for (i = 0; i < 8; i++)
245         {
246             if (key[i*2] == '\0')
247                 break;
248             data[i] |= atox (key[i*2]) << 8;
249             if (key[i*2+1] == '\0')
250                 break;
251             data[i] |= atox (key[i*2+1]);
252         }
253     }
254     else
255     {
256         for (i = 1; i < 8 && key[i-1]; i++)
257            data[i] = key[i-1];
258     }
259 }
260
261 static void
262 handle_request (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
263 {
264     int i;
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;
275     gchar **j;
276     guint16 family;
277     GInetAddress *x_server_address = NULL;
278     gchar *display_number;
279     XdmAuthKeyRec rho;
280
281     /* Try and find an IPv6 address */
282     for (i = 0; i < packet->Request.n_connections; i++)
283     {
284         XDMCPConnection *connection = &packet->Request.connections[i];
285         if (connection->type == XAUTH_FAMILY_INTERNET6 && connection->address.length == 16)
286         {
287             family = connection->type;
288             x_server_address = g_inet_address_new_from_bytes (connection->address.data, G_SOCKET_FAMILY_IPV6);
289
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))
292             {
293                 g_object_unref (x_server_address);
294                 x_server_address = NULL;
295             }
296             else
297                 break;
298         }
299     }
300
301     /* If no IPv6 address, then try and find an IPv4 one */
302     if (!x_server_address)
303     {
304         for (i = 0; i < packet->Request.n_connections; i++)
305         {
306             XDMCPConnection *connection = &packet->Request.connections[i];
307             if (connection->type == XAUTH_FAMILY_INTERNET && connection->address.length == 4)
308             {
309                 family = connection->type;
310                 x_server_address = g_inet_address_new_from_bytes (connection->address.data, G_SOCKET_FAMILY_IPV4);
311                 break;
312             }
313         }
314     }
315
316     /* Decline if haven't got an address we can connect on */
317     if (!x_server_address)
318     {
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);
326         return;
327     }
328
329     /* Must be using our authentication scheme */
330     if (strcmp (packet->Request.authentication_name, get_authentication_name (server)) != 0)
331     {
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));
335         else
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);
340         return;
341     }
342
343     /* Perform requested authentication */
344     if (server->priv->key)
345     {
346         guint8 input[8], key[8];
347
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);
350
351         /* Setup key */
352         decode_key (server->priv->key, key);
353
354         /* Decode message from server */
355         authentication_data = g_malloc (sizeof (guint8) * 8);
356         authentication_data_length = 8;
357
358         XdmcpUnwrap (input, key, rho.data, authentication_data_length);
359         XdmcpIncrementKey (&rho);
360         XdmcpWrap (rho.data, key, authentication_data, authentication_data_length);
361
362         authorization_name = g_strdup ("XDM-AUTHORIZATION-1");
363     }
364     else
365         authorization_name = g_strdup ("MIT-MAGIC-COOKIE-1");
366
367     /* Check if they support our authorization */
368     for (j = packet->Request.authorization_names; *j; j++)
369     {
370         if (strcmp (*j, authorization_name) == 0)
371         {
372              match_authorization = TRUE;
373              break;
374         }
375     }
376
377     /* Decline if don't support out authorization */
378     if (!match_authorization)
379     {
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);
388         return;
389     }
390
391     /* Perform requested authorization */
392     if (server->priv->key)
393     {
394         gint i;
395         guint8 key[8], session_key[8];
396
397         /* Setup key */
398         decode_key (server->priv->key, key);
399
400         /* Generate a private session key */
401         // FIXME: Pick a good DES key?
402         session_key[0] = 0;
403         for (i = 1; i < 8; i++)
404             session_key[i] = g_random_int () & 0xFF;
405
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);
410
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);
417     }
418     else
419     {
420         XAuthority *auth;
421
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);
428
429         g_object_unref (auth);
430     }
431
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);
436
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
439      * always */
440     if (g_inet_address_get_is_loopback (x_server_address))
441     {
442         gchar hostname[1024];
443         gethostname (hostname, 1024);
444
445         session->priv->authority = x_authority_new (XAUTH_FAMILY_LOCAL,
446                                               (guint8 *) hostname,
447                                               strlen (hostname),
448                                               display_number,
449                                               authorization_name,
450                                               session_authorization_data,
451                                               session_authorization_data_length);
452     }
453     else
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)),
457                                               display_number,
458                                               authorization_name,
459                                               session_authorization_data,
460                                               session_authorization_data_length);
461     g_free (display_number);
462
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);
473 }
474
475 static void
476 handle_manage (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
477 {
478     XDMCPSession *session;
479     gboolean result;
480
481     session = get_session (server, packet->Manage.session_id);
482     if (!session)
483     {
484         XDMCPPacket *response;
485
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);
490
491         return;
492     }
493
494     /* Ignore duplicate requests */
495     if (session->priv->started)
496     {
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");
500         return;
501     }
502
503     /* Reject if has changed display number */
504     if (packet->Manage.display_number != session->priv->display_number)
505     {
506         XDMCPPacket *response;
507
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);
513     }
514
515     session->priv->display_class = g_strdup (packet->Manage.display_class);
516
517     g_signal_emit (server, signals[NEW_SESSION], 0, session, &result);
518     if (result)
519     {
520         /* Cancel the inactive timer */
521         if (session->priv->inactive_timeout)
522             g_source_remove (session->priv->inactive_timeout);
523
524         session->priv->started = TRUE;
525     }
526     else
527     {
528         XDMCPPacket *response;
529
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);
535     }
536 }
537
538 static void
539 handle_keep_alive (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
540 {
541     XDMCPPacket *response;
542     XDMCPSession *session;
543     gboolean alive = FALSE;
544
545     session = get_session (server, packet->KeepAlive.session_id);
546     if (session)
547         alive = TRUE; // FIXME: xdmcp_session_get_alive (session);
548
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);
554 }
555
556 static gboolean
557 read_cb (GSocket *socket, GIOCondition condition, XDMCPServer *server)
558 {
559     GSocketAddress *address;
560     gchar data[1024];
561     GError *error = NULL;
562     gssize n_read;
563
564     n_read = g_socket_receive_from (socket, &address, data, 1024, NULL, &error);
565     if (error)
566         g_warning ("Failed to read from XDMCP socket: %s", error->message);
567     g_clear_error (&error);
568
569     if (n_read > 0)
570     {
571         XDMCPPacket *packet;
572
573         packet = xdmcp_packet_decode ((guint8 *)data, n_read);
574         if (packet)
575         {
576             g_debug ("Got %s", xdmcp_packet_tostring (packet));
577
578             switch (packet->opcode)
579             {
580             case XDMCP_BroadcastQuery:
581             case XDMCP_Query:
582             case XDMCP_IndirectQuery:
583                 handle_query (server, socket, address, packet);
584                 break;
585             case XDMCP_Request:
586                 handle_request (server, socket, address, packet);
587                 break;
588             case XDMCP_Manage:
589                 handle_manage (server, socket, address, packet);
590                 break;
591             case XDMCP_KeepAlive:
592                 handle_keep_alive (server, socket, address, packet);
593                 break;
594             default:
595                 g_warning ("Got unexpected XDMCP packet %d", packet->opcode);
596                 break;
597             }
598
599             xdmcp_packet_free (packet);
600         }
601     }
602
603     return TRUE;
604 }
605
606 static GSocket *
607 open_udp_socket (GSocketFamily family, guint port, GError **error)
608 {
609     GSocket *socket;
610     GSocketAddress *address;
611     gboolean result;
612
613     socket = g_socket_new (family, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, error);
614     if (!socket)
615         return NULL;
616
617     address = g_inet_socket_address_new (g_inet_address_new_any (family), port);
618     result = g_socket_bind (socket, address, TRUE, error);
619     if (!result)
620     {
621         g_object_unref (socket);
622         return NULL;
623     }
624
625     return socket;
626 }
627
628 gboolean
629 xdmcp_server_start (XDMCPServer *server)
630 {
631     GSource *source;
632     GError *error = NULL;
633
634     g_return_val_if_fail (server != NULL, FALSE);
635
636     server->priv->socket = open_udp_socket (G_SOCKET_FAMILY_IPV4, server->priv->port, &error);
637     if (error)
638         g_warning ("Failed to create IPv4 XDMCP socket: %s", error->message);
639     g_clear_error (&error);
640
641     if (server->priv->socket)
642     {
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);
646     }
647
648     server->priv->socket6 = open_udp_socket (G_SOCKET_FAMILY_IPV6, server->priv->port, &error);
649     if (error)
650         g_warning ("Failed to create IPv6 XDMCP socket: %s", error->message);
651     g_clear_error (&error);
652
653     if (server->priv->socket6)
654     {
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);
658     }
659
660     if (!server->priv->socket && !server->priv->socket6)
661         return FALSE;
662
663     return TRUE;
664 }
665
666 static void
667 xdmcp_server_init (XDMCPServer *server)
668 {
669     server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, XDMCP_SERVER_TYPE, XDMCPServerPrivate);
670
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);
675 }
676
677 static void
678 xdmcp_server_finalize (GObject *object)
679 {
680     XDMCPServer *self;
681
682     self = XDMCP_SERVER (object);
683
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);
692
693     G_OBJECT_CLASS (xdmcp_server_parent_class)->finalize (object);
694 }
695
696 static void
697 xdmcp_server_class_init (XDMCPServerClass *klass)
698 {
699     GObjectClass *object_class = G_OBJECT_CLASS (klass);
700
701     object_class->finalize = xdmcp_server_finalize;
702
703     g_type_class_add_private (klass, sizeof (XDMCPServerPrivate));
704
705     signals[NEW_SESSION] =
706         g_signal_new (XDMCP_SERVER_SIGNAL_NEW_SESSION,
707                       G_TYPE_FROM_CLASS (klass),
708                       G_SIGNAL_RUN_LAST,
709                       G_STRUCT_OFFSET (XDMCPServerClass, new_session),
710                       g_signal_accumulator_true_handled,
711                       NULL,
712                       NULL,
713                       G_TYPE_BOOLEAN, 1, XDMCP_SESSION_TYPE);
714 }