/src/suricata7/src/unix-manager.c
Line | Count | Source |
1 | | /* Copyright (C) 2013-2018 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 Eric Leblond <eric@regit.org> |
22 | | */ |
23 | | |
24 | | #include "suricata-common.h" |
25 | | #include "unix-manager.h" |
26 | | #include "threads.h" |
27 | | #include "detect-engine.h" |
28 | | #include "tm-threads.h" |
29 | | #include "runmodes.h" |
30 | | #include "conf.h" |
31 | | #include "runmode-unix-socket.h" |
32 | | |
33 | | #include "output-json-stats.h" |
34 | | |
35 | | #include "util-conf.h" |
36 | | #include "util-privs.h" |
37 | | #include "util-debug.h" |
38 | | #include "util-device.h" |
39 | | #include "util-ebpf.h" |
40 | | #include "util-signal.h" |
41 | | #include "util-buffer.h" |
42 | | #include "util-path.h" |
43 | | #include "util-profiling.h" |
44 | | |
45 | | #if (defined BUILD_UNIX_SOCKET) && (defined HAVE_SYS_UN_H) && (defined HAVE_SYS_STAT_H) && (defined HAVE_SYS_TYPES_H) |
46 | | #include <sys/un.h> |
47 | | #include <sys/stat.h> |
48 | | #include <sys/types.h> |
49 | | |
50 | | #include "output.h" |
51 | | #include "output-json.h" |
52 | | |
53 | | // MSG_NOSIGNAL does not exists on OS X |
54 | | #ifdef OS_DARWIN |
55 | | # ifndef MSG_NOSIGNAL |
56 | | # define MSG_NOSIGNAL SO_NOSIGPIPE |
57 | | # endif |
58 | | #endif |
59 | | |
60 | 0 | #define SOCKET_PATH LOCAL_STATE_DIR "/run/suricata/" |
61 | 0 | #define SOCKET_FILENAME "suricata-command.socket" |
62 | 0 | #define SOCKET_TARGET SOCKET_PATH SOCKET_FILENAME |
63 | | |
64 | | SCCtrlCondT unix_manager_ctrl_cond; |
65 | | SCCtrlMutex unix_manager_ctrl_mutex; |
66 | | |
67 | 0 | #define MAX_FAILED_RULES 20 |
68 | | |
69 | | typedef struct Command_ { |
70 | | char *name; |
71 | | TmEcode (*Func)(json_t *, json_t *, void *); |
72 | | void *data; |
73 | | int flags; |
74 | | TAILQ_ENTRY(Command_) next; |
75 | | } Command; |
76 | | |
77 | | typedef struct Task_ { |
78 | | TmEcode (*Func)(void *); |
79 | | void *data; |
80 | | TAILQ_ENTRY(Task_) next; |
81 | | } Task; |
82 | | |
83 | 0 | #define CLIENT_BUFFER_SIZE 4096 |
84 | | typedef struct UnixClient_ { |
85 | | int fd; |
86 | | MemBuffer *mbuf; /**< buffer for response construction */ |
87 | | int version; |
88 | | TAILQ_ENTRY(UnixClient_) next; |
89 | | } UnixClient; |
90 | | |
91 | | typedef struct UnixCommand_ { |
92 | | time_t start_timestamp; |
93 | | int socket; |
94 | | struct sockaddr_un client_addr; |
95 | | int select_max; |
96 | | TAILQ_HEAD(, Command_) commands; |
97 | | TAILQ_HEAD(, Task_) tasks; |
98 | | TAILQ_HEAD(, UnixClient_) clients; |
99 | | } UnixCommand; |
100 | | |
101 | | /** |
102 | | * \brief Create a command unix socket on system |
103 | | * |
104 | | * \retval 0 in case of error, 1 in case of success |
105 | | */ |
106 | | static int UnixNew(UnixCommand * this) |
107 | 0 | { |
108 | 0 | struct sockaddr_un addr; |
109 | 0 | int len; |
110 | 0 | int ret; |
111 | 0 | int on = 1; |
112 | 0 | char sockettarget[PATH_MAX]; |
113 | 0 | const char *socketname; |
114 | |
|
115 | 0 | this->start_timestamp = time(NULL); |
116 | 0 | this->socket = -1; |
117 | 0 | this->select_max = 0; |
118 | |
|
119 | 0 | TAILQ_INIT(&this->commands); |
120 | 0 | TAILQ_INIT(&this->tasks); |
121 | 0 | TAILQ_INIT(&this->clients); |
122 | |
|
123 | 0 | int check_dir = 0; |
124 | 0 | if (ConfGet("unix-command.filename", &socketname) == 1) { |
125 | 0 | if (PathIsAbsolute(socketname)) { |
126 | 0 | strlcpy(sockettarget, socketname, sizeof(sockettarget)); |
127 | 0 | } else { |
128 | 0 | snprintf(sockettarget, sizeof(sockettarget), "%s/%s", |
129 | 0 | SOCKET_PATH, socketname); |
130 | 0 | check_dir = 1; |
131 | 0 | } |
132 | 0 | } else { |
133 | 0 | strlcpy(sockettarget, SOCKET_TARGET, sizeof(sockettarget)); |
134 | 0 | check_dir = 1; |
135 | 0 | } |
136 | 0 | SCLogInfo("unix socket '%s'", sockettarget); |
137 | |
|
138 | 0 | if (check_dir) { |
139 | 0 | struct stat stat_buf; |
140 | | /* coverity[toctou] */ |
141 | 0 | if (stat(SOCKET_PATH, &stat_buf) != 0) { |
142 | | /* coverity[toctou] */ |
143 | 0 | ret = SCMkDir(SOCKET_PATH, S_IRWXU|S_IXGRP|S_IRGRP); |
144 | 0 | if (ret != 0) { |
145 | 0 | int err = errno; |
146 | 0 | if (err != EEXIST) { |
147 | 0 | SCLogError( |
148 | 0 | "failed to create socket directory %s: %s", SOCKET_PATH, strerror(err)); |
149 | 0 | return 0; |
150 | 0 | } |
151 | 0 | } else { |
152 | 0 | SCLogInfo("created socket directory %s", SOCKET_PATH); |
153 | 0 | } |
154 | 0 | } |
155 | 0 | } |
156 | | |
157 | | /* Remove socket file */ |
158 | 0 | (void) unlink(sockettarget); |
159 | | |
160 | | /* set address */ |
161 | 0 | addr.sun_family = AF_UNIX; |
162 | 0 | strlcpy(addr.sun_path, sockettarget, sizeof(addr.sun_path)); |
163 | 0 | addr.sun_path[sizeof(addr.sun_path) - 1] = 0; |
164 | 0 | len = strlen(addr.sun_path) + sizeof(addr.sun_family) + 1; |
165 | | |
166 | | /* create socket */ |
167 | 0 | this->socket = socket(AF_UNIX, SOCK_STREAM, 0); |
168 | 0 | if (this->socket == -1) { |
169 | 0 | SCLogWarning( |
170 | 0 | "Unix Socket: unable to create UNIX socket %s: %s", addr.sun_path, strerror(errno)); |
171 | 0 | return 0; |
172 | 0 | } |
173 | 0 | this->select_max = this->socket + 1; |
174 | | |
175 | | /* set reuse option */ |
176 | 0 | ret = setsockopt(this->socket, SOL_SOCKET, SO_REUSEADDR, |
177 | 0 | (char *) &on, sizeof(on)); |
178 | 0 | if ( ret != 0 ) { |
179 | 0 | SCLogWarning("Cannot set sockets options: %s.", strerror(errno)); |
180 | 0 | } |
181 | | |
182 | | /* bind socket */ |
183 | 0 | ret = bind(this->socket, (struct sockaddr *) &addr, len); |
184 | 0 | if (ret == -1) { |
185 | 0 | SCLogWarning("Unix socket: UNIX socket bind(%s) error: %s", sockettarget, strerror(errno)); |
186 | 0 | return 0; |
187 | 0 | } |
188 | | |
189 | 0 | #if !(defined OS_FREEBSD || defined __OpenBSD__) |
190 | | /* Set file mode: will not fully work on most system, the group |
191 | | * permission is not changed on some Linux. *BSD won't do the |
192 | | * chmod: it returns EINVAL when calling chmod on sockets. */ |
193 | 0 | ret = chmod(sockettarget, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); |
194 | 0 | if (ret == -1) { |
195 | 0 | int err = errno; |
196 | 0 | SCLogWarning("Unable to change permission on socket: %s (%d)", strerror(err), err); |
197 | 0 | } |
198 | 0 | #endif |
199 | | |
200 | | /* listen */ |
201 | 0 | if (listen(this->socket, 1) == -1) { |
202 | 0 | SCLogWarning("Command server: UNIX socket listen() error: %s", strerror(errno)); |
203 | 0 | return 0; |
204 | 0 | } |
205 | 0 | return 1; |
206 | 0 | } |
207 | | |
208 | | static void UnixCommandSetMaxFD(UnixCommand *this) |
209 | 0 | { |
210 | 0 | UnixClient *item; |
211 | |
|
212 | 0 | if (this == NULL) { |
213 | 0 | SCLogError("Unix command is NULL, warn devel"); |
214 | 0 | return; |
215 | 0 | } |
216 | | |
217 | 0 | this->select_max = this->socket + 1; |
218 | 0 | TAILQ_FOREACH(item, &this->clients, next) { |
219 | 0 | if (item->fd >= this->select_max) { |
220 | 0 | this->select_max = item->fd + 1; |
221 | 0 | } |
222 | 0 | } |
223 | 0 | } |
224 | | |
225 | | static UnixClient *UnixClientAlloc(void) |
226 | 0 | { |
227 | 0 | UnixClient *uclient = SCMalloc(sizeof(UnixClient)); |
228 | 0 | if (unlikely(uclient == NULL)) { |
229 | 0 | SCLogError("Can't allocate new client"); |
230 | 0 | return NULL; |
231 | 0 | } |
232 | 0 | uclient->mbuf = MemBufferCreateNew(CLIENT_BUFFER_SIZE); |
233 | 0 | if (uclient->mbuf == NULL) { |
234 | 0 | SCLogError("Can't allocate new client send buffer"); |
235 | 0 | SCFree(uclient); |
236 | 0 | return NULL; |
237 | 0 | } |
238 | 0 | return uclient; |
239 | 0 | } |
240 | | |
241 | | static void UnixClientFree(UnixClient *c) |
242 | 0 | { |
243 | 0 | if (c != NULL) { |
244 | 0 | MemBufferFree(c->mbuf); |
245 | 0 | SCFree(c); |
246 | 0 | } |
247 | 0 | } |
248 | | |
249 | | /** |
250 | | * \brief Close the unix socket |
251 | | */ |
252 | | static void UnixCommandClose(UnixCommand *this, int fd) |
253 | 0 | { |
254 | 0 | UnixClient *item; |
255 | 0 | UnixClient *safe = NULL; |
256 | 0 | int found = 0; |
257 | |
|
258 | 0 | TAILQ_FOREACH_SAFE (item, &this->clients, next, safe) { |
259 | 0 | if (item->fd == fd) { |
260 | 0 | found = 1; |
261 | 0 | break; |
262 | 0 | } |
263 | 0 | } |
264 | |
|
265 | 0 | if (found == 0) { |
266 | 0 | SCLogError("No fd found in client list"); |
267 | 0 | return; |
268 | 0 | } |
269 | | |
270 | 0 | TAILQ_REMOVE(&this->clients, item, next); |
271 | |
|
272 | 0 | close(item->fd); |
273 | 0 | UnixCommandSetMaxFD(this); |
274 | 0 | UnixClientFree(item); |
275 | 0 | } |
276 | | |
277 | | #define UNIX_PROTO_VERSION_LENGTH 200 |
278 | 0 | #define UNIX_PROTO_VERSION_V1 "0.1" |
279 | 0 | #define UNIX_PROTO_V1 1 |
280 | 0 | #define UNIX_PROTO_VERSION "0.2" |
281 | 0 | #define UNIX_PROTO_V2 2 |
282 | | |
283 | | static int UnixCommandSendJSONToClient(UnixClient *client, json_t *js) |
284 | 0 | { |
285 | 0 | MemBufferReset(client->mbuf); |
286 | |
|
287 | 0 | OutputJSONMemBufferWrapper wrapper = { |
288 | 0 | .buffer = &client->mbuf, |
289 | 0 | .expand_by = CLIENT_BUFFER_SIZE |
290 | 0 | }; |
291 | |
|
292 | 0 | int r = json_dump_callback(js, OutputJSONMemBufferCallback, &wrapper, |
293 | 0 | JSON_PRESERVE_ORDER|JSON_COMPACT|JSON_ENSURE_ASCII| |
294 | 0 | JSON_ESCAPE_SLASH); |
295 | 0 | if (r != 0) { |
296 | 0 | SCLogWarning("unable to serialize JSON object"); |
297 | 0 | return -1; |
298 | 0 | } |
299 | | |
300 | 0 | if (client->version > UNIX_PROTO_V1) { |
301 | 0 | if (MEMBUFFER_OFFSET(client->mbuf) + 1 >= MEMBUFFER_SIZE(client->mbuf)) { |
302 | 0 | MemBufferExpand(&client->mbuf, 1); |
303 | 0 | } |
304 | 0 | MemBufferWriteString(client->mbuf, "\n"); |
305 | 0 | } |
306 | |
|
307 | 0 | if (send(client->fd, (const char *)MEMBUFFER_BUFFER(client->mbuf), |
308 | 0 | MEMBUFFER_OFFSET(client->mbuf), MSG_NOSIGNAL) == -1) |
309 | 0 | { |
310 | 0 | SCLogWarning("unable to send block of size " |
311 | 0 | "%" PRIuMAX ": %s", |
312 | 0 | (uintmax_t)MEMBUFFER_OFFSET(client->mbuf), strerror(errno)); |
313 | 0 | return -1; |
314 | 0 | } |
315 | | |
316 | 0 | SCLogDebug("sent message of size %"PRIuMAX" to client socket %d", |
317 | 0 | (uintmax_t)MEMBUFFER_OFFSET(client->mbuf), client->fd); |
318 | 0 | return 0; |
319 | 0 | } |
320 | | |
321 | | /** |
322 | | * \brief Accept a new client on unix socket |
323 | | * |
324 | | * The function is called when a new user is detected |
325 | | * in UnixMain(). It does the initial protocol negotiation |
326 | | * with client. |
327 | | * |
328 | | * \retval 0 in case of error, 1 in case of success |
329 | | */ |
330 | | static int UnixCommandAccept(UnixCommand *this) |
331 | 0 | { |
332 | 0 | char buffer[UNIX_PROTO_VERSION_LENGTH + 1]; |
333 | 0 | json_t *client_msg; |
334 | 0 | json_t *server_msg; |
335 | 0 | json_t *version; |
336 | 0 | json_error_t jerror; |
337 | 0 | int client; |
338 | 0 | int client_version; |
339 | 0 | int ret; |
340 | 0 | UnixClient *uclient = NULL; |
341 | | |
342 | | /* accept client socket */ |
343 | 0 | socklen_t len = sizeof(this->client_addr); |
344 | 0 | client = accept(this->socket, (struct sockaddr *) &this->client_addr, |
345 | 0 | &len); |
346 | 0 | if (client < 0) { |
347 | 0 | SCLogInfo("Unix socket: accept() error: %s", |
348 | 0 | strerror(errno)); |
349 | 0 | return 0; |
350 | 0 | } |
351 | 0 | SCLogDebug("Unix socket: client connection"); |
352 | | |
353 | | /* read client version */ |
354 | 0 | buffer[sizeof(buffer)-1] = 0; |
355 | 0 | ret = recv(client, buffer, sizeof(buffer)-1, 0); |
356 | 0 | if (ret < 0) { |
357 | 0 | SCLogInfo("Command server: client doesn't send version"); |
358 | 0 | close(client); |
359 | 0 | return 0; |
360 | 0 | } |
361 | 0 | if (ret >= (int)(sizeof(buffer)-1)) { |
362 | 0 | SCLogInfo("Command server: client message is too long, " |
363 | 0 | "disconnect him."); |
364 | 0 | close(client); |
365 | 0 | return 0; |
366 | 0 | } |
367 | 0 | buffer[ret] = 0; |
368 | |
|
369 | 0 | client_msg = json_loads(buffer, 0, &jerror); |
370 | 0 | if (client_msg == NULL) { |
371 | 0 | SCLogInfo("Invalid command, error on line %d: %s\n", jerror.line, jerror.text); |
372 | 0 | close(client); |
373 | 0 | return 0; |
374 | 0 | } |
375 | | |
376 | 0 | version = json_object_get(client_msg, "version"); |
377 | 0 | if (!json_is_string(version)) { |
378 | 0 | SCLogInfo("error: version is not a string"); |
379 | 0 | close(client); |
380 | 0 | json_decref(client_msg); |
381 | 0 | return 0; |
382 | 0 | } |
383 | | |
384 | | /* check client version */ |
385 | 0 | if ((strcmp(json_string_value(version), UNIX_PROTO_VERSION) != 0) |
386 | 0 | && (strcmp(json_string_value(version), UNIX_PROTO_VERSION_V1) != 0)) { |
387 | 0 | SCLogInfo("Unix socket: invalid client version: \"%s\"", |
388 | 0 | json_string_value(version)); |
389 | 0 | json_decref(client_msg); |
390 | 0 | close(client); |
391 | 0 | return 0; |
392 | 0 | } else { |
393 | 0 | SCLogDebug("Unix socket: client version: \"%s\"", |
394 | 0 | json_string_value(version)); |
395 | 0 | if (strcmp(json_string_value(version), UNIX_PROTO_VERSION_V1) == 0) { |
396 | 0 | client_version = UNIX_PROTO_V1; |
397 | 0 | } else { |
398 | 0 | client_version = UNIX_PROTO_V2; |
399 | 0 | } |
400 | 0 | } |
401 | | |
402 | 0 | json_decref(client_msg); |
403 | | /* send answer */ |
404 | 0 | server_msg = json_object(); |
405 | 0 | if (server_msg == NULL) { |
406 | 0 | close(client); |
407 | 0 | return 0; |
408 | 0 | } |
409 | 0 | json_object_set_new(server_msg, "return", json_string("OK")); |
410 | |
|
411 | 0 | uclient = UnixClientAlloc(); |
412 | 0 | if (unlikely(uclient == NULL)) { |
413 | 0 | json_decref(server_msg); |
414 | 0 | close(client); |
415 | 0 | return 0; |
416 | 0 | } |
417 | 0 | uclient->fd = client; |
418 | 0 | uclient->version = client_version; |
419 | |
|
420 | 0 | if (UnixCommandSendJSONToClient(uclient, server_msg) != 0) { |
421 | 0 | SCLogWarning("Unable to send command"); |
422 | |
|
423 | 0 | UnixClientFree(uclient); |
424 | 0 | json_decref(server_msg); |
425 | 0 | close(client); |
426 | 0 | return 0; |
427 | 0 | } |
428 | | |
429 | 0 | json_decref(server_msg); |
430 | | |
431 | | /* client connected */ |
432 | 0 | SCLogDebug("Unix socket: client connected"); |
433 | 0 | TAILQ_INSERT_TAIL(&this->clients, uclient, next); |
434 | 0 | UnixCommandSetMaxFD(this); |
435 | 0 | return 1; |
436 | 0 | } |
437 | | |
438 | | static int UnixCommandBackgroundTasks(UnixCommand* this) |
439 | 0 | { |
440 | 0 | int ret = 1; |
441 | 0 | Task *ltask; |
442 | |
|
443 | 0 | TAILQ_FOREACH(ltask, &this->tasks, next) { |
444 | 0 | int fret = ltask->Func(ltask->data); |
445 | 0 | if (fret != TM_ECODE_OK) { |
446 | 0 | ret = 0; |
447 | 0 | } |
448 | 0 | } |
449 | 0 | return ret; |
450 | 0 | } |
451 | | |
452 | | /** |
453 | | * \brief Command dispatcher |
454 | | * |
455 | | * \param this a UnixCommand:: structure |
456 | | * \param command a string containing a json formatted |
457 | | * command |
458 | | * |
459 | | * \retval 0 in case of error, 1 in case of success |
460 | | */ |
461 | | static int UnixCommandExecute(UnixCommand * this, char *command, UnixClient *client) |
462 | 0 | { |
463 | 0 | int ret = 1; |
464 | 0 | json_error_t error; |
465 | 0 | json_t *jsoncmd = NULL; |
466 | 0 | json_t *cmd = NULL; |
467 | 0 | json_t *server_msg = json_object(); |
468 | 0 | const char * value; |
469 | 0 | int found = 0; |
470 | 0 | Command *lcmd; |
471 | |
|
472 | 0 | if (server_msg == NULL) { |
473 | 0 | return 0; |
474 | 0 | } |
475 | | |
476 | 0 | jsoncmd = json_loads(command, 0, &error); |
477 | 0 | if (jsoncmd == NULL) { |
478 | 0 | SCLogInfo("Invalid command, error on line %d: %s\n", error.line, error.text); |
479 | 0 | goto error; |
480 | 0 | } |
481 | | |
482 | 0 | cmd = json_object_get(jsoncmd, "command"); |
483 | 0 | if(!json_is_string(cmd)) { |
484 | 0 | SCLogInfo("error: command is not a string"); |
485 | 0 | goto error_cmd; |
486 | 0 | } |
487 | 0 | value = json_string_value(cmd); |
488 | |
|
489 | 0 | TAILQ_FOREACH(lcmd, &this->commands, next) { |
490 | 0 | if (!strcmp(value, lcmd->name)) { |
491 | 0 | int fret = TM_ECODE_OK; |
492 | 0 | found = 1; |
493 | 0 | if (lcmd->flags & UNIX_CMD_TAKE_ARGS) { |
494 | 0 | cmd = json_object_get(jsoncmd, "arguments"); |
495 | 0 | if(!json_is_object(cmd)) { |
496 | 0 | SCLogInfo("error: argument is not an object"); |
497 | 0 | goto error_cmd; |
498 | 0 | } |
499 | 0 | } |
500 | 0 | fret = lcmd->Func(cmd, server_msg, lcmd->data); |
501 | 0 | if (fret != TM_ECODE_OK) { |
502 | 0 | ret = 0; |
503 | 0 | } |
504 | 0 | break; |
505 | 0 | } |
506 | 0 | } |
507 | | |
508 | 0 | if (found == 0) { |
509 | 0 | json_object_set_new(server_msg, "message", json_string("Unknown command")); |
510 | 0 | ret = 0; |
511 | 0 | } |
512 | |
|
513 | 0 | switch (ret) { |
514 | 0 | case 0: |
515 | 0 | json_object_set_new(server_msg, "return", json_string("NOK")); |
516 | 0 | break; |
517 | 0 | case 1: |
518 | 0 | json_object_set_new(server_msg, "return", json_string("OK")); |
519 | 0 | break; |
520 | 0 | } |
521 | | |
522 | 0 | if (UnixCommandSendJSONToClient(client, server_msg) != 0) { |
523 | 0 | goto error_cmd; |
524 | 0 | } |
525 | | |
526 | 0 | json_decref(jsoncmd); |
527 | 0 | json_decref(server_msg); |
528 | 0 | return ret; |
529 | | |
530 | 0 | error_cmd: |
531 | 0 | json_decref(jsoncmd); |
532 | 0 | error: |
533 | 0 | json_decref(server_msg); |
534 | 0 | UnixCommandClose(this, client->fd); |
535 | 0 | return 0; |
536 | 0 | } |
537 | | |
538 | | static void UnixCommandRun(UnixCommand * this, UnixClient *client) |
539 | 0 | { |
540 | 0 | char buffer[4096]; |
541 | 0 | int ret; |
542 | 0 | if (client->version <= UNIX_PROTO_V1) { |
543 | 0 | ret = recv(client->fd, buffer, sizeof(buffer) - 1, 0); |
544 | 0 | if (ret <= 0) { |
545 | 0 | if (ret == 0) { |
546 | 0 | SCLogDebug("Unix socket: lost connection with client"); |
547 | 0 | } else { |
548 | 0 | SCLogError("Unix socket: error on recv() from client: %s", strerror(errno)); |
549 | 0 | } |
550 | 0 | UnixCommandClose(this, client->fd); |
551 | 0 | return; |
552 | 0 | } |
553 | 0 | if (ret >= (int)(sizeof(buffer)-1)) { |
554 | 0 | SCLogError("Command server: client command is too long, " |
555 | 0 | "disconnect him."); |
556 | 0 | UnixCommandClose(this, client->fd); |
557 | 0 | return; |
558 | 0 | } |
559 | 0 | buffer[ret] = 0; |
560 | 0 | } else { |
561 | 0 | int try = 0; |
562 | 0 | int offset = 0; |
563 | 0 | int cmd_over = 0; |
564 | 0 | ret = recv(client->fd, buffer + offset, sizeof(buffer) - offset - 1, 0); |
565 | 0 | do { |
566 | 0 | if (ret <= 0) { |
567 | 0 | if (ret == 0) { |
568 | 0 | SCLogDebug("Unix socket: lost connection with client"); |
569 | 0 | } else { |
570 | 0 | SCLogError("Unix socket: error on recv() from client: %s", strerror(errno)); |
571 | 0 | } |
572 | 0 | UnixCommandClose(this, client->fd); |
573 | 0 | return; |
574 | 0 | } |
575 | 0 | if (ret >= (int)(sizeof(buffer)- offset - 1)) { |
576 | 0 | SCLogInfo("Command server: client command is too long, " |
577 | 0 | "disconnect him."); |
578 | 0 | UnixCommandClose(this, client->fd); |
579 | 0 | return; |
580 | 0 | } |
581 | 0 | if (buffer[ret - 1] == '\n') { |
582 | 0 | buffer[ret-1] = 0; |
583 | 0 | cmd_over = 1; |
584 | 0 | } else { |
585 | 0 | struct timeval tv; |
586 | 0 | fd_set select_set; |
587 | 0 | offset += ret; |
588 | 0 | do { |
589 | 0 | FD_ZERO(&select_set); |
590 | 0 | FD_SET(client->fd, &select_set); |
591 | 0 | tv.tv_sec = 0; |
592 | 0 | tv.tv_usec = 200 * 1000; |
593 | 0 | try++; |
594 | 0 | ret = select(client->fd, &select_set, NULL, NULL, &tv); |
595 | | /* catch select() error */ |
596 | 0 | if (ret == -1) { |
597 | | /* Signal was caught: just ignore it */ |
598 | 0 | if (errno != EINTR) { |
599 | 0 | SCLogInfo("Unix socket: lost connection with client"); |
600 | 0 | UnixCommandClose(this, client->fd); |
601 | 0 | return; |
602 | 0 | } |
603 | 0 | } |
604 | 0 | } while (ret == 0 && try < 3); |
605 | 0 | if (ret > 0) { |
606 | 0 | ret = recv(client->fd, buffer + offset, |
607 | 0 | sizeof(buffer) - offset - 1, 0); |
608 | 0 | } |
609 | 0 | } |
610 | 0 | } while (try < 3 && cmd_over == 0); |
611 | | |
612 | 0 | if (try == 3 && cmd_over == 0) { |
613 | 0 | SCLogInfo("Unix socket: incomplete client message, closing connection"); |
614 | 0 | UnixCommandClose(this, client->fd); |
615 | 0 | return; |
616 | 0 | } |
617 | 0 | } |
618 | 0 | UnixCommandExecute(this, buffer, client); |
619 | 0 | } |
620 | | |
621 | | /** |
622 | | * \brief Select function |
623 | | * |
624 | | * \retval 0 in case of error, 1 in case of success |
625 | | */ |
626 | | static int UnixMain(UnixCommand * this) |
627 | 0 | { |
628 | 0 | struct timeval tv; |
629 | 0 | int ret; |
630 | 0 | fd_set select_set; |
631 | 0 | UnixClient *uclient; |
632 | 0 | UnixClient *tclient; |
633 | |
|
634 | 0 | if (suricata_ctl_flags & SURICATA_STOP) { |
635 | 0 | TAILQ_FOREACH_SAFE (uclient, &this->clients, next, tclient) { |
636 | 0 | UnixCommandClose(this, uclient->fd); |
637 | 0 | } |
638 | 0 | return 1; |
639 | 0 | } |
640 | | |
641 | | /* Wait activity on the socket */ |
642 | 0 | FD_ZERO(&select_set); |
643 | 0 | FD_SET(this->socket, &select_set); |
644 | 0 | TAILQ_FOREACH(uclient, &this->clients, next) { |
645 | 0 | FD_SET(uclient->fd, &select_set); |
646 | 0 | } |
647 | |
|
648 | 0 | tv.tv_sec = 0; |
649 | 0 | tv.tv_usec = 200 * 1000; |
650 | 0 | ret = select(this->select_max, &select_set, NULL, NULL, &tv); |
651 | | |
652 | | /* catch select() error */ |
653 | 0 | if (ret == -1) { |
654 | | /* Signal was caught: just ignore it */ |
655 | 0 | if (errno == EINTR) { |
656 | 0 | return 1; |
657 | 0 | } |
658 | 0 | SCLogError("Command server: select() fatal error: %s", strerror(errno)); |
659 | 0 | return 0; |
660 | 0 | } |
661 | | |
662 | | /* timeout: continue */ |
663 | 0 | if (ret == 0) { |
664 | 0 | return 1; |
665 | 0 | } |
666 | | |
667 | 0 | TAILQ_FOREACH_SAFE(uclient, &this->clients, next, tclient) { |
668 | 0 | if (FD_ISSET(uclient->fd, &select_set)) { |
669 | 0 | UnixCommandRun(this, uclient); |
670 | 0 | } |
671 | 0 | } |
672 | 0 | if (FD_ISSET(this->socket, &select_set)) { |
673 | 0 | if (!UnixCommandAccept(this)) |
674 | 0 | return 1; |
675 | 0 | } |
676 | | |
677 | 0 | return 1; |
678 | 0 | } |
679 | | |
680 | | static TmEcode UnixManagerShutdownCommand(json_t *cmd, |
681 | | json_t *server_msg, void *data) |
682 | 0 | { |
683 | 0 | SCEnter(); |
684 | 0 | json_object_set_new(server_msg, "message", json_string("Closing Suricata")); |
685 | 0 | EngineStop(); |
686 | 0 | SCReturnInt(TM_ECODE_OK); |
687 | 0 | } |
688 | | |
689 | | static TmEcode UnixManagerVersionCommand(json_t *cmd, |
690 | | json_t *server_msg, void *data) |
691 | 0 | { |
692 | 0 | SCEnter(); |
693 | 0 | json_object_set_new(server_msg, "message", json_string(GetProgramVersion())); |
694 | 0 | SCReturnInt(TM_ECODE_OK); |
695 | 0 | } |
696 | | |
697 | | static TmEcode UnixManagerUptimeCommand(json_t *cmd, |
698 | | json_t *server_msg, void *data) |
699 | 0 | { |
700 | 0 | SCEnter(); |
701 | 0 | int uptime; |
702 | 0 | UnixCommand *ucmd = (UnixCommand *)data; |
703 | |
|
704 | 0 | uptime = time(NULL) - ucmd->start_timestamp; |
705 | 0 | json_object_set_new(server_msg, "message", json_integer(uptime)); |
706 | 0 | SCReturnInt(TM_ECODE_OK); |
707 | 0 | } |
708 | | |
709 | | static TmEcode UnixManagerRunningModeCommand(json_t *cmd, |
710 | | json_t *server_msg, void *data) |
711 | 0 | { |
712 | 0 | SCEnter(); |
713 | 0 | json_object_set_new(server_msg, "message", json_string(RunmodeGetActive())); |
714 | 0 | SCReturnInt(TM_ECODE_OK); |
715 | 0 | } |
716 | | |
717 | | static TmEcode UnixManagerCaptureModeCommand(json_t *cmd, |
718 | | json_t *server_msg, void *data) |
719 | 0 | { |
720 | 0 | SCEnter(); |
721 | 0 | json_object_set_new(server_msg, "message", json_string(RunModeGetMainMode())); |
722 | 0 | SCReturnInt(TM_ECODE_OK); |
723 | 0 | } |
724 | | |
725 | | static TmEcode UnixManagerReloadRulesWrapper(json_t *cmd, json_t *server_msg, void *data, int do_wait) |
726 | 0 | { |
727 | 0 | SCEnter(); |
728 | |
|
729 | 0 | if (SuriHasSigFile()) { |
730 | 0 | json_object_set_new(server_msg, "message", |
731 | 0 | json_string("Live rule reload not possible if -s " |
732 | 0 | "or -S option used at runtime.")); |
733 | 0 | SCReturnInt(TM_ECODE_FAILED); |
734 | 0 | } |
735 | | |
736 | 0 | int r = DetectEngineReloadStart(); |
737 | |
|
738 | 0 | if (r == 0 && do_wait) { |
739 | 0 | while (!DetectEngineReloadIsIdle()) |
740 | 0 | usleep(100); |
741 | 0 | } else { |
742 | 0 | if (r == -1) { |
743 | 0 | json_object_set_new(server_msg, "message", json_string("Reload already in progress")); |
744 | 0 | SCReturnInt(TM_ECODE_FAILED); |
745 | 0 | } |
746 | 0 | } |
747 | | |
748 | 0 | json_object_set_new(server_msg, "message", json_string("done")); |
749 | 0 | SCReturnInt(TM_ECODE_OK); |
750 | 0 | } |
751 | | |
752 | | static TmEcode UnixManagerReloadRules(json_t *cmd, json_t *server_msg, void *data) |
753 | 0 | { |
754 | 0 | return UnixManagerReloadRulesWrapper(cmd, server_msg, data, 1); |
755 | 0 | } |
756 | | |
757 | | static TmEcode UnixManagerNonBlockingReloadRules(json_t *cmd, json_t *server_msg, void *data) |
758 | 0 | { |
759 | 0 | return UnixManagerReloadRulesWrapper(cmd, server_msg, data, 0); |
760 | 0 | } |
761 | | |
762 | | static TmEcode UnixManagerReloadTimeCommand(json_t *cmd, |
763 | | json_t *server_msg, void *data) |
764 | 0 | { |
765 | 0 | SCEnter(); |
766 | 0 | TmEcode retval; |
767 | 0 | json_t *jdata = NULL; |
768 | |
|
769 | 0 | retval = OutputEngineStatsReloadTime(&jdata); |
770 | 0 | json_object_set_new(server_msg, "message", jdata); |
771 | 0 | SCReturnInt(retval); |
772 | 0 | } |
773 | | |
774 | | static TmEcode UnixManagerRulesetStatsCommand(json_t *cmd, |
775 | | json_t *server_msg, void *data) |
776 | 0 | { |
777 | 0 | SCEnter(); |
778 | 0 | TmEcode retval; |
779 | 0 | json_t *jdata = NULL; |
780 | |
|
781 | 0 | retval = OutputEngineStatsRuleset(&jdata); |
782 | 0 | json_object_set_new(server_msg, "message", jdata); |
783 | 0 | SCReturnInt(retval); |
784 | 0 | } |
785 | | |
786 | | #ifdef PROFILE_RULES |
787 | | static TmEcode UnixManagerRulesetProfileCommand(json_t *cmd, json_t *server_msg, void *data) |
788 | | { |
789 | | SCEnter(); |
790 | | DetectEngineCtx *de_ctx = DetectEngineGetCurrent(); |
791 | | |
792 | | json_t *js = SCProfileRuleTriggerDump(de_ctx); |
793 | | if (js == NULL) { |
794 | | json_object_set_new(server_msg, "message", json_string("NOK")); |
795 | | SCReturnInt(TM_ECODE_FAILED); |
796 | | } |
797 | | json_object_set_new(server_msg, "message", js); |
798 | | SCReturnInt(TM_ECODE_OK); |
799 | | } |
800 | | |
801 | | static TmEcode UnixManagerRulesetProfileStartCommand(json_t *cmd, json_t *server_msg, void *data) |
802 | | { |
803 | | SCEnter(); |
804 | | SCProfileRuleStartCollection(); |
805 | | json_object_set_new(server_msg, "message", json_string("OK")); |
806 | | SCReturnInt(TM_ECODE_OK); |
807 | | } |
808 | | |
809 | | static TmEcode UnixManagerRulesetProfileStopCommand(json_t *cmd, json_t *server_msg, void *data) |
810 | | { |
811 | | SCEnter(); |
812 | | SCProfileRuleStopCollection(); |
813 | | json_object_set_new(server_msg, "message", json_string("OK")); |
814 | | SCReturnInt(TM_ECODE_OK); |
815 | | } |
816 | | #endif |
817 | | |
818 | | static TmEcode UnixManagerShowFailedRules(json_t *cmd, |
819 | | json_t *server_msg, void *data) |
820 | 0 | { |
821 | 0 | SCEnter(); |
822 | 0 | int rules_cnt = 0; |
823 | 0 | DetectEngineCtx *de_ctx = DetectEngineGetCurrent(); |
824 | 0 | if (de_ctx == NULL) { |
825 | 0 | json_object_set_new(server_msg, "message", json_string("Unable to get info")); |
826 | 0 | SCReturnInt(TM_ECODE_OK); |
827 | 0 | } |
828 | | |
829 | | /* Since we need to deference de_ctx, we don't want to lost it. */ |
830 | 0 | DetectEngineCtx *list = de_ctx; |
831 | 0 | json_t *js_sigs_array = json_array(); |
832 | |
|
833 | 0 | if (js_sigs_array == NULL) { |
834 | 0 | json_object_set_new(server_msg, "message", json_string("Unable to get info")); |
835 | 0 | goto error; |
836 | 0 | } |
837 | 0 | while (list) { |
838 | 0 | SigString *sigs_str = NULL; |
839 | 0 | TAILQ_FOREACH(sigs_str, &list->sig_stat.failed_sigs, next) { |
840 | 0 | json_t *jdata = json_object(); |
841 | 0 | if (jdata == NULL) { |
842 | 0 | json_object_set_new(server_msg, "message", json_string("Unable to get the sig")); |
843 | 0 | goto error; |
844 | 0 | } |
845 | | |
846 | 0 | json_object_set_new(jdata, "tenant_id", json_integer(list->tenant_id)); |
847 | 0 | json_object_set_new(jdata, "rule", json_string(sigs_str->sig_str)); |
848 | 0 | json_object_set_new(jdata, "filename", json_string(sigs_str->filename)); |
849 | 0 | json_object_set_new(jdata, "line", json_integer(sigs_str->line)); |
850 | 0 | if (sigs_str->sig_error) { |
851 | 0 | json_object_set_new(jdata, "error", json_string(sigs_str->sig_error)); |
852 | 0 | } |
853 | 0 | json_array_append_new(js_sigs_array, jdata); |
854 | 0 | if (++rules_cnt > MAX_FAILED_RULES) { |
855 | 0 | break; |
856 | 0 | } |
857 | 0 | } |
858 | 0 | if (rules_cnt > MAX_FAILED_RULES) { |
859 | 0 | break; |
860 | 0 | } |
861 | 0 | list = list->next; |
862 | 0 | } |
863 | | |
864 | 0 | json_object_set_new(server_msg, "message", js_sigs_array); |
865 | 0 | DetectEngineDeReference(&de_ctx); |
866 | 0 | SCReturnInt(TM_ECODE_OK); |
867 | | |
868 | 0 | error: |
869 | 0 | DetectEngineDeReference(&de_ctx); |
870 | 0 | json_object_clear(js_sigs_array); |
871 | 0 | json_decref(js_sigs_array); |
872 | 0 | SCReturnInt(TM_ECODE_FAILED); |
873 | 0 | } |
874 | | |
875 | | static TmEcode UnixManagerConfGetCommand(json_t *cmd, |
876 | | json_t *server_msg, void *data) |
877 | 0 | { |
878 | 0 | SCEnter(); |
879 | |
|
880 | 0 | const char *confval = NULL; |
881 | 0 | char *variable = NULL; |
882 | |
|
883 | 0 | json_t *jarg = json_object_get(cmd, "variable"); |
884 | 0 | if(!json_is_string(jarg)) { |
885 | 0 | SCLogInfo("error: variable is not a string"); |
886 | 0 | json_object_set_new(server_msg, "message", json_string("variable is not a string")); |
887 | 0 | SCReturnInt(TM_ECODE_FAILED); |
888 | 0 | } |
889 | | |
890 | 0 | variable = (char *)json_string_value(jarg); |
891 | 0 | if (ConfGet(variable, &confval) != 1) { |
892 | 0 | json_object_set_new(server_msg, "message", json_string("Unable to get value")); |
893 | 0 | SCReturnInt(TM_ECODE_FAILED); |
894 | 0 | } |
895 | | |
896 | 0 | if (confval) { |
897 | 0 | json_object_set_new(server_msg, "message", json_string(confval)); |
898 | 0 | SCReturnInt(TM_ECODE_OK); |
899 | 0 | } |
900 | | |
901 | 0 | json_object_set_new(server_msg, "message", json_string("No string value")); |
902 | 0 | SCReturnInt(TM_ECODE_FAILED); |
903 | 0 | } |
904 | | |
905 | | static TmEcode UnixManagerListCommand(json_t *cmd, |
906 | | json_t *answer, void *data) |
907 | 0 | { |
908 | 0 | SCEnter(); |
909 | 0 | json_t *jdata; |
910 | 0 | json_t *jarray; |
911 | 0 | Command *lcmd = NULL; |
912 | 0 | UnixCommand *gcmd = (UnixCommand *) data; |
913 | 0 | int i = 0; |
914 | |
|
915 | 0 | jdata = json_object(); |
916 | 0 | if (jdata == NULL) { |
917 | 0 | json_object_set_new(answer, "message", |
918 | 0 | json_string("internal error at json object creation")); |
919 | 0 | return TM_ECODE_FAILED; |
920 | 0 | } |
921 | 0 | jarray = json_array(); |
922 | 0 | if (jarray == NULL) { |
923 | 0 | json_object_set_new(answer, "message", |
924 | 0 | json_string("internal error at json object creation")); |
925 | 0 | return TM_ECODE_FAILED; |
926 | 0 | } |
927 | | |
928 | 0 | TAILQ_FOREACH(lcmd, &gcmd->commands, next) { |
929 | 0 | json_array_append_new(jarray, json_string(lcmd->name)); |
930 | 0 | i++; |
931 | 0 | } |
932 | |
|
933 | 0 | json_object_set_new(jdata, "count", json_integer(i)); |
934 | 0 | json_object_set_new(jdata, "commands", jarray); |
935 | 0 | json_object_set_new(answer, "message", jdata); |
936 | 0 | SCReturnInt(TM_ECODE_OK); |
937 | 0 | } |
938 | | |
939 | | static TmEcode UnixManagerReopenLogFiles(json_t *cmd, json_t *server_msg, void *data) |
940 | 0 | { |
941 | 0 | OutputNotifyFileRotation(); |
942 | 0 | json_object_set_new(server_msg, "message", json_string("done")); |
943 | 0 | SCReturnInt(TM_ECODE_OK); |
944 | 0 | } |
945 | | |
946 | | #if 0 |
947 | | TmEcode UnixManagerReloadRules(json_t *cmd, |
948 | | json_t *server_msg, void *data) |
949 | | { |
950 | | SCEnter(); |
951 | | if (suricata_ctl_flags != 0) { |
952 | | json_object_set_new(server_msg, "message", |
953 | | json_string("Live rule swap no longer possible." |
954 | | " Engine in shutdown mode.")); |
955 | | SCReturn(TM_ECODE_FAILED); |
956 | | } else { |
957 | | /* FIXME : need to check option value */ |
958 | | UtilSignalHandlerSetup(SIGUSR2, SignalHandlerSigusr2Idle); |
959 | | DetectEngineSpawnLiveRuleSwapMgmtThread(); |
960 | | json_object_set_new(server_msg, "message", json_string("Reloading rules")); |
961 | | } |
962 | | SCReturn(TM_ECODE_OK); |
963 | | } |
964 | | #endif |
965 | | |
966 | | static UnixCommand command; |
967 | | |
968 | | /** |
969 | | * \brief Add a command to the list of commands |
970 | | * |
971 | | * This function adds a command to the list of commands available |
972 | | * through the unix socket. |
973 | | * |
974 | | * When a command is received from user through the unix socket, the content |
975 | | * of 'Command' field in the JSON message is match against keyword, then the |
976 | | * Func is called. See UnixSocketAddPcapFile() for an example. |
977 | | * |
978 | | * \param keyword name of the command |
979 | | * \param Func function to run when command is received |
980 | | * \param data a pointer to data that are passed to Func when it is run |
981 | | * \param flags a flag now used to tune the command type |
982 | | * \retval TM_ECODE_OK in case of success, TM_ECODE_FAILED in case of failure |
983 | | */ |
984 | | TmEcode UnixManagerRegisterCommand(const char * keyword, |
985 | | TmEcode (*Func)(json_t *, json_t *, void *), |
986 | | void *data, int flags) |
987 | 0 | { |
988 | 0 | SCEnter(); |
989 | 0 | Command *cmd = NULL; |
990 | 0 | Command *lcmd = NULL; |
991 | |
|
992 | 0 | if (Func == NULL) { |
993 | 0 | SCLogError("Null function"); |
994 | 0 | SCReturnInt(TM_ECODE_FAILED); |
995 | 0 | } |
996 | | |
997 | 0 | if (keyword == NULL) { |
998 | 0 | SCLogError("Null keyword"); |
999 | 0 | SCReturnInt(TM_ECODE_FAILED); |
1000 | 0 | } |
1001 | | |
1002 | 0 | TAILQ_FOREACH(lcmd, &command.commands, next) { |
1003 | 0 | if (!strcmp(keyword, lcmd->name)) { |
1004 | 0 | SCLogError("%s already registered", keyword); |
1005 | 0 | SCReturnInt(TM_ECODE_FAILED); |
1006 | 0 | } |
1007 | 0 | } |
1008 | | |
1009 | 0 | cmd = SCMalloc(sizeof(Command)); |
1010 | 0 | if (unlikely(cmd == NULL)) { |
1011 | 0 | SCLogError("Can't alloc cmd"); |
1012 | 0 | SCReturnInt(TM_ECODE_FAILED); |
1013 | 0 | } |
1014 | 0 | cmd->name = SCStrdup(keyword); |
1015 | 0 | if (unlikely(cmd->name == NULL)) { |
1016 | 0 | SCLogError("Can't alloc cmd name"); |
1017 | 0 | SCFree(cmd); |
1018 | 0 | SCReturnInt(TM_ECODE_FAILED); |
1019 | 0 | } |
1020 | 0 | cmd->Func = Func; |
1021 | 0 | cmd->data = data; |
1022 | 0 | cmd->flags = flags; |
1023 | | /* Add it to the list */ |
1024 | 0 | TAILQ_INSERT_TAIL(&command.commands, cmd, next); |
1025 | |
|
1026 | 0 | SCReturnInt(TM_ECODE_OK); |
1027 | 0 | } |
1028 | | |
1029 | | /** |
1030 | | * \brief Add a task to the list of tasks |
1031 | | * |
1032 | | * This function adds a task to run in the background. The task is run |
1033 | | * each time the UnixMain() function exits from select. |
1034 | | * |
1035 | | * \param Func function to run when a command is received |
1036 | | * \param data a pointer to data that are passed to Func when it is run |
1037 | | * \retval TM_ECODE_OK in case of success, TM_ECODE_FAILED in case of failure |
1038 | | */ |
1039 | | TmEcode UnixManagerRegisterBackgroundTask(TmEcode (*Func)(void *), |
1040 | | void *data) |
1041 | 0 | { |
1042 | 0 | SCEnter(); |
1043 | 0 | Task *task = NULL; |
1044 | |
|
1045 | 0 | if (Func == NULL) { |
1046 | 0 | SCLogError("Null function"); |
1047 | 0 | SCReturnInt(TM_ECODE_FAILED); |
1048 | 0 | } |
1049 | | |
1050 | 0 | task = SCMalloc(sizeof(Task)); |
1051 | 0 | if (unlikely(task == NULL)) { |
1052 | 0 | SCLogError("Can't alloc task"); |
1053 | 0 | SCReturnInt(TM_ECODE_FAILED); |
1054 | 0 | } |
1055 | 0 | task->Func = Func; |
1056 | 0 | task->data = data; |
1057 | | /* Add it to the list */ |
1058 | 0 | TAILQ_INSERT_TAIL(&command.tasks, task, next); |
1059 | |
|
1060 | 0 | SCReturnInt(TM_ECODE_OK); |
1061 | 0 | } |
1062 | | |
1063 | | int UnixManagerInit(void) |
1064 | 0 | { |
1065 | 0 | if (UnixNew(&command) == 0) { |
1066 | 0 | int failure_fatal = 0; |
1067 | 0 | if (ConfGetBool("engine.init-failure-fatal", &failure_fatal) != 1) { |
1068 | 0 | SCLogDebug("ConfGetBool could not load the value."); |
1069 | 0 | } |
1070 | 0 | if (failure_fatal) { |
1071 | 0 | FatalError("Unable to create unix command socket"); |
1072 | 0 | } else { |
1073 | 0 | SCLogWarning("Unable to create unix command socket"); |
1074 | 0 | return -1; |
1075 | 0 | } |
1076 | 0 | } |
1077 | | |
1078 | | /* Init Unix socket */ |
1079 | 0 | UnixManagerRegisterCommand("shutdown", UnixManagerShutdownCommand, NULL, 0); |
1080 | 0 | UnixManagerRegisterCommand("command-list", UnixManagerListCommand, &command, 0); |
1081 | 0 | UnixManagerRegisterCommand("help", UnixManagerListCommand, &command, 0); |
1082 | 0 | UnixManagerRegisterCommand("version", UnixManagerVersionCommand, &command, 0); |
1083 | 0 | UnixManagerRegisterCommand("uptime", UnixManagerUptimeCommand, &command, 0); |
1084 | 0 | UnixManagerRegisterCommand("running-mode", UnixManagerRunningModeCommand, &command, 0); |
1085 | 0 | UnixManagerRegisterCommand("capture-mode", UnixManagerCaptureModeCommand, &command, 0); |
1086 | 0 | UnixManagerRegisterCommand("conf-get", UnixManagerConfGetCommand, &command, UNIX_CMD_TAKE_ARGS); |
1087 | 0 | UnixManagerRegisterCommand("dump-counters", StatsOutputCounterSocket, NULL, 0); |
1088 | 0 | UnixManagerRegisterCommand("reload-rules", UnixManagerReloadRules, NULL, 0); |
1089 | 0 | UnixManagerRegisterCommand("ruleset-reload-rules", UnixManagerReloadRules, NULL, 0); |
1090 | 0 | UnixManagerRegisterCommand("ruleset-reload-nonblocking", UnixManagerNonBlockingReloadRules, NULL, 0); |
1091 | 0 | UnixManagerRegisterCommand("ruleset-reload-time", UnixManagerReloadTimeCommand, NULL, 0); |
1092 | 0 | UnixManagerRegisterCommand("ruleset-stats", UnixManagerRulesetStatsCommand, NULL, 0); |
1093 | 0 | UnixManagerRegisterCommand("ruleset-failed-rules", UnixManagerShowFailedRules, NULL, 0); |
1094 | | #ifdef PROFILE_RULES |
1095 | | UnixManagerRegisterCommand("ruleset-profile", UnixManagerRulesetProfileCommand, NULL, 0); |
1096 | | UnixManagerRegisterCommand( |
1097 | | "ruleset-profile-start", UnixManagerRulesetProfileStartCommand, NULL, 0); |
1098 | | UnixManagerRegisterCommand( |
1099 | | "ruleset-profile-stop", UnixManagerRulesetProfileStopCommand, NULL, 0); |
1100 | | #endif |
1101 | 0 | UnixManagerRegisterCommand("register-tenant-handler", UnixSocketRegisterTenantHandler, &command, UNIX_CMD_TAKE_ARGS); |
1102 | 0 | UnixManagerRegisterCommand("unregister-tenant-handler", UnixSocketUnregisterTenantHandler, &command, UNIX_CMD_TAKE_ARGS); |
1103 | 0 | UnixManagerRegisterCommand("register-tenant", UnixSocketRegisterTenant, &command, UNIX_CMD_TAKE_ARGS); |
1104 | 0 | UnixManagerRegisterCommand("reload-tenant", UnixSocketReloadTenant, &command, UNIX_CMD_TAKE_ARGS); |
1105 | 0 | UnixManagerRegisterCommand("reload-tenants", UnixSocketReloadTenants, &command, 0); |
1106 | 0 | UnixManagerRegisterCommand("unregister-tenant", UnixSocketUnregisterTenant, &command, UNIX_CMD_TAKE_ARGS); |
1107 | 0 | UnixManagerRegisterCommand("add-hostbit", UnixSocketHostbitAdd, &command, UNIX_CMD_TAKE_ARGS); |
1108 | 0 | UnixManagerRegisterCommand("remove-hostbit", UnixSocketHostbitRemove, &command, UNIX_CMD_TAKE_ARGS); |
1109 | 0 | UnixManagerRegisterCommand("list-hostbit", UnixSocketHostbitList, &command, UNIX_CMD_TAKE_ARGS); |
1110 | 0 | UnixManagerRegisterCommand("reopen-log-files", UnixManagerReopenLogFiles, NULL, 0); |
1111 | 0 | UnixManagerRegisterCommand("memcap-set", UnixSocketSetMemcap, &command, UNIX_CMD_TAKE_ARGS); |
1112 | 0 | UnixManagerRegisterCommand("memcap-show", UnixSocketShowMemcap, &command, UNIX_CMD_TAKE_ARGS); |
1113 | 0 | UnixManagerRegisterCommand("memcap-list", UnixSocketShowAllMemcap, NULL, 0); |
1114 | |
|
1115 | 0 | UnixManagerRegisterCommand("dataset-add", UnixSocketDatasetAdd, &command, UNIX_CMD_TAKE_ARGS); |
1116 | 0 | UnixManagerRegisterCommand("dataset-remove", UnixSocketDatasetRemove, &command, UNIX_CMD_TAKE_ARGS); |
1117 | 0 | UnixManagerRegisterCommand( |
1118 | 0 | "get-flow-stats-by-id", UnixSocketGetFlowStatsById, &command, UNIX_CMD_TAKE_ARGS); |
1119 | 0 | UnixManagerRegisterCommand("dataset-dump", UnixSocketDatasetDump, NULL, 0); |
1120 | 0 | UnixManagerRegisterCommand( |
1121 | 0 | "dataset-clear", UnixSocketDatasetClear, &command, UNIX_CMD_TAKE_ARGS); |
1122 | 0 | UnixManagerRegisterCommand( |
1123 | 0 | "dataset-lookup", UnixSocketDatasetLookup, &command, UNIX_CMD_TAKE_ARGS); |
1124 | |
|
1125 | 0 | return 0; |
1126 | 0 | } |
1127 | | |
1128 | | typedef struct UnixManagerThreadData_ { |
1129 | | int padding; |
1130 | | } UnixManagerThreadData; |
1131 | | |
1132 | | static TmEcode UnixManagerThreadInit(ThreadVars *t, const void *initdata, void **data) |
1133 | 0 | { |
1134 | 0 | UnixManagerThreadData *utd = SCCalloc(1, sizeof(*utd)); |
1135 | 0 | if (utd == NULL) |
1136 | 0 | return TM_ECODE_FAILED; |
1137 | | |
1138 | 0 | *data = utd; |
1139 | 0 | return TM_ECODE_OK; |
1140 | 0 | } |
1141 | | |
1142 | | static TmEcode UnixManagerThreadDeinit(ThreadVars *t, void *data) |
1143 | 0 | { |
1144 | 0 | SCFree(data); |
1145 | 0 | return TM_ECODE_OK; |
1146 | 0 | } |
1147 | | |
1148 | | static TmEcode UnixManager(ThreadVars *th_v, void *thread_data) |
1149 | 0 | { |
1150 | 0 | int ret; |
1151 | | |
1152 | | /* set the thread name */ |
1153 | 0 | SCLogDebug("%s started...", th_v->name); |
1154 | |
|
1155 | 0 | StatsSetupPrivate(th_v); |
1156 | | |
1157 | | /* Set the threads capability */ |
1158 | 0 | th_v->cap_flags = 0; |
1159 | 0 | SCDropCaps(th_v); |
1160 | |
|
1161 | 0 | TmThreadsSetFlag(th_v, THV_INIT_DONE | THV_RUNNING); |
1162 | |
|
1163 | 0 | while (1) { |
1164 | 0 | ret = UnixMain(&command); |
1165 | 0 | if (ret == 0) { |
1166 | 0 | SCLogError("Fatal error on unix socket"); |
1167 | 0 | } |
1168 | |
|
1169 | 0 | if ((ret == 0) || (TmThreadsCheckFlag(th_v, THV_KILL))) { |
1170 | 0 | UnixClient *item; |
1171 | 0 | UnixClient *titem; |
1172 | 0 | TAILQ_FOREACH_SAFE(item, &(&command)->clients, next, titem) { |
1173 | 0 | close(item->fd); |
1174 | 0 | SCFree(item); |
1175 | 0 | } |
1176 | 0 | StatsSyncCounters(th_v); |
1177 | 0 | break; |
1178 | 0 | } |
1179 | | |
1180 | 0 | UnixCommandBackgroundTasks(&command); |
1181 | 0 | } |
1182 | 0 | return TM_ECODE_OK; |
1183 | 0 | } |
1184 | | |
1185 | | |
1186 | | /** \brief Spawn the unix socket manager thread |
1187 | | * |
1188 | | * \param mode if set to 1, init failure cause suricata exit |
1189 | | * */ |
1190 | | void UnixManagerThreadSpawn(int mode) |
1191 | 0 | { |
1192 | 0 | ThreadVars *tv_unixmgr = NULL; |
1193 | |
|
1194 | 0 | SCCtrlCondInit(&unix_manager_ctrl_cond, NULL); |
1195 | 0 | SCCtrlMutexInit(&unix_manager_ctrl_mutex, NULL); |
1196 | |
|
1197 | 0 | tv_unixmgr = TmThreadCreateCmdThreadByName(thread_name_unix_socket, |
1198 | 0 | "UnixManager", 0); |
1199 | |
|
1200 | 0 | if (tv_unixmgr == NULL) { |
1201 | 0 | FatalError("TmThreadsCreate failed"); |
1202 | 0 | } |
1203 | 0 | if (TmThreadSpawn(tv_unixmgr) != TM_ECODE_OK) { |
1204 | 0 | FatalError("TmThreadSpawn failed"); |
1205 | 0 | } |
1206 | 0 | if (mode == 1) { |
1207 | 0 | if (TmThreadsCheckFlag(tv_unixmgr, THV_RUNNING_DONE)) { |
1208 | 0 | FatalError("Unix socket init failed"); |
1209 | 0 | } |
1210 | 0 | } |
1211 | 0 | return; |
1212 | 0 | } |
1213 | | |
1214 | | // TODO can't think of a good name |
1215 | | void UnixManagerThreadSpawnNonRunmode(const bool unix_socket) |
1216 | 0 | { |
1217 | | /* Spawn the unix socket manager thread */ |
1218 | 0 | if (unix_socket) { |
1219 | 0 | if (UnixManagerInit() == 0) { |
1220 | 0 | UnixManagerRegisterCommand("iface-stat", LiveDeviceIfaceStat, NULL, |
1221 | 0 | UNIX_CMD_TAKE_ARGS); |
1222 | 0 | UnixManagerRegisterCommand("iface-list", LiveDeviceIfaceList, NULL, 0); |
1223 | 0 | UnixManagerRegisterCommand("iface-bypassed-stat", |
1224 | 0 | LiveDeviceGetBypassedStats, NULL, 0); |
1225 | | /* For backward compatibility */ |
1226 | 0 | UnixManagerRegisterCommand("ebpf-bypassed-stat", |
1227 | 0 | LiveDeviceGetBypassedStats, NULL, 0); |
1228 | 0 | UnixManagerThreadSpawn(0); |
1229 | 0 | } |
1230 | 0 | } |
1231 | 0 | } |
1232 | | |
1233 | | /** |
1234 | | * \brief Used to kill unix manager thread(s). |
1235 | | * |
1236 | | * \todo Kinda hackish since it uses the tv name to identify unix manager |
1237 | | * thread. We need an all weather identification scheme. |
1238 | | */ |
1239 | | void UnixSocketKillSocketThread(void) |
1240 | 0 | { |
1241 | 0 | ThreadVars *tv = NULL; |
1242 | |
|
1243 | 0 | again: |
1244 | 0 | SCMutexLock(&tv_root_lock); |
1245 | | |
1246 | | /* unix manager thread(s) is/are a part of command threads */ |
1247 | 0 | tv = tv_root[TVT_CMD]; |
1248 | |
|
1249 | 0 | while (tv != NULL) { |
1250 | 0 | if (strcasecmp(tv->name, "UnixManagerThread") == 0) { |
1251 | | /* If the thread dies during init it will have |
1252 | | * THV_RUNNING_DONE set, so we can set the correct flag |
1253 | | * and exit. |
1254 | | */ |
1255 | 0 | if (TmThreadsCheckFlag(tv, THV_RUNNING_DONE)) { |
1256 | 0 | TmThreadsSetFlag(tv, THV_KILL); |
1257 | 0 | TmThreadsSetFlag(tv, THV_DEINIT); |
1258 | 0 | TmThreadsSetFlag(tv, THV_CLOSED); |
1259 | 0 | break; |
1260 | 0 | } |
1261 | 0 | TmThreadsSetFlag(tv, THV_KILL); |
1262 | 0 | TmThreadsSetFlag(tv, THV_DEINIT); |
1263 | | /* Be sure it has shut down */ |
1264 | 0 | if (!TmThreadsCheckFlag(tv, THV_CLOSED)) { |
1265 | 0 | SCMutexUnlock(&tv_root_lock); |
1266 | 0 | usleep(100); |
1267 | 0 | goto again; |
1268 | 0 | } |
1269 | 0 | } |
1270 | 0 | tv = tv->next; |
1271 | 0 | } |
1272 | | |
1273 | 0 | SCMutexUnlock(&tv_root_lock); |
1274 | 0 | return; |
1275 | 0 | } |
1276 | | |
1277 | | #else /* BUILD_UNIX_SOCKET */ |
1278 | | |
1279 | | void UnixManagerThreadSpawn(int mode) |
1280 | | { |
1281 | | SCLogError("Unix socket is not compiled"); |
1282 | | return; |
1283 | | } |
1284 | | |
1285 | | void UnixSocketKillSocketThread(void) |
1286 | | { |
1287 | | return; |
1288 | | } |
1289 | | |
1290 | | void UnixManagerThreadSpawnNonRunmode(const bool unix_socket_enabled) |
1291 | | { |
1292 | | return; |
1293 | | } |
1294 | | |
1295 | | #endif /* BUILD_UNIX_SOCKET */ |
1296 | | |
1297 | | void TmModuleUnixManagerRegister (void) |
1298 | 71 | { |
1299 | 71 | #if defined(BUILD_UNIX_SOCKET) && defined(HAVE_SYS_UN_H) && defined(HAVE_SYS_STAT_H) && defined(HAVE_SYS_TYPES_H) |
1300 | 71 | tmm_modules[TMM_UNIXMANAGER].name = "UnixManager"; |
1301 | 71 | tmm_modules[TMM_UNIXMANAGER].ThreadInit = UnixManagerThreadInit; |
1302 | 71 | tmm_modules[TMM_UNIXMANAGER].ThreadDeinit = UnixManagerThreadDeinit; |
1303 | 71 | tmm_modules[TMM_UNIXMANAGER].Management = UnixManager; |
1304 | 71 | tmm_modules[TMM_UNIXMANAGER].cap_flags = 0; |
1305 | 71 | tmm_modules[TMM_UNIXMANAGER].flags = TM_FLAG_COMMAND_TM; |
1306 | 71 | #endif /* BUILD_UNIX_SOCKET */ |
1307 | 71 | } |