/src/suricata7/src/output-filedata.c
Line | Count | Source |
1 | | /* Copyright (C) 2007-2022 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 Victor Julien <victor@inliniac.net> |
22 | | * |
23 | | * AppLayer Filedata Logger Output registration functions |
24 | | */ |
25 | | |
26 | | #include "suricata-common.h" |
27 | | #include "output.h" |
28 | | #include "output-filedata.h" |
29 | | #include "app-layer-parser.h" |
30 | | #include "detect-filemagic.h" |
31 | | #include "conf.h" |
32 | | #include "util-profiling.h" |
33 | | #include "util-validate.h" |
34 | | #include "util-magic.h" |
35 | | #include "util-path.h" |
36 | | |
37 | | bool g_filedata_logger_enabled = false; |
38 | | |
39 | | /* logger instance, a module + a output ctx, |
40 | | * it's perfectly valid that have multiple instances of the same |
41 | | * log module (e.g. http.log) with different output ctx'. */ |
42 | | typedef struct OutputFiledataLogger_ { |
43 | | FiledataLogger LogFunc; |
44 | | OutputCtx *output_ctx; |
45 | | struct OutputFiledataLogger_ *next; |
46 | | const char *name; |
47 | | LoggerId logger_id; |
48 | | ThreadInitFunc ThreadInit; |
49 | | ThreadDeinitFunc ThreadDeinit; |
50 | | ThreadExitPrintStatsFunc ThreadExitPrintStats; |
51 | | } OutputFiledataLogger; |
52 | | |
53 | | static OutputFiledataLogger *list = NULL; |
54 | | |
55 | | int OutputRegisterFiledataLogger(LoggerId id, const char *name, |
56 | | FiledataLogger LogFunc, OutputCtx *output_ctx, ThreadInitFunc ThreadInit, |
57 | | ThreadDeinitFunc ThreadDeinit, |
58 | | ThreadExitPrintStatsFunc ThreadExitPrintStats) |
59 | 2 | { |
60 | 2 | OutputFiledataLogger *op = SCMalloc(sizeof(*op)); |
61 | 2 | if (op == NULL) |
62 | 0 | return -1; |
63 | 2 | memset(op, 0x00, sizeof(*op)); |
64 | | |
65 | 2 | op->LogFunc = LogFunc; |
66 | 2 | op->output_ctx = output_ctx; |
67 | 2 | op->name = name; |
68 | 2 | op->logger_id = id; |
69 | 2 | op->ThreadInit = ThreadInit; |
70 | 2 | op->ThreadDeinit = ThreadDeinit; |
71 | 2 | op->ThreadExitPrintStats = ThreadExitPrintStats; |
72 | | |
73 | 2 | if (list == NULL) |
74 | 2 | list = op; |
75 | 0 | else { |
76 | 0 | OutputFiledataLogger *t = list; |
77 | 0 | while (t->next) |
78 | 0 | t = t->next; |
79 | 0 | t->next = op; |
80 | 0 | } |
81 | | |
82 | 2 | SCLogDebug("OutputRegisterFiledataLogger happy"); |
83 | 2 | g_filedata_logger_enabled = true; |
84 | 2 | return 0; |
85 | 2 | } |
86 | | |
87 | | SC_ATOMIC_DECLARE(unsigned int, g_file_store_id); |
88 | | |
89 | | static int CallLoggers(ThreadVars *tv, OutputLoggerThreadStore *store_list, Packet *p, File *ff, |
90 | | void *tx, const uint64_t tx_id, const uint8_t *data, uint32_t data_len, uint8_t flags, |
91 | | uint8_t dir) |
92 | 2.24M | { |
93 | 2.24M | OutputFiledataLogger *logger = list; |
94 | 2.24M | OutputLoggerThreadStore *store = store_list; |
95 | 2.24M | int file_logged = 0; |
96 | | |
97 | 4.48M | while (logger && store) { |
98 | 2.24M | DEBUG_VALIDATE_BUG_ON(logger->LogFunc == NULL); |
99 | | |
100 | 2.24M | SCLogDebug("logger %p", logger); |
101 | 2.24M | PACKET_PROFILING_LOGGER_START(p, logger->logger_id); |
102 | 2.24M | logger->LogFunc(tv, store->thread_data, (const Packet *)p, ff, tx, tx_id, data, data_len, |
103 | 2.24M | flags, dir); |
104 | 2.24M | PACKET_PROFILING_LOGGER_END(p, logger->logger_id); |
105 | | |
106 | 2.24M | file_logged = 1; |
107 | | |
108 | 2.24M | logger = logger->next; |
109 | 2.24M | store = store->next; |
110 | | |
111 | 2.24M | DEBUG_VALIDATE_BUG_ON(logger == NULL && store != NULL); |
112 | 2.24M | DEBUG_VALIDATE_BUG_ON(logger != NULL && store == NULL); |
113 | 2.24M | } |
114 | 2.24M | return file_logged; |
115 | 2.24M | } |
116 | | |
117 | | static void CloseFile(const Packet *p, Flow *f, File *file, void *txv) |
118 | 52.2k | { |
119 | 52.2k | DEBUG_VALIDATE_BUG_ON((file->flags & FILE_STORED) != 0); |
120 | | |
121 | 52.2k | AppLayerTxData *txd = AppLayerParserGetTxData(f->proto, f->alproto, txv); |
122 | 52.2k | if (txd) { |
123 | 52.2k | BUG_ON(f->alproto == ALPROTO_SMB && txd->files_logged != 0); |
124 | 52.2k | txd->files_stored++; |
125 | 52.2k | } |
126 | 52.2k | file->flags |= FILE_STORED; |
127 | 52.2k | } |
128 | | |
129 | | void OutputFiledataLogFfc(ThreadVars *tv, OutputFiledataLoggerThreadData *td, Packet *p, |
130 | | AppLayerGetFileState files, void *txv, const uint64_t tx_id, AppLayerTxData *txd, |
131 | | const uint8_t call_flags, const bool file_close, const bool file_trunc, const uint8_t dir) |
132 | 3.96M | { |
133 | 3.96M | SCLogDebug("ffc %p", files.fc); |
134 | | |
135 | 3.96M | OutputLoggerThreadStore *store = td->store; |
136 | 6.20M | for (File *ff = files.fc->head; ff != NULL; ff = ff->next) { |
137 | 2.24M | FileApplyTxFlags(txd, dir, ff); |
138 | 2.24M | FilePrintFlags(ff); |
139 | | |
140 | 2.24M | uint8_t file_flags = call_flags; |
141 | | #ifdef HAVE_MAGIC |
142 | | if (FileForceMagic() && ff->magic == NULL) { |
143 | | FilemagicThreadLookup(&td->magic_ctx, ff); |
144 | | } |
145 | | #endif |
146 | 2.24M | if (ff->flags & FILE_STORED) { |
147 | 6 | continue; |
148 | 6 | } |
149 | | |
150 | 2.24M | if (!(ff->flags & FILE_STORE)) { |
151 | 0 | continue; |
152 | 0 | } |
153 | | |
154 | | /* if file_store_id == 0, this is the first store of this file */ |
155 | 2.24M | if (ff->file_store_id == 0) { |
156 | | /* new file */ |
157 | 169k | ff->file_store_id = SC_ATOMIC_ADD(g_file_store_id, 1); |
158 | 169k | file_flags |= OUTPUT_FILEDATA_FLAG_OPEN; |
159 | 169k | } |
160 | | |
161 | | /* if we have no data chunks left to log, we should still |
162 | | * close the logger(s) */ |
163 | 2.24M | if (FileDataSize(ff) == ff->content_stored && (file_trunc || file_close)) { |
164 | 1.73k | if (ff->state < FILE_STATE_CLOSED) { |
165 | 1.17k | FileCloseFilePtr(ff, files.cfg, NULL, 0, FILE_TRUNCATED); |
166 | 1.17k | } |
167 | 1.73k | file_flags |= OUTPUT_FILEDATA_FLAG_CLOSE; |
168 | 1.73k | CallLoggers(tv, store, p, ff, txv, tx_id, NULL, 0, file_flags, dir); |
169 | 1.73k | CloseFile(p, p->flow, ff, txv); |
170 | 1.73k | continue; |
171 | 1.73k | } |
172 | | |
173 | | /* if file needs to be closed or truncated, inform |
174 | | * loggers */ |
175 | 2.23M | if ((file_close || file_trunc) && ff->state < FILE_STATE_CLOSED) { |
176 | 1.27k | FileCloseFilePtr(ff, files.cfg, NULL, 0, FILE_TRUNCATED); |
177 | 1.27k | } |
178 | | |
179 | | /* tell the logger we're closing up */ |
180 | 2.23M | if (ff->state >= FILE_STATE_CLOSED) |
181 | 157k | file_flags |= OUTPUT_FILEDATA_FLAG_CLOSE; |
182 | | |
183 | | /* do the actual logging */ |
184 | 2.23M | const uint8_t *data = NULL; |
185 | 2.23M | uint32_t data_len = 0; |
186 | | |
187 | 2.23M | StreamingBufferGetDataAtOffset(ff->sb, &data, &data_len, ff->content_stored); |
188 | | |
189 | 2.23M | const int file_logged = |
190 | 2.23M | CallLoggers(tv, store, p, ff, txv, tx_id, data, data_len, file_flags, dir); |
191 | 2.23M | if (file_logged) { |
192 | 2.23M | ff->content_stored += data_len; |
193 | | |
194 | | /* all done */ |
195 | 2.23M | if (file_flags & OUTPUT_FILEDATA_FLAG_CLOSE) { |
196 | 157k | CloseFile(p, p->flow, ff, txv); |
197 | 157k | } |
198 | 2.23M | } |
199 | 2.23M | } |
200 | 3.96M | } |
201 | | |
202 | | /** \brief thread init for the filedata logger |
203 | | * This will run the thread init functions for the individual registered |
204 | | * loggers */ |
205 | | TmEcode OutputFiledataLogThreadInit(ThreadVars *tv, OutputFiledataLoggerThreadData **data) |
206 | 4 | { |
207 | 4 | OutputFiledataLoggerThreadData *td = SCMalloc(sizeof(*td)); |
208 | 4 | if (td == NULL) |
209 | 0 | return TM_ECODE_FAILED; |
210 | 4 | memset(td, 0x00, sizeof(*td)); |
211 | 4 | *data = td; |
212 | | |
213 | | #ifdef HAVE_MAGIC |
214 | | td->magic_ctx = MagicInitContext(); |
215 | | if (td->magic_ctx == NULL) { |
216 | | SCFree(td); |
217 | | return TM_ECODE_FAILED; |
218 | | } |
219 | | #endif |
220 | | |
221 | 4 | SCLogDebug("OutputFiledataLogThreadInit happy (*data %p)", *data); |
222 | | |
223 | 4 | OutputFiledataLogger *logger = list; |
224 | 8 | while (logger) { |
225 | 4 | if (logger->ThreadInit) { |
226 | 4 | void *retptr = NULL; |
227 | 4 | if (logger->ThreadInit(tv, (void *)logger->output_ctx, &retptr) == TM_ECODE_OK) { |
228 | 4 | OutputLoggerThreadStore *ts = SCMalloc(sizeof(*ts)); |
229 | 4 | /* todo */ BUG_ON(ts == NULL); |
230 | 4 | memset(ts, 0x00, sizeof(*ts)); |
231 | | |
232 | | /* store thread handle */ |
233 | 4 | ts->thread_data = retptr; |
234 | | |
235 | 4 | if (td->store == NULL) { |
236 | 4 | td->store = ts; |
237 | 4 | } else { |
238 | 0 | OutputLoggerThreadStore *tmp = td->store; |
239 | 0 | while (tmp->next != NULL) |
240 | 0 | tmp = tmp->next; |
241 | 0 | tmp->next = ts; |
242 | 0 | } |
243 | | |
244 | 4 | SCLogDebug("%s is now set up", logger->name); |
245 | 4 | } |
246 | 4 | } |
247 | | |
248 | 4 | logger = logger->next; |
249 | 4 | } |
250 | 4 | return TM_ECODE_OK; |
251 | 4 | } |
252 | | |
253 | | TmEcode OutputFiledataLogThreadDeinit( |
254 | | ThreadVars *tv, OutputFiledataLoggerThreadData *op_thread_data) |
255 | 0 | { |
256 | 0 | OutputLoggerThreadStore *store = op_thread_data->store; |
257 | 0 | OutputFiledataLogger *logger = list; |
258 | |
|
259 | 0 | while (logger && store) { |
260 | 0 | if (logger->ThreadDeinit) { |
261 | 0 | logger->ThreadDeinit(tv, store->thread_data); |
262 | 0 | } |
263 | |
|
264 | 0 | OutputLoggerThreadStore *next_store = store->next; |
265 | 0 | SCFree(store); |
266 | 0 | store = next_store; |
267 | 0 | logger = logger->next; |
268 | 0 | } |
269 | |
|
270 | | #ifdef HAVE_MAGIC |
271 | | MagicDeinitContext(op_thread_data->magic_ctx); |
272 | | #endif |
273 | |
|
274 | 0 | SCFree(op_thread_data); |
275 | 0 | return TM_ECODE_OK; |
276 | 0 | } |
277 | | |
278 | | void OutputFiledataLoggerRegister(void) |
279 | 71 | { |
280 | 71 | SC_ATOMIC_INIT(g_file_store_id); |
281 | 71 | SC_ATOMIC_SET(g_file_store_id, 1); |
282 | 71 | } |
283 | | |
284 | | void OutputFiledataShutdown(void) |
285 | 0 | { |
286 | 0 | OutputFiledataLogger *logger = list; |
287 | 0 | while (logger) { |
288 | 0 | OutputFiledataLogger *next_logger = logger->next; |
289 | 0 | SCFree(logger); |
290 | 0 | logger = next_logger; |
291 | 0 | } |
292 | |
|
293 | | list = NULL; |
294 | 0 | } |