diff --git a/config.h.in b/config.h.in index b5656c14e7..83df21570d 100644 --- a/config.h.in +++ b/config.h.in @@ -34,6 +34,9 @@ /* Define to 1 if you have the `pthread' library (-lpthread). */ #undef HAVE_LIBPTHREAD +/* Define to 1 if you have the `yaml' library (-lyaml). */ +#undef HAVE_LIBYAML + /* Define to 1 if you have the header file. */ #undef HAVE_LIMITS_H diff --git a/configure.in b/configure.in index d2f710d5f1..4c803f2007 100644 --- a/configure.in +++ b/configure.in @@ -85,6 +85,36 @@ AC_INIT(configure.in) exit 1 fi +#libyaml + AC_ARG_WITH(libyaml_includes, + [ --with-libyaml-includes=DIR libyaml include directory], + [with_libyaml_includes="$withval"],[with_libyaml_includes=no]) + AC_ARG_WITH(libyaml_libraries, + [ --with-libyaml-libraries=DIR libyaml library directory], + [with_libyaml_libraries="$withval"],[with_libyaml_libraries="no"]) + + if test "$with_libyaml_includes" != "no"; then + CPPFLAGS="${CPPFLAGS} -I${with_libyaml_includes}" + fi + + AC_CHECK_HEADER(yaml.h,,LIBYAML="no") + + if test "$with_libyaml_libraries" != "no"; then + LDFLAGS="${LDFLAGS} -L${with_libyaml_libraries}" + fi + + LIBYAML="" + AC_CHECK_LIB(yaml,yaml_parser_initialize,,LIBYAML="no") + + if test "$LIBYAML" = "no"; then + echo + echo " ERROR! libyaml library not found, go get it" + echo " from http://pyyaml.org/wiki/LibYAML." + echo " or check your package manager." + echo + exit 1 + fi + #libpthread AC_ARG_WITH(libpthread_includes, [ --with-libpthread-includes=DIR libpthread include directory], diff --git a/oisf.yaml b/oisf.yaml new file mode 100644 index 0000000000..761bcc7dda --- /dev/null +++ b/oisf.yaml @@ -0,0 +1,47 @@ +# The default logging directory. Any log or output file will be +# placed here if its not specified with a full path name. This can be +# overridden with the -l command line parameter. +default-log-dir: /var/log/eidps + +# Logging configuration. This is not about logging IDS alerts, but +# IDS output about what its doing, errors, etc. +logging: + + # The default log level, can be overridden in an output section. + default-log-level: debug + + # The default output format. Optional parameter, should default to + # something reasonable if not provided. Can be overriden in an + # output section. + default-format: "<%t> - <%l>" + + # Default startup message. Optional parameter, should default to + # something reasonable if not provided. Can be overridden in an + # output section. + default-startup-message: Your IDS has started. + + # A regex to filter output. Can be overridden in an output section. + # Defaults to empty (no filter). + default-output-filter: + + # Configure the outputs. If no outputs are specified the engine + # will log to the console with an error log level. + output: + + # Enable logging to the console. Be a little more verbose than + # default, log info and more critical. + - interface: console + log-level: info + + # Log to a file as well. No log level specified so level will be + # set to the default-log-level. + - interface: file + filename: /var/log/eidps.log + + # Log to syslog with facility local5. Again, no level specified so + # will level will be set to default-log-level. We also override the + # format as we don't want to log a timestamp, syslog will do that + # for us. + - interface: syslog + facility: local5 + format: "%l" diff --git a/src/Makefile.am b/src/Makefile.am index 1f89ef7906..f7de22519f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -103,7 +103,8 @@ app-layer-parser.c app-layer-parser.h \ app-layer-http.c app-layer-http.h \ app-layer-tls.c app-layer-tls.h \ app-layer-protos.h \ -conf.c conf.h +conf.c conf.h \ +conf-yaml-loader.c conf-yaml-loader.h # set the include path found by configure INCLUDES= $(all_includes) diff --git a/src/conf-yaml-loader.c b/src/conf-yaml-loader.c new file mode 100644 index 0000000000..18a89c87be --- /dev/null +++ b/src/conf-yaml-loader.c @@ -0,0 +1,206 @@ +/* Copyright (c) 2009 Open Information Security Foundation */ + +/** + * \file + * + * \author Endace Technology Limited - Jason Ish + * + * YAML configuration loader. + */ + +#include +#include +#include +#include + +#include + +#include "conf.h" + +#undef YAML_LOADER_DEBUG +#ifdef YAML_LOADER_DEBUG +#define DPRINTF(x) do { printf x ; } while (0) +#else +#define DPRINTF(x) +#endif /* YAML_LOADER_DEBUG */ + +/* Define to print the current YAML state. */ +#undef PRINT_STATES +#ifdef PRINT_STATES +#define DPRINT_STATE(x) do { printf x ; } while (0) +#else +#define DPRINT_STATE(x) +#endif /* PRINT_STATES */ + +/* Defines the maximum number of levels YAML may nest. This is + * primarily used for construction of lookup-keys for configuration + * values. */ +#define MAX_LEVELS 16 + +/* Configuration processing states. */ +enum conf_state { + CONF_KEY = 0, + CONF_VAL, +}; + +/** + * \brief Return the name of the current configuration key value. + * + * This function returns the current value of the configuration key. + * This is all the key components joined together with a ".". + * + * NOTE: This function is not re-entrant safe, but we do not expect to + * be loading configuration files concurrently. + */ +static char * +GetKeyName(char **key, int level) +{ + /* Statically allocate a string that should be large enough. */ + static char print_key[1024]; + int i; + + print_key[0] = '\0'; + + for (i = 0; i <= level; i++) { + if (key[i] == NULL) + break; + if (strlen(key[i]) + strlen(print_key) + 2 > sizeof(print_key)) { + /* Overflow. */ + return NULL; + } + else { + strncat(print_key, key[i], strlen(key[i])); + if (i < level) + strncat(print_key, ".", 1); + } + } + + return print_key; +} + +/** + * \brief Load a configuration file. + * + * Loads the IDS configuration file. On failure, the program will + * exist with an error message. + * + * \param filename Name of the filename to load. + */ +void +LoadYamlConf(const char *filename) +{ + FILE *conf_file; + yaml_parser_t parser; + yaml_event_t event; + int done; + int level; + int state; + int inseq; + char *key[MAX_LEVELS]; + + memset(key, 0, sizeof(key)); + + if (yaml_parser_initialize(&parser) != 1) { + fprintf(stderr, "Failed to initialize yaml parser.\n"); + exit(EXIT_FAILURE); + } + + conf_file = fopen(filename, "r"); + if (conf_file == NULL) { + fprintf(stderr, "Failed to open file: %s: %s\n", filename, + strerror(errno)); + exit(EXIT_FAILURE); + } + yaml_parser_set_input_file(&parser, conf_file); + + state = CONF_KEY; + done = 0; + level = -1; + inseq = 0; + while (!done) { + if (!yaml_parser_parse(&parser, &event)) { + fprintf(stderr, "Failed to parse configuration file: %s\n", + parser.problem); + exit(EXIT_FAILURE); + } + if (level > -1) { + DPRINTF(("Current key: %s\n", GetKeyName(key, level))); + } + switch (event.type) { + case YAML_STREAM_START_EVENT: + DPRINT_STATE(("YAML_STREAM_START_EVENT\n")); + break; + case YAML_STREAM_END_EVENT: + DPRINT_STATE(("YAML_STREAM_END_EVENT\n")); + done = 1; + break; + case YAML_DOCUMENT_START_EVENT: + DPRINT_STATE(("YAML_STREAM_END_EVENT\n")); + /* Ignored. */ + break; + case YAML_DOCUMENT_END_EVENT: + DPRINT_STATE(("YAML_DOCUMENT_END_EVENT\n")); + /* Ignored. */ + break; + case YAML_SEQUENCE_START_EVENT: + DPRINT_STATE(("YAML_SEQUENCE_START_EVENT\n")); + inseq = 1; + break; + case YAML_SEQUENCE_END_EVENT: + DPRINT_STATE(("YAML_SEQUENCE_END_EVENT\n")); + inseq = 0; + break; + case YAML_MAPPING_START_EVENT: + DPRINT_STATE(("YAML_MAPPING_START_EVENT\n")); + level++; + if (level == MAX_LEVELS) { + fprintf(stderr, "Reached maximum configuration nesting level.\n"); + exit(EXIT_FAILURE); + } + + /* Since we are entering a new mapping, state goes back to key. */ + state = CONF_KEY; + + break; + case YAML_MAPPING_END_EVENT: + DPRINT_STATE(("YAML_MAPPING_END_EVENT\n")); + if (level > -1) { + free(key[level]); + key[level] = NULL; + } + level--; + break; + case YAML_SCALAR_EVENT: + DPRINT_STATE(("YAML_SCALAR_EVENT\n")); + if (inseq) { + printf("Ignoring sequence value for %s\n", + GetKeyName(key, level)); + break; + } + if (state == CONF_KEY) { + if (key[level] != NULL) + free(key[level]); + key[level] = strdup((char *)event.data.scalar.value); + + /* Move state to expecting a value. */ + state = CONF_VAL; + } + else if (state == CONF_VAL) { + ConfSet(GetKeyName(key, level), (char *)event.data.scalar.value, + 1); + state = CONF_KEY; + } + break; + case YAML_ALIAS_EVENT: + DPRINT_STATE(("YAML_ALIAS_EVENT\n")); + break; + case YAML_NO_EVENT: + DPRINT_STATE(("YAML_NO_EVENT\n")); + break; + } + yaml_event_delete(&event); + } + + yaml_parser_delete(&parser); + fclose(conf_file); +} diff --git a/src/conf-yaml-loader.h b/src/conf-yaml-loader.h new file mode 100644 index 0000000000..245b0fbce2 --- /dev/null +++ b/src/conf-yaml-loader.h @@ -0,0 +1,8 @@ +/* Copyright (c) 2009 Open Information Security Foundation */ + +#ifndef __CONF_YAML_LOADER_H__ +#define __CONF_YAML_LOADER_H__ + +void LoadYamlConf(const char *); + +#endif /* !__CONF_YAML_LOADER_H__ */ diff --git a/src/eidps.c b/src/eidps.c index 5a32d89011..ccfcaa0262 100644 --- a/src/eidps.c +++ b/src/eidps.c @@ -73,6 +73,7 @@ #include "util-time.h" #include "conf.h" +#include "conf-yaml-loader.h" /* * we put this here, because we only use it here in main. @@ -1378,6 +1379,7 @@ int RunModeFilePcap2(DetectEngineCtx *de_ctx, char *file) { void usage(const char *progname) { printf("USAGE: %s\n\n", progname); + printf("\t-c : path to configuration file\n"); printf("\t-i : run in pcap live mode\n"); printf("\t-r : run in pcap file/offline mode\n"); printf("\t-q : run in inline nfqueue mode\n"); @@ -1397,6 +1399,8 @@ int main(int argc, char **argv) char *pcap_dev = NULL; char *sig_file = NULL; int nfq_id = 0; + char *conf_filename = NULL; + int dump_config = 0; /* registering signals we use */ SignalHandlerSetup(SIGINT, SignalHandlerSigint); @@ -1406,8 +1410,20 @@ int main(int argc, char **argv) /* Initialize the configuration module. */ ConfInit(); - while ((opt = getopt(argc, argv, "hi:l:q:r:us:")) != -1) { + struct option long_opts[] = { + {"dump-config", 0, &dump_config, 1}, + {NULL, 0, NULL, 0} + }; + char short_opts[] = "c:hi:l:q:r:us:"; + + while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) { switch (opt) { + case 0: + /* Long opt handler. */ + break; + case 'c': + conf_filename = optarg; + break; case 'h': usage(argv[0]); exit(EXIT_SUCCESS); @@ -1447,6 +1463,16 @@ int main(int argc, char **argv) } } + /* Load yaml configuration file if provided. */ + if (conf_filename != NULL) { + LoadYamlConf(conf_filename); + } + + if (dump_config) { + ConfDump(); + exit(EXIT_SUCCESS); + } + if (mode == MODE_UNKNOWN) { usage(argv[0]); exit(EXIT_FAILURE);