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