config-parser.c 97.8 KB
Newer Older
1
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 3
/* config-parser.c  XML-library-agnostic configuration file parser
 *
4
 * Copyright (C) 2003, 2004 Red Hat, Inc.
5
 *
6
 * Licensed under the Academic Free License version 2.1
7
 *
8 9 10 11 12 13 14 15 16
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
17
 *
18 19
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21 22
 *
 */
23 24

#include <config.h>
25
#include "config-parser-common.h"
26
#include "config-parser.h"
27
#include "test.h"
28
#include "utils.h"
29
#include "policy.h"
30
#include "selinux.h"
31
#include "apparmor.h"
32 33
#include <dbus/dbus-list.h>
#include <dbus/dbus-internals.h>
34
#include <dbus/dbus-misc.h>
35
#include <dbus/dbus-sysdeps.h>
36 37
#include <string.h>

38 39 40 41 42 43 44 45 46
typedef enum
{
  /* we ignore policies for unknown groups/users */
  POLICY_IGNORED,

  /* non-ignored */
  POLICY_DEFAULT,
  POLICY_MANDATORY,
  POLICY_USER,
John Palmieri's avatar
John Palmieri committed
47 48
  POLICY_GROUP,
  POLICY_CONSOLE
49 50
} PolicyType;

51 52 53 54
typedef struct
{
  ElementType type;

55 56
  unsigned int had_content : 1;

57 58 59 60
  union
  {
    struct
    {
61
      unsigned int ignore_missing : 1;
62
      unsigned int if_selinux_enabled : 1;
63
      unsigned int selinux_root_relative : 1;
64 65 66 67
    } include;

    struct
    {
68
      PolicyType type;
John Palmieri's avatar
John Palmieri committed
69
      unsigned long gid_uid_or_at_console;      
70 71
    } policy;

72 73 74 75 76 77
    struct
    {
      char *name;
      long value;
    } limit;
    
78
  } d;
79

80 81
} Element;

82 83 84
/**
 * Parser for bus configuration file. 
 */
85 86
struct BusConfigParser
{
87
  int refcount;        /**< Reference count */
88

89 90
  DBusString basedir;  /**< Directory we resolve paths relative to */
  
91
  DBusList *stack;     /**< stack of Element */
92

93 94
  char *user;          /**< user to run as */

95 96
  char *servicehelper; /**< location of the setuid helper */

97 98
  char *bus_type;          /**< Message bus type */
  
99
  DBusList *listen_on; /**< List of addresses to listen to */
100 101

  DBusList *mechanisms; /**< Auth mechanisms */
102

103
  DBusList *service_dirs; /**< Directories to look for session services in */
104

105 106
  DBusList *conf_dirs;   /**< Directories to look for policy configuration in */

107
  BusPolicy *policy;     /**< Security policy */
108 109

  BusLimits limits;      /**< Limits */
110 111 112

  char *pidfile;         /**< PID file */

113 114
  DBusList *included_files;  /**< Included files stack */

115
  DBusHashTable *service_context_table; /**< Map service names to SELinux contexts */
116

117
  unsigned int fork : 1; /**< TRUE to fork into daemon mode */
118

119
  unsigned int syslog : 1; /**< TRUE to enable syslog */
120
  unsigned int keep_umask : 1; /**< TRUE to keep original umask when forking */
121

122
  unsigned int is_toplevel : 1; /**< FALSE if we are a sub-config-file inside another one */
123 124

  unsigned int allow_anonymous : 1; /**< TRUE to allow anonymous connections */
125 126
};

127 128 129 130 131 132
static Element*
push_element (BusConfigParser *parser,
              ElementType      type)
{
  Element *e;

133 134
  _dbus_assert (type != ELEMENT_NONE);
  
135 136 137
  e = dbus_new0 (Element, 1);
  if (e == NULL)
    return NULL;
138 139 140 141 142 143

  if (!_dbus_list_append (&parser->stack, e))
    {
      dbus_free (e);
      return NULL;
    }
144 145 146 147 148 149
  
  e->type = type;

  return e;
}

150 151 152
static void
element_free (Element *e)
{
153 154 155
  if (e->type == ELEMENT_LIMIT)
    dbus_free (e->d.limit.name);
  
156 157 158
  dbus_free (e);
}

159 160 161 162 163 164
static void
pop_element (BusConfigParser *parser)
{
  Element *e;

  e = _dbus_list_pop_last (&parser->stack);
165 166 167
  
  element_free (e);
}
168

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
static Element*
peek_element (BusConfigParser *parser)
{
  Element *e;

  e = _dbus_list_get_last (&parser->stack);

  return e;
}

static ElementType
top_element_type (BusConfigParser *parser)
{
  Element *e;

  e = _dbus_list_get_last (&parser->stack);

  if (e)
    return e->type;
  else
    return ELEMENT_NONE;
}

