1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) University of Cambridge 1995 - 2018 */
6 /* Copyright (c) The Exim Maintainers 2020 */
7 /* See the file NOTICE for conditions of use and distribution. */
13 /* Functions for accessing Exim's hints database, which consists of a number of
14 different DBM files. This module does not contain code for reading DBM files
15 for (e.g.) alias expansion. That is all contained within the general search
16 functions. As Exim now has support for several DBM interfaces, all the relevant
17 functions are called as macros.
19 All the data in Exim's database is in the nature of *hints*. Therefore it
20 doesn't matter if it gets destroyed by accident. These functions are not
21 supposed to implement a "safe" database.
23 Keys are passed in as C strings, and the terminating zero *is* used when
24 building the dbm files. This just makes life easier when scanning the files
27 Synchronization is required on the database files, and this is achieved by
28 means of locking on independent lock files. (Earlier attempts to lock on the
29 DBM files themselves were never completely successful.) Since callers may in
30 general want to do more than one read or write while holding the lock, there
31 are separate open and close functions. However, the calling modules should
32 arrange to hold the locks for the bare minimum of time. */
36 /*************************************************
37 * Berkeley DB error callback *
38 *************************************************/
40 /* For Berkeley DB >= 2, we can define a function to be called in case of DB
41 errors. This should help with debugging strange DB problems, e.g. getting "File
42 exists" when you try to open a db file. The API for this function was changed
45 #if defined(USE_DB) && defined(DB_VERSION_STRING)
47 #if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3)
48 dbfn_bdb_error_callback(const DB_ENV *dbenv, const char *pfx, const char *msg)
52 dbfn_bdb_error_callback(const char *pfx, char *msg)
56 log_write(0, LOG_MAIN, "Berkeley DB error: %s", msg);
64 PRIV_DROPPING, PRIV_DROPPED,
65 PRIV_RESTORING, PRIV_RESTORED
66 } priv_state = PRIV_RESTORED;
68 static uid_t priv_euid;
69 static gid_t priv_egid;
70 static gid_t priv_groups[EXIM_GROUPLIST_SIZE + 1];
71 static int priv_ngroups;
73 /* Inspired by OpenSSH's temporarily_use_uid(). Thanks! */
76 priv_drop_temp(const uid_t temp_uid, const gid_t temp_gid)
78 if (priv_state != PRIV_RESTORED) _exit(EXIT_FAILURE);
79 priv_state = PRIV_DROPPING;
81 priv_euid = geteuid();
82 if (priv_euid == root_uid)
84 priv_egid = getegid();
85 priv_ngroups = getgroups(nelem(priv_groups), priv_groups);
86 if (priv_ngroups < 0) _exit(EXIT_FAILURE);
88 if (priv_ngroups > 0 && setgroups(1, &temp_gid) != 0) _exit(EXIT_FAILURE);
89 if (setegid(temp_gid) != 0) _exit(EXIT_FAILURE);
90 if (seteuid(temp_uid) != 0) _exit(EXIT_FAILURE);
92 if (geteuid() != temp_uid) _exit(EXIT_FAILURE);
93 if (getegid() != temp_gid) _exit(EXIT_FAILURE);
96 priv_state = PRIV_DROPPED;
99 /* Inspired by OpenSSH's restore_uid(). Thanks! */
104 if (priv_state != PRIV_DROPPED) _exit(EXIT_FAILURE);
105 priv_state = PRIV_RESTORING;
107 if (priv_euid == root_uid)
109 if (seteuid(priv_euid) != 0) _exit(EXIT_FAILURE);
110 if (setegid(priv_egid) != 0) _exit(EXIT_FAILURE);
111 if (priv_ngroups > 0 && setgroups(priv_ngroups, priv_groups) != 0) _exit(EXIT_FAILURE);
113 if (geteuid() != priv_euid) _exit(EXIT_FAILURE);
114 if (getegid() != priv_egid) _exit(EXIT_FAILURE);
117 priv_state = PRIV_RESTORED;
123 /*************************************************
124 * Open and lock a database file *
125 *************************************************/
127 /* Used for accessing Exim's hints databases.
130 name The single-component name of one of Exim's database files.
131 flags Either O_RDONLY or O_RDWR, indicating the type of open required;
132 O_RDWR implies "create if necessary"
133 dbblock Points to an open_db block to be filled in.
134 lof If TRUE, write to the log for actual open failures (locking failures
136 panic If TRUE, panic on failure to create the db directory
138 Returns: NULL if the open failed, or the locking failed. After locking
139 failures, errno is zero.
141 On success, dbblock is returned. This contains the dbm pointer and
142 the fd of the locked lock file.
144 There are some calls that use O_RDWR|O_CREAT for the flags. Having discovered
145 this in December 2005, I'm not sure if this is correct or not, but for the
146 moment I haven't changed them.
150 dbfn_open(uschar *name, int flags, open_db *dbblock, BOOL lof, BOOL panic)
153 BOOL read_only = flags == O_RDONLY;
155 uschar dirname[256], filename[256];
157 DEBUG(D_hints_lookup) acl_level++;
159 /* The first thing to do is to open a separate file on which to lock. This
160 ensures that Exim has exclusive use of the database before it even tries to
161 open it. Early versions tried to lock on the open database itself, but that
162 gave rise to mysterious problems from time to time - it was suspected that some
163 DB libraries "do things" on their open() calls which break the interlocking.
164 The lock file is never written to, but we open it for writing so we can get a
165 write lock if required. If it does not exist, we create it. This is done
166 separately so we know when we have done it, because when running as root we
167 need to change the ownership - see the bottom of this function. We also try to
168 make the directory as well, just in case. We won't be doing this many times
169 unnecessarily, because usually the lock file will be there. If the directory
170 exists, there is no error. */
172 snprintf(CS dirname, sizeof(dirname), "%s/db", spool_directory);
173 snprintf(CS filename, sizeof(filename), "%s/%s.lockfile", dirname, name);
175 priv_drop_temp(exim_uid, exim_gid);
176 if ((dbblock->lockfd = Uopen(filename, O_RDWR, EXIMDB_LOCKFILE_MODE)) < 0)
178 (void)directory_make(spool_directory, US"db", EXIMDB_DIRECTORY_MODE, panic);
179 dbblock->lockfd = Uopen(filename, O_RDWR|O_CREAT, EXIMDB_LOCKFILE_MODE);
183 if (dbblock->lockfd < 0)
185 log_write(0, LOG_MAIN, "%s",
186 string_open_failed(errno, "database lock file %s", filename));
187 errno = 0; /* Indicates locking failure */
188 DEBUG(D_hints_lookup) acl_level--;
192 /* Now we must get a lock on the opened lock file; do this with a blocking
193 lock that times out. */
195 lock_data.l_type = read_only? F_RDLCK : F_WRLCK;
196 lock_data.l_whence = lock_data.l_start = lock_data.l_len = 0;
198 DEBUG(D_hints_lookup|D_retry|D_route|D_deliver)
199 debug_printf_indent("locking %s\n", filename);
201 sigalrm_seen = FALSE;
202 ALARM(EXIMDB_LOCK_TIMEOUT);
203 rc = fcntl(dbblock->lockfd, F_SETLKW, &lock_data);
206 if (sigalrm_seen) errno = ETIMEDOUT;
209 log_write(0, LOG_MAIN|LOG_PANIC, "Failed to get %s lock for %s: %s",
210 read_only ? "read" : "write", filename,
211 errno == ETIMEDOUT ? "timed out" : strerror(errno));
212 (void)close(dbblock->lockfd);
213 errno = 0; /* Indicates locking failure */
214 DEBUG(D_hints_lookup) acl_level--;
218 DEBUG(D_hints_lookup) debug_printf_indent("locked %s\n", filename);
220 /* At this point we have an opened and locked separate lock file, that is,
221 exclusive access to the database, so we can go ahead and open it. If we are
222 expected to create it, don't do so at first, again so that we can detect
223 whether we need to change its ownership (see comments about the lock file
224 above.) There have been regular reports of crashes while opening hints
225 databases - often this is caused by non-matching db.h and the library. To make
226 it easy to pin this down, there are now debug statements on either side of the
229 snprintf(CS filename, sizeof(filename), "%s/%s", dirname, name);
231 priv_drop_temp(exim_uid, exim_gid);
232 EXIM_DBOPEN(filename, dirname, flags, EXIMDB_MODE, &(dbblock->dbptr));
233 if (!dbblock->dbptr && errno == ENOENT && flags == O_RDWR)
235 DEBUG(D_hints_lookup)
236 debug_printf_indent("%s appears not to exist: trying to create\n", filename);
237 EXIM_DBOPEN(filename, dirname, flags|O_CREAT, EXIMDB_MODE, &(dbblock->dbptr));
242 /* If the open has failed, return NULL, leaving errno set. If lof is TRUE,
243 log the event - also for debugging - but debug only if the file just doesn't
248 if (lof && save_errno != ENOENT)
249 log_write(0, LOG_MAIN, "%s", string_open_failed(save_errno, "DB file %s",
252 DEBUG(D_hints_lookup)
253 debug_printf_indent("%s\n", CS string_open_failed(save_errno, "DB file %s",
255 (void)close(dbblock->lockfd);
257 DEBUG(D_hints_lookup) acl_level--;
261 DEBUG(D_hints_lookup)
262 debug_printf_indent("opened hints database %s: flags=%s\n", filename,
263 flags == O_RDONLY ? "O_RDONLY"
264 : flags == O_RDWR ? "O_RDWR"
265 : flags == (O_RDWR|O_CREAT) ? "O_RDWR|O_CREAT"
268 /* Pass back the block containing the opened database handle and the open fd
277 /*************************************************
278 * Unlock and close a database file *
279 *************************************************/
281 /* Closing a file automatically unlocks it, so after closing the database, just
284 Argument: a pointer to an open database block
289 dbfn_close(open_db *dbblock)
291 EXIM_DBCLOSE(dbblock->dbptr);
292 (void)close(dbblock->lockfd);
293 DEBUG(D_hints_lookup)
294 { debug_printf_indent("closed hints database and lockfile\n"); acl_level--; }
300 /*************************************************
301 * Read from database file *
302 *************************************************/
304 /* Passing back the pointer unchanged is useless, because there is
305 no guarantee of alignment. Since all the records used by Exim need
306 to be properly aligned to pick out the timestamps, etc., we might as
307 well do the copying centrally here.
309 Most calls don't need the length, so there is a macro called dbfn_read which
310 has two arguments; it calls this function adding NULL as the third.
313 dbblock a pointer to an open database block
314 key the key of the record to be read
315 length a pointer to an int into which to return the length, if not NULL
317 Returns: a pointer to the retrieved record, or
318 NULL if the record is not found
322 dbfn_read_with_length(open_db *dbblock, const uschar *key, int *length)
325 EXIM_DATUM key_datum, result_datum;
326 int klen = Ustrlen(key) + 1;
327 uschar * key_copy = store_get(klen, is_tainted(key));
329 memcpy(key_copy, key, klen);
331 DEBUG(D_hints_lookup) debug_printf_indent("dbfn_read: key=%s\n", key);
333 EXIM_DATUM_INIT(key_datum); /* Some DBM libraries require the datum */
334 EXIM_DATUM_INIT(result_datum); /* to be cleared before use. */
335 EXIM_DATUM_DATA(key_datum) = CS key_copy;
336 EXIM_DATUM_SIZE(key_datum) = klen;
338 if (!EXIM_DBGET(dbblock->dbptr, key_datum, result_datum)) return NULL;
340 /* Assume the data store could have been tainted. Properly, we should
341 store the taint status with the data. */
343 yield = store_get(EXIM_DATUM_SIZE(result_datum), TRUE);
344 memcpy(yield, EXIM_DATUM_DATA(result_datum), EXIM_DATUM_SIZE(result_datum));
345 if (length != NULL) *length = EXIM_DATUM_SIZE(result_datum);
347 EXIM_DATUM_FREE(result_datum); /* Some DBM libs require freeing */
353 /*************************************************
354 * Write to database file *
355 *************************************************/
359 dbblock a pointer to an open database block
360 key the key of the record to be written
361 ptr a pointer to the record to be written
362 length the length of the record to be written
364 Returns: the yield of the underlying dbm or db "write" function. If this
365 is dbm, the value is zero for OK.
369 dbfn_write(open_db *dbblock, const uschar *key, void *ptr, int length)
371 EXIM_DATUM key_datum, value_datum;
372 dbdata_generic *gptr = (dbdata_generic *)ptr;
373 int klen = Ustrlen(key) + 1;
374 uschar * key_copy = store_get(klen, is_tainted(key));
376 memcpy(key_copy, key, klen);
377 gptr->time_stamp = time(NULL);
379 DEBUG(D_hints_lookup) debug_printf_indent("dbfn_write: key=%s\n", key);
381 EXIM_DATUM_INIT(key_datum); /* Some DBM libraries require the datum */
382 EXIM_DATUM_INIT(value_datum); /* to be cleared before use. */
383 EXIM_DATUM_DATA(key_datum) = CS key_copy;
384 EXIM_DATUM_SIZE(key_datum) = klen;
385 EXIM_DATUM_DATA(value_datum) = CS ptr;
386 EXIM_DATUM_SIZE(value_datum) = length;
387 return EXIM_DBPUT(dbblock->dbptr, key_datum, value_datum);
392 /*************************************************
393 * Delete record from database file *
394 *************************************************/
398 dbblock a pointer to an open database block
399 key the key of the record to be deleted
401 Returns: the yield of the underlying dbm or db "delete" function.
405 dbfn_delete(open_db *dbblock, const uschar *key)
407 int klen = Ustrlen(key) + 1;
408 uschar * key_copy = store_get(klen, is_tainted(key));
410 DEBUG(D_hints_lookup) debug_printf_indent("dbfn_delete: key=%s\n", key);
412 memcpy(key_copy, key, klen);
413 EXIM_DATUM key_datum;
414 EXIM_DATUM_INIT(key_datum); /* Some DBM libraries require clearing */
415 EXIM_DATUM_DATA(key_datum) = CS key_copy;
416 EXIM_DATUM_SIZE(key_datum) = klen;
417 return EXIM_DBDEL(dbblock->dbptr, key_datum);
422 /*************************************************
423 * Scan the keys of a database file *
424 *************************************************/
428 dbblock a pointer to an open database block
429 start TRUE if starting a new scan
430 FALSE if continuing with the current scan
431 cursor a pointer to a pointer to a cursor anchor, for those dbm libraries
432 that use the notion of a cursor
434 Returns: the next record from the file, or
435 NULL if there are no more
439 dbfn_scan(open_db *dbblock, BOOL start, EXIM_CURSOR **cursor)
441 EXIM_DATUM key_datum, value_datum;
443 value_datum = value_datum; /* dummy; not all db libraries use this */
445 DEBUG(D_hints_lookup) debug_printf_indent("dbfn_scan\n");
447 /* Some dbm require an initialization */
449 if (start) EXIM_DBCREATE_CURSOR(dbblock->dbptr, cursor);
451 EXIM_DATUM_INIT(key_datum); /* Some DBM libraries require the datum */
452 EXIM_DATUM_INIT(value_datum); /* to be cleared before use. */
454 yield = (EXIM_DBSCAN(dbblock->dbptr, key_datum, value_datum, start, *cursor))?
455 US EXIM_DATUM_DATA(key_datum) : NULL;
457 /* Some dbm require a termination */
459 if (!yield) EXIM_DBDELETE_CURSOR(*cursor);
465 /*************************************************
466 **************************************************
467 * Stand-alone test program *
468 **************************************************
469 *************************************************/
474 main(int argc, char **cargv)
477 int max_db = sizeof(dbblock)/sizeof(open_db);
481 dbdata_wait *dbwait = NULL;
482 uschar **argv = USS cargv;
484 uschar structbuffer[1024];
488 printf("Usage: test_dbfn directory\n");
489 printf("The subdirectory called \"db\" in the given directory is used for\n");
490 printf("the files used in this test program.\n");
496 spool_directory = argv[1];
497 debug_selector = D_all - D_memory;
499 big_buffer = malloc(big_buffer_size);
501 for (i = 0; i < max_db; i++) dbblock[i].dbptr = NULL;
503 printf("\nExim's db functions tester: interface type is %s\n", EXIM_DBTYPE);
504 printf("DBM library: ");
506 #ifdef DB_VERSION_STRING
507 printf("Berkeley DB: %s\n", DB_VERSION_STRING);
508 #elif defined(BTREEVERSION) && defined(HASHVERSION)
510 printf("probably Berkeley DB version 1.8x (native mode)\n");
512 printf("probably Berkeley DB version 1.8x (compatibility mode)\n");
514 #elif defined(_DBM_RDONLY) || defined(dbm_dirfno)
515 printf("probably ndbm\n");
516 #elif defined(USE_TDB)
517 printf("using tdb\n");
520 printf("probably GDBM (native mode)\n");
522 printf("probably GDBM (compatibility mode)\n");
526 /* Test the functions */
528 printf("\nTest the functions\n> ");
530 while (Ufgets(buffer, 256, stdin) != NULL)
532 int len = Ustrlen(buffer);
536 uschar *cmd = buffer;
537 while (len > 0 && isspace((uschar)buffer[len-1])) len--;
540 if (isdigit((uschar)*cmd))
543 while (isdigit((uschar)*cmd)) cmd++;
544 while (isspace((uschar)*cmd)) cmd++;
547 if (Ustrncmp(cmd, "open", 4) == 0)
552 while (isspace((uschar)*s)) s++;
554 for (i = 0; i < max_db; i++)
555 if (dbblock[i].dbptr == NULL) break;
559 printf("Too many open databases\n> ");
564 odb = dbfn_open(s, O_RDWR, dbblock + i, TRUE, TRUE);
570 printf("opened %d\n", current);
572 /* Other error cases will have written messages */
573 else if (errno == ENOENT)
575 printf("open failed: %s%s\n", strerror(errno),
577 " (or other Berkeley DB error)"
585 else if (Ustrncmp(cmd, "write", 5) == 0)
588 uschar *key = cmd + 5;
593 printf("No current database\n");
597 while (isspace((uschar)*key)) key++;
599 while (*data != 0 && !isspace((uschar)*data)) data++;
601 while (isspace((uschar)*data)) data++;
603 dbwait = (dbdata_wait *)(&structbuffer);
604 Ustrcpy(dbwait->text, data);
608 rc = dbfn_write(dbblock + current, key, dbwait,
609 Ustrlen(data) + sizeof(dbdata_wait));
611 if (rc != 0) printf("Failed: %s\n", strerror(errno));
614 else if (Ustrncmp(cmd, "read", 4) == 0)
616 uschar *key = cmd + 4;
619 printf("No current database\n");
622 while (isspace((uschar)*key)) key++;
625 dbwait = (dbdata_wait *)dbfn_read_with_length(dbblock+ current, key, NULL);
627 printf("%s\n", (dbwait == NULL)? "<not found>" : CS dbwait->text);
630 else if (Ustrncmp(cmd, "delete", 6) == 0)
632 uschar *key = cmd + 6;
635 printf("No current database\n");
638 while (isspace((uschar)*key)) key++;
639 dbfn_delete(dbblock + current, key);
642 else if (Ustrncmp(cmd, "scan", 4) == 0)
645 BOOL startflag = TRUE;
647 uschar keybuffer[256];
650 printf("No current database\n");
654 while ((key = dbfn_scan(dbblock + current, startflag, &cursor)) != NULL)
657 Ustrcpy(keybuffer, key);
658 dbwait = (dbdata_wait *)dbfn_read_with_length(dbblock + current,
660 printf("%s: %s\n", keybuffer, dbwait->text);
663 printf("End of scan\n");
666 else if (Ustrncmp(cmd, "close", 5) == 0)
669 while (isspace((uschar)*s)) s++;
671 if (i >= max_db || dbblock[i].dbptr == NULL) printf("Not open\n"); else
674 dbfn_close(dbblock + i);
676 dbblock[i].dbptr = NULL;
677 if (i == current) current = -1;
681 else if (Ustrncmp(cmd, "file", 4) == 0)
684 while (isspace((uschar)*s)) s++;
686 if (i >= max_db || dbblock[i].dbptr == NULL) printf("Not open\n");
690 else if (Ustrncmp(cmd, "time", 4) == 0)
692 showtime = ~showtime;
693 printf("Timing %s\n", showtime? "on" : "off");
696 else if (Ustrcmp(cmd, "q") == 0 || Ustrncmp(cmd, "quit", 4) == 0) break;
698 else if (Ustrncmp(cmd, "help", 4) == 0)
700 printf("close [<number>] close file [<number>]\n");
701 printf("delete <key> remove record from current file\n");
702 printf("file <number> make file <number> current\n");
703 printf("open <name> open db file\n");
704 printf("q[uit] exit program\n");
705 printf("read <key> read record from current file\n");
706 printf("scan scan current file\n");
707 printf("time time display on/off\n");
708 printf("write <key> <rest-of-line> write record to current file\n");
711 else printf("Eh?\n");
713 if (showtime && stop >= start)
714 printf("start=%d stop=%d difference=%d\n", (int)start, (int)stop,
715 (int)(stop - start));
720 for (i = 0; i < max_db; i++)
722 if (dbblock[i].dbptr != NULL)
724 printf("\nClosing %d", i);
725 dbfn_close(dbblock + i);