Logging: fix crash on local_part utf8-conversion fail
[exim.git] / src / src / dbstuff.h
index ee49d818d1253a58aef270a6e56e9d6fdfe3edac..8ebf6f6e40e875234865473cc1f05eba497eabd8 100644 (file)
@@ -3,7 +3,7 @@
 *************************************************/
 
 /* Copyright (c) University of Cambridge 1995 - 2018 */
-/* Copyright (c) The Exim Maintainers 2020 - 2021 */
+/* Copyright (c) The Exim Maintainers 2020 - 2022 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* This header file contains macro definitions so that a variety of DBM
@@ -120,26 +120,26 @@ definition of DB_VERSION_STRING, which is present in versions 2.x onwards. */
 
 /* The API changed (again!) between the 2.x and 3.x versions */
 
-# if DB_VERSION_MAJOR >= 3
+#  if DB_VERSION_MAJOR >= 3
 
 /***************** Berkeley db 3.x/4.x native definitions ******************/
 
 /* Basic DB type */
-#  if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 1)
-#   define EXIM_DB       DB_ENV
+#   if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 1)
+#    define EXIM_DB       DB_ENV
 /* Cursor type, for scanning */
-#   define EXIM_CURSOR   DBC
+#    define EXIM_CURSOR   DBC
 
 /* The datum type used for queries */
-#   define EXIM_DATUM    DBT
+#    define EXIM_DATUM    DBT
 
 /* Some text for messages */
-#   define EXIM_DBTYPE   "db (v4.1+)"
+#    define EXIM_DBTYPE   "db (v4.1+)"
 
 /* Only more-recent versions.  5+ ? */
-#   ifndef DB_FORCESYNC
-#    define DB_FORCESYNC 0
-#   endif
+#    ifndef DB_FORCESYNC
+#     define DB_FORCESYNC 0
+#    endif
 
 
 /* Access functions */
@@ -148,9 +148,9 @@ definition of DB_VERSION_STRING, which is present in versions 2.x onwards. */
 API changed for DB 4.1. - and we also starting using the "env" with a
 specified working dir, to avoid the DBCONFIG file trap. */
 
-#   define ENV_TO_DB(env) ((DB *)((env)->app_private))
+#    define ENV_TO_DB(env) ((DB *)((env)->app_private))
 
-#   define EXIM_DBOPEN__(name, dirname, flags, mode, dbpp) \
+#    define EXIM_DBOPEN__(name, dirname, flags, mode, dbpp) \
   if (  db_env_create(dbpp, 0) != 0                                            \
      || ((*dbpp)->set_errcall(*dbpp, dbfn_bdb_error_callback), 0)              \
      || (*dbpp)->open(*dbpp, CS dirname, DB_CREATE|DB_INIT_MPOOL|DB_PRIVATE, 0) != 0\
@@ -173,72 +173,72 @@ specified working dir, to avoid the DBCONFIG file trap. */
     }
 
 /* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */
-#   define EXIM_DBGET(db, key, data)      \
+#    define EXIM_DBGET(db, key, data)      \
        (ENV_TO_DB(db)->get(ENV_TO_DB(db), NULL, &key, &data, 0) == 0)
 
 /* EXIM_DBPUT - returns nothing useful, assumes replace mode */
-#   define EXIM_DBPUT(db, key, data)      \
+#    define EXIM_DBPUT(db, key, data)      \
        ENV_TO_DB(db)->put(ENV_TO_DB(db), NULL, &key, &data, 0)
 
 /* EXIM_DBPUTB - non-overwriting for use by dbmbuild */
-#   define EXIM_DBPUTB(db, key, data)      \
+#    define EXIM_DBPUTB(db, key, data)      \
        ENV_TO_DB(db)->put(ENV_TO_DB(db), NULL, &key, &data, DB_NOOVERWRITE)
 
 /* Return values from EXIM_DBPUTB */
 
-#   define EXIM_DBPUTB_OK  0
-#   define EXIM_DBPUTB_DUP DB_KEYEXIST
+#    define EXIM_DBPUTB_OK  0
+#    define EXIM_DBPUTB_DUP DB_KEYEXIST
 
 /* EXIM_DBDEL */
-#   define EXIM_DBDEL(db, key)     ENV_TO_DB(db)->del(ENV_TO_DB(db), NULL, &key, 0)
+#    define EXIM_DBDEL(db, key)     ENV_TO_DB(db)->del(ENV_TO_DB(db), NULL, &key, 0)
 
 /* EXIM_DBCREATE_CURSOR - initialize for scanning operation */
 