192 193 194 195 196
static dbus_bool_t
merge_service_context_hash (DBusHashTable *dest,
			    DBusHashTable *from)
{
  DBusHashIter iter;
197 198 199 200 201 202
  char *service_copy;
  char *context_copy;

  service_copy = NULL;
  context_copy = NULL;

203 204 205 206 207 208 209 210
  _dbus_hash_iter_init (from, &iter);
  while (_dbus_hash_iter_next (&iter))
    {
      const char *service = _dbus_hash_iter_get_string_key (&iter);
      const char *context = _dbus_hash_iter_get_value (&iter);

      service_copy = _dbus_strdup (service);
      if (service_copy == NULL)
211
        goto fail;
212 213
      context_copy = _dbus_strdup (context);
      if (context_copy == NULL)
214
        goto fail; 
215 216
      
      if (!_dbus_hash_table_insert_string (dest, service_copy, context_copy))
217 218 219 220
        goto fail;

      service_copy = NULL;
      context_copy = NULL;    
221 222 223
    }

  return TRUE;
224 225 226 227 228 229 230 231 232

 fail:
  if (service_copy)
    dbus_free (service_copy);

  if (context_copy)
    dbus_free (context_copy);

  return FALSE;
233 234
}

235 236 237 238 239 240 241 242
static dbus_bool_t
service_dirs_find_dir (DBusList **service_dirs,
                       const char *dir)
{
  DBusList *link;

  _dbus_assert (dir != NULL);

243
  for (link = *service_dirs; link; link = _dbus_list_get_next_link(service_dirs, link))
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
    {
      const char *link_dir;
      
      link_dir = (const char *)link->data;
      if (strcmp (dir, link_dir) == 0)
        return TRUE;
    }

  return FALSE;
}

static dbus_bool_t
service_dirs_append_unique_or_free (DBusList **service_dirs,
                                    char *dir)
{
  if (!service_dirs_find_dir (service_dirs, dir))
    return _dbus_list_append (service_dirs, dir);  

  dbus_free (dir);
  return TRUE;
}

static void 
service_dirs_append_link_unique_or_free (DBusList **service_dirs,
                                         DBusList *dir_link)
{
  if (!service_dirs_find_dir (service_dirs, dir_link->data))
    {
      _dbus_list_append_link (service_dirs, dir_link);
    }
  else
    {
      dbus_free (dir_link->data);
      _dbus_list_free_link (dir_link);
    }
}

281 282 283 284 285 286 287
static dbus_bool_t
merge_included (BusConfigParser *parser,
                BusConfigParser *included,
                DBusError       *error)
{
  DBusList *link;

288 289 290 291 292 293
  if (!bus_policy_merge (parser->policy,
                         included->policy))
    {
      BUS_SET_OOM (error);
      return FALSE;
    }
294

295 296
  if (!merge_service_context_hash (parser->service_context_table,
				   included->service_context_table))
297 298 299 300
    {
      BUS_SET_OOM (error);
      return FALSE;
    }
301
  
302 303 304 305 306 307 308
  if (included->user != NULL)
    {
      dbus_free (parser->user);
      parser->user = included->user;
      included->user = NULL;
    }

309 310 311 312 313 314 315
  if (included->bus_type != NULL)
    {
      dbus_free (parser->bus_type);
      parser->bus_type = included->bus_type;
      included->bus_type = NULL;
    }
  
316 317
  if (included->fork)
    parser->fork = TRUE;
318

319 320 321
  if (included->keep_umask)
    parser->keep_umask = TRUE;

322 323 324
  if (included->allow_anonymous)
    parser->allow_anonymous = TRUE;

325 326 327 328 329 330
  if (included->pidfile != NULL)
    {
      dbus_free (parser->pidfile);
      parser->pidfile = included->pidfile;
      included->pidfile = NULL;
    }
331 332 333 334 335 336 337 338

  if (included->servicehelper != NULL)
    {
      dbus_free (parser->servicehelper);
      parser->servicehelper = included->servicehelper;
      included->servicehelper = NULL;
    }

339 340 341
  while ((link = _dbus_list_pop_first_link (&included->listen_on)))
    _dbus_list_append_link (&parser->listen_on, link);

342 343
  while ((link = _dbus_list_pop_first_link (&included->mechanisms)))
    _dbus_list_append_link (&parser->mechanisms, link);
344 345

  while ((link = _dbus_list_pop_first_link (&included->service_dirs)))
346
    service_dirs_append_link_unique_or_free (&parser->service_dirs, link);
347 348 349

  while ((link = _dbus_list_pop_first_link (&included->conf_dirs)))
    _dbus_list_append_link (&parser->conf_dirs, link);
350
  
351
  return TRUE;
352 353
}

