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