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