-#   define EXIM_DBCREATE_CURSOR(db, cursor) \
+#    define EXIM_DBCREATE_CURSOR(db, cursor) \
        ENV_TO_DB(db)->cursor(ENV_TO_DB(db), NULL, cursor, 0)
 
 /* EXIM_DBSCAN - returns TRUE if data is returned, FALSE at end */
-#   define EXIM_DBSCAN(db, key, data, first, cursor)      \
+#    define EXIM_DBSCAN(db, key, data, first, cursor)      \
        ((cursor)->c_get(cursor, &key, &data,         \
          (first? DB_FIRST : DB_NEXT)) == 0)
 
 /* EXIM_DBDELETE_CURSOR - terminate scanning operation */
-#   define EXIM_DBDELETE_CURSOR(cursor) \
+#    define EXIM_DBDELETE_CURSOR(cursor) \
        (cursor)->c_close(cursor)
 
 /* EXIM_DBCLOSE */
-#   define EXIM_DBCLOSE__(db) \
+#    define EXIM_DBCLOSE__(db) \
        (ENV_TO_DB(db)->close(ENV_TO_DB(db), 0) , ((DB_ENV *)(db))->close((DB_ENV *)(db), DB_FORCESYNC))
 
 /* Datum access types - these are intended to be assignable. */
 
-#   define EXIM_DATUM_SIZE(datum)  (datum).size
-#   define EXIM_DATUM_DATA(datum)  (datum).data
+#    define EXIM_DATUM_SIZE(datum)  (datum).size
+#    define EXIM_DATUM_DATA(datum)  (datum).data
 
 /* The whole datum structure contains other fields that must be cleared
 before use, but we don't have to free anything after reading data. */
 
-#   define EXIM_DATUM_INIT(datum)   memset(&datum, 0, sizeof(datum))
-#   define EXIM_DATUM_FREE(datum)
+#    define EXIM_DATUM_INIT(datum)   memset(&datum, 0, sizeof(datum))
+#    define EXIM_DATUM_FREE(datum)
 
-#  else        /* pre- 4.1 */
+#   else       /* pre- 4.1 */
 
-#   define EXIM_DB       DB
+#    define EXIM_DB       DB
 
 /* Cursor type, for scanning */
-#   define EXIM_CURSOR   DBC
+#    define EXIM_CURSOR   DBC
 
 /* The datum type used for queries */
-#   define EXIM_DATUM    DBT
+#    define EXIM_DATUM    DBT
 
 /* Some text for messages */
-#   define EXIM_DBTYPE   "db (v3/4)"
+#    define EXIM_DBTYPE   "db (v3/4)"
 
 /* Access functions */
 
 /* EXIM_DBOPEN - sets *dbpp to point to an EXIM_DB, NULL if failed. */
 
-#   define EXIM_DBOPEN__(name, dirname, flags, mode, dbpp) \
+#    define EXIM_DBOPEN__(name, dirname, flags, mode, dbpp) \
        if (db_create(dbpp, NULL, 0) != 0 || \
          ((*dbpp)->set_errcall(*dbpp, dbfn_bdb_error_callback), \
          ((*dbpp)->open)(*dbpp, CS name, NULL, \
@@ -247,219 +247,61 @@ before use, but we don't have to free anything after reading data. */
          mode)) != 0) *(dbpp) = NULL
 
 /* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */
-#   define EXIM_DBGET(db, key, data)      \
+#    define EXIM_DBGET(db, key, data)      \
        ((db)->get(db, NULL, &key, &data, 0) == 0)
 
 /* EXIM_DBPUT - returns nothing useful, assumes replace mode */
-#   define EXIM_DBPUT(db, key, data)      \
+#    define EXIM_DBPUT(db, key, data)      \
        (db)->put(db, NULL, &key, &data, 0)
 
 /* EXIM_DBPUTB - non-overwriting for use by dbmbuild */
-#   define EXIM_DBPUTB(db, key, data)      \
+#    define EXIM_DBPUTB(db, key, data)      \
        (db)->put(db, NULL, &key, &data, DB_NOOVERWRITE)
 
 /* Return values from EXIM_DBPUTB */
 
-#   define EXIM_DBPUTB_OK  0
-#   define EXIM_DBPUTB_DUP DB_KEYEXIST
+#    define EXIM_DBPUTB_OK  0
+#    define EXIM_DBPUTB_DUP DB_KEYEXIST
 
 /* EXIM_DBDEL */
