*************************************************/
/* Copyright (c) University of Cambridge 1995 - 2018 */
+/* Copyright (c) The Exim Maintainers 2020 - 2021 */
/* See the file NOTICE for conditions of use and distribution. */
#include "exim.h"
+/* We have buffers holding path names for database files.
+PATH_MAX could be used here, but would be wasting memory, as we deal
+with database files like $spooldirectory/db/<name> */
+#define PATHLEN 256
+
/* Functions for accessing Exim's hints database, which consists of a number of
different DBM files. This module does not contain code for reading DBM files
#endif
-
-
/*************************************************
* Open and lock a database file *
*************************************************/
{
int rc, save_errno;
BOOL read_only = flags == O_RDONLY;
-BOOL created = FALSE;
flock_t lock_data;
-uschar dirname[256], filename[256];
+uschar dirname[PATHLEN], filename[PATHLEN];
DEBUG(D_hints_lookup) acl_level++;
snprintf(CS dirname, sizeof(dirname), "%s/db", spool_directory);
snprintf(CS filename, sizeof(filename), "%s/%s.lockfile", dirname, name);
+priv_drop_temp(exim_uid, exim_gid);
if ((dbblock->lockfd = Uopen(filename, O_RDWR, EXIMDB_LOCKFILE_MODE)) < 0)
{
- created = TRUE;
(void)directory_make(spool_directory, US"db", EXIMDB_DIRECTORY_MODE, panic);
dbblock->lockfd = Uopen(filename, O_RDWR|O_CREAT, EXIMDB_LOCKFILE_MODE);
}
+priv_restore();
if (dbblock->lockfd < 0)
{
log_write(0, LOG_MAIN, "%s",
- string_open_failed(errno, "database lock file %s", filename));
+ string_open_failed("database lock file %s", filename));
errno = 0; /* Indicates locking failure */
DEBUG(D_hints_lookup) acl_level--;
return NULL;
open call. */
snprintf(CS filename, sizeof(filename), "%s/%s", dirname, name);
-EXIM_DBOPEN(filename, dirname, flags, EXIMDB_MODE, &(dbblock->dbptr));
+priv_drop_temp(exim_uid, exim_gid);
+EXIM_DBOPEN(filename, dirname, flags, EXIMDB_MODE, &(dbblock->dbptr));
if (!dbblock->dbptr && errno == ENOENT && flags == O_RDWR)
{
DEBUG(D_hints_lookup)
debug_printf_indent("%s appears not to exist: trying to create\n", filename);
- created = TRUE;
EXIM_DBOPEN(filename, dirname, flags|O_CREAT, EXIMDB_MODE, &(dbblock->dbptr));
}
-
save_errno = errno;
-
-/* If we are running as root and this is the first access to the database, its
-files will be owned by root. We want them to be owned by exim. We detect this
-situation by noting above when we had to create the lock file or the database
-itself. Because the different dbm libraries use different extensions for their
-files, I don't know of any easier way of arranging this than scanning the
-directory for files with the appropriate base name. At least this deals with
-the lock file at the same time. Also, the directory will typically have only
-half a dozen files, so the scan will be quick.
-
-This code is placed here, before the test for successful opening, because there
-was a case when a file was created, but the DBM library still returned NULL
-because of some problem. It also sorts out the lock file if that was created
-but creation of the database file failed. */
-
-if (created && geteuid() == root_uid)
- {
- DIR *dd;
- struct dirent *ent;
- uschar *lastname = Ustrrchr(filename, '/') + 1;
- int namelen = Ustrlen(name);
-
- *lastname = 0;
- dd = opendir(CS filename);
-
- while ((ent = readdir(dd)))
- if (Ustrncmp(ent->d_name, name, namelen) == 0)
- {
- struct stat statbuf;
- Ustrcpy(lastname, ent->d_name);
- if (Ustat(filename, &statbuf) >= 0 && statbuf.st_uid != exim_uid)
- {
- DEBUG(D_hints_lookup) debug_printf_indent("ensuring %s is owned by exim\n", filename);
- if (exim_chown(filename, exim_uid, exim_gid))
- DEBUG(D_hints_lookup) debug_printf_indent("failed setting %s to owned by exim\n", filename);
- }
- }
-
- closedir(dd);
- }
+priv_restore();
/* If the open has failed, return NULL, leaving errno set. If lof is TRUE,
log the event - also for debugging - but debug only if the file just doesn't
if (!dbblock->dbptr)
{
+ errno = save_errno;
if (lof && save_errno != ENOENT)
- log_write(0, LOG_MAIN, "%s", string_open_failed(save_errno, "DB file %s",
+ log_write(0, LOG_MAIN, "%s", string_open_failed("DB file %s",
filename));
else
DEBUG(D_hints_lookup)
- debug_printf_indent("%s\n", CS string_open_failed(save_errno, "DB file %s",
+ debug_printf_indent("%s\n", CS string_open_failed("DB file %s",
filename));
(void)close(dbblock->lockfd);
errno = save_errno;
void *yield;
EXIM_DATUM key_datum, result_datum;
int klen = Ustrlen(key) + 1;
-uschar * key_copy = store_get(klen);
+uschar * key_copy = store_get(klen, is_tainted(key));
memcpy(key_copy, key, klen);
if (!EXIM_DBGET(dbblock->dbptr, key_datum, result_datum)) return NULL;
-yield = store_get(EXIM_DATUM_SIZE(result_datum));
+/* Assume the data store could have been tainted. Properly, we should
+store the taint status 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);
}
+/* Read a record. If the length is not as expected then delete it, write
+an error log line, delete the record 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 *
EXIM_DATUM key_datum, value_datum;
dbdata_generic *gptr = (dbdata_generic *)ptr;
int klen = Ustrlen(key) + 1;
-uschar * key_copy = store_get(klen);
+uschar * key_copy = store_get(klen, is_tainted(key));
memcpy(key_copy, key, klen);
gptr->time_stamp = time(NULL);
dbfn_delete(open_db *dbblock, const uschar *key)
{
int klen = Ustrlen(key) + 1;
-uschar * key_copy = store_get(klen);
+uschar * key_copy = store_get(klen, is_tainted(key));
DEBUG(D_hints_lookup) debug_printf_indent("dbfn_delete: key=%s\n", key);
{
EXIM_DATUM key_datum, value_datum;
uschar *yield;
-value_datum = value_datum; /* dummy; not all db libraries use this */
DEBUG(D_hints_lookup) debug_printf_indent("dbfn_scan\n");