From 6acb441b40bbcded2e85819c71a068db713e7ca6 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Mon, 11 Jan 2021 19:48:12 +0000 Subject: [PATCH] Hints DB: harden against corrupt files by ignoring unexpected size records --- doc/doc-txt/ChangeLog | 4 ++++ src/src/dbfn.c | 28 ++++++++++++++++++++++++++++ src/src/dbfunctions.h | 3 ++- src/src/enq.c | 4 ++-- src/src/transports/smtp.c | 2 +- 5 files changed, 37 insertions(+), 4 deletions(-) diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 87bf0d009..e1381c156 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -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 ----------------- diff --git a/src/src/dbfn.c b/src/src/dbfn.c index a37271f36..452e2ade6 100644 --- a/src/src/dbfn.c +++ b/src/src/dbfn.c @@ -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 * diff --git a/src/src/dbfunctions.h b/src/src/dbfunctions.h index 2e18e0eff..f3b04ad2f 100644 --- a/src/src/dbfunctions.h +++ b/src/src/dbfunctions.h @@ -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); diff --git a/src/src/enq.c b/src/src/enq.c index 7feba5531..0dcb9a732 100644 --- a/src/src/enq.c +++ b/src/src/enq.c @@ -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) diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 2a600d480..301d84c2e 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -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) { -- 2.30.2