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