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