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