354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
static dbus_bool_t
seen_include (BusConfigParser  *parser,
	      const DBusString *file)
{
  DBusList *iter;

  iter = parser->included_files;
  while (iter != NULL)
    {
      if (! strcmp (_dbus_string_get_const_data (file), iter->data))
	return TRUE;

      iter = _dbus_list_get_next_link (&parser->included_files, iter);
    }

  return FALSE;
}

372
BusConfigParser*
373 374 375
bus_config_parser_new (const DBusString      *basedir,
                       dbus_bool_t            is_toplevel,
                       const BusConfigParser *parent)
376 377 378 379 380 381 382
{
  BusConfigParser *parser;

  parser = dbus_new0 (BusConfigParser, 1);
  if (parser == NULL)
    return NULL;

383 384
  parser->is_toplevel = !!is_toplevel;
  
385 386 387 388 389 390
  if (!_dbus_string_init (&parser->basedir))
    {
      dbus_free (parser);
      return NULL;
    }

391
  if (((parser->policy = bus_policy_new ()) == NULL) ||
392
      !_dbus_string_copy (basedir, 0, &parser->basedir, 0) ||
393 394 395
      ((parser->service_context_table = _dbus_hash_table_new (DBUS_HASH_STRING,
							      dbus_free,
							      dbus_free)) == NULL))
396
    {
397 398 399
      if (parser->policy)
        bus_policy_unref (parser->policy);
      
400
      _dbus_string_free (&parser->basedir);
401

402 403 404
      dbus_free (parser);
      return NULL;
    }
405

406 407 408 409
  if (parent != NULL)
    {
      /* Initialize the parser's limits from the parent. */
      parser->limits = parent->limits;
410 411 412 413

      /* Use the parent's list of included_files to avoid
	 circular inclusions. */
      parser->included_files = parent->included_files;
414 415 416
    }
  else
    {
417

418
      /* Make up some numbers! woot! */
419 420
      parser->limits.max_incoming_bytes = _DBUS_ONE_MEGABYTE * 127;
      parser->limits.max_outgoing_bytes = _DBUS_ONE_MEGABYTE * 127;
421
      parser->limits.max_message_size = _DBUS_ONE_MEGABYTE * 32;
422 423 424 425 426 427

      /* We set relatively conservative values here since due to the
      way SCM_RIGHTS works we need to preallocate an array for the
      maximum number of file descriptors we can receive. Picking a
      high value here thus translates directly to more memory
      allocation. */
428 429 430
      parser->limits.max_incoming_unix_fds = DBUS_DEFAULT_MESSAGE_UNIX_FDS*4;
      parser->limits.max_outgoing_unix_fds = DBUS_DEFAULT_MESSAGE_UNIX_FDS*4;
      parser->limits.max_message_unix_fds = DBUS_DEFAULT_MESSAGE_UNIX_FDS;
431 432 433 434 435 436
      
      /* Making this long means the user has to wait longer for an error
       * message if something screws up, but making it too short means
       * they might see a false failure.
       */
      parser->limits.activation_timeout = 25000; /* 25 seconds */
437

438 439 440 441
      /* Making this long risks making a DOS attack easier, but too short
       * and legitimate auth will fail.  If interactive auth (ask user for
       * password) is allowed, then potentially it has to be quite long.
       */
442
      parser->limits.auth_timeout = 30000; /* 30 seconds */
443 444 445 446 447

      /* Do not allow a fd to stay forever in dbus-daemon
       * https://bugs.freedesktop.org/show_bug.cgi?id=80559
       */
      parser->limits.pending_fd_timeout = 150000; /* 2.5 minutes */
448
      
449 450
      parser->limits.max_incomplete_connections = 64;
      parser->limits.max_connections_per_user = 256;
451 452 453 454 455
      
      /* Note that max_completed_connections / max_connections_per_user
       * is the number of users that would have to work together to
       * DOS all the other users.
       */
456
      parser->limits.max_completed_connections = 2048;
457
      
458 459
      parser->limits.max_pending_activations = 512;
      parser->limits.max_services_per_connection = 512;
460 461 462 463 464 465 466 467 468

      /* For this one, keep in mind that it isn't only the memory used
       * by the match rules, but slowdown from linearly walking a big
       * list of them. A client adding more than this is almost
       * certainly a bad idea for that reason, and should change to a
       * smaller number of wider-net match rules - getting every last
       * message to the bus is probably better than having a thousand
       * match rules.
       */
469
      parser->limits.max_match_rules_per_connection = 512;
470
      
471
      parser->limits.reply_timeout = -1; /* never */
472 473 474 475

      /* this is effectively a limit on message queue size for messages
       * that require a reply
       */
476
      parser->limits.max_replies_per_connection = 128;
477 478
    }
      
479
  parser->refcount = 1;
480
      
481 482 483
  return parser;
}

