]> git.ipfire.org Git - people/ms/suricata.git/blob - src/app-layer-template.c
911e98e53d5cd496f43d31a72db189eb4f6ca861
[people/ms/suricata.git] / src / app-layer-template.c
1 /* Copyright (C) 2015-2020 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 * TODO: Update \author in this file and app-layer-template.h.
20 * TODO: Implement your app-layer logic with unit tests.
21 * TODO: Remove SCLogNotice statements or convert to debug.
22 */
23
24 /**
25 * \file
26 *
27 * \author FirstName LastName <yourname@domain>
28 *
29 * Template application layer detector and parser for learning and
30 * template purposes.
31 *
32 * This template implements a simple application layer for something
33 * like the echo protocol running on port 7.
34 */
35
36 #include "suricata-common.h"
37 #include "stream.h"
38 #include "conf.h"
39 #include "app-layer-detect-proto.h"
40 #include "app-layer-parser.h"
41 #include "app-layer-template.h"
42
43 #include "util-unittest.h"
44 #include "util-validate.h"
45
46 /* The default port to probe for echo traffic if not provided in the
47 * configuration file. */
48 #define TEMPLATE_DEFAULT_PORT "7"
49
50 /* The minimum size for a message. For some protocols this might
51 * be the size of a header. */
52 #define TEMPLATE_MIN_FRAME_LEN 1
53
54 /* Enum of app-layer events for the protocol. Normally you might
55 * have events for errors in parsing data, like unexpected data being
56 * received. For template we'll make something up, and log an app-layer
57 * level alert if an empty message is received.
58 *
59 * Example rule:
60 *
61 * alert template any any -> any any (msg:"SURICATA Template empty message"; \
62 * app-layer-event:template.empty_message; sid:X; rev:Y;)
63 */
64 enum {
65 TEMPLATE_DECODER_EVENT_EMPTY_MESSAGE,
66 };
67
68 SCEnumCharMap template_decoder_event_table[] = {
69 {"EMPTY_MESSAGE", TEMPLATE_DECODER_EVENT_EMPTY_MESSAGE},
70
71 // event table must be NULL-terminated
72 { NULL, -1 },
73 };
74
75 static TemplateTransaction *TemplateTxAlloc(TemplateState *state)
76 {
77 TemplateTransaction *tx = SCCalloc(1, sizeof(TemplateTransaction));
78 if (unlikely(tx == NULL)) {
79 return NULL;
80 }
81
82 /* Increment the transaction ID on the state each time one is
83 * allocated. */
84 tx->tx_id = state->transaction_max++;
85
86 TAILQ_INSERT_TAIL(&state->tx_list, tx, next);
87
88 return tx;
89 }
90
91 static void TemplateTxFree(void *txv)
92 {
93 TemplateTransaction *tx = txv;
94
95 if (tx->request_buffer != NULL) {
96 SCFree(tx->request_buffer);
97 }
98
99 if (tx->response_buffer != NULL) {
100 SCFree(tx->response_buffer);
101 }
102
103 AppLayerDecoderEventsFreeEvents(&tx->decoder_events);
104
105 SCFree(tx);
106 }
107
108 static void *TemplateStateAlloc(void *orig_state, AppProto proto_orig)
109 {
110 SCLogNotice("Allocating template state.");
111 TemplateState *state = SCCalloc(1, sizeof(TemplateState));
112 if (unlikely(state == NULL)) {
113 return NULL;
114 }
115 TAILQ_INIT(&state->tx_list);
116 return state;
117 }
118
119 static void TemplateStateFree(void *state)
120 {
121 TemplateState *template_state = state;
122 TemplateTransaction *tx;
123 SCLogNotice("Freeing template state.");
124 while ((tx = TAILQ_FIRST(&template_state->tx_list)) != NULL) {
125 TAILQ_REMOVE(&template_state->tx_list, tx, next);
126 TemplateTxFree(tx);
127 }
128 SCFree(template_state);
129 }
130
131 /**
132 * \brief Callback from the application layer to have a transaction freed.
133 *
134 * \param state a void pointer to the TemplateState object.
135 * \param tx_id the transaction ID to free.
136 */
137 static void TemplateStateTxFree(void *statev, uint64_t tx_id)
138 {
139 TemplateState *state = statev;
140 TemplateTransaction *tx = NULL, *ttx;
141
142 SCLogNotice("Freeing transaction %"PRIu64, tx_id);
143
144 TAILQ_FOREACH_SAFE(tx, &state->tx_list, next, ttx) {
145
146 /* Continue if this is not the transaction we are looking
147 * for. */
148 if (tx->tx_id != tx_id) {
149 continue;
150 }
151
152 /* Remove and free the transaction. */
153 TAILQ_REMOVE(&state->tx_list, tx, next);
154 TemplateTxFree(tx);
155 return;
156 }
157
158 SCLogNotice("Transaction %"PRIu64" not found.", tx_id);
159 }
160
161 static int TemplateStateGetEventInfo(const char *event_name, int *event_id,
162 AppLayerEventType *event_type)
163 {
164 *event_id = SCMapEnumNameToValue(event_name, template_decoder_event_table);
165 if (*event_id == -1) {
166 SCLogError(SC_ERR_INVALID_ENUM_MAP, "event \"%s\" not present in "
167 "template enum map table.", event_name);
168 /* This should be treated as fatal. */
169 return -1;
170 }
171
172 *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;
173
174 return 0;
175 }
176
177 static int TemplateStateGetEventInfoById(int event_id, const char **event_name,
178 AppLayerEventType *event_type)
179 {
180 *event_name = SCMapEnumValueToName(event_id, template_decoder_event_table);
181 if (*event_name == NULL) {
182 SCLogError(SC_ERR_INVALID_ENUM_MAP, "event \"%d\" not present in "
183 "template enum map table.", event_id);
184 /* This should be treated as fatal. */
185 return -1;
186 }
187
188 *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;
189
190 return 0;
191 }
192
193 static AppLayerDecoderEvents *TemplateGetEvents(void *tx)
194 {
195 return ((TemplateTransaction *)tx)->decoder_events;
196 }
197
198 /**
199 * \brief Probe the input to server to see if it looks like template.
200 *
201 * \retval ALPROTO_TEMPLATE if it looks like template,
202 * ALPROTO_FAILED, if it is clearly not ALPROTO_TEMPLATE,
203 * otherwise ALPROTO_UNKNOWN.
204 */
205 static AppProto TemplateProbingParserTs(Flow *f, uint8_t direction,
206 const uint8_t *input, uint32_t input_len, uint8_t *rdir)
207 {
208 /* Very simple test - if there is input, this is template. */
209 if (input_len >= TEMPLATE_MIN_FRAME_LEN) {
210 SCLogNotice("Detected as ALPROTO_TEMPLATE.");
211 return ALPROTO_TEMPLATE;
212 }
213
214 SCLogNotice("Protocol not detected as ALPROTO_TEMPLATE.");
215 return ALPROTO_UNKNOWN;
216 }
217
218 /**
219 * \brief Probe the input to client to see if it looks like template.
220 * TemplateProbingParserTs can be used instead if the protocol
221 * is symmetric.
222 *
223 * \retval ALPROTO_TEMPLATE if it looks like template,
224 * ALPROTO_FAILED, if it is clearly not ALPROTO_TEMPLATE,
225 * otherwise ALPROTO_UNKNOWN.
226 */
227 static AppProto TemplateProbingParserTc(Flow *f, uint8_t direction,
228 const uint8_t *input, uint32_t input_len, uint8_t *rdir)
229 {
230 /* Very simple test - if there is input, this is template. */
231 if (input_len >= TEMPLATE_MIN_FRAME_LEN) {
232 SCLogNotice("Detected as ALPROTO_TEMPLATE.");
233 return ALPROTO_TEMPLATE;
234 }
235
236 SCLogNotice("Protocol not detected as ALPROTO_TEMPLATE.");
237 return ALPROTO_UNKNOWN;
238 }
239
240 static AppLayerResult TemplateParseRequest(Flow *f, void *statev,
241 AppLayerParserState *pstate, const uint8_t *input, uint32_t input_len,
242 void *local_data, const uint8_t flags)
243 {
244 TemplateState *state = statev;
245
246 SCLogNotice("Parsing template request: len=%"PRIu32, input_len);
247
248 if (input == NULL) {
249 if (AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TS)) {
250 /* This is a signal that the stream is done. Do any
251 * cleanup if needed. Usually nothing is required here. */
252 SCReturnStruct(APP_LAYER_OK);
253 } else if (flags & STREAM_GAP) {
254 /* This is a signal that there has been a gap in the
255 * stream. This only needs to be handled if gaps were
256 * enabled during protocol registration. The input_len
257 * contains the size of the gap. */
258 SCReturnStruct(APP_LAYER_OK);
259 }
260 /* This should not happen. If input is NULL, one of the above should be
261 * true. */
262 DEBUG_VALIDATE_BUG_ON(true);
263 SCReturnStruct(APP_LAYER_ERROR);
264 }
265
266 /* Normally you would parse out data here and store it in the
267 * transaction object, but as this is echo, we'll just record the
268 * request data. */
269
270 /* Also, if this protocol may have a "protocol data unit" span
271 * multiple chunks of data, which is always a possibility with
272 * TCP, you may need to do some buffering here.
273 *
274 * For the sake of simplicity, buffering is left out here, but
275 * even for an echo protocol we may want to buffer until a new
276 * line is seen, assuming its text based.
277 */
278
279 /* Allocate a transaction.
280 *
281 * But note that if a "protocol data unit" is not received in one
282 * chunk of data, and the buffering is done on the transaction, we
283 * may need to look for the transaction that this newly received
284 * data belongs to.
285 */
286 TemplateTransaction *tx = TemplateTxAlloc(state);
287 if (unlikely(tx == NULL)) {
288 SCLogNotice("Failed to allocate new Template tx.");
289 goto end;
290 }
291 SCLogNotice("Allocated Template tx %"PRIu64".", tx->tx_id);
292
293 /* Make a copy of the request. */
294 tx->request_buffer = SCCalloc(1, input_len);
295 if (unlikely(tx->request_buffer == NULL)) {
296 goto end;
297 }
298 memcpy(tx->request_buffer, input, input_len);
299 tx->request_buffer_len = input_len;
300
301 /* Here we check for an empty message and create an app-layer
302 * event. */
303 if ((input_len == 1 && tx->request_buffer[0] == '\n') ||
304 (input_len == 2 && tx->request_buffer[0] == '\r')) {
305 SCLogNotice("Creating event for empty message.");
306 AppLayerDecoderEventsSetEventRaw(&tx->decoder_events,
307 TEMPLATE_DECODER_EVENT_EMPTY_MESSAGE);
308 }
309
310 end:
311 SCReturnStruct(APP_LAYER_OK);
312 }
313
314 static AppLayerResult TemplateParseResponse(Flow *f, void *statev, AppLayerParserState *pstate,
315 const uint8_t *input, uint32_t input_len, void *local_data,
316 const uint8_t flags)
317 {
318 TemplateState *state = statev;
319 TemplateTransaction *tx = NULL, *ttx;
320
321 SCLogNotice("Parsing Template response.");
322
323 /* Likely connection closed, we can just return here. */
324 if ((input == NULL || input_len == 0) &&
325 AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TC)) {
326 SCReturnStruct(APP_LAYER_OK);
327 }
328
329 /* Probably don't want to create a transaction in this case
330 * either. */
331 if (input == NULL || input_len == 0) {
332 SCReturnStruct(APP_LAYER_OK);
333 }
334
335 /* Look up the existing transaction for this response. In the case
336 * of echo, it will be the most recent transaction on the
337 * TemplateState object. */
338
339 /* We should just grab the last transaction, but this is to
340 * illustrate how you might traverse the transaction list to find
341 * the transaction associated with this response. */
342 TAILQ_FOREACH(ttx, &state->tx_list, next) {
343 tx = ttx;
344 }
345
346 if (tx == NULL) {
347 SCLogNotice("Failed to find transaction for response on state %p.",
348 state);
349 goto end;
350 }
351
352 SCLogNotice("Found transaction %"PRIu64" for response on state %p.",
353 tx->tx_id, state);
354
355 /* If the protocol requires multiple chunks of data to complete, you may
356 * run into the case where you have existing response data.
357 *
358 * In this case, we just log that there is existing data and free it. But
359 * you might want to realloc the buffer and append the data.
360 */
361 if (tx->response_buffer != NULL) {
362 SCLogNotice("WARNING: Transaction already has response data, "
363 "existing data will be overwritten.");
364 SCFree(tx->response_buffer);
365 }
366
367 /* Make a copy of the response. */
368 tx->response_buffer = SCCalloc(1, input_len);
369 if (unlikely(tx->response_buffer == NULL)) {
370 goto end;
371 }
372 memcpy(tx->response_buffer, input, input_len);
373 tx->response_buffer_len = input_len;
374
375 /* Set the response_done flag for transaction state checking in
376 * TemplateGetStateProgress(). */
377 tx->response_done = 1;
378
379 end:
380 SCReturnStruct(APP_LAYER_OK);
381 }
382
383 static uint64_t TemplateGetTxCnt(void *statev)
384 {
385 const TemplateState *state = statev;
386 SCLogNotice("Current tx count is %"PRIu64".", state->transaction_max);
387 return state->transaction_max;
388 }
389
390 static void *TemplateGetTx(void *statev, uint64_t tx_id)
391 {
392 TemplateState *state = statev;
393 TemplateTransaction *tx;
394
395 SCLogNotice("Requested tx ID %"PRIu64".", tx_id);
396
397 TAILQ_FOREACH(tx, &state->tx_list, next) {
398 if (tx->tx_id == tx_id) {
399 SCLogNotice("Transaction %"PRIu64" found, returning tx object %p.",
400 tx_id, tx);
401 return tx;
402 }
403 }
404
405 SCLogNotice("Transaction ID %"PRIu64" not found.", tx_id);
406 return NULL;
407 }
408
409 /**
410 * \brief Return the state of a transaction in a given direction.
411 *
412 * In the case of the echo protocol, the existence of a transaction
413 * means that the request is done. However, some protocols that may
414 * need multiple chunks of data to complete the request may need more
415 * than just the existence of a transaction for the request to be
416 * considered complete.
417 *
418 * For the response to be considered done, the response for a request
419 * needs to be seen. The response_done flag is set on response for
420 * checking here.
421 */
422 static int TemplateGetStateProgress(void *txv, uint8_t direction)
423 {
424 TemplateTransaction *tx = txv;
425
426 SCLogNotice("Transaction progress requested for tx ID %"PRIu64
427 ", direction=0x%02x", tx->tx_id, direction);
428
429 if (direction & STREAM_TOCLIENT && tx->response_done) {
430 return 1;
431 }
432 else if (direction & STREAM_TOSERVER) {
433 /* For the template, just the existence of the transaction means the
434 * request is done. */
435 return 1;
436 }
437
438 return 0;
439 }
440
441 /**
442 * \brief retrieve the tx data used for logging, config, detection
443 */
444 static AppLayerTxData *TemplateGetTxData(void *vtx)
445 {
446 TemplateTransaction *tx = vtx;
447 return &tx->tx_data;
448 }
449
450 void RegisterTemplateParsers(void)
451 {
452 const char *proto_name = "template";
453
454 /* Check if Template TCP detection is enabled. If it does not exist in
455 * the configuration file then it will be disabled by default. */
456 if (AppLayerProtoDetectConfProtoDetectionEnabledDefault("tcp", proto_name, false)) {
457
458 SCLogDebug("Template TCP protocol detection enabled.");
459
460 AppLayerProtoDetectRegisterProtocol(ALPROTO_TEMPLATE, proto_name);
461
462 if (RunmodeIsUnittests()) {
463
464 SCLogNotice("Unittest mode, registering default configuration.");
465 AppLayerProtoDetectPPRegister(IPPROTO_TCP, TEMPLATE_DEFAULT_PORT,
466 ALPROTO_TEMPLATE, 0, TEMPLATE_MIN_FRAME_LEN, STREAM_TOSERVER,
467 TemplateProbingParserTs, TemplateProbingParserTc);
468
469 }
470 else {
471
472 if (!AppLayerProtoDetectPPParseConfPorts("tcp", IPPROTO_TCP,
473 proto_name, ALPROTO_TEMPLATE, 0, TEMPLATE_MIN_FRAME_LEN,
474 TemplateProbingParserTs, TemplateProbingParserTc)) {
475 SCLogDebug("No template app-layer configuration, enabling echo"
476 " detection TCP detection on port %s.",
477 TEMPLATE_DEFAULT_PORT);
478 AppLayerProtoDetectPPRegister(IPPROTO_TCP,
479 TEMPLATE_DEFAULT_PORT, ALPROTO_TEMPLATE, 0,
480 TEMPLATE_MIN_FRAME_LEN, STREAM_TOSERVER,
481 TemplateProbingParserTs, TemplateProbingParserTc);
482 }
483
484 }
485
486 }
487
488 else {
489 SCLogDebug("Protocol detector and parser disabled for Template.");
490 return;
491 }
492
493 if (AppLayerParserConfParserEnabled("tcp", proto_name)) {
494
495 SCLogNotice("Registering Template protocol parser.");
496
497 /* Register functions for state allocation and freeing. A
498 * state is allocated for every new Template flow. */
499 AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_TEMPLATE,
500 TemplateStateAlloc, TemplateStateFree);
501
502 /* Register request parser for parsing frame from server to client. */
503 AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_TEMPLATE,
504 STREAM_TOSERVER, TemplateParseRequest);
505
506 /* Register response parser for parsing frames from server to client. */
507 AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_TEMPLATE,
508 STREAM_TOCLIENT, TemplateParseResponse);
509
510 /* Register a function to be called by the application layer
511 * when a transaction is to be freed. */
512 AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_TEMPLATE,
513 TemplateStateTxFree);
514
515 /* Register a function to return the current transaction count. */
516 AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_TEMPLATE,
517 TemplateGetTxCnt);
518
519 /* Transaction handling. */
520 AppLayerParserRegisterStateProgressCompletionStatus(ALPROTO_TEMPLATE, 1, 1);
521 AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP,
522 ALPROTO_TEMPLATE, TemplateGetStateProgress);
523 AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_TEMPLATE,
524 TemplateGetTx);
525 AppLayerParserRegisterTxDataFunc(IPPROTO_TCP, ALPROTO_TEMPLATE,
526 TemplateGetTxData);
527
528 AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_TEMPLATE,
529 TemplateStateGetEventInfo);
530 AppLayerParserRegisterGetEventInfoById(IPPROTO_TCP, ALPROTO_TEMPLATE,
531 TemplateStateGetEventInfoById);
532 AppLayerParserRegisterGetEventsFunc(IPPROTO_TCP, ALPROTO_TEMPLATE,
533 TemplateGetEvents);
534
535 /* Leave this is if your parser can handle gaps, otherwise
536 * remove. */
537 AppLayerParserRegisterOptionFlags(IPPROTO_TCP, ALPROTO_TEMPLATE,
538 APP_LAYER_PARSER_OPT_ACCEPT_GAPS);
539 }
540 else {
541 SCLogDebug("Template protocol parsing disabled.");
542 }
543
544 #ifdef UNITTESTS
545 AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_TEMPLATE,
546 TemplateParserRegisterTests);
547 #endif
548 }
549
550 #ifdef UNITTESTS
551 #endif
552
553 void TemplateParserRegisterTests(void)
554 {
555 #ifdef UNITTESTS
556 #endif
557 }