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