484
BusConfigParser *
485 486 487 488 489
bus_config_parser_ref (BusConfigParser *parser)
{
  _dbus_assert (parser->refcount > 0);

  parser->refcount += 1;
490 491

  return parser;
492 493 494 495 496 497 498 499 500 501 502
}

void
bus_config_parser_unref (BusConfigParser *parser)
{
  _dbus_assert (parser->refcount > 0);

  parser->refcount -= 1;

  if (parser->refcount == 0)
    {
503 504
      while (parser->stack != NULL)
        pop_element (parser);
505

506
      dbus_free (parser->user);
507
      dbus_free (parser->servicehelper);
508
      dbus_free (parser->bus_type);
509
      dbus_free (parser->pidfile);
510
      
511 512 513 514 515 516
      _dbus_list_foreach (&parser->listen_on,
                          (DBusForeachFunction) dbus_free,
                          NULL);

      _dbus_list_clear (&parser->listen_on);

517 518 519 520 521
      _dbus_list_foreach (&parser->service_dirs,
                          (DBusForeachFunction) dbus_free,
                          NULL);

      _dbus_list_clear (&parser->service_dirs);
522

523 524 525 526 527 528
      _dbus_list_foreach (&parser->conf_dirs,
                          (DBusForeachFunction) dbus_free,
                          NULL);

      _dbus_list_clear (&parser->conf_dirs);

529 530 531 532 533 534
      _dbus_list_foreach (&parser->mechanisms,
                          (DBusForeachFunction) dbus_free,
                          NULL);

      _dbus_list_clear (&parser->mechanisms);
      
535
      _dbus_string_free (&parser->basedir);
536 537 538

      if (parser->policy)
        bus_policy_unref (parser->policy);
539

540 541
      if (parser->service_context_table)
        _dbus_hash_table_unref (parser->service_context_table);
542
      
543 544 545 546 547 548 549 550 551
      dbus_free (parser);
    }
}

dbus_bool_t
bus_config_parser_check_doctype (BusConfigParser   *parser,
                                 const char        *doctype,
                                 DBusError         *error)
{
552
  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
553

554 555 556 557
  if (strcmp (doctype, "busconfig") != 0)
    {
      dbus_set_error (error,
                      DBUS_ERROR_FAILED,
558
                      "Configuration file has the wrong document type %s",
559 560 561 562 563 564 565
                      doctype);
      return FALSE;
    }
  else
    return TRUE;
}

566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692
typedef struct
{
  const char  *name;
  const char **retloc;
} LocateAttr;

static dbus_bool_t
locate_attributes (BusConfigParser  *parser,
                   const char       *element_name,
                   const char      **attribute_names,
                   const char      **attribute_values,
                   DBusError        *error,
                   const char       *first_attribute_name,
                   const char      **first_attribute_retloc,
                   ...)
{
  va_list args;
  const char *name;
  const char **retloc;
  int n_attrs;
#define MAX_ATTRS 24
  LocateAttr attrs[MAX_ATTRS];
  dbus_bool_t retval;
  int i;

  _dbus_assert (first_attribute_name != NULL);
  _dbus_assert (first_attribute_retloc != NULL);

  retval = TRUE;

  n_attrs = 1;
  attrs[0].name = first_attribute_name;
  attrs[0].retloc = first_attribute_retloc;
  *first_attribute_retloc = NULL;

  va_start (args, first_attribute_retloc);

  name = va_arg (args, const char*);
  retloc = va_arg (args, const char**);

  while (name != NULL)
    {
      _dbus_assert (retloc != NULL);
      _dbus_assert (n_attrs < MAX_ATTRS);

      attrs[n_attrs].name = name;
      attrs[n_attrs].retloc = retloc;
      n_attrs += 1;
      *retloc = NULL;

      name = va_arg (args, const char*);
      retloc = va_arg (args, const char**);
    }

  va_end (args);

  i = 0;
  while (attribute_names[i])
    {
      int j;
      dbus_bool_t found;
      
      found = FALSE;
      j = 0;
      while (j < n_attrs)
        {
          if (strcmp (attrs[j].name, attribute_names[i]) == 0)
            {
              retloc = attrs[j].retloc;

              if (*retloc != NULL)
                {
                  dbus_set_error (error, DBUS_ERROR_FAILED,
                                  "Attribute \"%s\" repeated twice on the same <%s> element",
                                  attrs[j].name, element_name);
                  retval = FALSE;
                  goto out;
                }

              *retloc = attribute_values[i];
              found = TRUE;
            }

          ++j;
        }

      if (!found)
        {
          dbus_set_error (error, DBUS_ERROR_FAILED,
                          "Attribute \"%s\" is invalid on <%s> element in this context",
                          attribute_names[i], element_name);
          retval = FALSE;
          goto out;
        }

      ++i;
    }

 out:
  return retval;
}

