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