X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/d7978c0f8af20ff4c3f770589b1bb81568aecff3..44226149c4c467c9d109ead4b3c7bbe15b634997:/src/src/exim_dbutil.c diff --git a/src/src/exim_dbutil.c b/src/src/exim_dbutil.c index a0514f009..697b87500 100644 --- a/src/src/exim_dbutil.c +++ b/src/src/exim_dbutil.c @@ -3,6 +3,7 @@ *************************************************/ /* Copyright (c) University of Cambridge 1995 - 2018 */ +/* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -16,10 +17,13 @@ maintaining Exim hints databases. In all cases, the first argument is the name of the spool directory. The second argument is the name of the database file. The available names are: - retry: retry delivery information - misc: miscellaneous hints data - wait-: message waiting information; is a transport name - callout: callout verification cache + callout: callout verification cache + misc: miscellaneous hints data + ratelimit: record for ACL "ratelimit" condition + retry: etry delivery information + seen: imestamp records for ACL "seen" condition + tls: TLS session resumption cache + wait-: message waiting information; is a transport name There are a number of common subroutines, followed by three main programs, whose inclusion is controlled by -D on the compilation command. */ @@ -35,12 +39,49 @@ whose inclusion is controlled by -D on the compilation command. */ #define type_misc 3 #define type_callout 4 #define type_ratelimit 5 +#define type_tls 6 +#define type_seen 7 /* This is used by our cut-down dbfn_open(). */ uschar *spool_directory; +BOOL utc = FALSE; + + +/******************************************************************************/ + /* dummies needed by Solaris build */ +void +millisleep(int msec) +{} +uschar * +readconf_printtime(int t) +{ return NULL; } +gstring * +string_vformat_trc(gstring * g, const uschar * func, unsigned line, + unsigned size_limit, unsigned flags, const char *format, va_list ap) +{ return NULL; } +uschar * +string_sprintf_trc(const char * fmt, const uschar * func, unsigned line, ...) +{ return NULL; } +BOOL +string_format_trc(uschar * buf, int len, const uschar * func, unsigned line, + const char * fmt, ...) +{ return FALSE; } + +struct global_flags f; +unsigned int log_selector[1]; +uschar * queue_name; +BOOL split_spool_directory; + + +/* These introduced by the taintwarn handling */ +#ifdef ALLOW_INSECURE_TAINTED_DATA +BOOL allow_insecure_tainted_data; +#endif + +/******************************************************************************/ /************************************************* @@ -77,7 +118,6 @@ SIGNAL_BOOL sigalrm_seen; void sigalrm_handler(int sig) { -sig = sig; /* Keep picky compilers happy */ sigalrm_seen = 1; } @@ -91,8 +131,8 @@ static void usage(uschar *name, uschar *options) { printf("Usage: exim_%s%s \n", name, options); -printf(" = retry | misc | wait- | callout | ratelimit\n"); -exit(1); +printf(" = retry | misc | wait- | callout | ratelimit | tls | seen\n"); +exit(EXIT_FAILURE); } @@ -107,19 +147,38 @@ second of them to be sure it is a known database name. */ static int check_args(int argc, uschar **argv, uschar *name, uschar *options) { -if (argc == 3) +uschar * aname = argv[optind + 1]; +if (argc - optind == 2) { - if (Ustrcmp(argv[2], "retry") == 0) return type_retry; - if (Ustrcmp(argv[2], "misc") == 0) return type_misc; - if (Ustrncmp(argv[2], "wait-", 5) == 0) return type_wait; - if (Ustrcmp(argv[2], "callout") == 0) return type_callout; - if (Ustrcmp(argv[2], "ratelimit") == 0) return type_ratelimit; + if (Ustrcmp(aname, "retry") == 0) return type_retry; + if (Ustrcmp(aname, "misc") == 0) return type_misc; + if (Ustrncmp(aname, "wait-", 5) == 0) return type_wait; + if (Ustrcmp(aname, "callout") == 0) return type_callout; + if (Ustrcmp(aname, "ratelimit") == 0) return type_ratelimit; + if (Ustrcmp(aname, "tls") == 0) return type_tls; + if (Ustrcmp(aname, "seen") == 0) return type_seen; } usage(name, options); return -1; /* Never obeyed */ } +static void +options(int argc, uschar * argv[], uschar * name) +{ +int opt; + +opterr = 0; +while ((opt = getopt(argc, (char * const *)argv, "z")) != -1) + switch (opt) + { + case 'z': utc = TRUE; break; + default: usage(name, US" [-z]"); + } +} + + + /************************************************* * Handle attempts to write the log * @@ -147,8 +206,6 @@ va_start(ap, format); vfprintf(stderr, format, ap); fprintf(stderr, "\n"); va_end(ap); -selector = selector; /* Keep picky compilers happy */ -flags = flags; } @@ -162,7 +219,7 @@ static uschar time_buffer[sizeof("09-xxx-1999 hh:mm:ss ")]; uschar * print_time(time_t t) { -struct tm *tmstr = localtime(&t); +struct tm *tmstr = utc ? gmtime(&t) : localtime(&t); Ustrftime(time_buffer, sizeof(time_buffer), "%d-%b-%Y %H:%M:%S", tmstr); return time_buffer; } @@ -176,8 +233,8 @@ return time_buffer; uschar * print_cache(int value) { -return (value == ccache_accept)? US"accept" : - (value == ccache_reject)? US"reject" : +return value == ccache_accept ? US"accept" : + value == ccache_reject ? US"reject" : US"unknown"; } @@ -240,6 +297,7 @@ Arguments: flags O_RDONLY or O_RDWR dbblock Points to an open_db block to be filled in. lof Unused. + panic Unused Returns: NULL if the open failed, or the locking failed. On success, dbblock is returned. This contains the dbm pointer and @@ -247,7 +305,7 @@ Returns: NULL if the open failed, or the locking failed. */ 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; struct flock lock_data; @@ -306,7 +364,7 @@ if (asprintf(CSS &filename, "%s/%s", dirname, name) < 0) return NULL; #else filename = string_sprintf("%s/%s", dirname, name); #endif -EXIM_DBOPEN(filename, dirname, flags, 0, &(dbblock->dbptr)); +EXIM_DBOPEN(filename, dirname, flags, 0, &dbblock->dbptr); if (!dbblock->dbptr) { @@ -360,7 +418,7 @@ pick out the timestamps, etc., do the copying centrally here. Arguments: dbblock a pointer to an open database block key the key of the record to be read - length where to put the length (or NULL if length not wanted) + length where to put the length (or NULL if length not wanted). Includes overhead. Returns: a pointer to the retrieved record, or NULL if the record is not found @@ -372,7 +430,7 @@ 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); @@ -383,9 +441,12 @@ 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 for now that anything stored could have been tainted. Properly +we should store the taint status along 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); +if (length) *length = EXIM_DATUM_SIZE(result_datum); EXIM_DATUM_FREE(result_datum); /* Some DBM libs require freeing */ return yield; @@ -416,7 +477,7 @@ 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); @@ -448,7 +509,7 @@ 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)); memcpy(key_copy, key, klen); EXIM_DATUM key_datum; @@ -484,7 +545,6 @@ 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 */ /* Some dbm require an initialization */ @@ -521,11 +581,16 @@ EXIM_CURSOR *cursor; uschar **argv = USS cargv; uschar keybuffer[1024]; +store_init(); +options(argc, argv, US"dumpdb"); + /* Check the arguments, and open the database */ -dbdata_type = check_args(argc, argv, US"dumpdb", US""); -spool_directory = argv[1]; -if (!(dbm = dbfn_open(argv[2], O_RDONLY, &dbblock, FALSE))) +dbdata_type = check_args(argc, argv, US"dumpdb", US" [-z]"); +argc -= optind; argv += optind; +spool_directory = argv[0]; + +if (!(dbm = dbfn_open(argv[1], O_RDONLY, &dbblock, FALSE, TRUE))) exit(1); /* Scan the file, formatting the information for each entry. Note @@ -541,11 +606,14 @@ for (uschar * key = dbfn_scan(dbm, TRUE, &cursor); dbdata_callout_cache *callout; dbdata_ratelimit *ratelimit; dbdata_ratelimit_unique *rate_unique; + dbdata_tls_session *session; + dbdata_seen *seen; int count_bad = 0; int length; uschar *t; uschar name[MESSAGE_ID_LENGTH + 1]; void *value; + rmark reset_point = store_mark(); /* Keep a copy of the key separate, as in some DBM's the pointer is into data which might change. */ @@ -584,6 +652,7 @@ for (uschar * key = dbfn_scan(dbm, TRUE, &cursor); t = wait->text; name[MESSAGE_ID_LENGTH] = 0; + /* Leave corrupt records alone */ if (wait->count > WAIT_NAME_MAX) { fprintf(stderr, @@ -673,9 +742,19 @@ for (uschar * key = dbfn_scan(dbm, TRUE, &cursor); keybuffer); } break; + + case type_tls: + session = (dbdata_tls_session *)value; + printf(" %s %.*s\n", keybuffer, length, session->session); + break; + + case type_seen: + seen = (dbdata_seen *)value; + printf("%s\t%s\n", keybuffer, print_time(seen->time_stamp)); + break; } - store_reset(value); } + store_reset(reset_point); } dbfn_close(dbm); @@ -719,23 +798,31 @@ If the record name is omitted from (2) or (3), the previously used record name is re-used. */ -int main(int argc, char **cargv) +int +main(int argc, char **cargv) { int dbdata_type; uschar **argv = USS cargv; uschar buffer[256]; uschar name[256]; -void *reset_point = store_get(0); +rmark reset_point; +uschar * aname; +store_init(); +options(argc, argv, US"fixdb"); name[0] = 0; /* No name set */ /* Sort out the database type, verify what we are working on and then process user requests */ -dbdata_type = check_args(argc, argv, US"fixdb", US""); -printf("Modifying Exim hints database %s/db/%s\n", argv[1], argv[2]); +dbdata_type = check_args(argc, argv, US"fixdb", US" [-z]"); +argc -= optind; argv += optind; +spool_directory = argv[0]; +aname = argv[1]; -for(;;) +printf("Modifying Exim hints database %s/db/%s\n", spool_directory, aname); + +for(; (reset_point = store_mark()); store_reset(reset_point)) { open_db dbblock; open_db *dbm; @@ -745,12 +832,11 @@ for(;;) dbdata_callout_cache *callout; dbdata_ratelimit *ratelimit; dbdata_ratelimit_unique *rate_unique; + dbdata_tls_session *session; int oldlength; uschar *t; uschar field[256], value[256]; - store_reset(reset_point); - printf("> "); if (Ufgets(buffer, 256, stdin) == NULL) break; @@ -781,9 +867,8 @@ for(;;) if (field[0] != 0) { int verify = 1; - spool_directory = argv[1]; - if (!(dbm = dbfn_open(argv[2], O_RDWR, &dbblock, FALSE))) + if (!(dbm = dbfn_open(aname, O_RDWR, &dbblock, FALSE, TRUE))) continue; if (Ustrcmp(field, "d") == 0) @@ -925,6 +1010,10 @@ for(;;) break; } break; + + case type_tls: + printf("Can't change contents of tls database record\n"); + break; } dbfn_write(dbm, name, record, oldlength); @@ -948,8 +1037,7 @@ for(;;) /* Handle a read request, or verify after an update. */ - spool_directory = argv[1]; - if (!(dbm = dbfn_open(argv[2], O_RDONLY, &dbblock, FALSE))) + if (!(dbm = dbfn_open(aname, O_RDONLY, &dbblock, FALSE, TRUE))) continue; if (!(record = dbfn_read_with_length(dbm, name, &oldlength))) @@ -1036,6 +1124,12 @@ for(;;) printf("5 add element to filter\n"); } break; + + case type_tls: + session = (dbdata_tls_session *)value; + printf("0 time stamp: %s\n", print_time(session->time_stamp)); + printf("1 session: .%s\n", session->session); + break; } } @@ -1073,13 +1167,14 @@ typedef struct key_item { } key_item; -int main(int argc, char **cargv) +int +main(int argc, char **cargv) { struct stat statbuf; int maxkeep = 30 * 24 * 60 * 60; int dbdata_type, i, oldest, path_len; key_item *keychain = NULL; -void *reset_point; +rmark reset_point; open_db dbblock; open_db *dbm; EXIM_CURSOR *cursor; @@ -1087,6 +1182,8 @@ uschar **argv = USS cargv; uschar buffer[256]; uschar *key; +store_init(); + /* Scan the options */ for (i = 1; i < argc; i++) @@ -1134,7 +1231,7 @@ oldest = time(NULL) - maxkeep; printf("Tidying Exim hints database %s/db/%s\n", argv[1], argv[2]); spool_directory = argv[1]; -if (!(dbm = dbfn_open(argv[2], O_RDWR, &dbblock, FALSE))) +if (!(dbm = dbfn_open(argv[2], O_RDWR, &dbblock, FALSE, TRUE))) exit(1); /* Prepare for building file names */ @@ -1152,7 +1249,7 @@ for (key = dbfn_scan(dbm, TRUE, &cursor); key; key = dbfn_scan(dbm, FALSE, &cursor)) { - key_item *k = store_get(sizeof(key_item) + Ustrlen(key)); + key_item *k = store_get(sizeof(key_item) + Ustrlen(key), is_tainted(key)); k->next = keychain; keychain = k; Ustrcpy(k->key, key); @@ -1161,13 +1258,10 @@ for (key = dbfn_scan(dbm, TRUE, &cursor); /* Now scan the collected keys and operate on the records, resetting the store each time round. */ -reset_point = store_get(0); - -while (keychain) +for (; keychain && (reset_point = store_mark()); store_reset(reset_point)) { dbdata_generic *value; - store_reset(reset_point); key = keychain->key; keychain = keychain->next; value = dbfn_read_with_length(dbm, key, NULL); @@ -1175,7 +1269,7 @@ while (keychain) /* A continuation record may have been deleted or renamed already, so non-existence is not serious. */ - if (value == NULL) continue; + if (!value) continue; /* Delete if too old */ @@ -1196,12 +1290,33 @@ while (keychain) /* Leave corrupt records alone */ + if (wait->time_stamp > time(NULL)) + { + printf("**** Data for '%s' corrupted\n time in future: %s\n", + key, print_time(((dbdata_generic *)value)->time_stamp)); + continue; + } if (wait->count > WAIT_NAME_MAX) { - printf("**** Data for %s corrupted\n count=%d=0x%x max=%d\n", + printf("**** Data for '%s' corrupted\n count=%d=0x%x max=%d\n", key, wait->count, wait->count, WAIT_NAME_MAX); continue; } + if (wait->sequence > WAIT_CONT_MAX) + { + printf("**** Data for '%s' corrupted\n sequence=%d=0x%x max=%d\n", + key, wait->sequence, wait->sequence, WAIT_CONT_MAX); + continue; + } + + /* Record over 1 year old; just remove it */ + + if (wait->time_stamp < time(NULL) - 365*24*60*60) + { + dbfn_delete(dbm, key); + printf("deleted %s (too old)\n", key); + continue; + } /* Loop for renamed continuation records. For each message id, check to see if the message exists, and if not, remove its entry