Coverage Report

Created: 2026-03-31 07:45

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