summaryrefslogtreecommitdiffstats
path: root/bus/config-parser-trivial.c
diff options
context:
space:
mode:
authorRichard Hughes <richard@hughsie.com>2007-07-24 11:50:59 +0000
committerRichard Hughes <richard@hughsie.com>2007-07-24 11:50:59 +0000
commit21dfdb6349056c2198a30b44eeae681b6213ca55 (patch)
treeca7f7e48462e91ade358a6ba40c9b60d04353e53 /bus/config-parser-trivial.c
parentdd8f96b8af8e079bc0292375cfd5da9625b7b5dd (diff)
2007-07-24 Richard Hughes <richard@hughsie.com>
* bus/config-parser-trivial.c: (service_dirs_find_dir), (service_dirs_append_link_unique_or_free), (bus_config_parser_new), (bus_config_parser_unref), (bus_config_parser_start_element), (bus_config_parser_end_element), (bus_config_parser_content), (bus_config_parser_finished), (bus_config_parser_get_user), (bus_config_parser_get_type), (bus_config_parser_get_service_dirs), (check_return_values), (do_load), (check_loader_oom_func), (process_test_valid_subdir), (make_full_path), (check_file_valid), (bus_config_parser_trivial_test): * bus/config-parser-trivial.h: Add a security sensitive stripped down config parser for the setuid launcher. This file only reads what it needs, and doesn't try to do anything remotely clever like including external files. It is not intended to validate the config file; it is expected that config-parser will do that before the setuid program tries to read it.
Diffstat (limited to 'bus/config-parser-trivial.c')
-rw-r--r--bus/config-parser-trivial.c693
1 files changed, 693 insertions, 0 deletions
diff --git a/bus/config-parser-trivial.c b/bus/config-parser-trivial.c
new file mode 100644
index 00000000..b825047a
--- /dev/null
+++ b/bus/config-parser-trivial.c
@@ -0,0 +1,693 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* config-parser-trivial.c XML-library-agnostic configuration file parser
+ * Does not do includes or anything remotely complicated.
+ *
+ * Copyright (C) 2003, 2004, 2007 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "config-parser-common.h"
+#include "config-parser-trivial.h"
+#include "utils.h"
+#include <dbus/dbus-list.h>
+#include <dbus/dbus-internals.h>
+#include <string.h>
+
+/**
+ * TRIVIAL parser for bus configuration file.
+ */
+struct BusConfigParser
+{
+ ElementType type;
+ DBusString user; /**< User the dbus-daemon runs as */
+ DBusString bus_type; /**< Message bus type */
+ DBusString service_helper; /**< Location of the setuid helper */
+ DBusList *service_dirs; /**< Directories to look for services in */
+};
+
+static dbus_bool_t
+service_dirs_find_dir (DBusList **service_dirs,
+ const char *dir)
+{
+ DBusList *link;
+
+ _dbus_assert (dir != NULL);
+
+ for (link = *service_dirs; link; link = _dbus_list_get_next_link(service_dirs, link))
+ {
+ const char *link_dir;
+
+ link_dir = (const char *)link->data;
+ if (strcmp (dir, link_dir) == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+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);
+ }
+}
+
+BusConfigParser*
+bus_config_parser_new (const DBusString *basedir,
+ dbus_bool_t is_toplevel,
+ const BusConfigParser *parent)
+{
+ BusConfigParser *parser;
+
+ parser = dbus_new0 (BusConfigParser, 1);
+ if (parser == NULL)
+ goto failed;
+
+ parser->type = ELEMENT_NONE;
+
+ /* init the lists */
+ parser->service_dirs = NULL;
+
+ /* init the strings */
+ if (!_dbus_string_init (&parser->user))
+ goto failed_parser;
+ if (!_dbus_string_init (&parser->bus_type))
+ goto failed_type;
+ if (!_dbus_string_init (&parser->service_helper))
+ goto failed_helper;
+
+ /* woot! */
+ return parser;
+
+/* argh. we have do do this carefully because of OOM */
+failed_helper:
+ _dbus_string_free (&parser->bus_type);
+failed_type:
+ _dbus_string_free (&parser->user);
+failed_parser:
+ dbus_free (parser);
+failed:
+ return NULL;
+}
+
+void
+bus_config_parser_unref (BusConfigParser *parser)
+{
+ _dbus_string_free (&parser->user);
+ _dbus_string_free (&parser->service_helper);
+ _dbus_string_free (&parser->bus_type);
+
+ _dbus_list_foreach (&parser->service_dirs,
+ (DBusForeachFunction) dbus_free,
+ NULL);
+
+ _dbus_list_clear (&parser->service_dirs);
+
+ dbus_free (parser);
+}
+
+dbus_bool_t
+bus_config_parser_start_element (BusConfigParser *parser,
+ const char *element_name,
+ const char **attribute_names,
+ const char **attribute_values,
+ DBusError *error)
+{
+ /* we don't do processing of attribute names, we don't need to */
+ parser->type = bus_config_parser_element_name_to_type (element_name);
+
+ switch (parser->type)
+ {
+ case ELEMENT_SERVICEHELPER:
+ case ELEMENT_USER:
+ case ELEMENT_TYPE:
+ /* content about to be handled */
+ break;
+
+ case ELEMENT_STANDARD_SYSTEM_SERVICEDIRS:
+ {
+ DBusList *link;
+ DBusList *dirs;
+ dirs = NULL;
+
+ 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);
+ break;
+ }
+
+ default:
+ {
+ /* we really don't care about the others... */
+ _dbus_verbose (" START We dont care about '%s' type '%i'\n", element_name, parser->type);
+ break;
+ }
+ }
+ return TRUE;
+}
+
+dbus_bool_t
+bus_config_parser_end_element (BusConfigParser *parser,
+ const char *element_name,
+ DBusError *error)
+{
+ /* we don't care */
+ return TRUE;
+}
+
+dbus_bool_t
+bus_config_parser_content (BusConfigParser *parser,
+ const DBusString *content,
+ DBusError *error)
+{
+ DBusString content_sane;
+ dbus_bool_t retval;
+
+ retval = FALSE;
+
+ if (!_dbus_string_init (&content_sane))
+ {
+ BUS_SET_OOM (error);
+ goto out;
+ }
+ if (!_dbus_string_copy (content, 0, &content_sane, 0))
+ {
+ BUS_SET_OOM (error);
+ goto out_content;
+ }
+
+ /* rip out white space */
+ _dbus_string_chop_white (&content_sane);
+ if (_dbus_string_get_length (&content_sane) == 0)
+ {
+ /* optimise, there is no content */
+ retval = TRUE;
+ goto out_content;
+ }
+
+ switch (parser->type)
+ {
+ case ELEMENT_SERVICEDIR:
+ {
+ char *cpath;
+
+ /* copy the sane data into a char array */
+ if (!_dbus_string_copy_data(&content_sane, &cpath))
+ {
+ BUS_SET_OOM (error);
+ goto out_content;
+ }
+
+ /* append the dynamic char string to service dirs */
+ if (!_dbus_list_append (&parser->service_dirs, cpath))
+ {
+ dbus_free (cpath);
+ BUS_SET_OOM (error);
+ goto out_content;
+ }
+ }
+ break;
+
+ case ELEMENT_SERVICEHELPER:
+ {
+ if (!_dbus_string_copy (&content_sane, 0, &parser->service_helper, 0))
+ {
+ BUS_SET_OOM (error);
+ goto out_content;
+ }
+ }
+ break;
+
+ case ELEMENT_USER:
+ {
+ if (!_dbus_string_copy (&content_sane, 0, &parser->user, 0))
+ {
+ BUS_SET_OOM (error);
+ goto out_content;
+ }
+ }
+ break;
+
+ case ELEMENT_TYPE:
+ {
+ if (!_dbus_string_copy (&content_sane, 0, &parser->bus_type, 0))
+ {
+ BUS_SET_OOM (error);
+ goto out_content;
+ }
+ }
+ break;
+ default:
+ {
+ /* we don't care about the others... really */
+ _dbus_verbose (" CONTENTS We dont care '%s' type '%i'\n", _dbus_string_get_const_data (&content_sane), parser->type);
+ break;
+ }
+ }
+
+ /* woot! */
+ retval = TRUE;
+
+out_content:
+ _dbus_string_free (&content_sane);
+out:
+ return retval;
+}
+
+dbus_bool_t
+bus_config_parser_finished (BusConfigParser *parser,
+ DBusError *error)
+{
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ _dbus_verbose ("finished scanning!\n");
+ return TRUE;
+}
+
+const char*
+bus_config_parser_get_user (BusConfigParser *parser)
+{
+ return _dbus_string_get_const_data (&parser->user);
+}
+
+const char*
+bus_config_parser_get_type (BusConfigParser *parser)
+{
+ return _dbus_string_get_const_data (&parser->bus_type);
+}
+
+DBusList**
+bus_config_parser_get_service_dirs (BusConfigParser *parser)
+{
+ return &parser->service_dirs;
+}
+
+#ifdef DBUS_BUILD_TESTS
+#include <stdio.h>
+#include "test.h"
+
+typedef enum
+{
+ VALID,
+ INVALID,
+ UNKNOWN
+} Validity;
+
+static dbus_bool_t
+check_return_values (const DBusString *full_path)
+{
+ BusConfigParser *parser;
+ DBusError error;
+ dbus_bool_t retval;
+ const char *user;
+ const char *type;
+ DBusList **dirs;
+
+ dbus_error_init (&error);
+ retval = FALSE;
+
+ printf ("Testing values from: %s\n", _dbus_string_get_const_data (full_path));
+
+ parser = bus_config_load (full_path, TRUE, NULL, &error);
+ if (parser == NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (&error);
+ if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+ _dbus_verbose ("Failed to load valid file due to OOM\n");
+ goto finish;
+ }
+ _DBUS_ASSERT_ERROR_IS_CLEAR (&error);
+
+ /* check user return value is okay */
+ user = bus_config_parser_get_user (parser);
+ if (user == NULL)
+ {
+ _dbus_warn ("User was NULL!\n");
+ goto finish;
+ }
+ if (strcmp (user, "dbus") != 0)
+ {
+ _dbus_warn ("User was invalid; '%s'!\n", user);
+ goto finish;
+ }
+ printf (" <user>dbus</user> OKAY!\n");
+
+ /* check type return value is okay */
+ type = bus_config_parser_get_type (parser);
+ if (type == NULL)
+ {
+ _dbus_warn ("Type was NULL!\n");
+ goto finish;
+ }
+ if (strcmp (type, "system") != 0)
+ {
+ _dbus_warn ("Type was invalid; '%s'!\n", user);
+ goto finish;
+ }
+ printf (" <type>system</type> OKAY!\n");
+
+ /* check dirs return value is okay */
+ dirs = bus_config_parser_get_service_dirs (parser);
+ if (dirs == NULL)
+ {
+ _dbus_warn ("Service dirs are NULL!\n");
+ goto finish;
+ }
+ printf (" <standard_system_service_dirs/> OKAY!\n");
+ /* NOTE: We tested the specific return values in the config-parser tests */
+
+ /* woohoo! */
+ retval = TRUE;
+finish:
+ if (parser != NULL)
+ bus_config_parser_unref (parser);
+ dbus_error_free (&error);
+ return retval;
+}
+
+static dbus_bool_t
+do_load (const DBusString *full_path,
+ Validity validity,
+ dbus_bool_t oom_possible)
+{
+ BusConfigParser *parser;
+ DBusError error;
+
+ dbus_error_init (&error);
+
+ parser = bus_config_load (full_path, TRUE, NULL, &error);
+ if (parser == NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (&error);
+
+ if (oom_possible &&
+ dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+ {
+ _dbus_verbose ("Failed to load valid file due to OOM\n");
+ dbus_error_free (&error);
+ return TRUE;
+ }
+ else if (validity == VALID)
+ {
+ _dbus_warn ("Failed to load valid file but still had memory: %s\n",
+ error.message);
+
+ dbus_error_free (&error);
+ return FALSE;
+ }
+ else
+ {
+ dbus_error_free (&error);
+ return TRUE;
+ }
+ }
+ else
+ {
+ _DBUS_ASSERT_ERROR_IS_CLEAR (&error);
+
+ bus_config_parser_unref (parser);
+
+ if (validity == INVALID)
+ {
+ _dbus_warn ("Accepted invalid file\n");
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+}
+
+typedef struct
+{
+ const DBusString *full_path;
+ Validity validity;
+} LoaderOomData;
+
+static dbus_bool_t
+check_loader_oom_func (void *data)
+{
+ LoaderOomData *d = data;
+
+ return do_load (d->full_path, d->validity, TRUE);
+}
+
+static dbus_bool_t
+process_test_valid_subdir (const DBusString *test_base_dir,
+ const char *subdir,
+ Validity validity)
+{
+ DBusString test_directory;
+ DBusString filename;
+ DBusDirIter *dir;
+ dbus_bool_t retval;
+ DBusError error;
+
+ retval = FALSE;
+ dir = NULL;
+
+ if (!_dbus_string_init (&test_directory))
+ _dbus_assert_not_reached ("didn't allocate test_directory\n");
+
+ _dbus_string_init_const (&filename, subdir);
+
+ if (!_dbus_string_copy (test_base_dir, 0,
+ &test_directory, 0))
+ _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory");
+
+ if (!_dbus_concat_dir_and_file (&test_directory, &filename))
+ _dbus_assert_not_reached ("couldn't allocate full path");
+
+ _dbus_string_free (&filename);
+ if (!_dbus_string_init (&filename))
+ _dbus_assert_not_reached ("didn't allocate filename string\n");
+
+ dbus_error_init (&error);
+ dir = _dbus_directory_open (&test_directory, &error);
+ if (dir == NULL)
+ {
+ _dbus_warn ("Could not open %s: %s\n",
+ _dbus_string_get_const_data (&test_directory),
+ error.message);
+ dbus_error_free (&error);
+ goto failed;
+ }
+
+ if (validity == VALID)
+ printf ("Testing valid files:\n");
+ else if (validity == INVALID)
+ printf ("Testing invalid files:\n");
+ else
+ printf ("Testing unknown files:\n");
+
+ next:
+ while (_dbus_directory_get_next_file (dir, &filename, &error))
+ {
+ DBusString full_path;
+ LoaderOomData d;
+
+ if (!_dbus_string_init (&full_path))
+ _dbus_assert_not_reached ("couldn't init string");
+
+ if (!_dbus_string_copy (&test_directory, 0, &full_path, 0))
+ _dbus_assert_not_reached ("couldn't copy dir to full_path");
+
+ if (!_dbus_concat_dir_and_file (&full_path, &filename))
+ _dbus_assert_not_reached ("couldn't concat file to dir");
+
+ if (!_dbus_string_ends_with_c_str (&full_path, ".conf"))
+ {
+ _dbus_verbose ("Skipping non-.conf file %s\n",
+ _dbus_string_get_const_data (&filename));
+ _dbus_string_free (&full_path);
+ goto next;
+ }
+
+ printf (" %s\n", _dbus_string_get_const_data (&filename));
+
+ _dbus_verbose (" expecting %s\n",
+ validity == VALID ? "valid" :
+ (validity == INVALID ? "invalid" :
+ (validity == UNKNOWN ? "unknown" : "???")));
+
+ d.full_path = &full_path;
+ d.validity = validity;
+
+ /* FIXME hackaround for an expat problem, see
+ * https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=124747
+ * http://freedesktop.org/pipermail/dbus/2004-May/001153.html
+ */
+ /* if (!_dbus_test_oom_handling ("config-loader", check_loader_oom_func, &d)) */
+ if (!check_loader_oom_func (&d))
+ _dbus_assert_not_reached ("test failed");
+
+ _dbus_string_free (&full_path);
+ }
+
+ if (dbus_error_is_set (&error))
+ {
+ _dbus_warn ("Could not get next file in %s: %s\n",
+ _dbus_string_get_const_data (&test_directory),
+ error.message);
+ dbus_error_free (&error);
+ goto failed;
+ }
+
+ retval = TRUE;
+
+ failed:
+
+ if (dir)
+ _dbus_directory_close (dir);
+ _dbus_string_free (&test_directory);
+ _dbus_string_free (&filename);
+
+ return retval;
+}
+
+/* convenience function, do not reuse outside of TEST */
+static dbus_bool_t
+make_full_path (const DBusString *test_data_dir,
+ const char *subdir,
+ const char *file,
+ DBusString *full_path)
+{
+ DBusString filename;
+ dbus_bool_t retval;
+
+ retval = FALSE;
+
+ if (!_dbus_string_init (full_path))
+ {
+ _dbus_warn ("couldn't allocate full path");
+ goto finish;
+ }
+
+ if (!_dbus_string_copy (test_data_dir, 0, full_path, 0))
+ {
+ _dbus_warn ("couldn't allocate full path");
+ goto finish;
+ }
+
+ _dbus_string_init_const (&filename, subdir);
+ if (!_dbus_concat_dir_and_file (full_path, &filename))
+ {
+ _dbus_warn ("couldn't allocate full path");
+ goto finish;
+ }
+ _dbus_string_free (&filename);
+
+ _dbus_string_init_const (&filename, file);
+ if (!_dbus_concat_dir_and_file (full_path, &filename))
+ {
+ _dbus_warn ("couldn't allocate full path");
+ goto finish;
+ }
+
+ /* woot! */
+ retval = TRUE;
+
+finish:
+ _dbus_string_free (&filename);
+ return retval;
+}
+
+static dbus_bool_t
+check_file_valid (DBusString *full_path,
+ Validity validity)
+{
+ dbus_bool_t retval;
+
+ if (validity == VALID)
+ printf ("Testing valid file:\n");
+ else if (validity == INVALID)
+ printf ("Testing invalid file:\n");
+ else
+ printf ("Testing unknown file:\n");
+
+ /* print the filename, just so we match the other output */
+ printf (" %s\n", _dbus_string_get_const_data (full_path));
+
+ /* only test one file */
+ retval = do_load (full_path, validity, TRUE);
+
+ return retval;
+}
+
+dbus_bool_t
+bus_config_parser_trivial_test (const DBusString *test_data_dir)
+{
+ DBusString full_path;
+ dbus_bool_t retval;
+
+ retval = FALSE;
+
+ if (test_data_dir == NULL ||
+ _dbus_string_get_length (test_data_dir) == 0)
+ {
+ printf ("No test data\n");
+ return TRUE;
+ }
+
+ /* We already test default_session_servicedirs and default_system_servicedirs
+ * in bus_config_parser_test() */
+ if (!process_test_valid_subdir (test_data_dir, "valid-config-files", VALID))
+ goto finish;
+
+ /* we don't process all the invalid files, as the trivial parser can't hope
+ * to validate them all for all different syntaxes. We just check one broken
+ * file to see if junk is received */
+ if (!make_full_path (test_data_dir, "invalid-config-files", "not-well-formed.conf", &full_path))
+ goto finish;
+ if (!check_file_valid (&full_path, INVALID))
+ goto finish;
+ _dbus_string_free (&full_path);
+
+ /* just test if the check_file_valid works okay and we got sane values */
+ if (!make_full_path (test_data_dir, "valid-config-files", "system.conf", &full_path))
+ goto finish;
+ if (!check_file_valid (&full_path, VALID))
+ goto finish;
+ /* check to see if we got the correct values from the parser */
+ if (!check_return_values (&full_path))
+ goto finish;
+
+ /* woot! */
+ retval = TRUE;
+
+finish:
+ _dbus_string_free (&full_path);
+
+ /* we don't process equiv-config-files as we don't handle <include> */
+ return retval;
+}
+
+#endif /* DBUS_BUILD_TESTS */
+