1/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2/* config-parser.c  XML-library-agnostic configuration file parser
3 *
4 * Copyright (C) 2003, 2004 Red Hat, Inc.
5 *
6 * Licensed under the Academic Free License version 2.1
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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21 *
22 */
23
24#include <config.h>
25#include "config-parser-common.h"
26#include "config-parser.h"
27#include "test.h"
28#include "utils.h"
29#include "policy.h"
30#include "selinux.h"
31#include <dbus/dbus-list.h>
32#include <dbus/dbus-internals.h>
33#include <string.h>
34
35typedef enum
36{
37  /* we ignore policies for unknown groups/users */
38  POLICY_IGNORED,
39
40  /* non-ignored */
41  POLICY_DEFAULT,
42  POLICY_MANDATORY,
43  POLICY_USER,
44  POLICY_GROUP,
45  POLICY_CONSOLE
46} PolicyType;
47
48typedef struct
49{
50  ElementType type;
51
52  unsigned int had_content : 1;
53
54  union
55  {
56    struct
57    {
58      unsigned int ignore_missing : 1;
59      unsigned int if_selinux_enabled : 1;
60      unsigned int selinux_root_relative : 1;
61    } include;
62
63    struct
64    {
65      PolicyType type;
66      unsigned long gid_uid_or_at_console;
67    } policy;
68
69    struct
70    {
71      char *name;
72      long value;
73    } limit;
74
75  } d;
76
77} Element;
78
79/**
80 * Parser for bus configuration file.
81 */
82struct BusConfigParser
83{
84  int refcount;        /**< Reference count */
85
86  DBusString basedir;  /**< Directory we resolve paths relative to */
87
88  DBusList *stack;     /**< stack of Element */
89
90  char *user;          /**< user to run as */
91
92  char *servicehelper; /**< location of the setuid helper */
93
94  char *bus_type;          /**< Message bus type */
95
96  DBusList *listen_on; /**< List of addresses to listen to */
97
98  DBusList *mechanisms; /**< Auth mechanisms */
99
100  DBusList *service_dirs; /**< Directories to look for session services in */
101
102  DBusList *conf_dirs;   /**< Directories to look for policy configuration in */
103
104  BusPolicy *policy;     /**< Security policy */
105
106  BusLimits limits;      /**< Limits */
107
108  char *pidfile;         /**< PID file */
109
110  DBusList *included_files;  /**< Included files stack */
111
112  DBusHashTable *service_context_table; /**< Map service names to SELinux contexts */
113
114  unsigned int fork : 1; /**< TRUE to fork into daemon mode */
115
116  unsigned int syslog : 1; /**< TRUE to enable syslog */
117  unsigned int keep_umask : 1; /**< TRUE to keep original umask when forking */
118
119  unsigned int is_toplevel : 1; /**< FALSE if we are a sub-config-file inside another one */
120
121  unsigned int allow_anonymous : 1; /**< TRUE to allow anonymous connections */
122};
123
124static Element*
125push_element (BusConfigParser *parser,
126              ElementType      type)
127{
128  Element *e;
129
130  _dbus_assert (type != ELEMENT_NONE);
131
132  e = dbus_new0 (Element, 1);
133  if (e == NULL)
134    return NULL;
135
136  if (!_dbus_list_append (&parser->stack, e))
137    {
138      dbus_free (e);
139      return NULL;
140    }
141
142  e->type = type;
143
144  return e;
145}
146
147static void
148element_free (Element *e)
149{
150  if (e->type == ELEMENT_LIMIT)
151    dbus_free (e->d.limit.name);
152
153  dbus_free (e);
154}
155
156static void
157pop_element (BusConfigParser *parser)
158{
159  Element *e;
160
161  e = _dbus_list_pop_last (&parser->stack);
162
163  element_free (e);
164}
165
166static Element*
167peek_element (BusConfigParser *parser)
168{
169  Element *e;
170
171  e = _dbus_list_get_last (&parser->stack);
172
173  return e;
174}
175
176static ElementType
177top_element_type (BusConfigParser *parser)
178{
179  Element *e;
180
181  e = _dbus_list_get_last (&parser->stack);
182
183  if (e)
184    return e->type;
185  else
186    return ELEMENT_NONE;
187}
188
189static dbus_bool_t
190merge_service_context_hash (DBusHashTable *dest,
191			    DBusHashTable *from)
192{
193  DBusHashIter iter;
194  char *service_copy;
195  char *context_copy;
196
197  service_copy = NULL;
198  context_copy = NULL;
199
200  _dbus_hash_iter_init (from, &iter);
201  while (_dbus_hash_iter_next (&iter))
202    {
203      const char *service = _dbus_hash_iter_get_string_key (&iter);
204      const char *context = _dbus_hash_iter_get_value (&iter);
205
206      service_copy = _dbus_strdup (service);
207      if (service_copy == NULL)
208        goto fail;
209      context_copy = _dbus_strdup (context);
210      if (context_copy == NULL)
211        goto fail;
212
213      if (!_dbus_hash_table_insert_string (dest, service_copy, context_copy))
214        goto fail;
215
216      service_copy = NULL;
217      context_copy = NULL;
218    }
219
220  return TRUE;
221
222 fail:
223  if (service_copy)
224    dbus_free (service_copy);
225
226  if (context_copy)
227    dbus_free (context_copy);
228
229  return FALSE;
230}
231
232static dbus_bool_t
233service_dirs_find_dir (DBusList **service_dirs,
234                       const char *dir)
235{
236  DBusList *link;
237
238  _dbus_assert (dir != NULL);
239
240  for (link = *service_dirs; link; link = _dbus_list_get_next_link(service_dirs, link))
241    {
242      const char *link_dir;
243
244      link_dir = (const char *)link->data;
245      if (strcmp (dir, link_dir) == 0)
246        return TRUE;
247    }
248
249  return FALSE;
250}
251
252static dbus_bool_t
253service_dirs_append_unique_or_free (DBusList **service_dirs,
254                                    char *dir)
255{
256  if (!service_dirs_find_dir (service_dirs, dir))
257    return _dbus_list_append (service_dirs, dir);
258
259  dbus_free (dir);
260  return TRUE;
261}
262
263static void
264service_dirs_append_link_unique_or_free (DBusList **service_dirs,
265                                         DBusList *dir_link)
266{
267  if (!service_dirs_find_dir (service_dirs, dir_link->data))
268    {
269      _dbus_list_append_link (service_dirs, dir_link);
270    }
271  else
272    {
273      dbus_free (dir_link->data);
274      _dbus_list_free_link (dir_link);
275    }
276}
277
278static dbus_bool_t
279merge_included (BusConfigParser *parser,
280                BusConfigParser *included,
281                DBusError       *error)
282{
283  DBusList *link;
284
285  if (!bus_policy_merge (parser->policy,
286                         included->policy))
287    {
288      BUS_SET_OOM (error);
289      return FALSE;
290    }
291
292  if (!merge_service_context_hash (parser->service_context_table,
293				   included->service_context_table))
294    {
295      BUS_SET_OOM (error);
296      return FALSE;
297    }
298
299  if (included->user != NULL)
300    {
301      dbus_free (parser->user);
302      parser->user = included->user;
303      included->user = NULL;
304    }
305
306  if (included->bus_type != NULL)
307    {
308      dbus_free (parser->bus_type);
309      parser->bus_type = included->bus_type;
310      included->bus_type = NULL;
311    }
312
313  if (included->fork)
314    parser->fork = TRUE;
315
316  if (included->keep_umask)
317    parser->keep_umask = TRUE;
318
319  if (included->pidfile != NULL)
320    {
321      dbus_free (parser->pidfile);
322      parser->pidfile = included->pidfile;
323      included->pidfile = NULL;
324    }
325
326  while ((link = _dbus_list_pop_first_link (&included->listen_on)))
327    _dbus_list_append_link (&parser->listen_on, link);
328
329  while ((link = _dbus_list_pop_first_link (&included->mechanisms)))
330    _dbus_list_append_link (&parser->mechanisms, link);
331
332  while ((link = _dbus_list_pop_first_link (&included->service_dirs)))
333    service_dirs_append_link_unique_or_free (&parser->service_dirs, link);
334
335  while ((link = _dbus_list_pop_first_link (&included->conf_dirs)))
336    _dbus_list_append_link (&parser->conf_dirs, link);
337
338  return TRUE;
339}
340
341static dbus_bool_t
342seen_include (BusConfigParser  *parser,
343	      const DBusString *file)
344{
345  DBusList *iter;
346
347  iter = parser->included_files;
348  while (iter != NULL)
349    {
350      if (! strcmp (_dbus_string_get_const_data (file), iter->data))
351	return TRUE;
352
353      iter = _dbus_list_get_next_link (&parser->included_files, iter);
354    }
355
356  return FALSE;
357}
358
359BusConfigParser*
360bus_config_parser_new (const DBusString      *basedir,
361                       dbus_bool_t            is_toplevel,
362                       const BusConfigParser *parent)
363{
364  BusConfigParser *parser;
365
366  parser = dbus_new0 (BusConfigParser, 1);
367  if (parser == NULL)
368    return NULL;
369
370  parser->is_toplevel = !!is_toplevel;
371
372  if (!_dbus_string_init (&parser->basedir))
373    {
374      dbus_free (parser);
375      return NULL;
376    }
377
378  if (((parser->policy = bus_policy_new ()) == NULL) ||
379      !_dbus_string_copy (basedir, 0, &parser->basedir, 0) ||
380      ((parser->service_context_table = _dbus_hash_table_new (DBUS_HASH_STRING,
381							      dbus_free,
382							      dbus_free)) == NULL))
383    {
384      if (parser->policy)
385        bus_policy_unref (parser->policy);
386
387      _dbus_string_free (&parser->basedir);
388
389      dbus_free (parser);
390      return NULL;
391    }
392
393  if (parent != NULL)
394    {
395      /* Initialize the parser's limits from the parent. */
396      parser->limits = parent->limits;
397
398      /* Use the parent's list of included_files to avoid
399	 circular inclusions. */
400      parser->included_files = parent->included_files;
401    }
402  else
403    {
404
405      /* Make up some numbers! woot! */
406      parser->limits.max_incoming_bytes = _DBUS_ONE_MEGABYTE * 127;
407      parser->limits.max_outgoing_bytes = _DBUS_ONE_MEGABYTE * 127;
408      parser->limits.max_message_size = _DBUS_ONE_MEGABYTE * 32;
409
410      /* We set relatively conservative values here since due to the
411      way SCM_RIGHTS works we need to preallocate an array for the
412      maximum number of file descriptors we can receive. Picking a
413      high value here thus translates directly to more memory
414      allocation. */
415      parser->limits.max_incoming_unix_fds = 1024*4;
416      parser->limits.max_outgoing_unix_fds = 1024*4;
417      parser->limits.max_message_unix_fds = 1024;
418
419      /* Making this long means the user has to wait longer for an error
420       * message if something screws up, but making it too short means
421       * they might see a false failure.
422       */
423      parser->limits.activation_timeout = 25000; /* 25 seconds */
424
425      /* Making this long risks making a DOS attack easier, but too short
426       * and legitimate auth will fail.  If interactive auth (ask user for
427       * password) is allowed, then potentially it has to be quite long.
428       */
429      parser->limits.auth_timeout = 30000; /* 30 seconds */
430
431      parser->limits.max_incomplete_connections = 64;
432      parser->limits.max_connections_per_user = 256;
433
434      /* Note that max_completed_connections / max_connections_per_user
435       * is the number of users that would have to work together to
436       * DOS all the other users.
437       */
438      parser->limits.max_completed_connections = 2048;
439
440      parser->limits.max_pending_activations = 512;
441      parser->limits.max_services_per_connection = 512;
442
443      /* For this one, keep in mind that it isn't only the memory used
444       * by the match rules, but slowdown from linearly walking a big
445       * list of them. A client adding more than this is almost
446       * certainly a bad idea for that reason, and should change to a
447       * smaller number of wider-net match rules - getting every last
448       * message to the bus is probably better than having a thousand
449       * match rules.
450       */
451      parser->limits.max_match_rules_per_connection = 512;
452
453      parser->limits.reply_timeout = -1; /* never */
454
455      /* this is effectively a limit on message queue size for messages
456       * that require a reply
457       */
458      parser->limits.max_replies_per_connection = 1024*8;
459    }
460
461  parser->refcount = 1;
462
463  return parser;
464}
465
466BusConfigParser *
467bus_config_parser_ref (BusConfigParser *parser)
468{
469  _dbus_assert (parser->refcount > 0);
470
471  parser->refcount += 1;
472
473  return parser;
474}
475
476void
477bus_config_parser_unref (BusConfigParser *parser)
478{
479  _dbus_assert (parser->refcount > 0);
480
481  parser->refcount -= 1;
482
483  if (parser->refcount == 0)
484    {
485      while (parser->stack != NULL)
486        pop_element (parser);
487
488      dbus_free (parser->user);
489      dbus_free (parser->servicehelper);
490      dbus_free (parser->bus_type);
491      dbus_free (parser->pidfile);
492
493      _dbus_list_foreach (&parser->listen_on,
494                          (DBusForeachFunction) dbus_free,
495                          NULL);
496
497      _dbus_list_clear (&parser->listen_on);
498
499      _dbus_list_foreach (&parser->service_dirs,
500                          (DBusForeachFunction) dbus_free,
501                          NULL);
502
503      _dbus_list_clear (&parser->service_dirs);
504
505      _dbus_list_foreach (&parser->conf_dirs,
506                          (DBusForeachFunction) dbus_free,
507                          NULL);
508
509      _dbus_list_clear (&parser->conf_dirs);
510
511      _dbus_list_foreach (&parser->mechanisms,
512                          (DBusForeachFunction) dbus_free,
513                          NULL);
514
515      _dbus_list_clear (&parser->mechanisms);
516
517      _dbus_string_free (&parser->basedir);
518
519      if (parser->policy)
520        bus_policy_unref (parser->policy);
521
522      if (parser->service_context_table)
523        _dbus_hash_table_unref (parser->service_context_table);
524
525      dbus_free (parser);
526    }
527}
528
529dbus_bool_t
530bus_config_parser_check_doctype (BusConfigParser   *parser,
531                                 const char        *doctype,
532                                 DBusError         *error)
533{
534  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
535
536  if (strcmp (doctype, "busconfig") != 0)
537    {
538      dbus_set_error (error,
539                      DBUS_ERROR_FAILED,
540                      "Configuration file has the wrong document type %s",
541                      doctype);
542      return FALSE;
543    }
544  else
545    return TRUE;
546}
547
548typedef struct
549{
550  const char  *name;
551  const char **retloc;
552} LocateAttr;
553
554static dbus_bool_t
555locate_attributes (BusConfigParser  *parser,
556                   const char       *element_name,
557                   const char      **attribute_names,
558                   const char      **attribute_values,
559                   DBusError        *error,
560                   const char       *first_attribute_name,
561                   const char      **first_attribute_retloc,
562                   ...)
563{
564  va_list args;
565  const char *name;
566  const char **retloc;
567  int n_attrs;
568#define MAX_ATTRS 24
569  LocateAttr attrs[MAX_ATTRS];
570  dbus_bool_t retval;
571  int i;
572
573  _dbus_assert (first_attribute_name != NULL);
574  _dbus_assert (first_attribute_retloc != NULL);
575
576  retval = TRUE;
577
578  n_attrs = 1;
579  attrs[0].name = first_attribute_name;
580  attrs[0].retloc = first_attribute_retloc;
581  *first_attribute_retloc = NULL;
582
583  va_start (args, first_attribute_retloc);
584
585  name = va_arg (args, const char*);
586  retloc = va_arg (args, const char**);
587
588  while (name != NULL)
589    {
590      _dbus_assert (retloc != NULL);
591      _dbus_assert (n_attrs < MAX_ATTRS);
592
593      attrs[n_attrs].name = name;
594      attrs[n_attrs].retloc = retloc;
595      n_attrs += 1;
596      *retloc = NULL;
597
598      name = va_arg (args, const char*);
599      retloc = va_arg (args, const char**);
600    }
601
602  va_end (args);
603
604  i = 0;
605  while (attribute_names[i])
606    {
607      int j;
608      dbus_bool_t found;
609
610      found = FALSE;
611      j = 0;
612      while (j < n_attrs)
613        {
614          if (strcmp (attrs[j].name, attribute_names[i]) == 0)
615            {
616              retloc = attrs[j].retloc;
617
618              if (*retloc != NULL)
619                {
620                  dbus_set_error (error, DBUS_ERROR_FAILED,
621                                  "Attribute \"%s\" repeated twice on the same <%s> element",
622                                  attrs[j].name, element_name);
623                  retval = FALSE;
624                  goto out;
625                }
626
627              *retloc = attribute_values[i];
628              found = TRUE;
629            }
630
631          ++j;
632        }
633
634      if (!found)
635        {
636          dbus_set_error (error, DBUS_ERROR_FAILED,
637                          "Attribute \"%s\" is invalid on <%s> element in this context",
638                          attribute_names[i], element_name);
639          retval = FALSE;
640          goto out;
641        }
642
643      ++i;
644    }
645
646 out:
647  return retval;
648}
649
650static dbus_bool_t
651check_no_attributes (BusConfigParser  *parser,
652                     const char       *element_name,
653                     const char      **attribute_names,
654                     const char      **attribute_values,
655                     DBusError        *error)
656{
657  if (attribute_names[0] != NULL)
658    {
659      dbus_set_error (error, DBUS_ERROR_FAILED,
660                      "Attribute \"%s\" is invalid on <%s> element in this context",
661                      attribute_names[0], element_name);
662      return FALSE;
663    }
664
665  return TRUE;
666}
667
668static dbus_bool_t
669start_busconfig_child (BusConfigParser   *parser,
670                       const char        *element_name,
671                       const char       **attribute_names,
672                       const char       **attribute_values,
673                       DBusError         *error)
674{
675  ElementType element_type;
676
677  element_type = bus_config_parser_element_name_to_type (element_name);
678
679  if (element_type == ELEMENT_USER)
680    {
681      if (!check_no_attributes (parser, "user", attribute_names, attribute_values, error))
682        return FALSE;
683
684      if (push_element (parser, ELEMENT_USER) == NULL)
685        {
686          BUS_SET_OOM (error);
687          return FALSE;
688        }
689
690      return TRUE;
691    }
692  else if (element_type == ELEMENT_TYPE)
693    {
694      if (!check_no_attributes (parser, "type", attribute_names, attribute_values, error))
695        return FALSE;
696
697      if (push_element (parser, ELEMENT_TYPE) == NULL)
698        {
699          BUS_SET_OOM (error);
700          return FALSE;
701        }
702
703      return TRUE;
704    }
705  else if (element_type == ELEMENT_FORK)
706    {
707      if (!check_no_attributes (parser, "fork", attribute_names, attribute_values, error))
708        return FALSE;
709
710      if (push_element (parser, ELEMENT_FORK) == NULL)
711        {
712          BUS_SET_OOM (error);
713          return FALSE;
714        }
715
716      parser->fork = TRUE;
717
718      return TRUE;
719    }
720  else if (element_type == ELEMENT_SYSLOG)
721    {
722      if (!check_no_attributes (parser, "syslog", attribute_names, attribute_values, error))
723        return FALSE;
724
725      if (push_element (parser, ELEMENT_SYSLOG) == NULL)
726        {
727          BUS_SET_OOM (error);
728          return FALSE;
729        }
730
731      parser->syslog = TRUE;
732
733      return TRUE;
734    }
735  else if (element_type == ELEMENT_KEEP_UMASK)
736    {
737      if (!check_no_attributes (parser, "keep_umask", attribute_names, attribute_values, error))
738        return FALSE;
739
740      if (push_element (parser, ELEMENT_KEEP_UMASK) == NULL)
741        {
742          BUS_SET_OOM (error);
743          return FALSE;
744        }
745
746      parser->keep_umask = TRUE;
747
748      return TRUE;
749    }
750  else if (element_type == ELEMENT_PIDFILE)
751    {
752      if (!check_no_attributes (parser, "pidfile", attribute_names, attribute_values, error))
753        return FALSE;
754
755      if (push_element (parser, ELEMENT_PIDFILE) == NULL)
756        {
757          BUS_SET_OOM (error);
758          return FALSE;
759        }
760
761      return TRUE;
762    }
763  else if (element_type == ELEMENT_LISTEN)
764    {
765      if (!check_no_attributes (parser, "listen", attribute_names, attribute_values, error))
766        return FALSE;
767
768      if (push_element (parser, ELEMENT_LISTEN) == NULL)
769        {
770          BUS_SET_OOM (error);
771          return FALSE;
772        }
773
774      return TRUE;
775    }
776  else if (element_type == ELEMENT_AUTH)
777    {
778      if (!check_no_attributes (parser, "auth", attribute_names, attribute_values, error))
779        return FALSE;
780
781      if (push_element (parser, ELEMENT_AUTH) == NULL)
782        {
783          BUS_SET_OOM (error);
784          return FALSE;
785        }
786
787      return TRUE;
788    }
789  else if (element_type == ELEMENT_SERVICEHELPER)
790    {
791      if (!check_no_attributes (parser, "servicehelper", attribute_names, attribute_values, error))
792        return FALSE;
793
794      if (push_element (parser, ELEMENT_SERVICEHELPER) == NULL)
795        {
796          BUS_SET_OOM (error);
797          return FALSE;
798        }
799
800      return TRUE;
801    }
802  else if (element_type == ELEMENT_INCLUDEDIR)
803    {
804      if (!check_no_attributes (parser, "includedir", attribute_names, attribute_values, error))
805        return FALSE;
806
807      if (push_element (parser, ELEMENT_INCLUDEDIR) == NULL)
808        {
809          BUS_SET_OOM (error);
810          return FALSE;
811        }
812
813      return TRUE;
814    }
815  else if (element_type == ELEMENT_STANDARD_SESSION_SERVICEDIRS)
816    {
817      DBusList *link;
818      DBusList *dirs;
819      dirs = NULL;
820
821      if (!check_no_attributes (parser, "standard_session_servicedirs", attribute_names, attribute_values, error))
822        return FALSE;
823
824      if (push_element (parser, ELEMENT_STANDARD_SESSION_SERVICEDIRS) == NULL)
825        {
826          BUS_SET_OOM (error);
827          return FALSE;
828        }
829
830      if (!_dbus_get_standard_session_servicedirs (&dirs))
831        {
832          BUS_SET_OOM (error);
833          return FALSE;
834        }
835
836        while ((link = _dbus_list_pop_first_link (&dirs)))
837          service_dirs_append_link_unique_or_free (&parser->service_dirs, link);
838
839      return TRUE;
840    }
841  else if (element_type == ELEMENT_STANDARD_SYSTEM_SERVICEDIRS)
842    {
843      DBusList *link;
844      DBusList *dirs;
845      dirs = NULL;
846
847      if (!check_no_attributes (parser, "standard_system_servicedirs", attribute_names, attribute_values, error))
848        return FALSE;
849
850      if (push_element (parser, ELEMENT_STANDARD_SYSTEM_SERVICEDIRS) == NULL)
851        {
852          BUS_SET_OOM (error);
853          return FALSE;
854        }
855
856      if (!_dbus_get_standard_system_servicedirs (&dirs))
857        {
858          BUS_SET_OOM (error);
859          return FALSE;
860        }
861
862        while ((link = _dbus_list_pop_first_link (&dirs)))
863          service_dirs_append_link_unique_or_free (&parser->service_dirs, link);
864
865      return TRUE;
866    }
867  else if (element_type == ELEMENT_ALLOW_ANONYMOUS)
868    {
869      if (!check_no_attributes (parser, "allow_anonymous", attribute_names, attribute_values, error))
870        return FALSE;
871
872      if (push_element (parser, ELEMENT_ALLOW_ANONYMOUS) == NULL)
873        {
874          BUS_SET_OOM (error);
875          return FALSE;
876        }
877
878      parser->allow_anonymous = TRUE;
879      return TRUE;
880    }
881  else if (element_type == ELEMENT_SERVICEDIR)
882    {
883      if (!check_no_attributes (parser, "servicedir", attribute_names, attribute_values, error))
884        return FALSE;
885
886      if (push_element (parser, ELEMENT_SERVICEDIR) == NULL)
887        {
888          BUS_SET_OOM (error);
889          return FALSE;
890        }
891
892      return TRUE;
893    }
894  else if (element_type == ELEMENT_INCLUDE)
895    {
896      Element *e;
897      const char *if_selinux_enabled;
898      const char *ignore_missing;
899      const char *selinux_root_relative;
900
901      if ((e = push_element (parser, ELEMENT_INCLUDE)) == NULL)
902        {
903          BUS_SET_OOM (error);
904          return FALSE;
905        }
906
907      e->d.include.ignore_missing = FALSE;
908      e->d.include.if_selinux_enabled = FALSE;
909      e->d.include.selinux_root_relative = FALSE;
910
911      if (!locate_attributes (parser, "include",
912                              attribute_names,
913                              attribute_values,
914                              error,
915                              "ignore_missing", &ignore_missing,
916                              "if_selinux_enabled", &if_selinux_enabled,
917                              "selinux_root_relative", &selinux_root_relative,
918                              NULL))
919        return FALSE;
920
921      if (ignore_missing != NULL)
922        {
923          if (strcmp (ignore_missing, "yes") == 0)
924            e->d.include.ignore_missing = TRUE;
925          else if (strcmp (ignore_missing, "no") == 0)
926            e->d.include.ignore_missing = FALSE;
927          else
928            {
929              dbus_set_error (error, DBUS_ERROR_FAILED,
930                              "ignore_missing attribute must have value \"yes\" or \"no\"");
931              return FALSE;
932            }
933        }
934
935      if (if_selinux_enabled != NULL)
936        {
937          if (strcmp (if_selinux_enabled, "yes") == 0)
938            e->d.include.if_selinux_enabled = TRUE;
939          else if (strcmp (if_selinux_enabled, "no") == 0)
940            e->d.include.if_selinux_enabled = FALSE;
941          else
942            {
943              dbus_set_error (error, DBUS_ERROR_FAILED,
944                              "if_selinux_enabled attribute must have value"
945                              " \"yes\" or \"no\"");
946              return FALSE;
947	    }
948        }
949
950      if (selinux_root_relative != NULL)
951        {
952          if (strcmp (selinux_root_relative, "yes") == 0)
953            e->d.include.selinux_root_relative = TRUE;
954          else if (strcmp (selinux_root_relative, "no") == 0)
955            e->d.include.selinux_root_relative = FALSE;
956          else
957            {
958              dbus_set_error (error, DBUS_ERROR_FAILED,
959                              "selinux_root_relative attribute must have value"
960                              " \"yes\" or \"no\"");
961              return FALSE;
962	    }
963        }
964
965      return TRUE;
966    }
967  else if (element_type == ELEMENT_POLICY)
968    {
969      Element *e;
970      const char *context;
971      const char *user;
972      const char *group;
973      const char *at_console;
974
975      if ((e = push_element (parser, ELEMENT_POLICY)) == NULL)
976        {
977          BUS_SET_OOM (error);
978          return FALSE;
979        }
980
981      e->d.policy.type = POLICY_IGNORED;
982
983      if (!locate_attributes (parser, "policy",
984                              attribute_names,
985                              attribute_values,
986                              error,
987                              "context", &context,
988                              "user", &user,
989                              "group", &group,
990                              "at_console", &at_console,
991                              NULL))
992        return FALSE;
993
994      if (((context && user) ||
995           (context && group) ||
996           (context && at_console)) ||
997           ((user && group) ||
998           (user && at_console)) ||
999           (group && at_console) ||
1000          !(context || user || group || at_console))
1001        {
1002          dbus_set_error (error, DBUS_ERROR_FAILED,
1003                          "<policy> element must have exactly one of (context|user|group|at_console) attributes");
1004          return FALSE;
1005        }
1006
1007      if (context != NULL)
1008        {
1009          if (strcmp (context, "default") == 0)
1010            {
1011              e->d.policy.type = POLICY_DEFAULT;
1012            }
1013          else if (strcmp (context, "mandatory") == 0)
1014            {
1015              e->d.policy.type = POLICY_MANDATORY;
1016            }
1017          else
1018            {
1019              dbus_set_error (error, DBUS_ERROR_FAILED,
1020                              "context attribute on <policy> must have the value \"default\" or \"mandatory\", not \"%s\"",
1021                              context);
1022              return FALSE;
1023            }
1024        }
1025      else if (user != NULL)
1026        {
1027          DBusString username;
1028          _dbus_string_init_const (&username, user);
1029
1030          if (_dbus_parse_unix_user_from_config (&username,
1031                                                 &e->d.policy.gid_uid_or_at_console))
1032            e->d.policy.type = POLICY_USER;
1033          else
1034            _dbus_warn ("Unknown username \"%s\" in message bus configuration file\n",
1035                        user);
1036        }
1037      else if (group != NULL)
1038        {
1039          DBusString group_name;
1040          _dbus_string_init_const (&group_name, group);
1041
1042          if (_dbus_parse_unix_group_from_config (&group_name,
1043                                                  &e->d.policy.gid_uid_or_at_console))
1044            e->d.policy.type = POLICY_GROUP;
1045          else
1046            _dbus_warn ("Unknown group \"%s\" in message bus configuration file\n",
1047                        group);
1048        }
1049      else if (at_console != NULL)
1050        {
1051           dbus_bool_t t;
1052           t = (strcmp (at_console, "true") == 0);
1053           if (t || strcmp (at_console, "false") == 0)
1054             {
1055               e->d.policy.gid_uid_or_at_console = t;
1056               e->d.policy.type = POLICY_CONSOLE;
1057             }
1058           else
1059             {
1060               dbus_set_error (error, DBUS_ERROR_FAILED,
1061                              "Unknown value \"%s\" for at_console in message bus configuration file",
1062                              at_console);
1063
1064               return FALSE;
1065             }
1066        }
1067      else
1068        {
1069          _dbus_assert_not_reached ("all <policy> attributes null and we didn't set error");
1070        }
1071
1072      return TRUE;
1073    }
1074  else if (element_type == ELEMENT_LIMIT)
1075    {
1076      Element *e;
1077      const char *name;
1078
1079      if ((e = push_element (parser, ELEMENT_LIMIT)) == NULL)
1080        {
1081          BUS_SET_OOM (error);
1082          return FALSE;
1083        }
1084
1085      if (!locate_attributes (parser, "limit",
1086                              attribute_names,
1087                              attribute_values,
1088                              error,
1089                              "name", &name,
1090                              NULL))
1091        return FALSE;
1092
1093      if (name == NULL)
1094        {
1095          dbus_set_error (error, DBUS_ERROR_FAILED,
1096                          "<limit> element must have a \"name\" attribute");
1097          return FALSE;
1098        }
1099
1100      e->d.limit.name = _dbus_strdup (name);
1101      if (e->d.limit.name == NULL)
1102        {
1103          BUS_SET_OOM (error);
1104          return FALSE;
1105        }
1106
1107      return TRUE;
1108    }
1109  else if (element_type == ELEMENT_SELINUX)
1110    {
1111      if (!check_no_attributes (parser, "selinux", attribute_names, attribute_values, error))
1112        return FALSE;
1113
1114      if (push_element (parser, ELEMENT_SELINUX) == NULL)
1115        {
1116          BUS_SET_OOM (error);
1117          return FALSE;
1118        }
1119
1120      return TRUE;
1121    }
1122  else
1123    {
1124      dbus_set_error (error, DBUS_ERROR_FAILED,
1125                      "Element <%s> not allowed inside <%s> in configuration file",
1126                      element_name, "busconfig");
1127      return FALSE;
1128    }
1129}
1130
1131static dbus_bool_t
1132append_rule_from_element (BusConfigParser   *parser,
1133                          const char        *element_name,
1134                          const char       **attribute_names,
1135                          const char       **attribute_values,
1136                          dbus_bool_t        allow,
1137                          DBusError         *error)
1138{
1139  const char *log;
1140  const char *send_interface;
1141  const char *send_member;
1142  const char *send_error;
1143  const char *send_destination;
1144  const char *send_path;
1145  const char *send_type;
1146  const char *receive_interface;
1147  const char *receive_member;
1148  const char *receive_error;
1149  const char *receive_sender;
1150  const char *receive_path;
1151  const char *receive_type;
1152  const char *eavesdrop;
1153  const char *send_requested_reply;
1154  const char *receive_requested_reply;
1155  const char *own;
1156  const char *user;
1157  const char *group;
1158
1159  BusPolicyRule *rule;
1160
1161  if (!locate_attributes (parser, element_name,
1162                          attribute_names,
1163                          attribute_values,
1164                          error,
1165                          "send_interface", &send_interface,
1166                          "send_member", &send_member,
1167                          "send_error", &send_error,
1168                          "send_destination", &send_destination,
1169                          "send_path", &send_path,
1170                          "send_type", &send_type,
1171                          "receive_interface", &receive_interface,
1172                          "receive_member", &receive_member,
1173                          "receive_error", &receive_error,
1174                          "receive_sender", &receive_sender,
1175                          "receive_path", &receive_path,
1176                          "receive_type", &receive_type,
1177                          "eavesdrop", &eavesdrop,
1178                          "send_requested_reply", &send_requested_reply,
1179                          "receive_requested_reply", &receive_requested_reply,
1180                          "own", &own,
1181                          "user", &user,
1182                          "group", &group,
1183                          "log", &log,
1184                          NULL))
1185    return FALSE;
1186
1187  if (!(send_interface || send_member || send_error || send_destination ||
1188        send_type || send_path ||
1189        receive_interface || receive_member || receive_error || receive_sender ||
1190        receive_type || receive_path || eavesdrop ||
1191        send_requested_reply || receive_requested_reply ||
1192        own || user || group))
1193    {
1194      dbus_set_error (error, DBUS_ERROR_FAILED,
1195                      "Element <%s> must have one or more attributes",
1196                      element_name);
1197      return FALSE;
1198    }
1199
1200  if ((send_member && (send_interface == NULL && send_path == NULL)) ||
1201      (receive_member && (receive_interface == NULL && receive_path == NULL)))
1202    {
1203      dbus_set_error (error, DBUS_ERROR_FAILED,
1204                      "On element <%s>, if you specify a member you must specify an interface or a path. Keep in mind that not all messages have an interface field.",
1205                      element_name);
1206      return FALSE;
1207    }
1208
1209  /* Allowed combinations of elements are:
1210   *
1211   *   base, must be all send or all receive:
1212   *     nothing
1213   *     interface
1214   *     interface + member
1215   *     error
1216   *
1217   *   base send_ can combine with send_destination, send_path, send_type, send_requested_reply
1218   *   base receive_ with receive_sender, receive_path, receive_type, receive_requested_reply, eavesdrop
1219   *
1220   *   user, group, own must occur alone
1221   *
1222   * Pretty sure the below stuff is broken, FIXME think about it more.
1223   */
1224
1225  if (((send_interface && send_error) ||
1226       (send_interface && receive_interface) ||
1227       (send_interface && receive_member) ||
1228       (send_interface && receive_error) ||
1229       (send_interface && receive_sender) ||
1230       (send_interface && receive_requested_reply) ||
1231       (send_interface && own) ||
1232       (send_interface && user) ||
1233       (send_interface && group)) ||
1234
1235      ((send_member && send_error) ||
1236       (send_member && receive_interface) ||
1237       (send_member && receive_member) ||
1238       (send_member && receive_error) ||
1239       (send_member && receive_sender) ||
1240       (send_member && receive_requested_reply) ||
1241       (send_member && own) ||
1242       (send_member && user) ||
1243       (send_member && group)) ||
1244
1245      ((send_error && receive_interface) ||
1246       (send_error && receive_member) ||
1247       (send_error && receive_error) ||
1248       (send_error && receive_sender) ||
1249       (send_error && receive_requested_reply) ||
1250       (send_error && own) ||
1251       (send_error && user) ||
1252       (send_error && group)) ||
1253
1254      ((send_destination && receive_interface) ||
1255       (send_destination && receive_member) ||
1256       (send_destination && receive_error) ||
1257       (send_destination && receive_sender) ||
1258       (send_destination && receive_requested_reply) ||
1259       (send_destination && own) ||
1260       (send_destination && user) ||
1261       (send_destination && group)) ||
1262
1263      ((send_type && receive_interface) ||
1264       (send_type && receive_member) ||
1265       (send_type && receive_error) ||
1266       (send_type && receive_sender) ||
1267       (send_type && receive_requested_reply) ||
1268       (send_type && own) ||
1269       (send_type && user) ||
1270       (send_type && group)) ||
1271
1272      ((send_path && receive_interface) ||
1273       (send_path && receive_member) ||
1274       (send_path && receive_error) ||
1275       (send_path && receive_sender) ||
1276       (send_path && receive_requested_reply) ||
1277       (send_path && own) ||
1278       (send_path && user) ||
1279       (send_path && group)) ||
1280
1281      ((send_requested_reply && receive_interface) ||
1282       (send_requested_reply && receive_member) ||
1283       (send_requested_reply && receive_error) ||
1284       (send_requested_reply && receive_sender) ||
1285       (send_requested_reply && receive_requested_reply) ||
1286       (send_requested_reply && own) ||
1287       (send_requested_reply && user) ||
1288       (send_requested_reply && group)) ||
1289
1290      ((receive_interface && receive_error) ||
1291       (receive_interface && own) ||
1292       (receive_interface && user) ||
1293       (receive_interface && group)) ||
1294
1295      ((receive_member && receive_error) ||
1296       (receive_member && own) ||
1297       (receive_member && user) ||
1298       (receive_member && group)) ||
1299
1300      ((receive_error && own) ||
1301       (receive_error && user) ||
1302       (receive_error && group)) ||
1303
1304      ((eavesdrop && own) ||
1305       (eavesdrop && user) ||
1306       (eavesdrop && group)) ||
1307
1308      ((receive_requested_reply && own) ||
1309       (receive_requested_reply && user) ||
1310       (receive_requested_reply && group)) ||
1311
1312      ((own && user) ||
1313       (own && group)) ||
1314
1315      ((user && group)))
1316    {
1317      dbus_set_error (error, DBUS_ERROR_FAILED,
1318                      "Invalid combination of attributes on element <%s>",
1319                      element_name);
1320      return FALSE;
1321    }
1322
1323  rule = NULL;
1324
1325  /* In BusPolicyRule, NULL represents wildcard.
1326   * In the config file, '*' represents it.
1327   */
1328#define IS_WILDCARD(str) ((str) && ((str)[0]) == '*' && ((str)[1]) == '\0')
1329
1330  if (send_interface || send_member || send_error || send_destination ||
1331      send_path || send_type || send_requested_reply)
1332    {
1333      int message_type;
1334
1335      if (IS_WILDCARD (send_interface))
1336        send_interface = NULL;
1337      if (IS_WILDCARD (send_member))
1338        send_member = NULL;
1339      if (IS_WILDCARD (send_error))
1340        send_error = NULL;
1341      if (IS_WILDCARD (send_destination))
1342        send_destination = NULL;
1343      if (IS_WILDCARD (send_path))
1344        send_path = NULL;
1345      if (IS_WILDCARD (send_type))
1346        send_type = NULL;
1347
1348      message_type = DBUS_MESSAGE_TYPE_INVALID;
1349      if (send_type != NULL)
1350        {
1351          message_type = dbus_message_type_from_string (send_type);
1352          if (message_type == DBUS_MESSAGE_TYPE_INVALID)
1353            {
1354              dbus_set_error (error, DBUS_ERROR_FAILED,
1355                              "Bad message type \"%s\"",
1356                              send_type);
1357              return FALSE;
1358            }
1359        }
1360
1361      if (eavesdrop &&
1362          !(strcmp (eavesdrop, "true") == 0 ||
1363            strcmp (eavesdrop, "false") == 0))
1364        {
1365          dbus_set_error (error, DBUS_ERROR_FAILED,
1366                          "Bad value \"%s\" for %s attribute, must be true or false",
1367                          "eavesdrop", eavesdrop);
1368          return FALSE;
1369        }
1370
1371      if (send_requested_reply &&
1372          !(strcmp (send_requested_reply, "true") == 0 ||
1373            strcmp (send_requested_reply, "false") == 0))
1374        {
1375          dbus_set_error (error, DBUS_ERROR_FAILED,
1376                          "Bad value \"%s\" for %s attribute, must be true or false",
1377                          "send_requested_reply", send_requested_reply);
1378          return FALSE;
1379        }
1380
1381      rule = bus_policy_rule_new (BUS_POLICY_RULE_SEND, allow);
1382      if (rule == NULL)
1383        goto nomem;
1384
1385      if (eavesdrop)
1386        rule->d.send.eavesdrop = (strcmp (eavesdrop, "true") == 0);
1387
1388      if (log)
1389        rule->d.send.log = (strcmp (log, "true") == 0);
1390
1391      if (send_requested_reply)
1392        rule->d.send.requested_reply = (strcmp (send_requested_reply, "true") == 0);
1393
1394      rule->d.send.message_type = message_type;
1395      rule->d.send.path = _dbus_strdup (send_path);
1396      rule->d.send.interface = _dbus_strdup (send_interface);
1397      rule->d.send.member = _dbus_strdup (send_member);
1398      rule->d.send.error = _dbus_strdup (send_error);
1399      rule->d.send.destination = _dbus_strdup (send_destination);
1400      if (send_path && rule->d.send.path == NULL)
1401        goto nomem;
1402      if (send_interface && rule->d.send.interface == NULL)
1403        goto nomem;
1404      if (send_member && rule->d.send.member == NULL)
1405        goto nomem;
1406      if (send_error && rule->d.send.error == NULL)
1407        goto nomem;
1408      if (send_destination && rule->d.send.destination == NULL)
1409        goto nomem;
1410    }
1411  else if (receive_interface || receive_member || receive_error || receive_sender ||
1412           receive_path || receive_type || eavesdrop || receive_requested_reply)
1413    {
1414      int message_type;
1415
1416      if (IS_WILDCARD (receive_interface))
1417        receive_interface = NULL;
1418      if (IS_WILDCARD (receive_member))
1419        receive_member = NULL;
1420      if (IS_WILDCARD (receive_error))
1421        receive_error = NULL;
1422      if (IS_WILDCARD (receive_sender))
1423        receive_sender = NULL;
1424      if (IS_WILDCARD (receive_path))
1425        receive_path = NULL;
1426      if (IS_WILDCARD (receive_type))
1427        receive_type = NULL;
1428
1429      message_type = DBUS_MESSAGE_TYPE_INVALID;
1430      if (receive_type != NULL)
1431        {
1432          message_type = dbus_message_type_from_string (receive_type);
1433          if (message_type == DBUS_MESSAGE_TYPE_INVALID)
1434            {
1435              dbus_set_error (error, DBUS_ERROR_FAILED,
1436                              "Bad message type \"%s\"",
1437                              receive_type);
1438              return FALSE;
1439            }
1440        }
1441
1442
1443      if (eavesdrop &&
1444          !(strcmp (eavesdrop, "true") == 0 ||
1445            strcmp (eavesdrop, "false") == 0))
1446        {
1447          dbus_set_error (error, DBUS_ERROR_FAILED,
1448                          "Bad value \"%s\" for %s attribute, must be true or false",
1449                          "eavesdrop", eavesdrop);
1450          return FALSE;
1451        }
1452
1453      if (receive_requested_reply &&
1454          !(strcmp (receive_requested_reply, "true") == 0 ||
1455            strcmp (receive_requested_reply, "false") == 0))
1456        {
1457          dbus_set_error (error, DBUS_ERROR_FAILED,
1458                          "Bad value \"%s\" for %s attribute, must be true or false",
1459                          "receive_requested_reply", receive_requested_reply);
1460          return FALSE;
1461        }
1462
1463      rule = bus_policy_rule_new (BUS_POLICY_RULE_RECEIVE, allow);
1464      if (rule == NULL)
1465        goto nomem;
1466
1467      if (eavesdrop)
1468        rule->d.receive.eavesdrop = (strcmp (eavesdrop, "true") == 0);
1469
1470      if (receive_requested_reply)
1471        rule->d.receive.requested_reply = (strcmp (receive_requested_reply, "true") == 0);
1472
1473      rule->d.receive.message_type = message_type;
1474      rule->d.receive.path = _dbus_strdup (receive_path);
1475      rule->d.receive.interface = _dbus_strdup (receive_interface);
1476      rule->d.receive.member = _dbus_strdup (receive_member);
1477      rule->d.receive.error = _dbus_strdup (receive_error);
1478      rule->d.receive.origin = _dbus_strdup (receive_sender);
1479
1480      if (receive_path && rule->d.receive.path == NULL)
1481        goto nomem;
1482      if (receive_interface && rule->d.receive.interface == NULL)
1483        goto nomem;
1484      if (receive_member && rule->d.receive.member == NULL)
1485        goto nomem;
1486      if (receive_error && rule->d.receive.error == NULL)
1487        goto nomem;
1488      if (receive_sender && rule->d.receive.origin == NULL)
1489        goto nomem;
1490    }
1491  else if (own)
1492    {
1493      rule = bus_policy_rule_new (BUS_POLICY_RULE_OWN, allow);
1494      if (rule == NULL)
1495        goto nomem;
1496
1497      if (IS_WILDCARD (own))
1498        own = NULL;
1499
1500      rule->d.own.service_name = _dbus_strdup (own);
1501      if (own && rule->d.own.service_name == NULL)
1502        goto nomem;
1503    }
1504  else if (user)
1505    {
1506      if (IS_WILDCARD (user))
1507        {
1508          rule = bus_policy_rule_new (BUS_POLICY_RULE_USER, allow);
1509          if (rule == NULL)
1510            goto nomem;
1511
1512          rule->d.user.uid = DBUS_UID_UNSET;
1513        }
1514      else
1515        {
1516          DBusString username;
1517          dbus_uid_t uid;
1518
1519          _dbus_string_init_const (&username, user);
1520
1521          if (_dbus_parse_unix_user_from_config (&username, &uid))
1522            {
1523              rule = bus_policy_rule_new (BUS_POLICY_RULE_USER, allow);
1524              if (rule == NULL)
1525                goto nomem;
1526
1527              rule->d.user.uid = uid;
1528            }
1529          else
1530            {
1531              _dbus_warn ("Unknown username \"%s\" on element <%s>\n",
1532                          user, element_name);
1533            }
1534        }
1535    }
1536  else if (group)
1537    {
1538      if (IS_WILDCARD (group))
1539        {
1540          rule = bus_policy_rule_new (BUS_POLICY_RULE_GROUP, allow);
1541          if (rule == NULL)
1542            goto nomem;
1543
1544          rule->d.group.gid = DBUS_GID_UNSET;
1545        }
1546      else
1547        {
1548          DBusString groupname;
1549          dbus_gid_t gid;
1550
1551          _dbus_string_init_const (&groupname, group);
1552
1553          if (_dbus_parse_unix_group_from_config (&groupname, &gid))
1554            {
1555              rule = bus_policy_rule_new (BUS_POLICY_RULE_GROUP, allow);
1556              if (rule == NULL)
1557                goto nomem;
1558
1559              rule->d.group.gid = gid;
1560            }
1561          else
1562            {
1563              _dbus_warn ("Unknown group \"%s\" on element <%s>\n",
1564                          group, element_name);
1565            }
1566        }
1567    }
1568  else
1569    _dbus_assert_not_reached ("Did not handle some combination of attributes on <allow> or <deny>");
1570
1571  if (rule != NULL)
1572    {
1573      Element *pe;
1574
1575      pe = peek_element (parser);
1576      _dbus_assert (pe != NULL);
1577      _dbus_assert (pe->type == ELEMENT_POLICY);
1578
1579      switch (pe->d.policy.type)
1580        {
1581        case POLICY_IGNORED:
1582          /* drop the rule on the floor */
1583          break;
1584
1585        case POLICY_DEFAULT:
1586          if (!bus_policy_append_default_rule (parser->policy, rule))
1587            goto nomem;
1588          break;
1589        case POLICY_MANDATORY:
1590          if (!bus_policy_append_mandatory_rule (parser->policy, rule))
1591            goto nomem;
1592          break;
1593        case POLICY_USER:
1594          if (!BUS_POLICY_RULE_IS_PER_CLIENT (rule))
1595            {
1596              dbus_set_error (error, DBUS_ERROR_FAILED,
1597                              "<%s> rule cannot be per-user because it has bus-global semantics",
1598                              element_name);
1599              goto failed;
1600            }
1601
1602          if (!bus_policy_append_user_rule (parser->policy, pe->d.policy.gid_uid_or_at_console,
1603                                            rule))
1604            goto nomem;
1605          break;
1606        case POLICY_GROUP:
1607          if (!BUS_POLICY_RULE_IS_PER_CLIENT (rule))
1608            {
1609              dbus_set_error (error, DBUS_ERROR_FAILED,
1610                              "<%s> rule cannot be per-group because it has bus-global semantics",
1611                              element_name);
1612              goto failed;
1613            }
1614
1615          if (!bus_policy_append_group_rule (parser->policy, pe->d.policy.gid_uid_or_at_console,
1616                                             rule))
1617            goto nomem;
1618          break;
1619
1620
1621        case POLICY_CONSOLE:
1622          if (!bus_policy_append_console_rule (parser->policy, pe->d.policy.gid_uid_or_at_console,
1623                                               rule))
1624            goto nomem;
1625          break;
1626        }
1627
1628      bus_policy_rule_unref (rule);
1629      rule = NULL;
1630    }
1631
1632  return TRUE;
1633
1634 nomem:
1635  BUS_SET_OOM (error);
1636 failed:
1637  if (rule)
1638    bus_policy_rule_unref (rule);
1639  return FALSE;
1640}
1641
1642static dbus_bool_t
1643start_policy_child (BusConfigParser   *parser,
1644                    const char        *element_name,
1645                    const char       **attribute_names,
1646                    const char       **attribute_values,
1647                    DBusError         *error)
1648{
1649  if (strcmp (element_name, "allow") == 0)
1650    {
1651      if (!append_rule_from_element (parser, element_name,
1652                                     attribute_names, attribute_values,
1653                                     TRUE, error))
1654        return FALSE;
1655
1656      if (push_element (parser, ELEMENT_ALLOW) == NULL)
1657        {
1658          BUS_SET_OOM (error);
1659          return FALSE;
1660        }
1661
1662      return TRUE;
1663    }
1664  else if (strcmp (element_name, "deny") == 0)
1665    {
1666      if (!append_rule_from_element (parser, element_name,
1667                                     attribute_names, attribute_values,
1668                                     FALSE, error))
1669        return FALSE;
1670
1671      if (push_element (parser, ELEMENT_DENY) == NULL)
1672        {
1673          BUS_SET_OOM (error);
1674          return FALSE;
1675        }
1676
1677      return TRUE;
1678    }
1679  else
1680    {
1681      dbus_set_error (error, DBUS_ERROR_FAILED,
1682                      "Element <%s> not allowed inside <%s> in configuration file",
1683                      element_name, "policy");
1684      return FALSE;
1685    }
1686}
1687
1688static dbus_bool_t
1689start_selinux_child (BusConfigParser   *parser,
1690                     const char        *element_name,
1691                     const char       **attribute_names,
1692                     const char       **attribute_values,
1693                     DBusError         *error)
1694{
1695  char *own_copy;
1696  char *context_copy;
1697
1698  own_copy = NULL;
1699  context_copy = NULL;
1700
1701  if (strcmp (element_name, "associate") == 0)
1702    {
1703      const char *own;
1704      const char *context;
1705
1706      if (!locate_attributes (parser, "associate",
1707                              attribute_names,
1708                              attribute_values,
1709                              error,
1710                              "own", &own,
1711                              "context", &context,
1712                              NULL))
1713        return FALSE;
1714
1715      if (push_element (parser, ELEMENT_ASSOCIATE) == NULL)
1716        {
1717          BUS_SET_OOM (error);
1718          return FALSE;
1719        }
1720
1721      if (own == NULL || context == NULL)
1722        {
1723          dbus_set_error (error, DBUS_ERROR_FAILED,
1724                          "Element <associate> must have attributes own=\"<servicename>\" and context=\"<selinux context>\"");
1725          return FALSE;
1726        }
1727
1728      own_copy = _dbus_strdup (own);
1729      if (own_copy == NULL)
1730        goto oom;
1731      context_copy = _dbus_strdup (context);
1732      if (context_copy == NULL)
1733        goto oom;
1734
1735      if (!_dbus_hash_table_insert_string (parser->service_context_table,
1736					   own_copy, context_copy))
1737        goto oom;
1738
1739      return TRUE;
1740    }
1741  else
1742    {
1743      dbus_set_error (error, DBUS_ERROR_FAILED,
1744                      "Element <%s> not allowed inside <%s> in configuration file",
1745                      element_name, "selinux");
1746      return FALSE;
1747    }
1748
1749 oom:
1750  if (own_copy)
1751    dbus_free (own_copy);
1752
1753  if (context_copy)
1754    dbus_free (context_copy);
1755
1756  BUS_SET_OOM (error);
1757  return FALSE;
1758}
1759
1760dbus_bool_t
1761bus_config_parser_start_element (BusConfigParser   *parser,
1762                                 const char        *element_name,
1763                                 const char       **attribute_names,
1764                                 const char       **attribute_values,
1765                                 DBusError         *error)
1766{
1767  ElementType t;
1768
1769  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
1770
1771  /* printf ("START: %s\n", element_name); */
1772
1773  t = top_element_type (parser);
1774
1775  if (t == ELEMENT_NONE)
1776    {
1777      if (strcmp (element_name, "busconfig") == 0)
1778        {
1779          if (!check_no_attributes (parser, "busconfig", attribute_names, attribute_values, error))
1780            return FALSE;
1781
1782          if (push_element (parser, ELEMENT_BUSCONFIG) == NULL)
1783            {
1784              BUS_SET_OOM (error);
1785              return FALSE;
1786            }
1787
1788          return TRUE;
1789        }
1790      else
1791        {
1792          dbus_set_error (error, DBUS_ERROR_FAILED,
1793                          "Unknown element <%s> at root of configuration file",
1794                          element_name);
1795          return FALSE;
1796        }
1797    }
1798  else if (t == ELEMENT_BUSCONFIG)
1799    {
1800      return start_busconfig_child (parser, element_name,
1801                                    attribute_names, attribute_values,
1802                                    error);
1803    }
1804  else if (t == ELEMENT_POLICY)
1805    {
1806      return start_policy_child (parser, element_name,
1807                                 attribute_names, attribute_values,
1808                                 error);
1809    }
1810  else if (t == ELEMENT_SELINUX)
1811    {
1812      return start_selinux_child (parser, element_name,
1813                                  attribute_names, attribute_values,
1814                                  error);
1815    }
1816  else
1817    {
1818      dbus_set_error (error, DBUS_ERROR_FAILED,
1819                      "Element <%s> is not allowed in this context",
1820                      element_name);
1821      return FALSE;
1822    }
1823}
1824
1825static dbus_bool_t
1826set_limit (BusConfigParser *parser,
1827           const char      *name,
1828           long             value,
1829           DBusError       *error)
1830{
1831  dbus_bool_t must_be_positive;
1832  dbus_bool_t must_be_int;
1833
1834  must_be_int = FALSE;
1835  must_be_positive = FALSE;
1836
1837  if (strcmp (name, "max_incoming_bytes") == 0)
1838    {
1839      must_be_positive = TRUE;
1840      parser->limits.max_incoming_bytes = value;
1841    }
1842  else if (strcmp (name, "max_incoming_unix_fds") == 0)
1843    {
1844      must_be_positive = TRUE;
1845      parser->limits.max_incoming_unix_fds = value;
1846    }
1847  else if (strcmp (name, "max_outgoing_bytes") == 0)
1848    {
1849      must_be_positive = TRUE;
1850      parser->limits.max_outgoing_bytes = value;
1851    }
1852  else if (strcmp (name, "max_outgoing_unix_fds") == 0)
1853    {
1854      must_be_positive = TRUE;
1855      parser->limits.max_outgoing_unix_fds = value;
1856    }
1857  else if (strcmp (name, "max_message_size") == 0)
1858    {
1859      must_be_positive = TRUE;
1860      parser->limits.max_message_size = value;
1861    }
1862  else if (strcmp (name, "max_message_unix_fds") == 0)
1863    {
1864      must_be_positive = TRUE;
1865      parser->limits.max_message_unix_fds = value;
1866    }
1867  else if (strcmp (name, "service_start_timeout") == 0)
1868    {
1869      must_be_positive = TRUE;
1870      must_be_int = TRUE;
1871      parser->limits.activation_timeout = value;
1872    }
1873  else if (strcmp (name, "auth_timeout") == 0)
1874    {
1875      must_be_positive = TRUE;
1876      must_be_int = TRUE;
1877      parser->limits.auth_timeout = value;
1878    }
1879  else if (strcmp (name, "reply_timeout") == 0)
1880    {
1881      must_be_positive = TRUE;
1882      must_be_int = TRUE;
1883      parser->limits.reply_timeout = value;
1884    }
1885  else if (strcmp (name, "max_completed_connections") == 0)
1886    {
1887      must_be_positive = TRUE;
1888      must_be_int = TRUE;
1889      parser->limits.max_completed_connections = value;
1890    }
1891  else if (strcmp (name, "max_incomplete_connections") == 0)
1892    {
1893      must_be_positive = TRUE;
1894      must_be_int = TRUE;
1895      parser->limits.max_incomplete_connections = value;
1896    }
1897  else if (strcmp (name, "max_connections_per_user") == 0)
1898    {
1899      must_be_positive = TRUE;
1900      must_be_int = TRUE;
1901      parser->limits.max_connections_per_user = value;
1902    }
1903  else if (strcmp (name, "max_pending_service_starts") == 0)
1904    {
1905      must_be_positive = TRUE;
1906      must_be_int = TRUE;
1907      parser->limits.max_pending_activations = value;
1908    }
1909  else if (strcmp (name, "max_names_per_connection") == 0)
1910    {
1911      must_be_positive = TRUE;
1912      must_be_int = TRUE;
1913      parser->limits.max_services_per_connection = value;
1914    }
1915  else if (strcmp (name, "max_match_rules_per_connection") == 0)
1916    {
1917      must_be_positive = TRUE;
1918      must_be_int = TRUE;
1919      parser->limits.max_match_rules_per_connection = value;
1920    }
1921  else if (strcmp (name, "max_replies_per_connection") == 0)
1922    {
1923      must_be_positive = TRUE;
1924      must_be_int = TRUE;
1925      parser->limits.max_replies_per_connection = value;
1926    }
1927  else
1928    {
1929      dbus_set_error (error, DBUS_ERROR_FAILED,
1930                      "There is no limit called \"%s\"\n",
1931                      name);
1932      return FALSE;
1933    }
1934
1935  if (must_be_positive && value < 0)
1936    {
1937      dbus_set_error (error, DBUS_ERROR_FAILED,
1938                      "<limit name=\"%s\"> must be a positive number\n",
1939                      name);
1940      return FALSE;
1941    }
1942
1943  if (must_be_int &&
1944      (value < _DBUS_INT_MIN || value > _DBUS_INT_MAX))
1945    {
1946      dbus_set_error (error, DBUS_ERROR_FAILED,
1947                      "<limit name=\"%s\"> value is too large\n",
1948                      name);
1949      return FALSE;
1950    }
1951
1952  return TRUE;
1953}
1954
1955dbus_bool_t
1956bus_config_parser_end_element (BusConfigParser   *parser,
1957                               const char        *element_name,
1958                               DBusError         *error)
1959{
1960  ElementType t;
1961  const char *n;
1962  Element *e;
1963
1964  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
1965
1966  /* printf ("END: %s\n", element_name); */
1967
1968  t = top_element_type (parser);
1969
1970  if (t == ELEMENT_NONE)
1971    {
1972      /* should probably be an assertion failure but
1973       * being paranoid about XML parsers
1974       */
1975      dbus_set_error (error, DBUS_ERROR_FAILED,
1976                      "XML parser ended element with no element on the stack");
1977      return FALSE;
1978    }
1979
1980  n = bus_config_parser_element_type_to_name (t);
1981  _dbus_assert (n != NULL);
1982  if (strcmp (n, element_name) != 0)
1983    {
1984      /* should probably be an assertion failure but
1985       * being paranoid about XML parsers
1986       */
1987      dbus_set_error (error, DBUS_ERROR_FAILED,
1988                      "XML element <%s> ended but topmost element on the stack was <%s>",
1989                      element_name, n);
1990      return FALSE;
1991    }
1992
1993  e = peek_element (parser);
1994  _dbus_assert (e != NULL);
1995
1996  switch (e->type)
1997    {
1998    case ELEMENT_NONE:
1999      _dbus_assert_not_reached ("element in stack has no type");
2000      break;
2001
2002    case ELEMENT_INCLUDE:
2003    case ELEMENT_USER:
2004    case ELEMENT_TYPE:
2005    case ELEMENT_LISTEN:
2006    case ELEMENT_PIDFILE:
2007    case ELEMENT_AUTH:
2008    case ELEMENT_SERVICEDIR:
2009    case ELEMENT_SERVICEHELPER:
2010    case ELEMENT_INCLUDEDIR:
2011    case ELEMENT_LIMIT:
2012      if (!e->had_content)
2013        {
2014          dbus_set_error (error, DBUS_ERROR_FAILED,
2015                          "XML element <%s> was expected to have content inside it",
2016                          bus_config_parser_element_type_to_name (e->type));
2017          return FALSE;
2018        }
2019
2020      if (e->type == ELEMENT_LIMIT)
2021        {
2022          if (!set_limit (parser, e->d.limit.name, e->d.limit.value,
2023                          error))
2024            return FALSE;
2025        }
2026      break;
2027
2028    case ELEMENT_BUSCONFIG:
2029    case ELEMENT_POLICY:
2030    case ELEMENT_ALLOW:
2031    case ELEMENT_DENY:
2032    case ELEMENT_FORK:
2033    case ELEMENT_SYSLOG:
2034    case ELEMENT_KEEP_UMASK:
2035    case ELEMENT_SELINUX:
2036    case ELEMENT_ASSOCIATE:
2037    case ELEMENT_STANDARD_SESSION_SERVICEDIRS:
2038    case ELEMENT_STANDARD_SYSTEM_SERVICEDIRS:
2039    case ELEMENT_ALLOW_ANONYMOUS:
2040      break;
2041    }
2042
2043  pop_element (parser);
2044
2045  return TRUE;
2046}
2047
2048static dbus_bool_t
2049all_whitespace (const DBusString *str)
2050{
2051  int i;
2052
2053  _dbus_string_skip_white (str, 0, &i);
2054
2055  return i == _dbus_string_get_length (str);
2056}
2057
2058static dbus_bool_t
2059make_full_path (const DBusString *basedir,
2060                const DBusString *filename,
2061                DBusString       *full_path)
2062{
2063  if (_dbus_path_is_absolute (filename))
2064    {
2065      return _dbus_string_copy (filename, 0, full_path, 0);
2066    }
2067  else
2068    {
2069      if (!_dbus_string_copy (basedir, 0, full_path, 0))
2070        return FALSE;
2071
2072      if (!_dbus_concat_dir_and_file (full_path, filename))
2073        return FALSE;
2074
2075      return TRUE;
2076    }
2077}
2078
2079static dbus_bool_t
2080include_file (BusConfigParser   *parser,
2081              const DBusString  *filename,
2082              dbus_bool_t        ignore_missing,
2083              DBusError         *error)
2084{
2085  /* FIXME good test case for this would load each config file in the
2086   * test suite both alone, and as an include, and check
2087   * that the result is the same
2088   */
2089  BusConfigParser *included;
2090  const char *filename_str;
2091  DBusError tmp_error;
2092
2093  dbus_error_init (&tmp_error);
2094
2095  filename_str = _dbus_string_get_const_data (filename);
2096
2097  /* Check to make sure this file hasn't already been included. */
2098  if (seen_include (parser, filename))
2099    {
2100      dbus_set_error (error, DBUS_ERROR_FAILED,
2101		      "Circular inclusion of file '%s'",
2102		      filename_str);
2103      return FALSE;
2104    }
2105
2106  if (! _dbus_list_append (&parser->included_files, (void *) filename_str))
2107    {
2108      BUS_SET_OOM (error);
2109      return FALSE;
2110    }
2111
2112  /* Since parser is passed in as the parent, included
2113     inherits parser's limits. */
2114  included = bus_config_load (filename, FALSE, parser, &tmp_error);
2115
2116  _dbus_list_pop_last (&parser->included_files);
2117
2118  if (included == NULL)
2119    {
2120      _DBUS_ASSERT_ERROR_CONTENT_IS_SET (&tmp_error);
2121
2122      if (dbus_error_has_name (&tmp_error, DBUS_ERROR_FILE_NOT_FOUND) &&
2123          ignore_missing)
2124        {
2125          dbus_error_free (&tmp_error);
2126          return TRUE;
2127        }
2128      else
2129        {
2130          dbus_move_error (&tmp_error, error);
2131          return FALSE;
2132        }
2133    }
2134  else
2135    {
2136      _DBUS_ASSERT_ERROR_CONTENT_IS_CLEAR (&tmp_error);
2137
2138      if (!merge_included (parser, included, error))
2139        {
2140          bus_config_parser_unref (included);
2141          return FALSE;
2142        }
2143
2144      /* Copy included's limits back to parser. */
2145      parser->limits = included->limits;
2146
2147      bus_config_parser_unref (included);
2148      return TRUE;
2149    }
2150}
2151
2152static dbus_bool_t
2153servicehelper_path (BusConfigParser   *parser,
2154                    const DBusString  *filename,
2155                    DBusError         *error)
2156{
2157  const char *filename_str;
2158  char *servicehelper;
2159
2160  filename_str = _dbus_string_get_const_data (filename);
2161
2162  /* copy to avoid overwriting with NULL on OOM */
2163  servicehelper = _dbus_strdup (filename_str);
2164
2165  /* check for OOM */
2166  if (servicehelper == NULL)
2167    {
2168      BUS_SET_OOM (error);
2169      return FALSE;
2170    }
2171
2172  /* save the latest servicehelper only if not OOM */
2173  dbus_free (parser->servicehelper);
2174  parser->servicehelper = servicehelper;
2175
2176  /* We don't check whether the helper exists; instead we
2177   * would just fail to ever activate anything if it doesn't.
2178   * This allows an admin to fix the problem if it doesn't exist.
2179   * It also allows the parser test suite to successfully parse
2180   * test cases without installing the helper. ;-)
2181   */
2182
2183  return TRUE;
2184}
2185
2186static dbus_bool_t
2187include_dir (BusConfigParser   *parser,
2188             const DBusString  *dirname,
2189             DBusError         *error)
2190{
2191  DBusString filename;
2192  dbus_bool_t retval;
2193  DBusError tmp_error;
2194  DBusDirIter *dir;
2195  char *s;
2196
2197  if (!_dbus_string_init (&filename))
2198    {
2199      BUS_SET_OOM (error);
2200      return FALSE;
2201    }
2202
2203  retval = FALSE;
2204
2205  dir = _dbus_directory_open (dirname, error);
2206
2207  if (dir == NULL)
2208    goto failed;
2209
2210  dbus_error_init (&tmp_error);
2211  while (_dbus_directory_get_next_file (dir, &filename, &tmp_error))
2212    {
2213      DBusString full_path;
2214
2215      if (!_dbus_string_init (&full_path))
2216        {
2217          BUS_SET_OOM (error);
2218          goto failed;
2219        }
2220
2221      if (!_dbus_string_copy (dirname, 0, &full_path, 0))
2222        {
2223          BUS_SET_OOM (error);
2224          _dbus_string_free (&full_path);
2225          goto failed;
2226        }
2227
2228      if (!_dbus_concat_dir_and_file (&full_path, &filename))
2229        {
2230          BUS_SET_OOM (error);
2231          _dbus_string_free (&full_path);
2232          goto failed;
2233        }
2234
2235      if (_dbus_string_ends_with_c_str (&full_path, ".conf"))
2236        {
2237          if (!include_file (parser, &full_path, TRUE, error))
2238            {
2239              _dbus_string_free (&full_path);
2240              goto failed;
2241            }
2242        }
2243
2244      _dbus_string_free (&full_path);
2245    }
2246
2247  if (dbus_error_is_set (&tmp_error))
2248    {
2249      dbus_move_error (&tmp_error, error);
2250      goto failed;
2251    }
2252
2253
2254  if (!_dbus_string_copy_data (dirname, &s))
2255    {
2256      BUS_SET_OOM (error);
2257      goto failed;
2258    }
2259
2260  if (!_dbus_list_append (&parser->conf_dirs, s))
2261    {
2262      dbus_free (s);
2263      BUS_SET_OOM (error);
2264      goto failed;
2265    }
2266
2267  retval = TRUE;
2268
2269 failed:
2270  _dbus_string_free (&filename);
2271
2272  if (dir)
2273    _dbus_directory_close (dir);
2274
2275  return retval;
2276}
2277
2278dbus_bool_t
2279bus_config_parser_content (BusConfigParser   *parser,
2280                           const DBusString  *content,
2281                           DBusError         *error)
2282{
2283  Element *e;
2284
2285  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
2286
2287#if 0
2288  {
2289    const char *c_str;
2290
2291    _dbus_string_get_const_data (content, &c_str);
2292
2293    printf ("CONTENT %d bytes: %s\n", _dbus_string_get_length (content), c_str);
2294  }
2295#endif
2296
2297  e = peek_element (parser);
2298  if (e == NULL)
2299    {
2300      dbus_set_error (error, DBUS_ERROR_FAILED,
2301                      "Text content outside of any XML element in configuration file");
2302      return FALSE;
2303    }
2304  else if (e->had_content)
2305    {
2306      _dbus_assert_not_reached ("Element had multiple content blocks");
2307      return FALSE;
2308    }
2309
2310  switch (top_element_type (parser))
2311    {
2312    case ELEMENT_NONE:
2313      _dbus_assert_not_reached ("element at top of stack has no type");
2314      return FALSE;
2315
2316    case ELEMENT_BUSCONFIG:
2317    case ELEMENT_POLICY:
2318    case ELEMENT_ALLOW:
2319    case ELEMENT_DENY:
2320    case ELEMENT_FORK:
2321    case ELEMENT_SYSLOG:
2322    case ELEMENT_KEEP_UMASK:
2323    case ELEMENT_STANDARD_SESSION_SERVICEDIRS:
2324    case ELEMENT_STANDARD_SYSTEM_SERVICEDIRS:
2325    case ELEMENT_ALLOW_ANONYMOUS:
2326    case ELEMENT_SELINUX:
2327    case ELEMENT_ASSOCIATE:
2328      if (all_whitespace (content))
2329        return TRUE;
2330      else
2331        {
2332          dbus_set_error (error, DBUS_ERROR_FAILED,
2333                          "No text content expected inside XML element %s in configuration file",
2334                          bus_config_parser_element_type_to_name (top_element_type (parser)));
2335          return FALSE;
2336        }
2337
2338    case ELEMENT_PIDFILE:
2339      {
2340        char *s;
2341
2342        e->had_content = TRUE;
2343
2344        if (!_dbus_string_copy_data (content, &s))
2345          goto nomem;
2346
2347        dbus_free (parser->pidfile);
2348        parser->pidfile = s;
2349      }
2350      break;
2351
2352    case ELEMENT_INCLUDE:
2353      {
2354        DBusString full_path, selinux_policy_root;
2355
2356        e->had_content = TRUE;
2357
2358	if (e->d.include.if_selinux_enabled
2359	    && !bus_selinux_enabled ())
2360	  break;
2361
2362        if (!_dbus_string_init (&full_path))
2363          goto nomem;
2364
2365        if (e->d.include.selinux_root_relative)
2366	  {
2367            if (!bus_selinux_get_policy_root ())
2368	      {
2369		dbus_set_error (error, DBUS_ERROR_FAILED,
2370				"Could not determine SELinux policy root for relative inclusion");
2371		_dbus_string_free (&full_path);
2372		return FALSE;
2373	      }
2374            _dbus_string_init_const (&selinux_policy_root,
2375                                     bus_selinux_get_policy_root ());
2376            if (!make_full_path (&selinux_policy_root, content, &full_path))
2377              {
2378                _dbus_string_free (&full_path);
2379                goto nomem;
2380              }
2381          }
2382        else if (!make_full_path (&parser->basedir, content, &full_path))
2383          {
2384            _dbus_string_free (&full_path);
2385            goto nomem;
2386          }
2387
2388        if (!include_file (parser, &full_path,
2389                           e->d.include.ignore_missing, error))
2390          {
2391            _dbus_string_free (&full_path);
2392            return FALSE;
2393          }
2394
2395        _dbus_string_free (&full_path);
2396      }
2397      break;
2398
2399    case ELEMENT_SERVICEHELPER:
2400      {
2401        DBusString full_path;
2402
2403        e->had_content = TRUE;
2404
2405        if (!_dbus_string_init (&full_path))
2406          goto nomem;
2407
2408        if (!make_full_path (&parser->basedir, content, &full_path))
2409          {
2410            _dbus_string_free (&full_path);
2411            goto nomem;
2412          }
2413
2414        if (!servicehelper_path (parser, &full_path, error))
2415          {
2416            _dbus_string_free (&full_path);
2417            return FALSE;
2418          }
2419
2420        _dbus_string_free (&full_path);
2421      }
2422      break;
2423
2424    case ELEMENT_INCLUDEDIR:
2425      {
2426        DBusString full_path;
2427
2428        e->had_content = TRUE;
2429
2430        if (!_dbus_string_init (&full_path))
2431          goto nomem;
2432
2433        if (!make_full_path (&parser->basedir, content, &full_path))
2434          {
2435            _dbus_string_free (&full_path);
2436            goto nomem;
2437          }
2438
2439        if (!include_dir (parser, &full_path, error))
2440          {
2441            _dbus_string_free (&full_path);
2442            return FALSE;
2443          }
2444
2445        _dbus_string_free (&full_path);
2446      }
2447      break;
2448
2449    case ELEMENT_USER:
2450      {
2451        char *s;
2452
2453        e->had_content = TRUE;
2454
2455        if (!_dbus_string_copy_data (content, &s))
2456          goto nomem;
2457
2458        dbus_free (parser->user);
2459        parser->user = s;
2460      }
2461      break;
2462
2463    case ELEMENT_TYPE:
2464      {
2465        char *s;
2466
2467        e->had_content = TRUE;
2468
2469        if (!_dbus_string_copy_data (content, &s))
2470          goto nomem;
2471
2472        dbus_free (parser->bus_type);
2473        parser->bus_type = s;
2474      }
2475      break;
2476
2477    case ELEMENT_LISTEN:
2478      {
2479        char *s;
2480
2481        e->had_content = TRUE;
2482
2483        if (!_dbus_string_copy_data (content, &s))
2484          goto nomem;
2485
2486        if (!_dbus_list_append (&parser->listen_on,
2487                                s))
2488          {
2489            dbus_free (s);
2490            goto nomem;
2491          }
2492      }
2493      break;
2494
2495    case ELEMENT_AUTH:
2496      {
2497        char *s;
2498
2499        e->had_content = TRUE;
2500
2501        if (!_dbus_string_copy_data (content, &s))
2502          goto nomem;
2503
2504        if (!_dbus_list_append (&parser->mechanisms,
2505                                s))
2506          {
2507            dbus_free (s);
2508            goto nomem;
2509          }
2510      }
2511      break;
2512
2513    case ELEMENT_SERVICEDIR:
2514      {
2515        char *s;
2516        DBusString full_path;
2517
2518        e->had_content = TRUE;
2519
2520        if (!_dbus_string_init (&full_path))
2521          goto nomem;
2522
2523        if (!make_full_path (&parser->basedir, content, &full_path))
2524          {
2525            _dbus_string_free (&full_path);
2526            goto nomem;
2527          }
2528
2529        if (!_dbus_string_copy_data (&full_path, &s))
2530          {
2531            _dbus_string_free (&full_path);
2532            goto nomem;
2533          }
2534
2535        /* _only_ extra session directories can be specified */
2536        if (!service_dirs_append_unique_or_free (&parser->service_dirs, s))
2537          {
2538            _dbus_string_free (&full_path);
2539            dbus_free (s);
2540            goto nomem;
2541          }
2542
2543        _dbus_string_free (&full_path);
2544      }
2545      break;
2546
2547    case ELEMENT_LIMIT:
2548      {
2549        long val;
2550
2551        e->had_content = TRUE;
2552
2553        val = 0;
2554        if (!_dbus_string_parse_int (content, 0, &val, NULL))
2555          {
2556            dbus_set_error (error, DBUS_ERROR_FAILED,
2557                            "<limit name=\"%s\"> element has invalid value (could not parse as integer)",
2558                            e->d.limit.name);
2559            return FALSE;
2560          }
2561
2562        e->d.limit.value = val;
2563
2564        _dbus_verbose ("Loaded value %ld for limit %s\n",
2565                       e->d.limit.value,
2566                       e->d.limit.name);
2567      }
2568      break;
2569    }
2570
2571  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
2572  return TRUE;
2573
2574 nomem:
2575  BUS_SET_OOM (error);
2576  return FALSE;
2577}
2578
2579dbus_bool_t
2580bus_config_parser_finished (BusConfigParser   *parser,
2581                            DBusError         *error)
2582{
2583  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
2584
2585  if (parser->stack != NULL)
2586    {
2587      dbus_set_error (error, DBUS_ERROR_FAILED,
2588                      "Element <%s> was not closed in configuration file",
2589                      bus_config_parser_element_type_to_name (top_element_type (parser)));
2590
2591      return FALSE;
2592    }
2593
2594  if (parser->is_toplevel && parser->listen_on == NULL)
2595    {
2596      dbus_set_error (error, DBUS_ERROR_FAILED,
2597                      "Configuration file needs one or more <listen> elements giving addresses");
2598      return FALSE;
2599    }
2600
2601  return TRUE;
2602}
2603
2604const char*
2605bus_config_parser_get_user (BusConfigParser *parser)
2606{
2607  return parser->user;
2608}
2609
2610const char*
2611bus_config_parser_get_type (BusConfigParser *parser)
2612{
2613  return parser->bus_type;
2614}
2615
2616DBusList**
2617bus_config_parser_get_addresses (BusConfigParser *parser)
2618{
2619  return &parser->listen_on;
2620}
2621
2622DBusList**
2623bus_config_parser_get_mechanisms (BusConfigParser *parser)
2624{
2625  return &parser->mechanisms;
2626}
2627
2628DBusList**
2629bus_config_parser_get_service_dirs (BusConfigParser *parser)
2630{
2631  return &parser->service_dirs;
2632}
2633
2634DBusList**
2635bus_config_parser_get_conf_dirs (BusConfigParser *parser)
2636{
2637  return &parser->conf_dirs;
2638}
2639
2640dbus_bool_t
2641bus_config_parser_get_fork (BusConfigParser   *parser)
2642{
2643  return parser->fork;
2644}
2645
2646dbus_bool_t
2647bus_config_parser_get_syslog (BusConfigParser   *parser)
2648{
2649  return parser->syslog;
2650}
2651
2652dbus_bool_t
2653bus_config_parser_get_keep_umask (BusConfigParser   *parser)
2654{
2655  return parser->keep_umask;
2656}
2657
2658dbus_bool_t
2659bus_config_parser_get_allow_anonymous (BusConfigParser   *parser)
2660{
2661  return parser->allow_anonymous;
2662}
2663
2664const char *
2665bus_config_parser_get_pidfile (BusConfigParser   *parser)
2666{
2667  return parser->pidfile;
2668}
2669
2670const char *
2671bus_config_parser_get_servicehelper (BusConfigParser   *parser)
2672{
2673  return parser->servicehelper;
2674}
2675
2676BusPolicy*
2677bus_config_parser_steal_policy (BusConfigParser *parser)
2678{
2679  BusPolicy *policy;
2680
2681  _dbus_assert (parser->policy != NULL); /* can only steal the policy 1 time */
2682
2683  policy = parser->policy;
2684
2685  parser->policy = NULL;
2686
2687  return policy;
2688}
2689
2690/* Overwrite any limits that were set in the configuration file */
2691void
2692bus_config_parser_get_limits (BusConfigParser *parser,
2693                              BusLimits       *limits)
2694{
2695  *limits = parser->limits;
2696}
2697
2698DBusHashTable*
2699bus_config_parser_steal_service_context_table (BusConfigParser *parser)
2700{
2701  DBusHashTable *table;
2702
2703  _dbus_assert (parser->service_context_table != NULL); /* can only steal once */
2704
2705  table = parser->service_context_table;
2706
2707  parser->service_context_table = NULL;
2708
2709  return table;
2710}
2711
2712#ifdef DBUS_BUILD_TESTS
2713#include <stdio.h>
2714
2715typedef enum
2716{
2717  VALID,
2718  INVALID,
2719  UNKNOWN
2720} Validity;
2721
2722static dbus_bool_t
2723do_load (const DBusString *full_path,
2724         Validity          validity,
2725         dbus_bool_t       oom_possible)
2726{
2727  BusConfigParser *parser;
2728  DBusError error;
2729
2730  dbus_error_init (&error);
2731
2732  parser = bus_config_load (full_path, TRUE, NULL, &error);
2733  if (parser == NULL)
2734    {
2735      _DBUS_ASSERT_ERROR_IS_SET (&error);
2736
2737      if (oom_possible &&
2738          dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
2739        {
2740          _dbus_verbose ("Failed to load valid file due to OOM\n");
2741          dbus_error_free (&error);
2742          return TRUE;
2743        }
2744      else if (validity == VALID)
2745        {
2746          _dbus_warn ("Failed to load valid file but still had memory: %s\n",
2747                      error.message);
2748
2749          dbus_error_free (&error);
2750          return FALSE;
2751        }
2752      else
2753        {
2754          dbus_error_free (&error);
2755          return TRUE;
2756        }
2757    }
2758  else
2759    {
2760      _DBUS_ASSERT_ERROR_IS_CLEAR (&error);
2761
2762      bus_config_parser_unref (parser);
2763
2764      if (validity == INVALID)
2765        {
2766          _dbus_warn ("Accepted invalid file\n");
2767          return FALSE;
2768        }
2769
2770      return TRUE;
2771    }
2772}
2773
2774typedef struct
2775{
2776  const DBusString *full_path;
2777  Validity          validity;
2778} LoaderOomData;
2779
2780static dbus_bool_t
2781check_loader_oom_func (void *data)
2782{
2783  LoaderOomData *d = data;
2784
2785  return do_load (d->full_path, d->validity, TRUE);
2786}
2787
2788static dbus_bool_t
2789process_test_valid_subdir (const DBusString *test_base_dir,
2790                           const char       *subdir,
2791                           Validity          validity)
2792{
2793  DBusString test_directory;
2794  DBusString filename;
2795  DBusDirIter *dir;
2796  dbus_bool_t retval;
2797  DBusError error;
2798
2799  retval = FALSE;
2800  dir = NULL;
2801
2802  if (!_dbus_string_init (&test_directory))
2803    _dbus_assert_not_reached ("didn't allocate test_directory\n");
2804
2805  _dbus_string_init_const (&filename, subdir);
2806
2807  if (!_dbus_string_copy (test_base_dir, 0,
2808                          &test_directory, 0))
2809    _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory");
2810
2811  if (!_dbus_concat_dir_and_file (&test_directory, &filename))
2812    _dbus_assert_not_reached ("couldn't allocate full path");
2813
2814  _dbus_string_free (&filename);
2815  if (!_dbus_string_init (&filename))
2816    _dbus_assert_not_reached ("didn't allocate filename string\n");
2817
2818  dbus_error_init (&error);
2819  dir = _dbus_directory_open (&test_directory, &error);
2820  if (dir == NULL)
2821    {
2822      _dbus_warn ("Could not open %s: %s\n",
2823                  _dbus_string_get_const_data (&test_directory),
2824                  error.message);
2825      dbus_error_free (&error);
2826      goto failed;
2827    }
2828
2829  if (validity == VALID)
2830    printf ("Testing valid files:\n");
2831  else if (validity == INVALID)
2832    printf ("Testing invalid files:\n");
2833  else
2834    printf ("Testing unknown files:\n");
2835
2836 next:
2837  while (_dbus_directory_get_next_file (dir, &filename, &error))
2838    {
2839      DBusString full_path;
2840      LoaderOomData d;
2841
2842      if (!_dbus_string_init (&full_path))
2843        _dbus_assert_not_reached ("couldn't init string");
2844
2845      if (!_dbus_string_copy (&test_directory, 0, &full_path, 0))
2846        _dbus_assert_not_reached ("couldn't copy dir to full_path");
2847
2848      if (!_dbus_concat_dir_and_file (&full_path, &filename))
2849        _dbus_assert_not_reached ("couldn't concat file to dir");
2850
2851      if (!_dbus_string_ends_with_c_str (&full_path, ".conf"))
2852        {
2853          _dbus_verbose ("Skipping non-.conf file %s\n",
2854                         _dbus_string_get_const_data (&filename));
2855          _dbus_string_free (&full_path);
2856          goto next;
2857        }
2858
2859      printf ("    %s\n", _dbus_string_get_const_data (&filename));
2860
2861      _dbus_verbose (" expecting %s\n",
2862                     validity == VALID ? "valid" :
2863                     (validity == INVALID ? "invalid" :
2864                      (validity == UNKNOWN ? "unknown" : "???")));
2865
2866      d.full_path = &full_path;
2867      d.validity = validity;
2868
2869      /* FIXME hackaround for an expat problem, see
2870       * https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=124747
2871       * http://freedesktop.org/pipermail/dbus/2004-May/001153.html
2872       */
2873      /* if (!_dbus_test_oom_handling ("config-loader", check_loader_oom_func, &d)) */
2874      if (!check_loader_oom_func (&d))
2875        _dbus_assert_not_reached ("test failed");
2876
2877      _dbus_string_free (&full_path);
2878    }
2879
2880  if (dbus_error_is_set (&error))
2881    {
2882      _dbus_warn ("Could not get next file in %s: %s\n",
2883                  _dbus_string_get_const_data (&test_directory),
2884                  error.message);
2885      dbus_error_free (&error);
2886      goto failed;
2887    }
2888
2889  retval = TRUE;
2890
2891 failed:
2892
2893  if (dir)
2894    _dbus_directory_close (dir);
2895  _dbus_string_free (&test_directory);
2896  _dbus_string_free (&filename);
2897
2898  return retval;
2899}
2900
2901static dbus_bool_t
2902bools_equal (dbus_bool_t a,
2903	     dbus_bool_t b)
2904{
2905  return a ? b : !b;
2906}
2907
2908static dbus_bool_t
2909strings_equal_or_both_null (const char *a,
2910                            const char *b)
2911{
2912  if (a == NULL || b == NULL)
2913    return a == b;
2914  else
2915    return !strcmp (a, b);
2916}
2917
2918static dbus_bool_t
2919elements_equal (const Element *a,
2920		const Element *b)
2921{
2922  if (a->type != b->type)
2923    return FALSE;
2924
2925  if (!bools_equal (a->had_content, b->had_content))
2926    return FALSE;
2927
2928  switch (a->type)
2929    {
2930
2931    case ELEMENT_INCLUDE:
2932      if (!bools_equal (a->d.include.ignore_missing,
2933			b->d.include.ignore_missing))
2934	return FALSE;
2935      break;
2936
2937    case ELEMENT_POLICY:
2938      if (a->d.policy.type != b->d.policy.type)
2939	return FALSE;
2940      if (a->d.policy.gid_uid_or_at_console != b->d.policy.gid_uid_or_at_console)
2941	return FALSE;
2942      break;
2943
2944    case ELEMENT_LIMIT:
2945      if (strcmp (a->d.limit.name, b->d.limit.name))
2946	return FALSE;
2947      if (a->d.limit.value != b->d.limit.value)
2948	return FALSE;
2949      break;
2950
2951    default:
2952      /* do nothing */
2953      break;
2954    }
2955
2956  return TRUE;
2957
2958}
2959
2960static dbus_bool_t
2961lists_of_elements_equal (DBusList *a,
2962			 DBusList *b)
2963{
2964  DBusList *ia;
2965  DBusList *ib;
2966
2967  ia = a;
2968  ib = b;
2969
2970  while (ia != NULL && ib != NULL)
2971    {
2972      if (elements_equal (ia->data, ib->data))
2973	return FALSE;
2974      ia = _dbus_list_get_next_link (&a, ia);
2975      ib = _dbus_list_get_next_link (&b, ib);
2976    }
2977
2978  return ia == NULL && ib == NULL;
2979}
2980
2981static dbus_bool_t
2982lists_of_c_strings_equal (DBusList *a,
2983			  DBusList *b)
2984{
2985  DBusList *ia;
2986  DBusList *ib;
2987
2988  ia = a;
2989  ib = b;
2990
2991  while (ia != NULL && ib != NULL)
2992    {
2993      if (strcmp (ia->data, ib->data))
2994	return FALSE;
2995      ia = _dbus_list_get_next_link (&a, ia);
2996      ib = _dbus_list_get_next_link (&b, ib);
2997    }
2998
2999  return ia == NULL && ib == NULL;
3000}
3001
3002static dbus_bool_t
3003limits_equal (const BusLimits *a,
3004	      const BusLimits *b)
3005{
3006  return
3007    (a->max_incoming_bytes == b->max_incoming_bytes
3008     || a->max_incoming_unix_fds == b->max_incoming_unix_fds
3009     || a->max_outgoing_bytes == b->max_outgoing_bytes
3010     || a->max_outgoing_unix_fds == b->max_outgoing_unix_fds
3011     || a->max_message_size == b->max_message_size
3012     || a->max_message_unix_fds == b->max_message_unix_fds
3013     || a->activation_timeout == b->activation_timeout
3014     || a->auth_timeout == b->auth_timeout
3015     || a->max_completed_connections == b->max_completed_connections
3016     || a->max_incomplete_connections == b->max_incomplete_connections
3017     || a->max_connections_per_user == b->max_connections_per_user
3018     || a->max_pending_activations == b->max_pending_activations
3019     || a->max_services_per_connection == b->max_services_per_connection
3020     || a->max_match_rules_per_connection == b->max_match_rules_per_connection
3021     || a->max_replies_per_connection == b->max_replies_per_connection
3022     || a->reply_timeout == b->reply_timeout);
3023}
3024
3025static dbus_bool_t
3026config_parsers_equal (const BusConfigParser *a,
3027                      const BusConfigParser *b)
3028{
3029  if (!_dbus_string_equal (&a->basedir, &b->basedir))
3030    return FALSE;
3031
3032  if (!lists_of_elements_equal (a->stack, b->stack))
3033    return FALSE;
3034
3035  if (!strings_equal_or_both_null (a->user, b->user))
3036    return FALSE;
3037
3038  if (!lists_of_c_strings_equal (a->listen_on, b->listen_on))
3039    return FALSE;
3040
3041  if (!lists_of_c_strings_equal (a->mechanisms, b->mechanisms))
3042    return FALSE;
3043
3044  if (!lists_of_c_strings_equal (a->service_dirs, b->service_dirs))
3045    return FALSE;
3046
3047  /* FIXME: compare policy */
3048
3049  /* FIXME: compare service selinux ID table */
3050
3051  if (! limits_equal (&a->limits, &b->limits))
3052    return FALSE;
3053
3054  if (!strings_equal_or_both_null (a->pidfile, b->pidfile))
3055    return FALSE;
3056
3057  if (! bools_equal (a->fork, b->fork))
3058    return FALSE;
3059
3060  if (! bools_equal (a->keep_umask, b->keep_umask))
3061    return FALSE;
3062
3063  if (! bools_equal (a->is_toplevel, b->is_toplevel))
3064    return FALSE;
3065
3066  return TRUE;
3067}
3068
3069static dbus_bool_t
3070all_are_equiv (const DBusString *target_directory)
3071{
3072  DBusString filename;
3073  DBusDirIter *dir;
3074  BusConfigParser *first_parser;
3075  BusConfigParser *parser;
3076  DBusError error;
3077  dbus_bool_t equal;
3078  dbus_bool_t retval;
3079
3080  dir = NULL;
3081  first_parser = NULL;
3082  parser = NULL;
3083  retval = FALSE;
3084
3085  if (!_dbus_string_init (&filename))
3086    _dbus_assert_not_reached ("didn't allocate filename string");
3087
3088  dbus_error_init (&error);
3089  dir = _dbus_directory_open (target_directory, &error);
3090  if (dir == NULL)
3091    {
3092      _dbus_warn ("Could not open %s: %s\n",
3093		  _dbus_string_get_const_data (target_directory),
3094		  error.message);
3095      dbus_error_free (&error);
3096      goto finished;
3097    }
3098
3099  printf ("Comparing equivalent files:\n");
3100
3101 next:
3102  while (_dbus_directory_get_next_file (dir, &filename, &error))
3103    {
3104      DBusString full_path;
3105
3106      if (!_dbus_string_init (&full_path))
3107	_dbus_assert_not_reached ("couldn't init string");
3108
3109      if (!_dbus_string_copy (target_directory, 0, &full_path, 0))
3110        _dbus_assert_not_reached ("couldn't copy dir to full_path");
3111
3112      if (!_dbus_concat_dir_and_file (&full_path, &filename))
3113        _dbus_assert_not_reached ("couldn't concat file to dir");
3114
3115      if (!_dbus_string_ends_with_c_str (&full_path, ".conf"))
3116        {
3117          _dbus_verbose ("Skipping non-.conf file %s\n",
3118                         _dbus_string_get_const_data (&filename));
3119	  _dbus_string_free (&full_path);
3120          goto next;
3121        }
3122
3123      printf ("    %s\n", _dbus_string_get_const_data (&filename));
3124
3125      parser = bus_config_load (&full_path, TRUE, NULL, &error);
3126
3127      if (parser == NULL)
3128	{
3129	  _dbus_warn ("Could not load file %s: %s\n",
3130		      _dbus_string_get_const_data (&full_path),
3131		      error.message);
3132          _dbus_string_free (&full_path);
3133	  dbus_error_free (&error);
3134	  goto finished;
3135	}
3136      else if (first_parser == NULL)
3137	{
3138          _dbus_string_free (&full_path);
3139	  first_parser = parser;
3140	}
3141      else
3142	{
3143          _dbus_string_free (&full_path);
3144	  equal = config_parsers_equal (first_parser, parser);
3145	  bus_config_parser_unref (parser);
3146	  if (! equal)
3147	    goto finished;
3148	}
3149    }
3150
3151  retval = TRUE;
3152
3153 finished:
3154  _dbus_string_free (&filename);
3155  if (first_parser)
3156    bus_config_parser_unref (first_parser);
3157  if (dir)
3158    _dbus_directory_close (dir);
3159
3160  return retval;
3161
3162}
3163
3164static dbus_bool_t
3165process_test_equiv_subdir (const DBusString *test_base_dir,
3166			   const char       *subdir)
3167{
3168  DBusString test_directory;
3169  DBusString filename;
3170  DBusDirIter *dir;
3171  DBusError error;
3172  dbus_bool_t equal;
3173  dbus_bool_t retval;
3174
3175  dir = NULL;
3176  retval = FALSE;
3177
3178  if (!_dbus_string_init (&test_directory))
3179    _dbus_assert_not_reached ("didn't allocate test_directory");
3180
3181  _dbus_string_init_const (&filename, subdir);
3182
3183  if (!_dbus_string_copy (test_base_dir, 0,
3184			  &test_directory, 0))
3185    _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory");
3186
3187  if (!_dbus_concat_dir_and_file (&test_directory, &filename))
3188    _dbus_assert_not_reached ("couldn't allocate full path");
3189
3190  _dbus_string_free (&filename);
3191  if (!_dbus_string_init (&filename))
3192    _dbus_assert_not_reached ("didn't allocate filename string");
3193
3194  dbus_error_init (&error);
3195  dir = _dbus_directory_open (&test_directory, &error);
3196  if (dir == NULL)
3197    {
3198      _dbus_warn ("Could not open %s: %s\n",
3199		  _dbus_string_get_const_data (&test_directory),
3200		  error.message);
3201      dbus_error_free (&error);
3202      goto finished;
3203    }
3204
3205  while (_dbus_directory_get_next_file (dir, &filename, &error))
3206    {
3207      DBusString full_path;
3208
3209      /* Skip CVS's magic directories! */
3210      if (_dbus_string_equal_c_str (&filename, "CVS"))
3211	continue;
3212
3213      if (!_dbus_string_init (&full_path))
3214	_dbus_assert_not_reached ("couldn't init string");
3215
3216      if (!_dbus_string_copy (&test_directory, 0, &full_path, 0))
3217        _dbus_assert_not_reached ("couldn't copy dir to full_path");
3218
3219      if (!_dbus_concat_dir_and_file (&full_path, &filename))
3220        _dbus_assert_not_reached ("couldn't concat file to dir");
3221
3222      equal = all_are_equiv (&full_path);
3223      _dbus_string_free (&full_path);
3224
3225      if (!equal)
3226	goto finished;
3227    }
3228
3229  retval = TRUE;
3230
3231 finished:
3232  _dbus_string_free (&test_directory);
3233  _dbus_string_free (&filename);
3234  if (dir)
3235    _dbus_directory_close (dir);
3236
3237  return retval;
3238
3239}
3240
3241static const char *test_session_service_dir_matches[] =
3242        {
3243#ifdef DBUS_UNIX
3244         "/testusr/testlocal/testshare/dbus-1/services",
3245         "/testusr/testshare/dbus-1/services",
3246#endif
3247         DBUS_DATADIR"/dbus-1/services",
3248#ifdef DBUS_UNIX
3249         "/testhome/foo/.testlocal/testshare/dbus-1/services",
3250#endif
3251#ifdef DBUS_WIN
3252         NULL,
3253#endif
3254         NULL
3255        };
3256
3257static dbus_bool_t
3258test_default_session_servicedirs (void)
3259{
3260  DBusList *dirs;
3261  DBusList *link;
3262  DBusString progs;
3263  const char *common_progs;
3264  int i;
3265
3266  /* On Unix we don't actually use this variable, but it's easier to handle the
3267   * deallocation if we always allocate it, whether needed or not */
3268  if (!_dbus_string_init (&progs))
3269    _dbus_assert_not_reached ("OOM allocating progs");
3270
3271  common_progs = _dbus_getenv ("CommonProgramFiles");
3272#ifndef DBUS_UNIX
3273  if (common_progs)
3274    {
3275      if (!_dbus_string_append (&progs, common_progs))
3276        {
3277          _dbus_string_free (&progs);
3278          return FALSE;
3279        }
3280
3281      if (!_dbus_string_append (&progs, "/dbus-1/services"))
3282        {
3283          _dbus_string_free (&progs);
3284          return FALSE;
3285        }
3286      test_session_service_dir_matches[1] = _dbus_string_get_const_data(&progs);
3287    }
3288#endif
3289  dirs = NULL;
3290
3291  printf ("Testing retrieving the default session service directories\n");
3292  if (!_dbus_get_standard_session_servicedirs (&dirs))
3293    _dbus_assert_not_reached ("couldn't get stardard dirs");
3294
3295  /* make sure our defaults end with share/dbus-1/service */
3296  while ((link = _dbus_list_pop_first_link (&dirs)))
3297    {
3298      DBusString path;
3299
3300      printf ("    default service dir: %s\n", (char *)link->data);
3301      _dbus_string_init_const (&path, (char *)link->data);
3302      if (!_dbus_string_ends_with_c_str (&path, "dbus-1/services"))
3303        {
3304          printf ("error with default session service directories\n");
3305	      dbus_free (link->data);
3306    	  _dbus_list_free_link (link);
3307          _dbus_string_free (&progs);
3308          return FALSE;
3309        }
3310
3311      dbus_free (link->data);
3312      _dbus_list_free_link (link);
3313    }
3314
3315#ifdef DBUS_UNIX
3316  if (!_dbus_setenv ("XDG_DATA_HOME", "/testhome/foo/.testlocal/testshare"))
3317    _dbus_assert_not_reached ("couldn't setenv XDG_DATA_HOME");
3318
3319  if (!_dbus_setenv ("XDG_DATA_DIRS", ":/testusr/testlocal/testshare: :/testusr/testshare:"))
3320    _dbus_assert_not_reached ("couldn't setenv XDG_DATA_DIRS");
3321#endif
3322  if (!_dbus_get_standard_session_servicedirs (&dirs))
3323    _dbus_assert_not_reached ("couldn't get stardard dirs");
3324
3325  /* make sure we read and parse the env variable correctly */
3326  i = 0;
3327  while ((link = _dbus_list_pop_first_link (&dirs)))
3328    {
3329      printf ("    test service dir: %s\n", (char *)link->data);
3330      if (test_session_service_dir_matches[i] == NULL)
3331        {
3332          printf ("more directories parsed than in match set\n");
3333          dbus_free (link->data);
3334          _dbus_list_free_link (link);
3335          _dbus_string_free (&progs);
3336          return FALSE;
3337        }
3338
3339      if (strcmp (test_session_service_dir_matches[i],
3340                  (char *)link->data) != 0)
3341        {
3342          printf ("%s directory does not match %s in the match set\n",
3343                  (char *)link->data,
3344                  test_session_service_dir_matches[i]);
3345          dbus_free (link->data);
3346          _dbus_list_free_link (link);
3347          _dbus_string_free (&progs);
3348          return FALSE;
3349        }
3350
3351      ++i;
3352
3353      dbus_free (link->data);
3354      _dbus_list_free_link (link);
3355    }
3356
3357  if (test_session_service_dir_matches[i] != NULL)
3358    {
3359      printf ("extra data %s in the match set was not matched\n",
3360              test_session_service_dir_matches[i]);
3361
3362      _dbus_string_free (&progs);
3363      return FALSE;
3364    }
3365
3366  _dbus_string_free (&progs);
3367  return TRUE;
3368}
3369
3370static const char *test_system_service_dir_matches[] =
3371        {
3372#ifdef DBUS_UNIX
3373         "/testusr/testlocal/testshare/dbus-1/system-services",
3374         "/testusr/testshare/dbus-1/system-services",
3375#endif
3376         DBUS_DATADIR"/dbus-1/system-services",
3377#ifdef DBUS_WIN
3378         NULL,
3379#endif
3380         NULL
3381        };
3382
3383static dbus_bool_t
3384test_default_system_servicedirs (void)
3385{
3386  DBusList *dirs;
3387  DBusList *link;
3388  DBusString progs;
3389  const char *common_progs;
3390  int i;
3391
3392  /* On Unix we don't actually use this variable, but it's easier to handle the
3393   * deallocation if we always allocate it, whether needed or not */
3394  if (!_dbus_string_init (&progs))
3395    _dbus_assert_not_reached ("OOM allocating progs");
3396
3397  common_progs = _dbus_getenv ("CommonProgramFiles");
3398#ifndef DBUS_UNIX
3399  if (common_progs)
3400    {
3401      if (!_dbus_string_append (&progs, common_progs))
3402        {
3403          _dbus_string_free (&progs);
3404          return FALSE;
3405        }
3406
3407      if (!_dbus_string_append (&progs, "/dbus-1/system-services"))
3408        {
3409          _dbus_string_free (&progs);
3410          return FALSE;
3411        }
3412      test_system_service_dir_matches[1] = _dbus_string_get_const_data(&progs);
3413    }
3414#endif
3415  dirs = NULL;
3416
3417  printf ("Testing retrieving the default system service directories\n");
3418  if (!_dbus_get_standard_system_servicedirs (&dirs))
3419    _dbus_assert_not_reached ("couldn't get stardard dirs");
3420
3421  /* make sure our defaults end with share/dbus-1/system-service */
3422  while ((link = _dbus_list_pop_first_link (&dirs)))
3423    {
3424      DBusString path;
3425
3426      printf ("    default service dir: %s\n", (char *)link->data);
3427      _dbus_string_init_const (&path, (char *)link->data);
3428      if (!_dbus_string_ends_with_c_str (&path, "dbus-1/system-services"))
3429        {
3430          printf ("error with default system service directories\n");
3431	      dbus_free (link->data);
3432    	  _dbus_list_free_link (link);
3433          _dbus_string_free (&progs);
3434          return FALSE;
3435        }
3436
3437      dbus_free (link->data);
3438      _dbus_list_free_link (link);
3439    }
3440
3441#ifdef DBUS_UNIX
3442  if (!_dbus_setenv ("XDG_DATA_HOME", "/testhome/foo/.testlocal/testshare"))
3443    _dbus_assert_not_reached ("couldn't setenv XDG_DATA_HOME");
3444
3445  if (!_dbus_setenv ("XDG_DATA_DIRS", ":/testusr/testlocal/testshare: :/testusr/testshare:"))
3446    _dbus_assert_not_reached ("couldn't setenv XDG_DATA_DIRS");
3447#endif
3448  if (!_dbus_get_standard_system_servicedirs (&dirs))
3449    _dbus_assert_not_reached ("couldn't get stardard dirs");
3450
3451  /* make sure we read and parse the env variable correctly */
3452  i = 0;
3453  while ((link = _dbus_list_pop_first_link (&dirs)))
3454    {
3455      printf ("    test service dir: %s\n", (char *)link->data);
3456      if (test_system_service_dir_matches[i] == NULL)
3457        {
3458          printf ("more directories parsed than in match set\n");
3459          dbus_free (link->data);
3460          _dbus_list_free_link (link);
3461          _dbus_string_free (&progs);
3462          return FALSE;
3463        }
3464
3465      if (strcmp (test_system_service_dir_matches[i],
3466                  (char *)link->data) != 0)
3467        {
3468          printf ("%s directory does not match %s in the match set\n",
3469                  (char *)link->data,
3470                  test_system_service_dir_matches[i]);
3471          dbus_free (link->data);
3472          _dbus_list_free_link (link);
3473          _dbus_string_free (&progs);
3474          return FALSE;
3475        }
3476
3477      ++i;
3478
3479      dbus_free (link->data);
3480      _dbus_list_free_link (link);
3481    }
3482
3483  if (test_system_service_dir_matches[i] != NULL)
3484    {
3485      printf ("extra data %s in the match set was not matched\n",
3486              test_system_service_dir_matches[i]);
3487
3488      _dbus_string_free (&progs);
3489      return FALSE;
3490    }
3491
3492  _dbus_string_free (&progs);
3493  return TRUE;
3494}
3495
3496dbus_bool_t
3497bus_config_parser_test (const DBusString *test_data_dir)
3498{
3499  if (test_data_dir == NULL ||
3500      _dbus_string_get_length (test_data_dir) == 0)
3501    {
3502      printf ("No test data\n");
3503      return TRUE;
3504    }
3505
3506  if (!test_default_session_servicedirs())
3507    return FALSE;
3508
3509#ifdef DBUS_WIN
3510  printf("default system service dir skipped\n");
3511#else
3512  if (!test_default_system_servicedirs())
3513    return FALSE;
3514#endif
3515
3516  if (!process_test_valid_subdir (test_data_dir, "valid-config-files", VALID))
3517    return FALSE;
3518
3519  if (!process_test_valid_subdir (test_data_dir, "invalid-config-files", INVALID))
3520    return FALSE;
3521
3522  if (!process_test_equiv_subdir (test_data_dir, "equiv-config-files"))
3523    return FALSE;
3524
3525  return TRUE;
3526}
3527
3528#endif /* DBUS_BUILD_TESTS */
3529