]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/xdmcp-server.c
Ignore IPv6 link-local addresses, we can't use them without knowing the interface...
[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 #include "ldm-marshal.h"
19
20 #include "xdmcp-server.h"
21 #include "xdmcp-protocol.h"
22 #include "xdmcp-session-private.h"
23 #include "xauthority.h"
24
25 enum {
26     NEW_SESSION,
27     LAST_SIGNAL
28 };
29 static guint signals[LAST_SIGNAL] = { 0 };
30
31 struct XDMCPServerPrivate
32 {
33     /* Port to listen on */
34     guint port;
35
36     /* Listening sockets */
37     GSocket *socket, *socket6;
38
39     /* Hostname to report to client */
40     gchar *hostname;
41
42     /* Status to report to clients */
43     gchar *status;
44
45     /* XDM-AUTHENTICATION-1 key */
46     gchar *key;
47
48     /* Active XDMCP sessions */
49     GHashTable *sessions;
50 };
51
52 G_DEFINE_TYPE (XDMCPServer, xdmcp_server, G_TYPE_OBJECT);
53
54 XDMCPServer *
55 xdmcp_server_new (void)
56 {
57     return g_object_new (XDMCP_SERVER_TYPE, NULL);
58 }
59
60 void
61 xdmcp_server_set_port (XDMCPServer *server, guint port)
62 {
63     g_return_if_fail (server != NULL);
64     server->priv->port = port;
65 }
66
67 guint
68 xdmcp_server_get_port (XDMCPServer *server)
69 {
70     g_return_val_if_fail (server != NULL, 0);
71     return server->priv->port;
72 }
73
74 void
75 xdmcp_server_set_hostname (XDMCPServer *server, const gchar *hostname)
76 {
77     g_return_if_fail (server != NULL);
78
79     g_free (server->priv->hostname);
80     server->priv->hostname = g_strdup (hostname);
81 }
82
83 const gchar *
84 xdmcp_server_get_hostname (XDMCPServer *server)
85 {
86     g_return_val_if_fail (server != NULL, NULL);
87     return server->priv->hostname;
88 }
89
90 void
91 xdmcp_server_set_status (XDMCPServer *server, const gchar *status)
92 {
93     g_return_if_fail (server != NULL);
94
95     g_free (server->priv->status);
96     server->priv->status = g_strdup (status);
97 }
98
99 const gchar *
100 xdmcp_server_get_status (XDMCPServer *server)
101 {
102     g_return_val_if_fail (server != NULL, NULL);
103     return server->priv->status;
104 }
105
106 void
107 xdmcp_server_set_key (XDMCPServer *server, const gchar *key)
108 {
109     g_return_if_fail (server != NULL);
110     g_free (server->priv->key);
111     server->priv->key = g_strdup (key);
112 }
113
114 static gboolean
115 session_timeout_cb (XDMCPSession *session)
116 {
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));
119     return FALSE;
120 }
121
122 static XDMCPSession *
123 add_session (XDMCPServer *server)
124 {
125     XDMCPSession *session;
126     guint16 id;
127
128     do
129     {
130         id = g_random_int () & 0xFFFFFFFF;
131     } while (g_hash_table_lookup (server->priv->sessions, GINT_TO_POINTER ((gint) id)));
132
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);
137
138     return session;
139 }
140
141 static XDMCPSession *
142 get_session (XDMCPServer *server, guint16 id)
143 {
144     return g_hash_table_lookup (server->priv->sessions, GINT_TO_POINTER ((gint) id));
145 }
146
147 static void
148 send_packet (GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
149 {
150     guint8 data[1024];
151     gssize n_written;
152
153     g_debug ("Send %s", xdmcp_packet_tostring (packet));
154               
155     n_written = xdmcp_packet_encode (packet, data, 1024);
156     if (n_written < 0)
157       g_critical ("Failed to encode XDMCP packet");
158     else
159     {
160         GError *error = NULL;
161
162         if (g_socket_send_to (socket, address, (gchar *) data, n_written, NULL, &error) < 0)
163             g_warning ("Error sending packet: %s", error->message);
164
165         g_clear_error (&error);
166     }
167 }
168
169 static const gchar *
170 get_authentication_name (XDMCPServer *server)
171 {
172     if (server->priv->key)
173         return "XDM-AUTHENTICATION-1";
174     else
175         return "";
176 }
177
178 static void
179 handle_query (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
180 {
181     XDMCPPacket *response;
182     gchar **i;
183     gchar *authentication_name = NULL;
184
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 = "";
188
189     for (i = packet->Query.authentication_names; *i; i++)
190     {
191         if (strcmp (*i, get_authentication_name (server)) == 0 && server->priv->key != NULL)
192         {
193             authentication_name = *i;
194             break;
195         }
196     }
197
198     if (authentication_name)
199     {
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);
204     }
205     else
206     {
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));
211         else
212             response->Unwilling.status = g_strdup ("Server does not support authentication");
213     }
214   
215     send_packet (socket, address, response);
216
217     xdmcp_packet_free (response);
218 }
219
220 static guint8
221 atox (char c)
222 {
223     if (c >= '0' && c <= '9')
224         return c - '0';
225     if (c >= 'a' && c <= 'f')
226         return c - 'a' + 10;
227     if (c >= 'A' && c <= 'F')
228         return c - 'A' + 10;
229     return 0;
230 }
231
232 static void
233 decode_key (const gchar *key, guint8 *data)
234 {
235     gint i;
236
237     memset (data, 0, sizeof (data));
238     if (strncmp (key, "0x", 2) == 0 || strncmp (key, "0X", 2) == 0)
239     {
240         for (i = 0; i < 8; i++)
241         {
242             if (key[i*2] == '\0')
243                 break;
244             data[i] |= atox (key[i*2]) << 8;
245             if (key[i*2+1] == '\0')
246                 break;
247             data[i] |= atox (key[i*2+1]);
248         }
249     }
250     else
251     {
252         for (i = 1; i < 8 && key[i-1]; i++)
253            data[i] = key[i-1];
254     }
255 }
256
257 static void
258 handle_request (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
259 {
260     int i;
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;
271     gchar **j;
272     guint16 family;
273     GInetAddress *xserver_address = NULL;
274     gchar *display_number;
275     XdmAuthKeyRec rho;
276
277     /* Try and find an IPv6 address */
278     for (i = 0; i < packet->Request.n_connections; i++)
279     {
280         XDMCPConnection *connection = &packet->Request.connections[i];
281         if (connection->type == XAUTH_FAMILY_INTERNET6 && connection->address.length == 16)
282         {
283             family = connection->type;
284             xserver_address = g_inet_address_new_from_bytes (connection->address.data, G_SOCKET_FAMILY_IPV6);
285
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))
288             {
289                 g_object_unref (xserver_address);
290                 xserver_address = NULL;
291             }
292             else
293                 break;
294         }
295     }
296
297     /* If no IPv6 address, then try and find an IPv4 one */
298     if (!xserver_address)
299     {
300         for (i = 0; i < packet->Request.n_connections; i++)
301         {
302             XDMCPConnection *connection = &packet->Request.connections[i];
303             if (connection->type == XAUTH_FAMILY_INTERNET && connection->address.length == 4)
304             {
305                 family = connection->type;
306                 xserver_address = g_inet_address_new_from_bytes (connection->address.data, G_SOCKET_FAMILY_IPV4);
307                 break;
308             }
309         }
310     }
311
312     /* Decline if haven't got an address we can connect on */
313     if (!xserver_address)
314     {
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);
322         return;
323     }
324   
325     /* Must be using our authentication scheme */
326     if (strcmp (packet->Request.authentication_name, get_authentication_name (server)) != 0)
327     {
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));
331         else
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);
336         return;
337     }
338
339     /* Perform requested authentication */
340     if (server->priv->key)
341     {
342         guint8 input[8], key[8];
343
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);
346
347         /* Setup key */
348         decode_key (server->priv->key, key);
349
350         /* Decode message from server */
351         authentication_data = g_malloc (sizeof (guint8) * 8);
352         authentication_data_length = 8;
353
354         XdmcpUnwrap (input, key, rho.data, authentication_data_length);
355         XdmcpIncrementKey (&rho);
356         XdmcpWrap (rho.data, key, authentication_data, authentication_data_length);
357
358         authorization_name = g_strdup ("XDM-AUTHORIZATION-1");
359     }
360     else
361         authorization_name = g_strdup ("MIT-MAGIC-COOKIE-1");
362
363     /* Check if they support our authorization */
364     for (j = packet->Request.authorization_names; *j; j++)
365     {
366         if (strcmp (*j, authorization_name) == 0)
367         {
368              match_authorization = TRUE;
369              break;
370         }
371     }
372
373     /* Decline if don't support out authorization */
374     if (!match_authorization)
375     {
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);
384         return;
385     }
386
387     /* Perform requested authorization */
388     if (server->priv->key)
389     {
390         gint i;
391         guint8 key[8], session_key[8];
392
393         /* Setup key */
394         decode_key (server->priv->key, key);
395
396         /* Generate a private session key */
397         // FIXME: Pick a good DES key?
398         session_key[0] = 0;
399         for (i = 1; i < 8; i++)
400             session_key[i] = g_random_int () & 0xFF;
401
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);
406
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);
413     }
414     else
415     {
416         XAuthority *auth;
417
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);
424
425         g_object_unref (auth);
426     }
427
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);
432
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
435      * always */
436     if (g_inet_address_get_is_loopback (xserver_address))
437     {
438         gchar hostname[1024];
439         gethostname (hostname, 1024);
440
441         session->priv->authority = xauth_new (XAUTH_FAMILY_LOCAL,
442                                               (guint8 *) hostname,
443                                               strlen (hostname),
444                                               display_number,
445                                               authorization_name,
446                                               session_authorization_data,
447                                               session_authorization_data_length);
448     }
449     else
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)),
453                                               display_number,
454                                               authorization_name,
455                                               session_authorization_data,
456                                               session_authorization_data_length);
457     g_free (display_number);
458
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);
469 }
470
471 static void
472 handle_manage (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
473 {
474     XDMCPSession *session;
475     gboolean result;
476
477     session = get_session (server, packet->Manage.session_id);
478     if (!session)
479     {
480         XDMCPPacket *response;
481
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);
486
487         return;
488     }
489
490     /* Ignore duplicate requests */
491     if (session->priv->started)
492     {
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");
496         return;
497     }
498
499     /* Reject if has changed display number */
500     if (packet->Manage.display_number != session->priv->display_number)
501     {
502         XDMCPPacket *response;
503
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);
509     }
510
511     session->priv->display_class = g_strdup (packet->Manage.display_class);
512
513     g_signal_emit (server, signals[NEW_SESSION], 0, session, &result);
514     if (result)
515     {
516         /* Cancel the inactive timer */
517         g_source_remove (session->priv->inactive_timeout);
518
519         session->priv->started = TRUE;
520     }
521     else
522     {
523         XDMCPPacket *response;
524
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);
530     }
531 }
532
533 static void
534 handle_keep_alive (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
535 {
536     XDMCPPacket *response;
537     XDMCPSession *session;
538     gboolean alive = FALSE;
539
540     session = get_session (server, packet->KeepAlive.session_id);
541     if (session)
542         alive = TRUE; // FIXME: xdmcp_session_get_alive (session);
543
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);
549 }
550
551 static gboolean
552 read_cb (GSocket *socket, GIOCondition condition, XDMCPServer *server)
553 {
554     GSocketAddress *address;
555     gchar data[1024];
556     GError *error = NULL;
557     gssize n_read;
558
559     n_read = g_socket_receive_from (socket, &address, data, 1024, NULL, &error);
560     if (n_read > 0)
561     {
562         XDMCPPacket *packet;
563
564         packet = xdmcp_packet_decode ((guint8 *)data, n_read);
565         if (packet)
566         {        
567             g_debug ("Got %s", xdmcp_packet_tostring (packet));
568
569             switch (packet->opcode)
570             {
571             case XDMCP_BroadcastQuery:
572             case XDMCP_Query:
573             case XDMCP_IndirectQuery:
574                 handle_query (server, socket, address, packet);
575                 break;
576             case XDMCP_Request:
577                 handle_request (server, socket, address, packet);
578                 break;
579             case XDMCP_Manage:
580                 handle_manage (server, socket, address, packet);              
581                 break;
582             case XDMCP_KeepAlive:
583                 handle_keep_alive (server, socket, address, packet);
584                 break;
585             default:
586                 g_warning ("Got unexpected XDMCP packet %d", packet->opcode);
587                 break;
588             }
589
590             xdmcp_packet_free (packet);
591         }
592     }
593     else
594         g_warning ("Failed to read from XDMCP socket: %s", error->message);
595
596     g_clear_error (&error);
597
598     return TRUE;
599 }
600
601 static GSocket *
602 open_udp_socket (GSocketFamily family, guint port, GError **error)
603 {
604     GSocket *socket;
605     GSocketAddress *address;
606     gboolean result;
607   
608     socket = g_socket_new (family, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, error);
609     if (!socket)
610         return NULL;
611
612     address = g_inet_socket_address_new (g_inet_address_new_any (family), port);
613     result = g_socket_bind (socket, address, TRUE, error);
614     if (!result)
615     {
616         g_object_unref (socket);
617         return NULL;
618     }
619
620     return socket;
621 }
622
623 gboolean
624 xdmcp_server_start (XDMCPServer *server)
625 {
626     GSource *source;
627     GError *error = NULL;
628
629     g_return_val_if_fail (server != NULL, FALSE);
630   
631     server->priv->socket = open_udp_socket (G_SOCKET_FAMILY_IPV4, server->priv->port, &error);
632     if (server->priv->socket)
633     {
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);
637     }
638     else
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)
643     {
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);
647     }
648     else
649         g_warning ("Failed to create IPv6 XDMCP socket: %s", error->message);
650     g_clear_error (&error);
651
652     if (!server->priv->socket && !server->priv->socket6)
653         return FALSE;
654
655     return TRUE;
656 }
657
658 static void
659 xdmcp_server_init (XDMCPServer *server)
660 {
661     server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, XDMCP_SERVER_TYPE, XDMCPServerPrivate);
662
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);
667 }
668
669 static void
670 xdmcp_server_finalize (GObject *object)
671 {
672     XDMCPServer *self;
673
674     self = XDMCP_SERVER (object);
675   
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);
684   
685     G_OBJECT_CLASS (xdmcp_server_parent_class)->finalize (object);  
686 }
687
688 static void
689 xdmcp_server_class_init (XDMCPServerClass *klass)
690 {
691     GObjectClass *object_class = G_OBJECT_CLASS (klass);
692
693     object_class->finalize = xdmcp_server_finalize;  
694
695     g_type_class_add_private (klass, sizeof (XDMCPServerPrivate));
696
697     signals[NEW_SESSION] =
698         g_signal_new ("new-session",
699                       G_TYPE_FROM_CLASS (klass),
700                       G_SIGNAL_RUN_LAST,
701                       G_STRUCT_OFFSET (XDMCPServerClass, new_session),
702                       g_signal_accumulator_true_handled,
703                       NULL,
704                       ldm_marshal_BOOLEAN__OBJECT,
705                       G_TYPE_BOOLEAN, 1, XDMCP_SESSION_TYPE);
706 }