2003-04-01 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 *address;
40   DBusList *servers;
41   BusConnections *connections;
42   BusActivation *activation;
43   BusRegistry *registry;
44   DBusList *default_rules;      /**< Default policy rules */
45   DBusList *mandatory_rules;    /**< Mandatory policy rules */
46   DBusHashTable *rules_by_uid;  /**< per-UID policy rules */
47   DBusHashTable *rules_by_gid;  /**< per-GID policy rules */
48 };
49
50 static dbus_bool_t
51 server_watch_callback (DBusWatch     *watch,
52                        unsigned int   condition,
53                        void          *data)
54 {
55   DBusServer *server = data;
56
57   return dbus_server_handle_watch (server, watch, condition);
58 }
59
60 static dbus_bool_t
61 add_server_watch (DBusWatch  *watch,
62                   void       *data)
63 {
64   return bus_loop_add_watch (watch, server_watch_callback, data,
65                              NULL);
66 }
67
68 static void
69 remove_server_watch (DBusWatch  *watch,
70                      void       *data)
71 {
72   bus_loop_remove_watch (watch, server_watch_callback, data);
73 }
74
75
76 static void
77 server_timeout_callback (DBusTimeout   *timeout,
78                          void          *data)
79 {
80   /* can return FALSE on OOM but we just let it fire again later */
81   dbus_timeout_handle (timeout);
82 }
83
84 static dbus_bool_t
85 add_server_timeout (DBusTimeout *timeout,
86                     void        *data)
87 {
88   return bus_loop_add_timeout (timeout, server_timeout_callback, data, NULL);
89 }
90
91 static void
92 remove_server_timeout (DBusTimeout *timeout,
93                        void        *data)
94 {
95   bus_loop_remove_timeout (timeout, server_timeout_callback, data);
96 }
97
98 static void
99 new_connection_callback (DBusServer     *server,
100                          DBusConnection *new_connection,
101                          void           *data)
102 {
103   BusContext *context = data;
104   
105   if (!bus_connections_setup_connection (context->connections, new_connection))
106     {
107       _dbus_verbose ("No memory to setup new connection\n");
108
109       /* if we don't do this, it will get unref'd without
110        * being disconnected... kind of strange really
111        * that we have to do this, people won't get it right
112        * in general.
113        */
114       dbus_connection_disconnect (new_connection);
115     }
116   
117   /* on OOM, we won't have ref'd the connection so it will die. */
118 }
119
120 static void
121 free_rule_func (void *data,
122                 void *user_data)
123 {
124   BusPolicyRule *rule = data;
125
126   bus_policy_rule_unref (rule);
127 }
128
129 static void
130 free_rule_list_func (void *data)
131 {
132   DBusList **list = data;
133
134   _dbus_list_foreach (list, free_rule_func, NULL);
135   
136   _dbus_list_clear (list);
137
138   dbus_free (list);
139 }
140
141 static dbus_bool_t
142 setup_server (BusContext *context,
143               DBusServer *server,
144               char      **auth_mechanisms,
145               DBusError  *error)
146 {
147   if (!dbus_server_set_auth_mechanisms (server, (const char**) auth_mechanisms))
148     {
149       BUS_SET_OOM (error);
150       return FALSE;
151     }
152   
153   dbus_server_set_new_connection_function (server,
154                                            new_connection_callback,
155                                            context, NULL);
156   
157   if (!dbus_server_set_watch_functions (server,
158                                         add_server_watch,
159                                         remove_server_watch,
160                                         NULL,
161                                         server,
162                                         NULL))
163     {
164       BUS_SET_OOM (error);
165       return FALSE;
166     }
167
168   if (!dbus_server_set_timeout_functions (server,
169                                           add_server_timeout,
170                                           remove_server_timeout,
171                                           NULL,
172                                           server, NULL))
173     {
174       BUS_SET_OOM (error);
175       return FALSE;
176     }
177
178   return TRUE;
179 }
180
181 BusContext*
182 bus_context_new (const DBusString *config_file,
183                  DBusError        *error)
184 {
185   BusContext *context;
186   DBusList *link;
187   DBusList **addresses;
188   BusConfigParser *parser;
189   DBusString full_address;
190   const char *user;
191   char **auth_mechanisms;
192   DBusList **auth_mechanisms_list;
193   int len;
194   
195   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
196
197   if (!_dbus_string_init (&full_address))
198     return NULL;
199
200   parser = NULL;
201   context = NULL;
202   auth_mechanisms = NULL;
203   
204   parser = bus_config_load (config_file, error);
205   if (parser == NULL)
206     goto failed;
207   
208   context = dbus_new0 (BusContext, 1);
209   if (context == NULL)
210     {
211       BUS_SET_OOM (error);
212       goto failed;
213     }
214   
215   context->refcount = 1;
216
217   /* Build an array of auth mechanisms */
218   
219   auth_mechanisms_list = bus_config_parser_get_mechanisms (parser);
220   len = _dbus_list_get_length (auth_mechanisms_list);
221
222   if (len > 0)
223     {
224       int i;
225
226       auth_mechanisms = dbus_new0 (char*, len + 1);
227       if (auth_mechanisms == NULL)
228         goto failed;
229       
230       i = 0;
231       link = _dbus_list_get_first_link (auth_mechanisms_list);
232       while (link != NULL)
233         {
234           auth_mechanisms[i] = _dbus_strdup (link->data);
235           if (auth_mechanisms[i] == NULL)
236             goto failed;
237           link = _dbus_list_get_next_link (auth_mechanisms_list, link);
238         }
239     }
240   else
241     {
242       auth_mechanisms = NULL;
243     }
244
245   /* Listen on our addresses */
246   
247   addresses = bus_config_parser_get_addresses (parser);  
248   
249   link = _dbus_list_get_first_link (addresses);
250   while (link != NULL)
251     {
252       DBusServer *server;
253       
254       server = dbus_server_listen (link->data, error);
255       if (server == NULL)
256         goto failed;
257       else if (!setup_server (context, server, auth_mechanisms, error))
258         goto failed;
259
260       if (!_dbus_list_append (&context->servers, server))
261         {
262           BUS_SET_OOM (error);
263           goto failed;
264         }          
265       
266       link = _dbus_list_get_next_link (addresses, link);
267     }
268
269   /* Here we change our credentials if required,
270    * as soon as we've set up our sockets
271    */
272   user = bus_config_parser_get_user (parser);
273   if (user != NULL)
274     {
275       DBusCredentials creds;
276       DBusString u;
277
278       _dbus_string_init_const (&u, user);
279
280       if (!_dbus_credentials_from_username (&u, &creds) ||
281           creds.uid < 0 ||
282           creds.gid < 0)
283         {
284           dbus_set_error (error, DBUS_ERROR_FAILED,
285                           "Could not get UID and GID for username \"%s\"",
286                           user);
287           goto failed;
288         }
289       
290       if (!_dbus_change_identity (creds.uid, creds.gid, error))
291         goto failed;
292     }
293   
294   /* We have to build the address backward, so that
295    * <listen> later in the config file have priority
296    */
297   link = _dbus_list_get_last_link (&context->servers);
298   while (link != NULL)
299     {
300       char *addr;
301       
302       addr = dbus_server_get_address (link->data);
303       if (addr == NULL)
304         {
305           BUS_SET_OOM (error);
306           goto failed;
307         }
308
309       if (_dbus_string_get_length (&full_address) > 0)
310         {
311           if (!_dbus_string_append (&full_address, ";"))
312             {
313               BUS_SET_OOM (error);
314               goto failed;
315             }
316         }
317
318       if (!_dbus_string_append (&full_address, addr))
319         {
320           BUS_SET_OOM (error);
321           goto failed;
322         }
323
324       dbus_free (addr);
325
326       link = _dbus_list_get_prev_link (&context->servers, link);
327     }
328
329   if (!_dbus_string_copy_data (&full_address, &context->address))
330     {
331       BUS_SET_OOM (error);
332       goto failed;
333     }
334
335   /* Create activation subsystem */
336   
337   context->activation = bus_activation_new (context, &full_address,
338                                             bus_config_parser_get_service_dirs (parser),
339                                             error);
340   if (context->activation == NULL)
341     {
342       _DBUS_ASSERT_ERROR_IS_SET (error);
343       goto failed;
344     }
345
346   context->connections = bus_connections_new (context);
347   if (context->connections == NULL)
348     {
349       BUS_SET_OOM (error);
350       goto failed;
351     }
352
353   context->registry = bus_registry_new (context);
354   if (context->registry == NULL)
355     {
356       BUS_SET_OOM (error);
357       goto failed;
358     }
359   
360   context->rules_by_uid = _dbus_hash_table_new (DBUS_HASH_ULONG,
361                                                 NULL,
362                                                 free_rule_list_func);
363   if (context->rules_by_uid == NULL)
364     {
365       BUS_SET_OOM (error);
366       goto failed;
367     }
368
369   context->rules_by_gid = _dbus_hash_table_new (DBUS_HASH_ULONG,
370                                                 NULL,
371                                                 free_rule_list_func);
372   if (context->rules_by_gid == NULL)
373     {
374       BUS_SET_OOM (error);
375       goto failed;
376     }
377
378   /* Now become a daemon if appropriate */
379   if (bus_config_parser_get_fork (parser))
380     {
381       if (!_dbus_become_daemon (error))
382         goto failed;
383     }
384   
385   bus_config_parser_unref (parser);
386   _dbus_string_free (&full_address);
387   dbus_free_string_array (auth_mechanisms);
388   
389   return context;
390   
391  failed:  
392   if (parser != NULL)
393     bus_config_parser_unref (parser);
394
395   if (context != NULL)
396     bus_context_unref (context);
397
398   _dbus_string_free (&full_address);
399   dbus_free_string_array (auth_mechanisms);
400   return NULL;
401 }
402
403 static void
404 shutdown_server (BusContext *context,
405                  DBusServer *server)
406 {
407   if (server == NULL ||
408       !dbus_server_get_is_connected (server))
409     return;
410   
411   if (!dbus_server_set_watch_functions (server,
412                                         NULL, NULL, NULL,
413                                         context,
414                                         NULL))
415     _dbus_assert_not_reached ("setting watch functions to NULL failed");
416   
417   if (!dbus_server_set_timeout_functions (server,
418                                           NULL, NULL, NULL,
419                                           context,
420                                           NULL))
421     _dbus_assert_not_reached ("setting timeout functions to NULL failed");
422   
423   dbus_server_disconnect (server);
424 }
425
426 void
427 bus_context_shutdown (BusContext  *context)
428 {
429   DBusList *link;
430
431   link = _dbus_list_get_first_link (&context->servers);
432   while (link != NULL)
433     {
434       shutdown_server (context, link->data);
435
436       link = _dbus_list_get_next_link (&context->servers, link);
437     }
438 }
439
440 void
441 bus_context_ref (BusContext *context)
442 {
443   _dbus_assert (context->refcount > 0);
444   context->refcount += 1;
445 }
446
447 void
448 bus_context_unref (BusContext *context)
449 {
450   _dbus_assert (context->refcount > 0);
451   context->refcount -= 1;
452
453   if (context->refcount == 0)
454     {
455       DBusList *link;
456       
457       _dbus_verbose ("Finalizing bus context %p\n", context);
458       
459       bus_context_shutdown (context);
460
461       if (context->connections)
462         {
463           bus_connections_unref (context->connections);
464           context->connections = NULL;
465         }
466       
467       if (context->registry)
468         {
469           bus_registry_unref (context->registry);
470           context->registry = NULL;
471         }
472       
473       if (context->activation)
474         {
475           bus_activation_unref (context->activation);
476           context->activation = NULL;
477         }
478
479       link = _dbus_list_get_first_link (&context->servers);
480       while (link != NULL)
481         {
482           dbus_server_unref (link->data);
483           
484           link = _dbus_list_get_next_link (&context->servers, link);
485         }
486       _dbus_list_clear (&context->servers);
487
488       if (context->rules_by_uid)
489         {
490           _dbus_hash_table_unref (context->rules_by_uid);
491           context->rules_by_uid = NULL;
492         }
493
494       if (context->rules_by_gid)
495         {
496           _dbus_hash_table_unref (context->rules_by_gid);
497           context->rules_by_gid = NULL;
498         }
499       
500       dbus_free (context->address);
501       dbus_free (context);
502     }
503 }
504
505 BusRegistry*
506 bus_context_get_registry (BusContext  *context)
507 {
508   return context->registry;
509 }
510
511 BusConnections*
512 bus_context_get_connections (BusContext  *context)
513 {
514   return context->connections;
515 }
516
517 BusActivation*
518 bus_context_get_activation (BusContext  *context)
519 {
520   return context->activation;
521 }
522
523 static dbus_bool_t
524 list_allows_user (dbus_bool_t           def,
525                   DBusList            **list,
526                   unsigned long         uid,
527                   const unsigned long  *group_ids,
528                   int                   n_group_ids)
529 {
530   DBusList *link;
531   dbus_bool_t allowed;
532   
533   allowed = def;
534
535   link = _dbus_list_get_first_link (list);
536   while (link != NULL)
537     {
538       BusPolicyRule *rule = link->data;
539       link = _dbus_list_get_next_link (list, link);
540       
541       if (rule->type == BUS_POLICY_RULE_USER)
542         {
543           if (rule->d.user.uid != uid)
544             continue;
545         }
546       else if (rule->type == BUS_POLICY_RULE_GROUP)
547         {
548           int i;
549
550           i = 0;
551           while (i < n_group_ids)
552             {
553               if (rule->d.group.gid == group_ids[i])
554                 break;
555               ++i;
556             }
557
558           if (i == n_group_ids)
559             continue;
560         }
561       else
562         continue;
563
564       allowed = rule->allow;
565     }
566   
567   return allowed;
568 }
569
570 dbus_bool_t
571 bus_context_allow_user (BusContext   *context,
572                         unsigned long uid)
573 {
574   dbus_bool_t allowed;
575   unsigned long *group_ids;
576   int n_group_ids;
577
578   /* On OOM or error we always reject the user */
579   if (!_dbus_get_groups (uid, &group_ids, &n_group_ids))
580     {
581       _dbus_verbose ("Did not get any groups for UID %lu\n",
582                      uid);
583       return FALSE;
584     }
585   
586   allowed = FALSE;
587
588   allowed = list_allows_user (allowed,
589                               &context->default_rules,
590                               uid,
591                               group_ids, n_group_ids);
592
593   allowed = list_allows_user (allowed,
594                               &context->mandatory_rules,
595                               uid,
596                               group_ids, n_group_ids);
597
598   dbus_free (group_ids);
599
600   return allowed;
601 }
602
603 static dbus_bool_t
604 add_list_to_policy (DBusList       **list,
605                     BusPolicy       *policy)
606 {
607   DBusList *link;
608
609   link = _dbus_list_get_first_link (list);
610   while (link != NULL)
611     {
612       BusPolicyRule *rule = link->data;
613       link = _dbus_list_get_next_link (list, link);
614
615       switch (rule->type)
616         {
617         case BUS_POLICY_RULE_USER:
618         case BUS_POLICY_RULE_GROUP:
619           /* These aren't per-connection policies */
620           break;
621
622         case BUS_POLICY_RULE_OWN:
623         case BUS_POLICY_RULE_SEND:
624         case BUS_POLICY_RULE_RECEIVE:
625           /* These are per-connection */
626           if (!bus_policy_append_rule (policy, rule))
627             return FALSE;
628           break;
629         }
630     }
631   
632   return TRUE;
633 }
634
635 BusPolicy*
636 bus_context_create_connection_policy (BusContext      *context,
637                                       DBusConnection  *connection)
638 {
639   BusPolicy *policy;
640   unsigned long uid;
641   DBusList **list;
642
643   _dbus_assert (dbus_connection_get_is_authenticated (connection));
644   
645   policy = bus_policy_new ();
646   if (policy == NULL)
647     return NULL;
648
649   if (!add_list_to_policy (&context->default_rules,
650                                       policy))
651     goto failed;
652
653   /* we avoid the overhead of looking up user's groups
654    * if we don't have any group rules anyway
655    */
656   if (_dbus_hash_table_get_n_entries (context->rules_by_gid) > 0)
657     {
658       const unsigned long *groups;
659       int n_groups;
660       int i;
661       
662       if (!bus_connection_get_groups (connection, &groups, &n_groups))
663         goto failed;
664       
665       i = 0;
666       while (i < n_groups)
667         {
668           list = _dbus_hash_table_lookup_ulong (context->rules_by_gid,
669                                                 groups[i]);
670           
671           if (list != NULL)
672             {
673               if (!add_list_to_policy (list, policy))
674                 goto failed;
675             }
676           
677           ++i;
678         }
679     }
680
681   if (!dbus_connection_get_unix_user (connection, &uid))
682     goto failed;
683
684   list = _dbus_hash_table_lookup_ulong (context->rules_by_uid,
685                                         uid);
686
687   if (!add_list_to_policy (list, policy))
688     goto failed;
689   
690   if (!add_list_to_policy (&context->mandatory_rules,
691                            policy))
692     goto failed;
693
694   bus_policy_optimize (policy);
695   
696   return policy;
697   
698  failed:
699   bus_policy_unref (policy);
700   return NULL;
701 }