Utilities: harden exim_tidydb against corrupt wait-records. Bug 2343
authorJeremy Harris <jgh146exb@wizmail.org>
Sat, 9 Jan 2021 13:08:35 +0000 (13:08 +0000)
committerJeremy Harris <jgh146exb@wizmail.org>
Tue, 12 Jan 2021 19:15:28 +0000 (19:15 +0000)
(cherry picked from commit fc96555ab63243de9d468325aeaaa14cd77b9943)

doc/doc-txt/ChangeLog
src/src/exim_dbutil.c
src/src/macros.h

index ddf755d27aefc77b8068a531627d5f2f88e0eeec..888bd828bb24e6f987924e497a45769b68c4e91b 100644 (file)
@@ -129,6 +129,8 @@ JH/32 Bug 2599: fix delay of delivery to a local address where there is also
 JH/33 Fix a taint trap in the ${listextract } expansion when the source data
       was tainted.
 
+JH/35 Bug 2343: Harden exim_tidydb against corrupt wait- files.
+
 
 Exim version 4.94
 -----------------
index 5c7b6650ed5726f00160ea63008ff36d217e9877..bd7b53865fc5925c213a144aa8a223187c0d1ff5 100644 (file)
@@ -391,7 +391,7 @@ pick out the timestamps, etc., do the copying centrally here.
 Arguments:
   dbblock   a pointer to an open database block
   key       the key of the record to be read
-  length    where to put the length (or NULL if length not wanted)
+  length    where to put the length (or NULL if length not wanted). Includes overhead.
 
 Returns: a pointer to the retrieved record, or
          NULL if the record is not found
@@ -419,7 +419,7 @@ we should store the taint status along with the data. */
 
 yield = store_get(EXIM_DATUM_SIZE(result_datum), TRUE);
 memcpy(yield, EXIM_DATUM_DATA(result_datum), EXIM_DATUM_SIZE(result_datum));
-if (length != NULL) *length = EXIM_DATUM_SIZE(result_datum);
+if (length) *length = EXIM_DATUM_SIZE(result_datum);
 
 EXIM_DATUM_FREE(result_datum);    /* Some DBM libs require freeing */
 return yield;
@@ -620,6 +620,7 @@ for (uschar * key = dbfn_scan(dbm, TRUE, &cursor);
        t = wait->text;
        name[MESSAGE_ID_LENGTH] = 0;
 
+    /* Leave corrupt records alone */
        if (wait->count > WAIT_NAME_MAX)
          {
          fprintf(stderr,
@@ -1222,7 +1223,7 @@ for (; keychain && (reset_point = store_mark()); store_reset(reset_point))
   /* A continuation record may have been deleted or renamed already, so
   non-existence is not serious. */
 
-  if (value == NULL) continue;
+  if (!value) continue;
 
   /* Delete if too old */
 
@@ -1243,12 +1244,33 @@ for (; keychain && (reset_point = store_mark()); store_reset(reset_point))
 
     /* Leave corrupt records alone */
 
+    if (wait->time_stamp > time(NULL))
+      {
+      printf("**** Data for '%s' corrupted\n  time in future: %s\n",
+        key, print_time(((dbdata_generic *)value)->time_stamp));
+      continue;
+      }
     if (wait->count > WAIT_NAME_MAX)
       {
-      printf("**** Data for %s corrupted\n  count=%d=0x%x max=%d\n",
+      printf("**** Data for '%s' corrupted\n  count=%d=0x%x max=%d\n",
         key, wait->count, wait->count, WAIT_NAME_MAX);
       continue;
       }
+    if (wait->sequence > WAIT_CONT_MAX)
+      {
+      printf("**** Data for '%s' corrupted\n  sequence=%d=0x%x max=%d\n",
+        key, wait->sequence, wait->sequence, WAIT_CONT_MAX);
+      continue;
+      }
+
+    /* Record over 1 year old; just remove it */
+
+    if (wait->time_stamp < time(NULL) - 365*24*60*60)
+      {
+      dbfn_delete(dbm, key);
+      printf("deleted %s (too old)\n", key);
+      continue;
+      }
 
     /* Loop for renamed continuation records. For each message id,
     check to see if the message exists, and if not, remove its entry
index 2fe364ed443c3f44c0146b2e265f09cc317380d9..f78ae2e3d4b465677882517d332d6522aa81d1f5 100644 (file)
@@ -191,9 +191,10 @@ message id with a trailing "-H" or "-D" added. */
 #define SPOOL_NAME_LENGTH (MESSAGE_ID_LENGTH+2)
 
 /* The maximum number of message ids to store in a waiting database
-record. */
+record, and the max number of continuation records allowed. */
 
 #define WAIT_NAME_MAX 50
+#define WAIT_CONT_MAX 1000
 
 /* Wait this long before determining that a Proxy Protocol configured
 host isn't speaking the protocol, and so is disallowed. Can be moved to