2003-04-03 Havoc Pennington <hp@redhat.com>
[platform/upstream/dbus.git] / bus / bus.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* bus.c  message bus context object
3  *
4  * Copyright (C) 2003 Red Hat, Inc.
5  *
6  * Licensed under the Academic Free License version 1.2
7  * 
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  * 
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23
24 #include "bus.h"
25 #include "loop.h"
26 #include "activation.h"
27 #include "connection.h"
28 #include "services.h"
29 #include "utils.h"
30 #include "policy.h"
31 #include "config-parser.h"
32 #include <dbus/dbus-list.h>
33 #include <dbus/dbus-hash.h>
34 #include <dbus/dbus-internals.h>
35
36 struct BusContext
37 {
38   int refcount;
39   char *type;
40   char *address;
41   BusLoop *loop;
42   DBusList *servers;
43   BusConnections *connections;
44   BusActivation *activation;
45   BusRegistry *registry;
46   DBusList *default_rules;      /**< Default policy rules */
47   DBusList *mandatory_rules;    /**< Mandatory policy rules */
48   DBusHashTable *rules_by_uid;  /**< per-UID policy rules */
49   DBusHashTable *rules_by_gid;  /**< per-GID policy rules */
50 };
51
52 static int server_data_slot = -1;
53 static int server_data_slot_refcount = 0;
54
55 typedef struct
56 {
57   BusContext *context;
58 } BusServerData;
59
60 #define BUS_SERVER_DATA(server) (dbus_server_get_data ((server), server_data_slot))
61
62 static dbus_bool_t
63 server_data_slot_ref (void)
64 {
65   if (server_data_slot < 0)
66     {
67       server_data_slot = dbus_server_allocate_data_slot ();
68       
69       if (server_data_slot < 0)
70         return FALSE;
71
72       _dbus_assert (server_data_slot_refcount == 0);
73     }  
74
75   server_data_slot_refcount += 1;
76
77   return TRUE;
78 }
79
80 static void
81 server_data_slot_unref (void)
82 {
83   _dbus_assert (server_data_slot_refcount > 0);
84
85   server_data_slot_refcount -= 1;
86   
87   if (server_data_slot_refcount == 0)
88     {
89       dbus_server_free_data_slot (server_data_slot);
90       server_data_slot = -1;
91     }
92 }
93
94 static BusContext*
95 server_get_context (DBusServer *server)
96 {
97   BusContext *context;
98   BusServerData *bd;
99   
100   if (!server_data_slot_ref ())
101     return NULL;
102
103   bd = BUS_SERVER_DATA (server);
104   if (bd == NULL)
105     return NULL;
106
107   context = bd->context;
108
109   server_data_slot_unref ();
110
111   return context;
112 }
113
114 static dbus_bool_t
115 server_watch_callback (DBusWatch     *watch,
116                        unsigned int   condition,
117                        void          *data)
118 {
119   DBusServer *server = data;
120
121   return dbus_server_handle_watch (server, watch, condition);
122 }
123
124 static dbus_bool_t
125 add_server_watch (DBusWatch  *watch,
126                   void       *data)
127 {
128   DBusServer *server = data;
129   BusContext *context;
130   
131   context = server_get_context (server);
132   
133   return bus_loop_add_watch (context->loop,
134                              watch, server_watch_callback, server,
135                              NULL);
136 }
137
138 static void
139 remove_server_watch (DBusWatch  *watch,
140                      void       *data)
141 {
142   DBusServer *server = data;
143   BusContext *context;
144   
145   context = server_get_context (server);
146   
147   bus_loop_remove_watch (context->loop,
148                          watch, server_watch_callback, server);
149 }
150
151
152 static void
153 server_timeout_callback (DBusTimeout   *timeout,
154                          void          *data)
155 {
156   /* can return FALSE on OOM but we just let it fire again later */
157   dbus_timeout_handle (timeout);
158 }
159
160 static dbus_bool_t
161 add_server_timeout (DBusTimeout *timeout,
162                     void        *data)
163 {
164   DBusServer *server = data;
165   BusContext *context;
166   
167   context = server_get_context (server);
168
169   return bus_loop_add_timeout (context->loop,
170                                timeout, server_timeout_callback, server, NULL);
171 }
172
173 static void
174 remove_server_timeout (DBusTimeout *timeout,
175                        void        *data)
176 {
177   DBusServer *server = data;
178   BusContext *context;
179   
180   context = server_get_context (server);
181   
182   bus_loop_remove_timeout (context->loop,
183                            timeout, server_timeout_callback, server);
184 }
185
186 static void
187 new_connection_callback (DBusServer     *server,
188                          DBusConnection *new_connection,
189                          void           *data)
190 {
191   BusContext *context = data;
192   
193   if (!bus_connections_setup_connection (context->connections, new_connection))
194     {
195       _dbus_verbose ("No memory to setup new connection\n");
196
197       /* if we don't do this, it will get unref'd without
198        * being disconnected... kind of strange really
199        * that we have to do this, people won't get it right
200        * in general.
201        */
202       dbus_connection_disconnect (new_connection);
203     }
204   
205   /* on OOM, we won't have ref'd the connection so it will die. */
206 }
207
208 static void
209 free_rule_func (void *data,
210                 void *user_data)
211 {
212   BusPolicyRule *rule = data;
213
214   bus_policy_rule_unref (rule);
215 }
216
217 static void
218 free_rule_list_func (void *data)
219 {
220   DBusList **list = data;
221
222   _dbus_list_foreach (list, free_rule_func, NULL);
223   
224   _dbus_list_clear (list);
225
226   dbus_free (list);
227 }
228
229 static void
230 free_server_data (void *data)
231 {
232   BusServerData *bd = data;  
233   
234   dbus_free (bd);
235 }
236
237 static dbus_bool_t
238 setup_server (BusContext *context,
239               DBusServer *server,
240               char      **auth_mechanisms,
241               DBusError  *error)
242 {
243   BusServerData *bd;
244   
245   if (!dbus_server_set_auth_mechanisms (server, (const char**) auth_mechanisms))
246     {
247       BUS_SET_OOM (error);
248       return FALSE;
249     }
250   
251   dbus_server_set_new_connection_function (server,
252                                            new_connection_callback,
253                                            context, NULL);
254   
255   if (!dbus_server_set_watch_functions (server,
256                                         add_server_watch,
257                                         remove_server_watch,
258                                         NULL,
259                                         server,
260                                         NULL))
261     {
262       BUS_SET_OOM (error);
263       return FALSE;
264     }
265
266   if (!dbus_server_set_timeout_functions (server,
267                                           add_server_timeout,
268                                           remove_server_timeout,
269                                           NULL,
270                                           server, NULL))
271     {
272       BUS_SET_OOM (error);
273       return FALSE;
274     }
275   
276   bd = dbus_new0 (BusServerData, 1);
277   if (!dbus_server_set_data (server,
278                              server_data_slot,
279                              bd, free_server_data))
280     {
281       dbus_free (bd);
282       return FALSE;
283     }
284
285   bd->context = context;
286   
287   return TRUE;
288 }
289
290 BusContext*
291 bus_context_new (const DBusString *config_file,
292                  DBusError        *error)
293 {
294   BusContext *context;
295   DBusList *link;
296   DBusList **addresses;
297   BusConfigParser *parser;
298   DBusString full_address;
299   const char *user;
300   char **auth_mechanisms;
301   DBusList **auth_mechanisms_list;
302   int len;
303   
304   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
305
306   if (!_dbus_string_init (&full_address))
307     {
308       BUS_SET_OOM (error);
309       return NULL;
310     }
311   
312   if (!server_data_slot_ref ())
313     {
314       BUS_SET_OOM (error);
315       _dbus_string_free (&full_address);
316       return NULL;
317     }
318   
319   parser = NULL;
320   context = NULL;
321   auth_mechanisms = NULL;
322   
323   parser = bus_config_load (config_file, error);
324   if (parser == NULL)
325     goto failed;
326   
327   context = dbus_new0 (BusContext, 1);
328   if (context == NULL)
329     {
330       BUS_SET_OOM (error);
331       goto failed;
332     }
333   
334   context->refcount = 1;
335
336   context->loop = bus_loop_new ();
337   if (context->loop == NULL)
338     {
339       BUS_SET_OOM (error);
340       goto failed;
341     }
342   
343   /* Build an array of auth mechanisms */
344   
345   auth_mechanisms_list = bus_config_parser_get_mechanisms (parser);
346   len = _dbus_list_get_length (auth_mechanisms_list);
347
348   if (len > 0)
349     {
350       int i;
351
352       auth_mechanisms = dbus_new0 (char*, len + 1);
353       if (auth_mechanisms == NULL)
354         goto failed;
355       
356       i = 0;
357       link = _dbus_list_get_first_link (auth_mechanisms_list);
358       while (link != NULL)
359         {
360           auth_mechanisms[i] = _dbus_strdup (link->data);
361           if (auth_mechanisms[i] == NULL)
362             goto failed;
363           link = _dbus_list_get_next_link (auth_mechanisms_list, link);
364         }
365     }
366   else
367     {
368       auth_mechanisms = NULL;
369     }
370
371   /* Listen on our addresses */
372   
373   addresses = bus_config_parser_get_addresses (parser);  
374   
375   link = _dbus_list_get_first_link (addresses);
376   while (link != NULL)
377     {
378       DBusServer *server;
379       
380       server = dbus_server_listen (link->data, error);
381       if (server == NULL)
382         goto failed;
383       else if (!setup_server (context, server, auth_mechanisms, error))
384         goto failed;
385
386       if (!_dbus_list_append (&context->servers, server))
387         {
388           BUS_SET_OOM (error);
389           goto failed;
390         }          
391       
392       link = _dbus_list_get_next_link (addresses, link);
393     }
394
395   /* Here we change our credentials if required,
396    * as soon as we've set up our sockets
397    */
398   user = bus_config_parser_get_user (parser);
399   if (user != NULL)
400     {
401       DBusCredentials creds;
402       DBusString u;
403
404       _dbus_string_init_const (&u, user);
405
406       if (!_dbus_credentials_from_username (&u, &creds) ||
407           creds.uid < 0 ||
408           creds.gid < 0)
409         {
410           dbus_set_error (error, DBUS_ERROR_FAILED,
411                           "Could not get UID and GID for username \"%s\"",
412                           user);
413           goto failed;
414         }
415       
416       if (!_dbus_change_identity (creds.uid, creds.gid, error))
417         goto failed;
418     }
419
420   /* note that type may be NULL */
421   context->type = _dbus_strdup (bus_config_parser_get_type (parser));
422   
423   /* We have to build the address backward, so that
424    * <listen> later in the config file have priority
425    */
426   link = _dbus_list_get_last_link (&context->servers);
427   while (link != NULL)
428     {
429       char *addr;
430       
431       addr = dbus_server_get_address (link->data);
432       if (addr == NULL)
433         {
434           BUS_SET_OOM (error);
435           goto failed;
436         }
437
438       if (_dbus_string_get_length (&full_address) > 0)
439         {
440           if (!_dbus_string_append (&full_address, ";"))
441             {
442               BUS_SET_OOM (error);
443               goto failed;
444             }
445         }
446
447       if (!_dbus_string_append (&full_address, addr))
448         {
449           BUS_SET_OOM (error);
450           goto failed;
451         }
452
453       dbus_free (addr);
454
455       link = _dbus_list_get_prev_link (&context->servers, link);
456     }
457
458   if (!_dbus_string_copy_data (&full_address, &context->address))
459     {
460       BUS_SET_OOM (error);
461       goto failed;
462     }
463
464   /* Create activation subsystem */
465   
466   context->activation = bus_activation_new (context, &full_address,
467                                             bus_config_parser_get_service_dirs (parser),
468                                             error);
469   if (context->activation == NULL)
470     {
471       _DBUS_ASSERT_ERROR_IS_SET (error);
472       goto failed;
473     }
474
475   context->connections = bus_connections_new (context);
476   if (context->connections == NULL)
477     {
478       BUS_SET_OOM (error);
479       goto failed;
480     }
481
482   context->registry = bus_registry_new (context);
483   if (context->registry == NULL)
484     {
485       BUS_SET_OOM (error);
486       goto failed;
487     }
488   
489   context->rules_by_uid = _dbus_hash_table_new (DBUS_HASH_ULONG,
490                                                 NULL,
491                                                 free_rule_list_func);
492   if (context->rules_by_uid == NULL)
493     {
494       BUS_SET_OOM (error);
495       goto failed;
496     }
497
498   context->rules_by_gid = _dbus_hash_table_new (DBUS_HASH_ULONG,
499                                                 NULL,
500                                                 free_rule_list_func);
501   if (context->rules_by_gid == NULL)
502     {
503       BUS_SET_OOM (error);
504       goto failed;
505     }
506
507   /* Now become a daemon if appropriate */
508   if (bus_config_parser_get_fork (parser))
509     {
510       if (!_dbus_become_daemon (error))
511         goto failed;
512     }
513   
514   bus_config_parser_unref (parser);
515   _dbus_string_free (&full_address);
516   dbus_free_string_array (auth_mechanisms);
517   
518   return context;
519   
520  failed:  
521   if (parser != NULL)
522     bus_config_parser_unref (parser);
523
524   if (context != NULL)
525     bus_context_unref (context);
526
527   _dbus_string_free (&full_address);
528   dbus_free_string_array (auth_mechanisms);
529
530   server_data_slot_unref ();
531   
532   return NULL;
533 }
534
535 static void
536 shutdown_server (BusContext *context,
537                  DBusServer *server)
538 {
539   if (server == NULL ||
540       !dbus_server_get_is_connected (server))
541     return;
542   
543   if (!dbus_server_set_watch_functions (server,
544                                         NULL, NULL, NULL,
545                                         context,
546                                         NULL))
547     _dbus_assert_not_reached ("setting watch functions to NULL failed");
548   
549   if (!dbus_server_set_timeout_functions (server,
550                                           NULL, NULL, NULL,
551                                           context,
552                                           NULL))
553     _dbus_assert_not_reached ("setting timeout functions to NULL failed");
554   
555   dbus_server_disconnect (server);
556 }
557
558 void
559 bus_context_shutdown (BusContext  *context)
560 {
561   DBusList *link;
562
563   link = _dbus_list_get_first_link (&context->servers);
564   while (link != NULL)
565     {
566       shutdown_server (context, link->data);
567
568       link = _dbus_list_get_next_link (&context->servers, link);
569     }
570 }
571
572 void
573 bus_context_ref (BusContext *context)
574 {
575   _dbus_assert (context->refcount > 0);
576   context->refcount += 1;
577 }
578
579 void
580 bus_context_unref (BusContext *context)
581 {
582   _dbus_assert (context->refcount > 0);
583   context->refcount -= 1;
584
585   if (context->refcount == 0)
586     {
587       DBusList *link;
588       
589       _dbus_verbose ("Finalizing bus context %p\n", context);
590       
591       bus_context_shutdown (context);
592
593       if (context->connections)
594         {
595           bus_connections_unref (context->connections);
596           context->connections = NULL;
597         }
598       
599       if (context->registry)
600         {
601           bus_registry_unref (context->registry);
602           context->registry = NULL;
603         }
604       
605       if (context->activation)
606         {
607           bus_activation_unref (context->activation);
608           context->activation = NULL;
609         }
610
611       link = _dbus_list_get_first_link (&context->servers);
612       while (link != NULL)
613         {
614           dbus_server_unref (link->data);
615           
616           link = _dbus_list_get_next_link (&context->servers, link);
617         }
618       _dbus_list_clear (&context->servers);
619
620       if (context->rules_by_uid)
621         {
622           _dbus_hash_table_unref (context->rules_by_uid);
623           context->rules_by_uid = NULL;
624         }
625
626       if (context->rules_by_gid)
627         {
628           _dbus_hash_table_unref (context->rules_by_gid);
629           context->rules_by_gid = NULL;
630         }
631
632       if (context->loop)
633         {
634           bus_loop_unref (context->loop);
635           context->loop = NULL;
636         }
637       
638       dbus_free (context->type);
639       dbus_free (context->address);
640       dbus_free (context);
641
642       server_data_slot_unref ();
643     }
644 }
645
646 /* type may be NULL */
647 const char*
648 bus_context_get_type (BusContext *context)
649 {
650   return context->type;
651 }
652
653 BusRegistry*
654 bus_context_get_registry (BusContext  *context)
655 {
656   return context->registry;
657 }
658
659 BusConnections*
660 bus_context_get_connections (BusContext  *context)
661 {
662   return context->connections;
663 }
664
665 BusActivation*
666 bus_context_get_activation (BusContext  *context)
667 {
668   return context->activation;
669 }
670
671 BusLoop*
672 bus_context_get_loop (BusContext *context)
673 {
674   return context->loop;
675 }
676
677 static dbus_bool_t
678 list_allows_user (dbus_bool_t           def,
679                   DBusList            **list,
680                   unsigned long         uid,
681                   const unsigned long  *group_ids,
682                   int                   n_group_ids)
683 {
684   DBusList *link;
685   dbus_bool_t allowed;
686   
687   allowed = def;
688
689   link = _dbus_list_get_first_link (list);
690   while (link != NULL)
691     {
692       BusPolicyRule *rule = link->data;
693       link = _dbus_list_get_next_link (list, link);
694       
695       if (rule->type == BUS_POLICY_RULE_USER)
696         {
697           if (rule->d.user.uid != uid)
698             continue;
699         }
700       else if (rule->type == BUS_POLICY_RULE_GROUP)
701         {
702           int i;
703
704           i = 0;
705           while (i < n_group_ids)
706             {
707               if (rule->d.group.gid == group_ids[i])
708                 break;
709               ++i;
710             }
711
712           if (i == n_group_ids)
713             continue;
714         }
715       else
716         continue;
717
718       allowed = rule->allow;
719     }
720   
721   return allowed;
722 }
723
724 dbus_bool_t
725 bus_context_allow_user (BusContext   *context,
726                         unsigned long uid)
727 {
728   dbus_bool_t allowed;
729   unsigned long *group_ids;
730   int n_group_ids;
731
732   /* On OOM or error we always reject the user */
733   if (!_dbus_get_groups (uid, &group_ids, &n_group_ids))
734     {
735       _dbus_verbose ("Did not get any groups for UID %lu\n",
736                      uid);
737       return FALSE;
738     }
739   
740   allowed = FALSE;
741
742   allowed = list_allows_user (allowed,
743                               &context->default_rules,
744                               uid,
745                               group_ids, n_group_ids);
746
747   allowed = list_allows_user (allowed,
748                               &context->mandatory_rules,
749                               uid,
750                               group_ids, n_group_ids);
751
752   dbus_free (group_ids);
753
754   return allowed;
755 }
756
757 static dbus_bool_t
758 add_list_to_policy (DBusList       **list,
759                     BusPolicy       *policy)
760 {
761   DBusList *link;
762
763   link = _dbus_list_get_first_link (list);
764   while (link != NULL)
765     {
766       BusPolicyRule *rule = link->data;
767       link = _dbus_list_get_next_link (list, link);
768
769       switch (rule->type)
770         {
771         case BUS_POLICY_RULE_USER:
772         case BUS_POLICY_RULE_GROUP:
773           /* These aren't per-connection policies */
774           break;
775
776         case BUS_POLICY_RULE_OWN:
777         case BUS_POLICY_RULE_SEND:
778         case BUS_POLICY_RULE_RECEIVE:
779           /* These are per-connection */
780           if (!bus_policy_append_rule (policy, rule))
781             return FALSE;
782           break;
783         }
784     }
785   
786   return TRUE;
787 }
788
789 BusPolicy*
790 bus_context_create_connection_policy (BusContext      *context,
791                                       DBusConnection  *connection)
792 {
793   BusPolicy *policy;
794   unsigned long uid;
795   DBusList **list;
796
797   _dbus_assert (dbus_connection_get_is_authenticated (connection));
798   
799   policy = bus_policy_new ();
800   if (policy == NULL)
801     return NULL;
802
803   if (!add_list_to_policy (&context->default_rules,
804                                       policy))
805     goto failed;
806
807   /* we avoid the overhead of looking up user's groups
808    * if we don't have any group rules anyway
809    */
810   if (_dbus_hash_table_get_n_entries (context->rules_by_gid) > 0)
811     {
812       const unsigned long *groups;
813       int n_groups;
814       int i;
815       
816       if (!bus_connection_get_groups (connection, &groups, &n_groups))
817         goto failed;
818       
819       i = 0;
820       while (i < n_groups)
821         {
822           list = _dbus_hash_table_lookup_ulong (context->rules_by_gid,
823                                                 groups[i]);
824           
825           if (list != NULL)
826             {
827               if (!add_list_to_policy (list, policy))
828                 goto failed;
829             }
830           
831           ++i;
832         }
833     }
834
835   if (!dbus_connection_get_unix_user (connection, &uid))
836     goto failed;
837
838   list = _dbus_hash_table_lookup_ulong (context->rules_by_uid,
839                                         uid);
840
841   if (!add_list_to_policy (list, policy))
842     goto failed;
843   
844   if (!add_list_to_policy (&context->mandatory_rules,
845                            policy))
846     goto failed;
847
848   bus_policy_optimize (policy);
849   
850   return policy;
851   
852  failed:
853   bus_policy_unref (policy);
854   return NULL;
855 }