Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package exim for openSUSE:Factory checked in 
at 2025-03-27 22:34:18
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/exim (Old)
 and      /work/SRC/openSUSE:Factory/.exim.new.2696 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "exim"

Thu Mar 27 22:34:18 2025 rev:85 rq:1262987 version:4.98.2

Changes:
--------
--- /work/SRC/openSUSE:Factory/exim/exim.changes        2025-03-19 
22:32:50.303889624 +0100
+++ /work/SRC/openSUSE:Factory/.exim.new.2696/exim.changes      2025-03-27 
22:34:57.411201480 +0100
@@ -1,0 +2,9 @@
+Thu Mar 27 14:41:49 UTC 2025 - Alexander Bergmann <[email protected]>
+
+- security release 4.98.2 for CVE-2025-30232 (bsc#1239794)
+  * Fixed use-after-free with potential for privilege escalation. 
+- security release 4.98.1 for CVE-2025-26794 (bsc#1237424)
+  * Fixed remote SQL injection when SQLite hints and ETRN serialization
+    are used.
+
+-------------------------------------------------------------------

Old:
----
  exim-4.98.tar.bz2
  exim-4.98.tar.bz2.asc

New:
----
  exim-4.98.2.tar.bz2
  exim-4.98.2.tar.bz2.asc

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ exim.spec ++++++
--- /var/tmp/diff_new_pack.Gm8OxG/_old  2025-03-27 22:34:58.199234099 +0100
+++ /var/tmp/diff_new_pack.Gm8OxG/_new  2025-03-27 22:34:58.203234265 +0100
@@ -31,7 +31,7 @@
 %bcond_with     dane
 %endif
 Name:           exim
-Version:        4.98
+Version:        4.98.2
 Release:        0
 Summary:        The Exim Mail Transfer Agent, a Replacement for sendmail
 License:        GPL-2.0-or-later

++++++ exim-4.98.tar.bz2 -> exim-4.98.2.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/exim-4.98/doc/ChangeLog new/exim-4.98.2/doc/ChangeLog
--- old/exim-4.98/doc/ChangeLog 2024-07-10 16:45:44.000000000 +0200
+++ new/exim-4.98.2/doc/ChangeLog       2025-03-21 14:45:58.000000000 +0100
@@ -2,13 +2,32 @@
 affect Exim's operation, with an unchanged configuration file.  For new
 options, and new features, see the NewStuff file next to this ChangeLog.
 
+Exim version 4.98.2
+-------------------
+
+This is a security release, addressing CVE-2025-30232
+
+JH/01 Fix use-after-free notified by Trend Micro (ref: ZDI-CAN-26250).
+      Null out debug_pretrigger_buf pointer before freeing the buffer;
+      the use of this buffer by the storage management checks the pointer
+      for non-null before using it.
+
+Exim version 4.98.1
+-------------------
+
+This is a security release, addressing CVE-2025-26794
+
+JH/01 Serialization of SMTP commands is based on the MD5 sum of
+      the command's argument.
+
+HS/01 Prevent SQL injection for the hints database.
 Exim version 4.98
 -----------------
 
 JH/01 Support list of dkim results in the dkim_status ACL condition, making
       it more usable in the data ACL.
 
-JH/02 Bug 3040: Handle error on close of the spool data file during reception. 
+JH/02 Bug 3040: Handle error on close of the spool data file during reception.
       Previously This was only logged, on the assumption that errors would be
       seen for a previous fflush().  However, a fuse filesystem has been
       reported as showing this an error for the fclose().  The spool is now in
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/exim-4.98/doc/exim.8 new/exim-4.98.2/doc/exim.8
--- old/exim-4.98/doc/exim.8    2024-07-10 16:50:10.000000000 +0200
+++ new/exim-4.98.2/doc/exim.8  2025-03-25 11:13:50.000000000 +0100
@@ -309,7 +309,7 @@
 Features such as authentication and encryption, where the client input is not
 plain text, cannot easily be tested with \fB\-bh\fP. Instead, you should use a
 specialized SMTP test program such as
-\fBswaks\fP.
+swaks [\fBhttps://www.jetmore.org/john/code/swaks/\fP].
 .TP 10
 \fB\-bhc\fP <\fIIP address\fP>
 This option operates in the same way as \fB\-bh\fP, except that address
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/exim-4.98/doc/filter.txt 
new/exim-4.98.2/doc/filter.txt
--- old/exim-4.98/doc/filter.txt        2024-07-10 16:50:10.000000000 +0200
+++ new/exim-4.98.2/doc/filter.txt      2025-03-25 11:13:50.000000000 +0100
@@ -4,7 +4,7 @@
 
 Copyright (c) 2023 The Exim Maintainers
 
-Revision 4.98  10 Jul 2024 PH
+Revision 4.98.2  25 Mar 2025 PH
 
 -------------------------------------------------------------------------------
 
@@ -72,7 +72,7 @@
 
 This document describes the user interfaces to Exim's in-built mail filtering
 facilities, and is copyright (c) The Exim Maintainers 2023. It corresponds to
-Exim version 4.98.
+Exim version 4.98.2.
 
 
 1.1 Introduction
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/exim-4.98/doc/spec.txt new/exim-4.98.2/doc/spec.txt
--- old/exim-4.98/doc/spec.txt  2024-07-10 16:50:09.000000000 +0200
+++ new/exim-4.98.2/doc/spec.txt        2025-03-25 11:13:49.000000000 +0100
@@ -4,7 +4,7 @@
 
 Copyright (c) 2024 The Exim Maintainers
 
-Revision 4.98  10 Jul 2024 EM
+Revision 4.98.2  25 Mar 2025 EM
 
 -------------------------------------------------------------------------------
 
@@ -635,7 +635,7 @@
 1.1 Exim documentation
 ----------------------
 
-This edition of the Exim specification applies to version 4.98 of Exim.
+This edition of the Exim specification applies to version 4.98.2 of Exim.
 Substantive changes from the 4.97 edition are marked in some renditions of this
 document; this paragraph is so marked if the rendition is capable of showing a
 change indicator.
@@ -1760,7 +1760,7 @@
 
 Exim is distributed as a gzipped or bzipped tar file which, when unpacked,
 creates a directory with the name of the current release (for example,
-exim-4.98) into which the following files are placed:
+exim-4.98.2) into which the following files are placed:
 
     ACKNOWLEDGMENTS contains some acknowledgments
     CHANGES         contains a reference to where changes are documented
@@ -2381,7 +2381,7 @@
 For the utility programs, old versions are renamed by adding the suffix .O to
 their names. The Exim binary itself, however, is handled differently. It is
 installed under a name that includes the version number and the compile number,
-for example, exim-4.98-1. The script then arranges for a symbolic link called
+for example, exim-4.98.2-1. The script then arranges for a symbolic link called
 exim to point to the binary. If you are updating a previous version of Exim,
 the script takes care to ensure that the name exim is never absent from the
 directory (as seen by other processes).
@@ -2945,7 +2945,8 @@
 
     Features such as authentication and encryption, where the client input is
     not plain text, cannot easily be tested with -bh. Instead, you should use a
-    specialized SMTP test program such as swaks.
+    specialized SMTP test program such as swaks [https://www.jetmore.org/john/
+    code/swaks/].
 
 -bhc <IP address>
 
@@ -16461,6 +16462,19 @@
 SMTP, so it is not possible for it to change the uid before running the
 command.
 
+Serialization of ETRN commands is based on the MD5 hash of the command's
+argument. No more than one ETRN command with the same hash of its arguments can
+run at a given time. Other ETRN commands get a 458 SMTP rejection.
+
+To protect against attacks flooding the ETRN serialization table, you should
+consider rate limiting the ETRN command.
+
+deny
+  ratelimit = 3 / 1m / per_cmd / $sender_host_address
+  message = rate for ETRN is over the limit ($sender_rate > $sender_rate_limit)
+
+accept
+
 +---------------------------------------------------------+
 |smtp_etrn_serialize|Use: main|Type: boolean|Default: true|
 +---------------------------------------------------------+
@@ -28161,7 +28175,8 @@
 
 A TLSA record consist of 4 fields, the "Certificate Usage", the "Selector", the
 "Matching type", and the "Certificate Association Data". For a detailed
-description of the TLSA record see RFC 7671.
+description of the TLSA record see RFC 7671 [https://tools.ietf.org/html/
+rfc7671#page-5].
 
 The TLSA record for the server may have "Certificate Usage" (1st) field of
 DANE-TA(2) or DANE-EE(3). These are the "Trust Anchor" and "End Entity"
@@ -28199,7 +28214,7 @@
 random serial numbers. The list of requirements is subject to change as best
 practices evolve. If you're not already using a private CA, or it doesn't meet
 these requirements, then we encourage you to avoid all these issues and use a
-public CA such as Let's Encrypt instead.
+public CA such as Let's Encrypt [https://letsencrypt.org/] instead.
 
 The TLSA record should have a "Selector" (2nd) field of SPKI(1) and a "Matching
 Type" (3rd) field of SHA2-512(2).
@@ -28327,12 +28342,13 @@
 which is recognized by clients sending to you. That selection of which CAs are
 trusted by others is outside your control.
 
-The most interoperable course of action is probably to use Let's Encrypt, with
-automated certificate renewal; to publish the anchor information in
-DNSSEC-secured DNS via TLSA records for DANE clients (such as Exim and Postfix)
-and to publish anchor information for MTA-STS as well. This is what is done for
-the exim.org domain itself (with caveats around occasionally broken MTA-STS
-because of incompatible specification changes prior to reaching RFC status).
+The most interoperable course of action is probably to use Let's Encrypt [
+https://letsencrypt.org/], with automated certificate renewal; to publish the
+anchor information in DNSSEC-secured DNS via TLSA records for DANE clients
+(such as Exim and Postfix) and to publish anchor information for MTA-STS as
+well. This is what is done for the exim.org domain itself (with caveats around
+occasionally broken MTA-STS because of incompatible specification changes prior
+to reaching RFC status).
 
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/exim-4.98/src/debug.c new/exim-4.98.2/src/debug.c
--- old/exim-4.98/src/debug.c   2024-07-10 16:45:44.000000000 +0200
+++ new/exim-4.98.2/src/debug.c 2025-03-21 14:45:58.000000000 +0100
@@ -453,6 +453,18 @@
 What ends up in the buffer is subject to the usual debug_selector. */
 
 void
+debug_pretrigger_discard(void)
+{
+dtrigger_selector = 0;
+if (debug_pretrigger_buf)
+  {
+  uschar * buf = debug_pretrigger_buf;
+  debug_pretrigger_buf = NULL;
+  store_free(buf);
+  }
+}
+
+void
 debug_pretrigger_setup(const uschar * size_string)
 {
 long size = Ustrtol(size_string, NULL, 0);
@@ -460,10 +472,10 @@
   {
   unsigned bufsize = MIN(size, 16384);
 
-  dtrigger_selector |= BIT(DTi_pretrigger);
-  if (debug_pretrigger_buf) store_free(debug_pretrigger_buf);
-  debug_pretrigger_buf = store_malloc((size_t)(debug_pretrigger_bsize = 
bufsize));
+  debug_pretrigger_discard();
   pretrigger_readoff = pretrigger_writeoff = 0;
+  debug_pretrigger_buf = store_malloc((size_t)(debug_pretrigger_bsize = 
bufsize));
+  dtrigger_selector |= BIT(DTi_pretrigger);
   }
 }
 
@@ -487,13 +499,5 @@
 debug_pretrigger_discard();
 }
 
-void
-debug_pretrigger_discard(void)
-{
-if (debug_pretrigger_buf) store_free(debug_pretrigger_buf);
-debug_pretrigger_buf = NULL;
-dtrigger_selector = 0;
-}
-
 
 /* End of debug.c */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/exim-4.98/src/hintsdb.h new/exim-4.98.2/src/hintsdb.h
--- old/exim-4.98/src/hintsdb.h 2024-07-10 16:45:44.000000000 +0200
+++ new/exim-4.98.2/src/hintsdb.h       2025-03-21 14:45:58.000000000 +0100
@@ -66,6 +66,15 @@
 #ifndef HINTSDB_H
 #define HINTSDB_H
 
+# if COMPILE_UTILITY
+# undef DEBUG
+# define DEBUG(...) if (getenv("DEBUG"))
+# define debug_printf_indent(x, y...) fprintf(stderr, "# " x, y)
+# define debug_printf(x, y...) fprintf(stderr, "# " x, y)
+# else
+extern void debug_printf_indent(const char *, ...);
+# endif
+static inline BOOL is_tainted(const void *);
 
 #ifdef USE_SQLITE
 # if defined(USE_DB) || defined(USE_GDBM) || defined(USE_TDB)
@@ -89,6 +98,27 @@
 
 # /* Access functions */
 
+/* The key must be zero terminated, an empty key has len == 1. */
+static inline BOOL
+is_cstring(EXIM_DATUM *key)
+{
+if (key->len < 1)
+  {
+# ifdef SQL_DEBUG
+  fprintf(stderr, "invalid key length %d (must be >= 1)\n", key->len);
+# endif
+  return FALSE;
+  }
+if (key->data[key->len-1] != '\0')
+  {
+# ifdef SQL_DEBUG
+  fprintf(stderr, "key %.*s is not zero terminated\n", key->len, key->data);
+# endif
+  return FALSE;
+  }
+return TRUE;
+}
+
 static inline BOOL
 exim_lockfile_needed(void)
 {
@@ -119,68 +149,75 @@
 return ret == SQLITE_OK ? dbp : NULL;
 }
 
-/* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */
-/* note we alloc'n'copy - the caller need not do so */
-/* result has a NUL appended, but the length is as per the DB */
-
 static inline BOOL
-exim_dbget__(EXIM_DB * dbp, const uschar * s, EXIM_DATUM * res)
+exim_dbget__(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res)
 {
-sqlite3_stmt * statement;
-int ret;
+int ret = FALSE;
+sqlite3_stmt * stmt = NULL; /* don't make it static, as it depends on the dbp 
*/
+const char query[] = "SELECT dat FROM tbl WHERE ky = ?";
 
-res->len = (size_t) -1;
-/* fprintf(stderr, "exim_dbget__(%s)\n", s); */
-if ((ret = sqlite3_prepare_v2(dbp, CCS s, -1, &statement, NULL)) != SQLITE_OK)
+if (SQLITE_OK != sqlite3_prepare_v2(dbp, query, sizeof(query)-1, &stmt, NULL))
   {
-/* fprintf(stderr, "prepare fail: %s\n", sqlite3_errmsg(dbp)); */
-  return FALSE;
+# ifdef SQL_DEBUG
+  fprintf(stderr, EXIM_DBTYPE " prepare %s: %s\n", query, sqlite3_errmsg(dbp));
+# endif
+  goto DONE;
   }
-if (sqlite3_step(statement) != SQLITE_ROW)
+
+# ifdef SQL_DEBUG
+DEBUG(D_hints_lookup) debug_printf_indent("prepared SQL: %s\n", 
sqlite3_sql(stmt));
+# endif
+
+if (SQLITE_OK != sqlite3_bind_text(stmt, 1, CCS key->data, key->len-1, 
SQLITE_STATIC))
   {
-/* fprintf(stderr, "step fail: %s\n", sqlite3_errmsg(dbp)); */
-  sqlite3_finalize(statement);
-  return FALSE;
+# ifdef SQL_DEBUG
+  fprintf(stderr, EXIM_DBTYPE " bind text (%s): %s\n", sqlite3_sql(stmt), 
sqlite3_errmsg(dbp));
+# endif
+  goto DONE;
   }
 
-res->len = sqlite3_column_bytes(statement, 0);
+# ifdef SQL_DEBUG
+DEBUG(D_hints_lookup) debug_printf_indent("expanded SQL: %s\n", 
sqlite3_expanded_sql(stmt));
+# endif
+
+if (SQLITE_ROW != sqlite3_step(stmt))
+  {
+# ifdef SQL_DEBUG
+  DEBUG(D_hints_lookup) debug_printf_indent("step (%s): %s\n", 
sqlite3_expanded_sql(stmt), sqlite3_errmsg(dbp));
+# endif
+  goto DONE;
+  }
+
+res->len = sqlite3_column_bytes(stmt, 0);
+
 # ifdef COMPILE_UTILITY
-if (!(res->data = malloc(res->len +1)))
-  { sqlite3_finalize(statement); return FALSE; }
+if (!(res->data = malloc(res->len +1))) goto DONE;
 # else
 res->data = store_get(res->len +1, GET_TAINTED);
 # endif
-memcpy(res->data, sqlite3_column_blob(statement, 0), res->len);
+
+memcpy(res->data, sqlite3_column_blob(stmt, 0), res->len);
 res->data[res->len] = '\0';
 /* fprintf(stderr, "res %d bytes: '%.*s'\n", (int)res->len, (int)res->len, 
res->data); */
-sqlite3_finalize(statement);
-return TRUE;
+
+ret = TRUE;
+
+DONE:
+sqlite3_finalize(stmt);
+
+return ret;
 }
 
+/* EXIM_DBGET - returns the value associated with the key. The key must
+be zero terminated, an empty key has len == 1. */
 static inline BOOL
 exim_dbget(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res)
 {
-# define FMT "SELECT dat FROM tbl WHERE ky = '%.*s';"
-uschar * qry;
-int i;
-BOOL ret;
-
-# ifdef COMPILE_UTILITY
-/* fprintf(stderr, "exim_dbget(k len %d '%.*s')\n", (int)key->len, 
(int)key->len, key->data); */
-i = snprintf(NULL, 0, FMT, (int) key->len, key->data)+1;
-if (!(qry = malloc(i)))
-  return FALSE;
-snprintf(CS qry, i, FMT, (int) key->len, key->data);
-ret = exim_dbget__(dbp, qry, res);
-free(qry);
-# else
-/* fprintf(stderr, "exim_dbget(k len %d '%.*s')\n", (int)key->len, 
(int)key->len, key->data); */
-qry = string_sprintf(FMT, (int) key->len, key->data);
-ret = exim_dbget__(dbp, qry, res);
+# ifdef SQL_DEBUG
+DEBUG(D_hints_lookup) debug_printf_indent(EXIM_DBTYPE " get key: len=%d, 
strlen=%d, key=%.*s\n", key->len, Ustrlen(key->data), key->len, key->data);
 # endif
-
-return ret;
-# undef FMT
+if (!is_cstring(key)) return FALSE;
+return exim_dbget__(dbp, key, res);
 }
 
 /**/
@@ -190,46 +227,83 @@
 static inline int
 exim_s_dbp(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data, const uschar * 
alt)
 {
-int hlen = data->len * 2, off = 0, res;
-# define FMT "INSERT OR %s INTO tbl (ky,dat) VALUES ('%.*s', X'%.*s');"
-uschar * qry;
+const char sql[] = "INSERT OR %s INTO tbl (ky, dat) VALUES(?, ?)";
+int ret = EXIM_DBPUTB_DUP;
+sqlite3_stmt *stmt = NULL;
+uschar * query;
+
 # ifdef COMPILE_UTILITY
-uschar * hex = malloc(hlen+1);
-if (!hex) return EXIM_DBPUTB_DUP;      /* best we can do */
+int i = 1 + snprintf(NULL, 0, sql, alt);
+if (NULL == (query = US malloc(i)))
+  {
+  fprintf(stderr, "can't allocate memory for %s", sql);
+  return EXIM_DBPUTB_DUP;
+  }
+snprintf(CS query, i, sql, alt);
 # else
-uschar * hex = store_get(hlen+1, data->data);
+query = string_sprintf(sql, alt);
 # endif
 
-for (const uschar * s = data->data, * t = s + data->len; s < t; s++, off += 2)
-  sprintf(CS hex + off, "%02X", *s);
+if (SQLITE_OK != sqlite3_prepare_v2(dbp, CCS query, -1, &stmt, NULL))
+  {
+# ifdef SQL_DEBUG
+  fprintf(stderr, EXIM_DBTYPE " prepare %s: %s\n", query, sqlite3_errmsg(dbp));
+# endif
+  goto DONE;
+  }
 
-# ifdef COMPILE_UTILITY
-res = snprintf(CS hex, 0, FMT, alt, (int) key->len, key->data, hlen, hex) +1;
-if (!(qry = malloc(res))) return EXIM_DBPUTB_DUP;
-snprintf(CS qry, res, FMT, alt, (int) key->len, key->data, hlen, hex);
-/* fprintf(stderr, "exim_s_dbp(%s)\n", qry); */
-res = sqlite3_exec(dbp, CS qry, NULL, NULL, NULL);
-free(qry);
-free(hex);
-# else
-qry = string_sprintf(FMT, alt, (int) key->len, key->data, hlen, hex);
-/* fprintf(stderr, "exim_s_dbp(%s)\n", qry); */
-res = sqlite3_exec(dbp, CS qry, NULL, NULL, NULL);
-/* fprintf(stderr, "exim_s_dbp res %d\n", res); */
+# ifdef SQL_DEBUG
+DEBUG(D_hints_lookup) debug_printf_indent("prepared SQL: %s\n", 
sqlite3_sql(stmt));
 # endif
 
-if (res != SQLITE_OK)
-  fprintf(stderr, "sqlite3_exec: %s\n", sqlite3_errmsg(dbp));
+if (SQLITE_OK != sqlite3_bind_text(stmt, 1, CCS key->data, key->len-1, NULL))
+  {
+# ifdef SQL_DEBUG
+  fprintf(stderr, EXIM_DBTYPE " bind to value 1: %s\n", sqlite3_errmsg(dbp));
+# endif
+  goto DONE;
+  }
 
-return res == SQLITE_OK ? EXIM_DBPUTB_OK : EXIM_DBPUTB_DUP;
-# undef FMT
-}
+if (SQLITE_OK != sqlite3_bind_blob(stmt, 2, data->data, data->len, NULL))
+  {
+# ifdef SQL_DEBUG
+  fprintf(stderr, EXIM_DBTYPE " bind to value 2: %s\n", sqlite3_errmsg(dbp));
+# endif
+  goto DONE;
+  }
 
-/* EXIM_DBPUT - returns nothing useful, assumes replace mode */
+# ifdef SQL_DEBUG
+DEBUG(D_hints_lookup) debug_printf_indent("expanded SQL: %s\n", 
sqlite3_expanded_sql(stmt));
+# endif
+
+if (SQLITE_DONE != sqlite3_step(stmt))
+  {
+# ifdef SQL_DEBUG
+  fprintf(stderr, EXIM_DBTYPE " step (%s): %s\n", sqlite3_expanded_sql(stmt), 
sqlite3_errmsg(dbp));
+# endif
+  goto DONE;
+  }
+
+ret = EXIM_DBPUTB_OK;
 
+DONE:
+sqlite3_finalize(stmt);
+# ifdef COMPILE_UTILITY
+free(query);
+# endif
+
+return ret;
+}
+
+/* EXIM_DBPUT - returns nothing useful, assumes replace mode
+The key must be zero terminated. An empty key has len == 1. */
 static inline int
 exim_dbput(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
 {
+# ifdef SQL_DEBUG
+DEBUG(D_hints_lookup) debug_printf_indent(EXIM_DBTYPE " put: key: len=%d, 
strlen=%d, key=%.*s\n", key->len, Ustrlen(key->data), key->len, key->data);
+# endif
+if (!is_cstring(key)) return -1;
 /* fprintf(stderr, "exim_dbput()\n"); */
 (void) exim_s_dbp(dbp, key, data, US"REPLACE");
 return 0;
@@ -249,26 +323,52 @@
 static inline int
 exim_dbdel(EXIM_DB * dbp, EXIM_DATUM * key)
 {
-# define FMT "DELETE FROM tbl WHERE ky = '%.*s';"
-uschar * qry;
-int res;
+int res = -1;
+sqlite3_stmt *stmt = NULL; /* don't make it static, because it depends on the 
dbp */
+const char query[] = "DELETE FROM tbl WHERE ky = ?";
 
-# ifdef COMPILE_UTILITY
-res = snprintf(NULL, 0, FMT, (int) key->len, key->data) +1; /* res includes 
nul */
-if (!(qry = malloc(res))) return SQLITE_NOMEM;
-snprintf(CS qry, res, FMT, (int) key->len, key->data);
-res = sqlite3_exec(dbp, CS qry, NULL, NULL, NULL);
-free(qry);
-# else
-qry = string_sprintf(FMT, (int) key->len, key->data);
-res = sqlite3_exec(dbp, CS qry, NULL, NULL, NULL);
+DEBUG(D_hints_lookup) debug_printf_indent(EXIM_DBTYPE " del key: len=%d, 
strlen=%d, key=%.*s\n", key->len, Ustrlen(key->data), key->len, key->data);
+if (!is_cstring(key)) return -1;
+
+if (SQLITE_OK != sqlite3_prepare_v2(dbp, query, sizeof(query)-1, &stmt, NULL))
+  {
+# ifdef SQL_DEBUG
+  fprintf(stderr, EXIM_DBTYPE " prepare %s: %s\n", query, sqlite3_errmsg(dbp));
 # endif
+  goto DONE;
+  }
+
+# ifdef SQL_DEBUG
+DEBUG(D_hints_lookup) debug_printf_indent("query: %s\n", sqlite3_sql(stmt));
+# endif
+
+if (SQLITE_OK != sqlite3_bind_text(stmt, 1, CCS key->data, key->len-1, 
SQLITE_STATIC))
+  {
+# ifdef SQL_DEBUG
+  fprintf(stderr, EXIM_DBTYPE " bind value 1: %s\n", sqlite3_errmsg(dbp));
+# endif
+  goto DONE;
+  }
+
+# ifdef SQL_DEBUG
+DEBUG(D_hints_lookup) debug_printf_indent("expanded query: %s\n", 
sqlite3_expanded_sql(stmt));
+# endif
+
+if (SQLITE_DONE != sqlite3_step(stmt))
+  {
+# ifdef SQL_DEBUG
+  fprintf(stderr, EXIM_DBTYPE " step: %s: %s\n", sqlite3_expanded_sql(stmt), 
sqlite3_errmsg(dbp));
+# endif
+  goto DONE;
+  }
 
+res = 0;
+
+DONE:
+sqlite3_finalize(stmt);
 return res;
-# undef FMT
 }
 
-
 /* EXIM_DBCREATE_CURSOR - initialize for scanning operation */
 /* Cursors are inefficiently emulated by repeating searches */
 
@@ -286,33 +386,66 @@
 }
 
 /* EXIM_DBSCAN */
-/* Note that we return the (next) key, not the record value */
+/* Note that we return the (next) key, not the record value.
+ * We've to add the zero terminator, as this isn't stored in the database */
 static inline BOOL
-exim_dbscan(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res, BOOL first,
-  EXIM_CURSOR * cursor)
+exim_dbscan(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res /* unusied */, 
BOOL first /*unused*/, EXIM_CURSOR * cursor)
 {
-# define FMT "SELECT ky FROM tbl ORDER BY ky LIMIT 1 OFFSET %d;"
-uschar * qry;
-int i;
-BOOL ret;
+BOOL more = FALSE;
+sqlite3_stmt *stmt = NULL;
+const char query[] = "SELECT ky FROM tbl ORDER BY ky LIMIT 1 OFFSET ?";
 
-# ifdef COMPILE_UTILITY
-i = snprintf(NULL, 0, FMT, *cursor)+1;
-if (!(qry = malloc(i))) return FALSE;
-snprintf(CS qry, i, FMT, *cursor);
-/* fprintf(stderr, "exim_dbscan(%s)\n", qry); */
-ret = exim_dbget__(dbp, qry, key);
-free(qry);
-/* fprintf(stderr, "exim_dbscan ret %c\n", ret ? 'T':'F'); */
-# else
-qry = string_sprintf(FMT, *cursor);
-/* fprintf(stderr, "exim_dbscan(%s)\n", qry); */
-ret = exim_dbget__(dbp, qry, key);
-/* fprintf(stderr, "exim_dbscan ret %c\n", ret ? 'T':'F'); */
+if (SQLITE_OK != sqlite3_prepare_v2(dbp, query, sizeof(query)-1, &stmt, NULL))
+  {
+# ifdef SQL_DEBUG
+  fprintf(stderr, EXIM_DBTYPE " prepare %s: %s\n", query, sqlite3_errmsg(dbp));
 # endif
-if (ret) *cursor = *cursor + 1;
-return ret;
-# undef FMT
+  goto DONE;
+  }
+
+# ifdef SQL_DEBUG
+DEBUG(D_hints_lookup) debug_printf_indent("prepared query: %s\n", 
sqlite3_sql(stmt));
+# endif
+
+if (SQLITE_OK != sqlite3_bind_int(stmt, 1, *cursor))
+  {
+# ifdef SQL_DEBUG
+  fprintf(stderr, EXIM_DBTYPE " bind value 1: %s\n", query, 
sqlite3_errmsg(dbp));
+# endif
+  goto DONE;
+  }
+
+# ifdef SQL_DEBUG
+DEBUG(D_hints_lookup) debug_printf_indent("expanded query: %s\n", 
sqlite3_expanded_sql(stmt));
+# endif
+
+switch (sqlite3_step(stmt))
+  {
+    case SQLITE_DONE: goto DONE;
+    case SQLITE_ROW: (*cursor)++;
+                      key->len = sqlite3_column_bytes(stmt, 0);
+#ifdef COMPILE_UTILITY
+                      if (!(key->data = malloc(key->len+1))) goto DONE;
+#else
+                      key->data = store_get(key->len+1, GET_TAINTED); // 
TAINTED? We're talking about the key!
+#endif
+                      memcpy(key->data, sqlite3_column_blob(stmt, 0), 
key->len);
+                      key->data[key->len] = '\0';
+# ifdef SQL_DEBUG
+                      DEBUG(D_hints_lookup) debug_printf_indent("key 
length=%d, val=%s\n", key->len, key->data);
+# endif
+                      more = TRUE;
+                      goto DONE;
+    default:
+# ifdef SQL_DEBUG
+                      fprintf(stderr, EXIM_DBTYPE " step: %s: %s\n", 
sqlite3_expanded_sql(stmt), sqlite3_errmsg(dbp));
+# endif
+                      goto DONE;
+  }
+
+DONE:
+sqlite3_finalize(stmt);
+return more;
 }
 
 /* EXIM_DBDELETE_CURSOR - terminate scanning operation. */
@@ -326,7 +459,6 @@
 # endif
 }
 
-
 /* EXIM_DBCLOSE */
 static void
 exim_dbclose__(EXIM_DB * dbp)
@@ -335,16 +467,16 @@
 sqlite3_close(dbp);
 }
 
-
 /* Datum access */
 
 static uschar *
 exim_datum_data_get(EXIM_DATUM * dp)
 { return US dp->data; }
+
 static void
 exim_datum_data_set(EXIM_DATUM * dp, void * s)
 { dp->data = s; }
- 
+
 static unsigned
 exim_datum_size_get(EXIM_DATUM * dp)
 { return dp->len; }
@@ -352,8 +484,6 @@
 exim_datum_size_set(EXIM_DATUM * dp, unsigned n)
 { dp->len = n; }
 
-
-
 static inline void
 exim_datum_init(EXIM_DATUM * dp)
 { dp->data = NULL; }                   /* compiler quietening */
@@ -368,11 +498,6 @@
 
 # define EXIM_DB_RLIMIT        150
 
-
-
-
-
-
 #elif defined(USE_TDB)
 
 # if defined(USE_DB) || defined(USE_GDBM) || defined(USE_SQLITE)
@@ -513,11 +638,6 @@
 
 # define EXIM_DB_RLIMIT        150
 
-
-
-
-
-
 /********************* Berkeley db native definitions **********************/
 
 #elif defined USE_DB
@@ -574,13 +694,11 @@
 static inline void
 dbfn_bdb_error_callback(const DB_ENV * dbenv, const char * pfx, const char * 
msg)
 {
-#ifndef MACRO_PREDEF 
+#ifndef MACRO_PREDEF
 log_write(0, LOG_MAIN, "Berkeley DB error: %s", msg);
 #endif
 }
 
-
-
 /* Access functions (BDB 4.1+) */
 
 static inline BOOL
@@ -841,7 +959,6 @@
 
 #  endif
 
-
 #  else /* DB_VERSION_MAJOR >= 3 */
 #   error Berkeley DB versions earlier than 3 are not supported */
 #  endif /* DB_VERSION_MAJOR */
@@ -849,17 +966,11 @@
 #  error Berkeley DB version 1 is no longer supported
 # endif /* DB_VERSION_STRING */
 
-
 /* all BDB versions */
 /* size limit */
 
 # define EXIM_DB_RLIMIT        150
 
-
-
-
-
-
 /********************* gdbm interface definitions **********************/
 
 #elif defined USE_GDBM
@@ -1007,15 +1118,9 @@
 
 #else  /* USE_GDBM */
 
-
-
-
-
-
 /* If none of USE_DB, USG_GDBM, USE_SQLITE or USE_TDB are set,
 the default is the NDBM interface (which seems to be a wrapper for GDBM) */
 
-
 /********************* ndbm interface definitions **********************/
 
 # include <ndbm.h>
@@ -1145,10 +1250,6 @@
 
 #endif /* !USE_GDBM */
 
-
-
-
-
 #if defined(COMPILE_UTILITY) || defined(MACRO_PREDEF)
 
 static inline EXIM_DB *
@@ -1166,9 +1267,6 @@
 
 /* Wrappers for open/close with debug tracing */
 
-extern void debug_printf_indent(const char *, ...);
-static inline BOOL is_tainted(const void *);
-
 static inline EXIM_DB *
 exim_dbopen(const uschar * name, const uschar * dirname, int flags,
   unsigned mode)
@@ -1204,7 +1302,11 @@
 
 /********************* End of dbm library definitions **********************/
 
-
+# if COMPILE_UTILITY
+# undef debug_printf_indent
+# undef debug_printf
+# undef DEBUG
+# endif
 #endif /* whole file */
 /* End of hintsdb.h */
 /* vi: aw ai sw=2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/exim-4.98/src/smtp_in.c new/exim-4.98.2/src/smtp_in.c
--- old/exim-4.98/src/smtp_in.c 2024-07-10 16:45:44.000000000 +0200
+++ new/exim-4.98.2/src/smtp_in.c       2025-03-21 14:45:58.000000000 +0100
@@ -5576,14 +5576,27 @@
        break;
        }
 
-      /* Compute the serialization key for this command. */
+      /* Compute the serialization key for this command. We used (all the way
+      back to 4.00) to include the given string as part of the key, but this
+      opens a security hole for hintsdb types that use a command-string for
+      operations. All ETRN with the same command hash are serialized */
 
-      etrn_serialize_key = string_sprintf("etrn-%s\n", smtp_cmd_data);
+      md5 hash;
+      uschar *digest = store_get(16, GET_TAINTED);
+
+      md5_start(&hash);
+      md5_end(&hash, smtp_cmd_argument, Ustrlen(smtp_cmd_argument), digest);
+
+      etrn_serialize_key = string_sprintf("etrn-" /* don't we have a function 
doing exactly this? */
+          "%02x%02x%02x%02x" "%02x%02x%02x%02x"   /* we have, since 2024-09-xx 
we can use %.16H */
+          "%02x%02x%02x%02x" "%02x%02x%02x%02x",
+          digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], 
digest[6], digest[7],
+          digest[8], digest[9], digest[10], digest[11], digest[12], 
digest[13], digest[14], digest[15]);
 
       /* If a command has been specified for running as a result of ETRN, we
-      permit any argument to ETRN. If not, only the # standard form is 
permitted,
-      since that is strictly the only kind of ETRN that can be implemented
-      according to the RFC. */
+      permit any argument to ETRN. If not, only the # standard form is
+      permitted, since that is strictly the only kind of ETRN that can be
+      implemented according to the RFC. */
 
       GET_OPTION("smtp_etrn_command");
       if (smtp_etrn_command)
@@ -5592,8 +5605,8 @@
        BOOL rc;
        etrn_command = smtp_etrn_command;
        deliver_domain = smtp_cmd_data;
-       rc = transport_set_up_command(&argv, smtp_etrn_command, 
TSUC_EXPAND_ARGS, 0, NULL,
-         US"ETRN processing", &error);
+       rc = transport_set_up_command(&argv, smtp_etrn_command,
+                       TSUC_EXPAND_ARGS, 0, NULL, US"ETRN processing", &error);
        deliver_domain = NULL;
        if (!rc)
          {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/exim-4.98/src/version.h new/exim-4.98.2/src/version.h
--- old/exim-4.98/src/version.h 2024-07-10 16:49:39.000000000 +0200
+++ new/exim-4.98.2/src/version.h       2025-03-25 11:13:22.000000000 +0100
@@ -1,5 +1,5 @@
 /* automatically generated file - see ../scripts/reversion */
-#define EXIM_RELEASE_VERSION "4.98"
+#define EXIM_RELEASE_VERSION "4.98.2"
 #ifdef EXIM_VARIANT_VERSION
 #define EXIM_VERSION_STR EXIM_RELEASE_VERSION "-" EXIM_VARIANT_VERSION
 #else
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/exim-4.98/src/version.sh 
new/exim-4.98.2/src/version.sh
--- old/exim-4.98/src/version.sh        2024-07-10 16:49:39.000000000 +0200
+++ new/exim-4.98.2/src/version.sh      2025-03-25 11:13:22.000000000 +0100
@@ -1,3 +1,3 @@
 # automatically generated file - see ../scripts/reversion
-EXIM_RELEASE_VERSION="4.98"
+EXIM_RELEASE_VERSION="4.98.2"
 EXIM_COMPILE_NUMBER="1"

Reply via email to