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