static dbus_bool_t
check_no_attributes (BusConfigParser  *parser,
                     const char       *element_name,
                     const char      **attribute_names,
                     const char      **attribute_values,
                     DBusError        *error)
{
  if (attribute_names[0] != NULL)
    {
      dbus_set_error (error, DBUS_ERROR_FAILED,
                      "Attribute \"%s\" is invalid on <%s> element in this context",
                      attribute_names[0], element_name);
      return FALSE;
    }

  return TRUE;
}

static dbus_bool_t
start_busconfig_child (BusConfigParser   *parser,
                       const char        *element_name,
                       const char       **attribute_names,
                       const char       **attribute_values,
                       DBusError         *error)
{
693 694 695 696 697
  ElementType element_type;

  element_type = bus_config_parser_element_name_to_type (element_name);

  if (element_type == ELEMENT_USER)
698 699 700 701 702 703
    {
      if (!check_no_attributes (parser, "user", attribute_names, attribute_values, error))
        return FALSE;

      if (push_element (parser, ELEMENT_USER) == NULL)
        {
704 705 706 707 708 709
          BUS_SET_OOM (error);
          return FALSE;
        }

      return TRUE;
    }
710
  else if (element_type == ELEMENT_CONFIGTYPE)
711 712 713 714
    {
      if (!check_no_attributes (parser, "type", attribute_names, attribute_values, error))
        return FALSE;

715
      if (push_element (parser, ELEMENT_CONFIGTYPE) == NULL)
716 717
        {
          BUS_SET_OOM (error);
718 719 720
          return FALSE;
        }

721 722
      return TRUE;
    }
723
  else if (element_type == ELEMENT_FORK)
724 725 726 727 728 729
    {
      if (!check_no_attributes (parser, "fork", attribute_names, attribute_values, error))
        return FALSE;

      if (push_element (parser, ELEMENT_FORK) == NULL)
        {
730
          BUS_SET_OOM (error);
731 732 733 734 735
          return FALSE;
        }

      parser->fork = TRUE;
      
736 737
      return TRUE;
    }
738
  else if (element_type == ELEMENT_SYSLOG)
739
    {
740
      if (!check_no_attributes (parser, "syslog", attribute_names, attribute_values, error))
741 742
        return FALSE;

743
      if (push_element (parser, ELEMENT_SYSLOG) == NULL)
744 745 746 747
        {
          BUS_SET_OOM (error);
          return FALSE;
        }
748
      
749
      parser->syslog = TRUE;
750
      
751 752
      return TRUE;
    }
753
  else if (element_type == ELEMENT_KEEP_UMASK)
754
    {
755
      if (!check_no_attributes (parser, "keep_umask", attribute_names, attribute_values, error))
756 757
        return FALSE;

758
      if (push_element (parser, ELEMENT_KEEP_UMASK) == NULL)
759 760 761 762 763
        {
          BUS_SET_OOM (error);
          return FALSE;
        }

764
      parser->keep_umask = TRUE;
765
      
766 767
      return TRUE;
    }
768
  else if (element_type == ELEMENT_PIDFILE)
769 770 771 772 773 774 775 776 777 778
    {
      if (!check_no_attributes (parser, "pidfile", attribute_names, attribute_values, error))
        return FALSE;

      if (push_element (parser, ELEMENT_PIDFILE) == NULL)
        {
          BUS_SET_OOM (error);
          return FALSE;
        }

779 780
      return TRUE;
    }
781
  else if (element_type == ELEMENT_LISTEN)
782 783 784 785 786 787
    {
      if (!check_no_attributes (parser, "listen", attribute_names, attribute_values, error))
        return FALSE;

      if (push_element (parser, ELEMENT_LISTEN) == NULL)
        {
788
          BUS_SET_OOM (error);
789 790 791
          return FALSE;
        }

792 793
      return TRUE;
    }
794
  else if (element_type == ELEMENT_AUTH)
795 796 797 798 799 800
    {
      if (!check_no_attributes (parser, "auth", attribute_names, attribute_values, error))
        return FALSE;

      if (push_element (parser, ELEMENT_AUTH) == NULL)
        {
801
          BUS_SET_OOM (error);
802 803
          return FALSE;
        }
804
      
805 806
      return TRUE;
    }
807 808 809 810 811 812 813 814 815 816 817 818 819 820
  else if (element_type == ELEMENT_SERVICEHELPER)
    {
      if (!check_no_attributes (parser, "servicehelper", attribute_names, attribute_values, error))
        return FALSE;

      if (push_element (parser, ELEMENT_SERVICEHELPER) == NULL)
        {
          BUS_SET_OOM (error);
          return FALSE;
        }

      return TRUE;
    }
  else if (element_type == ELEMENT_INCLUDEDIR)
821 822 823 824 825 826
    {
      if (!check_no_attributes (parser, "includedir", attribute_names, attribute_values, error))
        return FALSE;

      if (push_element (parser, ELEMENT_INCLUDEDIR) == NULL)
        {
827
          BUS_SET_OOM (error);
828 829 830
          return FALSE;
        }

831 832
      return TRUE;
    }
833
  else if (element_type == ELEMENT_STANDARD_SESSION_SERVICEDIRS)
834
    {
835 836 837 838
      DBusList *link;
      DBusList *dirs;
      dirs = NULL;

839 840 841 842 843 844 845 846 847
      if (!check_no_attributes (parser, "standard_session_servicedirs", attribute_names, attribute_values, error))
        return FALSE;

      if (push_element (parser, ELEMENT_STANDARD_SESSION_SERVICEDIRS) == NULL)
        {
          BUS_SET_OOM (error);
          return FALSE;
        }

848 849 850 851 852 853 854 855 856
      if (!_dbus_get_standard_session_servicedirs (&dirs))
        {
          BUS_SET_OOM (error);
          return FALSE;
        }

        while ((link = _dbus_list_pop_first_link (&dirs)))
          service_dirs_append_link_unique_or_free (&parser->service_dirs, link);

857 858
      return TRUE;
    }
859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884
  else if (element_type == ELEMENT_STANDARD_SYSTEM_SERVICEDIRS)
    {
      DBusList *link;
      DBusList *dirs;
      dirs = NULL;

      if (!check_no_attributes (parser, "standard_system_servicedirs", attribute_names, attribute_values, error))
        return FALSE;

      if (push_element (parser, ELEMENT_STANDARD_SYSTEM_SERVICEDIRS) == NULL)
        {
          BUS_SET_OOM (error);
          return FALSE;
        }

      if (!_dbus_get_standard_system_servicedirs (&dirs))
        {
          BUS_SET_OOM (error);
          return FALSE;
        }

        while ((link = _dbus_list_pop_first_link (&dirs)))
          service_dirs_append_link_unique_or_free (&parser->service_dirs, link);

      return TRUE;
    }
885 886 887 888 889 890 891 892 893 894 895 896 897 898
  else if (element_type == ELEMENT_ALLOW_ANONYMOUS)
    {
      if (!check_no_attributes (parser, "allow_anonymous", attribute_names, attribute_values, error))
        return FALSE;

      if (push_element (parser, ELEMENT_ALLOW_ANONYMOUS) == NULL)
        {
          BUS_SET_OOM (error);
          return FALSE;
        }

      parser->allow_anonymous = TRUE;
      return TRUE;
    }
899
  else if (element_type == ELEMENT_SERVICEDIR)
900 901 902 903 904 905
    {
      if (!check_no_attributes (parser, "servicedir", attribute_names, attribute_values, error))
        return FALSE;

      if (push_element (parser, ELEMENT_SERVICEDIR) == NULL)
        {
906
          BUS_SET_OOM (error);
907 908 909
          return FALSE;
        }

910 911
      return TRUE;
    }
912
  else if (element_type == ELEMENT_INCLUDE)
913 914
    {
      Element *e;
915
      const char *if_selinux_enabled;
916
      const char *ignore_missing;
917
      const char *selinux_root_relative;
918 919 920

      if ((e = push_element (parser, ELEMENT_INCLUDE)) == NULL)
        {
921
          BUS_SET_OOM (error);
922 923 924 925
          return FALSE;
        }

      e->d.include.ignore_missing = FALSE;
926
      e->d.include.if_selinux_enabled = FALSE;
927
      e->d.include.selinux_root_relative = FALSE;
928 929 930 931 932 933

      if (!locate_attributes (parser, "include",
                              attribute_names,
                              attribute_values,
                              error,
                              "ignore_missing", &ignore_missing,
934
                              "if_selinux_enabled", &if_selinux_enabled,
935
                              "selinux_root_relative", &selinux_root_relative,
936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951
                              NULL))
        return FALSE;

      if (ignore_missing != NULL)
        {
          if (strcmp (ignore_missing, "yes") == 0)
            e->d.include.ignore_missing = TRUE;
          else if (strcmp (ignore_missing, "no") == 0)
            e->d.include.ignore_missing = FALSE;
          else
            {
              dbus_set_error (error, DBUS_ERROR_FAILED,
                              "ignore_missing attribute must have value \"yes\" or \"no\"");
              return FALSE;
            }
        }
952 953 954 955 956 957 958 959 960 961 962 963 964 965 966

      if (if_selinux_enabled != NULL)
        {
          if (strcmp (if_selinux_enabled, "yes") == 0)
            e->d.include.if_selinux_enabled = TRUE;
          else if (strcmp (if_selinux_enabled, "no") == 0)
            e->d.include.if_selinux_enabled = FALSE;
          else
            {
              dbus_set_error (error, DBUS_ERROR_FAILED,
                              "if_selinux_enabled attribute must have value"
                              " \"yes\" or \"no\"");
              return FALSE;
	    }
        }
967 968 969 970 971 972 973 974 975 976 977 978 979 980 981
      
      if (selinux_root_relative != NULL)
        {
          if (strcmp (selinux_root_relative, "yes") == 0)
            e->d.include.selinux_root_relative = TRUE;
          else if (strcmp (selinux_root_relative, "no") == 0)
            e->d.include.selinux_root_relative = FALSE;
          else
            {
              dbus_set_error (error, DBUS_ERROR_FAILED,
                              "selinux_root_relative attribute must have value"
                              " \"yes\" or \"no\"");
              return FALSE;
	    }
        }
982 983 984

      return TRUE;
    }
985
  else if (element_type == ELEMENT_POLICY)
986 987 988 989 990
    {
      Element *e;
      const char *context;
      const char *user;
      const char *group;
John Palmieri's avatar
John Palmieri committed
991
      const char *at_console;
992 993 994

      if ((e = push_element (parser, ELEMENT_POLICY)) == NULL)
        {
995
          BUS_SET_OOM (error);
996 997 998
          return FALSE;
        }

999 1000
      e->d.policy.type = POLICY_IGNORED;
      
1001
      if (!locate_attributes (parser, "policy",
1002 1003 1004 1005 1006 1007
                              attribute_names,
                              attribute_values,
                              error,
                              "context", &context,
                              "user", &user,
                              "group", &group,
John Palmieri's avatar
John Palmieri committed
1008
                              "at_console", &at_console,
1009 1010 1011
                              NULL))
        return FALSE;

1012
      if (((context && user) ||
John Palmieri's avatar
John Palmieri committed
1013 1014 1015 1016 1017 1018
           (context && group) ||
           (context && at_console)) ||
           ((user && group) ||
           (user && at_console)) ||
           (group && at_console) ||
          !(context || user || group || at_console))
1019 1020
        {
          dbus_set_error (error, DBUS_ERROR_FAILED,
John Palmieri's avatar
John Palmieri committed
1021
                          "<policy> element must have exactly one of (context|user|group|at_console) attributes");
1022 1023 1024 1025 1026 1027 1028
          return FALSE;
        }

      if (context != NULL)
        {
          if (strcmp (context, "default") == 0)
            {
1029
              e->d.policy.type = POLICY_DEFAULT;
1030 1031 1032
            }
          else if (strcmp (context, "mandatory") == 0)
            {
1033
              e->d.policy.type = POLICY_MANDATORY;
1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044
            }
          else
            {
              dbus_set_error (error, DBUS_ERROR_FAILED,
                              "context attribute on <policy> must have the value \"default\" or \"mandatory\", not \"%s\"",
                              context);
              return FALSE;
            }
        }
      else if (user != NULL)
        {
1045 1046
          DBusString username;
          _dbus_string_init_const (&username, user);
1047

1048 1049
          if (_dbus_parse_unix_user_from_config (&username,
                                                 &e->d.policy.gid_uid_or_at_console))
1050 1051 1052 1053
            e->d.policy.type = POLICY_USER;
          else
            _dbus_warn ("Unknown username \"%s\" in message bus configuration file\n",
                        user);
1054 1055 1056
        }
      else if (group != NULL)
        {
1057 1058
          DBusString group_name;
          _dbus_string_init_const (&group_name, group);
1059

1060 1061
          if (_dbus_parse_unix_group_from_config (&group_name,
                                                  &e->d.policy.gid_uid_or_at_console))
1062 1063 1064 1065
            e->d.policy.type = POLICY_GROUP;
          else
            _dbus_warn ("Unknown group \"%s\" in message bus configuration file\n",
                        group);          
1066
        }
John Palmieri's avatar
John Palmieri committed
1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084
      else if (at_console != NULL)
        {
           dbus_bool_t t;
           t = (strcmp (at_console, "true") == 0);
           if (t || strcmp (at_console, "false") == 0)
             {
               e->d.policy.gid_uid_or_at_console = t; 
               e->d.policy.type = POLICY_CONSOLE;
             }  
           else
             {
               dbus_set_error (error, DBUS_ERROR_FAILED,
                              "Unknown value \"%s\" for at_console in message bus configuration file",
                              at_console);

               return FALSE;
             }
        }
1085 1086 1087 1088
      else
        {
          _dbus_assert_not_reached ("all <policy> attributes null and we didn't set error");
        }
1089
      
1090 1091
      return TRUE;
    }
1092
  else if (element_type == ELEMENT_LIMIT)
1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124
    {
      Element *e;
      const char *name;

      if ((e = push_element (parser, ELEMENT_LIMIT)) == NULL)
        {
          BUS_SET_OOM (error);
          return FALSE;
        }
      
      if (!locate_attributes (parser, "limit",
                              attribute_names,
                              attribute_values,
                              error,
                              "name", &name,
                              NULL))
        return FALSE;

      if (name == NULL)
        {
          dbus_set_error (error, DBUS_ERROR_FAILED,
                          "<limit> element must have a \"name\" attribute");
          return FALSE;
        }

      e->d.limit.name = _dbus_strdup (name);
      if (e->d.limit.name == NULL)
        {
          BUS_SET_OOM (error);
          return FALSE;
        }

1125 1126
      return TRUE;
    }
1127
  else if (element_type == ELEMENT_SELINUX)
1128 1129 1130 1131 1132 1133 1134 1135 1136 1137
    {
      if (!check_no_attributes (parser, "selinux", attribute_names, attribute_values, error))
        return FALSE;

      if (push_element (parser, ELEMENT_SELINUX) == NULL)
        {
          BUS_SET_OOM (error);
          return FALSE;
        }

1138 1139
      return TRUE;
    }
1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160
  else if (element_type == ELEMENT_APPARMOR)
    {
      Element *e;
      const char *mode;

      if ((e = push_element (parser, ELEMENT_APPARMOR)) == NULL)
        {
          BUS_SET_OOM (error);
          return FALSE;
        }

      if (!locate_attributes (parser, "apparmor",
                              attribute_names,
                              attribute_values,
                              error,
                              "mode", &mode,
                              NULL))
        return FALSE;

      return bus_apparmor_set_mode_from_config (mode, error);
    }
1161 1162 1163 1164 1165 1166 1167 1168 1169
  else
    {
      dbus_set_error (error, DBUS_ERROR_FAILED,
                      "Element <%s> not allowed inside <%s> in configuration file",
                      element_name, "busconfig");
      return FALSE;
    }
}

