Hints DB: harden against corrupt files by ignoring unexpected size records
authorJeremy Harris <jgh146exb@wizmail.org>
Mon, 11 Jan 2021 19:48:12 +0000 (19:48 +0000)
committerJeremy Harris <jgh146exb@wizmail.org>
Fri, 15 Jan 2021 00:42:38 +0000 (00:42 +0000)
doc/doc-txt/ChangeLog
src/src/dbfn.c
src/src/dbfunctions.h
src/src/enq.c
src/src/transports/smtp.c

index 87bf0d009ff1612923d224e35981b826e72cb6de..e1381c156fe3d3f9b3e637d4df71500d46cb8fab 100644 (file)
@@ -177,6 +177,10 @@ JH/36 Bug 2687: Fix interpretation of multiple ^ chars in a plaintext
       documentation.  There is still no way to get a leading ^ immediately
       after a NUL (ie. for the password of a PLAIN method authenticator.
 
+JH/37 Enforce the expected size, for fixed-size records read from hints-DB
+      files.  For bad sizes read, delete the record and whine to paniclog.
+
+
 
 Exim version 4.94
 -----------------
index a37271f36f00a5f9f1c0e99e4f9d94e9bf17e385..452e2ade6a0330fc96256188dbaf7086768806f6 100644 (file)
@@ -333,6 +333,34 @@ return yield;
 }
 
 
+/* Read a record.  If the length is not as expected then delete it, write
+an error log line and return NULL.
+Use this for fixed-size records (so not retry or wait records).
+
+Arguments:
+  dbblock   a pointer to an open database block
+  key       the key of the record to be read
+  length    the expected record length
+
+Returns: a pointer to the retrieved record, or
+         NULL if the record is not found/bad
+*/
+
+void *
+dbfn_read_enforce_length(open_db * dbblock, const uschar * key, size_t length)
+{
+int rlen;
+void * yield = dbfn_read_with_length(dbblock, key, &rlen);
+
+if (yield)
+  {
+  if (rlen == length) return yield;
+  log_write(0, LOG_MAIN|LOG_PANIC, "Bad db record size for '%s'", key);
+  dbfn_delete(dbblock, key);
+  }
+return NULL;
+}
+
 
 /*************************************************
 *             Write to database file             *
index 2e18e0effe2c4d122bbfe556b57b59f2232c6022..f3b04ad2f29d3243b41818062b095d1a52304494 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2015 */
+/* Copyright (c) University of Cambridge 1995 - 2021 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 
@@ -12,6 +12,7 @@ void     dbfn_close(open_db *);
 int      dbfn_delete(open_db *, const uschar *);
 open_db *dbfn_open(uschar *, int, open_db *, BOOL, BOOL);
 void    *dbfn_read_with_length(open_db *, const uschar *, int *);
+void    *dbfn_read_enforce_length(open_db *, const uschar *, size_t);
 uschar  *dbfn_scan(open_db *, BOOL, EXIM_CURSOR **);
 int      dbfn_write(open_db *, const uschar *, void *, int);
 
index 7feba55319efc2982796d07a72086dc24afff07c..0dcb9a732b10f9d57bb4141158724edda95b9d2c 100644 (file)
@@ -53,7 +53,7 @@ if (!(dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE, TRUE)))
 /* See if there is a record for this host or queue run; if there is, we cannot
 proceed with the connection unless the record is very old. */
 
-serial_record = dbfn_read(dbm_file, key);
+serial_record = dbfn_read_enforce_length(dbm_file, key, sizeof(dbdata_serialize));
 if (serial_record && time(NULL) - serial_record->time_stamp < 6*60*60)
   {
   if (serial_record->count >= lim)
@@ -102,7 +102,7 @@ dbdata_serialize *serial_record;
 DEBUG(D_transport) debug_printf("end serialized: %s\n", key);
 
 if (  !(dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE, TRUE))
-   || !(serial_record = dbfn_read(dbm_file, key))
+   || !(serial_record = dbfn_read_enforce_length(dbm_file, key, sizeof(dbdata_serialize)))
    )
   return;
 if (--serial_record->count > 0)
index 2a600d4806cffa0226a22af3d655c69797adaa4e..301d84c2e4415aabff5fbe0a370764395be64be9 100644 (file)
@@ -805,7 +805,7 @@ else
   uschar * ehlo_resp_key = ehlo_cache_key(sx);
   dbdata_ehlo_resp * er;
 
-  if (!(er = dbfn_read(dbm_file, ehlo_resp_key)))
+  if (!(er = dbfn_read_enforce_length(dbm_file, ehlo_resp_key, sizeof(dbdata_ehlo_resp))))
     { DEBUG(D_transport) debug_printf("no ehlo-resp record\n"); }
   else if (time(NULL) - er->time_stamp > retry_data_expire)
     {