File nofreeze-c72f740a.patch of Package mutt.23792

From c72f740aa7c80c0a79775628e62daa2a43357cd5 Mon Sep 17 00:00:00 2001
From: Kevin McCarthy <kevin@8t8.us>
Date: Tue, 19 May 2020 12:26:55 -0700
Subject: [PATCH] Add mitigation against DoS from thousands of parts.

A demonstration attack using a million tiny parts will freeze Mutt for
several minutes.  This is actually better than some other mail
software, but can still be a problem at large levels.

For now, set it to a very conservative 5000, but this can be adjusted
up (or down) if necessary.

Declare the previous stack-limit max depth as a constant too, and
decrease it down to 50.  Change the handler to return non-fatal "1" on
reaching the limit.
---
 handler.c |    9 ++++++++
 mime.h    |    5 ++++
 parse.c   |   69 ++++++++++++++++++++++++++++++++++++++++++++++++++++----------
 3 files changed, 72 insertions(+), 11 deletions(-)

--- handler.c
+++ handler.c	2020-12-02 13:13:03.692676976 +0000
@@ -1720,8 +1720,16 @@ int mutt_body_handler (BODY *b, STATE *s
   int plaintext = 0;
   handler_t handler = NULL;
   int rc = 0;
+  static unsigned short recurse_level = 0;
 
   int oflags = s->flags;
+
+  if (recurse_level >= MUTT_MIME_MAX_DEPTH)
+  {
+    dprint (1, (debugfile, "mutt_body_handler: recurse level too deep. giving up!\n"));
+    return 1;
+  }
+  recurse_level++;
   
   /* first determine which handler to use to process this part */
 
@@ -1836,6 +1844,7 @@ int mutt_body_handler (BODY *b, STATE *s
     fputs (" --]\n", s->fpout);
   }
 
+  recurse_level--;
   s->flags = oflags | (s->flags & MUTT_FIRSTDONE);
   if (rc)
   {
--- mime.h
+++ mime.h	2020-12-02 13:11:27.322523653 +0000
@@ -52,6 +52,11 @@ enum
   DISPNONE /* no preferred disposition */
 };
 
+/* Some limits to mitigate stack overflow and denial of service attacks */
+#define MUTT_MIME_MAX_DEPTH  50
+#define MUTT_MIME_MAX_PARTS  5000
+
+
 /* MIME encoding/decoding global vars */
 
 #ifndef _SENDLIB_C
--- parse.c
+++ parse.c	2020-12-08 09:03:21.076476696 +0000
@@ -34,6 +34,12 @@
 #include <sys/stat.h>
 #include <stdlib.h>
 
+static void _parse_part (FILE *fp, BODY *b, int *counter);
+static BODY *_parse_messageRFC822 (FILE *fp, BODY *parent, int *counter);
+static BODY *_parse_multipart (FILE *fp, const char *boundary, LOFF_T end_off,
+                               int digest, int *counter);
+
+
 /* Reads an arbitrarily long header field, and looks ahead for continuation
  * lines.  ``line'' must point to a dynamically allocated string; it is
  * increased if more space is required to fit the whole line.
@@ -481,9 +487,17 @@ BODY *mutt_read_mime_header (FILE *fp, i
   return (p);
 }
 
-void mutt_parse_part (FILE *fp, BODY *b)
+static void _parse_part (FILE *fp, BODY *b, int *counter)
 {
   char *bound = 0;
+  static unsigned short recurse_level = 0;
+
+  if (recurse_level >= MUTT_MIME_MAX_DEPTH)
+  {
+    dprint (1, (debugfile, "mutt_parse_part(): recurse level too deep. giving up!\n"));
+    return;
+  }
+  recurse_level++;
 
   switch (b->type)
   {
@@ -496,9 +510,10 @@ void mutt_parse_part (FILE *fp, BODY *b)
           bound = mutt_get_parameter ("boundary", b->parameter);
 
       fseeko (fp, b->offset, SEEK_SET);
-      b->parts =  mutt_parse_multipart (fp, bound, 
-					b->offset + b->length,
-					ascii_strcasecmp ("digest", b->subtype) == 0);
+      b->parts =  _parse_multipart (fp, bound, 
+				    b->offset + b->length,
+				    ascii_strcasecmp ("digest", b->subtype) == 0,
+				    counter);
       break;
 
     case TYPEMESSAGE:
@@ -506,16 +521,16 @@ void mutt_parse_part (FILE *fp, BODY *b)
       {
 	fseeko (fp, b->offset, SEEK_SET);
 	if (mutt_is_message_type(b->type, b->subtype))
-	  b->parts = mutt_parse_messageRFC822 (fp, b);
+	  b->parts = _parse_messageRFC822 (fp, b, counter);
 	else if (ascii_strcasecmp (b->subtype, "external-body") == 0)
 	  b->parts = mutt_read_mime_header (fp, 0);
 	else
-	  return;
+	  goto bail;
       }
       break;
 
     default:
-      return;
+      goto bail;
   }
 
   /* try to recover from parsing error */
@@ -524,6 +539,8 @@ void mutt_parse_part (FILE *fp, BODY *b)
     b->type = TYPETEXT;
     mutt_str_replace (&b->subtype, "plain");
   }
