Gitweb:
https://git.exim.org/exim.git/commitdiff/fc96555ab63243de9d468325aeaaa14cd77b9943
Commit: fc96555ab63243de9d468325aeaaa14cd77b9943
Parent: 80acfd092b5f785bd971dcf911b4fe7aec9faaa2
Author: Jeremy Harris <jgh146exb@???>
AuthorDate: Sat Jan 9 13:08:35 2021 +0000
Committer: Jeremy Harris <jgh146exb@???>
CommitDate: Sat Jan 9 13:08:35 2021 +0000
Utilities: harden exim_tidydb against corrupt wait-records. Bug 2343
---
src/src/exim_dbutil.c | 30 ++++++++++++++++++++++++++----
src/src/macros.h | 3 ++-
2 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/src/exim_dbutil.c b/src/src/exim_dbutil.c
index 7429527..7ca7a30 100644
--- a/src/src/exim_dbutil.c
+++ b/src/src/exim_dbutil.c
@@ -388,7 +388,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
@@ -416,7 +416,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;
@@ -616,6 +616,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,
@@ -1218,7 +1219,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 */
@@ -1239,12 +1240,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
diff --git a/src/src/macros.h b/src/src/macros.h
index a584551..68470a9 100644
--- a/src/src/macros.h
+++ b/src/src/macros.h
@@ -192,9 +192,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
/* Fixed option values for all PCRE functions */