util-pages.c util-pages.h \
util-path.c util-path.h \
util-pidfile.c util-pidfile.h \
+util-plugin.c util-plugin.h \
util-pool.c util-pool.h \
util-pool-thread.c util-pool-thread.h \
util-prefilter.c util-prefilter.h \
win32-service.c win32-service.h \
win32-syslog.h
+include_HEADERS = \
+suricata-plugin.h
+
EXTRA_DIST = tests
# set the include path found by configure
#include "util-device.h"
#include "util-validate.h"
#include "util-crypt.h"
+#include "util-plugin.h"
#include "flow-var.h"
#include "flow-bit.h"
#include "source-pcap-file.h"
+#include "suricata-plugin.h"
+
#define DEFAULT_LOG_FILENAME "eve.json"
#define DEFAULT_ALERT_SYSLOG_FACILITY_STR "local0"
#define DEFAULT_ALERT_SYSLOG_FACILITY LOG_LOCAL0
"redis JSON output option is not compiled");
#endif
} else {
- SCLogError(SC_ERR_INVALID_ARGUMENT,
- "Invalid JSON output option: %s", output_s);
- exit(EXIT_FAILURE);
+#ifdef HAVE_PLUGINS
+ SCPluginFileType *plugin = SCPluginFindFileType(output_s);
+ if (plugin == NULL) {
+ FatalError(SC_ERR_INVALID_ARGUMENT,
+ "Invalid JSON output option: %s", output_s);
+ } else {
+ json_ctx->json_out = LOGFILE_TYPE_PLUGIN;
+ json_ctx->plugin = plugin;
+ }
+#else
+ FatalError(SC_ERR_INVALID_ARGUMENT,
+ "Invalid JSON output option: %s", output_s);
+#endif
}
}
}
}
#endif
+ else if (json_ctx->json_out == LOGFILE_TYPE_PLUGIN) {
+ ConfNode *plugin_conf = ConfNodeLookupChild(conf,
+ json_ctx->plugin->name);
+ void *plugin_data = NULL;
+ if (json_ctx->plugin->Open(plugin_conf, &plugin_data) < 0) {
+ LogFileFreeCtx(json_ctx->file_ctx);
+ SCFree(json_ctx);
+ SCFree(output_ctx);
+ } else {
+ json_ctx->file_ctx->plugin = json_ctx->plugin;
+ json_ctx->file_ctx->plugin_data = plugin_data;
+ }
+ }
const char *sensor_id_s = ConfNodeLookupChildValue(conf, "sensor-id");
if (sensor_id_s != NULL) {
#include "rust.h"
#include "app-layer-htp-xff.h"
+#include "suricata-plugin.h"
void OutputJsonRegister(void);
enum LogFileType json_out;
OutputJsonCommonSettings cfg;
HttpXFFCfg *xff_cfg;
+ SCPluginFileType *plugin;
} OutputJsonCtx;
typedef struct OutputJsonThreadCtx_ {
} LoggerId;
#include "util-optimize.h"
+#ifndef SURICATA_PLUGIN
#include <htp/htp.h>
+#endif
#include "threads.h"
#include "tm-threads-common.h"
#include "util-debug.h"
#include "util-error.h"
#include "util-mem.h"
+#ifndef SURICATA_PLUGIN
#include "detect-engine-alert.h"
+#endif
#include "util-path.h"
#include "util-conf.h"
--- /dev/null
+/* Copyright (C) 2020 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef __SURICATA_PLUGIN_H__
+#define __SURICATA_PLUGIN_H__
+
+#include "autoconf.h"
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "conf.h"
+
+/**
+ * Structure to define a Suricata plugin.
+ */
+typedef struct SCPlugin_ {
+ const char *name;
+ const char *license;
+ const char *author;
+ void (*Init)(void);
+} SCPlugin;
+
+/**
+ * Structure used to define a file type plugin.
+ *
+ * Currently only used by the Eve output type.
+ */
+typedef struct SCPluginFileType_ {
+ char *name;
+ int (*Open)(ConfNode *conf, void **data);
+ int (*Write)(const char *buffer, int buffer_len, void *ctx);
+ void (*Close)(void *ctx);
+ TAILQ_ENTRY(SCPluginFileType_) entries;
+} SCPluginFileType;
+
+bool SCPluginRegisterFileType(SCPluginFileType *);
+
+#endif /* __SURICATA_PLUGIN_H */
#include "util-lua.h"
+#include "util-plugin.h"
+
#include "rust.h"
/*
FeatureTrackingRegister(); /* must occur prior to output mod registration */
RegisterAllModules();
+ SCPluginsLoad();
+
AppLayerHtpNeedFileInspection();
StorageFinalize();
SCFree(lf_ctx->threads->append);
SCFree(lf_ctx->threads);
} else {
- if (lf_ctx->fp != NULL) {
+ if (lf_ctx->type == LOGFILE_TYPE_PLUGIN) {
+ if (lf_ctx->plugin->Close != NULL) {
+ lf_ctx->plugin->Close(lf_ctx->plugin_data);
+ }
+ } else if (lf_ctx->fp != NULL) {
lf_ctx->Close(lf_ctx);
}
if (lf_ctx->parent) {
SCMutexUnlock(&file_ctx->fp_mutex);
}
#endif
+ else if (file_ctx->type == LOGFILE_TYPE_PLUGIN) {
+ file_ctx->plugin->Write((const char *)MEMBUFFER_BUFFER(buffer),
+ MEMBUFFER_OFFSET(buffer), file_ctx->plugin_data);
+ }
return 0;
}
#include "util-log-redis.h"
#endif /* HAVE_LIBHIREDIS */
+#include "suricata-plugin.h"
typedef struct {
uint16_t fileno;
LOGFILE_TYPE_SYSLOG,
LOGFILE_TYPE_UNIX_DGRAM,
LOGFILE_TYPE_UNIX_STREAM,
- LOGFILE_TYPE_REDIS };
+ LOGFILE_TYPE_REDIS,
+ LOGFILE_TYPE_PLUGIN };
typedef struct SyslogSetup_ {
int alert_syslog_level;
FILE *fp;
PcieFile *pcie_fp;
LogThreadedFileCtx *threads;
+ void *plugin_data;
#ifdef HAVE_LIBHIREDIS
void *redis;
#endif
int (*Write)(const char *buffer, int buffer_len, struct LogFileCtx_ *fp);
void (*Close)(struct LogFileCtx_ *fp);
+ SCPluginFileType *plugin;
+
/** It will be locked if the log/alert
* record cannot be written to the file in one call */
SCMutex fp_mutex;
--- /dev/null
+/* Copyright (C) 2020 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "suricata-common.h"
+#include "suricata-plugin.h"
+#include "util-plugin.h"
+
+#ifdef HAVE_PLUGINS
+
+#include <dlfcn.h>
+
+static TAILQ_HEAD(, SCPluginFileType_) output_types =
+ TAILQ_HEAD_INITIALIZER(output_types);
+
+static void InitPlugin(char *path)
+{
+ void *lib = dlopen(path, RTLD_NOW);
+ if (lib == NULL) {
+ SCLogNotice("Failed to open %s as a plugin: %s", path, dlerror());
+ } else {
+ SCLogNotice("Loading plugin %s", path);
+ SCPlugin *plugin = dlsym(lib, "PluginSpec");
+ if (plugin == NULL) {
+ SCLogError(SC_ERR_PLUGIN, "Plugin does not export a plugin specification: %s", path);
+ } else {
+ BUG_ON(plugin->name == NULL);
+ BUG_ON(plugin->author == NULL);
+ BUG_ON(plugin->license == NULL);
+ BUG_ON(plugin->Init == NULL);
+ SCLogNotice("Initializing plugin %s; author=%s; license=%s",
+ plugin->name, plugin->author, plugin->license);
+ (*plugin->Init)();
+ }
+ }
+}
+
+void SCPluginsLoad(void)
+{
+ ConfNode *conf = ConfGetNode("plugins");
+ if (conf == NULL) {
+ return;
+ }
+ ConfNode *plugin = NULL;
+ TAILQ_FOREACH(plugin, &conf->head, next) {
+ struct stat statbuf;
+ if (stat(plugin->val, &statbuf) == -1) {
+ SCLogError(SC_ERR_STAT, "Bad plugin path: %s: %s",
+ plugin->val, strerror(errno));
+ continue;
+ }
+ if (S_ISDIR(statbuf.st_mode)) {
+ DIR *dir = opendir(plugin->val);
+ if (dir == NULL) {
+ SCLogError(SC_ERR_DIR_OPEN, "Failed to open plugin directory %s: %s",
+ plugin->val, strerror(errno));
+ continue;
+ }
+ struct dirent *entry = NULL;
+ char path[PATH_MAX];
+ while ((entry = readdir(dir)) != NULL) {
+ if (strstr(entry->d_name, ".so") != NULL) {
+ snprintf(path, sizeof(path), "%s/%s", plugin->val, entry->d_name);
+ InitPlugin(path);
+ }
+ }
+ free(dir);
+ } else {
+ InitPlugin(plugin->val);
+ }
+ }
+}
+
+/**
+ * \brief Register an Eve/JSON file type plugin.
+ *
+ * \retval true if registered successfully, false if the plugin name
+ * conflicts with a built-in or previously registered
+ * plugin file type.
+ *
+ * TODO: As this is Eve specific, perhaps Eve should be in the filename.
+ */
+bool SCPluginRegisterFileType(SCPluginFileType *plugin)
+{
+ const char *builtin[] = {
+ "regular",
+ "syslog",
+ "unix_dgram",
+ "unix_stream",
+ "redis",
+ NULL,
+ };
+ for (int i = 0;; i++) {
+ if (builtin[i] == NULL) {
+ break;
+ }
+ if (strcmp(builtin[i], plugin->name) == 0) {
+ SCLogNotice("Eve filetype plugin name \"%s\" conflicts "
+ "with built-in name", plugin->name);
+ return false;
+ }
+ }
+
+ SCPluginFileType *existing = NULL;
+ TAILQ_FOREACH(existing, &output_types, entries) {
+ if (strcmp(existing->name, plugin->name) == 0) {
+ SCLogNotice("Eve filetype plugin name conflicts with previously "
+ "registered plugin: %s", plugin->name);
+ return false;
+ }
+ }
+
+ SCLogNotice("Registering JSON file type plugin %s", plugin->name);
+ TAILQ_INSERT_TAIL(&output_types, plugin, entries);
+ return true;
+}
+
+SCPluginFileType *SCPluginFindFileType(const char *name)
+{
+ SCPluginFileType *plugin = NULL;
+ TAILQ_FOREACH(plugin, &output_types, entries) {
+ if (strcmp(name, plugin->name) == 0) {
+ return plugin;
+ }
+ }
+ return NULL;
+}
+
+#else
+
+void PluginsLoad(void)
+{
+}
+
+#endif
--- /dev/null
+/* Copyright (C) 2020 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef __UTIL_PLUGIN_H__
+#define __UTIL_PLUGIN_H__
+
+#include "suricata-plugin.h"
+
+void SCPluginsLoad(void);
+SCPluginFileType *SCPluginFindFileType(const char *name);
+
+#endif /* __UTIL_PLUGIN_H__ */