1170 1171 1172 1173 1174 1175 1176 1177
static dbus_bool_t
append_rule_from_element (BusConfigParser   *parser,
                          const char        *element_name,
                          const char       **attribute_names,
                          const char       **attribute_values,
                          dbus_bool_t        allow,
                          DBusError         *error)
{
1178
  const char *log;
1179 1180 1181
  const char *send_interface;
  const char *send_member;
  const char *send_error;
1182
  const char *send_destination;
1183 1184
  const char *send_path;
  const char *send_type;
1185 1186 1187
  const char *receive_interface;
  const char *receive_member;
  const char *receive_error;
1188
  const char *receive_sender;
1189 1190
  const char *receive_path;
  const char *receive_type;
1191
  const char *eavesdrop;
1192 1193
  const char *send_requested_reply;
  const char *receive_requested_reply;
1194
  const char *own;
Alban Crequy's avatar
Alban Crequy committed
1195
  const char *own_prefix;
1196 1197
  const char *user;
  const char *group;
John Palmieri's avatar
John Palmieri committed
1198

1199 1200 1201 1202 1203 1204
  BusPolicyRule *rule;
  
  if (!locate_attributes (parser, element_name,
                          attribute_names,
                          attribute_values,
                          error,
1205 1206 1207
                          "send_interface", &send_interface,
                          "send_member", &send_member,
                          "send_error", &send_error,
1208
                          "send_destination", &send_destination,
1209 1210
                          "send_path", &send_path,
                          "send_type", &send_type,
1211 1212 1213
                          "receive_interface", &receive_interface,
                          "receive_member", &receive_member,
                          "receive_error", &receive_error,
1214
                          "receive_sender", &receive_sender,
1215 1216
                          "receive_path", &receive_path,
                          "receive_type", &receive_type,
1217
                          "eavesdrop", &eavesdrop,
1218 1219
                          "send_requested_reply", &send_requested_reply,
                          "receive_requested_reply", &receive_requested_reply,
1220
                          "own", &own,
Alban Crequy's avatar
Alban Crequy committed
1221
                          "own_prefix", &own_prefix,
1222 1223
                          "user", &user,
                          "group", &group,
1224
                          "log", &log,
1225 1226 1227
                          NULL))
    return FALSE;

1228
  if (!(send_interface || send_member || send_error || send_destination ||
1229
        send_type || send_path ||
1230
        receive_interface || receive_member || receive_error || receive_sender ||
1231 1232
        receive_type || receive_path || eavesdrop ||
        send_requested_reply || receive_requested_reply ||
Alban Crequy's avatar
Alban Crequy committed
1233
        own || own_prefix || user || group))
1234 1235 1236 1237 1238 1239 1240
    {
      dbus_set_error (error, DBUS_ERROR_FAILED,
                      "Element <%s> must have one or more attributes",
                      element_name);
      return FALSE;
    }