suricata
conf-yaml-loader.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2010 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 #define YAML_VERSION_MAJOR 1
35 #define YAML_VERSION_MINOR 1
36 
37 /* The maximum level of recursion allowed while parsing the YAML
38  * file. */
39 #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 #define DEFAULT_NAME_LEN 16
45 
46 #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);
52 
53 /* Configuration processing states. */
54 enum conf_state {
55  CONF_KEY = 0,
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 {
70  char *c;
71 
72  while ((c = strchr(string, '_')))
73  *c = '-';
74 
75  return;
76 }
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 {
86  char *ep;
87 
88  ep = strrchr(filename, '\\');
89  if (ep == NULL)
90  ep = strrchr(filename, '/');
91 
92  if (ep == NULL) {
93  conf_dirname = SCStrdup(".");
94  if (conf_dirname == NULL) {
96  "ERROR: Failed to allocate memory while loading configuration.");
97  }
98  }
99  else {
100  conf_dirname = SCStrdup(filename);
101  if (conf_dirname == NULL) {
103  "ERROR: Failed to allocate memory while loading configuration.");
104  }
105  conf_dirname[ep - filename] = '\0';
106  }
107 }
108 
109 /**
110  * \brief Include a file in the configuration.
111  *
112  * \param parent The configuration node the included configuration will be
113  * placed at.
114  * \param filename The filename to include.
115  *
116  * \retval 0 on success, -1 on failure.
117  */
118 static int
119 ConfYamlHandleInclude(ConfNode *parent, const char *filename)
120 {
121  yaml_parser_t parser;
122  char include_filename[PATH_MAX];
123  FILE *file = NULL;
124  int ret = -1;
125 
126  if (yaml_parser_initialize(&parser) != 1) {
127  SCLogError(SC_ERR_CONF_YAML_ERROR, "Failed to initialize YAML parser");
128  return -1;
129  }
130 
131  if (PathIsAbsolute(filename)) {
132  strlcpy(include_filename, filename, sizeof(include_filename));
133  }
134  else {
135  snprintf(include_filename, sizeof(include_filename), "%s/%s",
136  conf_dirname, filename);
137  }
138 
139  file = fopen(include_filename, "r");
140  if (file == NULL) {
142  "Failed to open configuration include file %s: %s",
143  include_filename, strerror(errno));
144  goto done;
145  }
146 
147  yaml_parser_set_input_file(&parser, file);
148 
149  if (ConfYamlParse(&parser, parent, 0, 0) != 0) {
151  "Failed to include configuration file %s", filename);
152  goto done;
153  }
154 
155  ret = 0;
156 
157 done:
158  yaml_parser_delete(&parser);
159  if (file != NULL) {
160  fclose(file);
161  }
162 
163  return ret;
164 }
165 
166 /**
167  * \brief Parse a YAML layer.
168  *
169  * \param parser A pointer to an active yaml_parser_t.
170  * \param parent The parent configuration node.
171  *
172  * \retval 0 on success, -1 on failure.
173  */
174 static int
175 ConfYamlParse(yaml_parser_t *parser, ConfNode *parent, int inseq, int rlevel)
176 {
177  ConfNode *node = parent;
178  yaml_event_t event;
179  memset(&event, 0, sizeof(event));
180  int done = 0;
181  int state = 0;
182  int seq_idx = 0;
183  int retval = 0;
184 
185  if (rlevel++ > RECURSION_LIMIT) {
186  SCLogError(SC_ERR_CONF_YAML_ERROR, "Recursion limit reached while parsing "
187  "configuration file, aborting.");
188  return -1;
189  }
190 
191  while (!done) {
192  if (!yaml_parser_parse(parser, &event)) {
194  "Failed to parse configuration file at line %" PRIuMAX ": %s\n",
195  (uintmax_t)parser->problem_mark.line, parser->problem);
196  retval = -1;
197  break;
198  }
199 
200  if (event.type == YAML_DOCUMENT_START_EVENT) {
201  SCLogDebug("event.type=YAML_DOCUMENT_START_EVENT; state=%d", state);
202  /* Verify YAML version - its more likely to be a valid
203  * Suricata configuration file if the version is
204  * correct. */
205  yaml_version_directive_t *ver =
206  event.data.document_start.version_directive;
207  if (ver == NULL) {
208  SCLogError(SC_ERR_CONF_YAML_ERROR, "ERROR: Invalid configuration file.");
210  "The configuration file must begin with the following two lines: %%YAML 1.1 and ---");
211  goto fail;
212  }
213  int major = ver->major;
214  int minor = ver->minor;
215  if (!(major == YAML_VERSION_MAJOR && minor == YAML_VERSION_MINOR)) {
216  SCLogError(SC_ERR_CONF_YAML_ERROR, "ERROR: Invalid YAML version. Must be 1.1");
217  goto fail;
218  }
219  }
220  else if (event.type == YAML_SCALAR_EVENT) {
221  char *value = (char *)event.data.scalar.value;
222  char *tag = (char *)event.data.scalar.tag;
223  SCLogDebug("event.type=YAML_SCALAR_EVENT; state=%d; value=%s; "
224  "tag=%s; inseq=%d", state, value, tag, inseq);
225 
226  /* Skip over empty scalar values while in KEY state. This
227  * tends to only happen on an empty file, where a scalar
228  * event probably shouldn't fire anyways. */
229  if (state == CONF_KEY && strlen(value) == 0) {
230  goto next;
231  }
232 
233  if (inseq) {
234  char sequence_node_name[DEFAULT_NAME_LEN];
235  snprintf(sequence_node_name, DEFAULT_NAME_LEN, "%d", seq_idx++);
236  ConfNode *seq_node = ConfNodeLookupChild(parent,
237  sequence_node_name);
238  if (seq_node != NULL) {
239  /* The sequence node has already been set, probably
240  * from the command line. Remove it so it gets
241  * re-added in the expected order for iteration.
242  */
243  TAILQ_REMOVE(&parent->head, seq_node, next);
244  }
245  else {
246  seq_node = ConfNodeNew();
247  if (unlikely(seq_node == NULL)) {
248  goto fail;
249  }
250  seq_node->name = SCStrdup(sequence_node_name);
251  if (unlikely(seq_node->name == NULL)) {
252  SCFree(seq_node);
253  goto fail;
254  }
255  seq_node->val = SCStrdup(value);
256  if (unlikely(seq_node->val == NULL)) {
257  SCFree(seq_node->name);
258  goto fail;
259  }
260  }
261  TAILQ_INSERT_TAIL(&parent->head, seq_node, next);
262  }
263  else {
264  if (state == CONF_INCLUDE) {
265  SCLogInfo("Including configuration file %s.", value);
266  if (ConfYamlHandleInclude(parent, value) != 0) {
267  goto fail;
268  }
269  state = CONF_KEY;
270  }
271  else if (state == CONF_KEY) {
272 
273  if (strcmp(value, "include") == 0) {
274  state = CONF_INCLUDE;
275  goto next;
276  }
277 
278  if (parent->is_seq) {
279  if (parent->val == NULL) {
280  parent->val = SCStrdup(value);
281  if (parent->val && strchr(parent->val, '_'))
282  Mangle(parent->val);
283  }
284  }
285  ConfNode *existing = ConfNodeLookupChild(parent, value);
286  if (existing != NULL) {
287  if (!existing->final) {
288  SCLogInfo("Configuration node '%s' redefined.",
289  existing->name);
290  ConfNodePrune(existing);
291  }
292  node = existing;
293  }
294  else {
295  node = ConfNodeNew();
296  node->name = SCStrdup(value);
297  if (node->name && strchr(node->name, '_')) {
298  if (!(parent->name &&
299  ((strcmp(parent->name, "address-groups") == 0) ||
300  (strcmp(parent->name, "port-groups") == 0)))) {
301  Mangle(node->name);
302  if (mangle_errors < MANGLE_ERRORS_MAX) {
304  "%s is deprecated. Please use %s on line %"PRIuMAX".",
305  value, node->name, (uintmax_t)parser->mark.line+1);
306  mangle_errors++;
307  if (mangle_errors >= MANGLE_ERRORS_MAX)
308  SCLogWarning(SC_WARN_DEPRECATED, "not showing more "
309  "parameter name warnings.");
310  }
311  }
312  }
313  TAILQ_INSERT_TAIL(&parent->head, node, next);
314  }
315  state = CONF_VAL;
316  }
317  else {
318  if ((tag != NULL) && (strcmp(tag, "!include") == 0)) {
319  SCLogInfo("Including configuration file %s at "
320  "parent node %s.", value, node->name);
321  if (ConfYamlHandleInclude(node, value) != 0)
322  goto fail;
323  }
324  else if (!node->final) {
325  if (node->val != NULL)
326  SCFree(node->val);
327  node->val = SCStrdup(value);
328  }
329  state = CONF_KEY;
330  }
331  }
332  }
333  else if (event.type == YAML_SEQUENCE_START_EVENT) {
334  SCLogDebug("event.type=YAML_SEQUENCE_START_EVENT; state=%d", state);
335  if (ConfYamlParse(parser, node, 1, rlevel) != 0)
336  goto fail;
337  node->is_seq = 1;
338  state = CONF_KEY;
339  }
340  else if (event.type == YAML_SEQUENCE_END_EVENT) {
341  SCLogDebug("event.type=YAML_SEQUENCE_END_EVENT; state=%d", state);
342  done = 1;
343  }
344  else if (event.type == YAML_MAPPING_START_EVENT) {
345  SCLogDebug("event.type=YAML_MAPPING_START_EVENT; state=%d", state);
346  if (inseq) {
347  char sequence_node_name[DEFAULT_NAME_LEN];
348  snprintf(sequence_node_name, DEFAULT_NAME_LEN, "%d", seq_idx++);
349  ConfNode *seq_node = ConfNodeLookupChild(node,
350  sequence_node_name);
351  if (seq_node != NULL) {
352  /* The sequence node has already been set, probably
353  * from the command line. Remove it so it gets
354  * re-added in the expected order for iteration.
355  */
356  TAILQ_REMOVE(&node->head, seq_node, next);
357  }
358  else {
359  seq_node = ConfNodeNew();
360  if (unlikely(seq_node == NULL)) {
361  goto fail;
362  }
363  seq_node->name = SCStrdup(sequence_node_name);
364  if (unlikely(seq_node->name == NULL)) {
365  SCFree(seq_node);
366  goto fail;
367  }
368  }
369  seq_node->is_seq = 1;
370  TAILQ_INSERT_TAIL(&node->head, seq_node, next);
371  if (ConfYamlParse(parser, seq_node, 0, rlevel) != 0)
372  goto fail;
373  }
374  else {
375  if (ConfYamlParse(parser, node, inseq, rlevel) != 0)
376  goto fail;
377  }
378  state = CONF_KEY;
379  }
380  else if (event.type == YAML_MAPPING_END_EVENT) {
381  SCLogDebug("event.type=YAML_MAPPING_END_EVENT; state=%d", state);
382  done = 1;
383  }
384  else if (event.type == YAML_STREAM_END_EVENT) {
385  SCLogDebug("event.type=YAML_STREAM_END_EVENT; state=%d", state);
386  done = 1;
387  }
388 
389  next:
390  yaml_event_delete(&event);
391  continue;
392 
393  fail:
394  yaml_event_delete(&event);
395  retval = -1;
396  break;
397  }
398 
399  rlevel--;
400  return retval;
401 }
402 
403 /**
404  * \brief Load configuration from a YAML file.
405  *
406  * This function will load a configuration file. On failure -1 will
407  * be returned and it is suggested that the program then exit. Any
408  * errors while loading the configuration file will have already been
409  * logged.
410  *
411  * \param filename Filename of configuration file to load.
412  *
413  * \retval 0 on success, -1 on failure.
414  */
415 int
416 ConfYamlLoadFile(const char *filename)
417 {
418  FILE *infile;
419  yaml_parser_t parser;
420  int ret;
421  ConfNode *root = ConfGetRootNode();
422 
423  if (yaml_parser_initialize(&parser) != 1) {
424  SCLogError(SC_ERR_FATAL, "failed to initialize yaml parser.");
425  return -1;
426  }
427 
428  struct stat stat_buf;
429  if (stat(filename, &stat_buf) == 0) {
430  if (stat_buf.st_mode & S_IFDIR) {
431  SCLogError(SC_ERR_FATAL, "yaml argument is not a file but a directory: %s. "
432  "Please specify the yaml file in your -c option.", filename);
433  yaml_parser_delete(&parser);
434  return -1;
435  }
436  }
437 
438  // coverity[toctou : FALSE]
439  infile = fopen(filename, "r");
440  if (infile == NULL) {
441  SCLogError(SC_ERR_FATAL, "failed to open file: %s: %s", filename,
442  strerror(errno));
443  yaml_parser_delete(&parser);
444  return -1;
445  }
446 
447  if (conf_dirname == NULL) {
448  ConfYamlSetConfDirname(filename);
449  }
450 
451  yaml_parser_set_input_file(&parser, infile);
452  ret = ConfYamlParse(&parser, root, 0, 0);
453  yaml_parser_delete(&parser);
454  fclose(infile);
455 
456  return ret;
457 }
458 
459 /**
460  * \brief Load configuration from a YAML string.
461  */
462 int
463 ConfYamlLoadString(const char *string, size_t len)
464 {
465  ConfNode *root = ConfGetRootNode();
466  yaml_parser_t parser;
467  int ret;
468 
469  if (yaml_parser_initialize(&parser) != 1) {
470  fprintf(stderr, "Failed to initialize yaml parser.\n");
471  exit(EXIT_FAILURE);
472  }
473  yaml_parser_set_input_string(&parser, (const unsigned char *)string, len);
474  ret = ConfYamlParse(&parser, root, 0, 0);
475  yaml_parser_delete(&parser);
476 
477  return ret;
478 }
479 
480 /**
481  * \brief Load configuration from a YAML file, insert in tree at 'prefix'
482  *
483  * This function will load a configuration file and insert it into the
484  * config tree at 'prefix'. This means that if this is called with prefix
485  * "abc" and the file contains a parameter "def", it will be loaded as
486  * "abc.def".
487  *
488  * \param filename Filename of configuration file to load.
489  * \param prefix Name prefix to use.
490  *
491  * \retval 0 on success, -1 on failure.
492  */
493 int
494 ConfYamlLoadFileWithPrefix(const char *filename, const char *prefix)
495 {
496  FILE *infile;
497  yaml_parser_t parser;
498  int ret;
499  ConfNode *root = ConfGetNode(prefix);
500 
501  if (yaml_parser_initialize(&parser) != 1) {
502  SCLogError(SC_ERR_FATAL, "failed to initialize yaml parser.");
503  return -1;
504  }
505 
506  struct stat stat_buf;
507  /* coverity[toctou] */
508  if (stat(filename, &stat_buf) == 0) {
509  if (stat_buf.st_mode & S_IFDIR) {
510  SCLogError(SC_ERR_FATAL, "yaml argument is not a file but a directory: %s. "
511  "Please specify the yaml file in your -c option.", filename);
512  return -1;
513  }
514  }
515 
516  /* coverity[toctou] */
517  infile = fopen(filename, "r");
518  if (infile == NULL) {
519  SCLogError(SC_ERR_FATAL, "failed to open file: %s: %s", filename,
520  strerror(errno));
521  yaml_parser_delete(&parser);
522  return -1;
523  }
524 
525  if (conf_dirname == NULL) {
526  ConfYamlSetConfDirname(filename);
527  }
528 
529  if (root == NULL) {
530  /* if node at 'prefix' doesn't yet exist, add a place holder */
531  ConfSet(prefix, "<prefix root node>");
532  root = ConfGetNode(prefix);
533  if (root == NULL) {
534  fclose(infile);
535  yaml_parser_delete(&parser);
536  return -1;
537  }
538  }
539  yaml_parser_set_input_file(&parser, infile);
540  ret = ConfYamlParse(&parser, root, 0, 0);
541  yaml_parser_delete(&parser);
542  fclose(infile);
543 
544  return ret;
545 }
546 
547 #ifdef UNITTESTS
548 
549 static int
550 ConfYamlSequenceTest(void)
551 {
552  char input[] = "\
553 %YAML 1.1\n\
554 ---\n\
555 rule-files:\n\
556  - netbios.rules\n\
557  - x11.rules\n\
558 \n\
559 default-log-dir: /tmp\n\
560 ";
561 
563  ConfInit();
564 
565  ConfYamlLoadString(input, strlen(input));
566 
567  ConfNode *node;
568  node = ConfGetNode("rule-files");
569  if (node == NULL)
570  return 0;
571  if (!ConfNodeIsSequence(node))
572  return 0;
573  if (TAILQ_EMPTY(&node->head))
574  return 0;
575  int i = 0;
576  ConfNode *filename;
577  TAILQ_FOREACH(filename, &node->head, next) {
578  if (i == 0) {
579  if (strcmp(filename->val, "netbios.rules") != 0)
580  return 0;
581  if (ConfNodeIsSequence(filename))
582  return 0;
583  if (filename->is_seq != 0)
584  return 0;
585  }
586  else if (i == 1) {
587  if (strcmp(filename->val, "x11.rules") != 0)
588  return 0;
589  if (ConfNodeIsSequence(filename))
590  return 0;
591  }
592  else {
593  return 0;
594  }
595  i++;
596  }
597 
598  ConfDeInit();
600 
601  return 1;
602 }
603 
604 static int
605 ConfYamlLoggingOutputTest(void)
606 {
607  char input[] = "\
608 %YAML 1.1\n\
609 ---\n\
610 logging:\n\
611  output:\n\
612  - interface: console\n\
613  log-level: error\n\
614  - interface: syslog\n\
615  facility: local4\n\
616  log-level: info\n\
617 ";
618 
620  ConfInit();
621 
622  ConfYamlLoadString(input, strlen(input));
623 
624  ConfNode *outputs;
625  outputs = ConfGetNode("logging.output");
626  if (outputs == NULL)
627  return 0;
628 
629  ConfNode *output;
630  ConfNode *output_param;
631 
632  output = TAILQ_FIRST(&outputs->head);
633  if (output == NULL)
634  return 0;
635  if (strcmp(output->name, "0") != 0)
636  return 0;
637  output_param = TAILQ_FIRST(&output->head);
638  if (output_param == NULL)
639  return 0;
640  if (strcmp(output_param->name, "interface") != 0)
641  return 0;
642  if (strcmp(output_param->val, "console") != 0)
643  return 0;
644  output_param = TAILQ_NEXT(output_param, next);
645  if (strcmp(output_param->name, "log-level") != 0)
646  return 0;
647  if (strcmp(output_param->val, "error") != 0)
648  return 0;
649 
650  output = TAILQ_NEXT(output, next);
651  if (output == NULL)
652  return 0;
653  if (strcmp(output->name, "1") != 0)
654  return 0;
655  output_param = TAILQ_FIRST(&output->head);
656  if (output_param == NULL)
657  return 0;
658  if (strcmp(output_param->name, "interface") != 0)
659  return 0;
660  if (strcmp(output_param->val, "syslog") != 0)
661  return 0;
662  output_param = TAILQ_NEXT(output_param, next);
663  if (strcmp(output_param->name, "facility") != 0)
664  return 0;
665  if (strcmp(output_param->val, "local4") != 0)
666  return 0;
667  output_param = TAILQ_NEXT(output_param, next);
668  if (strcmp(output_param->name, "log-level") != 0)
669  return 0;
670  if (strcmp(output_param->val, "info") != 0)
671  return 0;
672 
673  ConfDeInit();
675 
676  return 1;
677 }
678 
679 /**
680  * Try to load something that is not a valid YAML file.
681  */
682 static int
683 ConfYamlNonYamlFileTest(void)
684 {
686  ConfInit();
687 
688  if (ConfYamlLoadFile("/etc/passwd") != -1)
689  return 0;
690 
691  ConfDeInit();
693 
694  return 1;
695 }
696 
697 static int
698 ConfYamlBadYamlVersionTest(void)
699 {
700  char input[] = "\
701 %YAML 9.9\n\
702 ---\n\
703 logging:\n\
704  output:\n\
705  - interface: console\n\
706  log-level: error\n\
707  - interface: syslog\n\
708  facility: local4\n\
709  log-level: info\n\
710 ";
711 
713  ConfInit();
714 
715  if (ConfYamlLoadString(input, strlen(input)) != -1)
716  return 0;
717 
718  ConfDeInit();
720 
721  return 1;
722 }
723 
724 static int
725 ConfYamlSecondLevelSequenceTest(void)
726 {
727  char input[] = "\
728 %YAML 1.1\n\
729 ---\n\
730 libhtp:\n\
731  server-config:\n\
732  - apache-php:\n\
733  address: [\"192.168.1.0/24\"]\n\
734  personality: [\"Apache_2_2\", \"PHP_5_3\"]\n\
735  path-parsing: [\"compress_separators\", \"lowercase\"]\n\
736  - iis-php:\n\
737  address:\n\
738  - 192.168.0.0/24\n\
739 \n\
740  personality:\n\
741  - IIS_7_0\n\
742  - PHP_5_3\n\
743 \n\
744  path-parsing:\n\
745  - compress_separators\n\
746 ";
747 
749  ConfInit();
750 
751  if (ConfYamlLoadString(input, strlen(input)) != 0)
752  return 0;
753 
754  ConfNode *outputs;
755  outputs = ConfGetNode("libhtp.server-config");
756  if (outputs == NULL)
757  return 0;
758 
759  ConfNode *node;
760 
761  node = TAILQ_FIRST(&outputs->head);
762  if (node == NULL)
763  return 0;
764  if (strcmp(node->name, "0") != 0)
765  return 0;
766  node = TAILQ_FIRST(&node->head);
767  if (node == NULL)
768  return 0;
769  if (strcmp(node->name, "apache-php") != 0)
770  return 0;
771 
772  node = ConfNodeLookupChild(node, "address");
773  if (node == NULL)
774  return 0;
775  node = TAILQ_FIRST(&node->head);
776  if (node == NULL)
777  return 0;
778  if (strcmp(node->name, "0") != 0)
779  return 0;
780  if (strcmp(node->val, "192.168.1.0/24") != 0)
781  return 0;
782 
783  ConfDeInit();
785 
786  return 1;
787 }
788 
789 /**
790  * Test file inclusion support.
791  */
792 static int
793 ConfYamlFileIncludeTest(void)
794 {
795  int ret = 0;
796  FILE *config_file;
797 
798  const char config_filename[] = "ConfYamlFileIncludeTest-config.yaml";
799  const char config_file_contents[] =
800  "%YAML 1.1\n"
801  "---\n"
802  "# Include something at the root level.\n"
803  "include: ConfYamlFileIncludeTest-include.yaml\n"
804  "# Test including under a mapping.\n"
805  "mapping: !include ConfYamlFileIncludeTest-include.yaml\n";
806 
807  const char include_filename[] = "ConfYamlFileIncludeTest-include.yaml";
808  const char include_file_contents[] =
809  "%YAML 1.1\n"
810  "---\n"
811  "host-mode: auto\n"
812  "unix-command:\n"
813  " enabled: no\n";
814 
816  ConfInit();
817 
818  /* Write out the test files. */
819  if ((config_file = fopen(config_filename, "w")) == NULL) {
820  goto cleanup;
821  }
822  if (fwrite(config_file_contents, strlen(config_file_contents), 1,
823  config_file) != 1) {
824  goto cleanup;
825  }
826  fclose(config_file);
827  if ((config_file = fopen(include_filename, "w")) == NULL) {
828  goto cleanup;
829  }
830  if (fwrite(include_file_contents, strlen(include_file_contents), 1,
831  config_file) != 1) {
832  goto cleanup;
833  }
834  fclose(config_file);
835 
836  /* Reset conf_dirname. */
837  if (conf_dirname != NULL) {
838  SCFree(conf_dirname);
839  conf_dirname = NULL;
840  }
841 
842  if (ConfYamlLoadFile("ConfYamlFileIncludeTest-config.yaml") != 0)
843  goto cleanup;
844 
845  /* Check values that should have been loaded into the root of the
846  * configuration. */
847  ConfNode *node;
848  node = ConfGetNode("host-mode");
849  if (node == NULL)
850  goto cleanup;
851  if (strcmp(node->val, "auto") != 0)
852  goto cleanup;
853  node = ConfGetNode("unix-command.enabled");
854  if (node == NULL)
855  goto cleanup;
856  if (strcmp(node->val, "no") != 0)
857  goto cleanup;
858 
859  /* Check for values that were included under a mapping. */
860  node = ConfGetNode("mapping.host-mode");
861  if (node == NULL)
862  goto cleanup;
863  if (strcmp(node->val, "auto") != 0)
864  goto cleanup;
865  node = ConfGetNode("mapping.unix-command.enabled");
866  if (node == NULL)
867  goto cleanup;
868  if (strcmp(node->val, "no") != 0)
869  goto cleanup;
870 
871  ConfDeInit();
873 
874  ret = 1;
875 
876 cleanup:
877  unlink(config_filename);
878  unlink(include_filename);
879 
880  return ret;
881 }
882 
883 /**
884  * Test that a configuration section is overridden but subsequent
885  * occurrences.
886  */
887 static int
888 ConfYamlOverrideTest(void)
889 {
890  char config[] =
891  "%YAML 1.1\n"
892  "---\n"
893  "some-log-dir: /var/log\n"
894  "some-log-dir: /tmp\n"
895  "\n"
896  "parent:\n"
897  " child0:\n"
898  " key: value\n"
899  "parent:\n"
900  " child1:\n"
901  " key: value\n"
902  ;
903  const char *value;
904 
906  ConfInit();
907 
908  if (ConfYamlLoadString(config, strlen(config)) != 0)
909  return 0;
910  if (!ConfGet("some-log-dir", &value))
911  return 0;
912  if (strcmp(value, "/tmp") != 0)
913  return 0;
914 
915  /* Test that parent.child0 does not exist, but child1 does. */
916  if (ConfGetNode("parent.child0") != NULL)
917  return 0;
918  if (!ConfGet("parent.child1.key", &value))
919  return 0;
920  if (strcmp(value, "value") != 0)
921  return 0;
922 
923  ConfDeInit();
925 
926  return 1;
927 }
928 
929 /**
930  * Test that a configuration parameter loaded from YAML doesn't
931  * override a 'final' value that may be set on the command line.
932  */
933 static int
934 ConfYamlOverrideFinalTest(void)
935 {
937  ConfInit();
938 
939  char config[] =
940  "%YAML 1.1\n"
941  "---\n"
942  "default-log-dir: /var/log\n";
943 
944  /* Set the log directory as if it was set on the command line. */
945  if (!ConfSetFinal("default-log-dir", "/tmp"))
946  return 0;
947  if (ConfYamlLoadString(config, strlen(config)) != 0)
948  return 0;
949 
950  const char *default_log_dir;
951 
952  if (!ConfGet("default-log-dir", &default_log_dir))
953  return 0;
954  if (strcmp(default_log_dir, "/tmp") != 0) {
955  fprintf(stderr, "final value was reassigned\n");
956  return 0;
957  }
958 
959  ConfDeInit();
961 
962  return 1;
963 }
964 
965 #endif /* UNITTESTS */
966 
967 void
969 {
970 #ifdef UNITTESTS
971  UtRegisterTest("ConfYamlSequenceTest", ConfYamlSequenceTest);
972  UtRegisterTest("ConfYamlLoggingOutputTest", ConfYamlLoggingOutputTest);
973  UtRegisterTest("ConfYamlNonYamlFileTest", ConfYamlNonYamlFileTest);
974  UtRegisterTest("ConfYamlBadYamlVersionTest", ConfYamlBadYamlVersionTest);
975  UtRegisterTest("ConfYamlSecondLevelSequenceTest",
976  ConfYamlSecondLevelSequenceTest);
977  UtRegisterTest("ConfYamlFileIncludeTest", ConfYamlFileIncludeTest);
978  UtRegisterTest("ConfYamlOverrideTest", ConfYamlOverrideTest);
979  UtRegisterTest("ConfYamlOverrideFinalTest", ConfYamlOverrideFinalTest);
980 #endif /* UNITTESTS */
981 }
len
uint8_t len
Definition: app-layer-dnp3.h:2
ConfNode_::val
char * val
Definition: conf.h:34
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:103
YAML_VERSION_MAJOR
#define YAML_VERSION_MAJOR
Definition: conf-yaml-loader.c:34
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:298
next
struct HtpBodyChunk_ * next
Definition: app-layer-htp.h:0
ConfGetNode
ConfNode * ConfGetNode(const char *name)
Get a ConfNode by name.
Definition: conf.c:175
ConfNodeNew
ConfNode * ConfNodeNew(void)
Allocate a new configuration node.
Definition: conf.c:133
ConfYamlLoadFileWithPrefix
int ConfYamlLoadFileWithPrefix(const char *filename, const char *prefix)
Load configuration from a YAML file, insert in tree at 'prefix'.
Definition: conf-yaml-loader.c:494
TAILQ_EMPTY
#define TAILQ_EMPTY(head)
Definition: queue.h:347
TAILQ_FOREACH
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:350
ConfSetFinal
int ConfSetFinal(const char *name, const char *val)
Set a final configuration value.
Definition: conf.c:298
TAILQ_INSERT_TAIL
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:385
YAML_VERSION_MINOR
#define YAML_VERSION_MINOR
Definition: conf-yaml-loader.c:35
conf_state
conf_state
Definition: conf-yaml-loader.c:54
util-unittest.h
ConfNode_::is_seq
int is_seq
Definition: conf.h:36
strlcpy
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: util-strlcpyu.c:43
ConfGet
int ConfGet(const char *name, const char **vptr)
Retrieve the value of a configuration node.
Definition: conf.c:330
ConfGetRootNode
ConfNode * ConfGetRootNode(void)
Get the root configuration node.
Definition: conf.c:202
TAILQ_REMOVE
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:412
util-debug.h
TAILQ_FIRST
#define TAILQ_FIRST(head)
Definition: queue.h:339
CONF_KEY
@ CONF_KEY
Definition: conf-yaml-loader.c:55
ConfNodePrune
void ConfNodePrune(ConfNode *node)
Prune a configuration node.
Definition: conf.c:942
SC_ERR_CONF_YAML_ERROR
@ SC_ERR_CONF_YAML_ERROR
Definition: util-error.h:274
ConfYamlLoadFile
int ConfYamlLoadFile(const char *filename)
Load configuration from a YAML file.
Definition: conf-yaml-loader.c:416
RECURSION_LIMIT
#define RECURSION_LIMIT
Definition: conf-yaml-loader.c:39
ConfNode_::final
int final
Definition: conf.h:39
ConfYamlLoadString
int ConfYamlLoadString(const char *string, size_t len)
Load configuration from a YAML string.
Definition: conf-yaml-loader.c:463
SC_WARN_DEPRECATED
@ SC_WARN_DEPRECATED
Definition: util-error.h:235
SC_ERR_FOPEN
@ SC_ERR_FOPEN
Definition: util-error.h:74
conf-yaml-loader.h
conf.h
ConfCreateContextBackup
void ConfCreateContextBackup(void)
Creates a backup of the conf_hash hash_table used by the conf API.
Definition: conf.c:698
MANGLE_ERRORS_MAX
#define MANGLE_ERRORS_MAX
Definition: conf-yaml-loader.c:46
SCLogInfo
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition: util-debug.h:217
ConfNodeLookupChild
ConfNode * ConfNodeLookupChild(const ConfNode *node, const char *name)
Lookup a child configuration node by name.
Definition: conf.c:814
ConfYamlRegisterTests
void ConfYamlRegisterTests(void)
Definition: conf-yaml-loader.c:968
suricata-common.h
util-path.h
ConfNode_::name
char * name
Definition: conf.h:33
TAILQ_NEXT
#define TAILQ_NEXT(elm, field)
Definition: queue.h:341
PathIsAbsolute
int PathIsAbsolute(const char *path)
Check if a path is absolute.
Definition: util-path.c:45
SCLogError
#define SCLogError(err_code,...)
Macro used to log ERROR messages.
Definition: util-debug.h:257
ConfNodeIsSequence
int ConfNodeIsSequence(const ConfNode *node)
Check if a node is a sequence or node.
Definition: conf.c:974
ConfRestoreContextBackup
void ConfRestoreContextBackup(void)
Restores the backup of the hash_table present in backup_conf_hash back to conf_hash.
Definition: conf.c:710
SCStrdup
#define SCStrdup(s)
Definition: util-mem.h:56
FatalError
#define FatalError(x,...)
Definition: util-debug.h:532
ConfInit
void ConfInit(void)
Initialize the configuration system.
Definition: conf.c:113
SCLogWarning
#define SCLogWarning(err_code,...)
Macro used to log WARNING messages.
Definition: util-debug.h:244
SCFree
#define SCFree(p)
Definition: util-mem.h:61
ConfNode_
Definition: conf.h:32
SC_ERR_FATAL
@ SC_ERR_FATAL
Definition: util-error.h:203
ConfDeInit
void ConfDeInit(void)
De-initializes the configuration system.
Definition: conf.c:721
ConfSet
int ConfSet(const char *name, const char *val)
Set a configuration value.
Definition: conf.c:219
CONF_VAL
@ CONF_VAL
Definition: conf-yaml-loader.c:56
DEFAULT_NAME_LEN
#define DEFAULT_NAME_LEN
Definition: conf-yaml-loader.c:44
CONF_INCLUDE
@ CONF_INCLUDE
Definition: conf-yaml-loader.c:57