]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/xdmcp-server.c
Add g_return_if_fail lines to more modules
[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     /* Must be using our authentication scheme */
300     if (strcmp (packet->Request.authentication_name, server->priv->authentication_name) != 0)
301     {
302         response = xdmcp_packet_alloc (XDMCP_Decline);
303         if (strcmp (server->priv->authentication_name, "") == 0)
304             response->Decline.status = g_strdup ("Server does not support authentication");
305         else
306             response->Decline.status = g_strdup_printf ("Server only supports %s authentication", server->priv->authentication_name);
307         response->Decline.authentication_name = g_strdup ("");
308         send_packet (socket, address, response);
309         xdmcp_packet_free (response);
310         return;
311     }
312
313     /* Perform requested authentication */
314     if (strcmp (server->priv->authentication_name, "XDM-AUTHENTICATION-1") == 0)
315     {
316         guchar input[8], key[8];
317
318         memset (input, 0, 8);
319         memcpy (input, packet->Request.authentication_data.data, packet->Request.authentication_data.length > 8 ? 8 : packet->Request.authentication_data.length);
320
321         /* Setup key */
322         memset (key, 0, 8);
323         memcpy (key, server->priv->authentication_data, server->priv->authentication_data_length > 8 ? 8 : server->priv->authentication_data_length);
324
325         /* Decode message from server */
326         authentication_data = g_malloc (sizeof (guchar) * 8);
327         authentication_data_length = 8;
328
329         XdmcpUnwrap (input, key, rho.data, authentication_data_length);
330         XdmcpIncrementKey (&rho);
331         XdmcpWrap (rho.data, key, authentication_data, authentication_data_length);
332     }
333
334     /* Check if they support our authorization */
335     for (j = packet->Request.authorization_names; *j; j++)
336     {
337         if (strcmp (*j, server->priv->authorization_name) == 0)
338         {
339              match_authorization = TRUE;
340              break;
341         }
342     }
343   
344     if (!match_authorization)
345     {
346         response = xdmcp_packet_alloc (XDMCP_Decline);
347         if (strcmp (server->priv->authorization_name, "") == 0)
348             response->Decline.status = g_strdup ("Server does not support authorization");
349         else
350             response->Decline.status = g_strdup_printf ("Server only supports %s authorization", server->priv->authorization_name);
351         response->Decline.authentication_name = g_strdup (packet->Request.authentication_name);
352         response->Decline.authentication_data.data = authentication_data;
353         response->Decline.authentication_data.length = authentication_data_length;
354         send_packet (socket, address, response);
355         xdmcp_packet_free (response);
356         return;
357     }
358
359     /* Perform requested authorization */
360     if (strcmp (server->priv->authorization_name, "MIT-MAGIC-COOKIE-1") == 0)
361     {
362         XAuthorization *auth;
363
364         /* Data is the cookie */
365         auth = xauth_new_cookie ();
366         authorization_data = xauth_copy_authorization_data (auth);
367         authorization_data_length = xauth_get_authorization_data_length (auth);
368         session_authorization_data = xauth_copy_authorization_data (auth);
369         session_authorization_data_length = xauth_get_authorization_data_length (auth);
370     }
371     else if (strcmp (server->priv->authorization_name, "XDM-AUTHORIZATION-1") == 0)
372     {
373         gint i;
374         guchar key[8], session_key[8];
375
376         /* Setup key */
377         memset (key, 0, 8);
378         memcpy (key, server->priv->authentication_data, server->priv->authentication_data_length > 8 ? 8 : server->priv->authentication_data_length);
379
380         /* Generate a private session key */
381         // FIXME: Pick a good DES key?
382         session_key[0] = 0;
383         for (i = 1; i < 8; i++)
384             session_key[i] = g_random_int () & 0xFF;
385
386         /* Encrypt the session key and send it to the server */
387         authorization_data = g_malloc (8);
388         authorization_data_length = 8;
389         XdmcpWrap (session_key, key, authorization_data, authorization_data_length);
390
391         /* Authorization data is the number received from the client followed by the private session key */
392         session_authorization_data = g_malloc (16);
393         session_authorization_data_length = 16;
394         XdmcpDecrementKey (&rho);
395         memcpy (session_authorization_data, rho.data, 8);
396         memcpy (session_authorization_data + 8, session_key, 8);
397     }
398
399     for (i = 0; i < packet->Request.n_connections; i++)
400     {
401         XDMCPConnection *connection;
402
403         connection = &packet->Request.connections[i];
404         switch (connection->type)
405         {
406         case FamilyInternet:
407             if (connection->address.length == 4)
408                 address4 = g_inet_address_new_from_bytes (connection->address.data, G_SOCKET_FAMILY_IPV4);
409             break;
410         case FamilyInternet6:
411             if (connection->address.length == 16)
412                 address6 = g_inet_address_new_from_bytes (connection->address.data, G_SOCKET_FAMILY_IPV6);          
413             break;
414         }
415     }
416
417     if (!address4) // FIXME: && !address6)
418     {
419         response = xdmcp_packet_alloc (XDMCP_Decline);
420         response->Decline.status = g_strdup ("No valid address found");
421         response->Decline.authentication_name = g_strdup (packet->Request.authentication_name);
422         response->Decline.authentication_data.data = authentication_data;
423         response->Decline.authentication_data.length = authentication_data_length;
424         send_packet (socket, address, response);
425         xdmcp_packet_free (response);
426         return;
427     }
428
429     session = add_session (server);
430     session->priv->address = address4;
431     session->priv->address6 = address6;
432     session->priv->authorization_name = g_strdup (server->priv->authorization_name);
433     session->priv->authorization_data = session_authorization_data;
434     session->priv->authorization_data_length = session_authorization_data_length;
435
436     response = xdmcp_packet_alloc (XDMCP_Accept);
437     response->Accept.session_id = xdmcp_session_get_id (session);
438     response->Accept.authentication_name = g_strdup (packet->Request.authentication_name);
439     response->Accept.authentication_data.data = authentication_data;
440     response->Accept.authentication_data.length = authentication_data_length;
441     response->Accept.authorization_name = g_strdup (server->priv->authorization_name);
442     response->Accept.authorization_data.data = authorization_data;
443     response->Accept.authorization_data.length = authorization_data_length;
444     send_packet (socket, address, response);
445     xdmcp_packet_free (response);
446 }
447
448 static void
449 handle_manage (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
450 {
451     XDMCPSession *session;
452
453     session = get_session (server, packet->Manage.session_id);
454     if (session)
455     {
456         gboolean result;
457
458         /* Ignore duplicate requests */
459         if (session->priv->started)
460         {
461             if (session->priv->display_number != packet->Manage.display_number ||
462                 strcmp (session->priv->display_class, packet->Manage.display_class) != 0)
463                 g_warning ("Duplicate Manage received with different data");
464             return;
465         }
466
467         session->priv->display_number = packet->Manage.display_number;  
468         session->priv->display_class = g_strdup (packet->Manage.display_class);
469
470         g_signal_emit (server, signals[NEW_SESSION], 0, session, &result);
471         if (result)
472         {
473             /* Cancel the inactive timer */
474             g_source_remove (session->priv->inactive_timeout);
475
476             session->priv->started = TRUE;
477         }
478         else
479         {
480             XDMCPPacket *response;
481
482             response = xdmcp_packet_alloc (XDMCP_Failed);
483             response->Failed.session_id = packet->Manage.session_id;
484             response->Failed.status = g_strdup_printf ("Failed to connect to display :%d", packet->Manage.display_number);
485             send_packet (socket, address, response);
486             xdmcp_packet_free (response);
487         }
488     }
489     else
490     {
491         XDMCPPacket *response;
492
493         response = xdmcp_packet_alloc (XDMCP_Refuse);
494         response->Refuse.session_id = packet->Manage.session_id;
495         send_packet (socket, address, response);
496         xdmcp_packet_free (response);
497     }
498 }
499
500 static void
501 handle_keep_alive (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
502 {
503     XDMCPPacket *response;
504     XDMCPSession *session;
505     gboolean alive = FALSE;
506
507     session = get_session (server, packet->KeepAlive.session_id);
508     if (session)
509         alive = TRUE; //xdmcp_session_get_alive (session);
510
511     response = xdmcp_packet_alloc (XDMCP_Alive);
512     response->Alive.session_running = alive;
513     response->Alive.session_id = alive ? packet->KeepAlive.session_id : 0;
514     send_packet (socket, address, response);
515     xdmcp_packet_free (response);
516 }
517
518 static gboolean
519 read_cb (GSocket *socket, GIOCondition condition, XDMCPServer *server)
520 {
521     GSocketAddress *address;
522     gchar data[1024];
523     GError *error = NULL;
524     gssize n_read;
525
526     n_read = g_socket_receive_from (socket, &address, data, 1024, NULL, &error);
527     if (n_read > 0)
528     {
529         XDMCPPacket *packet;
530
531         packet = xdmcp_packet_decode ((guchar *)data, n_read);
532         if (packet)
533         {        
534             g_debug ("Got %s", xdmcp_packet_tostring (packet));
535
536             switch (packet->opcode)
537             {
538             case XDMCP_BroadcastQuery:
539             case XDMCP_Query:
540             case XDMCP_IndirectQuery:
541                 handle_query (server, socket, address, packet);
542                 break;
543             case XDMCP_Request:
544                 handle_request (server, socket, address, packet);
545                 break;
546             case XDMCP_Manage:
547                 handle_manage (server, socket, address, packet);              
548                 break;
549             case XDMCP_KeepAlive:
550                 handle_keep_alive (server, socket, address, packet);
551                 break;
552             default:
553                 g_warning ("Got unexpected XDMCP packet %d", packet->opcode);
554                 break;
555             }
556
557             xdmcp_packet_free (packet);
558         }
559     }
560     else
561         g_warning ("Failed to read from XDMCP socket: %s", error->message);
562
563     g_clear_error (&error);
564
565     return TRUE;
566 }
567
568 static GSocket *
569 open_udp_socket (GSocketFamily family, guint port, GError **error)
570 {
571     GSocket *socket;
572     GSocketAddress *address;
573     gboolean result;
574   
575     socket = g_socket_new (family, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, error);
576     if (!socket)
577         return NULL;
578
579     address = g_inet_socket_address_new (g_inet_address_new_any (family), port);
580     result = g_socket_bind (socket, address, TRUE, error);
581     if (!result)
582     {
583         g_object_unref (socket);
584         return NULL;
585     }
586
587     return socket;
588 }
589
590 gboolean
591 xdmcp_server_start (XDMCPServer *server)
592 {
593     GSource *source;
594     GError *error = NULL;
595
596     g_return_val_if_fail (server != NULL, FALSE);
597   
598     server->priv->socket = open_udp_socket (G_SOCKET_FAMILY_IPV4, server->priv->port, &error);
599     if (server->priv->socket)
600     {
601         source = g_socket_create_source (server->priv->socket, G_IO_IN, NULL);
602         g_source_set_callback (source, (GSourceFunc) read_cb, server, NULL);
603         g_source_attach (source, NULL);
604     }
605     else
606         g_warning ("Failed to create IPv4 XDMCP socket: %s", error->message);
607     g_clear_error (&error);
608     server->priv->socket6 = open_udp_socket (G_SOCKET_FAMILY_IPV6, server->priv->port, &error);  
609     if (server->priv->socket6)
610     {
611         source = g_socket_create_source (server->priv->socket6, G_IO_IN, NULL);
612         g_source_set_callback (source, (GSourceFunc) read_cb, server, NULL);
613         g_source_attach (source, NULL);
614     }
615     else
616         g_warning ("Failed to create IPv6 XDMCP socket: %s", error->message);
617     g_clear_error (&error);
618
619     if (!server->priv->socket && !server->priv->socket6)
620         return FALSE;
621
622     return TRUE;
623 }
624
625 static void
626 xdmcp_server_init (XDMCPServer *server)
627 {
628     server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, XDMCP_SERVER_TYPE, XDMCPServerPrivate);
629
630     server->priv->port = XDM_UDP_PORT;
631     server->priv->hostname = g_strdup ("");
632     server->priv->status = g_strdup ("");
633     server->priv->sessions = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
634     server->priv->authentication_name = g_strdup ("");
635     server->priv->authorization_name = g_strdup ("");
636 }
637
638 static void
639 xdmcp_server_finalize (GObject *object)
640 {
641     XDMCPServer *self;
642
643     self = XDMCP_SERVER (object);
644   
645     if (self->priv->socket)
646         g_object_unref (self->priv->socket);
647     if (self->priv->socket6)
648         g_object_unref (self->priv->socket6);
649     g_free (self->priv->hostname);
650     g_free (self->priv->status);
651     g_free (self->priv->authentication_name);
652     g_free (self->priv->authentication_data);
653     g_free (self->priv->authorization_name);
654     g_free (self->priv->authorization_data);
655     g_hash_table_unref (self->priv->sessions);
656   
657     G_OBJECT_CLASS (xdmcp_server_parent_class)->finalize (object);  
658 }
659
660 static void
661 xdmcp_server_class_init (XDMCPServerClass *klass)
662 {
663     GObjectClass *object_class = G_OBJECT_CLASS (klass);
664
665     object_class->finalize = xdmcp_server_finalize;  
666
667     g_type_class_add_private (klass, sizeof (XDMCPServerPrivate));
668
669     signals[NEW_SESSION] =
670         g_signal_new ("new-session",
671                       G_TYPE_FROM_CLASS (klass),
672                       G_SIGNAL_RUN_LAST,
673                       G_STRUCT_OFFSET (XDMCPServerClass, new_session),
674                       g_signal_accumulator_true_handled,
675                       NULL,
676                       ldm_marshal_BOOLEAN__OBJECT,
677                       G_TYPE_BOOLEAN, 1, XDMCP_SESSION_TYPE);
678 }