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