1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /* config-loader-expat.c expat XML loader
4 * Copyright (C) 2003 Red Hat, Inc.
6 * Licensed under the Academic Free License version 2.1
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.
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.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 #include "config-parser.h"
26 #include <dbus/dbus-internals.h>
29 static XML_Memory_Handling_Suite memsuite;
33 BusConfigParser *parser;
41 process_content (ExpatParseContext *context)
46 if (_dbus_string_get_length (&context->content) > 0)
48 if (!bus_config_parser_content (context->parser,
52 context->failed = TRUE;
55 _dbus_string_set_length (&context->content, 0);
62 expat_StartElementHandler (void *userData,
64 const XML_Char **atts)
66 ExpatParseContext *context = userData;
71 /* Expat seems to suck and can't abort the parse if we
72 * throw an error. Expat 2.0 is supposed to fix this.
77 if (!process_content (context))
80 /* "atts" is key, value, key, value, NULL */
81 for (i = 0; atts[i] != NULL; ++i)
84 _dbus_assert (i % 2 == 0);
85 names = dbus_new0 (char *, i / 2 + 1);
86 values = dbus_new0 (char *, i / 2 + 1);
88 if (names == NULL || values == NULL)
90 dbus_set_error (context->error, DBUS_ERROR_NO_MEMORY, NULL);
91 context->failed = TRUE;
98 while (atts[i] != NULL)
100 _dbus_assert (i % 2 == 0);
101 names [i / 2] = (char*) atts[i];
102 values[i / 2] = (char*) atts[i+1];
107 if (!bus_config_parser_start_element (context->parser,
109 (const char **) names,
110 (const char **) values,
115 context->failed = TRUE;
124 expat_EndElementHandler (void *userData,
125 const XML_Char *name)
127 ExpatParseContext *context = userData;
129 if (!process_content (context))
132 if (!bus_config_parser_end_element (context->parser,
136 context->failed = TRUE;
141 /* s is not 0 terminated. */
143 expat_CharacterDataHandler (void *userData,
147 ExpatParseContext *context = userData;
151 if (!_dbus_string_append_len (&context->content,
154 dbus_set_error (context->error, DBUS_ERROR_NO_MEMORY, NULL);
155 context->failed = TRUE;
162 bus_config_load (const DBusString *file,
163 dbus_bool_t is_toplevel,
164 const BusConfigParser *parent,
168 const char *filename;
169 BusConfigParser *parser;
170 ExpatParseContext context;
173 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
177 context.error = error;
178 context.failed = FALSE;
180 filename = _dbus_string_get_const_data (file);
182 if (!_dbus_string_init (&context.content))
184 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
188 if (!_dbus_string_init (&dirname))
190 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
191 _dbus_string_free (&context.content);
195 memsuite.malloc_fcn = dbus_malloc;
196 memsuite.realloc_fcn = dbus_realloc;
197 memsuite.free_fcn = dbus_free;
199 expat = XML_ParserCreate_MM ("UTF-8", &memsuite, NULL);
202 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
206 /* We do not need protection against hash collisions (CVE-2012-0876)
207 * because we are only parsing trusted XML; and if we let Expat block
208 * waiting for the CSPRNG to be initialized, as it does by default to
209 * defeat CVE-2012-0876, it can cause timeouts during early boot on
210 * entropy-starved embedded devices.
212 * TODO: When Expat gets a more explicit API for this than
213 * XML_SetHashSalt, check for that too, and use it preferentially.
214 * https://github.com/libexpat/libexpat/issues/91 */
215 #if defined(HAVE_XML_SETHASHSALT)
216 /* Any nonzero number will do. https://xkcd.com/221/ */
217 XML_SetHashSalt (expat, 4);
220 if (!_dbus_string_get_dirname (file, &dirname))
222 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
226 parser = bus_config_parser_new (&dirname, is_toplevel, parent);
229 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
232 context.parser = parser;
234 XML_SetUserData (expat, &context);
235 XML_SetElementHandler (expat,
236 expat_StartElementHandler,
237 expat_EndElementHandler);
238 XML_SetCharacterDataHandler (expat,
239 expat_CharacterDataHandler);
243 const char *data_str;
245 if (!_dbus_string_init (&data))
247 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
251 if (!_dbus_file_get_contents (&data, file, error))
253 _dbus_string_free (&data);
257 data_str = _dbus_string_get_const_data (&data);
259 if (XML_Parse (expat, data_str, _dbus_string_get_length (&data), TRUE) == XML_STATUS_ERROR)
261 if (context.error != NULL &&
262 !dbus_error_is_set (context.error))
266 e = XML_GetErrorCode (expat);
267 if (e == XML_ERROR_NO_MEMORY)
268 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
270 dbus_set_error (error, DBUS_ERROR_FAILED,
271 "Error in file %s, line %lu, column %lu: %s\n",
273 /* The XML_Size type varies according to
274 * build options, so cast to something we can
276 (unsigned long) XML_GetCurrentLineNumber (expat),
277 (unsigned long) XML_GetCurrentColumnNumber (expat),
278 XML_ErrorString (e));
281 _dbus_string_free (&data);
285 _dbus_string_free (&data);
291 if (!bus_config_parser_finished (parser, error))
294 _dbus_string_free (&dirname);
295 _dbus_string_free (&context.content);
296 XML_ParserFree (expat);
298 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
302 _DBUS_ASSERT_ERROR_IS_SET (error);
304 _dbus_string_free (&dirname);
305 _dbus_string_free (&context.content);
307 XML_ParserFree (expat);
309 bus_config_parser_unref (parser);