X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/d5b80e59458182b2d557a929a18cb8c70cd56b68..a375c22c1df005c1bd710af1e540fba6e9a496d1:/src/src/dbfn.c diff --git a/src/src/dbfn.c b/src/src/dbfn.c index 5529fe93f..5cbe10c1f 100644 --- a/src/src/dbfn.c +++ b/src/src/dbfn.c @@ -2,12 +2,18 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2015 */ +/* Copyright (c) University of Cambridge 1995 - 2018 */ +/* Copyright (c) The Exim Maintainers 2020 */ /* 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/ */ +#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 @@ -57,8 +63,6 @@ log_write(0, LOG_MAIN, "Berkeley DB error: %s", msg); #endif - - /************************************************* * Open and lock a database file * *************************************************/ @@ -72,6 +76,7 @@ Arguments: dbblock Points to an open_db block to be filled in. lof If TRUE, write to the log for actual open failures (locking failures are always logged). + panic If TRUE, panic on failure to create the db directory Returns: NULL if the open failed, or the locking failed. After locking failures, errno is zero. @@ -85,13 +90,14 @@ moment I haven't changed them. */ open_db * -dbfn_open(uschar *name, int flags, open_db *dbblock, BOOL lof) +dbfn_open(uschar *name, int flags, open_db *dbblock, BOOL lof, BOOL panic) { 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++; /* The first thing to do is to open a separate file on which to lock. This ensures that Exim has exclusive use of the database before it even tries to @@ -109,18 +115,20 @@ exists, there is no error. */ 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, 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; } @@ -131,12 +139,12 @@ lock_data.l_type = read_only? F_RDLCK : F_WRLCK; lock_data.l_whence = lock_data.l_start = lock_data.l_len = 0; DEBUG(D_hints_lookup|D_retry|D_route|D_deliver) - debug_printf("locking %s\n", filename); + debug_printf_indent("locking %s\n", filename); sigalrm_seen = FALSE; -alarm(EXIMDB_LOCK_TIMEOUT); +ALARM(EXIMDB_LOCK_TIMEOUT); rc = fcntl(dbblock->lockfd, F_SETLKW, &lock_data); -alarm(0); +ALARM_CLR(0); if (sigalrm_seen) errno = ETIMEDOUT; if (rc < 0) @@ -146,10 +154,11 @@ if (rc < 0) errno == ETIMEDOUT ? "timed out" : strerror(errno)); (void)close(dbblock->lockfd); errno = 0; /* Indicates locking failure */ + DEBUG(D_hints_lookup) acl_level--; return NULL; } -DEBUG(D_hints_lookup) debug_printf("locked %s\n", filename); +DEBUG(D_hints_lookup) debug_printf_indent("locked %s\n", filename); /* At this point we have an opened and locked separate lock file, that is, exclusive access to the database, so we can go ahead and open it. If we are @@ -161,57 +170,17 @@ it easy to pin this down, there are now debug statements on either side of the 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("%s appears not to exist: trying to create\n", filename); - created = TRUE; + debug_printf_indent("%s appears not to exist: trying to create\n", filename); 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("ensuring %s is owned by exim\n", filename); - if (Uchown(filename, exim_uid, exim_gid)) - DEBUG(D_hints_lookup) debug_printf("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 @@ -219,20 +188,22 @@ exist. */ 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("%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; + DEBUG(D_hints_lookup) acl_level--; return NULL; } DEBUG(D_hints_lookup) - debug_printf("opened hints database %s: flags=%s\n", filename, + debug_printf_indent("opened hints database %s: flags=%s\n", filename, flags == O_RDONLY ? "O_RDONLY" : flags == O_RDWR ? "O_RDWR" : flags == (O_RDWR|O_CREAT) ? "O_RDWR|O_CREAT" @@ -263,7 +234,8 @@ dbfn_close(open_db *dbblock) { EXIM_DBCLOSE(dbblock->dbptr); (void)close(dbblock->lockfd); -DEBUG(D_hints_lookup) debug_printf("closed hints database and lockfile\n"); +DEBUG(D_hints_lookup) + { debug_printf_indent("closed hints database and lockfile\n"); acl_level--; } } @@ -296,11 +268,11 @@ dbfn_read_with_length(open_db *dbblock, const uschar *key, int *length) 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); -DEBUG(D_hints_lookup) debug_printf("dbfn_read: key=%s\n", key); +DEBUG(D_hints_lookup) debug_printf_indent("dbfn_read: key=%s\n", key); EXIM_DATUM_INIT(key_datum); /* Some DBM libraries require the datum */ EXIM_DATUM_INIT(result_datum); /* to be cleared before use. */ @@ -309,7 +281,10 @@ EXIM_DATUM_SIZE(key_datum) = 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); @@ -318,6 +293,34 @@ return yield; } +/* 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 * @@ -340,12 +343,12 @@ dbfn_write(open_db *dbblock, const uschar *key, void *ptr, int length) 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); -DEBUG(D_hints_lookup) debug_printf("dbfn_write: key=%s\n", key); +DEBUG(D_hints_lookup) debug_printf_indent("dbfn_write: key=%s\n", key); EXIM_DATUM_INIT(key_datum); /* Some DBM libraries require the datum */ EXIM_DATUM_INIT(value_datum); /* to be cleared before use. */ @@ -374,7 +377,9 @@ int 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); memcpy(key_copy, key, klen); EXIM_DATUM key_datum; @@ -407,7 +412,8 @@ dbfn_scan(open_db *dbblock, BOOL start, EXIM_CURSOR **cursor) { 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"); /* Some dbm require an initialization */ @@ -526,7 +532,7 @@ while (Ufgets(buffer, 256, stdin) != NULL) } start = clock(); - odb = dbfn_open(s, O_RDWR, dbblock + i, TRUE); + odb = dbfn_open(s, O_RDWR, dbblock + i, TRUE, TRUE); stop = clock(); if (odb)