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