-#   define EXIM_DBDEL(db, key)     (db)->del(db, NULL, &key, 0)
+#    define EXIM_DBDEL(db, key)     (db)->del(db, NULL, &key, 0)
 
 /* EXIM_DBCREATE_CURSOR - initialize for scanning operation */
 
-#   define EXIM_DBCREATE_CURSOR(db, cursor) \
+#    define EXIM_DBCREATE_CURSOR(db, cursor) \
        (db)->cursor(db, NULL, cursor, 0)
 
 /* EXIM_DBSCAN - returns TRUE if data is returned, FALSE at end */
-#   define EXIM_DBSCAN(db, key, data, first, cursor)      \
+#    define EXIM_DBSCAN(db, key, data, first, cursor)      \
        ((cursor)->c_get(cursor, &key, &data,         \
          (first? DB_FIRST : DB_NEXT)) == 0)
 
 /* EXIM_DBDELETE_CURSOR - terminate scanning operation */
-#   define EXIM_DBDELETE_CURSOR(cursor) \
+#    define EXIM_DBDELETE_CURSOR(cursor) \
        (cursor)->c_close(cursor)
 
 /* EXIM_DBCLOSE */
-#   define EXIM_DBCLOSE__(db)        (db)->close(db, 0)
+#    define EXIM_DBCLOSE__(db)        (db)->close(db, 0)
 
 /* Datum access types - these are intended to be assignable. */
 
-#   define EXIM_DATUM_SIZE(datum)  (datum).size
-#   define EXIM_DATUM_DATA(datum)  (datum).data
+#    define EXIM_DATUM_SIZE(datum)  (datum).size
+#    define EXIM_DATUM_DATA(datum)  (datum).data
 
 /* The whole datum structure contains other fields that must be cleared
 before use, but we don't have to free anything after reading data. */
 
-#   define EXIM_DATUM_INIT(datum)   memset(&datum, 0, sizeof(datum))
-#   define EXIM_DATUM_FREE(datum)
-
-#  endif
-
-
-# else /* DB_VERSION_MAJOR >= 3 */
-
-/******************* Berkeley db 2.x native definitions ********************/
-
-/* Basic DB type */
-# define EXIM_DB       DB
-
-/* Cursor type, for scanning */
-# define EXIM_CURSOR   DBC
-
-/* The datum type used for queries */
-# define EXIM_DATUM    DBT
-
-/* Some text for messages */
-# define EXIM_DBTYPE   "db (v2)"
-
-/* Access functions */
-
-/* EXIM_DBOPEN - sets *dbpp to point to an EXIM_DB, NULL if failed */
-# define EXIM_DBOPEN__(name, dirname, flags, mode, dbpp)         \
-       if ((errno = db_open(CS name, DB_HASH,           \
-         ((flags) == O_RDONLY)? DB_RDONLY : DB_CREATE, \
-         mode, NULL, NULL, dbpp)) != 0) *(dbpp) = NULL
-
-/* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */
-# define EXIM_DBGET(db, key, data)      \
-       ((db)->get(db, NULL, &key, &data, 0) == 0)
+#    define EXIM_DATUM_INIT(datum)   memset(&datum, 0, sizeof(datum))
+#    define EXIM_DATUM_FREE(datum)
 
-/* EXIM_DBPUT - returns nothing useful, assumes replace mode */
-# define EXIM_DBPUT(db, key, data)      \
-       (db)->put(db, NULL, &key, &data, 0)
-
-/* EXIM_DBPUTB - non-overwriting for use by dbmbuild */
-# define EXIM_DBPUTB(db, key, data)      \
-       (db)->put(db, NULL, &key, &data, DB_NOOVERWRITE)
-
-/* Return values from EXIM_DBPUTB */
-
-# define EXIM_DBPUTB_OK  0
-# define EXIM_DBPUTB_DUP DB_KEYEXIST
-
-/* EXIM_DBDEL */
-# define EXIM_DBDEL(db, key)     (db)->del(db, NULL, &key, 0)
-
-/* EXIM_DBCREATE_CURSOR - initialize for scanning operation */
+#   endif
 
-/* The API of this function was changed between releases 2.4.14 and 2.7.3. I do
-not know exactly where the change happened, but the Change Log for 2.5.9 lists
-the new option that is available, so I guess that it happened at 2.5.x. */
 
-# if DB_VERSION_MINOR >= 5
-# define EXIM_DBCREATE_CURSOR(db, cursor) \
-       (db)->cursor(db, NULL, cursor, 0)
+#  else /* DB_VERSION_MAJOR >= 3 */
+#   error Berkeley DB versions earlier than 3 are not supported */
+#  endif /* DB_VERSION_MAJOR */
 # else
