]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/xdmcp-server.c
Merge with trunk
[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 /* Maximum number of milliseconds client will resend manage requests before giving up */
55 #define MANAGE_TIMEOUT 126000
56
57 XDMCPServer *
58 xdmcp_server_new (void)
59 {
60     return g_object_new (XDMCP_SERVER_TYPE, NULL);
61 }
62
63 void
64 xdmcp_server_set_port (XDMCPServer *server, guint port)
65 {
66     g_return_if_fail (server != NULL);
67     server->priv->port = port;
68 }
69
70 guint
71 xdmcp_server_get_port (XDMCPServer *server)
72 {
73     g_return_val_if_fail (server != NULL, 0);
74     return server->priv->port;
75 }
76
77 void
78 xdmcp_server_set_hostname (XDMCPServer *server, const gchar *hostname)
79 {
80     g_return_if_fail (server != NULL);
81
82     g_free (server->priv->hostname);
83     server->priv->hostname = g_strdup (hostname);
84 }
85
86 const gchar *
87 xdmcp_server_get_hostname (XDMCPServer *server)
88 {
89     g_return_val_if_fail (server != NULL, NULL);
90     return server->priv->hostname;
91 }
92
93 void
94 xdmcp_server_set_status (XDMCPServer *server, const gchar *status)
95 {
96     g_return_if_fail (server != NULL);
97
98     g_free (server->priv->status);
99     server->priv->status = g_strdup (status);
100 }
101
102 const gchar *
103 xdmcp_server_get_status (XDMCPServer *server)
104 {
105     g_return_val_if_fail (server != NULL, NULL);
106     return server->priv->status;
107 }
108
109 void
110 xdmcp_server_set_key (XDMCPServer *server, const gchar *key)
111 {
112     g_return_if_fail (server != NULL);
113     g_free (server->priv->key);
114     server->priv->key = g_strdup (key);
115 }
116
117 static gboolean
118 session_timeout_cb (XDMCPSession *session)
119 {
120     g_debug ("Timing out unmanaged session %d", session->priv->id);
121     g_hash_table_remove (session->priv->server->priv->sessions, GINT_TO_POINTER ((gint) session->priv->id));
122     return FALSE;
123 }
124
125 static XDMCPSession *
126 add_session (XDMCPServer *server)
127 {
128     XDMCPSession *session;
129     guint16 id;
130
131     do
132     {
133         id = g_random_int () & 0xFFFFFFFF;
134     } while (g_hash_table_lookup (server->priv->sessions, GINT_TO_POINTER ((gint) id)));
135
136     session = xdmcp_session_new (id);
137     session->priv->server = server;
138     g_hash_table_insert (server->priv->sessions, GINT_TO_POINTER ((gint) id), g_object_ref (session));
139     session->priv->inactive_timeout = g_timeout_add (MANAGE_TIMEOUT, (GSourceFunc) session_timeout_cb, session);
140
141     return session;
142 }
143
144 static XDMCPSession *
145 get_session (XDMCPServer *server, guint16 id)
146 {
147     return g_hash_table_lookup (server->priv->sessions, GINT_TO_POINTER ((gint) id));
148 }
149
150 static void
151 send_packet (GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
152 {
153     guint8 data[1024];
154     gssize n_written;
155
156     g_debug ("Send %s", xdmcp_packet_tostring (packet));
157               
158     n_written = xdmcp_packet_encode (packet, data, 1024);
159     if (n_written < 0)
160       g_critical ("Failed to encode XDMCP packet");
161     else
162     {
163         GError *error = NULL;
164
165         g_socket_send_to (socket, address, (gchar *) data, n_written, NULL, &error);
166         if (error)
167             g_warning ("Error sending packet: %s", error->message);
168         g_clear_error (&error);
169     }
170 }
171
172 static const gchar *
173 get_authentication_name (XDMCPServer *server)
174 {
175     if (server->priv->key)
176         return "XDM-AUTHENTICATION-1";
177     else
178         return "";
179 }
180
181 static void
182 handle_query (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
183 {
184     XDMCPPacket *response;
185     gchar **i;
186     gchar *authentication_name = NULL;
187
188     /* If no authentication requested and we are configured for none then allow */
189     if (packet->Query.authentication_names[0] == NULL && server->priv->key == NULL)
190         authentication_name = "";
191
192     for (i = packet->Query.authentication_names; *i; i++)
193     {
194         if (strcmp (*i, get_authentication_name (server)) == 0 && server->priv->key != NULL)
195         {
196             authentication_name = *i;
197             break;
198         }
199     }
200
201     if (authentication_name)
202     {
203         response = xdmcp_packet_alloc (XDMCP_Willing);
204         response->Willing.authentication_name = g_strdup (authentication_name);
205         response->Willing.hostname = g_strdup (server->priv->hostname);
206         response->Willing.status = g_strdup (server->priv->status);
207     }
208     else
209     {
210         response = xdmcp_packet_alloc (XDMCP_Unwilling);
211         response->Unwilling.hostname = g_strdup (server->priv->hostname);
212         if (server->priv->key)
213             response->Unwilling.status = g_strdup_printf ("No matching authentication, server requires %s", get_authentication_name (server));
214         else
215             response->Unwilling.status = g_strdup ("Server does not support authentication");
216     }
217   
218     send_packet (socket, address, response);
219
220     xdmcp_packet_free (response);
221 }
222
223 static guint8
224 atox (char c)
225 {
226     if (c >= '0' && c <= '9')
227         return c - '0';
228     if (c >= 'a' && c <= 'f')
229         return c - 'a' + 10;
230     if (c >= 'A' && c <= 'F')
231         return c - 'A' + 10;
232     return 0;
233 }
234
235 static void
236 decode_key (const gchar *key, guint8 *data)
237 {
238     gint i;
239
240     memset (data, 0, 8);
241     if (strncmp (key, "0x", 2) == 0 || strncmp (key, "0X", 2) == 0)
242     {
243         for (i = 0; i < 8; i++)
244         {
245             if (key[i*2] == '\0')
246                 break;
247             data[i] |= atox (key[i*2]) << 8;
248             if (key[i*2+1] == '\0')
249                 break;
250             data[i] |= atox (key[i*2+1]);
251         }
252     }
253     else
254     {
255         for (i = 1; i < 8 && key[i-1]; i++)
256            data[i] = key[i-1];
257     }
258 }
259
260 static void
261 handle_request (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
262 {
263     int i;
264     XDMCPPacket *response;
265     XDMCPSession *session;
266     guint8 *authentication_data = NULL;
267     gsize authentication_data_length = 0;
268     gboolean match_authorization = FALSE;
269     gchar *authorization_name;
270     guint8 *authorization_data = NULL;
271     gsize authorization_data_length = 0;
272     guint8 *session_authorization_data = NULL;
273     gsize session_authorization_data_length = 0;
274     gchar **j;
275     guint16 family;
276     GInetAddress *xserver_address = NULL;
277     gchar *display_number;
278     XdmAuthKeyRec rho;
279
280     /* Try and find an IPv6 address */
281     for (i = 0; i < packet->Request.n_connections; i++)
282     {
283         XDMCPConnection *connection = &packet->Request.connections[i];
284         if (connection->type == XAUTH_FAMILY_INTERNET6 && connection->address.length == 16)
285         {
286             family = connection->type;
287             xserver_address = g_inet_address_new_from_bytes (connection->address.data, G_SOCKET_FAMILY_IPV6);
288
289             /* We can't use link-local addresses, as we need to know what interface it is on */
290             if (g_inet_address_get_is_link_local (xserver_address))
291             {
292                 g_object_unref (xserver_address);
293                 xserver_address = NULL;
294             }
295             else
296                 break;
297         }
298     }
299
300     /* If no IPv6 address, then try and find an IPv4 one */
301     if (!xserver_address)
302     {
303         for (i = 0; i < packet->Request.n_connections; i++)
304         {
305             XDMCPConnection *connection = &packet->Request.connections[i];
306             if (connection->type == XAUTH_FAMILY_INTERNET && connection->address.length == 4)
307             {
308                 family = connection->type;
309                 xserver_address = g_inet_address_new_from_bytes (connection->address.data, G_SOCKET_FAMILY_IPV4);
310                 break;
311             }
312         }
313     }
314
315     /* Decline if haven't got an address we can connect on */
316     if (!xserver_address)
317     {
318         response = xdmcp_packet_alloc (XDMCP_Decline);
319         response->Decline.status = g_strdup ("No valid address found");
320         response->Decline.authentication_name = g_strdup (packet->Request.authentication_name);
321         response->Decline.authentication_data.data = authentication_data;
322         response->Decline.authentication_data.length = authentication_data_length;
323         send_packet (socket, address, response);
324         xdmcp_packet_free (response);
325         return;
326     }
327   
328     /* Must be using our authentication scheme */
329     if (strcmp (packet->Request.authentication_name, get_authentication_name (server)) != 0)
330     {
331         response = xdmcp_packet_alloc (XDMCP_Decline);
332         if (server->priv->key)
333             response->Decline.status = g_strdup_printf ("Server only supports %s authentication", get_authentication_name (server));
334         else
335             response->Decline.status = g_strdup ("Server does not support authentication");
336         response->Decline.authentication_name = g_strdup ("");
337         send_packet (socket, address, response);
338         xdmcp_packet_free (response);
339         return;
340     }
341
342     /* Perform requested authentication */
343     if (server->priv->key)
344     {
345         guint8 input[8], key[8];
346
347         memset (input, 0, 8);
348         memcpy (input, packet->Request.authentication_data.data, packet->Request.authentication_data.length > 8 ? 8 : packet->Request.authentication_data.length);
349
350         /* Setup key */
351         decode_key (server->priv->key, key);
352
353         /* Decode message from server */
354         authentication_data = g_malloc (sizeof (guint8) * 8);
355         authentication_data_length = 8;
356
357         XdmcpUnwrap (input, key, rho.data, authentication_data_length);
358         XdmcpIncrementKey (&rho);
359         XdmcpWrap (rho.data, key, authentication_data, authentication_data_length);
360
361         authorization_name = g_strdup ("XDM-AUTHORIZATION-1");
362     }
363     else
364         authorization_name = g_strdup ("MIT-MAGIC-COOKIE-1");
365
366     /* Check if they support our authorization */
367     for (j = packet->Request.authorization_names; *j; j++)
368     {
369         if (strcmp (*j, authorization_name) == 0)
370         {
371              match_authorization = TRUE;
372              break;
373         }
374     }
375
376     /* Decline if don't support out authorization */
377     if (!match_authorization)
378     {
379         response = xdmcp_packet_alloc (XDMCP_Decline);
380         response->Decline.status = g_strdup_printf ("Server requires %s authorization", authorization_name);
381         g_free (authorization_name);
382         response->Decline.authentication_name = g_strdup (packet->Request.authentication_name);
383         response->Decline.authentication_data.data = authentication_data;
384         response->Decline.authentication_data.length = authentication_data_length;
385         send_packet (socket, address, response);
386         xdmcp_packet_free (response);
387         return;
388     }
389
390     /* Perform requested authorization */
391     if (server->priv->key)
392     {
393         gint i;
394         guint8 key[8], session_key[8];
395
396         /* Setup key */
397         decode_key (server->priv->key, key);
398
399         /* Generate a private session key */
400         // FIXME: Pick a good DES key?
401         session_key[0] = 0;
402         for (i = 1; i < 8; i++)
403             session_key[i] = g_random_int () & 0xFF;
404
405         /* Encrypt the session key and send it to the server */
406         authorization_data = g_malloc (8);
407         authorization_data_length = 8;
408         XdmcpWrap (session_key, key, authorization_data, authorization_data_length);
409
410         /* Authorization data is the number received from the client followed by the private session key */
411         session_authorization_data = g_malloc (16);
412         session_authorization_data_length = 16;
413         XdmcpDecrementKey (&rho);
414         memcpy (session_authorization_data, rho.data, 8);
415         memcpy (session_authorization_data + 8, session_key, 8);
416     }
417     else
418     {
419         XAuthority *auth;
420
421         /* Data is the cookie */
422         auth = xauth_new_cookie (XAUTH_FAMILY_WILD, NULL, 0, "");
423         authorization_data = xauth_copy_authorization_data (auth);
424         authorization_data_length = xauth_get_authorization_data_length (auth);
425         session_authorization_data = xauth_copy_authorization_data (auth);
426         session_authorization_data_length = xauth_get_authorization_data_length (auth);
427
428         g_object_unref (auth);
429     }
430
431     session = add_session (server);
432     session->priv->address = xserver_address;
433     session->priv->display_number = packet->Request.display_number;
434     display_number = g_strdup_printf ("%d", packet->Request.display_number);
435
436     /* We need to check if this is the loopback address and set the authority
437      * for a local connection if this is so as XCB treats "127.0.0.1" as local
438      * always */
439     if (g_inet_address_get_is_loopback (xserver_address))
440     {
441         gchar hostname[1024];
442         gethostname (hostname, 1024);
443
444         session->priv->authority = xauth_new (XAUTH_FAMILY_LOCAL,
445                                               (guint8 *) hostname,
446                                               strlen (hostname),
447                                               display_number,
448                                               authorization_name,
449                                               session_authorization_data,
450                                               session_authorization_data_length);
451     }
452     else
453         session->priv->authority = xauth_new (family,
454                                               g_inet_address_to_bytes (G_INET_ADDRESS (xserver_address)),
455                                               g_inet_address_get_native_size (G_INET_ADDRESS (xserver_address)),
456                                               display_number,
457                                               authorization_name,
458                                               session_authorization_data,
459                                               session_authorization_data_length);
460     g_free (display_number);
461
462     response = xdmcp_packet_alloc (XDMCP_Accept);
463     response->Accept.session_id = xdmcp_session_get_id (session);
464     response->Accept.authentication_name = g_strdup (packet->Request.authentication_name);
465     response->Accept.authentication_data.data = authentication_data;
466     response->Accept.authentication_data.length = authentication_data_length;
467     response->Accept.authorization_name = authorization_name;
468     response->Accept.authorization_data.data = authorization_data;
469     response->Accept.authorization_data.length = authorization_data_length;
470     send_packet (socket, address, response);
471     xdmcp_packet_free (response);
472 }
473
474 static void
475 handle_manage (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
476 {
477     XDMCPSession *session;
478     gboolean result;
479
480     session = get_session (server, packet->Manage.session_id);
481     if (!session)
482     {
483         XDMCPPacket *response;
484
485         response = xdmcp_packet_alloc (XDMCP_Refuse);
486         response->Refuse.session_id = packet->Manage.session_id;
487         send_packet (socket, address, response);
488         xdmcp_packet_free (response);
489
490         return;
491     }
492
493     /* Ignore duplicate requests */
494     if (session->priv->started)
495     {
496         if (session->priv->display_number != packet->Manage.display_number ||
497             strcmp (session->priv->display_class, packet->Manage.display_class) != 0)
498             g_debug ("Ignoring duplicate Manage with different data");
499         return;
500     }
501
502     /* Reject if has changed display number */
503     if (packet->Manage.display_number != session->priv->display_number)
504     {
505         XDMCPPacket *response;
506
507         g_debug ("Received Manage for display number %d, but Request was %d", packet->Manage.display_number, session->priv->display_number);
508         response = xdmcp_packet_alloc (XDMCP_Refuse);
509         response->Refuse.session_id = packet->Manage.session_id;
510         send_packet (socket, address, response);
511         xdmcp_packet_free (response);
512     }
513
514     session->priv->display_class = g_strdup (packet->Manage.display_class);
515
516     g_signal_emit (server, signals[NEW_SESSION], 0, session, &result);
517     if (result)
518     {
519         /* Cancel the inactive timer */
520         g_source_remove (session->priv->inactive_timeout);
521
522         session->priv->started = TRUE;
523     }
524     else
525     {
526         XDMCPPacket *response;
527
528         response = xdmcp_packet_alloc (XDMCP_Failed);
529         response->Failed.session_id = packet->Manage.session_id;
530         response->Failed.status = g_strdup_printf ("Failed to connect to display :%d", packet->Manage.display_number);
531         send_packet (socket, address, response);
532         xdmcp_packet_free (response);
533     }
534 }
535
536 static void
537 handle_keep_alive (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
538 {
539     XDMCPPacket *response;
540     XDMCPSession *session;
541     gboolean alive = FALSE;
542
543     session = get_session (server, packet->KeepAlive.session_id);
544     if (session)
545         alive = TRUE; // FIXME: xdmcp_session_get_alive (session);
546
547     response = xdmcp_packet_alloc (XDMCP_Alive);
548     response->Alive.session_running = alive;
549     response->Alive.session_id = alive ? packet->KeepAlive.session_id : 0;
550     send_packet (socket, address, response);
551     xdmcp_packet_free (response);
552 }
553
554 static gboolean
555 read_cb (GSocket *socket, GIOCondition condition, XDMCPServer *server)
556 {
557     GSocketAddress *address;
558     gchar data[1024];
559     GError *error = NULL;
560     gssize n_read;
561
562     n_read = g_socket_receive_from (socket, &address, data, 1024, NULL, &error);
563     if (error)
564         g_warning ("Failed to read from XDMCP socket: %s", error->message);
565     g_clear_error (&error);
566
567     if (n_read > 0)
568     {
569         XDMCPPacket *packet;
570
571         packet = xdmcp_packet_decode ((guint8 *)data, n_read);
572         if (packet)
573         {        
574             g_debug ("Got %s", xdmcp_packet_tostring (packet));
575
576             switch (packet->opcode)
577             {
578             case XDMCP_BroadcastQuery:
579             case XDMCP_Query:
580             case XDMCP_IndirectQuery:
581                 handle_query (server, socket, address, packet);
582                 break;
583             case XDMCP_Request:
584                 handle_request (server, socket, address, packet);
585                 break;
586             case XDMCP_Manage:
587                 handle_manage (server, socket, address, packet);              
588                 break;
589             case XDMCP_KeepAlive:
590                 handle_keep_alive (server, socket, address, packet);
591                 break;
592             default:
593                 g_warning ("Got unexpected XDMCP packet %d", packet->opcode);
594                 break;
595             }
596
597             xdmcp_packet_free (packet);
598         }
599     }
600
601     return TRUE;
602 }
603
604 static GSocket *
605 open_udp_socket (GSocketFamily family, guint port, GError **error)
606 {
607     GSocket *socket;
608     GSocketAddress *address;
609     gboolean result;
610   
611     socket = g_socket_new (family, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, error);
612     if (!socket)
613         return NULL;
614
615     address = g_inet_socket_address_new (g_inet_address_new_any (family), port);
616     result = g_socket_bind (socket, address, TRUE, error);
617     if (!result)
618     {
619         g_object_unref (socket);
620         return NULL;
621     }
622
623     return socket;
624 }
625
626 gboolean
627 xdmcp_server_start (XDMCPServer *server)
628 {
629     GSource *source;
630     GError *error = NULL;
631
632     g_return_val_if_fail (server != NULL, FALSE);
633   
634     server->priv->socket = open_udp_socket (G_SOCKET_FAMILY_IPV4, server->priv->port, &error);
635     if (error)
636         g_warning ("Failed to create IPv4 XDMCP socket: %s", error->message);
637     g_clear_error (&error);
638   
639     if (server->priv->socket)
640     {
641         source = g_socket_create_source (server->priv->socket, G_IO_IN, NULL);
642         g_source_set_callback (source, (GSourceFunc) read_cb, server, NULL);
643         g_source_attach (source, NULL);
644     }
645     
646     server->priv->socket6 = open_udp_socket (G_SOCKET_FAMILY_IPV6, server->priv->port, &error);
647     if (error)
648         g_warning ("Failed to create IPv6 XDMCP socket: %s", error->message);
649     g_clear_error (&error);
650
651     if (server->priv->socket6)
652     {
653         source = g_socket_create_source (server->priv->socket6, G_IO_IN, NULL);
654         g_source_set_callback (source, (GSourceFunc) read_cb, server, NULL);
655         g_source_attach (source, NULL);
656     }
657
658     if (!server->priv->socket && !server->priv->socket6)
659         return FALSE;
660
661     return TRUE;
662 }
663
664 static void
665 xdmcp_server_init (XDMCPServer *server)
666 {
667     server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, XDMCP_SERVER_TYPE, XDMCPServerPrivate);
668
669     server->priv->port = XDM_UDP_PORT;
670     server->priv->hostname = g_strdup ("");
671     server->priv->status = g_strdup ("");
672     server->priv->sessions = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
673 }
674
675 static void
676 xdmcp_server_finalize (GObject *object)
677 {
678     XDMCPServer *self;
679
680     self = XDMCP_SERVER (object);
681   
682     if (self->priv->socket)
683         g_object_unref (self->priv->socket);
684     if (self->priv->socket6)
685         g_object_unref (self->priv->socket6);
686     g_free (self->priv->hostname);
687     g_free (self->priv->status);
688     g_free (self->priv->key);
689     g_hash_table_unref (self->priv->sessions);
690   
691     G_OBJECT_CLASS (xdmcp_server_parent_class)->finalize (object);  
692 }
693
694 static void
695 xdmcp_server_class_init (XDMCPServerClass *klass)
696 {
697     GObjectClass *object_class = G_OBJECT_CLASS (klass);
698
699     object_class->finalize = xdmcp_server_finalize;  
700
701     g_type_class_add_private (klass, sizeof (XDMCPServerPrivate));
702
703     signals[NEW_SESSION] =
704         g_signal_new ("new-session",
705                       G_TYPE_FROM_CLASS (klass),
706                       G_SIGNAL_RUN_LAST,
707                       G_STRUCT_OFFSET (XDMCPServerClass, new_session),
708                       g_signal_accumulator_true_handled,
709                       NULL,
710                       ldm_marshal_BOOLEAN__OBJECT,
711                       G_TYPE_BOOLEAN, 1, XDMCP_SESSION_TYPE);
712 }