suricata
util-decode-mime.c
Go to the documentation of this file.
1 /* Copyright (C) 2012 BAE Systems
2  * Copyright (C) 2020-2021 Open Information Security Foundation
3  *
4  * You can copy, redistribute or modify this Program under the terms of
5  * the GNU General Public License version 2 as published by the Free
6  * Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * version 2 along with this program; if not, write to the Free Software
15  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16  * 02110-1301, USA.
17  */
18 
19 /**
20  * \file
21  *
22  * \author David Abarbanel <david.abarbanel@baesystems.com>
23  *
24  */
25 
26 #include "suricata-common.h"
27 #include "suricata.h"
28 #include "util-decode-mime.h"
29 #include "util-ip.h"
30 #include "util-spm-bs.h"
31 #include "util-unittest.h"
32 #include "util-memcmp.h"
33 #include "util-print.h"
34 #include "util-validate.h"
35 #include "rust.h"
36 
37 /* Character constants */
38 #ifndef CR
39 #define CR 13
40 #define LF 10
41 #endif
42 
43 #define CRLF "\r\n"
44 #define COLON 58
45 #define DASH 45
46 #define PRINTABLE_START 33
47 #define PRINTABLE_END 126
48 #define UC_START 65
49 #define UC_END 90
50 #define LC_START 97
51 #define LC_END 122
52 #define UC_LC_DIFF 32
53 #define EOL_LEN 2
54 
55 /* Base-64 constants */
56 #define BASE64_STR "Base64"
57 
58 /* Mime Constants */
59 #define MAX_LINE_LEN 998 /* Def in RFC 2045, excluding CRLF sequence */
60 #define MAX_ENC_LINE_LEN 76 /* Def in RFC 2045, excluding CRLF sequence */
61 #define MAX_HEADER_NAME 75 /* 75 + ":" = 76 */
62 #define MAX_HEADER_VALUE 2000 /* Default - arbitrary limit */
63 #define BOUNDARY_BUF 256
64 #define CTNT_TYPE_STR "content-type"
65 #define CTNT_DISP_STR "content-disposition"
66 #define CTNT_TRAN_STR "content-transfer-encoding"
67 #define MSG_ID_STR "message-id"
68 #define MSG_STR "message/"
69 #define MULTIPART_STR "multipart/"
70 #define QP_STR "quoted-printable"
71 #define TXT_STR "text/plain"
72 #define HTML_STR "text/html"
73 
74 /* Memory Usage Constants */
75 #define STACK_FREE_NODES 10
76 
77 /* Other Constants */
78 #define MAX_IP4_CHARS 15
79 #define MAX_IP6_CHARS 39
80 
81 /* Globally hold configuration data */
82 static MimeDecConfig mime_dec_config = { true, true, true, NULL, false, false, MAX_HEADER_VALUE };
83 
84 /* Mime Parser String translation */
85 static const char *StateFlags[] = { "NONE",
86  "HEADER_READY",
87  "HEADER_STARTED",
88  "HEADER_DONE",
89  "BODY_STARTED",
90  "BODY_DONE",
91  "BODY_END_BOUND",
92  "PARSE_DONE",
93  "PARSE_ERROR",
94  NULL };
95 
96 /* URL executable file extensions */
97 static const char *UrlExeExts[] = { ".exe", ".vbs", ".bin", ".cmd", ".bat", ".jar", ".js", ".ps",
98  ".ps1", ".sh", ".run", ".hta", ".bin", ".elf", NULL };
99 
100 /**
101  * \brief Function used to print character strings that are not null-terminated
102  *
103  * \param log_level The logging level in which to print
104  * \param label A label for the string to print
105  * \param src The source string
106  * \param len The length of the string
107  *
108  * \return none
109  */
110 static void PrintChars(int log_level, const char *label, const uint8_t *src, uint32_t len)
111 {
112 #ifdef DEBUG
113  if (log_level <= sc_log_global_log_level) {
114  printf("[%s]\n", label);
115  PrintRawDataFp(stdout, (uint8_t *)src, len);
116  }
117 #endif
118 }
119 
120 /**
121  * \brief Set global config policy
122  *
123  * \param config Config policy to set
124  * \return none
125  */
127 {
128  if (config != NULL) {
129  mime_dec_config = *config;
130 
131  /* Set to default */
132  if (mime_dec_config.header_value_depth == 0) {
133  mime_dec_config.header_value_depth = MAX_HEADER_VALUE;
134  }
135  } else {
136  SCLogWarning(SC_ERR_MISSING_CONFIG_PARAM, "Invalid null configuration parameters");
137  }
138 }
139 
140 /**
141  * \brief Get global config policy
142  *
143  * \return config data structure
144  */
146 {
147  return &mime_dec_config;
148 }
149 
150 /**
151  * \brief Follow the 'next' pointers to the leaf
152  *
153  * \param node The root entity
154  *
155  * \return Pointer to leaf on 'next' side
156  *
157  */
158 static MimeDecEntity *findLastSibling(MimeDecEntity *node)
159 {
160  if (node == NULL)
161  return NULL;
162  while(node->next != NULL)
163  node = node->next;
164  return node;
165 }
166 
167 /**
168  * \brief Frees a mime entity tree
169  *
170  * \param entity The root entity
171  *
172  * \return none
173  *
174  */
176 {
177  if (entity == NULL)
178  return;
179  MimeDecEntity *lastSibling = findLastSibling(entity);
180  while (entity != NULL)
181  {
182  /**
183  * Move child to next
184  * Transform tree into list
185  */
186  if (entity->child != NULL)
187  {
188  lastSibling->next = entity->child;
189  lastSibling = findLastSibling(lastSibling);
190  }
191 
192  /**
193  * Move to next element
194  */
195  MimeDecEntity *old = entity;
196  entity = entity->next;
197 
199  MimeDecFreeUrl(old->url_list);
200  SCFree(old->filename);
201 
202  SCFree(old);
203  }
204 }
205 
206 /**
207  * \brief Iteratively frees a header field entry list
208  *
209  * \param field The header field
210  *
211  * \return none
212  *
213  */
215 {
216  MimeDecField *temp, *curr;
217 
218  if (field != NULL) {
219 
220  curr = field;
221  while (curr != NULL) {
222  temp = curr;
223  curr = curr->next;
224 
225  /* Free contents of node */
226  SCFree(temp->name);
227  SCFree(temp->value);
228 
229  /* Now free node data */
230  SCFree(temp);
231  }
232  }
233 }
234 
235 /**
236  * \brief Iteratively frees a URL entry list
237  *
238  * \param url The url entry
239  *
240  * \return none
241  *
242  */
244 {
245  MimeDecUrl *temp, *curr;
246 
247  if (url != NULL) {
248 
249  curr = url;
250  while (curr != NULL) {
251  temp = curr;
252  curr = curr->next;
253 
254  /* Now free node data */
255  SCFree(temp->url);
256  SCFree(temp);
257  }
258  }
259 }
260 
261 /**
262  * \brief Creates and adds a header field entry to an entity
263  *
264  * The entity is optional. If NULL is specified, than a new stand-alone field
265  * is created.
266  *
267  * \param entity The parent entity
268  *
269  * \return The field object, or NULL if the operation fails
270  *
271  */
273 {
274  MimeDecField *node = SCMalloc(sizeof(MimeDecField));
275  if (unlikely(node == NULL)) {
276  return NULL;
277  }
278  memset(node, 0x00, sizeof(MimeDecField));
279 
280  /* If list is empty, then set as head of list */
281  if (entity->field_list == NULL) {
282  entity->field_list = node;
283  } else {
284  /* Otherwise add to beginning of list since these are out-of-order in
285  * the message */
286  node->next = entity->field_list;
287  entity->field_list = node;
288  }
289 
290  return node;
291 }
292 
293 
294 /**
295  * \brief Searches for header fields with the specified name
296  *
297  * \param entity The entity to search
298  * \param name The header name (lowercase)
299  *
300  * \return number of items found
301  *
302  */
303 int MimeDecFindFieldsForEach(const MimeDecEntity *entity, const char *name, int (*DataCallback)(const uint8_t *val, const size_t, void *data), void *data)
304 {
305  MimeDecField *curr = entity->field_list;
306  int found = 0;
307 
308  while (curr != NULL) {
309  /* name is stored lowercase */
310  if (strlen(name) == curr->name_len) {
311  if (SCMemcmp(curr->name, name, curr->name_len) == 0) {
312  if (DataCallback(curr->value, curr->value_len, data))
313  found++;
314  }
315  }
316  curr = curr->next;
317  }
318 
319  return found;
320 }
321 
322 /**
323  * \brief Searches for a header field with the specified name
324  *
325  * \param entity The entity to search
326  * \param name The header name (lowercase)
327  *
328  * \return The field object, or NULL if not found
329  *
330  */
331 MimeDecField * MimeDecFindField(const MimeDecEntity *entity, const char *name) {
332  MimeDecField *curr = entity->field_list;
333 
334  while (curr != NULL) {
335  /* name is stored lowercase */
336  if (strlen(name) == curr->name_len) {
337  if (SCMemcmp(curr->name, name, curr->name_len) == 0) {
338  break;
339  }
340  }
341  curr = curr->next;
342  }
343 
344  return curr;
345 }
346 
347 /**
348  * \brief Creates and adds a URL entry to the specified entity
349  *
350  * The entity is optional and if NULL is specified, then a new list will be created.
351  *
352  * \param entity The entity
353  *
354  * \return URL entry or NULL if the operation fails
355  *
356  */
357 static MimeDecUrl * MimeDecAddUrl(MimeDecEntity *entity, uint8_t *url, uint32_t url_len, uint8_t flags)
358 {
359  MimeDecUrl *node = SCMalloc(sizeof(MimeDecUrl));
360  if (unlikely(node == NULL)) {
361  return NULL;
362  }
363  memset(node, 0x00, sizeof(MimeDecUrl));
364 
365  node->url = url;
366  node->url_len = url_len;
367  node->url_flags = flags;
368 
369  /* If list is empty, then set as head of list */
370  if (entity->url_list == NULL) {
371  entity->url_list = node;
372  } else {
373  /* Otherwise add to beginning of list since these are out-of-order in
374  * the message */
375  node->next = entity->url_list;
376  entity->url_list = node;
377  }
378 
379  return node;
380 }
381 
382 /**
383  * \brief Creates and adds a child entity to the specified parent entity
384  *
385  * \param parent The parent entity
386  *
387  * \return The child entity, or NULL if the operation fails
388  *
389  */
391 {
392  MimeDecEntity *curr, *node = SCMalloc(sizeof(MimeDecEntity));
393  if (unlikely(node == NULL)) {
394  return NULL;
395  }
396  memset(node, 0x00, sizeof(MimeDecEntity));
397 
398  /* If parent is NULL then just return the new pointer */
399  if (parent != NULL) {
400  if (parent->child == NULL) {
401  parent->child = node;
402  } else {
403  curr = parent->child;
404  while (curr->next != NULL) {
405  curr = curr->next;
406  }
407  curr->next = node;
408  }
409  }
410 
411  return node;
412 }
413 
414 /**
415  * \brief Creates a mime header field and fills in its values and adds it to the
416  * specified entity
417  *
418  * \param entity Entity in which to add the field
419  * \param name String containing the name
420  * \param nlen Length of the name
421  * \param value String containing the value
422  * \param vlen Length of the value
423  *
424  * \return The field or NULL if the operation fails
425  */
426 static MimeDecField * MimeDecFillField(MimeDecEntity *entity, uint8_t *name,
427  uint32_t nlen, const uint8_t *value, uint32_t vlen)
428 {
429  if (nlen == 0 && vlen == 0)
430  return NULL;
431 
432  MimeDecField *field = MimeDecAddField(entity);
433  if (unlikely(field == NULL)) {
434  return NULL;
435  }
436 
437  if (nlen > 0) {
438  /* convert to lowercase and store */
439  uint32_t u;
440  for (u = 0; u < nlen; u++)
441  name[u] = u8_tolower(name[u]);
442 
443  field->name = (uint8_t *)name;
444  field->name_len = nlen;
445  }
446 
447  if (vlen > 0) {
448  field->value = (uint8_t *)value;
449  field->value_len = vlen;
450  }
451 
452  return field;
453 }
454 
455 /**
456  * \brief Pushes a node onto a stack and returns the new node.
457  *
458  * \param stack The top of the stack
459  *
460  * \return pointer to a new node, otherwise NULL if it fails
461  */
462 static MimeDecStackNode * PushStack(MimeDecStack *stack)
463 {
464  /* Attempt to pull from free nodes list */
465  MimeDecStackNode *node = stack->free_nodes;
466  if (node == NULL) {
467  node = SCMalloc(sizeof(MimeDecStackNode));
468  if (unlikely(node == NULL)) {
469  return NULL;
470  }
471  } else {
472  /* Move free nodes pointer over */
473  stack->free_nodes = stack->free_nodes->next;
474  stack->free_nodes_cnt--;
475  }
476  memset(node, 0x00, sizeof(MimeDecStackNode));
477 
478  /* Push to top of stack */
479  node->next = stack->top;
480  stack->top = node;
481 
482  /* Return a pointer to the top of the stack */
483  return node;
484 }
485 
486 /**
487  * \brief Pops the top node from the stack and returns the next node.
488  *
489  * \param stack The top of the stack
490  *
491  * \return pointer to the next node, otherwise NULL if no nodes remain
492  */
493 static MimeDecStackNode * PopStack(MimeDecStack *stack)
494 {
495  /* Move stack pointer to next item */
496  MimeDecStackNode *curr = stack->top;
497  if (curr != NULL) {
498  curr = curr->next;
499  }
500 
501  /* Always free alloc'd memory */
502  SCFree(stack->top->bdef);
503 
504  /* Now move head to free nodes list */
505  if (stack->free_nodes_cnt < STACK_FREE_NODES) {
506  stack->top->next = stack->free_nodes;
507  stack->free_nodes = stack->top;
508  stack->free_nodes_cnt++;
509  } else {
510  SCFree(stack->top);
511  }
512  stack->top = curr;
513 
514  /* Return a pointer to the top of the stack */
515  return curr;
516 }
517 
518 /**
519  * \brief Frees the stack along with the free-nodes list
520  *
521  * \param stack The stack pointer
522  *
523  * \return none
524  */
525 static void FreeMimeDecStack(MimeDecStack *stack)
526 {
527  MimeDecStackNode *temp, *curr;
528 
529  if (stack != NULL) {
530  /* Top of stack */
531  curr = stack->top;
532  while (curr != NULL) {
533  temp = curr;
534  curr = curr->next;
535 
536  /* Now free node */
537  SCFree(temp->bdef);
538  SCFree(temp);
539  }
540 
541  /* Free nodes */
542  curr = stack->free_nodes;
543  while (curr != NULL) {
544  temp = curr;
545  curr = curr->next;
546 
547  /* Now free node */
548  SCFree(temp);
549  }
550 
551  SCFree(stack);
552  }
553 }
554 
555 /**
556  * \brief Adds a data value to the data values linked list
557  *
558  * \param dv The head of the linked list (NULL if new list)
559  *
560  * \return pointer to a new node, otherwise NULL if it fails
561  */
562 static DataValue * AddDataValue(DataValue *dv)
563 {
564  DataValue *curr, *node = SCMalloc(sizeof(DataValue));
565  if (unlikely(node == NULL)) {
566  return NULL;
567  }
568  memset(node, 0x00, sizeof(DataValue));
569 
570  if (dv != NULL) {
571  curr = dv;
572  while (curr->next != NULL) {
573  curr = curr->next;
574  }
575 
576  curr->next = node;
577  }
578 
579  return node;
580 }
581 
582 /**
583  * \brief Frees a linked list of data values starting at the head
584  *
585  * \param dv The head of the linked list
586  *
587  * \return none
588  */
589 static void FreeDataValue(DataValue *dv)
590 {
591  DataValue *temp, *curr;
592 
593  if (dv != NULL) {
594  curr = dv;
595  while (curr != NULL) {
596  temp = curr;
597  curr = curr->next;
598 
599  /* Now free node */
600  SCFree(temp->value);
601  SCFree(temp);
602  }
603  }
604 }
605 
606 /**
607  * \brief Converts a list of data values into a single value (returns dynamically
608  * allocated memory)
609  *
610  * \param dv The head of the linked list (NULL if new list)
611  * \param olen The output length of the single value
612  *
613  * \return pointer to a single value, otherwise NULL if it fails or is zero-length
614  */
615 static uint8_t *GetFullValue(const DataValue *dv, uint32_t *olen)
616 {
617  uint32_t offset = 0;
618  uint8_t *val = NULL;
619  uint32_t len = 0;
620  *olen = 0;
621 
622  /* First calculate total length */
623  for (const DataValue *curr = dv; curr != NULL; curr = curr->next) {
624  len += curr->value_len;
625  }
626  /* Must have at least one character in the value */
627  if (len > 0) {
628  val = SCCalloc(1, len);
629  if (unlikely(val == NULL)) {
630  return NULL;
631  }
632  for (const DataValue *curr = dv; curr != NULL; curr = curr->next) {
633  memcpy(val + offset, curr->value, curr->value_len);
634  offset += curr->value_len;
635  }
636  }
637  *olen = len;
638  return val;
639 }
640 
641 /**
642  * \brief Find a string while searching up to N characters within a source
643  * buffer
644  *
645  * \param src The source string (not null-terminated)
646  * \param len The length of the source string
647  * \param find The string to find (null-terminated)
648  * \param find_len length of the 'find' string
649  *
650  * \return Pointer to the position it was found, otherwise NULL if not found
651  */
652 static inline uint8_t *FindBuffer(
653  const uint8_t *src, uint32_t len, const uint8_t *find, uint16_t find_len)
654 {
655  /* Use utility search function */
656  return BasicSearchNocase(src, len, find, find_len);
657 }
658 
659 /**
660  * \brief Get a line (CRLF or just CR or LF) from a buffer (similar to GetToken)
661  *
662  * \param buf The input buffer (not null-terminated)
663  * \param blen The length of the input buffer
664  * \param remainPtr Pointer to remaining after tokenizing iteration
665  * \param tokLen Output token length (if non-null line)
666  *
667  * \return Pointer to line
668  */
669 static uint8_t * GetLine(uint8_t *buf, uint32_t blen, uint8_t **remainPtr,
670  uint32_t *tokLen)
671 {
672  uint32_t i;
673  uint8_t *tok;
674 
675  /* So that it can be used just like strtok_r */
676  if (buf == NULL) {
677  buf = *remainPtr;
678  } else {
679  *remainPtr = buf;
680  }
681  if (buf == NULL)
682  return NULL;
683 
684  tok = buf;
685 
686  /* length must be specified */
687  for (i = 0; i < blen && buf[i] != 0; i++) {
688 
689  /* Found delimiter */
690  if (buf[i] == CR || buf[i] == LF) {
691 
692  /* Add another if we find either CRLF or LFCR */
693  *remainPtr += (i + 1);
694  if ((i + 1 < blen) && buf[i] != buf[i + 1] &&
695  (buf[i + 1] == CR || buf[i + 1] == LF)) {
696  (*remainPtr)++;
697  }
698  break;
699  }
700  }
701 
702  /* If no delimiter found, then point to end of buffer */
703  if (buf == *remainPtr) {
704  (*remainPtr) += i;
705  }
706 
707  /* Calculate token length */
708  *tokLen = (buf + i) - tok;
709 
710  return tok;
711 }
712 
713 /**
714  * \brief Get token from buffer and return pointer to it
715  *
716  * \param buf The input buffer (not null-terminated)
717  * \param blen The length of the input buffer
718  * \param delims Character delimiters (null-terminated)
719  * \param remainPtr Pointer to remaining after tokenizing iteration
720  * \param tokLen Output token length (if non-null line)
721  *
722  * \return Pointer to token, or NULL if not found
723  */
724 static uint8_t * GetToken(uint8_t *buf, uint32_t blen, const char *delims,
725  uint8_t **remainPtr, uint32_t *tokenLen)
726 {
727  uint32_t i, j, delimFound = 0;
728  uint8_t *tok = NULL;
729 
730  /* So that it can be used just like strtok_r */
731  if (buf == NULL) {
732  buf = *remainPtr;
733  } else {
734  *remainPtr = buf;
735  }
736  if (buf == NULL)
737  return NULL;
738 
739  /* Must specify length */
740  for (i = 0; i < blen && buf[i] != 0; i++) {
741 
742  /* Look for delimiters */
743  for (j = 0; delims[j] != 0; j++) {
744  if (buf[i] == delims[j]) {
745  /* Data must be found before delimiter matters */
746  if (tok != NULL) {
747  (*remainPtr) += (i + 1);
748  }
749  delimFound = 1;
750  break;
751  }
752  }
753 
754  /* If at least one non-delimiter found, then a token is found */
755  if (tok == NULL && !delimFound) {
756  tok = buf + i;
757  } else {
758  /* Reset delimiter */
759  delimFound = 0;
760  }
761 
762  /* If delimiter found, then break out of loop */
763  if (buf != *remainPtr) {
764  break;
765  }
766  }
767 
768  /* Make sure remaining points to end of buffer if delimiters not found */
769  if (tok != NULL) {
770  if (buf == *remainPtr) {
771  (*remainPtr) += i;
772  }
773 
774  /* Calculate token length */
775  *tokenLen = (buf + i) - tok;
776  }
777 
778  return tok;
779 }
780 
781 /**
782  * \brief Stores the final MIME header value into the current entity on the
783  * stack.
784  *
785  * \param state The parser state
786  *
787  * \return MIME_DEC_OK if stored, otherwise a negative number indicating error
788  */
789 static int StoreMimeHeader(MimeDecParseState *state)
790 {
791  int ret = MIME_DEC_OK, stored = 0;
792  uint8_t *val;
793  uint32_t vlen;
794 
795  /* Lets save the most recent header */
796  if (state->hname != NULL || state->hvalue != NULL) {
797  SCLogDebug("Storing last header");
798  val = GetFullValue(state->hvalue, &vlen);
799  if (val != NULL) {
800  if (state->hname == NULL) {
801  SCLogDebug("Error: Invalid parser state - header value without"
802  " name");
803  ret = MIME_DEC_ERR_PARSE;
804  } else if (state->stack->top != NULL) {
805  /* Store each header name and value */
806  if (MimeDecFillField(state->stack->top->data, state->hname, state->hlen, val,
807  vlen) == NULL) {
808  ret = MIME_DEC_ERR_MEM;
809  } else {
810  stored = 1;
811  }
812  } else {
813  SCLogDebug("Error: Stack pointer missing");
814  ret = MIME_DEC_ERR_DATA;
815  }
816  } else if (state->hvalue != NULL) {
817  /* Memory allocation must have failed since val is NULL */
818  ret = MIME_DEC_ERR_MEM;
819  }
820 
821  /* Do cleanup here */
822  if (!stored) {
823  SCFree(state->hname);
824  SCFree(val);
825  }
826  state->hname = NULL;
827  FreeDataValue(state->hvalue);
828  state->hvalue = NULL;
829  state->hvlen = 0;
830  }
831 
832  return ret;
833 }
834 
835 /**
836  * \brief Function determines whether a url string points to an executable
837  * based on file extension only.
838  *
839  * \param url The url string
840  * \param len The url string length
841  *
842  * \retval 1 The url points to an EXE
843  * \retval 0 The url does NOT point to an EXE
844  */
845 static int IsExeUrl(const uint8_t *url, uint32_t len)
846 {
847  int isExeUrl = 0;
848  uint32_t i, extLen;
849  uint8_t *ext;
850 
851  /* Now check for executable extensions and if not found, cut off at first '/' */
852  for (i = 0; UrlExeExts[i] != NULL; i++) {
853  extLen = strlen(UrlExeExts[i]);
854  ext = FindBuffer(url, len, (uint8_t *)UrlExeExts[i], (uint16_t)strlen(UrlExeExts[i]));
855  if (ext != NULL && (ext + extLen - url == (int)len || ext[extLen] == '?')) {
856  isExeUrl = 1;
857  break;
858  }
859  }
860 
861  return isExeUrl;
862 }
863 
864 /**
865  * \brief Function determines whether a host string is a numeric IP v4 address
866  *
867  * \param urlhost The host string
868  * \param len The host string length
869  *
870  * \retval 1 The host is a numeric IP
871  * \retval 0 The host is NOT a numeric IP
872  */
873 static int IsIpv4Host(const uint8_t *urlhost, uint32_t len)
874 {
875  struct sockaddr_in sa;
876  char tempIp[MAX_IP4_CHARS + 1];
877 
878  /* Cut off at '/' */
879  uint32_t i = 0;
880  for ( ; i < len && urlhost[i] != 0; i++) {
881 
882  if (urlhost[i] == '/') {
883  break;
884  }
885  }
886 
887  /* Too many chars */
888  if (i > MAX_IP4_CHARS) {
889  return 0;
890  }
891 
892  /* Create null-terminated string */
893  memcpy(tempIp, urlhost, i);
894  tempIp[i] = '\0';
895 
896  if (!IPv4AddressStringIsValid(tempIp))
897  return 0;
898 
899  return inet_pton(AF_INET, tempIp, &(sa.sin_addr));
900 }
901 
902 /**
903  * \brief Function determines whether a host string is a numeric IP v6 address
904  *
905  * \param urlhost The host string
906  * \param len The host string length
907  *
908  * \retval 1 The host is a numeric IP
909  * \retval 0 The host is NOT a numeric IP
910  */
911 static int IsIpv6Host(const uint8_t *urlhost, uint32_t len)
912 {
913  struct in6_addr in6;
914  char tempIp[MAX_IP6_CHARS + 1];
915 
916  /* Cut off at '/' */
917  uint32_t i = 0;
918  for (i = 0; i < len && urlhost[i] != 0; i++) {
919  if (urlhost[i] == '/') {
920  break;
921  }
922  }
923 
924  /* Too many chars */
925  if (i > MAX_IP6_CHARS) {
926  return 0;
927  }
928 
929  /* Create null-terminated string */
930  memcpy(tempIp, urlhost, i);
931  tempIp[i] = '\0';
932 
933  if (!IPv6AddressStringIsValid(tempIp))
934  return 0;
935 
936  return inet_pton(AF_INET6, tempIp, &in6);
937 }
938 
939 /**
940  * \brief Traverses through the list of URLs for an exact match of the specified
941  * string
942  *
943  * \param entity The MIME entity
944  * \param url The matching URL string (lowercase)
945  * \param url_len The matching URL string length
946  *
947  * \return URL object or NULL if not found
948  */
949 static MimeDecUrl *FindExistingUrl(MimeDecEntity *entity, uint8_t *url, uint32_t url_len)
950 {
951  MimeDecUrl *curr = entity->url_list;
952 
953  while (curr != NULL) {
954  if (url_len == curr->url_len) {
955  /* search url and stored url are both in
956  * lowercase, so we can do an exact match */
957  if (SCMemcmp(curr->url, url, url_len) == 0) {
958  break;
959  }
960  }
961  curr = curr->next;
962  }
963 
964  return curr;
965 }
966 
967 /**
968  * \brief This function searches a text or html line for a URL string
969  *
970  * The URL strings are searched for using the URL schemes defined in the global
971  * MIME config e.g. "http", "https".
972  *
973  * The found URL strings are stored in lowercase and with their schemes
974  * stripped unless the MIME config flag for log_url_scheme is set.
975  *
976  * Numeric IPs, malformed numeric IPs, and URLs pointing to executables are
977  * also flagged as URLs of interest.
978  *
979  * \param line the line
980  * \param len the line length
981  * \param state The current parser state
982  *
983  * \return MIME_DEC_OK on success, otherwise < 0 on failure
984  */
985 static int FindUrlStrings(const uint8_t *line, uint32_t len,
986  MimeDecParseState *state)
987 {
988  int ret = MIME_DEC_OK;
989  MimeDecEntity *entity = (MimeDecEntity *) state->stack->top->data;
990  MimeDecConfig *mdcfg = MimeDecGetConfig();
991  uint8_t *fptr, *remptr, *tok = NULL, *tempUrl, *urlHost;
992  uint32_t tokLen = 0, i, tempUrlLen, urlHostLen;
993  uint16_t schemeStrLen = 0;
994  uint8_t flags = 0;
995  ConfNode *scheme = NULL;
996  char *schemeStr = NULL;
997 
998  if (mdcfg != NULL && mdcfg->extract_urls_schemes == NULL) {
999  SCLogDebug("Error: MIME config extract_urls_schemes was NULL.");
1000  return MIME_DEC_ERR_DATA;
1001  }
1002 
1003  TAILQ_FOREACH (scheme, &mdcfg->extract_urls_schemes->head, next) {
1004  schemeStr = scheme->val;
1005  // checked against UINT16_MAX when setting in SMTPConfigure
1006  schemeStrLen = (uint16_t)strlen(schemeStr);
1007 
1008  remptr = (uint8_t *)line;
1009  do {
1010  SCLogDebug("Looking for URL String starting with: %s", schemeStr);
1011 
1012  /* Check for token definition */
1013  fptr = FindBuffer(remptr, len - (remptr - line), (uint8_t *)schemeStr, schemeStrLen);
1014  if (fptr != NULL) {
1015  if (!mdcfg->log_url_scheme) {
1016  fptr += schemeStrLen; /* Strip scheme from stored URL */
1017  }
1018  tok = GetToken(fptr, len - (fptr - line), " \"\'<>]\t", &remptr, &tokLen);
1019  if (tok == fptr) {
1020  SCLogDebug("Found url string");
1021 
1022  /* First copy to temp URL string */
1023  tempUrl = SCMalloc(tokLen);
1024  if (unlikely(tempUrl == NULL)) {
1025  return MIME_DEC_ERR_MEM;
1026  }
1027 
1028  PrintChars(SC_LOG_DEBUG, "RAW URL", tok, tokLen);
1029 
1030  /* Copy over to temp URL while decoding */
1031  tempUrlLen = 0;
1032  for (i = 0; i < tokLen && tok[i] != 0; i++) {
1033  /* url is all lowercase */
1034  tempUrl[tempUrlLen] = u8_tolower(tok[i]);
1035  tempUrlLen++;
1036  }
1037 
1038  urlHost = tempUrl;
1039  urlHostLen = tempUrlLen;
1040  if (mdcfg->log_url_scheme) {
1041  /* tempUrl contains the scheme in the string but
1042  * IsIpv4Host & IsPv6Host methods below require
1043  * an input URL string with scheme stripped. Get a
1044  * reference sub-string urlHost which starts with
1045  * the host instead of the scheme. */
1046  urlHost += schemeStrLen;
1047  urlHostLen -= schemeStrLen;
1048  }
1049 
1050  /* Determine if URL points to an EXE */
1051  if (IsExeUrl(tempUrl, tempUrlLen)) {
1052  flags |= URL_IS_EXE;
1053 
1054  PrintChars(SC_LOG_DEBUG, "EXE URL", tempUrl, tempUrlLen);
1055  }
1056 
1057  /* Make sure remaining URL exists */
1058  if (tempUrlLen > 0) {
1059  if (!(FindExistingUrl(entity, tempUrl, tempUrlLen))) {
1060  /* Now look for numeric IP */
1061  if (IsIpv4Host(urlHost, urlHostLen)) {
1062  flags |= URL_IS_IP4;
1063 
1064  PrintChars(SC_LOG_DEBUG, "IP URL4", tempUrl, tempUrlLen);
1065  } else if (IsIpv6Host(urlHost, urlHostLen)) {
1066  flags |= URL_IS_IP6;
1067 
1068  PrintChars(SC_LOG_DEBUG, "IP URL6", tempUrl, tempUrlLen);
1069  }
1070 
1071  /* Add URL list item */
1072  MimeDecAddUrl(entity, tempUrl, tempUrlLen, flags);
1073  } else {
1074  SCFree(tempUrl);
1075  }
1076  } else {
1077  SCFree(tempUrl);
1078  }
1079 
1080  /* Reset flags for next URL */
1081  flags = 0;
1082  }
1083  }
1084  } while (fptr != NULL);
1085  }
1086 
1087  return ret;
1088 }
1089 
1090 /**
1091  * \brief This function is a pre-processor for handling decoded data chunks that
1092  * then invokes the caller's callback function for further processing
1093  *
1094  * \param chunk The decoded chunk
1095  * \param len The decoded chunk length (varies)
1096  * \param state The current parser state
1097  *
1098  * \return MIME_DEC_OK on success, otherwise < 0 on failure
1099  */
1100 static int ProcessDecodedDataChunk(const uint8_t *chunk, uint32_t len,
1101  MimeDecParseState *state)
1102 {
1103  int ret = MIME_DEC_OK;
1104  uint8_t *remainPtr, *tok;
1105  uint32_t tokLen;
1106 
1107  if ((state->stack != NULL) && (state->stack->top != NULL) &&
1108  (state->stack->top->data != NULL)) {
1109  MimeDecConfig *mdcfg = MimeDecGetConfig();
1110  if (mdcfg != NULL && mdcfg->extract_urls) {
1111  MimeDecEntity *entity = (MimeDecEntity *) state->stack->top->data;
1112  /* If plain text or html, then look for URLs */
1113  if (((entity->ctnt_flags & CTNT_IS_TEXT) ||
1114  (entity->ctnt_flags & CTNT_IS_MSG) ||
1115  (entity->ctnt_flags & CTNT_IS_HTML)) &&
1116  ((entity->ctnt_flags & CTNT_IS_ATTACHMENT) == 0)) {
1117 
1118  /* Parse each line one by one */
1119  remainPtr = (uint8_t *)chunk;
1120  do {
1121  tok = GetLine(
1122  remainPtr, len - (remainPtr - (uint8_t *)chunk), &remainPtr, &tokLen);
1123  if (tok != remainPtr) {
1124  /* If last token found without CR/LF delimiter, then save
1125  * and reconstruct with next chunk
1126  */
1127  if (tok + tokLen - (uint8_t *)chunk == (int)len) {
1128  PrintChars(SC_LOG_DEBUG, "LAST CHUNK LINE - CUTOFF", tok, tokLen);
1129  SCLogDebug("\nCHUNK CUTOFF CHARS: %u delim %u\n", tokLen,
1130  len - (uint32_t)(tok + tokLen - (uint8_t *)chunk));
1131  } else {
1132  /* Search line for URL */
1133  ret = FindUrlStrings(tok, tokLen, state);
1134  if (ret != MIME_DEC_OK) {
1135  SCLogDebug("Error: FindUrlStrings() function"
1136  " failed: %d",
1137  ret);
1138  break;
1139  }
1140  }
1141  }
1142  } while (tok != remainPtr && remainPtr - (uint8_t *)chunk < (int)len);
1143  }
1144  }
1145 
1146  /* Now invoke callback */
1147  if (state->DataChunkProcessorFunc != NULL) {
1148  ret = state->DataChunkProcessorFunc(chunk, len, state);
1149  if (ret != MIME_DEC_OK) {
1150  SCLogDebug("Error: state->dataChunkProcessor() callback function"
1151  " failed");
1152  }
1153  }
1154  } else {
1155  SCLogDebug("Error: Stack pointer missing");
1156  ret = MIME_DEC_ERR_DATA;
1157  }
1158 
1159  /* Reset data chunk buffer */
1160  state->data_chunk_len = 0;
1161 
1162  /* Mark body / file as no longer at beginning */
1163  state->body_begin = 0;
1164 
1165  return ret;
1166 }
1167 
1168 /**
1169  * \brief Processes a remainder (line % 4 = remainder) from the previous line
1170  * such that all base64 decoding attempts are divisible by 4
1171  *
1172  * \param buf The current line
1173  * \param len The length of the line
1174  * \param state The current parser state
1175  * \param force Flag indicating whether decoding should always occur
1176  *
1177  * \return Number of bytes consumed from `buf`
1178  */
1179 static uint8_t ProcessBase64Remainder(
1180  const uint8_t *buf, const uint32_t len, MimeDecParseState *state, int force)
1181 {
1182  uint8_t buf_consumed = 0; /* consumed bytes from 'buf' */
1183  uint8_t cnt = 0;
1184  uint8_t block[B64_BLOCK];
1185 
1186  SCLogDebug("len %u force %d", len, force);
1187 
1188  /* should be impossible, but lets be defensive */
1190  if (state->bvr_len > B64_BLOCK) {
1191  state->bvr_len = 0;
1192  return 0;
1193  }
1194 
1195  /* Strip spaces in remainder */
1196  for (uint8_t i = 0; i < state->bvr_len; i++) {
1197  if (state->bvremain[i] != ' ') {
1198  block[cnt++] = state->bvremain[i];
1199  }
1200  }
1201 
1202  /* if we don't have 4 bytes see if we can fill it from `buf` */
1203  if (buf && len > 0 && cnt != B64_BLOCK) {
1204  for (uint32_t i = 0; i < len && cnt < B64_BLOCK; i++) {
1205  if (buf[i] != ' ') {
1206  block[cnt++] = buf[i];
1207  }
1208  buf_consumed++;
1209  }
1210  if (cnt != 0) {
1211  memcpy(state->bvremain, block, cnt);
1212  }
1213  state->bvr_len = cnt;
1214  } else if (!force && cnt != B64_BLOCK) {
1215  SCLogDebug("incomplete data and no buffer to backfill");
1216  return 0;
1217  }
1218 
1219  /* in force mode pad the block */
1220  if (force && cnt != B64_BLOCK) {
1221  SCLogDebug("force and cnt %u != %u", cnt, B64_BLOCK);
1222  for (uint8_t i = state->bvr_len; i < B64_BLOCK; i++) {
1223  state->bvremain[state->bvr_len++] = '=';
1224  }
1225  }
1226 
1227  /* If data chunk buffer will be full, then clear it now */
1228  if (DATA_CHUNK_SIZE - state->data_chunk_len < ASCII_BLOCK) {
1229 
1230  /* Invoke pre-processor and callback */
1231  uint32_t ret = ProcessDecodedDataChunk(state->data_chunk, state->data_chunk_len, state);
1232  if (ret != MIME_DEC_OK) {
1233  SCLogDebug("Error: ProcessDecodedDataChunk() function failed");
1234  }
1235  }
1236 
1237  if (state->bvr_len == B64_BLOCK || force) {
1238  uint32_t consumed_bytes = 0;
1239  uint32_t remdec = 0;
1240  const uint32_t avail_space = DATA_CHUNK_SIZE - state->data_chunk_len;
1241  PrintChars(SC_LOG_DEBUG, "BASE64 INPUT (bvremain)", state->bvremain, state->bvr_len);
1242  Base64Ecode code = DecodeBase64(state->data_chunk + state->data_chunk_len, avail_space,
1243  state->bvremain, state->bvr_len, &consumed_bytes, &remdec, BASE64_MODE_RFC2045);
1244  SCLogDebug("DecodeBase64 result %u", code);
1245  if (remdec > 0 && (code == BASE64_ECODE_OK || code == BASE64_ECODE_BUF)) {
1246  PrintChars(SC_LOG_DEBUG, "BASE64 DECODED (bvremain)",
1247  state->data_chunk + state->data_chunk_len, remdec);
1248 
1249  state->data_chunk_len += remdec;
1250 
1251  /* If data chunk buffer is now full, then clear */
1252  if (DATA_CHUNK_SIZE - state->data_chunk_len < ASCII_BLOCK) {
1253 
1254  /* Invoke pre-processor and callback */
1255  uint32_t ret =
1256  ProcessDecodedDataChunk(state->data_chunk, state->data_chunk_len, state);
1257  if (ret != MIME_DEC_OK) {
1258  SCLogDebug("Error: ProcessDecodedDataChunk() function "
1259  "failed");
1260  }
1261  }
1262  } else if (code == BASE64_ECODE_ERR) {
1263  /* Track failed base64 */
1266  SCLogDebug("Error: DecodeBase64() function failed");
1267  PrintChars(SC_LOG_DEBUG, "Base64 failed string", state->bvremain, state->bvr_len);
1268  }
1269 
1270  /* Reset remaining */
1271  state->bvr_len = 0;
1272  }
1273 
1274  DEBUG_VALIDATE_BUG_ON(buf_consumed > len);
1275  return buf_consumed;
1276 }
1277 
1278 /**
1279  * \brief Processes a body line by base64-decoding and passing to the data chunk
1280  * processing callback function when the buffer is read
1281  *
1282  * \param buf The current line
1283  * \param len The length of the line
1284  * \param state The current parser state
1285  *
1286  * \return MIME_DEC_OK on success, otherwise < 0 on failure
1287  */
1288 static int ProcessBase64BodyLine(const uint8_t *buf, uint32_t len,
1289  MimeDecParseState *state)
1290 {
1291  int ret = MIME_DEC_OK;
1292  uint32_t numDecoded, remaining = len, offset = 0;
1293 
1294  /* Track long line TODO should we count space padding too? */
1295  if (len > MAX_ENC_LINE_LEN) {
1298  SCLogDebug("Error: Max encoded input line length exceeded %u > %u",
1300  }
1301 
1302  if (state->bvr_len + len < B64_BLOCK) {
1303  memcpy(state->bvremain + state->bvr_len, buf, len);
1304  state->bvr_len += len;
1305  return MIME_DEC_OK;
1306  }
1307 
1308  /* First process remaining from previous line. We will consume
1309  * state->bvremain, filling it from 'buf' until we have a properly
1310  * sized block. Spaces are skipped (rfc2045). If state->bvr_len
1311  * is not 0 after procesing we have no data left at 'buf'. */
1312  if (state->bvr_len > 0) {
1313  uint32_t consumed = ProcessBase64Remainder(buf, len, state, 0);
1314  DEBUG_VALIDATE_BUG_ON(consumed > len);
1315  if (consumed > len)
1316  return MIME_DEC_ERR_PARSE;
1317 
1318  uint32_t left = len - consumed;
1319  if (left < B64_BLOCK) {
1320  DEBUG_VALIDATE_BUG_ON(left + state->bvr_len > B64_BLOCK);
1321  if (left + state->bvr_len > B64_BLOCK)
1322  return MIME_DEC_ERR_PARSE;
1323  memcpy(state->bvremain, buf + consumed, left);
1324  state->bvr_len += left;
1325  return MIME_DEC_OK;
1326  }
1327 
1328  remaining -= consumed;
1329  offset = consumed;
1330  }
1331 
1332  while (remaining > 0 && remaining >= B64_BLOCK) {
1333  uint32_t consumed_bytes = 0;
1334  uint32_t avail_space = DATA_CHUNK_SIZE - state->data_chunk_len;
1335  PrintChars(SC_LOG_DEBUG, "BASE64 INPUT (line)", buf + offset, remaining);
1336  Base64Ecode code = DecodeBase64(state->data_chunk + state->data_chunk_len, avail_space,
1337  buf + offset, remaining, &consumed_bytes, &numDecoded, BASE64_MODE_RFC2045);
1338  SCLogDebug("DecodeBase64 result %u", code);
1339  DEBUG_VALIDATE_BUG_ON(consumed_bytes > remaining);
1340  if (consumed_bytes > remaining)
1341  return MIME_DEC_ERR_PARSE;
1342 
1343  uint32_t leftover_bytes = remaining - consumed_bytes;
1344  if (numDecoded > 0 && (code == BASE64_ECODE_OK || code == BASE64_ECODE_BUF)) {
1345  PrintChars(SC_LOG_DEBUG, "BASE64 DECODED (line)",
1346  state->data_chunk + state->data_chunk_len, numDecoded);
1347 
1348  state->data_chunk_len += numDecoded;
1349 
1350  if ((int)(DATA_CHUNK_SIZE - state->data_chunk_len) < 0) {
1351  SCLogDebug("Error: Invalid Chunk length: %u", state->data_chunk_len);
1352  return MIME_DEC_ERR_PARSE;
1353  }
1354  /* If buffer full, then invoke callback */
1355  if (DATA_CHUNK_SIZE - state->data_chunk_len < ASCII_BLOCK) {
1356  /* Invoke pre-processor and callback */
1357  ret = ProcessDecodedDataChunk(state->data_chunk, state->data_chunk_len, state);
1358  if (ret != MIME_DEC_OK) {
1359  SCLogDebug("Error: ProcessDecodedDataChunk() function failed");
1360  break;
1361  }
1362  }
1363  } else if (code == BASE64_ECODE_ERR) {
1364  /* Track failed base64 */
1367  SCLogDebug("Error: DecodeBase64() function failed");
1368  return MIME_DEC_ERR_DATA;
1369  }
1370 
1371  /* corner case: multiples spaces in the last data, leading it to exceed the block
1372  * size. We strip of spaces this while storing it in bvremain */
1373  if (consumed_bytes == 0 && leftover_bytes > B64_BLOCK) {
1374  DEBUG_VALIDATE_BUG_ON(state->bvr_len != 0);
1375  for (uint32_t i = 0; i < leftover_bytes; i++) {
1376  if (buf[offset] != ' ') {
1378  if (state->bvr_len >= B64_BLOCK)
1379  return MIME_DEC_ERR_DATA;
1380  state->bvremain[state->bvr_len++] = buf[offset];
1381  }
1382  offset++;
1383  }
1384  return MIME_DEC_OK;
1385 
1386  } else if (leftover_bytes > 0 && leftover_bytes <= B64_BLOCK) {
1387  /* If remaining is 4 by this time, we encountered spaces during processing */
1388  DEBUG_VALIDATE_BUG_ON(state->bvr_len != 0);
1389  memcpy(state->bvremain, buf + offset + consumed_bytes, leftover_bytes);
1390  state->bvr_len = (uint8_t)leftover_bytes;
1391  return MIME_DEC_OK;
1392  }
1393 
1394  /* Update counts */
1395  remaining = leftover_bytes;
1396  offset += consumed_bytes;
1397  }
1398  return ret;
1399 }
1400 
1401 /**
1402  * \brief Decoded a hex character into its equivalent byte value for
1403  * quoted-printable decoding
1404  *
1405  * \param h The hex char
1406  *
1407  * \return byte value on success, -1 if failed
1408  **/
1409 static int8_t DecodeQPChar(char h)
1410 {
1411  int8_t res = 0;
1412 
1413  /* 0-9 */
1414  if (h >= 48 && h <= 57) {
1415  res = h - 48;
1416  } else if (h >= 65 && h <= 70) {
1417  /* A-F */
1418  res = h - 55;
1419  } else {
1420  /* Invalid */
1421  res = -1;
1422  }
1423 
1424  return res;
1425 
1426 }
1427 
1428 /**
1429  * \brief Processes a quoted-printable encoded body line by decoding and passing
1430  * to the data chunk processing callback function when the buffer is read
1431  *
1432  * \param buf The current line
1433  * \param len The length of the line
1434  * \param state The current parser state
1435  *
1436  * \return MIME_DEC_OK on success, otherwise < 0 on failure
1437  */
1438 static int ProcessQuotedPrintableBodyLine(const uint8_t *buf, uint32_t len,
1439  MimeDecParseState *state)
1440 {
1441  int ret = MIME_DEC_OK;
1442  uint32_t remaining, offset;
1443  MimeDecEntity *entity = (MimeDecEntity *) state->stack->top->data;
1444  uint8_t c, h1, h2, val;
1445  int16_t res;
1446 
1447  /* Track long line */
1448  if (len > MAX_ENC_LINE_LEN) {
1451  SCLogDebug("Error: Max encoded input line length exceeded %u > %u",
1453  }
1454 
1455  remaining = len;
1456  offset = 0;
1457  while (remaining > 0) {
1458 
1459  c = *(buf + offset);
1460 
1461  /* Copy over normal character */
1462  if (c != '=') {
1463  state->data_chunk[state->data_chunk_len] = c;
1464  state->data_chunk_len++;
1465 
1466  /* Add CRLF sequence if end of line, unless its a partial line */
1467  if (remaining == 1 && state->current_line_delimiter_len > 0) {
1468  memcpy(state->data_chunk + state->data_chunk_len, CRLF, EOL_LEN);
1469  state->data_chunk_len += EOL_LEN;
1470  }
1471  } else if (remaining > 1) {
1472  /* If last character handle as soft line break by ignoring,
1473  otherwise process as escaped '=' character */
1474 
1475  /* Not enough characters */
1476  if (remaining < 3) {
1477  entity->anomaly_flags |= ANOM_INVALID_QP;
1478  state->msg->anomaly_flags |= ANOM_INVALID_QP;
1479  SCLogDebug("Error: Quoted-printable decoding failed");
1480  } else {
1481  h1 = *(buf + offset + 1);
1482  res = DecodeQPChar(h1);
1483  if (res < 0) {
1484  entity->anomaly_flags |= ANOM_INVALID_QP;
1485  state->msg->anomaly_flags |= ANOM_INVALID_QP;
1486  SCLogDebug("Error: Quoted-printable decoding failed");
1487  } else {
1488  val = (uint8_t)(res << 4); /* Shift result left */
1489  h2 = *(buf + offset + 2);
1490  res = DecodeQPChar(h2);
1491  if (res < 0) {
1492  entity->anomaly_flags |= ANOM_INVALID_QP;
1493  state->msg->anomaly_flags |= ANOM_INVALID_QP;
1494  SCLogDebug("Error: Quoted-printable decoding failed");
1495  } else {
1496  /* Decoding sequence succeeded */
1497  val += res;
1498 
1499  state->data_chunk[state->data_chunk_len] = val;
1500  state->data_chunk_len++;
1501 
1502  /* Add CRLF sequence if end of line, unless for partial lines */
1503  if (remaining == 3 && state->current_line_delimiter_len > 0) {
1504  memcpy(state->data_chunk + state->data_chunk_len,
1505  CRLF, EOL_LEN);
1506  state->data_chunk_len += EOL_LEN;
1507  }
1508 
1509  /* Account for extra 2 characters in 3-characted QP
1510  * sequence */
1511  remaining -= 2;
1512  offset += 2;
1513  }
1514  }
1515  }
1516  }
1517 
1518  /* Change by 1 */
1519  remaining--;
1520  offset++;
1521 
1522  /* If buffer full, then invoke callback */
1523  if (DATA_CHUNK_SIZE - state->data_chunk_len < EOL_LEN + 1) {
1524 
1525  /* Invoke pre-processor and callback */
1526  ret = ProcessDecodedDataChunk(state->data_chunk, state->data_chunk_len,
1527  state);
1528  if (ret != MIME_DEC_OK) {
1529  SCLogDebug("Error: ProcessDecodedDataChunk() function "
1530  "failed");
1531  }
1532  }
1533  }
1534 
1535  return ret;
1536 }
1537 
1538 /**
1539  * \brief Processes a body line by base64-decoding (if applicable) and passing to
1540  * the data chunk processing callback function
1541  *
1542  * \param buf The current line
1543  * \param len The length of the line
1544  * \param state The current parser state
1545  *
1546  * \return MIME_DEC_OK on success, otherwise < 0 on failure
1547  */
1548 static int ProcessBodyLine(const uint8_t *buf, uint32_t len,
1549  MimeDecParseState *state)
1550 {
1551  int ret = MIME_DEC_OK;
1552  uint32_t remaining, offset, avail, tobuf;
1553  MimeDecEntity *entity = (MimeDecEntity *) state->stack->top->data;
1554 
1555  SCLogDebug("Processing body line");
1556 
1557  /* Process base-64 content if enabled */
1558  MimeDecConfig *mdcfg = MimeDecGetConfig();
1559  if (mdcfg != NULL && mdcfg->decode_base64 &&
1560  (entity->ctnt_flags & CTNT_IS_BASE64)) {
1561 
1562  ret = ProcessBase64BodyLine(buf, len, state);
1563  if (ret != MIME_DEC_OK) {
1564  SCLogDebug("Error: ProcessBase64BodyLine() function failed");
1565  }
1566  } else if (mdcfg != NULL && mdcfg->decode_quoted_printable &&
1567  (entity->ctnt_flags & CTNT_IS_QP)) {
1568  /* Process quoted-printable content if enabled */
1569  ret = ProcessQuotedPrintableBodyLine(buf, len, state);
1570  if (ret != MIME_DEC_OK) {
1571  SCLogDebug("Error: ProcessQuotedPrintableBodyLine() function "
1572  "failed");
1573  }
1574  } else {
1575  /* Process non-decoded content */
1576  remaining = len + state->current_line_delimiter_len;
1577  offset = 0;
1578  while (remaining > 0) {
1579  /* Plan to add CRLF to the end of each line */
1580  avail = DATA_CHUNK_SIZE - state->data_chunk_len;
1581  tobuf = avail > remaining ? remaining : avail;
1582 
1583  /* Copy over to buffer */
1584  memcpy(state->data_chunk + state->data_chunk_len, buf + offset, tobuf);
1585  state->data_chunk_len += tobuf;
1586 
1587  if ((int) (DATA_CHUNK_SIZE - state->data_chunk_len) < 0) {
1588  SCLogDebug("Error: Invalid Chunk length: %u",
1589  state->data_chunk_len);
1590  ret = MIME_DEC_ERR_PARSE;
1591  break;
1592  }
1593 
1594  /* If buffer full, then invoke callback */
1595  if (DATA_CHUNK_SIZE - state->data_chunk_len == 0) {
1596  /* Invoke pre-processor and callback */
1597  ret = ProcessDecodedDataChunk(state->data_chunk,
1598  state->data_chunk_len, state);
1599  if (ret != MIME_DEC_OK) {
1600  SCLogDebug("Error: ProcessDecodedDataChunk() function "
1601  "failed");
1602  }
1603  }
1604 
1605  remaining -= tobuf;
1606  offset += tobuf;
1607  }
1608  }
1609 
1610  return ret;
1611 }
1612 
1613 /**
1614  * \brief Find the start of a header name on the current line
1615  *
1616  * \param buf The input line (not null-terminated)
1617  * \param blen The length of the input line
1618  * \param glen The output length of the header name
1619  *
1620  * \return Pointer to header name, or NULL if not found
1621  */
1622 static uint8_t * FindMimeHeaderStart(const uint8_t *buf, uint32_t blen, uint32_t *hlen)
1623 {
1624  uint32_t i, valid = 0;
1625  uint8_t *hname = NULL;
1626 
1627  /* Init */
1628  *hlen = 0;
1629 
1630  /* Look for sequence of printable characters followed by ':', or
1631  CRLF then printable characters followed by ':' */
1632  for (i = 0; i < blen && buf[i] != 0; i++) {
1633 
1634  /* If ready for printable characters and found one, then increment */
1635  if (buf[i] != COLON && buf[i] >= PRINTABLE_START &&
1636  buf[i] <= PRINTABLE_END) {
1637  valid++;
1638  } else if (valid > 0 && buf[i] == COLON) {
1639  /* If ready for printable characters, found some, and found colon
1640  * delimiter, then a match is found */
1641  hname = (uint8_t *) buf + i - valid;
1642  *hlen = valid;
1643  break;
1644  } else {
1645  /* Otherwise reset and quit */
1646  break;
1647  }
1648  }
1649 
1650  return hname;
1651 }
1652 
1653 /**
1654  * \brief Find full header name and value on the current line based on the
1655  * current state
1656  *
1657  * \param buf The current line (no CRLF)
1658  * \param blen The length of the current line
1659  * \param state The current state
1660  *
1661  * \return MIME_DEC_OK on success, otherwise < 0 on failure
1662  */
1663 static int FindMimeHeader(const uint8_t *buf, uint32_t blen,
1664  MimeDecParseState *state)
1665 {
1666  int ret = MIME_DEC_OK;
1667  uint8_t *hname, *hval = NULL;
1668  DataValue *dv;
1669  uint32_t hlen, vlen;
1670  int finish_header = 0, new_header = 0;
1671  MimeDecConfig *mdcfg = MimeDecGetConfig();
1672 
1673  /* should not get here with incomplete lines */
1675 
1676  /* Find first header */
1677  hname = FindMimeHeaderStart(buf, blen, &hlen);
1678  if (hname != NULL) {
1679 
1680  /* Warn and track but don't do anything yet */
1681  if (hlen > MAX_HEADER_NAME) {
1684  SCLogDebug("Error: Header name exceeds limit (%u > %u)",
1685  hlen, MAX_HEADER_NAME);
1686  }
1687 
1688  /* Value starts after 'header:' (normalize spaces) */
1689  hval = hname + hlen + 1;
1690  if (hval - buf >= (int)blen) {
1691  SCLogDebug("No Header value found");
1692  hval = NULL;
1693  } else {
1694  while (hval[0] == ' ') {
1695 
1696  /* If last character before end of bounds, set to NULL */
1697  if (hval - buf >= (int)blen - 1) {
1698  SCLogDebug("No Header value found");
1699  hval = NULL;
1700  break;
1701  }
1702 
1703  hval++;
1704  }
1705  }
1706 
1707  /* If new header found, then previous header is finished */
1708  if (state->state_flag == HEADER_STARTED) {
1709  finish_header = 1;
1710  }
1711 
1712  /* Now process new header */
1713  new_header = 1;
1714 
1715  /* Must wait for next line to determine if finished */
1716  state->state_flag = HEADER_STARTED;
1717  } else if (blen == 0) {
1718  /* Found body */
1719  /* No more headers */
1720  state->state_flag = HEADER_DONE;
1721 
1722  finish_header = 1;
1723 
1724  SCLogDebug("All Header processing finished");
1725  } else if (state->state_flag == HEADER_STARTED) {
1726  /* Found multi-line value (ie. Received header) */
1727  /* If max header value exceeded, flag it */
1728  vlen = blen;
1729  if ((mdcfg != NULL) && (state->hvlen + vlen > mdcfg->header_value_depth)) {
1730  SCLogDebug("Error: Header value of length (%u) is too long",
1731  state->hvlen + vlen);
1732  vlen = mdcfg->header_value_depth - state->hvlen;
1735  }
1736  if (vlen > 0) {
1737  dv = AddDataValue(state->hvalue);
1738  if (dv == NULL) {
1739  return MIME_DEC_ERR_MEM;
1740  }
1741  if (state->hvalue == NULL) {
1742  state->hvalue = dv;
1743  }
1744 
1745  dv->value = SCMalloc(vlen);
1746  if (unlikely(dv->value == NULL)) {
1747  return MIME_DEC_ERR_MEM;
1748  }
1749  memcpy(dv->value, buf, vlen);
1750  dv->value_len = vlen;
1751  state->hvlen += vlen;
1752  }
1753  } else {
1754  /* Likely a body without headers */
1755  SCLogDebug("No headers found");
1756 
1757  state->state_flag = BODY_STARTED;
1758 
1759  /* Flag beginning of body */
1760  state->body_begin = 1;
1761  state->body_end = 0;
1762 
1763  ret = ProcessBodyLine(buf, blen, state);
1764  if (ret != MIME_DEC_OK) {
1765  SCLogDebug("Error: ProcessBodyLine() function failed");
1766  return ret;
1767  }
1768  }
1769 
1770  /* If we need to finish a header, then do so below and then cleanup */
1771  if (finish_header) {
1772  /* Store the header value */
1773  ret = StoreMimeHeader(state);
1774  if (ret != MIME_DEC_OK) {
1775  SCLogDebug("Error: StoreMimeHeader() function failed");
1776  return ret;
1777  }
1778  }
1779 
1780  /* When next header is found, we always create a new one */
1781  if (new_header) {
1782  /* Copy name and value to state */
1783  state->hname = SCMalloc(hlen);
1784  if (unlikely(state->hname == NULL)) {
1785  return MIME_DEC_ERR_MEM;
1786  }
1787  memcpy(state->hname, hname, hlen);
1788  state->hlen = hlen;
1789 
1790  if (state->hvalue != NULL) {
1791  SCLogDebug("Error: Parser failed due to unexpected header "
1792  "value");
1793  return MIME_DEC_ERR_DATA;
1794  }
1795 
1796  if (hval != NULL) {
1797  /* If max header value exceeded, flag it */
1798  vlen = blen - (hval - buf);
1799  if ((mdcfg != NULL) && (state->hvlen + vlen > mdcfg->header_value_depth)) {
1800  SCLogDebug("Error: Header value of length (%u) is too long",
1801  state->hvlen + vlen);
1802  vlen = mdcfg->header_value_depth - state->hvlen;
1805  }
1806 
1807  if (vlen > 0) {
1808  state->hvalue = AddDataValue(NULL);
1809  if (state->hvalue == NULL) {
1810  return MIME_DEC_ERR_MEM;
1811  }
1812  state->hvalue->value = SCMalloc(vlen);
1813  if (unlikely(state->hvalue->value == NULL)) {
1814  return MIME_DEC_ERR_MEM;
1815  }
1816  memcpy(state->hvalue->value, hval, vlen);
1817  state->hvalue->value_len = vlen;
1818  state->hvlen += vlen;
1819  }
1820  }
1821  }
1822 
1823  return ret;
1824 }
1825 
1826 /**
1827  * \brief Processes the current line for mime headers and also does post-processing
1828  * when all headers found
1829  *
1830  * \param buf The current line
1831  * \param len The length of the line
1832  * \param state The current parser state
1833  *
1834  * \return MIME_DEC_OK on success, otherwise < 0 on failure
1835  */
1836 static int ProcessMimeHeaders(const uint8_t *buf, uint32_t len,
1837  MimeDecParseState *state)
1838 {
1839  int ret = MIME_DEC_OK;
1840  MimeDecField *field;
1841  uint8_t *rptr = NULL;
1842  uint32_t blen = 0;
1843  MimeDecEntity *entity = (MimeDecEntity *) state->stack->top->data;
1844  uint8_t bptr[RS_MIME_MAX_TOKEN_LEN];
1845 
1846  /* Look for mime header in current line */
1847  ret = FindMimeHeader(buf, len, state);
1848  if (ret != MIME_DEC_OK) {
1849  SCLogDebug("Error: FindMimeHeader() function failed: %d", ret);
1850  return ret;
1851  }
1852 
1853  /* Post-processing after all headers done */
1854  if (state->state_flag == HEADER_DONE) {
1855  /* First determine encoding by looking at Content-Transfer-Encoding */
1856  field = MimeDecFindField(entity, CTNT_TRAN_STR);
1857  if (field != NULL) {
1858  /* Look for base64 */
1859  if (FindBuffer(field->value, field->value_len, (const uint8_t *)BASE64_STR,
1860  (uint16_t)strlen(BASE64_STR))) {
1861  SCLogDebug("Base64 encoding found");
1862  entity->ctnt_flags |= CTNT_IS_BASE64;
1863  } else if (FindBuffer(field->value, field->value_len, (const uint8_t *)QP_STR,
1864  (uint16_t)strlen(QP_STR))) {
1865  /* Look for quoted-printable */
1866  SCLogDebug("quoted-printable encoding found");
1867  entity->ctnt_flags |= CTNT_IS_QP;
1868  }
1869  }
1870 
1871  /* Check for file attachment in content disposition */
1872  field = MimeDecFindField(entity, CTNT_DISP_STR);
1873  if (field != NULL) {
1874  bool truncated_name = false;
1875  if (rs_mime_find_header_token(field->value, field->value_len,
1876  (const uint8_t *)"filename", strlen("filename"), &bptr, &blen)) {
1877  SCLogDebug("File attachment found in disposition");
1878  entity->ctnt_flags |= CTNT_IS_ATTACHMENT;
1879 
1880  if (blen > RS_MIME_MAX_TOKEN_LEN) {
1881  blen = RS_MIME_MAX_TOKEN_LEN;
1882  truncated_name = true;
1883  }
1884 
1885  /* Copy over using dynamic memory */
1886  entity->filename = SCMalloc(blen);
1887  if (unlikely(entity->filename == NULL)) {
1888  return MIME_DEC_ERR_MEM;
1889  }
1890  memcpy(entity->filename, bptr, blen);
1891  entity->filename_len = blen;
1892 
1893  if (truncated_name) {
1896  }
1897  }
1898  }
1899 
1900  /* Check for boundary, encapsulated message, and file name in Content-Type */
1901  field = MimeDecFindField(entity, CTNT_TYPE_STR);
1902  if (field != NULL) {
1903  /* Check if child entity boundary definition found */
1904  // RS_MIME_MAX_TOKEN_LEN is RS_MIME_MAX_TOKEN_LEN on the rust side
1905  if (rs_mime_find_header_token(field->value, field->value_len,
1906  (const uint8_t *)"boundary", strlen("boundary"), &bptr, &blen)) {
1907  state->found_child = 1;
1908  entity->ctnt_flags |= CTNT_IS_MULTIPART;
1909 
1910  if (blen > (BOUNDARY_BUF - 2)) {
1912  return MIME_DEC_ERR_PARSE;
1913  }
1914 
1915  /* Store boundary in parent node */
1916  state->stack->top->bdef = SCMalloc(blen);
1917  if (unlikely(state->stack->top->bdef == NULL)) {
1918  return MIME_DEC_ERR_MEM;
1919  }
1920  memcpy(state->stack->top->bdef, bptr, blen);
1921  state->stack->top->bdef_len = (uint16_t)blen;
1922  }
1923 
1924  /* Look for file name (if not already found) */
1925  if (!(entity->ctnt_flags & CTNT_IS_ATTACHMENT)) {
1926  bool truncated_name = false;
1927  if (rs_mime_find_header_token(field->value, field->value_len,
1928  (const uint8_t *)"name", strlen("name"), &bptr, &blen)) {
1929  SCLogDebug("File attachment found");
1930  entity->ctnt_flags |= CTNT_IS_ATTACHMENT;
1931 
1932  if (blen > RS_MIME_MAX_TOKEN_LEN) {
1933  blen = RS_MIME_MAX_TOKEN_LEN;
1934  truncated_name = true;
1935  }
1936 
1937  /* Copy over using dynamic memory */
1938  entity->filename = SCMalloc(blen);
1939  if (unlikely(entity->filename == NULL)) {
1940  return MIME_DEC_ERR_MEM;
1941  }
1942  memcpy(entity->filename, bptr, blen);
1943  entity->filename_len = blen;
1944 
1945  if (truncated_name) {
1948  }
1949  }
1950  }
1951 
1952  /* Pull out short-hand content type */
1953  entity->ctnt_type = GetToken(field->value, field->value_len, " \r\n;",
1954  &rptr, &entity->ctnt_type_len);
1955  if (entity->ctnt_type != NULL) {
1956  /* Check for encapsulated message */
1957  if (FindBuffer(entity->ctnt_type, entity->ctnt_type_len, (const uint8_t *)MSG_STR,
1958  (uint16_t)strlen(MSG_STR))) {
1959  SCLogDebug("Found encapsulated message entity");
1960 
1961  entity->ctnt_flags |= CTNT_IS_ENV;
1962 
1963  /* Create and push child to stack */
1964  MimeDecEntity *child = MimeDecAddEntity(entity);
1965  if (child == NULL)
1966  return MIME_DEC_ERR_MEM;
1967  child->ctnt_flags |= (CTNT_IS_ENCAP | CTNT_IS_MSG);
1968  PushStack(state->stack);
1969  state->stack->top->data = child;
1970 
1971  /* Mark as encapsulated child */
1972  state->stack->top->is_encap = 1;
1973 
1974  /* Ready to parse headers */
1975  state->state_flag = HEADER_READY;
1976  } else if (FindBuffer(entity->ctnt_type, entity->ctnt_type_len,
1977  (const uint8_t *)MULTIPART_STR,
1978  (uint16_t)strlen(MULTIPART_STR))) {
1979  /* Check for multipart */
1980  SCLogDebug("Found multipart entity");
1981  entity->ctnt_flags |= CTNT_IS_MULTIPART;
1982  } else if (FindBuffer(entity->ctnt_type, entity->ctnt_type_len,
1983  (const uint8_t *)TXT_STR, (uint16_t)strlen(TXT_STR))) {
1984  /* Check for plain text */
1985  SCLogDebug("Found plain text entity");
1986  entity->ctnt_flags |= CTNT_IS_TEXT;
1987  } else if (FindBuffer(entity->ctnt_type, entity->ctnt_type_len,
1988  (const uint8_t *)HTML_STR, (uint16_t)strlen(HTML_STR))) {
1989  /* Check for html */
1990  SCLogDebug("Found html entity");
1991  entity->ctnt_flags |= CTNT_IS_HTML;
1992  }
1993  }
1994  }
1995 
1996  /* Store pointer to Message-ID */
1997  field = MimeDecFindField(entity, MSG_ID_STR);
1998  if (field != NULL) {
1999  entity->msg_id = field->value;
2000  entity->msg_id_len = field->value_len;
2001  }
2002 
2003  /* Flag beginning of body */
2004  state->body_begin = 1;
2005  state->body_end = 0;
2006  }
2007 
2008  return ret;
2009 }
2010 
2011 /**
2012  * \brief Indicates to the parser that the body of an entity has completed
2013  * processing on the previous line
2014  *
2015  * \param state The current parser state
2016  *
2017  * \return MIME_DEC_OK on success, otherwise < 0 on failure
2018  */
2019 
2020 static int ProcessBodyComplete(MimeDecParseState *state)
2021 {
2022  int ret = MIME_DEC_OK;
2023 
2024  SCLogDebug("Process body complete called");
2025 
2026  /* Mark the file as hitting the end */
2027  state->body_end = 1;
2028 
2029  if (state->bvr_len > 0) {
2030  SCLogDebug("Found (%u) remaining base64 bytes not processed",
2031  state->bvr_len);
2032 
2033  /* Process the remainder */
2034  ret = ProcessBase64Remainder(NULL, 0, state, 1);
2035  if (ret != MIME_DEC_OK) {
2036  SCLogDebug("Error: ProcessBase64BodyLine() function failed");
2037  }
2038  }
2039 
2040  /* Invoke pre-processor and callback with remaining data */
2041  ret = ProcessDecodedDataChunk(state->data_chunk, state->data_chunk_len, state);
2042  if (ret != MIME_DEC_OK) {
2043  SCLogDebug("Error: ProcessDecodedDataChunk() function failed");
2044  }
2045 
2046  /* Now reset */
2047  state->body_begin = 0;
2048  state->body_end = 0;
2049 
2050  return ret;
2051 }
2052 
2053 /**
2054  * \brief When a mime boundary is found, look for end boundary and also do stack
2055  * management
2056  *
2057  * \param buf The current line
2058  * \param len The length of the line
2059  * \param bdef_len The length of the current boundary
2060  *
2061  * \return MIME_DEC_OK on success, otherwise < 0 on failure
2062  */
2063 static int ProcessMimeBoundary(
2064  const uint8_t *buf, uint32_t len, uint16_t bdef_len, MimeDecParseState *state)
2065 {
2066  int ret = MIME_DEC_OK;
2067  uint8_t *rptr;
2068  MimeDecEntity *child;
2069 
2070  SCLogDebug("PROCESSING BOUNDARY - START: %d",
2071  state->state_flag);
2072 
2073  /* If previous line was not an end boundary, then we process the body as
2074  * completed */
2075  if (state->state_flag != BODY_END_BOUND) {
2076 
2077  /* First lets complete the body */
2078  ret = ProcessBodyComplete(state);
2079  if (ret != MIME_DEC_OK) {
2080  SCLogDebug("Error: ProcessBodyComplete() function failed");
2081  return ret;
2082  }
2083  } else {
2084  /* If last line was an end boundary, then now we are ready to parse
2085  * headers again */
2086  state->state_flag = HEADER_READY;
2087  }
2088 
2089  /* Update remaining buffer */
2090  rptr = (uint8_t *) buf + bdef_len + 2;
2091 
2092  /* If entity is encapsulated and current and parent didn't define the boundary,
2093  * then pop out */
2094  if (state->stack->top->is_encap && state->stack->top->bdef_len == 0) {
2095 
2096  if (state->stack->top->next == NULL) {
2097  SCLogDebug("Error: Missing parent entity from stack");
2098  return MIME_DEC_ERR_DATA;
2099  }
2100 
2101  if (state->stack->top->next->bdef_len == 0) {
2102 
2103  SCLogDebug("POPPED ENCAPSULATED CHILD FROM STACK: %p=%p",
2104  state->stack->top, state->stack->top->data);
2105 
2106  /* If end of boundary found, pop the child off the stack */
2107  PopStack(state->stack);
2108  if (state->stack->top == NULL) {
2109  SCLogDebug("Error: Message is malformed");
2110  return MIME_DEC_ERR_DATA;
2111  }
2112  }
2113  }
2114 
2115  /* Now check for end of nested boundary */
2116  if (len - (rptr - buf) > 1 && rptr[0] == DASH && rptr[1] == DASH) {
2117  SCLogDebug("FOUND END BOUNDARY, POPPING: %p=%p",
2118  state->stack->top, state->stack->top->data);
2119 
2120  /* If end of boundary found, pop the child off the stack */
2121  PopStack(state->stack);
2122  if (state->stack->top == NULL) {
2123  SCLogDebug("Error: Message is malformed");
2124  return MIME_DEC_ERR_DATA;
2125  }
2126 
2127  /* If current is an encapsulated message with a boundary definition,
2128  * then pop him as well */
2129  if (state->stack->top->is_encap && state->stack->top->bdef_len != 0) {
2130  SCLogDebug("FOUND END BOUNDARY AND ENCAP, POPPING: %p=%p",
2131  state->stack->top, state->stack->top->data);
2132 
2133  PopStack(state->stack);
2134  if (state->stack->top == NULL) {
2135  SCLogDebug("Error: Message is malformed");
2136  return MIME_DEC_ERR_DATA;
2137  }
2138  }
2139 
2140  state->state_flag = BODY_END_BOUND;
2141  } else if (state->found_child) {
2142  /* Otherwise process new child */
2143  SCLogDebug("Child entity created");
2144 
2145  /* Create and push child to stack */
2146  child = MimeDecAddEntity(state->stack->top->data);
2147  if (child == NULL)
2148  return MIME_DEC_ERR_MEM;
2149  child->ctnt_flags |= CTNT_IS_BODYPART;
2150  PushStack(state->stack);
2151  state->stack->top->data = child;
2152 
2153  /* Reset flag */
2154  state->found_child = 0;
2155  } else {
2156  /* Otherwise process sibling */
2157  if (state->stack->top->next == NULL) {
2158  SCLogDebug("Error: Missing parent entity from stack");
2159  return MIME_DEC_ERR_DATA;
2160  }
2161 
2162  SCLogDebug("SIBLING CREATED, POPPING PARENT: %p=%p",
2163  state->stack->top, state->stack->top->data);
2164 
2165  /* First pop current to get access to parent */
2166  PopStack(state->stack);
2167  if (state->stack->top == NULL) {
2168  SCLogDebug("Error: Message is malformed");
2169  return MIME_DEC_ERR_DATA;
2170  }
2171 
2172  /* Create and push child to stack */
2173  child = MimeDecAddEntity(state->stack->top->data);
2174  if (child == NULL)
2175  return MIME_DEC_ERR_MEM;
2176  child->ctnt_flags |= CTNT_IS_BODYPART;
2177  PushStack(state->stack);
2178  state->stack->top->data = child;
2179  }
2180 
2181  /* After boundary look for headers */
2182  if (state->state_flag != BODY_END_BOUND) {
2183  state->state_flag = HEADER_READY;
2184  }
2185 
2186  SCLogDebug("PROCESSING BOUNDARY - END: %d", state->state_flag);
2187  return ret;
2188 }
2189 
2190 /**
2191  * \brief Processes the MIME Entity body based on the input line and current
2192  * state of the parser
2193  *
2194  * \param buf The current line
2195  * \param len The length of the line
2196  *
2197  * \return MIME_DEC_OK on success, otherwise < 0 on failure
2198  */
2199 static int ProcessMimeBody(const uint8_t *buf, uint32_t len,
2200  MimeDecParseState *state)
2201 {
2202  int ret = MIME_DEC_OK;
2203  uint8_t temp[BOUNDARY_BUF];
2204  uint8_t *bstart;
2205  int body_found = 0;
2206  uint16_t tlen;
2207 
2208  if (!g_disable_hashing) {
2209  if (MimeDecGetConfig()->body_md5) {
2210  if (state->body_begin == 1) {
2211  if (state->md5_ctx == NULL) {
2212  state->md5_ctx = SCMd5New();
2213  }
2214  }
2215  SCMd5Update(state->md5_ctx, buf, len + state->current_line_delimiter_len);
2216  }
2217  }
2218 
2219  /* pass empty lines on if we're parsing the body, otherwise we have no use
2220  * for them, and in fact they would disrupt the state tracking */
2221  if (len == 0) {
2222  /* don't start a new body after an end bound based on an empty line */
2223  if (state->state_flag == BODY_END_BOUND) {
2224  SCLogDebug("skip empty line");
2225  return MIME_DEC_OK;
2226  } else if (state->state_flag == HEADER_DONE) {
2227  SCLogDebug("empty line, lets see if we skip it. We're in state %s",
2229  MimeDecEntity *entity = (MimeDecEntity *)state->stack->top->data;
2230  MimeDecConfig *mdcfg = MimeDecGetConfig();
2231  if (entity != NULL && mdcfg != NULL) {
2232  if (mdcfg->decode_base64 && (entity->ctnt_flags & CTNT_IS_BASE64)) {
2233  SCLogDebug("skip empty line");
2234  return MIME_DEC_OK;
2235  } else if (mdcfg->decode_quoted_printable && (entity->ctnt_flags & CTNT_IS_QP)) {
2236  SCLogDebug("skip empty line");
2237  return MIME_DEC_OK;
2238  }
2239  SCLogDebug("not skipping empty line");
2240  }
2241  } else {
2242  SCLogDebug("not skipping line at state %s", MimeDecParseStateGetStatus(state));
2243  }
2244  }
2245 
2246  /* First look for boundary */
2247  MimeDecStackNode *node = state->stack->top;
2248  if (node == NULL) {
2249  SCLogDebug("Error: Invalid stack state");
2250  return MIME_DEC_ERR_PARSE;
2251  }
2252 
2253  /* Traverse through stack to find a boundary definition */
2254  if (state->state_flag == BODY_END_BOUND || node->bdef == NULL) {
2255 
2256  /* If not found, then use parent's boundary */
2257  node = node->next;
2258  while (node != NULL && node->bdef == NULL) {
2259  SCLogDebug("Traversing through stack for node with boundary");
2260  node = node->next;
2261  }
2262  }
2263 
2264  /* This means no boundary / parent w/boundary was found so we are in the body */
2265  if (node == NULL) {
2266  body_found = 1;
2267  } else {
2268 
2269  /* Now look for start of boundary */
2270  if (len > 1 && buf[0] == '-' && buf[1] == '-') {
2271 
2272  tlen = node->bdef_len + 2;
2273  if (tlen > BOUNDARY_BUF) {
2274  if (state->stack->top->data)
2276  SCLogDebug("Error: Long boundary: tlen %u > %d. Set ANOM_LONG_BOUNDARY", tlen,
2277  BOUNDARY_BUF);
2278  return MIME_DEC_ERR_PARSE;
2279  }
2280 
2281  memcpy(temp, "--", 2);
2282  memcpy(temp + 2, node->bdef, node->bdef_len);
2283 
2284  /* Find either next boundary or end boundary */
2285  bstart = FindBuffer(buf, len, temp, tlen);
2286  if (bstart != NULL) {
2287  ret = ProcessMimeBoundary(buf, len, node->bdef_len, state);
2288  if (ret != MIME_DEC_OK) {
2289  SCLogDebug("Error: ProcessMimeBoundary() function "
2290  "failed");
2291  return ret;
2292  }
2293  } else {
2294  /* Otherwise add value to body */
2295  body_found = 1;
2296  }
2297  } else {
2298  /* Otherwise add value to body */
2299  body_found = 1;
2300  }
2301  }
2302 
2303  /* Process body line */
2304  if (body_found) {
2305  state->state_flag = BODY_STARTED;
2306 
2307  ret = ProcessBodyLine(buf, len, state);
2308  if (ret != MIME_DEC_OK) {
2309  SCLogDebug("Error: ProcessBodyLine() function failed");
2310  return ret;
2311  }
2312  }
2313 
2314  return ret;
2315 }
2316 
2318 {
2319  return StateFlags[state->state_flag];
2320 }
2321 
2322 /**
2323  * \brief Processes the MIME Entity based on the input line and current state of
2324  * the parser
2325  *
2326  * \param buf The current line
2327  * \param len The length of the line
2328  *
2329  * \return MIME_DEC_OK on success, otherwise < 0 on failure
2330  */
2331 static int ProcessMimeEntity(const uint8_t *buf, uint32_t len,
2332  MimeDecParseState *state)
2333 {
2334  int ret = MIME_DEC_OK;
2335 
2336  SCLogDebug("START FLAG: %s", StateFlags[state->state_flag]);
2337 
2338  if (state->state_flag == PARSE_ERROR) {
2339  SCLogDebug("START FLAG: PARSE_ERROR, bail");
2340  return MIME_DEC_ERR_STATE;
2341  }
2342 
2343  /* Track long line */
2344  if (len > MAX_LINE_LEN) {
2345  state->stack->top->data->anomaly_flags |= ANOM_LONG_LINE;
2346  state->msg->anomaly_flags |= ANOM_LONG_LINE;
2347  SCLogDebug("Error: Max input line length exceeded %u > %u", len,
2348  MAX_LINE_LEN);
2349  }
2350 
2351  /* Looking for headers */
2352  if (state->state_flag == HEADER_READY ||
2353  state->state_flag == HEADER_STARTED) {
2354 
2355  SCLogDebug("Processing Headers");
2356 
2357  /* Process message headers */
2358  ret = ProcessMimeHeaders(buf, len, state);
2359  if (ret != MIME_DEC_OK) {
2360  SCLogDebug("Error: ProcessMimeHeaders() function failed: %d",
2361  ret);
2362  return ret;
2363  }
2364  } else {
2365  /* Processing body */
2366  SCLogDebug("Processing Body of: %p", state->stack->top);
2367 
2368  ret = ProcessMimeBody(buf, len, state);
2369  if (ret != MIME_DEC_OK) {
2370  SCLogDebug("Error: ProcessMimeBody() function failed: %d",
2371  ret);
2372  return ret;
2373  }
2374  }
2375 
2376  SCLogDebug("END FLAG: %s", StateFlags[state->state_flag]);
2377 
2378  return ret;
2379 }
2380 
2381 /**
2382  * \brief Init the parser by allocating memory for the state and top-level entity
2383  *
2384  * \param data A caller-specified pointer to data for access within the data chunk
2385  * processor callback function
2386  * \param dcpfunc The data chunk processor callback function
2387  *
2388  * \return A pointer to the state object, or NULL if the operation fails
2389  */
2391  int (*DataChunkProcessorFunc)(const uint8_t *chunk, uint32_t len,
2392  MimeDecParseState *state))
2393 {
2394  MimeDecParseState *state;
2395  MimeDecEntity *mimeMsg;
2396 
2397  state = SCMalloc(sizeof(MimeDecParseState));
2398  if (unlikely(state == NULL)) {
2399  return NULL;
2400  }
2401  memset(state, 0x00, sizeof(MimeDecParseState));
2402 
2403  state->stack = SCMalloc(sizeof(MimeDecStack));
2404  if (unlikely(state->stack == NULL)) {
2405  SCFree(state);
2406  return NULL;
2407  }
2408  memset(state->stack, 0x00, sizeof(MimeDecStack));
2409 
2410  mimeMsg = SCMalloc(sizeof(MimeDecEntity));
2411  if (unlikely(mimeMsg == NULL)) {
2412  SCFree(state->stack);
2413  SCFree(state);
2414  return NULL;
2415  }
2416  memset(mimeMsg, 0x00, sizeof(MimeDecEntity));
2417  mimeMsg->ctnt_flags |= CTNT_IS_MSG;
2418 
2419  /* Init state */
2420  state->msg = mimeMsg;
2421  PushStack(state->stack);
2422  if (state->stack->top == NULL) {
2423  SCFree(state->stack);
2424  SCFree(state);
2425  return NULL;
2426  }
2427  state->stack->top->data = mimeMsg;
2428  state->state_flag = HEADER_READY;
2429  state->data = data;
2430  state->DataChunkProcessorFunc = DataChunkProcessorFunc;
2431 
2432  return state;
2433 }
2434 
2435 /**
2436  * \brief De-Init parser by freeing up any residual memory
2437  *
2438  * \param state The parser state
2439  *
2440  * \return none
2441  */
2443 {
2444  uint32_t cnt = 0;
2445 
2446  while (state->stack->top != NULL) {
2447  SCLogDebug("Remaining on stack: [%p]=>[%p]",
2448  state->stack->top, state->stack->top->data);
2449 
2450  PopStack(state->stack);
2451  cnt++;
2452  }
2453 
2454  if (cnt > 1) {
2456  SCLogDebug("Warning: Stack is not empty upon completion of "
2457  "processing (%u items remaining)", cnt);
2458  }
2459 
2460  SCFree(state->hname);
2461  FreeDataValue(state->hvalue);
2462  FreeMimeDecStack(state->stack);
2463  if (state->md5_ctx)
2464  SCMd5Free(state->md5_ctx);
2465  SCFree(state);
2466 }
2467 
2468 /**
2469  * \brief Called to indicate that the last message line has been processed and
2470  * the parsing operation is complete
2471  *
2472  * This function should be called directly by the caller.
2473  *
2474  * \param state The parser state
2475  *
2476  * \return MIME_DEC_OK on success, otherwise < 0 on failure
2477  */
2479 {
2480  int ret = MIME_DEC_OK;
2481 
2482  SCLogDebug("Parsing flagged as completed");
2483 
2484  if (state->state_flag == PARSE_ERROR) {
2485  SCLogDebug("parser in error state: PARSE_ERROR");
2486  return MIME_DEC_ERR_STATE;
2487  }
2488 
2489  /* Store the header value */
2490  ret = StoreMimeHeader(state);
2491  if (ret != MIME_DEC_OK) {
2492  SCLogDebug("Error: StoreMimeHeader() function failed");
2493  return ret;
2494  }
2495 
2496  /* Lets complete the body */
2497  ret = ProcessBodyComplete(state);
2498  if (ret != MIME_DEC_OK) {
2499  SCLogDebug("Error: ProcessBodyComplete() function failed");
2500  return ret;
2501  }
2502 
2503  if (state->md5_ctx) {
2504  SCMd5Finalize(state->md5_ctx, state->md5, sizeof(state->md5));
2505  state->md5_ctx = NULL;
2506  state->has_md5 = true;
2507  }
2508 
2509  if (state->stack->top == NULL) {
2511  SCLogDebug("Error: Message is malformed");
2512  return MIME_DEC_ERR_DATA;
2513  }
2514 
2515  /* If encapsulated, pop off the stack */
2516  if (state->stack->top->is_encap) {
2517  PopStack(state->stack);
2518  if (state->stack->top == NULL) {
2520  SCLogDebug("Error: Message is malformed");
2521  return MIME_DEC_ERR_DATA;
2522  }
2523  }
2524 
2525  /* Look extra stack items remaining */
2526  if (state->stack->top->next != NULL) {
2528  SCLogDebug("Warning: Message has unclosed message part boundary");
2529  }
2530 
2531  state->state_flag = PARSE_DONE;
2532 
2533  return ret;
2534 }
2535 
2536 /**
2537  * \brief Parse a line of a MIME message and update the parser state
2538  *
2539  * \param line A string representing the line (w/out CRLF)
2540  * \param len The length of the line
2541  * \param delim_len The length of the line end delimiter
2542  * \param state The parser state
2543  *
2544  * \return MIME_DEC_OK on success, otherwise < 0 on failure
2545  */
2546 int MimeDecParseLine(const uint8_t *line, const uint32_t len,
2547  const uint8_t delim_len, MimeDecParseState *state)
2548 {
2549  int ret = MIME_DEC_OK;
2550 
2551  /* For debugging purposes */
2552  if (len > 0) {
2553  PrintChars(SC_LOG_DEBUG, "SMTP LINE", line, len);
2554  } else {
2555  SCLogDebug("SMTP LINE - EMPTY");
2556  }
2557 
2558  state->current_line_delimiter_len = delim_len;
2559  /* Process the entity */
2560  ret = ProcessMimeEntity(line, len, state);
2561  if (ret != MIME_DEC_OK) {
2562  state->state_flag = PARSE_ERROR;
2563  SCLogDebug("Error: ProcessMimeEntity() function failed: %d", ret);
2564  }
2565 
2566  return ret;
2567 }
2568 
2569 /**
2570  * \brief Parses an entire message when available in its entirety (wraps the
2571  * line-based parsing functions)
2572  *
2573  * \param buf Buffer pointing to the full message
2574  * \param blen Length of the buffer
2575  * \param data Caller data to be available in callback
2576  * \param dcpfunc Callback for processing each decoded body data chunk
2577  *
2578  * \return A pointer to the decoded MIME message, or NULL if the operation fails
2579  */
2580 MimeDecEntity * MimeDecParseFullMsg(const uint8_t *buf, uint32_t blen, void *data,
2581  int (*dcpfunc)(const uint8_t *chunk, uint32_t len,
2582  MimeDecParseState *state))
2583 {
2584  int ret = MIME_DEC_OK;
2585  uint8_t *remainPtr, *tok;
2586  uint32_t tokLen;
2587 
2588  MimeDecParseState *state = MimeDecInitParser(data, dcpfunc);
2589  if (state == NULL) {
2590  SCLogDebug("Error: MimeDecInitParser() function failed to create "
2591  "state");
2592  return NULL;
2593  }
2594 
2595  MimeDecEntity *msg = state->msg;
2596 
2597  /* Parse each line one by one */
2598  remainPtr = (uint8_t *) buf;
2599  uint8_t *line = NULL;
2600  do {
2601  tok = GetLine(remainPtr, blen - (remainPtr - buf), &remainPtr, &tokLen);
2602  if (tok != remainPtr) {
2603 
2604  line = tok;
2605 
2606  if ((remainPtr - tok) - tokLen > UINT8_MAX) {
2607  SCLogDebug("Error: MimeDecParseLine() overflow: %ld", (remainPtr - tok) - tokLen);
2608  ret = MIME_DEC_ERR_OVERFLOW;
2609  break;
2610  }
2611  state->current_line_delimiter_len = (uint8_t)((remainPtr - tok) - tokLen);
2612  /* Parse the line */
2613  ret = MimeDecParseLine(line, tokLen, state->current_line_delimiter_len, state);
2614  if (ret != MIME_DEC_OK) {
2615  SCLogDebug("Error: MimeDecParseLine() function failed: %d",
2616  ret);
2617  break;
2618  }
2619  }
2620 
2621  } while (tok != remainPtr && remainPtr - buf < (int)blen);
2622 
2623  if (ret == MIME_DEC_OK) {
2624  SCLogDebug("Message parser was successful");
2625 
2626  /* Now complete message */
2627  ret = MimeDecParseComplete(state);
2628  if (ret != MIME_DEC_OK) {
2629  SCLogDebug("Error: MimeDecParseComplete() function failed");
2630  }
2631  }
2632 
2633  /* De-allocate memory for parser */
2634  MimeDecDeInitParser(state);
2635 
2636  if (ret != MIME_DEC_OK) {
2638  msg = NULL;
2639  }
2640 
2641  return msg;
2642 }
2643 
2644 #ifdef UNITTESTS
2645 
2646 /* Helper body chunk callback function */
2647 static int TestDataChunkCallback(const uint8_t *chunk, uint32_t len,
2648  MimeDecParseState *state)
2649 {
2650  uint32_t *line_count = (uint32_t *) state->data;
2651 
2652  if (state->body_begin) {
2653  SCLogDebug("Body begin (len=%u)", len);
2654  }
2655 
2656  /* Add up the line counts */
2657  if (len > 0) {
2658 
2659  uint8_t *remainPtr;
2660  uint8_t *tok;
2661  uint32_t tokLen;
2662 
2663  PrintChars(SC_LOG_DEBUG, "CHUNK", chunk, len);
2664 
2665  /* Parse each line one by one */
2666  remainPtr = (uint8_t *) chunk;
2667  do {
2668  tok = GetLine(remainPtr, len - (remainPtr - (uint8_t *) chunk),
2669  &remainPtr, &tokLen);
2670  if (tok != NULL && tok != remainPtr) {
2671  (*line_count)++;
2672  }
2673 
2674  } while (tok != NULL && tok != remainPtr &&
2675  (uint32_t)(remainPtr - (uint8_t *) chunk) < len);
2676 
2677  SCLogDebug("line count (len=%u): %u", len, *line_count);
2678  }
2679 
2680  if (state->body_end) {
2681  SCLogDebug("Body end (len=%u)", len);
2682  }
2683 
2684  return MIME_DEC_OK;
2685 }
2686 
2687 /* Test simple case of line counts */
2688 static int MimeDecParseLineTest01(void)
2689 {
2690  uint32_t line_count = 0;
2691 
2692  /* Init parser */
2693  MimeDecParseState *state = MimeDecInitParser(&line_count,
2694  TestDataChunkCallback);
2695 
2696  const char *str = "From: Sender1\n";
2697  FAIL_IF_NOT(MimeDecParseLine((uint8_t *)str, strlen(str) - 1, 1, state) == MIME_DEC_OK);
2698 
2699  str = "To: Recipient1\n";
2700  FAIL_IF_NOT(MimeDecParseLine((uint8_t *)str, strlen(str) - 1, 1, state) == MIME_DEC_OK);
2701 
2702  str = "Content-Type: text/plain\n";
2703  FAIL_IF_NOT(MimeDecParseLine((uint8_t *)str, strlen(str) - 1, 1, state) == MIME_DEC_OK);
2704 
2705  str = "\n";
2706  FAIL_IF_NOT(MimeDecParseLine((uint8_t *)str, strlen(str) - 1, 1, state) == MIME_DEC_OK);
2707 
2708  str = "A simple message line 1\n";
2709  FAIL_IF_NOT(MimeDecParseLine((uint8_t *)str, strlen(str) - 1, 1, state) == MIME_DEC_OK);
2710 
2711  str = "A simple message line 2\n";
2712  FAIL_IF_NOT(MimeDecParseLine((uint8_t *)str, strlen(str) - 1, 1, state) == MIME_DEC_OK);
2713 
2714  str = "A simple message line 3\n";
2715  FAIL_IF_NOT(MimeDecParseLine((uint8_t *)str, strlen(str) - 1, 1, state) == MIME_DEC_OK);
2716 
2717  /* Completed */
2719 
2720  MimeDecEntity *msg = state->msg;
2721  FAIL_IF_NOT_NULL(msg->next);
2722  FAIL_IF_NOT_NULL(msg->child);
2723 
2725 
2726  /* De Init parser */
2727  MimeDecDeInitParser(state);
2728 
2729  FAIL_IF_NOT(line_count == 3);
2730  PASS;
2731 }
2732 
2733 /* Test simple case of EXE URL extraction */
2734 static int MimeDecParseLineTest02(void)
2735 {
2736  uint32_t line_count = 0;
2737 
2738  ConfNode *url_schemes = ConfNodeNew();
2739  ConfNode *scheme = ConfNodeNew();
2740  FAIL_IF_NULL(url_schemes);
2741  FAIL_IF_NULL(scheme);
2742 
2743  url_schemes->is_seq = 1;
2744  scheme->val = SCStrdup("http://");
2745  FAIL_IF_NULL(scheme->val);
2746  TAILQ_INSERT_TAIL(&url_schemes->head, scheme, next);
2747 
2748  MimeDecGetConfig()->decode_base64 = true;
2750  MimeDecGetConfig()->extract_urls = true;
2751  MimeDecGetConfig()->extract_urls_schemes = url_schemes;
2752 
2753  /* Init parser */
2754  MimeDecParseState *state = MimeDecInitParser(&line_count,
2755  TestDataChunkCallback);
2756 
2757  const char *str = "From: Sender1\r\n";
2758  FAIL_IF_NOT(MimeDecParseLine((uint8_t *)str, strlen(str) - 2, 2, state) == MIME_DEC_OK);
2759 
2760  str = "To: Recipient1\r\n";
2761  FAIL_IF_NOT(MimeDecParseLine((uint8_t *)str, strlen(str) - 2, 2, state) == MIME_DEC_OK);
2762 
2763  str = "Content-Type: text/plain\r\n";
2764  FAIL_IF_NOT(MimeDecParseLine((uint8_t *)str, strlen(str) - 2, 2, state) == MIME_DEC_OK);
2765 
2766  str = "\r\n";
2767  FAIL_IF_NOT(MimeDecParseLine((uint8_t *)str, strlen(str) - 2, 2, state) == MIME_DEC_OK);
2768 
2769  str = "A simple message line 1\r\n";
2770  FAIL_IF_NOT(MimeDecParseLine((uint8_t *)str, strlen(str) - 2, 2, state) == MIME_DEC_OK);
2771 
2772  str = "A simple message line 2 click on http://www.test.com/malware.exe?"
2773  "hahah hopefully you click this link\r\n";
2774  FAIL_IF_NOT(MimeDecParseLine((uint8_t *)str, strlen(str) - 2, 2, state) == MIME_DEC_OK);
2775 
2776  /* Completed */
2778 
2779  MimeDecEntity *msg = state->msg;
2780  FAIL_IF_NULL(msg);
2781  FAIL_IF_NULL(msg->url_list);
2782  FAIL_IF_NOT((msg->url_list->url_flags & URL_IS_EXE));
2784 
2785  /* De Init parser */
2786  MimeDecDeInitParser(state);
2787  ConfNodeFree(url_schemes);
2789 
2790  FAIL_IF_NOT(line_count == 2);
2791  PASS;
2792 }
2793 
2794 /* Test error case where no url schemes set in config */
2795 static int MimeFindUrlStringsTest01(void)
2796 {
2797  int ret = MIME_DEC_OK;
2798  uint32_t line_count = 0;
2799 
2800  MimeDecGetConfig()->extract_urls = true;
2802  MimeDecGetConfig()->log_url_scheme = false;
2803 
2804  /* Init parser */
2805  MimeDecParseState *state = MimeDecInitParser(&line_count, TestDataChunkCallback);
2806 
2807  const char *str = "test";
2808  ret = FindUrlStrings((uint8_t *)str, strlen(str), state);
2809  /* Expected error since extract_url_schemes is NULL */
2811 
2812  /* Completed */
2813  ret = MimeDecParseComplete(state);
2814  FAIL_IF_NOT(ret == MIME_DEC_OK);
2815 
2816  MimeDecEntity *msg = state->msg;
2818 
2819  /* De Init parser */
2820  MimeDecDeInitParser(state);
2821 
2822  PASS;
2823 }
2824 
2825 /* Test simple case of URL extraction */
2826 static int MimeFindUrlStringsTest02(void)
2827 {
2828  int ret = MIME_DEC_OK;
2829  uint32_t line_count = 0;
2830  ConfNode *url_schemes = ConfNodeNew();
2831  ConfNode *scheme = ConfNodeNew();
2832  FAIL_IF_NULL(url_schemes);
2833  FAIL_IF_NULL(scheme);
2834 
2835  url_schemes->is_seq = 1;
2836  scheme->val = SCStrdup("http://");
2837  FAIL_IF_NULL(scheme->val);
2838  TAILQ_INSERT_TAIL(&url_schemes->head, scheme, next);
2839 
2840  MimeDecGetConfig()->extract_urls = true;
2841  MimeDecGetConfig()->extract_urls_schemes = url_schemes;
2842  MimeDecGetConfig()->log_url_scheme = false;
2843 
2844  /* Init parser */
2845  MimeDecParseState *state = MimeDecInitParser(&line_count, TestDataChunkCallback);
2846 
2847  const char *str = "A simple message click on "
2848  "http://www.test.com/malware.exe? "
2849  "hahah hopefully you click this link";
2850  ret = FindUrlStrings((uint8_t *)str, strlen(str), state);
2851  FAIL_IF_NOT(ret == MIME_DEC_OK);
2852 
2853  /* Completed */
2854  ret = MimeDecParseComplete(state);
2855  FAIL_IF_NOT(ret == MIME_DEC_OK);
2856 
2857  MimeDecEntity *msg = state->msg;
2858 
2859  FAIL_IF(msg->url_list == NULL);
2860 
2861  FAIL_IF_NOT(msg->url_list->url_flags & URL_IS_EXE);
2862  FAIL_IF_NOT(
2863  memcmp("www.test.com/malware.exe?", msg->url_list->url, msg->url_list->url_len) == 0);
2864 
2866 
2867  /* De Init parser */
2868  MimeDecDeInitParser(state);
2869 
2870  ConfNodeFree(url_schemes);
2872 
2873  PASS;
2874 }
2875 
2876 /* Test URL extraction with multiple schemes and URLs */
2877 static int MimeFindUrlStringsTest03(void)
2878 {
2879  int ret = MIME_DEC_OK;
2880  uint32_t line_count = 0;
2881  ConfNode *url_schemes = ConfNodeNew();
2882  ConfNode *scheme1 = ConfNodeNew();
2883  ConfNode *scheme2 = ConfNodeNew();
2884  FAIL_IF_NULL(url_schemes);
2885  FAIL_IF_NULL(scheme1);
2886  FAIL_IF_NULL(scheme2);
2887 
2888  url_schemes->is_seq = 1;
2889  scheme1->val = SCStrdup("http://");
2890  FAIL_IF_NULL(scheme1->val);
2891  TAILQ_INSERT_TAIL(&url_schemes->head, scheme1, next);
2892  scheme2->val = SCStrdup("https://");
2893  FAIL_IF_NULL(scheme2->val);
2894  TAILQ_INSERT_TAIL(&url_schemes->head, scheme2, next);
2895 
2896  MimeDecGetConfig()->extract_urls = true;
2897  MimeDecGetConfig()->extract_urls_schemes = url_schemes;
2898  MimeDecGetConfig()->log_url_scheme = false;
2899 
2900  /* Init parser */
2901  MimeDecParseState *state = MimeDecInitParser(&line_count, TestDataChunkCallback);
2902 
2903  const char *str = "A simple message click on "
2904  "http://www.test.com/malware.exe? "
2905  "hahah hopefully you click this link, or "
2906  "you can go to http://www.test.com/test/01.html and "
2907  "https://www.test.com/test/02.php";
2908  ret = FindUrlStrings((uint8_t *)str, strlen(str), state);
2909  FAIL_IF_NOT(ret == MIME_DEC_OK);
2910 
2911  /* Completed */
2912  ret = MimeDecParseComplete(state);
2913  FAIL_IF_NOT(ret == MIME_DEC_OK);
2914 
2915  MimeDecEntity *msg = state->msg;
2916 
2917  FAIL_IF(msg->url_list == NULL);
2918 
2919  MimeDecUrl *url = msg->url_list;
2920  FAIL_IF_NOT(memcmp("www.test.com/test/02.php", url->url, url->url_len) == 0);
2921 
2922  url = url->next;
2923  FAIL_IF_NOT(memcmp("www.test.com/test/01.html", url->url, url->url_len) == 0);
2924 
2925  url = url->next;
2926  FAIL_IF_NOT(memcmp("www.test.com/malware.exe?", url->url, url->url_len) == 0);
2927 
2929 
2930  /* De Init parser */
2931  MimeDecDeInitParser(state);
2932 
2933  ConfNodeFree(url_schemes);
2935 
2936  PASS;
2937 }
2938 
2939 /* Test URL extraction with multiple schemes and URLs with
2940  * log_url_scheme enabled in the MIME config */
2941 static int MimeFindUrlStringsTest04(void)
2942 {
2943  int ret = MIME_DEC_OK;
2944  uint32_t line_count = 0;
2945  ConfNode *url_schemes = ConfNodeNew();
2946  ConfNode *scheme1 = ConfNodeNew();
2947  ConfNode *scheme2 = ConfNodeNew();
2948  FAIL_IF_NULL(url_schemes);
2949  FAIL_IF_NULL(scheme1);
2950  FAIL_IF_NULL(scheme2);
2951 
2952  url_schemes->is_seq = 1;
2953  scheme1->val = SCStrdup("http://");
2954  FAIL_IF_NULL(scheme1->val);
2955  TAILQ_INSERT_TAIL(&url_schemes->head, scheme1, next);
2956  scheme2->val = SCStrdup("https://");
2957  FAIL_IF_NULL(scheme2->val);
2958  TAILQ_INSERT_TAIL(&url_schemes->head, scheme2, next);
2959 
2960  MimeDecGetConfig()->extract_urls = true;
2961  MimeDecGetConfig()->extract_urls_schemes = url_schemes;
2962  MimeDecGetConfig()->log_url_scheme = true;
2963 
2964  /* Init parser */
2965  MimeDecParseState *state = MimeDecInitParser(&line_count, TestDataChunkCallback);
2966 
2967  const char *str = "A simple message click on "
2968  "http://www.test.com/malware.exe? "
2969  "hahah hopefully you click this link, or "
2970  "you can go to http://www.test.com/test/01.html and "
2971  "https://www.test.com/test/02.php";
2972  ret = FindUrlStrings((uint8_t *)str, strlen(str), state);
2973  FAIL_IF_NOT(ret == MIME_DEC_OK);
2974 
2975  /* Completed */
2976  ret = MimeDecParseComplete(state);
2977  FAIL_IF_NOT(ret == MIME_DEC_OK);
2978 
2979  MimeDecEntity *msg = state->msg;
2980 
2981  FAIL_IF(msg->url_list == NULL);
2982 
2983  MimeDecUrl *url = msg->url_list;
2984  FAIL_IF_NOT(memcmp("https://www.test.com/test/02.php", url->url, url->url_len) == 0);
2985 
2986  url = url->next;
2987  FAIL_IF_NOT(memcmp("http://www.test.com/test/01.html", url->url, url->url_len) == 0);
2988 
2989  url = url->next;
2990  FAIL_IF_NOT(memcmp("http://www.test.com/malware.exe?", url->url, url->url_len) == 0);
2991 
2993 
2994  /* De Init parser */
2995  MimeDecDeInitParser(state);
2996 
2997  ConfNodeFree(url_schemes);
2999 
3000  PASS;
3001 }
3002 
3003 /* Test URL extraction of IPV4 and IPV6 URLs with log_url_scheme
3004  * enabled in the MIME config */
3005 static int MimeFindUrlStringsTest05(void)
3006 {
3007  int ret = MIME_DEC_OK;
3008  uint32_t line_count = 0;
3009  ConfNode *url_schemes = ConfNodeNew();
3010  ConfNode *scheme = ConfNodeNew();
3011  FAIL_IF_NULL(url_schemes);
3012  FAIL_IF_NULL(scheme);
3013 
3014  url_schemes->is_seq = 1;
3015  scheme->val = SCStrdup("http://");
3016  FAIL_IF_NULL(scheme->val);
3017  TAILQ_INSERT_TAIL(&url_schemes->head, scheme, next);
3018 
3019  MimeDecGetConfig()->extract_urls = true;
3020  MimeDecGetConfig()->extract_urls_schemes = url_schemes;
3021  MimeDecGetConfig()->log_url_scheme = true;
3022 
3023  /* Init parser */
3024  MimeDecParseState *state = MimeDecInitParser(&line_count, TestDataChunkCallback);
3025 
3026  const char *str = "A simple message click on "
3027  "http://192.168.1.1/test/01.html "
3028  "hahah hopefully you click this link or this one "
3029  "http://0:0:0:0:0:0:0:0/test/02.php";
3030  ret = FindUrlStrings((uint8_t *)str, strlen(str), state);
3031  FAIL_IF_NOT(ret == MIME_DEC_OK);
3032 
3033  /* Completed */
3034  ret = MimeDecParseComplete(state);
3035  FAIL_IF_NOT(ret == MIME_DEC_OK);
3036 
3037  MimeDecEntity *msg = state->msg;
3038 
3039  FAIL_IF(msg->url_list == NULL);
3040 
3041  MimeDecUrl *url = msg->url_list;
3043  FAIL_IF_NOT(memcmp("http://0:0:0:0:0:0:0:0/test/02.php", url->url, url->url_len) == 0);
3044 
3045  url = url->next;
3047  FAIL_IF_NOT(memcmp("http://192.168.1.1/test/01.html", url->url, url->url_len) == 0);
3048 
3050 
3051  /* De Init parser */
3052  MimeDecDeInitParser(state);
3053 
3054  ConfNodeFree(url_schemes);
3056 
3057  PASS;
3058 }
3059 
3060 /* Test full message with linebreaks */
3061 static int MimeDecParseFullMsgTest01(void)
3062 {
3063  uint32_t expected_count = 3;
3064  uint32_t line_count = 0;
3065 
3066  char msg[] = "From: Sender1\r\n"
3067  "To: Recipient1\r\n"
3068  "Content-Type: text/plain\r\n"
3069  "\r\n"
3070  "Line 1\r\n"
3071  "Line 2\r\n"
3072  "Line 3\r\n";
3073 
3074  MimeDecEntity *entity = MimeDecParseFullMsg((uint8_t *)msg, strlen(msg), &line_count,
3075  TestDataChunkCallback);
3076  if (entity == NULL) {
3077  SCLogInfo("Warning: Message failed to parse");
3078  return 0;
3079  }
3080 
3081  MimeDecFreeEntity(entity);
3082 
3083  if (expected_count != line_count) {
3084  SCLogInfo("Warning: Line count is invalid: expected - %d actual - %d",
3085  expected_count, line_count);
3086  return 0;
3087  }
3088 
3089  return 1;
3090 }
3091 
3092 /* Test full message with linebreaks */
3093 static int MimeDecParseFullMsgTest02(void)
3094 {
3095  uint32_t expected_count = 3;
3096  uint32_t line_count = 0;
3097 
3098  char msg[] = "From: Sender2\r\n"
3099  "To: Recipient2\r\n"
3100  "Subject: subject2\r\n"
3101  "Content-Type: text/plain\r\n"
3102  "\r\n"
3103  "Line 1\r\n"
3104  "Line 2\r\n"
3105  "Line 3\r\n";
3106 
3107  MimeDecEntity *entity = MimeDecParseFullMsg((uint8_t *)msg, strlen(msg), &line_count,
3108  TestDataChunkCallback);
3109 
3110  if (entity == NULL) {
3111  SCLogInfo("Warning: Message failed to parse");
3112  return 0;
3113  }
3114 
3115  MimeDecField *field = MimeDecFindField(entity, "subject");
3116  if (field == NULL) {
3117  SCLogInfo("Warning: Message failed to parse");
3118  return 0;
3119  }
3120 
3121  if (field->value_len != sizeof("subject2") - 1) {
3122  SCLogInfo("Warning: failed to get subject");
3123  return 0;
3124  }
3125 
3126  if (memcmp(field->value, "subject2", field->value_len) != 0) {
3127  SCLogInfo("Warning: failed to get subject");
3128  return 0;
3129  }
3130 
3131 
3132  MimeDecFreeEntity(entity);
3133 
3134  if (expected_count != line_count) {
3135  SCLogInfo("Warning: Line count is invalid: expected - %d actual - %d", expected_count,
3136  line_count);
3137  return 0;
3138  }
3139 
3140  return 1;
3141 }
3142 
3143 static int MimeBase64DecodeTest01(void)
3144 {
3145  int ret = 0;
3146  uint32_t consumed_bytes = 0, num_decoded = 0;
3147 
3148  const char *msg = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890@"
3149  "#$%^&*()-=_+,./;'[]<>?:";
3150  const char *base64msg = "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QU"
3151  "VJTVFVWV1hZWjEyMzQ1Njc4OTBAIyQlXiYqKCktPV8rLC4vOydbXTw+Pzo=";
3152 
3153  uint8_t *dst = SCMalloc(strlen(msg) + 1);
3154  if (dst == NULL)
3155  return 0;
3156 
3157  ret = DecodeBase64(dst, strlen(msg) + 1, (const uint8_t *)base64msg, strlen(base64msg),
3158  &consumed_bytes, &num_decoded, BASE64_MODE_RFC2045);
3159 
3160  if (memcmp(dst, msg, strlen(msg)) == 0) {
3161  ret = 1;
3162  }
3163 
3164  SCFree(dst);
3165 
3166  return ret;
3167 }
3168 
3169 static int MimeIsExeURLTest01(void)
3170 {
3171  int ret = 0;
3172  const char *url1 = "http://www.google.com/";
3173  const char *url2 = "http://www.google.com/test.exe";
3174 
3175  if(IsExeUrl((const uint8_t *)url1, strlen(url1)) != 0){
3176  SCLogDebug("Debug: URL1 error");
3177  goto end;
3178  }
3179  if(IsExeUrl((const uint8_t *)url2, strlen(url2)) != 1){
3180  SCLogDebug("Debug: URL2 error");
3181  goto end;
3182  }
3183  ret = 1;
3184 
3185  end:
3186 
3187  return ret;
3188 }
3189 
3190 #define TEST(str, len, expect) { \
3191  SCLogDebug("str %s", (str)); \
3192  int r = IsIpv4Host((const uint8_t *)(str),(len)); \
3193  FAIL_IF_NOT(r == (expect)); \
3194 }
3195 static int MimeIsIpv4HostTest01(void)
3196 {
3197  TEST("192.168.1.1", 11, 1);
3198  TEST("192.168.1.1.4", 13, 0);
3199  TEST("999.168.1.1", 11, 0);
3200  TEST("1111.168.1.1", 12, 0);
3201  TEST("999.oogle.com", 14, 0);
3202  TEST("0:0:0:0:0:0:0:0", 15, 0);
3203  TEST("192.168.255.255", 15, 1);
3204  TEST("192.168.255.255/testurl.html", 28, 1);
3205  TEST("www.google.com", 14, 0);
3206  PASS;
3207 }
3208 #undef TEST
3209 
3210 #define TEST(str, len, expect) { \
3211  SCLogDebug("str %s", (str)); \
3212  int r = IsIpv6Host((const uint8_t *)(str),(len)); \
3213  FAIL_IF_NOT(r == (expect)); \
3214 }
3215 static int MimeIsIpv6HostTest01(void)
3216 {
3217  TEST("0:0:0:0:0:0:0:0", 19, 1);
3218  TEST("0000:0000:0000:0000:0000:0000:0000:0000", 39, 1);
3219  TEST("XXXX:0000:0000:0000:0000:0000:0000:0000", 39, 0);
3220  TEST("00001:0000:0000:0000:0000:0000:0000:0000", 40, 0);
3221  TEST("0:0:0:0:0:0:0:0", 19, 1);
3222  TEST("0:0:0:0:0:0:0:0:0", 20, 0);
3223  TEST("192:168:1:1:0:0:0:0", 19, 1);
3224  TEST("999.oogle.com", 14, 0);
3225  TEST("192.168.255.255", 15, 0);
3226  TEST("192.168.255.255/testurl.html", 28, 0);
3227  TEST("www.google.com", 14, 0);
3228  PASS;
3229 }
3230 #undef TEST
3231 
3232 static int MimeDecParseLongFilename01(void)
3233 {
3234  /* contains 276 character filename -- length restricted to 255 chars */
3235  char mimemsg[] = "Content-Disposition: attachment; filename=\""
3236  "12characters12characters12characters12characters"
3237  "12characters12characters12characters12characters"
3238  "12characters12characters12characters12characters"
3239  "12characters12characters12characters12characters"
3240  "12characters12characters12characters12characters"
3241  "12characters12characters12characters.exe\"";
3242 
3243  uint32_t line_count = 0;
3244 
3245  MimeDecGetConfig()->decode_base64 = true;
3247  MimeDecGetConfig()->extract_urls = true;
3248 
3249  /* Init parser */
3250  MimeDecParseState *state = MimeDecInitParser(&line_count,
3251  TestDataChunkCallback);
3252 
3253  const char *str = "From: Sender1";
3254  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3255 
3256  str = "To: Recipient1";
3257  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3258 
3259  str = "Content-Type: text/plain";
3260  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3261 
3262  /* Contains 276 character filename */
3263  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)mimemsg, strlen(mimemsg), 1, state));
3264 
3265  str = "";
3266  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3267 
3268  str = "A simple message line 1";
3269  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3270 
3271  /* Completed */
3273 
3274  MimeDecEntity *msg = state->msg;
3275  FAIL_IF_NOT(msg);
3276 
3277  FAIL_IF_NOT(msg->anomaly_flags & ANOM_LONG_FILENAME);
3278  FAIL_IF_NOT(msg->filename_len == RS_MIME_MAX_TOKEN_LEN);
3279 
3281 
3282  /* De Init parser */
3283  MimeDecDeInitParser(state);
3284 
3285  PASS;
3286 }
3287 
3288 static int MimeDecParseSmallRemInp(void)
3289 {
3290  // Remainder dA
3291  // New input: AAAA
3292  char mimemsg[] = "TWltZSBkZWNvZGluZyB pcyBzbyBO T1QgZnV uISBJIGNhbm5vdA";
3293 
3294  uint32_t line_count = 0;
3295 
3296  MimeDecGetConfig()->decode_base64 = true;
3298  MimeDecGetConfig()->extract_urls = true;
3299 
3300  /* Init parser */
3301  MimeDecParseState *state = MimeDecInitParser(&line_count, TestDataChunkCallback);
3303 
3304  const char *str = "From: Sender1";
3305  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3306 
3307  str = "To: Recipient1";
3308  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3309 
3310  str = "Content-Type: text/plain";
3311  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3312 
3313  str = "Content-Transfer-Encoding: base64";
3314  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3315 
3316  str = "";
3317  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3318 
3319  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)mimemsg, strlen(mimemsg), 1, state));
3320 
3321  str = "AAAA";
3322  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3323 
3324  /* Completed */
3326 
3327  MimeDecEntity *msg = state->msg;
3328  FAIL_IF_NOT(msg);
3329 
3330  /* filename is not too long */
3331  FAIL_IF(msg->anomaly_flags & ANOM_LONG_FILENAME);
3332 
3334 
3335  /* De Init parser */
3336  MimeDecDeInitParser(state);
3337 
3338  PASS;
3339 }
3340 
3341 static int MimeDecParseRemSp(void)
3342 {
3343  // Should have remainder vd A
3344  char mimemsg[] = "TWltZSBkZWNvZGluZyBpc yBzbyBOT1QgZnVuISBJIGNhbm5vd A";
3345 
3346  uint32_t line_count = 0;
3347 
3348  MimeDecGetConfig()->decode_base64 = true;
3350  MimeDecGetConfig()->extract_urls = true;
3351 
3352  /* Init parser */
3353  MimeDecParseState *state = MimeDecInitParser(&line_count, TestDataChunkCallback);
3355 
3356  const char *str = "From: Sender1";
3357  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3358 
3359  str = "To: Recipient1";
3360  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3361 
3362  str = "Content-Type: text/plain";
3363  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3364 
3365  str = "Content-Transfer-Encoding: base64";
3366  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3367 
3368  str = "";
3369  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3370 
3371  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)mimemsg, strlen(mimemsg), 1, state));
3372  /* Completed */
3374 
3375  MimeDecEntity *msg = state->msg;
3376  FAIL_IF_NOT(msg);
3377 
3378  /* filename is not too long */
3379  FAIL_IF(msg->anomaly_flags & ANOM_LONG_FILENAME);
3380 
3382 
3383  /* De Init parser */
3384  MimeDecDeInitParser(state);
3385 
3386  PASS;
3387 }
3388 
3389 static int MimeDecVerySmallInp(void)
3390 {
3391  // Remainder: A
3392  // New input: aA
3393  char mimemsg[] = "TWltZSBkZWNvZGluZyB pcyBzbyBO T1QgZnV uISBJIGNhbm5vA";
3394 
3395  uint32_t line_count = 0;
3396 
3397  MimeDecGetConfig()->decode_base64 = true;
3399  MimeDecGetConfig()->extract_urls = true;
3400 
3401  /* Init parser */
3402  MimeDecParseState *state = MimeDecInitParser(&line_count, TestDataChunkCallback);
3404 
3405  const char *str = "From: Sender1";
3406  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3407 
3408  str = "To: Recipient1";
3409  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3410 
3411  str = "Content-Type: text/plain";
3412  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3413 
3414  str = "Content-Transfer-Encoding: base64";
3415  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3416 
3417  str = "";
3418  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3419 
3420  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)mimemsg, strlen(mimemsg), 1, state));
3421 
3422  str = "aA";
3423  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3424 
3425  /* Completed */
3427 
3428  MimeDecEntity *msg = state->msg;
3429  FAIL_IF_NOT(msg);
3430 
3431  /* filename is not too long */
3432  FAIL_IF(msg->anomaly_flags & ANOM_LONG_FILENAME);
3433 
3435 
3436  /* De Init parser */
3437  MimeDecDeInitParser(state);
3438 
3439  PASS;
3440 }
3441 
3442 static int MimeDecParseOddLen(void)
3443 {
3444  char mimemsg[] = "TWltZSBkZWNvZGluZyB pcyBzbyBO T1QgZnV uISBJIGNhbm5vdA";
3445 
3446  uint32_t line_count = 0;
3447 
3448  MimeDecGetConfig()->decode_base64 = true;
3450  MimeDecGetConfig()->extract_urls = true;
3451 
3452  /* Init parser */
3453  MimeDecParseState *state = MimeDecInitParser(&line_count, TestDataChunkCallback);
3455 
3456  const char *str = "From: Sender1";
3457  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3458 
3459  str = "To: Recipient1";
3460  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3461 
3462  str = "Content-Type: text/plain";
3463  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3464 
3465  str = "Content-Transfer-Encoding: base64";
3466  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3467 
3468  str = "";
3469  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3470 
3471  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)mimemsg, strlen(mimemsg), 1, state));
3472  /* Completed */
3474 
3475  MimeDecEntity *msg = state->msg;
3476  FAIL_IF_NOT(msg);
3477 
3478  /* filename is not too long */
3479  FAIL_IF(msg->anomaly_flags & ANOM_LONG_FILENAME);
3480 
3482 
3483  /* De Init parser */
3484  MimeDecDeInitParser(state);
3485 
3486  PASS;
3487 }
3488 
3489 static int MimeDecParseLongFilename02(void)
3490 {
3491  /* contains 40 character filename and 500+ characters following filename */
3492  char mimemsg[] = "Content-Disposition: attachment; filename=\""
3493  "12characters12characters12characters.exe\"; "
3494  "somejunkasfdasfsafasafdsasdasassdssdsd"
3495  "somejunkasfdasfsafasafdsasdasassdssdsd"
3496  "somejunkasfdasfsafasafdsasdasassdssdsd"
3497  "somejunkasfdasfsafasafdsasdasassdssdsd"
3498  "somejunkasfdasfsafasafdsasdasassdssdsd"
3499  "somejunkasfdasfsafasafdsasdasassdssdsd"
3500  "somejunkasfdasfsafasafdsasdasassdssdsd"
3501  "somejunkasfdasfsafasafdsasdasassdssdsd"
3502  "somejunkasfdasfsafasafdsasdasassdssdsd"
3503  "somejunkasfdasfsafasafdsasdasassdssdsd"
3504  "somejunkasfdasfsafasafdsasdasassdssdsd"
3505  "somejunkasfdasfsafasafdsasdasassdssdsd"
3506  "somejunkasfdasfsafasafdsasdasassdssdsd";
3507 
3508  uint32_t line_count = 0;
3509 
3510  MimeDecGetConfig()->decode_base64 = true;
3512  MimeDecGetConfig()->extract_urls = true;
3513 
3514  /* Init parser */
3515  MimeDecParseState *state = MimeDecInitParser(&line_count,
3516  TestDataChunkCallback);
3517 
3518  const char *str = "From: Sender1";
3519  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3520 
3521  str = "To: Recipient1";
3522  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3523 
3524  str = "Content-Type: text/plain";
3525  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3526 
3527  /* Contains 40 character filename */
3528  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)mimemsg, strlen(mimemsg), 1, state));
3529 
3530  str = "";
3531  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3532 
3533  str = "A simple message line 1";
3534  FAIL_IF_NOT(MIME_DEC_OK == MimeDecParseLine((uint8_t *)str, strlen(str), 1, state));
3535 
3536  /* Completed */
3538 
3539  MimeDecEntity *msg = state->msg;
3540  FAIL_IF_NOT(msg);
3541 
3542  /* filename is not too long */
3543  FAIL_IF(msg->anomaly_flags & ANOM_LONG_FILENAME);
3544 
3546 
3547  /* De Init parser */
3548  MimeDecDeInitParser(state);
3549 
3550  PASS;
3551 }
3552 
3553 #endif /* UNITTESTS */
3554 
3556 {
3557 #ifdef UNITTESTS
3558  UtRegisterTest("MimeDecParseLineTest01", MimeDecParseLineTest01);
3559  UtRegisterTest("MimeDecParseLineTest02", MimeDecParseLineTest02);
3560  UtRegisterTest("MimeFindUrlStringsTest01", MimeFindUrlStringsTest01);
3561  UtRegisterTest("MimeFindUrlStringsTest02", MimeFindUrlStringsTest02);
3562  UtRegisterTest("MimeFindUrlStringsTest03", MimeFindUrlStringsTest03);
3563  UtRegisterTest("MimeFindUrlStringsTest04", MimeFindUrlStringsTest04);
3564  UtRegisterTest("MimeFindUrlStringsTest05", MimeFindUrlStringsTest05);
3565  UtRegisterTest("MimeDecParseFullMsgTest01", MimeDecParseFullMsgTest01);
3566  UtRegisterTest("MimeDecParseFullMsgTest02", MimeDecParseFullMsgTest02);
3567  UtRegisterTest("MimeBase64DecodeTest01", MimeBase64DecodeTest01);
3568  UtRegisterTest("MimeIsExeURLTest01", MimeIsExeURLTest01);
3569  UtRegisterTest("MimeIsIpv4HostTest01", MimeIsIpv4HostTest01);
3570  UtRegisterTest("MimeIsIpv6HostTest01", MimeIsIpv6HostTest01);
3571  UtRegisterTest("MimeDecParseLongFilename01", MimeDecParseLongFilename01);
3572  UtRegisterTest("MimeDecParseLongFilename02", MimeDecParseLongFilename02);
3573  UtRegisterTest("MimeDecParseSmallRemInp", MimeDecParseSmallRemInp);
3574  UtRegisterTest("MimeDecParseRemSp", MimeDecParseRemSp);
3575  UtRegisterTest("MimeDecVerySmallInp", MimeDecVerySmallInp);
3576  UtRegisterTest("MimeDecParseOddLen", MimeDecParseOddLen);
3577 #endif /* UNITTESTS */
3578 }
MimeDecParseState::data_chunk
uint8_t data_chunk[DATA_CHUNK_SIZE]
Definition: util-decode-mime.h:194
PARSE_ERROR
#define PARSE_ERROR
Definition: util-decode-mime.h:72
MimeDecEntity::ctnt_flags
uint32_t ctnt_flags
Definition: util-decode-mime.h:136
CTNT_IS_MSG
#define CTNT_IS_MSG
Definition: util-decode-mime.h:34
MimeDecEntity::child
struct MimeDecEntity * child
Definition: util-decode-mime.h:145
MimeDecParseComplete
int MimeDecParseComplete(MimeDecParseState *state)
Called to indicate that the last message line has been processed and the parsing operation is complet...
Definition: util-decode-mime.c:2478
BASE64_ECODE_BUF
@ BASE64_ECODE_BUF
Definition: util-base64.h:75
len
uint8_t len
Definition: app-layer-dnp3.h:2
MIME_DEC_OK
@ MIME_DEC_OK
Definition: util-decode-mime.h:78
EOL_LEN
#define EOL_LEN
Definition: util-decode-mime.c:53
MIME_DEC_ERR_MEM
@ MIME_DEC_ERR_MEM
Definition: util-decode-mime.h:81
FAIL_IF_NULL
#define FAIL_IF_NULL(expr)
Fail a test if expression evaluates to NULL.
Definition: util-unittest.h:89
DataValue::next
struct DataValue * next
Definition: util-decode-mime.h:178
MimeDecEntity::ctnt_type_len
uint32_t ctnt_type_len
Definition: util-decode-mime.h:141
DASH
#define DASH
Definition: util-decode-mime.c:45
MimeDecFreeUrl
void MimeDecFreeUrl(MimeDecUrl *url)
Iteratively frees a URL entry list.
Definition: util-decode-mime.c:243
SC_ERR_MISSING_CONFIG_PARAM
@ SC_ERR_MISSING_CONFIG_PARAM
Definition: util-error.h:148
URL_IS_IP4
#define URL_IS_IP4
Definition: util-decode-mime.h:46
offset
uint64_t offset
Definition: util-streaming-buffer.h:0
MimeDecParseState::md5_ctx
SCMd5 * md5_ctx
Definition: util-decode-mime.h:195
ANOM_LONG_BOUNDARY
#define ANOM_LONG_BOUNDARY
Definition: util-decode-mime.h:58
BOUNDARY_BUF
#define BOUNDARY_BUF
Definition: util-decode-mime.c:63
SC_LOG_DEBUG
@ SC_LOG_DEBUG
Definition: util-debug.h:60
MimeDecConfig::header_value_depth
uint32_t header_value_depth
Definition: util-decode-mime.h:99
ConfNode_::val
char * val
Definition: conf.h:34
CTNT_IS_HTML
#define CTNT_IS_HTML
Definition: util-decode-mime.h:43
MimeDecField::value
uint8_t * value
Definition: util-decode-mime.h:110
PRINTABLE_END
#define PRINTABLE_END
Definition: util-decode-mime.c:47
BASE64_ECODE_ERR
@ BASE64_ECODE_ERR
Definition: util-base64.h:73
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
MAX_IP4_CHARS
#define MAX_IP4_CHARS
Definition: util-decode-mime.c:78
MimeDecStack
Structure holds the top of the stack along with some free reusable nodes.
Definition: util-decode-mime.h:165
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:103
PARSE_DONE
#define PARSE_DONE
Definition: util-decode-mime.h:71
HEADER_STARTED
#define HEADER_STARTED
Definition: util-decode-mime.h:66
MimeDecParseState::data
void * data
Definition: util-decode-mime.h:204
MimeDecParseLine
int MimeDecParseLine(const uint8_t *line, const uint32_t len, const uint8_t delim_len, MimeDecParseState *state)
Parse a line of a MIME message and update the parser state.
Definition: util-decode-mime.c:2546
MimeDecEntity::filename
uint8_t * filename
Definition: util-decode-mime.h:139
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:296
QP_STR
#define QP_STR
Definition: util-decode-mime.c:70
MimeDecAddEntity
MimeDecEntity * MimeDecAddEntity(MimeDecEntity *parent)
Creates and adds a child entity to the specified parent entity.
Definition: util-decode-mime.c:390
next
struct HtpBodyChunk_ * next
Definition: app-layer-htp.h:0
MimeDecParseState::bvremain
uint8_t bvremain[B64_BLOCK]
Definition: util-decode-mime.h:192
HTML_STR
#define HTML_STR
Definition: util-decode-mime.c:72
MimeDecStackNode::bdef_len
uint16_t bdef_len
Definition: util-decode-mime.h:156
MimeDecAddField
MimeDecField * MimeDecAddField(MimeDecEntity *entity)
Creates and adds a header field entry to an entity.
Definition: util-decode-mime.c:272
MimeDecParseState::current_line_delimiter_len
uint8_t current_line_delimiter_len
Definition: util-decode-mime.h:203
ANOM_LONG_LINE
#define ANOM_LONG_LINE
Definition: util-decode-mime.h:55
MimeDecEntity::field_list
MimeDecField * field_list
Definition: util-decode-mime.h:133
MimeDecStack::top
MimeDecStackNode * top
Definition: util-decode-mime.h:166
TXT_STR
#define TXT_STR
Definition: util-decode-mime.c:71
ConfNodeNew
ConfNode * ConfNodeNew(void)
Allocate a new configuration node.
Definition: conf.c:134
MimeDecEntity::filename_len
uint32_t filename_len
Definition: util-decode-mime.h:138
DataValue::value_len
uint32_t value_len
Definition: util-decode-mime.h:177
TAILQ_FOREACH
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:252
rust.h
CTNT_IS_ENV
#define CTNT_IS_ENV
Definition: util-decode-mime.h:35
u8_tolower
#define u8_tolower(c)
Definition: suricata-common.h:425
MimeDecParseState::found_child
int found_child
Definition: util-decode-mime.h:200
PRINTABLE_START
#define PRINTABLE_START
Definition: util-decode-mime.c:46
MimeDecUrl::next
struct MimeDecUrl * next
Definition: util-decode-mime.h:125
MimeDecField
This represents a header field name and associated value.
Definition: util-decode-mime.h:106
MimeDecParseState::hvlen
uint32_t hvlen
Definition: util-decode-mime.h:190
MimeDecSetConfig
void MimeDecSetConfig(MimeDecConfig *config)
Set global config policy.
Definition: util-decode-mime.c:126
TAILQ_INSERT_TAIL
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:294
MimeDecInitParser
MimeDecParseState * MimeDecInitParser(void *data, int(*DataChunkProcessorFunc)(const uint8_t *chunk, uint32_t len, MimeDecParseState *state))
Init the parser by allocating memory for the state and top-level entity.
Definition: util-decode-mime.c:2390
MimeDecEntity::url_list
MimeDecUrl * url_list
Definition: util-decode-mime.h:134
MIME_DEC_ERR_OVERFLOW
@ MIME_DEC_ERR_OVERFLOW
Definition: util-decode-mime.h:84
MimeDecFreeField
void MimeDecFreeField(MimeDecField *field)
Iteratively frees a header field entry list.
Definition: util-decode-mime.c:214
IPv4AddressStringIsValid
bool IPv4AddressStringIsValid(const char *str)
determine if a string is a valid ipv4 address
Definition: util-ip.c:35
util-spm-bs.h
STACK_FREE_NODES
#define STACK_FREE_NODES
Definition: util-decode-mime.c:75
MimeDecUrl::url_len
uint32_t url_len
Definition: util-decode-mime.h:123
util-unittest.h
MULTIPART_STR
#define MULTIPART_STR
Definition: util-decode-mime.c:69
BASE64_MODE_RFC2045
@ BASE64_MODE_RFC2045
Definition: util-base64.h:51
ConfNode_::is_seq
int is_seq
Definition: conf.h:36
FAIL_IF_NOT
#define FAIL_IF_NOT(expr)
Fail a test if expression evaluates to false.
Definition: util-unittest.h:82
BASE64_STR
#define BASE64_STR
Definition: util-decode-mime.c:56
CTNT_IS_BASE64
#define CTNT_IS_BASE64
Definition: util-decode-mime.h:40
util-memcmp.h
MSG_ID_STR
#define MSG_ID_STR
Definition: util-decode-mime.c:67
MimeDecField::name
uint8_t * name
Definition: util-decode-mime.h:107
CTNT_IS_BODYPART
#define CTNT_IS_BODYPART
Definition: util-decode-mime.h:37
MimeDecParseState::body_begin
int body_begin
Definition: util-decode-mime.h:201
ConfNodeFree
void ConfNodeFree(ConfNode *node)
Free a ConfNode and all of its children.
Definition: conf.c:152
CTNT_IS_ENCAP
#define CTNT_IS_ENCAP
Definition: util-decode-mime.h:36
ANOM_MALFORMED_MSG
#define ANOM_MALFORMED_MSG
Definition: util-decode-mime.h:57
FAIL_IF_NOT_NULL
#define FAIL_IF_NOT_NULL(expr)
Fail a test if expression evaluates to non-NULL.
Definition: util-unittest.h:96
MimeDecStackNode::bdef
uint8_t * bdef
Definition: util-decode-mime.h:155
MimeDecConfig::extract_urls_schemes
ConfNode * extract_urls_schemes
Definition: util-decode-mime.h:95
PASS
#define PASS
Pass the test.
Definition: util-unittest.h:105
BasicSearchNocase
uint8_t * BasicSearchNocase(const uint8_t *haystack, uint32_t haystack_len, const uint8_t *needle, uint16_t needle_len)
Basic search case less.
Definition: util-spm-bs.c:102
MAX_ENC_LINE_LEN
#define MAX_ENC_LINE_LEN
Definition: util-decode-mime.c:60
BODY_STARTED
#define BODY_STARTED
Definition: util-decode-mime.h:68
TEST
#define TEST(str, len, expect)
Definition: util-decode-mime.c:3210
MimeDecEntity::ctnt_type
uint8_t * ctnt_type
Definition: util-decode-mime.h:140
MimeDecParseState::stack
MimeDecStack * stack
Definition: util-decode-mime.h:187
util-print.h
BODY_END_BOUND
#define BODY_END_BOUND
Definition: util-decode-mime.h:70
util-ip.h
CTNT_IS_QP
#define CTNT_IS_QP
Definition: util-decode-mime.h:41
MIME_DEC_ERR_PARSE
@ MIME_DEC_ERR_PARSE
Definition: util-decode-mime.h:82
MimeDecParseState::hvalue
DataValue * hvalue
Definition: util-decode-mime.h:191
PrintRawDataFp
void PrintRawDataFp(FILE *fp, const uint8_t *buf, uint32_t buflen)
Definition: util-print.c:143
MimeDecStackNode::is_encap
bool is_encap
Definition: util-decode-mime.h:157
MSG_STR
#define MSG_STR
Definition: util-decode-mime.c:68
MimeDecField::next
struct MimeDecField * next
Definition: util-decode-mime.h:111
MAX_LINE_LEN
#define MAX_LINE_LEN
Definition: util-decode-mime.c:59
MAX_HEADER_VALUE
#define MAX_HEADER_VALUE
Definition: util-decode-mime.c:62
LF
#define LF
Definition: util-decode-mime.c:40
MimeDecGetConfig
MimeDecConfig * MimeDecGetConfig(void)
Get global config policy.
Definition: util-decode-mime.c:145
MimeDecStackNode
Structure contains boundary and entity for the current node (entity) in the stack.
Definition: util-decode-mime.h:153
CR
#define CR
Definition: util-decode-mime.c:39
IPv6AddressStringIsValid
bool IPv6AddressStringIsValid(const char *str)
determine if a string is a valid ipv6 address
Definition: util-ip.c:82
MimeDecConfig::decode_base64
bool decode_base64
Definition: util-decode-mime.h:92
CTNT_TYPE_STR
#define CTNT_TYPE_STR
Definition: util-decode-mime.c:64
MimeDecParseState::hlen
uint32_t hlen
Definition: util-decode-mime.h:189
MimeDecParseState::data_chunk_len
uint32_t data_chunk_len
Definition: util-decode-mime.h:199
MimeDecParseState::hname
uint8_t * hname
Definition: util-decode-mime.h:188
MimeDecStackNode::next
struct MimeDecStackNode * next
Definition: util-decode-mime.h:158
CTNT_IS_TEXT
#define CTNT_IS_TEXT
Definition: util-decode-mime.h:42
SCLogInfo
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition: util-debug.h:215
ANOM_LONG_HEADER_VALUE
#define ANOM_LONG_HEADER_VALUE
Definition: util-decode-mime.h:54
MimeDecUrl::url_flags
uint32_t url_flags
Definition: util-decode-mime.h:124
MimeDecConfig::log_url_scheme
bool log_url_scheme
Definition: util-decode-mime.h:97
MimeDecConfig
Structure for containing configuration options.
Definition: util-decode-mime.h:91
MimeDecEntity::msg_id
uint8_t * msg_id
Definition: util-decode-mime.h:143
MimeDecParseState::msg
MimeDecEntity * msg
Definition: util-decode-mime.h:186
HEADER_READY
#define HEADER_READY
Definition: util-decode-mime.h:65
MimeDecField::value_len
uint32_t value_len
Definition: util-decode-mime.h:109
ANOM_LONG_ENC_LINE
#define ANOM_LONG_ENC_LINE
Definition: util-decode-mime.h:56
FAIL_IF
#define FAIL_IF(expr)
Fail a test if expression evaluates to true.
Definition: util-unittest.h:71
MimeDecParseState::has_md5
bool has_md5
Definition: util-decode-mime.h:197
flags
uint8_t flags
Definition: decode-gre.h:0
suricata-common.h
HEADER_DONE
#define HEADER_DONE
Definition: util-decode-mime.h:67
util-decode-mime.h
CTNT_DISP_STR
#define CTNT_DISP_STR
Definition: util-decode-mime.c:65
MimeDecRegisterTests
void MimeDecRegisterTests(void)
Definition: util-decode-mime.c:3555
MimeDecEntity::anomaly_flags
uint32_t anomaly_flags
Definition: util-decode-mime.h:137
MIME_DEC_ERR_STATE
@ MIME_DEC_ERR_STATE
Definition: util-decode-mime.h:83
ANOM_INVALID_QP
#define ANOM_INVALID_QP
Definition: util-decode-mime.h:52
BASE64_ECODE_OK
@ BASE64_ECODE_OK
Definition: util-base64.h:74
MimeDecConfig::decode_quoted_printable
bool decode_quoted_printable
Definition: util-decode-mime.h:93
CRLF
#define CRLF
Definition: util-decode-mime.c:43
SCStrdup
#define SCStrdup(s)
Definition: util-mem.h:56
MAX_IP6_CHARS
#define MAX_IP6_CHARS
Definition: util-decode-mime.c:79
MimeDecConfig::extract_urls
bool extract_urls
Definition: util-decode-mime.h:94
util-validate.h
URL_IS_IP6
#define URL_IS_IP6
Definition: util-decode-mime.h:47
ANOM_LONG_FILENAME
#define ANOM_LONG_FILENAME
Definition: util-decode-mime.h:59
MimeDecParseState::body_end
int body_end
Definition: util-decode-mime.h:202
MimeDecParseState
Structure contains the current state of the MIME parser.
Definition: util-decode-mime.h:185
SCMalloc
#define SCMalloc(sz)
Definition: util-mem.h:47
MimeDecParseStateGetStatus
const char * MimeDecParseStateGetStatus(MimeDecParseState *state)
Definition: util-decode-mime.c:2317
MAX_HEADER_NAME
#define MAX_HEADER_NAME
Definition: util-decode-mime.c:61
str
#define str(s)
Definition: suricata-common.h:280
MimeDecFindField
MimeDecField * MimeDecFindField(const MimeDecEntity *entity, const char *name)
Searches for a header field with the specified name.
Definition: util-decode-mime.c:331
SCLogWarning
#define SCLogWarning(err_code,...)
Macro used to log WARNING messages.
Definition: util-debug.h:242
SCFree
#define SCFree(p)
Definition: util-mem.h:61
DATA_CHUNK_SIZE
#define DATA_CHUNK_SIZE
Definition: util-decode-mime.h:62
ConfNode_
Definition: conf.h:32
ANOM_INVALID_BASE64
#define ANOM_INVALID_BASE64
Definition: util-decode-mime.h:51
MimeDecFreeEntity
void MimeDecFreeEntity(MimeDecEntity *entity)
Frees a mime entity tree.
Definition: util-decode-mime.c:175
MimeDecUrl
This represents a URL value node in a linked list.
Definition: util-decode-mime.h:121
src
uint16_t src
Definition: app-layer-dnp3.h:5
MimeDecStack::free_nodes_cnt
uint32_t free_nodes_cnt
Definition: util-decode-mime.h:168
sc_log_global_log_level
SCLogLevel sc_log_global_log_level
Holds the global log level. Is the same as sc_log_config->log_level.
Definition: util-debug.c:84
MimeDecParseState::bvr_len
uint8_t bvr_len
Definition: util-decode-mime.h:193
DataValue
Structure contains a list of value and lengths for robust data processing.
Definition: util-decode-mime.h:175
CTNT_IS_MULTIPART
#define CTNT_IS_MULTIPART
Definition: util-decode-mime.h:38
MimeDecFindFieldsForEach
int MimeDecFindFieldsForEach(const MimeDecEntity *entity, const char *name, int(*DataCallback)(const uint8_t *val, const size_t, void *data), void *data)
Searches for header fields with the specified name.
Definition: util-decode-mime.c:303
suricata.h
B64_BLOCK
#define B64_BLOCK
Definition: util-base64.h:32
MimeDecEntity::msg_id_len
uint32_t msg_id_len
Definition: util-decode-mime.h:142
MimeDecEntity::next
struct MimeDecEntity * next
Definition: util-decode-mime.h:144
MimeDecParseFullMsg
MimeDecEntity * MimeDecParseFullMsg(const uint8_t *buf, uint32_t blen, void *data, int(*dcpfunc)(const uint8_t *chunk, uint32_t len, MimeDecParseState *state))
Parses an entire message when available in its entirety (wraps the line-based parsing functions)
Definition: util-decode-mime.c:2580
ANOM_LONG_HEADER_NAME
#define ANOM_LONG_HEADER_NAME
Definition: util-decode-mime.h:53
MimeDecField::name_len
uint32_t name_len
Definition: util-decode-mime.h:108
CTNT_IS_ATTACHMENT
#define CTNT_IS_ATTACHMENT
Definition: util-decode-mime.h:39
MimeDecStack::free_nodes
MimeDecStackNode * free_nodes
Definition: util-decode-mime.h:167
ASCII_BLOCK
#define ASCII_BLOCK
Definition: util-base64.h:31
dst
uint16_t dst
Definition: app-layer-dnp3.h:4
MimeDecDeInitParser
void MimeDecDeInitParser(MimeDecParseState *state)
De-Init parser by freeing up any residual memory.
Definition: util-decode-mime.c:2442
msg
const char * msg
Definition: app-layer-htp.c:571
URL_IS_EXE
#define URL_IS_EXE
Definition: util-decode-mime.h:48
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
MimeDecEntity
This represents the MIME Entity (or also top level message) in a child-sibling tree.
Definition: util-decode-mime.h:132
MimeDecParseState::state_flag
uint8_t state_flag
Definition: util-decode-mime.h:198
code
uint8_t code
Definition: decode-icmpv4.h:1
SCMemcmp
#define SCMemcmp(a, b, c)
Definition: util-memcmp.h:290
DEBUG_VALIDATE_BUG_ON
#define DEBUG_VALIDATE_BUG_ON(exp)
Definition: util-validate.h:111
Base64Ecode
Base64Ecode
Definition: util-base64.h:72
DecodeBase64
Base64Ecode DecodeBase64(uint8_t *dest, uint32_t dest_size, const uint8_t *src, uint32_t len, uint32_t *consumed_bytes, uint32_t *decoded_bytes, Base64Mode mode)
Decodes a base64-encoded string buffer into an ascii-encoded byte buffer.
Definition: util-base64.c:94
MIME_DEC_ERR_DATA
@ MIME_DEC_ERR_DATA
Definition: util-decode-mime.h:80
MimeDecStackNode::data
MimeDecEntity * data
Definition: util-decode-mime.h:154
MimeDecParseState::md5
uint8_t md5[SC_MD5_LEN]
Definition: util-decode-mime.h:196
COLON
#define COLON
Definition: util-decode-mime.c:44
CTNT_TRAN_STR
#define CTNT_TRAN_STR
Definition: util-decode-mime.c:66
MimeDecParseState::DataChunkProcessorFunc
int(* DataChunkProcessorFunc)(const uint8_t *chunk, uint32_t len, struct MimeDecParseState *state)
Definition: util-decode-mime.h:205
MimeDecUrl::url
uint8_t * url
Definition: util-decode-mime.h:122
DataValue::value
uint8_t * value
Definition: util-decode-mime.h:176
g_disable_hashing
bool g_disable_hashing
Definition: suricata.c:201