+bail:
+  recurse_level--;
 }
 
 /* parse a MESSAGE/RFC822 body
@@ -537,7 +554,7 @@ void mutt_parse_part (FILE *fp, BODY *b)
  * NOTE: this assumes that `parent->length' has been set!
  */
 
-BODY *mutt_parse_messageRFC822 (FILE *fp, BODY *parent)
+static BODY *_parse_messageRFC822 (FILE *fp, BODY *parent, int *counter)
 {
   BODY *msg;
 
@@ -555,7 +572,7 @@ BODY *mutt_parse_messageRFC822 (FILE *fp
   if (msg->length < 0)
     msg->length = 0;
 
-  mutt_parse_part(fp, msg);
+  _parse_part(fp, msg, counter);
   return (msg);
 }
 
@@ -572,7 +589,8 @@ BODY *mutt_parse_messageRFC822 (FILE *fp
  *	digest		1 if reading a multipart/digest, 0 otherwise
  */
 
-BODY *mutt_parse_multipart (FILE *fp, const char *boundary, LOFF_T end_off, int digest)
+static BODY *_parse_multipart (FILE *fp, const char *boundary, LOFF_T end_off,
+                               int digest, int *counter)
 {
 #ifdef SUN_ATTACHMENT
   int lines;
@@ -649,6 +667,14 @@ BODY *mutt_parse_multipart (FILE *fp, co
 	}
 	else
 	  last = head = new;
+
+        /* It seems more intuitive to add the counter increment to
+         * _parse_part(), but we want to stop the case where a multipart
+         * contains thousands of tiny parts before the memory and data
+         * structures are allocated.
+         */
+        if (++(*counter) >= MUTT_MIME_MAX_PARTS)
+          break;
       }
     }
   }
@@ -659,11 +685,32 @@ BODY *mutt_parse_multipart (FILE *fp, co
 
   /* parse recursive MIME parts */
   for(last = head; last; last = last->next)
-    mutt_parse_part(fp, last);
+    _parse_part(fp, last, counter);
   
   return (head);
 }
 
+void mutt_parse_part (FILE *fp, BODY *b)
+{
+  int counter = 0;
+
+  _parse_part (fp, b, &counter);
+}
+
+BODY *mutt_parse_messageRFC822 (FILE *fp, BODY *parent)
+{
+  int counter = 0;
+
+  return _parse_messageRFC822 (fp, parent, &counter);
+}
+
+BODY *mutt_parse_multipart (FILE *fp, const char *boundary, LOFF_T end_off, int digest)
+{
+  int counter = 0;
+
+  return _parse_multipart (fp, boundary, end_off, digest, &counter);
+}
+
 static const char *uncomment_timezone (char *buf, size_t buflen, const char *tz)
 {
   char *p;
openSUSE Build Service is sponsored by