bus.c revision 29c71168cd17b11eed65023c97aff401d5305b01
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
36struct 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
50static dbus_bool_t
51server_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
60static dbus_bool_t
61add_server_watch (DBusWatch  *watch,
62                  void       *data)
63{
64  return bus_loop_add_watch (watch, server_watch_callback, data,
65                             NULL);
66}
67
68static void
69remove_server_watch (DBusWatch  *watch,
70                     void       *data)
71{
72  bus_loop_remove_watch (watch, server_watch_callback, data);
73}
74
75
76static void
77server_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
84static dbus_bool_t
85add_server_timeout (DBusTimeout *timeout,
86                    void        *data)
87{
88  return bus_loop_add_timeout (timeout, server_timeout_callback, data, NULL);
89}
90
91static void
92remove_server_timeout (DBusTimeout *timeout,
93                       void        *data)
94{
95  bus_loop_remove_timeout (timeout, server_timeout_callback, data);
96}
97
98static void
99new_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
120static void
121free_rule_func (void *data,
122                void *user_data)
123{
124  BusPolicyRule *rule = data;
125
126  bus_policy_rule_unref (rule);
127}
128
129static void
130free_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
141static dbus_bool_t
142setup_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
174BusContext*
175bus_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, _DBUS_INT_MAX))
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
325static void
326shutdown_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
348void
349bus_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
362void
363bus_context_ref (BusContext *context)
364{
365  _dbus_assert (context->refcount > 0);
366  context->refcount += 1;
367}
368
369void
370bus_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
427BusRegistry*
428bus_context_get_registry (BusContext  *context)
429{
430  return context->registry;
431}
432
433BusConnections*
434bus_context_get_connections (BusContext  *context)
435{
436  return context->connections;
437}
438
439BusActivation*
440bus_context_get_activation (BusContext  *context)
441{
442  return context->activation;
443}
444
445static dbus_bool_t
446list_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
492dbus_bool_t
493bus_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
525static dbus_bool_t
526add_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
557BusPolicy*
558bus_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}
624