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