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