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