2003-03-31 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               DBusError  *error)
145 {  
146   dbus_server_set_new_connection_function (server,
147                                            new_connection_callback,
148                                            context, NULL);
149   
150   if (!dbus_server_set_watch_functions (server,
151                                         add_server_watch,
152                                         remove_server_watch,
153                                         NULL,
154                                         server,
155                                         NULL))
156     {
157       BUS_SET_OOM (error);
158       return FALSE;
159     }
160
161   if (!dbus_server_set_timeout_functions (server,
162                                           add_server_timeout,
163                                           remove_server_timeout,
164                                           NULL,
165                                           server, NULL))
166     {
167       BUS_SET_OOM (error);
168       return FALSE;
169     }
170
171   return TRUE;
172 }
173
174 BusContext*
175 bus_context_new (const DBusString *config_file,
176                  DBusError        *error)
177 {
178   BusContext *context;
179   DBusList *link;
180   DBusList **addresses;
181   BusConfigParser *parser;
182   DBusString full_address;
183   const char *service_dirs[] = { NULL, NULL };
184   
185   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
186
187   if (!_dbus_string_init (&full_address))
188     return NULL;
189
190   parser = NULL;
191   context = NULL;
192   
193   parser = bus_config_load (config_file, error);
194   if (parser == NULL)
195     goto failed;
196   
197   context = dbus_new0 (BusContext, 1);
198   if (context == NULL)
199     {
200       BUS_SET_OOM (error);
201       goto failed;
202     }
203   
204   context->refcount = 1;
205   
206   addresses = bus_config_parser_get_addresses (parser);  
207   
208   link = _dbus_list_get_first_link (addresses);
209   while (link != NULL)
210     {
211       DBusServer *server;
212       
213       server = dbus_server_listen (link->data, error);
214       if (server == NULL)
215         goto failed;
216       else if (!setup_server (context, server, error))
217         goto failed;
218
219       if (!_dbus_list_append (&context->servers, server))
220         {
221           BUS_SET_OOM (error);
222           goto failed;
223         }          
224       
225       link = _dbus_list_get_next_link (addresses, link);
226     }
227
228   /* We have to build the address backward, so that
229    * <listen> later in the config file have priority
230    */
231   link = _dbus_list_get_last_link (&context->servers);
232   while (link != NULL)
233     {
234       char *addr;
235       
236       addr = dbus_server_get_address (link->data);
237       if (addr == NULL)
238         {
239           BUS_SET_OOM (error);
240           goto failed;
241         }
242
243       if (_dbus_string_get_length (&full_address) > 0)
244         {
245           if (!_dbus_string_append (&full_address, ";"))
246             {
247               BUS_SET_OOM (error);
248               goto failed;
249             }
250         }
251
252       if (!_dbus_string_append (&full_address, addr))
253         {
254           BUS_SET_OOM (error);
255           goto failed;
256         }
257
258       dbus_free (addr);
259
260       link = _dbus_list_get_prev_link (&context->servers, link);
261     }
262
263   if (!_dbus_string_copy_data (&full_address, &context->address))
264     {
265       BUS_SET_OOM (error);
266       goto failed;
267     }
268   
269   context->activation = bus_activation_new (context, &full_address,
270                                             service_dirs, error);
271   if (context->activation == NULL)
272     {
273       _DBUS_ASSERT_ERROR_IS_SET (error);
274       goto failed;
275     }
276
277   context->connections = bus_connections_new (context);
278   if (context->connections == NULL)
279     {
280       BUS_SET_OOM (error);
281       goto failed;
282     }
283
284   context->registry = bus_registry_new (context);
285   if (context->registry == NULL)
286     {
287       BUS_SET_OOM (error);
288       goto failed;
289     }
290   
291   context->rules_by_uid = _dbus_hash_table_new (DBUS_HASH_ULONG,
292                                                 NULL,
293                                                 free_rule_list_func);
294   if (context->rules_by_uid == NULL)
295     {
296       BUS_SET_OOM (error);
297       goto failed;
298     }
299
300   context->rules_by_gid = _dbus_hash_table_new (DBUS_HASH_ULONG,
301                                                 NULL,
302                                                 free_rule_list_func);
303   if (context->rules_by_gid == NULL)
304     {
305       BUS_SET_OOM (error);
306       goto failed;
307     }
308
309   bus_config_parser_unref (parser);
310   _dbus_string_free (&full_address);
311   
312   return context;
313   
314  failed:
315   if (parser != NULL)
316     bus_config_parser_unref (parser);
317
318   if (context != NULL)
319     bus_context_unref (context);
320
321   _dbus_string_free (&full_address);
322   return NULL;
323 }
324
325 static void
326 shutdown_server (BusContext *context,
327                  DBusServer *server)
328 {
329   if (server == NULL ||
330       !dbus_server_get_is_connected (server))
331     return;
332   
333   if (!dbus_server_set_watch_functions (server,
334                                         NULL, NULL, NULL,
335                                         context,
336                                         NULL))
337     _dbus_assert_not_reached ("setting watch functions to NULL failed");
338   
339   if (!dbus_server_set_timeout_functions (server,
340                                           NULL, NULL, NULL,
341                                           context,
342                                           NULL))
343     _dbus_assert_not_reached ("setting timeout functions to NULL failed");
344   
345   dbus_server_disconnect (server);
346 }
347
348 void
349 bus_context_shutdown (BusContext  *context)
350 {
351   DBusList *link;
352
353   link = _dbus_list_get_first_link (&context->servers);
354   while (link != NULL)
355     {
356       shutdown_server (context, link->data);
357
358       link = _dbus_list_get_next_link (&context->servers, link);
359     }
360 }
361
362 void
363 bus_context_ref (BusContext *context)
364 {
365   _dbus_assert (context->refcount > 0);
366   context->refcount += 1;
367 }
368
369 void
370 bus_context_unref (BusContext *context)
371 {
372   _dbus_assert (context->refcount > 0);
373   context->refcount -= 1;
374
375   if (context->refcount == 0)
376     {
377       DBusList *link;
378       
379       _dbus_verbose ("Finalizing bus context %p\n", context);
380       
381       bus_context_shutdown (context);
382
383       if (context->connections)
384         {
385           bus_connections_unref (context->connections);
386           context->connections = NULL;
387         }
388       
389       if (context->registry)
390         {
391           bus_registry_unref (context->registry);
392           context->registry = NULL;
393         }
394       
395       if (context->activation)
396         {
397           bus_activation_unref (context->activation);
398           context->activation = NULL;
399         }
400
401       link = _dbus_list_get_first_link (&context->servers);
402       while (link != NULL)
403         {
404           dbus_server_unref (link->data);
405           
406           link = _dbus_list_get_next_link (&context->servers, link);
407         }
408       _dbus_list_clear (&context->servers);
409
410       if (context->rules_by_uid)
411         {
412           _dbus_hash_table_unref (context->rules_by_uid);
413           context->rules_by_uid = NULL;
414         }
415
416       if (context->rules_by_gid)
417         {
418           _dbus_hash_table_unref (context->rules_by_gid);
419           context->rules_by_gid = NULL;
420         }
421       
422       dbus_free (context->address);
423       dbus_free (context);
424     }
425 }
426
427 BusRegistry*
428 bus_context_get_registry (BusContext  *context)
429 {
430   return context->registry;
431 }
432
433 BusConnections*
434 bus_context_get_connections (BusContext  *context)
435 {
436   return context->connections;
437 }
438
439 BusActivation*
440 bus_context_get_activation (BusContext  *context)
441 {
442   return context->activation;
443 }
444
445 static dbus_bool_t
446 list_allows_user (dbus_bool_t           def,
447                   DBusList            **list,
448                   unsigned long         uid,
449                   const unsigned long  *group_ids,
450                   int                   n_group_ids)
451 {
452   DBusList *link;
453   dbus_bool_t allowed;
454   
455   allowed = def;
456
457   link = _dbus_list_get_first_link (list);
458   while (link != NULL)
459     {
460       BusPolicyRule *rule = link->data;
461       link = _dbus_list_get_next_link (list, link);
462       
463       if (rule->type == BUS_POLICY_RULE_USER)
464         {
465           if (rule->d.user.uid != uid)
466             continue;
467         }
468       else if (rule->type == BUS_POLICY_RULE_GROUP)
469         {
470           int i;
471
472           i = 0;
473           while (i < n_group_ids)
474             {
475               if (rule->d.group.gid == group_ids[i])
476                 break;
477               ++i;
478             }
479
480           if (i == n_group_ids)
481             continue;
482         }
483       else
484         continue;
485
486       allowed = rule->allow;
487     }
488   
489   return allowed;
490 }
491
492 dbus_bool_t
493 bus_context_allow_user (BusContext   *context,
494                         unsigned long uid)
495 {
496   dbus_bool_t allowed;
497   unsigned long *group_ids;
498   int n_group_ids;
499
500   /* On OOM or error we always reject the user */
501   if (!_dbus_get_groups (uid, &group_ids, &n_group_ids))
502     {
503       _dbus_verbose ("Did not get any groups for UID %lu\n",
504                      uid);
505       return FALSE;
506     }
507   
508   allowed = FALSE;
509
510   allowed = list_allows_user (allowed,
511                               &context->default_rules,
512                               uid,
513                               group_ids, n_group_ids);
514
515   allowed = list_allows_user (allowed,
516                               &context->mandatory_rules,
517                               uid,
518                               group_ids, n_group_ids);
519
520   dbus_free (group_ids);
521
522   return allowed;
523 }
524
525 static dbus_bool_t
526 add_list_to_policy (DBusList       **list,
527                     BusPolicy       *policy)
528 {
529   DBusList *link;
530
531   link = _dbus_list_get_first_link (list);
532   while (link != NULL)
533     {
534       BusPolicyRule *rule = link->data;
535       link = _dbus_list_get_next_link (list, link);
536
537       switch (rule->type)
538         {
539         case BUS_POLICY_RULE_USER:
540         case BUS_POLICY_RULE_GROUP:
541           /* These aren't per-connection policies */
542           break;
543
544         case BUS_POLICY_RULE_OWN:
545         case BUS_POLICY_RULE_SEND:
546         case BUS_POLICY_RULE_RECEIVE:
547           /* These are per-connection */
548           if (!bus_policy_append_rule (policy, rule))
549             return FALSE;
550           break;
551         }
552     }
553   
554   return TRUE;
555 }
556
557 BusPolicy*
558 bus_context_create_connection_policy (BusContext      *context,
559                                       DBusConnection  *connection)
560 {
561   BusPolicy *policy;
562   unsigned long uid;
563   DBusList **list;
564
565   _dbus_assert (dbus_connection_get_is_authenticated (connection));
566   
567   policy = bus_policy_new ();
568   if (policy == NULL)
569     return NULL;
570
571   if (!add_list_to_policy (&context->default_rules,
572                                       policy))
573     goto failed;
574
575   /* we avoid the overhead of looking up user's groups
576    * if we don't have any group rules anyway
577    */
578   if (_dbus_hash_table_get_n_entries (context->rules_by_gid) > 0)
579     {
580       const unsigned long *groups;
581       int n_groups;
582       int i;
583       
584       if (!bus_connection_get_groups (connection, &groups, &n_groups))
585         goto failed;
586       
587       i = 0;
588       while (i < n_groups)
589         {
590           list = _dbus_hash_table_lookup_ulong (context->rules_by_gid,
591                                                 groups[i]);
592           
593           if (list != NULL)
594             {
595               if (!add_list_to_policy (list, policy))
596                 goto failed;
597             }
598           
599           ++i;
600         }
601     }
602
603   if (!dbus_connection_get_unix_user (connection, &uid))
604     goto failed;
605
606   list = _dbus_hash_table_lookup_ulong (context->rules_by_uid,
607                                         uid);
608
609   if (!add_list_to_policy (list, policy))
610     goto failed;
611   
612   if (!add_list_to_policy (&context->mandatory_rules,
613                            policy))
614     goto failed;
615
616   bus_policy_optimize (policy);
617   
618   return policy;
619   
620  failed:
621   bus_policy_unref (policy);
622   return NULL;
623 }