-# define EXIM_DBCREATE_CURSOR(db, cursor) \
-       (db)->cursor(db, NULL, cursor)
-# endif
-
-/* EXIM_DBSCAN - returns TRUE if data is returned, FALSE at end */
-# define EXIM_DBSCAN(db, key, data, first, cursor)      \
-       ((cursor)->c_get(cursor, &key, &data,         \
-         (first? DB_FIRST : DB_NEXT)) == 0)
-
-/* EXIM_DBDELETE_CURSOR - terminate scanning operation */
-# define EXIM_DBDELETE_CURSOR(cursor) \
-       (cursor)->c_close(cursor)
-
-/* EXIM_DBCLOSE */
-# define EXIM_DBCLOSE__(db)        (db)->close(db, 0)
-
-/* Datum access types - these are intended to be assignable. */
-
-# define EXIM_DATUM_SIZE(datum)  (datum).size
-# define EXIM_DATUM_DATA(datum)  (datum).data
-
-/* The whole datum structure contains other fields that must be cleared
-before use, but we don't have to free anything after reading data. */
-
-# define EXIM_DATUM_INIT(datum)   memset(&datum, 0, sizeof(datum))
-# define EXIM_DATUM_FREE(datum)
-
-# endif /* DB_VERSION_MAJOR >= 3 */
-
-
-/* If DB_VERSION_TYPE is not defined, we have version 1.x */
-
-# else  /* DB_VERSION_TYPE */
-
-/******************* Berkeley db 1.x native definitions ********************/
-
-/* Basic DB type */
-# define EXIM_DB       DB
-
-/* Cursor type, not used with DB 1.x: just set up a dummy */
-# define EXIM_CURSOR   int
-
-/* The datum type used for queries */
-# define EXIM_DATUM    DBT
-
-/* Some text for messages */
-# define EXIM_DBTYPE   "db (v1)"
-
-/* When scanning, for the non-first case we historically just passed 0
-as the flags field and it worked.  On FreeBSD 8 it no longer works and
-instead leads to memory exhaustion.  The man-page on FreeBSD says to use
-R_NEXT, but this 1.x is a historical fallback and I've no idea how portable
-the use of that flag is; so the solution is to define R_NEXT here if it's not
-already defined, with a default value of 0 because that's what we've always
-before been able to pass successfully. */
-# ifndef R_NEXT
-#  define R_NEXT 0
-# endif
-
-/* Access functions */
-
-/* EXIM_DBOPEN - sets *dbpp to point to an EXIM_DB, NULL if failed */
-# define EXIM_DBOPEN__(name, dirname, flags, mode, dbpp) \
-       *(dbpp) = dbopen(CS name, flags, mode, DB_HASH, NULL)
-
-/* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */
-# define EXIM_DBGET(db, key, data)      \
-       ((db)->get(db, &key, &data, 0) == 0)
-
-/* EXIM_DBPUT - returns nothing useful, assumes replace mode */
-# define EXIM_DBPUT(db, key, data)      \
-       (db)->put(db, &key, &data, 0)
-
-/* EXIM_DBPUTB - non-overwriting for use by dbmbuild */
-# define EXIM_DBPUTB(db, key, data)      \
-       (db)->put(db, &key, &data, R_NOOVERWRITE)
-
-/* Returns from EXIM_DBPUTB */
-
-# define EXIM_DBPUTB_OK  0
-# define EXIM_DBPUTB_DUP 1
-
-/* EXIM_DBDEL */
-# define EXIM_DBDEL(db, key)     (db)->del(db, &key, 0)
-
-/* EXIM_DBCREATE_CURSOR - initialize for scanning operation (null) */
-# define EXIM_DBCREATE_CURSOR(db, cursor) {}
-
-/* EXIM_DBSCAN - returns TRUE if data is returned, FALSE at end */
-# define EXIM_DBSCAN(db, key, data, first, cursor)      \
-       ((db)->seq(db, &key, &data, (first? R_FIRST : R_NEXT)) == 0)
-
-/* EXIM_DBDELETE_CURSOR - terminate scanning operation (null). */
-# define EXIM_DBDELETE_CURSOR(cursor) { }
-
-/* EXIM_DBCLOSE */
-# define EXIM_DBCLOSE__(db)        (db)->close(db)
-
-/* Datum access types - these are intended to be assignable */
-
-# define EXIM_DATUM_SIZE(datum)  (datum).size
-# define EXIM_DATUM_DATA(datum)  (datum).data
-
-/* There's no clearing required before use, and we don't have to free anything
-after reading data. */
-
-# define EXIM_DATUM_INIT(datum)
-# define EXIM_DATUM_FREE(datum)
-
+#  error Berkeley DB version 1 is no longer supported
 # endif /* DB_VERSION_STRING */
 
 
