/src/suricata7/src/conf-yaml-loader.c
Line | Count | Source |
1 | | /* Copyright (C) 2007-2023 Open Information Security Foundation |
2 | | * |
3 | | * You can copy, redistribute or modify this Program under the terms of |
4 | | * the GNU General Public License version 2 as published by the Free |
5 | | * Software Foundation. |
6 | | * |
7 | | * This program is distributed in the hope that it will be useful, |
8 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | | * GNU General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU General Public License |
13 | | * version 2 along with this program; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
15 | | * 02110-1301, USA. |
16 | | */ |
17 | | |
18 | | /** |
19 | | * \file |
20 | | * |
21 | | * \author Endace Technology Limited - Jason Ish <jason.ish@endace.com> |
22 | | * |
23 | | * YAML configuration loader. |
24 | | */ |
25 | | |
26 | | #include "suricata-common.h" |
27 | | #include "conf.h" |
28 | | #include "conf-yaml-loader.h" |
29 | | #include <yaml.h> |
30 | | #include "util-path.h" |
31 | | #include "util-debug.h" |
32 | | #include "util-unittest.h" |
33 | | |
34 | 559k | #define YAML_VERSION_MAJOR 1 |
35 | 279k | #define YAML_VERSION_MINOR 1 |
36 | | |
37 | | /* The maximum level of recursion allowed while parsing the YAML |
38 | | * file. */ |
39 | 2.02M | #define RECURSION_LIMIT 128 |
40 | | |
41 | | /* Sometimes we'll have to create a node name on the fly (integer |
42 | | * conversion, etc), so this is a default length to allocate that will |
43 | | * work most of the time. */ |
44 | 441k | #define DEFAULT_NAME_LEN 16 |
45 | | |
46 | 2.43k | #define MANGLE_ERRORS_MAX 10 |
47 | | static int mangle_errors = 0; |
48 | | |
49 | | static char *conf_dirname = NULL; |
50 | | |
51 | | static int ConfYamlParse(yaml_parser_t *parser, ConfNode *parent, int inseq, int rlevel, int state); |
52 | | |
53 | | /* Configuration processing states. */ |
54 | | enum conf_state { |
55 | | CONF_KEY = 0, |
56 | | CONF_VAL, |
57 | | CONF_INCLUDE, |
58 | | }; |
59 | | |
60 | | /** |
61 | | * \brief Mangle unsupported characters. |
62 | | * |
63 | | * \param string A pointer to an null terminated string. |
64 | | * |
65 | | * \retval none |
66 | | */ |
67 | | static void |
68 | | Mangle(char *string) |
69 | 3.35k | { |
70 | 3.35k | char *c; |
71 | | |
72 | 11.8k | while ((c = strchr(string, '_'))) |
73 | 8.45k | *c = '-'; |
74 | | |
75 | 3.35k | return; |
76 | 3.35k | } |
77 | | |
78 | | /** |
79 | | * \brief Set the directory name of the configuration file. |
80 | | * |
81 | | * \param filename The configuration filename. |
82 | | */ |
83 | | static void |
84 | | ConfYamlSetConfDirname(const char *filename) |
85 | 0 | { |
86 | 0 | const char *ep; |
87 | |
|
88 | 0 | ep = strrchr(filename, '\\'); |
89 | 0 | if (ep == NULL) |
90 | 0 | ep = strrchr(filename, '/'); |
91 | |
|
92 | 0 | if (ep == NULL) { |
93 | 0 | conf_dirname = SCStrdup("."); |
94 | 0 | if (conf_dirname == NULL) { |
95 | 0 | FatalError("ERROR: Failed to allocate memory while loading configuration."); |
96 | 0 | } |
97 | 0 | } |
98 | 0 | else { |
99 | 0 | conf_dirname = SCStrdup(filename); |
100 | 0 | if (conf_dirname == NULL) { |
101 | 0 | FatalError("ERROR: Failed to allocate memory while loading configuration."); |
102 | 0 | } |
103 | 0 | conf_dirname[ep - filename] = '\0'; |
104 | 0 | } |
105 | 0 | } |
106 | | |
107 | | /** |
108 | | * \brief Include a file in the configuration. |
109 | | * |
110 | | * \param parent The configuration node the included configuration will be |
111 | | * placed at. |
112 | | * \param filename The filename to include. |
113 | | * |
114 | | * \retval 0 on success, -1 on failure. |
115 | | */ |
116 | | int ConfYamlHandleInclude(ConfNode *parent, const char *filename) |
117 | 75 | { |
118 | 75 | yaml_parser_t parser; |
119 | 75 | char include_filename[PATH_MAX]; |
120 | 75 | FILE *file = NULL; |
121 | 75 | int ret = -1; |
122 | | |
123 | 75 | if (yaml_parser_initialize(&parser) != 1) { |
124 | 0 | SCLogError("Failed to initialize YAML parser"); |
125 | 0 | return -1; |
126 | 0 | } |
127 | | |
128 | 75 | if (PathIsAbsolute(filename)) { |
129 | 5 | strlcpy(include_filename, filename, sizeof(include_filename)); |
130 | 5 | } |
131 | 70 | else { |
132 | 70 | snprintf(include_filename, sizeof(include_filename), "%s/%s", |
133 | 70 | conf_dirname, filename); |
134 | 70 | } |
135 | | |
136 | 75 | file = fopen(include_filename, "r"); |
137 | 75 | if (file == NULL) { |
138 | 70 | SCLogError("Failed to open configuration include file %s: %s", include_filename, |
139 | 70 | strerror(errno)); |
140 | 70 | goto done; |
141 | 70 | } |
142 | | |
143 | 5 | yaml_parser_set_input_file(&parser, file); |
144 | | |
145 | 5 | if (ConfYamlParse(&parser, parent, 0, 0, 0) != 0) { |
146 | 5 | SCLogError("Failed to include configuration file %s", filename); |
147 | 5 | goto done; |
148 | 5 | } |
149 | | |
150 | 0 | ret = 0; |
151 | |
|
152 | 75 | done: |
153 | 75 | yaml_parser_delete(&parser); |
154 | 75 | if (file != NULL) { |
155 | 5 | fclose(file); |
156 | 5 | } |
157 | | |
158 | 75 | return ret; |
159 | 0 | } |
160 | | |
161 | | /** |
162 | | * \brief Parse a YAML layer. |
163 | | * |
164 | | * \param parser A pointer to an active yaml_parser_t. |
165 | | * \param parent The parent configuration node. |
166 | | * |
167 | | * \retval 0 on success, -1 on failure. |
168 | | */ |
169 | | static int ConfYamlParse(yaml_parser_t *parser, ConfNode *parent, int inseq, int rlevel, int state) |
170 | 2.02M | { |
171 | 2.02M | ConfNode *node = parent; |
172 | 2.02M | yaml_event_t event; |
173 | 2.02M | memset(&event, 0, sizeof(event)); |
174 | 2.02M | int done = 0; |
175 | 2.02M | int seq_idx = 0; |
176 | 2.02M | int retval = 0; |
177 | 2.02M | int was_empty = -1; |
178 | 2.02M | int include_count = 0; |
179 | | |
180 | 2.02M | if (rlevel++ > RECURSION_LIMIT) { |
181 | 19 | SCLogError("Recursion limit reached while parsing " |
182 | 19 | "configuration file, aborting."); |
183 | 19 | return -1; |
184 | 19 | } |
185 | | |
186 | 10.1M | while (!done) { |
187 | 8.10M | if (!yaml_parser_parse(parser, &event)) { |
188 | 4.02k | SCLogError("Failed to parse configuration file at line %" PRIuMAX ": %s", |
189 | 4.02k | (uintmax_t)parser->problem_mark.line, parser->problem); |
190 | 4.02k | retval = -1; |
191 | 4.02k | break; |
192 | 4.02k | } |
193 | | |
194 | 8.09M | if (event.type == YAML_DOCUMENT_START_EVENT) { |
195 | 282k | SCLogDebug("event.type=YAML_DOCUMENT_START_EVENT; state=%d", state); |
196 | | /* Verify YAML version - its more likely to be a valid |
197 | | * Suricata configuration file if the version is |
198 | | * correct. */ |
199 | 282k | yaml_version_directive_t *ver = |
200 | 282k | event.data.document_start.version_directive; |
201 | 282k | if (ver == NULL) { |
202 | 2.80k | SCLogError("ERROR: Invalid configuration file."); |
203 | 2.80k | SCLogError("The configuration file must begin with the following two lines: %%YAML " |
204 | 2.80k | "1.1 and ---"); |
205 | 2.80k | goto fail; |
206 | 2.80k | } |
207 | 279k | int major = ver->major; |
208 | 279k | int minor = ver->minor; |
209 | 279k | if (!(major == YAML_VERSION_MAJOR && minor == YAML_VERSION_MINOR)) { |
210 | 1 | SCLogError("ERROR: Invalid YAML version. Must be 1.1"); |
211 | 1 | goto fail; |
212 | 1 | } |
213 | 279k | } |
214 | 7.81M | else if (event.type == YAML_SCALAR_EVENT) { |
215 | 3.50M | char *value = (char *)event.data.scalar.value; |
216 | 3.50M | char *tag = (char *)event.data.scalar.tag; |
217 | 3.50M | SCLogDebug("event.type=YAML_SCALAR_EVENT; state=%d; value=%s; " |
218 | 3.50M | "tag=%s; inseq=%d", state, value, tag, inseq); |
219 | | |
220 | | /* Skip over empty scalar values while in KEY state. This |
221 | | * tends to only happen on an empty file, where a scalar |
222 | | * event probably shouldn't fire anyways. */ |
223 | 3.50M | if (state == CONF_KEY && strlen(value) == 0) { |
224 | 1.32M | goto next; |
225 | 1.32M | } |
226 | | |
227 | | /* If the value is unquoted, certain strings in YAML represent NULL. */ |
228 | 2.18M | if ((inseq || state == CONF_VAL) && |
229 | 1.07M | event.data.scalar.style == YAML_PLAIN_SCALAR_STYLE) { |
230 | 954k | if (strlen(value) == 0 || strcmp(value, "~") == 0 || strcmp(value, "null") == 0 || |
231 | 838k | strcmp(value, "Null") == 0 || strcmp(value, "NULL") == 0) { |
232 | 838k | value = NULL; |
233 | 838k | } |
234 | 954k | } |
235 | | |
236 | 2.18M | if (inseq) { |
237 | 139k | if (state == CONF_INCLUDE) { |
238 | 129k | if (value != NULL) { |
239 | 2 | SCLogInfo("Including configuration file %s.", value); |
240 | 2 | if (ConfYamlHandleInclude(parent, value) != 0) { |
241 | 2 | goto fail; |
242 | 2 | } |
243 | 2 | } |
244 | 129k | goto next; |
245 | 129k | } |
246 | 10.3k | char sequence_node_name[DEFAULT_NAME_LEN]; |
247 | 10.3k | snprintf(sequence_node_name, DEFAULT_NAME_LEN, "%d", seq_idx++); |
248 | 10.3k | ConfNode *seq_node = NULL; |
249 | 10.3k | if (was_empty < 0) { |
250 | | // initialize was_empty |
251 | 3.16k | if (TAILQ_EMPTY(&parent->head)) { |
252 | 900 | was_empty = 1; |
253 | 2.26k | } else { |
254 | 2.26k | was_empty = 0; |
255 | 2.26k | } |
256 | 3.16k | } |
257 | | // we only check if the node's list was not empty at first |
258 | 10.3k | if (was_empty == 0) { |
259 | 7.95k | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
260 | | // do not fuzz quadratic-complexity overlong sequence of scalars |
261 | 7.95k | if (seq_idx > 256) { |
262 | 4 | goto fail; |
263 | 4 | } |
264 | 7.95k | #endif |
265 | 7.95k | seq_node = ConfNodeLookupChild(parent, sequence_node_name); |
266 | 7.95k | } |
267 | 10.3k | if (seq_node != NULL) { |
268 | | /* The sequence node has already been set, probably |
269 | | * from the command line. Remove it so it gets |
270 | | * re-added in the expected order for iteration. |
271 | | */ |
272 | 7.66k | TAILQ_REMOVE(&parent->head, seq_node, next); |
273 | 7.66k | } |
274 | 2.70k | else { |
275 | 2.70k | seq_node = ConfNodeNew(); |
276 | 2.70k | if (unlikely(seq_node == NULL)) { |
277 | 0 | goto fail; |
278 | 0 | } |
279 | 2.70k | seq_node->name = SCStrdup(sequence_node_name); |
280 | 2.70k | if (unlikely(seq_node->name == NULL)) { |
281 | 0 | SCFree(seq_node); |
282 | 0 | goto fail; |
283 | 0 | } |
284 | 2.70k | if (value != NULL) { |
285 | 1.82k | seq_node->val = SCStrdup(value); |
286 | 1.82k | if (unlikely(seq_node->val == NULL)) { |
287 | 0 | SCFree(seq_node->name); |
288 | 0 | goto fail; |
289 | 0 | } |
290 | 1.82k | } else { |
291 | 883 | seq_node->val = NULL; |
292 | 883 | } |
293 | 2.70k | } |
294 | 10.3k | TAILQ_INSERT_TAIL(&parent->head, seq_node, next); |
295 | 10.3k | } |
296 | 2.04M | else { |
297 | 2.04M | if (state == CONF_INCLUDE) { |
298 | 72 | SCLogInfo("Including configuration file %s.", value); |
299 | 72 | if (ConfYamlHandleInclude(parent, value) != 0) { |
300 | 72 | goto fail; |
301 | 72 | } |
302 | 0 | state = CONF_KEY; |
303 | 0 | } |
304 | 2.04M | else if (state == CONF_KEY) { |
305 | | |
306 | 1.10M | if (strcmp(value, "include") == 0) { |
307 | 129k | state = CONF_INCLUDE; |
308 | 129k | if (++include_count > 1) { |
309 | 128k | SCLogWarning("Multipline \"include\" fields at the same level are " |
310 | 128k | "deprecated and will not work in Suricata 8, please move " |
311 | 128k | "to an array of include files: line: %zu", |
312 | 128k | parser->mark.line); |
313 | 128k | } |
314 | 129k | goto next; |
315 | 129k | } |
316 | | |
317 | 979k | if (parent->is_seq) { |
318 | 964k | if (parent->val == NULL) { |
319 | 9.19k | parent->val = SCStrdup(value); |
320 | 9.19k | if (parent->val && strchr(parent->val, '_')) |
321 | 1.00k | Mangle(parent->val); |
322 | 9.19k | } |
323 | 964k | } |
324 | | |
325 | 979k | if (strchr(value, '.') != NULL) { |
326 | 9.07k | node = ConfNodeGetNodeOrCreate(parent, value, 0); |
327 | 9.07k | if (node == NULL) { |
328 | | /* Error message already logged. */ |
329 | 18 | goto fail; |
330 | 18 | } |
331 | 970k | } else { |
332 | 970k | ConfNode *existing = ConfNodeLookupChild(parent, value); |
333 | 970k | if (existing != NULL) { |
334 | 937k | if (!existing->final) { |
335 | 937k | SCLogInfo("Configuration node '%s' redefined.", existing->name); |
336 | 937k | ConfNodePrune(existing); |
337 | 937k | } |
338 | 937k | node = existing; |
339 | 937k | } else { |
340 | 32.3k | node = ConfNodeNew(); |
341 | 32.3k | if (unlikely(node == NULL)) { |
342 | 0 | goto fail; |
343 | 0 | } |
344 | 32.3k | node->name = SCStrdup(value); |
345 | 32.3k | node->parent = parent; |
346 | 32.3k | if (node->name && strchr(node->name, '_')) { |
347 | 2.81k | if (!(parent->name && |
348 | 2.48k | ((strcmp(parent->name, "address-groups") == 0) || |
349 | 2.35k | (strcmp(parent->name, "port-groups") == 0)))) { |
350 | 2.35k | Mangle(node->name); |
351 | 2.35k | if (mangle_errors < MANGLE_ERRORS_MAX) { |
352 | 76 | SCLogWarning( |
353 | 76 | "%s is deprecated. Please use %s on line %" PRIuMAX |
354 | 76 | ".", |
355 | 76 | value, node->name, |
356 | 76 | (uintmax_t)parser->mark.line + 1); |
357 | 76 | mangle_errors++; |
358 | 76 | if (mangle_errors >= MANGLE_ERRORS_MAX) |
359 | 1 | SCLogWarning("not showing more " |
360 | 76 | "parameter name warnings."); |
361 | 76 | } |
362 | 2.35k | } |
363 | 2.81k | } |
364 | 32.3k | TAILQ_INSERT_TAIL(&parent->head, node, next); |
365 | 32.3k | } |
366 | 970k | } |
367 | 979k | state = CONF_VAL; |
368 | 979k | } |
369 | 932k | else { |
370 | 932k | if (value != NULL && (tag != NULL) && (strcmp(tag, "!include") == 0)) { |
371 | 1 | SCLogInfo("Including configuration file %s at " |
372 | 1 | "parent node %s.", value, node->name); |
373 | 1 | if (ConfYamlHandleInclude(node, value) != 0) |
374 | 1 | goto fail; |
375 | 932k | } else if (!node->final && value != NULL) { |
376 | 226k | if (node->val != NULL) |
377 | 1.04k | SCFree(node->val); |
378 | 226k | node->val = SCStrdup(value); |
379 | 226k | } |
380 | 932k | state = CONF_KEY; |
381 | 932k | } |
382 | 2.04M | } |
383 | 2.18M | } |
384 | 4.31M | else if (event.type == YAML_SEQUENCE_START_EVENT) { |
385 | 1.56M | SCLogDebug("event.type=YAML_SEQUENCE_START_EVENT; state=%d", state); |
386 | | /* If we're processing a list of includes, use the current parent. */ |
387 | 1.56M | if (ConfYamlParse(parser, state == CONF_INCLUDE ? parent : node, 1, rlevel, |
388 | 1.56M | state == CONF_INCLUDE ? CONF_INCLUDE : 0) != 0) |
389 | 5.61k | goto fail; |
390 | 1.55M | node->is_seq = 1; |
391 | 1.55M | state = CONF_KEY; |
392 | 1.55M | } |
393 | 2.75M | else if (event.type == YAML_SEQUENCE_END_EVENT) { |
394 | 1.55M | SCLogDebug("event.type=YAML_SEQUENCE_END_EVENT; state=%d", state); |
395 | 1.55M | done = 1; |
396 | 1.55M | } |
397 | 1.19M | else if (event.type == YAML_MAPPING_START_EVENT) { |
398 | 455k | SCLogDebug("event.type=YAML_MAPPING_START_EVENT; state=%d", state); |
399 | 455k | if (state == CONF_INCLUDE) { |
400 | 1 | SCLogError("Include fields cannot be a mapping: line %zu", parser->mark.line); |
401 | 1 | goto fail; |
402 | 1 | } |
403 | 455k | if (inseq) { |
404 | 430k | char sequence_node_name[DEFAULT_NAME_LEN]; |
405 | 430k | snprintf(sequence_node_name, DEFAULT_NAME_LEN, "%d", seq_idx++); |
406 | 430k | ConfNode *seq_node = NULL; |
407 | 430k | if (was_empty < 0) { |
408 | | // initialize was_empty |
409 | 426k | if (TAILQ_EMPTY(&node->head)) { |
410 | 7.36k | was_empty = 1; |
411 | 419k | } else { |
412 | 419k | was_empty = 0; |
413 | 419k | } |
414 | 426k | } |
415 | | // we only check if the node's list was not empty at first |
416 | 430k | if (was_empty == 0) { |
417 | 422k | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
418 | | // do not fuzz quadratic-complexity overlong sequence of scalars |
419 | 422k | if (seq_idx > 256) { |
420 | 2 | goto fail; |
421 | 2 | } |
422 | 422k | #endif |
423 | 422k | seq_node = ConfNodeLookupChild(node, sequence_node_name); |
424 | 422k | } |
425 | 430k | if (seq_node != NULL) { |
426 | | /* The sequence node has already been set, probably |
427 | | * from the command line. Remove it so it gets |
428 | | * re-added in the expected order for iteration. |
429 | | */ |
430 | 421k | TAILQ_REMOVE(&node->head, seq_node, next); |
431 | 421k | } |
432 | 9.35k | else { |
433 | 9.35k | seq_node = ConfNodeNew(); |
434 | 9.35k | if (unlikely(seq_node == NULL)) { |
435 | 0 | goto fail; |
436 | 0 | } |
437 | 9.35k | seq_node->name = SCStrdup(sequence_node_name); |
438 | 9.35k | if (unlikely(seq_node->name == NULL)) { |
439 | 0 | SCFree(seq_node); |
440 | 0 | goto fail; |
441 | 0 | } |
442 | 9.35k | } |
443 | 430k | seq_node->is_seq = 1; |
444 | 430k | TAILQ_INSERT_TAIL(&node->head, seq_node, next); |
445 | 430k | if (ConfYamlParse(parser, seq_node, 0, rlevel, 0) != 0) |
446 | 1.46k | goto fail; |
447 | 430k | } |
448 | 24.4k | else { |
449 | 24.4k | if (ConfYamlParse(parser, node, inseq, rlevel, 0) != 0) |
450 | 5.74k | goto fail; |
451 | 24.4k | } |
452 | 448k | state = CONF_KEY; |
453 | 448k | } |
454 | 737k | else if (event.type == YAML_MAPPING_END_EVENT) { |
455 | 448k | SCLogDebug("event.type=YAML_MAPPING_END_EVENT; state=%d", state); |
456 | 448k | done = 1; |
457 | 448k | } |
458 | 289k | else if (event.type == YAML_STREAM_END_EVENT) { |
459 | 1.31k | SCLogDebug("event.type=YAML_STREAM_END_EVENT; state=%d", state); |
460 | 1.31k | done = 1; |
461 | 1.31k | } |
462 | | |
463 | 8.08M | next: |
464 | 8.08M | yaml_event_delete(&event); |
465 | 8.08M | continue; |
466 | | |
467 | 15.7k | fail: |
468 | 15.7k | yaml_event_delete(&event); |
469 | 15.7k | retval = -1; |
470 | 15.7k | break; |
471 | 8.09M | } |
472 | | |
473 | 2.02M | rlevel--; |
474 | 2.02M | return retval; |
475 | 2.02M | } |
476 | | |
477 | | /** |
478 | | * \brief Load configuration from a YAML file. |
479 | | * |
480 | | * This function will load a configuration file. On failure -1 will |
481 | | * be returned and it is suggested that the program then exit. Any |
482 | | * errors while loading the configuration file will have already been |
483 | | * logged. |
484 | | * |
485 | | * \param filename Filename of configuration file to load. |
486 | | * |
487 | | * \retval 0 on success, -1 on failure. |
488 | | */ |
489 | | int |
490 | | ConfYamlLoadFile(const char *filename) |
491 | 0 | { |
492 | 0 | FILE *infile; |
493 | 0 | yaml_parser_t parser; |
494 | 0 | int ret; |
495 | 0 | ConfNode *root = ConfGetRootNode(); |
496 | |
|
497 | 0 | if (yaml_parser_initialize(&parser) != 1) { |
498 | 0 | SCLogError("failed to initialize yaml parser."); |
499 | 0 | return -1; |
500 | 0 | } |
501 | | |
502 | 0 | struct stat stat_buf; |
503 | 0 | if (stat(filename, &stat_buf) == 0) { |
504 | 0 | if (stat_buf.st_mode & S_IFDIR) { |
505 | 0 | SCLogError("yaml argument is not a file but a directory: %s. " |
506 | 0 | "Please specify the yaml file in your -c option.", |
507 | 0 | filename); |
508 | 0 | yaml_parser_delete(&parser); |
509 | 0 | return -1; |
510 | 0 | } |
511 | 0 | } |
512 | | |
513 | | // coverity[toctou : FALSE] |
514 | 0 | infile = fopen(filename, "r"); |
515 | 0 | if (infile == NULL) { |
516 | 0 | SCLogError("failed to open file: %s: %s", filename, strerror(errno)); |
517 | 0 | yaml_parser_delete(&parser); |
518 | 0 | return -1; |
519 | 0 | } |
520 | | |
521 | 0 | if (conf_dirname == NULL) { |
522 | 0 | ConfYamlSetConfDirname(filename); |
523 | 0 | } |
524 | |
|
525 | 0 | yaml_parser_set_input_file(&parser, infile); |
526 | 0 | ret = ConfYamlParse(&parser, root, 0, 0, 0); |
527 | 0 | yaml_parser_delete(&parser); |
528 | 0 | fclose(infile); |
529 | |
|
530 | 0 | return ret; |
531 | 0 | } |
532 | | |
533 | | /** |
534 | | * \brief Load configuration from a YAML string. |
535 | | */ |
536 | | int |
537 | | ConfYamlLoadString(const char *string, size_t len) |
538 | 8.25k | { |
539 | 8.25k | ConfNode *root = ConfGetRootNode(); |
540 | 8.25k | yaml_parser_t parser; |
541 | 8.25k | int ret; |
542 | | |
543 | 8.25k | if (yaml_parser_initialize(&parser) != 1) { |
544 | 0 | fprintf(stderr, "Failed to initialize yaml parser.\n"); |
545 | 0 | exit(EXIT_FAILURE); |
546 | 0 | } |
547 | 8.25k | yaml_parser_set_input_string(&parser, (const unsigned char *)string, len); |
548 | 8.25k | ret = ConfYamlParse(&parser, root, 0, 0, 0); |
549 | 8.25k | yaml_parser_delete(&parser); |
550 | | |
551 | 8.25k | return ret; |
552 | 8.25k | } |
553 | | |
554 | | /** |
555 | | * \brief Load configuration from a YAML file, insert in tree at 'prefix' |
556 | | * |
557 | | * This function will load a configuration file and insert it into the |
558 | | * config tree at 'prefix'. This means that if this is called with prefix |
559 | | * "abc" and the file contains a parameter "def", it will be loaded as |
560 | | * "abc.def". |
561 | | * |
562 | | * \param filename Filename of configuration file to load. |
563 | | * \param prefix Name prefix to use. |
564 | | * |
565 | | * \retval 0 on success, -1 on failure. |
566 | | */ |
567 | | int |
568 | | ConfYamlLoadFileWithPrefix(const char *filename, const char *prefix) |
569 | 0 | { |
570 | 0 | FILE *infile; |
571 | 0 | yaml_parser_t parser; |
572 | 0 | int ret; |
573 | 0 | ConfNode *root = ConfGetNode(prefix); |
574 | |
|
575 | 0 | struct stat stat_buf; |
576 | | /* coverity[toctou] */ |
577 | 0 | if (stat(filename, &stat_buf) == 0) { |
578 | 0 | if (stat_buf.st_mode & S_IFDIR) { |
579 | 0 | SCLogError("yaml argument is not a file but a directory: %s. " |
580 | 0 | "Please specify the yaml file in your -c option.", |
581 | 0 | filename); |
582 | 0 | return -1; |
583 | 0 | } |
584 | 0 | } |
585 | | |
586 | 0 | if (yaml_parser_initialize(&parser) != 1) { |
587 | 0 | SCLogError("failed to initialize yaml parser."); |
588 | 0 | return -1; |
589 | 0 | } |
590 | | |
591 | | /* coverity[toctou] */ |
592 | 0 | infile = fopen(filename, "r"); |
593 | 0 | if (infile == NULL) { |
594 | 0 | SCLogError("failed to open file: %s: %s", filename, strerror(errno)); |
595 | 0 | yaml_parser_delete(&parser); |
596 | 0 | return -1; |
597 | 0 | } |
598 | | |
599 | 0 | if (conf_dirname == NULL) { |
600 | 0 | ConfYamlSetConfDirname(filename); |
601 | 0 | } |
602 | |
|
603 | 0 | if (root == NULL) { |
604 | | /* if node at 'prefix' doesn't yet exist, add a place holder */ |
605 | 0 | ConfSet(prefix, "<prefix root node>"); |
606 | 0 | root = ConfGetNode(prefix); |
607 | 0 | if (root == NULL) { |
608 | 0 | fclose(infile); |
609 | 0 | yaml_parser_delete(&parser); |
610 | 0 | return -1; |
611 | 0 | } |
612 | 0 | } |
613 | 0 | yaml_parser_set_input_file(&parser, infile); |
614 | 0 | ret = ConfYamlParse(&parser, root, 0, 0, 0); |
615 | 0 | yaml_parser_delete(&parser); |
616 | 0 | fclose(infile); |
617 | |
|
618 | 0 | return ret; |
619 | 0 | } |
620 | | |
621 | | #ifdef UNITTESTS |
622 | | |
623 | | static int |
624 | | ConfYamlSequenceTest(void) |
625 | | { |
626 | | char input[] = "\ |
627 | | %YAML 1.1\n\ |
628 | | ---\n\ |
629 | | rule-files:\n\ |
630 | | - netbios.rules\n\ |
631 | | - x11.rules\n\ |
632 | | \n\ |
633 | | default-log-dir: /tmp\n\ |
634 | | "; |
635 | | |
636 | | ConfCreateContextBackup(); |
637 | | ConfInit(); |
638 | | |
639 | | ConfYamlLoadString(input, strlen(input)); |
640 | | |
641 | | ConfNode *node; |
642 | | node = ConfGetNode("rule-files"); |
643 | | FAIL_IF_NULL(node); |
644 | | FAIL_IF_NOT(ConfNodeIsSequence(node)); |
645 | | FAIL_IF(TAILQ_EMPTY(&node->head)); |
646 | | int i = 0; |
647 | | ConfNode *filename; |
648 | | TAILQ_FOREACH(filename, &node->head, next) { |
649 | | if (i == 0) { |
650 | | FAIL_IF(strcmp(filename->val, "netbios.rules") != 0); |
651 | | FAIL_IF(ConfNodeIsSequence(filename)); |
652 | | FAIL_IF(filename->is_seq != 0); |
653 | | } |
654 | | else if (i == 1) { |
655 | | FAIL_IF(strcmp(filename->val, "x11.rules") != 0); |
656 | | FAIL_IF(ConfNodeIsSequence(filename)); |
657 | | } |
658 | | FAIL_IF(i > 1); |
659 | | i++; |
660 | | } |
661 | | |
662 | | ConfDeInit(); |
663 | | ConfRestoreContextBackup(); |
664 | | PASS; |
665 | | } |
666 | | |
667 | | static int |
668 | | ConfYamlLoggingOutputTest(void) |
669 | | { |
670 | | char input[] = "\ |
671 | | %YAML 1.1\n\ |
672 | | ---\n\ |
673 | | logging:\n\ |
674 | | output:\n\ |
675 | | - interface: console\n\ |
676 | | log-level: error\n\ |
677 | | - interface: syslog\n\ |
678 | | facility: local4\n\ |
679 | | log-level: info\n\ |
680 | | "; |
681 | | |
682 | | ConfCreateContextBackup(); |
683 | | ConfInit(); |
684 | | |
685 | | ConfYamlLoadString(input, strlen(input)); |
686 | | |
687 | | ConfNode *outputs; |
688 | | outputs = ConfGetNode("logging.output"); |
689 | | FAIL_IF_NULL(outputs); |
690 | | |
691 | | ConfNode *output; |
692 | | ConfNode *output_param; |
693 | | |
694 | | output = TAILQ_FIRST(&outputs->head); |
695 | | FAIL_IF_NULL(output); |
696 | | FAIL_IF(strcmp(output->name, "0") != 0); |
697 | | |
698 | | output_param = TAILQ_FIRST(&output->head); |
699 | | FAIL_IF_NULL(output_param); |
700 | | FAIL_IF(strcmp(output_param->name, "interface") != 0); |
701 | | FAIL_IF(strcmp(output_param->val, "console") != 0); |
702 | | |
703 | | output_param = TAILQ_NEXT(output_param, next); |
704 | | FAIL_IF(strcmp(output_param->name, "log-level") != 0); |
705 | | FAIL_IF(strcmp(output_param->val, "error") != 0); |
706 | | |
707 | | output = TAILQ_NEXT(output, next); |
708 | | FAIL_IF_NULL(output); |
709 | | FAIL_IF(strcmp(output->name, "1") != 0); |
710 | | |
711 | | output_param = TAILQ_FIRST(&output->head); |
712 | | FAIL_IF_NULL(output_param); |
713 | | FAIL_IF(strcmp(output_param->name, "interface") != 0); |
714 | | FAIL_IF(strcmp(output_param->val, "syslog") != 0); |
715 | | |
716 | | output_param = TAILQ_NEXT(output_param, next); |
717 | | FAIL_IF(strcmp(output_param->name, "facility") != 0); |
718 | | FAIL_IF(strcmp(output_param->val, "local4") != 0); |
719 | | |
720 | | output_param = TAILQ_NEXT(output_param, next); |
721 | | FAIL_IF(strcmp(output_param->name, "log-level") != 0); |
722 | | FAIL_IF(strcmp(output_param->val, "info") != 0); |
723 | | |
724 | | ConfDeInit(); |
725 | | ConfRestoreContextBackup(); |
726 | | |
727 | | PASS; |
728 | | } |
729 | | |
730 | | /** |
731 | | * Try to load something that is not a valid YAML file. |
732 | | */ |
733 | | static int |
734 | | ConfYamlNonYamlFileTest(void) |
735 | | { |
736 | | ConfCreateContextBackup(); |
737 | | ConfInit(); |
738 | | |
739 | | FAIL_IF(ConfYamlLoadFile("/etc/passwd") != -1); |
740 | | |
741 | | ConfDeInit(); |
742 | | ConfRestoreContextBackup(); |
743 | | |
744 | | PASS; |
745 | | } |
746 | | |
747 | | static int |
748 | | ConfYamlBadYamlVersionTest(void) |
749 | | { |
750 | | char input[] = "\ |
751 | | %YAML 9.9\n\ |
752 | | ---\n\ |
753 | | logging:\n\ |
754 | | output:\n\ |
755 | | - interface: console\n\ |
756 | | log-level: error\n\ |
757 | | - interface: syslog\n\ |
758 | | facility: local4\n\ |
759 | | log-level: info\n\ |
760 | | "; |
761 | | |
762 | | ConfCreateContextBackup(); |
763 | | ConfInit(); |
764 | | |
765 | | FAIL_IF(ConfYamlLoadString(input, strlen(input)) != -1); |
766 | | |
767 | | ConfDeInit(); |
768 | | ConfRestoreContextBackup(); |
769 | | |
770 | | PASS; |
771 | | } |
772 | | |
773 | | static int |
774 | | ConfYamlSecondLevelSequenceTest(void) |
775 | | { |
776 | | char input[] = "\ |
777 | | %YAML 1.1\n\ |
778 | | ---\n\ |
779 | | libhtp:\n\ |
780 | | server-config:\n\ |
781 | | - apache-php:\n\ |
782 | | address: [\"192.168.1.0/24\"]\n\ |
783 | | personality: [\"Apache_2_2\", \"PHP_5_3\"]\n\ |
784 | | path-parsing: [\"compress_separators\", \"lowercase\"]\n\ |
785 | | - iis-php:\n\ |
786 | | address:\n\ |
787 | | - 192.168.0.0/24\n\ |
788 | | \n\ |
789 | | personality:\n\ |
790 | | - IIS_7_0\n\ |
791 | | - PHP_5_3\n\ |
792 | | \n\ |
793 | | path-parsing:\n\ |
794 | | - compress_separators\n\ |
795 | | "; |
796 | | |
797 | | ConfCreateContextBackup(); |
798 | | ConfInit(); |
799 | | |
800 | | FAIL_IF(ConfYamlLoadString(input, strlen(input)) != 0); |
801 | | |
802 | | ConfNode *outputs; |
803 | | outputs = ConfGetNode("libhtp.server-config"); |
804 | | FAIL_IF_NULL(outputs); |
805 | | |
806 | | ConfNode *node; |
807 | | |
808 | | node = TAILQ_FIRST(&outputs->head); |
809 | | FAIL_IF_NULL(node); |
810 | | FAIL_IF(strcmp(node->name, "0") != 0); |
811 | | |
812 | | node = TAILQ_FIRST(&node->head); |
813 | | FAIL_IF_NULL(node); |
814 | | FAIL_IF(strcmp(node->name, "apache-php") != 0); |
815 | | |
816 | | node = ConfNodeLookupChild(node, "address"); |
817 | | FAIL_IF_NULL(node); |
818 | | |
819 | | node = TAILQ_FIRST(&node->head); |
820 | | FAIL_IF_NULL(node); |
821 | | FAIL_IF(strcmp(node->name, "0") != 0); |
822 | | FAIL_IF(strcmp(node->val, "192.168.1.0/24") != 0); |
823 | | |
824 | | ConfDeInit(); |
825 | | ConfRestoreContextBackup(); |
826 | | |
827 | | PASS; |
828 | | } |
829 | | |
830 | | /** |
831 | | * Test file inclusion support. |
832 | | */ |
833 | | static int |
834 | | ConfYamlFileIncludeTest(void) |
835 | | { |
836 | | FILE *config_file; |
837 | | |
838 | | const char config_filename[] = "ConfYamlFileIncludeTest-config.yaml"; |
839 | | const char config_file_contents[] = |
840 | | "%YAML 1.1\n" |
841 | | "---\n" |
842 | | "# Include something at the root level.\n" |
843 | | "include: ConfYamlFileIncludeTest-include.yaml\n" |
844 | | "# Test including under a mapping.\n" |
845 | | "mapping: !include ConfYamlFileIncludeTest-include.yaml\n"; |
846 | | |
847 | | const char include_filename[] = "ConfYamlFileIncludeTest-include.yaml"; |
848 | | const char include_file_contents[] = |
849 | | "%YAML 1.1\n" |
850 | | "---\n" |
851 | | "host-mode: auto\n" |
852 | | "unix-command:\n" |
853 | | " enabled: no\n"; |
854 | | |
855 | | ConfCreateContextBackup(); |
856 | | ConfInit(); |
857 | | |
858 | | /* Write out the test files. */ |
859 | | FAIL_IF_NULL((config_file = fopen(config_filename, "w"))); |
860 | | FAIL_IF(fwrite(config_file_contents, strlen(config_file_contents), 1, config_file) != 1); |
861 | | fclose(config_file); |
862 | | |
863 | | FAIL_IF_NULL((config_file = fopen(include_filename, "w"))); |
864 | | FAIL_IF(fwrite(include_file_contents, strlen(include_file_contents), 1, config_file) != 1); |
865 | | fclose(config_file); |
866 | | |
867 | | /* Reset conf_dirname. */ |
868 | | if (conf_dirname != NULL) { |
869 | | SCFree(conf_dirname); |
870 | | conf_dirname = NULL; |
871 | | } |
872 | | |
873 | | FAIL_IF(ConfYamlLoadFile("ConfYamlFileIncludeTest-config.yaml") != 0); |
874 | | |
875 | | /* Check values that should have been loaded into the root of the |
876 | | * configuration. */ |
877 | | ConfNode *node; |
878 | | node = ConfGetNode("host-mode"); |
879 | | FAIL_IF_NULL(node); |
880 | | FAIL_IF(strcmp(node->val, "auto") != 0); |
881 | | |
882 | | node = ConfGetNode("unix-command.enabled"); |
883 | | FAIL_IF_NULL(node); |
884 | | FAIL_IF(strcmp(node->val, "no") != 0); |
885 | | |
886 | | /* Check for values that were included under a mapping. */ |
887 | | node = ConfGetNode("mapping.host-mode"); |
888 | | FAIL_IF_NULL(node); |
889 | | FAIL_IF(strcmp(node->val, "auto") != 0); |
890 | | |
891 | | node = ConfGetNode("mapping.unix-command.enabled"); |
892 | | FAIL_IF_NULL(node); |
893 | | FAIL_IF(strcmp(node->val, "no") != 0); |
894 | | |
895 | | ConfDeInit(); |
896 | | ConfRestoreContextBackup(); |
897 | | |
898 | | unlink(config_filename); |
899 | | unlink(include_filename); |
900 | | |
901 | | PASS; |
902 | | } |
903 | | |
904 | | /** |
905 | | * Test that a configuration section is overridden but subsequent |
906 | | * occurrences. |
907 | | */ |
908 | | static int |
909 | | ConfYamlOverrideTest(void) |
910 | | { |
911 | | char config[] = "%YAML 1.1\n" |
912 | | "---\n" |
913 | | "some-log-dir: /var/log\n" |
914 | | "some-log-dir: /tmp\n" |
915 | | "\n" |
916 | | "parent:\n" |
917 | | " child0:\n" |
918 | | " key: value\n" |
919 | | "parent:\n" |
920 | | " child1:\n" |
921 | | " key: value\n" |
922 | | "vars:\n" |
923 | | " address-groups:\n" |
924 | | " HOME_NET: \"[192.168.0.0/16,10.0.0.0/8,172.16.0.0/12]\"\n" |
925 | | " EXTERNAL_NET: any\n" |
926 | | "vars.address-groups.HOME_NET: \"10.10.10.10/32\"\n"; |
927 | | const char *value; |
928 | | |
929 | | ConfCreateContextBackup(); |
930 | | ConfInit(); |
931 | | |
932 | | FAIL_IF(ConfYamlLoadString(config, strlen(config)) != 0); |
933 | | FAIL_IF_NOT(ConfGet("some-log-dir", &value)); |
934 | | FAIL_IF(strcmp(value, "/tmp") != 0); |
935 | | |
936 | | /* Test that parent.child0 does not exist, but child1 does. */ |
937 | | FAIL_IF_NOT_NULL(ConfGetNode("parent.child0")); |
938 | | FAIL_IF_NOT(ConfGet("parent.child1.key", &value)); |
939 | | FAIL_IF(strcmp(value, "value") != 0); |
940 | | |
941 | | /* First check that vars.address-groups.EXTERNAL_NET has the |
942 | | * expected parent of vars.address-groups and save this |
943 | | * pointer. We want to make sure that the overrided value has the |
944 | | * same parent later on. */ |
945 | | ConfNode *vars_address_groups = ConfGetNode("vars.address-groups"); |
946 | | FAIL_IF_NULL(vars_address_groups); |
947 | | ConfNode *vars_address_groups_external_net = ConfGetNode("vars.address-groups.EXTERNAL_NET"); |
948 | | FAIL_IF_NULL(vars_address_groups_external_net); |
949 | | FAIL_IF_NOT(vars_address_groups_external_net->parent == vars_address_groups); |
950 | | |
951 | | /* Now check that HOME_NET has the overrided value. */ |
952 | | ConfNode *vars_address_groups_home_net = ConfGetNode("vars.address-groups.HOME_NET"); |
953 | | FAIL_IF_NULL(vars_address_groups_home_net); |
954 | | FAIL_IF(strcmp(vars_address_groups_home_net->val, "10.10.10.10/32") != 0); |
955 | | |
956 | | /* And check that it has the correct parent. */ |
957 | | FAIL_IF_NOT(vars_address_groups_home_net->parent == vars_address_groups); |
958 | | |
959 | | ConfDeInit(); |
960 | | ConfRestoreContextBackup(); |
961 | | |
962 | | PASS; |
963 | | } |
964 | | |
965 | | /** |
966 | | * Test that a configuration parameter loaded from YAML doesn't |
967 | | * override a 'final' value that may be set on the command line. |
968 | | */ |
969 | | static int |
970 | | ConfYamlOverrideFinalTest(void) |
971 | | { |
972 | | ConfCreateContextBackup(); |
973 | | ConfInit(); |
974 | | |
975 | | char config[] = |
976 | | "%YAML 1.1\n" |
977 | | "---\n" |
978 | | "default-log-dir: /var/log\n"; |
979 | | |
980 | | /* Set the log directory as if it was set on the command line. */ |
981 | | FAIL_IF_NOT(ConfSetFinal("default-log-dir", "/tmp")); |
982 | | FAIL_IF(ConfYamlLoadString(config, strlen(config)) != 0); |
983 | | |
984 | | const char *default_log_dir; |
985 | | |
986 | | FAIL_IF_NOT(ConfGet("default-log-dir", &default_log_dir)); |
987 | | FAIL_IF(strcmp(default_log_dir, "/tmp") != 0); |
988 | | |
989 | | ConfDeInit(); |
990 | | ConfRestoreContextBackup(); |
991 | | |
992 | | PASS; |
993 | | } |
994 | | |
995 | | static int ConfYamlNull(void) |
996 | | { |
997 | | ConfCreateContextBackup(); |
998 | | ConfInit(); |
999 | | |
1000 | | char config[] = "%YAML 1.1\n" |
1001 | | "---\n" |
1002 | | "quoted-tilde: \"~\"\n" |
1003 | | "unquoted-tilde: ~\n" |
1004 | | "quoted-null: \"null\"\n" |
1005 | | "unquoted-null: null\n" |
1006 | | "quoted-Null: \"Null\"\n" |
1007 | | "unquoted-Null: Null\n" |
1008 | | "quoted-NULL: \"NULL\"\n" |
1009 | | "unquoted-NULL: NULL\n" |
1010 | | "empty-quoted: \"\"\n" |
1011 | | "empty-unquoted: \n" |
1012 | | "list: [\"null\", null, \"Null\", Null, \"NULL\", NULL, \"~\", ~]\n"; |
1013 | | FAIL_IF(ConfYamlLoadString(config, strlen(config)) != 0); |
1014 | | |
1015 | | const char *val; |
1016 | | |
1017 | | FAIL_IF_NOT(ConfGet("quoted-tilde", &val)); |
1018 | | FAIL_IF_NULL(val); |
1019 | | FAIL_IF_NOT(ConfGet("unquoted-tilde", &val)); |
1020 | | FAIL_IF_NOT_NULL(val); |
1021 | | |
1022 | | FAIL_IF_NOT(ConfGet("quoted-null", &val)); |
1023 | | FAIL_IF_NULL(val); |
1024 | | FAIL_IF_NOT(ConfGet("unquoted-null", &val)); |
1025 | | FAIL_IF_NOT_NULL(val); |
1026 | | |
1027 | | FAIL_IF_NOT(ConfGet("quoted-Null", &val)); |
1028 | | FAIL_IF_NULL(val); |
1029 | | FAIL_IF_NOT(ConfGet("unquoted-Null", &val)); |
1030 | | FAIL_IF_NOT_NULL(val); |
1031 | | |
1032 | | FAIL_IF_NOT(ConfGet("quoted-NULL", &val)); |
1033 | | FAIL_IF_NULL(val); |
1034 | | FAIL_IF_NOT(ConfGet("unquoted-NULL", &val)); |
1035 | | FAIL_IF_NOT_NULL(val); |
1036 | | |
1037 | | FAIL_IF_NOT(ConfGet("empty-quoted", &val)); |
1038 | | FAIL_IF_NULL(val); |
1039 | | FAIL_IF_NOT(ConfGet("empty-unquoted", &val)); |
1040 | | FAIL_IF_NOT_NULL(val); |
1041 | | |
1042 | | FAIL_IF_NOT(ConfGet("list.0", &val)); |
1043 | | FAIL_IF_NULL(val); |
1044 | | FAIL_IF_NOT(ConfGet("list.1", &val)); |
1045 | | FAIL_IF_NOT_NULL(val); |
1046 | | |
1047 | | FAIL_IF_NOT(ConfGet("list.2", &val)); |
1048 | | FAIL_IF_NULL(val); |
1049 | | FAIL_IF_NOT(ConfGet("list.3", &val)); |
1050 | | FAIL_IF_NOT_NULL(val); |
1051 | | |
1052 | | FAIL_IF_NOT(ConfGet("list.4", &val)); |
1053 | | FAIL_IF_NULL(val); |
1054 | | FAIL_IF_NOT(ConfGet("list.5", &val)); |
1055 | | FAIL_IF_NOT_NULL(val); |
1056 | | |
1057 | | FAIL_IF_NOT(ConfGet("list.6", &val)); |
1058 | | FAIL_IF_NULL(val); |
1059 | | FAIL_IF_NOT(ConfGet("list.7", &val)); |
1060 | | FAIL_IF_NOT_NULL(val); |
1061 | | |
1062 | | ConfDeInit(); |
1063 | | ConfRestoreContextBackup(); |
1064 | | |
1065 | | PASS; |
1066 | | } |
1067 | | |
1068 | | #endif /* UNITTESTS */ |
1069 | | |
1070 | | void |
1071 | | ConfYamlRegisterTests(void) |
1072 | 0 | { |
1073 | | #ifdef UNITTESTS |
1074 | | UtRegisterTest("ConfYamlSequenceTest", ConfYamlSequenceTest); |
1075 | | UtRegisterTest("ConfYamlLoggingOutputTest", ConfYamlLoggingOutputTest); |
1076 | | UtRegisterTest("ConfYamlNonYamlFileTest", ConfYamlNonYamlFileTest); |
1077 | | UtRegisterTest("ConfYamlBadYamlVersionTest", ConfYamlBadYamlVersionTest); |
1078 | | UtRegisterTest("ConfYamlSecondLevelSequenceTest", |
1079 | | ConfYamlSecondLevelSequenceTest); |
1080 | | UtRegisterTest("ConfYamlFileIncludeTest", ConfYamlFileIncludeTest); |
1081 | | UtRegisterTest("ConfYamlOverrideTest", ConfYamlOverrideTest); |
1082 | | UtRegisterTest("ConfYamlOverrideFinalTest", ConfYamlOverrideFinalTest); |
1083 | | UtRegisterTest("ConfYamlNull", ConfYamlNull); |
1084 | | #endif /* UNITTESTS */ |
1085 | 0 | } |