suricata
util-plugin.c
Go to the documentation of this file.
1 /* Copyright (C) 2020-2021 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 #include "suricata-common.h"
19 #include "suricata-plugin.h"
20 #include "suricata.h"
21 #include "runmodes.h"
22 #include "output-eve-syslog.h"
23 #include "util-plugin.h"
24 
25 #ifdef HAVE_PLUGINS
26 
27 #include <dlfcn.h>
28 
29 typedef struct PluginListNode_ {
30  SCPlugin *plugin;
31  void *lib;
32  TAILQ_ENTRY(PluginListNode_) entries;
33 } PluginListNode;
34 
35 /**
36  * The list of loaded plugins.
37  *
38  * Currently only used as a place to stash the pointer returned from
39  * dlopen, but could have other uses, such as a plugin unload destructor.
40  */
41 static TAILQ_HEAD(, PluginListNode_) plugins = TAILQ_HEAD_INITIALIZER(plugins);
42 
43 static TAILQ_HEAD(, SCEveFileType_) output_types = TAILQ_HEAD_INITIALIZER(output_types);
44 
45 static TAILQ_HEAD(, SCCapturePlugin_) capture_plugins = TAILQ_HEAD_INITIALIZER(capture_plugins);
46 
47 bool RegisterPlugin(SCPlugin *plugin, void *lib)
48 {
49  BUG_ON(plugin->name == NULL);
50  BUG_ON(plugin->author == NULL);
51  BUG_ON(plugin->license == NULL);
52  BUG_ON(plugin->Init == NULL);
53 
54  PluginListNode *node = SCCalloc(1, sizeof(*node));
55  if (node == NULL) {
56  SCLogError(SC_ERR_MEM_ALLOC, "Failed to allocate memory for plugin");
57  return false;
58  }
59  node->plugin = plugin;
60  node->lib = lib;
61  TAILQ_INSERT_TAIL(&plugins, node, entries);
62  SCLogNotice("Initializing plugin %s; author=%s; license=%s", plugin->name, plugin->author,
63  plugin->license);
64  (*plugin->Init)();
65  return true;
66 }
67 
68 static void InitPlugin(char *path)
69 {
70  void *lib = dlopen(path, RTLD_NOW);
71  if (lib == NULL) {
72  SCLogNotice("Failed to open %s as a plugin: %s", path, dlerror());
73  } else {
74  SCLogNotice("Loading plugin %s", path);
75 
76  SCPluginRegisterFunc plugin_register = dlsym(lib, "SCPluginRegister");
77  if (plugin_register == NULL) {
78  SCLogError(SC_ERR_PLUGIN, "Plugin does not export SCPluginRegister function: %s", path);
79  dlclose(lib);
80  return;
81  }
82 
83  if (!RegisterPlugin(plugin_register(), lib)) {
84  SCLogError(SC_ERR_PLUGIN, "Plugin registration failed: %s", path);
85  dlclose(lib);
86  return;
87  }
88  }
89 }
90 
91 void SCPluginsLoad(const char *capture_plugin_name, const char *capture_plugin_args)
92 {
93  ConfNode *conf = ConfGetNode("plugins");
94  if (conf == NULL) {
95  return;
96  }
97  ConfNode *plugin = NULL;
98  TAILQ_FOREACH(plugin, &conf->head, next) {
99  struct stat statbuf;
100  if (stat(plugin->val, &statbuf) == -1) {
101  SCLogError(SC_ERR_STAT, "Bad plugin path: %s: %s",
102  plugin->val, strerror(errno));
103  continue;
104  }
105  if (S_ISDIR(statbuf.st_mode)) {
106  // coverity[toctou : FALSE]
107  DIR *dir = opendir(plugin->val);
108  if (dir == NULL) {
109  SCLogError(SC_ERR_DIR_OPEN, "Failed to open plugin directory %s: %s",
110  plugin->val, strerror(errno));
111  continue;
112  }
113  struct dirent *entry = NULL;
114  char path[PATH_MAX];
115  while ((entry = readdir(dir)) != NULL) {
116  if (strstr(entry->d_name, ".so") != NULL) {
117  snprintf(path, sizeof(path), "%s/%s", plugin->val, entry->d_name);
118  InitPlugin(path);
119  }
120  }
121  closedir(dir);
122  } else {
123  InitPlugin(plugin->val);
124  }
125  }
126 
127  if (run_mode == RUNMODE_PLUGIN) {
128  SCCapturePlugin *capture = SCPluginFindCaptureByName(capture_plugin_name);
129  if (capture == NULL) {
130  FatalError(SC_ERR_PLUGIN, "No capture plugin found with name %s",
131  capture_plugin_name);
132  }
133  capture->Init(capture_plugin_args, RUNMODE_PLUGIN, TMM_RECEIVEPLUGIN,
135  }
136 }
137 
138 static bool IsBuiltinTypeName(const char *name)
139 {
140  const char *builtin[] = {
141  "regular",
142  "unix_dgram",
143  "unix_stream",
144  "redis",
145  NULL,
146  };
147  for (int i = 0;; i++) {
148  if (builtin[i] == NULL) {
149  break;
150  }
151  if (strcmp(builtin[i], name) == 0) {
152  return true;
153  }
154  }
155  return false;
156 }
157 
158 /**
159  * \brief Register an Eve file type.
160  *
161  * \retval true if registered successfully, false if the file type name
162  * conflicts with a built-in or previously registered
163  * file type.
164  */
166 {
167  /* First check that the name doesn't conflict with a built-in filetype. */
168  if (IsBuiltinTypeName(plugin->name)) {
169  SCLogError(SC_ERR_LOG_OUTPUT, "Eve file type name conflicts with built-in type: %s",
170  plugin->name);
171  return false;
172  }
173 
174  /* Now check against previously registered file types. */
175  SCEveFileType *existing = NULL;
176  TAILQ_FOREACH (existing, &output_types, entries) {
177  if (strcmp(existing->name, plugin->name) == 0) {
179  "Eve file type name conflicts with previously registered type: %s",
180  plugin->name);
181  return false;
182  }
183  }
184 
185  SCLogDebug("Registering EVE file type plugin %s", plugin->name);
186  TAILQ_INSERT_TAIL(&output_types, plugin, entries);
187  return true;
188 }
189 
190 SCEveFileType *SCPluginFindFileType(const char *name)
191 {
192  SCEveFileType *plugin = NULL;
193  TAILQ_FOREACH(plugin, &output_types, entries) {
194  if (strcmp(name, plugin->name) == 0) {
195  return plugin;
196  }
197  }
198  return NULL;
199 }
200 
202 {
203  TAILQ_INSERT_TAIL(&capture_plugins, plugin, entries);
204  SCLogNotice("Capture plugin registered: %s", plugin->name);
205  return 0;
206 }
207 
209 {
210  SCCapturePlugin *plugin = NULL;
211  TAILQ_FOREACH(plugin, &capture_plugins, entries) {
212  if (strcmp(name, plugin->name) == 0) {
213  return plugin;
214  }
215  }
216  return plugin;
217 }
218 #endif
suricata-plugin.h
SCPluginRegisterCapture
int SCPluginRegisterCapture(SCCapturePlugin *)
TMM_RECEIVEPLUGIN
@ TMM_RECEIVEPLUGIN
Definition: tm-threads-common.h:44
SCEveFileType_::name
const char * name
Definition: suricata-plugin.h:50
ConfNode_::val
char * val
Definition: conf.h:34
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:296
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
SCCapturePlugin_::Init
void(* Init)(const char *args, int plugin_slot, int receive_slot, int decode_slot)
Definition: suricata-plugin.h:69
TAILQ_FOREACH
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:252
TAILQ_INSERT_TAIL
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:294
TAILQ_ENTRY
#define TAILQ_ENTRY(type)
Definition: queue.h:239
TMM_DECODEPLUGIN
@ TMM_DECODEPLUGIN
Definition: tm-threads-common.h:45
TAILQ_HEAD_INITIALIZER
#define TAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:236
SC_ERR_PLUGIN
@ SC_ERR_PLUGIN
Definition: util-error.h:367
SCCapturePlugin_::name
char * name
Definition: suricata-plugin.h:68
SCRegisterEveFileType
bool SCRegisterEveFileType(SCEveFileType *)
SCCapturePlugin_
Definition: suricata-plugin.h:67
RegisterPlugin
bool RegisterPlugin(SCPlugin *, void *)
BUG_ON
#define BUG_ON(x)
Definition: suricata-common.h:281
SCPlugin_::license
const char * license
Definition: suricata-plugin.h:37
SCPlugin_::author
const char * author
Definition: suricata-plugin.h:38
SCPlugin_
Definition: suricata-plugin.h:35
util-plugin.h
runmodes.h
SCPluginsLoad
void SCPluginsLoad(const char *capture_plugin_name, const char *capture_plugin_args)
SCPluginFindFileType
SCEveFileType * SCPluginFindFileType(const char *name)
suricata-common.h
run_mode
int run_mode
Definition: suricata.c:209
SCLogError
#define SCLogError(err_code,...)
Macro used to log ERROR messages.
Definition: util-debug.h:255
output-eve-syslog.h
SCPlugin_::Init
void(* Init)(void)
Definition: suricata-plugin.h:39
FatalError
#define FatalError(x,...)
Definition: util-debug.h:530
ConfNode_
Definition: conf.h:32
TAILQ_HEAD
#define TAILQ_HEAD(name, type)
Definition: queue.h:230
SC_ERR_STAT
@ SC_ERR_STAT
Definition: util-error.h:145
SC_ERR_MEM_ALLOC
@ SC_ERR_MEM_ALLOC
Definition: util-error.h:31
suricata.h
SC_ERR_LOG_OUTPUT
@ SC_ERR_LOG_OUTPUT
Definition: util-error.h:368
SCPlugin_::name
const char * name
Definition: suricata-plugin.h:36
SCLogNotice
#define SCLogNotice(...)
Macro used to log NOTICE messages.
Definition: util-debug.h:230
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
SC_ERR_DIR_OPEN
@ SC_ERR_DIR_OPEN
Definition: util-error.h:324
RUNMODE_PLUGIN
@ RUNMODE_PLUGIN
Definition: runmodes.h:44
SCPluginFindCaptureByName
SCCapturePlugin * SCPluginFindCaptureByName(const char *name)
SCPluginRegisterFunc
SCPlugin *(* SCPluginRegisterFunc)(void)
Definition: suricata-plugin.h:42
SCEveFileType_
Definition: suricata-plugin.h:47