@@ -483,7 +325,9 @@ after reading data. */
 typedef struct {
        GDBM_FILE gdbm;  /* Database */
        datum lkey;      /* Last key, for scans */
-} EXIM_DB;
+} gdbm_db;
+
+#define EXIM_DB gdbm_db
 
 /* Cursor type, not used with gdbm: just set up a dummy */
 # define EXIM_CURSOR int
@@ -499,15 +343,16 @@ typedef struct {
 
 /* EXIM_DBOPEN - returns a EXIM_DB *, NULL if failed */
 # define EXIM_DBOPEN__(name, dirname, flags, mode, dbpp) \
-     { (*(dbpp)) = (EXIM_DB *) malloc(sizeof(EXIM_DB));\
-       if (*(dbpp) != NULL) { \
-         (*(dbpp))->lkey.dptr = NULL;\
-         (*(dbpp))->gdbm = gdbm_open(CS name, 0, (((flags) & O_CREAT))?GDBM_WRCREAT:(((flags) & (O_RDWR|O_WRONLY))?GDBM_WRITER:GDBM_READER), mode, 0);\
-          if ((*(dbpp))->gdbm == NULL) {\
-              free(*(dbpp));\
-              *(dbpp) = NULL;\
+     { EXIM_DB * dbp = malloc(sizeof(EXIM_DB));\
+       if (dbp) { \
+         dbp->lkey.dptr = NULL;\
+         dbp->gdbm = gdbm_open(CS name, 0, (((flags) & O_CREAT))?GDBM_WRCREAT:(((flags) & (O_RDWR|O_WRONLY))?GDBM_WRITER:GDBM_READER), (mode), 0);\
+          if (!dbp->gdbm) {\
+              free(dbp);\
+              dbp = NULL;\
           }\
        }\
+       *(dbpp) = dbp;\
      }
 
 /* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */
@@ -594,8 +439,19 @@ interface */
 /* Access functions */
 
 /* EXIM_DBOPEN - returns a EXIM_DB *, NULL if failed */
-# define EXIM_DBOPEN__(name, dirname, flags, mode, dbpp) \
-       *(dbpp) = dbm_open(CS name, flags, mode)
+/* Check that the name given is not present. This catches
+a directory name; otherwise we would create the name.pag and
+name.dir files in the directory's parent. */
+
+# define EXIM_DBOPEN__(name, dirname, flags, mode, dbpp)               \
+       {                                                               \
+       struct stat st;                                                 \
+       *(dbpp) =    !(flags & O_CREAT)                                 \
+                || lstat(CCS (name), &st) != 0 && errno == ENOENT      \
+         ? dbm_open(CS (name), (flags), (mode))                                \
+        : (errno = (st.st_mode & S_IFMT) == S_IFDIR ? EISDIR : EEXIST, \
+          NULL);                                                       \
+       }
 
 /* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */
 # define EXIM_DBGET(db, key, data)      \
@@ -668,9 +524,11 @@ after reading data. */
       : (flags) == O_RDWR ? "O_RDWR"   \
       : (flags) == (O_RDWR|O_CREAT) ? "O_RDWR|O_CREAT" \
       : "??"); \
-  if (is_tainted2(name, LOG_MAIN|LOG_PANIC, "Tainted name '%s' for DB file not permitted", name) \
-      || is_tainted2(dirname, LOG_MAIN|LOG_PANIC, "Tainted name '%s' for DB directory not permitted", dirname)) \
+  if (is_tainted(name) || is_tainted(dirname)) \
+    { \
+    log_write(0, LOG_MAIN|LOG_PANIC, "Tainted name for DB file not permitted"); \
     *dbpp = NULL; \
+    } \
   else \
     { EXIM_DBOPEN__(name, dirname, flags, mode, dbpp); } \
   DEBUG(D_hints_lookup) debug_printf_indent("returned from EXIM_DBOPEN: %p\n", *dbpp); \
@@ -683,6 +541,12 @@ after reading data. */
 
 # endif
 
+#ifndef EXIM_DB
+# define EXIM_DB void          /* dummy */
+#endif
+#ifndef EXIM_CURSOR
+# define EXIM_CURSOR void      /* dummy */
+#endif
 /********************* End of dbm library definitions **********************/