From 298849d8ea217fd104d167f5233bd11240b3ddae Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Wed, 1 Apr 2020 23:29:40 +0100 Subject: [PATCH] Sqlite: new main option sqlite_dbfile --- doc/doc-docbook/spec.xfpt | 41 +++++++++++++++++++++++++++-------- doc/doc-txt/NewStuff | 5 +++++ doc/doc-txt/OptionLists.txt | 1 + src/src/expand.c | 17 ++++++--------- src/src/globals.c | 1 + src/src/globals.h | 1 + src/src/lookups/sqlite.c | 7 ++++-- src/src/readconf.c | 1 + test/confs/2600 | 3 +++ test/scripts/2600-SQLite/2600 | 5 +++++ test/stderr/2600 | 34 ++++++++++++++++++++--------- test/stdout/2600 | 2 ++ 12 files changed, 87 insertions(+), 31 deletions(-) diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index b9d73ad3f..bfe59fccc 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -7029,7 +7029,11 @@ passed to a Redis database. See section &<>&. .next .cindex "sqlite lookup type" .cindex "lookup" "sqlite" -&(sqlite)&: The format of the query is a filename followed by an SQL statement +&(sqlite)&: The format of the query is +new +an optional filename +.wen +followed by an SQL statement that is passed to an SQLite database. See section &<>&. .next @@ -7936,6 +7940,11 @@ If any MySQL, PostgreSQL, Oracle, InterBase or Redis lookups are used, the or &%redis_servers%& option (as appropriate) must be set to a colon-separated list of server information. +.oindex &%mysql_servers%& +.oindex &%pgsql_servers%& +.oindex &%oracle_servers%& +.oindex &%ibase_servers%& +.oindex &%redis_servers%& (For MySQL and PostgreSQL, the global option need not be set if all queries contain their own server information &-- see section &<>&.) @@ -8073,17 +8082,31 @@ affected. .cindex "sqlite lookup type" SQLite is different to the other SQL lookups because a filename is required in addition to the SQL query. An SQLite database is a single file, and there is no -daemon as in the other SQL databases. The interface to Exim requires the name -of the file, as an absolute path, to be given at the start of the query. It is -separated from the query by white space. This means that the path name cannot -contain white space. Here is a lookup expansion example: -.code -${lookup sqlite {/some/thing/sqlitedb \ - select name from aliases where id='userx';}} +daemon as in the other SQL databases. + +.new +.oindex &%sqlite_dbfile%& +The preferred way of specifying the file is by using the +&%sqlite_dbfile%& option, set to +.wen +an absolute path. +A deprecated method is available, prefixing the query with the filename +separated by white space. +This means that the path name cannot contain white space. +.cindex "tainted data" "sqlite file" +It also means that the query cannot use any tainted values, as that taints +the entire query including the filename - resulting in a refusal to open +the file. + +Here is a lookup expansion example: +.code +sqlite_dbfile = /some/thing/sqlitedb +... +${lookup sqlite {select name from aliases where id='userx';}} .endd In a list, the syntax is similar. For example: .code -domainlist relay_to_domains = sqlite;/some/thing/sqlitedb \ +domainlist relay_to_domains = sqlite;\ select * from relays where ip='$sender_host_address'; .endd The only character affected by the &%quote_sqlite%& operator is a single diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff index fb6e444d3..62763e2ac 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -43,6 +43,11 @@ Version 4.94 11. New variables $local_part_{pre,suf}fix_v. +12. New main option "sqlite_dbfile", for use in preference to prefixing the + lookup string. The older method fails when tainted variables are used + in the lookup, as the filename becomes tainted. The new method keeps the + filename separate. + Version 4.93 diff --git a/doc/doc-txt/OptionLists.txt b/doc/doc-txt/OptionLists.txt index 717f87e57..bb5a32091 100644 --- a/doc/doc-txt/OptionLists.txt +++ b/doc/doc-txt/OptionLists.txt @@ -555,6 +555,7 @@ spamd_address string* + main split_spool_directory boolean false main 1.70 spool_directory string ++ main spool_wireformat boolean false main 4.90 +sqlite_dbfile string* unset main 4.94 with LOOKUP_SQLITE sqlite_lock_timeout time 5s main 4.53 strict_acl_vars boolean false main 4.64 srv_fail_domains domain list unset dnslookup 4.43 diff --git a/src/src/expand.c b/src/src/expand.c index f937ac337..2366eb882 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -4892,16 +4892,13 @@ while (*s != 0) if (mac_islookup(stype, lookup_querystyle)) filename = NULL; else - { - if (*filename != '/') - { - expand_string_message = string_sprintf( - "absolute file name expected for \"%s\" lookup", name); - goto EXPAND_FAILED; - } - while (*key != 0 && !isspace(*key)) key++; - if (*key != 0) *key++ = 0; - } + if (*filename == '/') + { + while (*key && !isspace(*key)) key++; + if (*key) *key++ = '\0'; + } + else + filename = NULL; } /* If skipping, don't do the next bit - just lookup_value == NULL, as if diff --git a/src/src/globals.c b/src/src/globals.c index 57f857e5a..b8bf7ced2 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -89,6 +89,7 @@ uschar *redis_servers = NULL; #endif #ifdef LOOKUP_SQLITE +uschar *sqlite_dbfile = NULL; int sqlite_lock_timeout = 5; #endif diff --git a/src/src/globals.h b/src/src/globals.h index c9222743e..882f289d7 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -68,6 +68,7 @@ extern uschar *redis_servers; /* List of servers and connect info */ #endif #ifdef LOOKUP_SQLITE +extern uschar *sqlite_dbfile; /* Filname for database */ extern int sqlite_lock_timeout; /* Internal lock waiting timeout */ #endif diff --git a/src/src/lookups/sqlite.c b/src/src/lookups/sqlite.c index cf68b121a..51d28c2ca 100644 --- a/src/src/lookups/sqlite.c +++ b/src/src/lookups/sqlite.c @@ -23,10 +23,13 @@ sqlite_open(const uschar * filename, uschar ** errmsg) sqlite3 *db = NULL; int ret; -if ((ret = sqlite3_open(CCS filename, &db)) != 0) +if (!filename || !*filename) filename = sqlite_dbfile; +if (*filename != '/') + *errmsg = US"absolute file name expected for \"sqlite\" lookup"; +else if ((ret = sqlite3_open(CCS filename, &db)) != 0) { *errmsg = (void *)sqlite3_errmsg(db); - debug_printf_indent("Error opening database: %s\n", *errmsg); + DEBUG(D_lookup) debug_printf_indent("Error opening database: %s\n", *errmsg); } sqlite3_busy_timeout(db, 1000 * sqlite_lock_timeout); diff --git a/src/src/readconf.c b/src/src/readconf.c index 1243e9082..2bf1e63d6 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -329,6 +329,7 @@ static optionlist optionlist_config[] = { { "spool_directory", opt_stringptr, {&spool_directory} }, { "spool_wireformat", opt_bool, {&spool_wireformat} }, #ifdef LOOKUP_SQLITE + { "sqlite_dbfile", opt_stringptr, {&sqlite_dbfile} }, { "sqlite_lock_timeout", opt_int, {&sqlite_lock_timeout} }, #endif #ifdef EXPERIMENTAL_SRS diff --git a/test/confs/2600 b/test/confs/2600 index 566e260f2..e42cbe33c 100644 --- a/test/confs/2600 +++ b/test/confs/2600 @@ -14,6 +14,9 @@ hostlist relay_hosts = sqlite;DIR/aux-fixed/sqlitedb \ acl_smtp_rcpt = check_recipient sqlite_lock_timeout = 2 +.ifdef DATA +sqlite_dbfile = DATA +.endif # ----- ACL ----- diff --git a/test/scripts/2600-SQLite/2600 b/test/scripts/2600-SQLite/2600 index f388b1ac1..2a557a46a 100644 --- a/test/scripts/2600-SQLite/2600 +++ b/test/scripts/2600-SQLite/2600 @@ -30,3 +30,8 @@ Test message exim -d-all+lookup -be ${lookup sqlite{DIR/aux-fixed/sqlitedb select name from them where id='userx';}} **** +# +# +exim -DDATA=DIR/aux-fixed/sqlitedb -d-all+lookup -be +${lookup sqlite{select name from them where id='userx';}} +**** diff --git a/test/stderr/2600 b/test/stderr/2600 index 15e9bb540..69e23f848 100644 --- a/test/stderr/2600 +++ b/test/stderr/2600 @@ -137,12 +137,12 @@ log directory space = nnnnnK inodes = nnnnn check_space = 10240K inodes = 100 SMTP>> 250 OK SMTP<< rcpt to: using ACL "check_recipient" -processing "accept" (TESTSUITE/test-config 23) +processing "accept" (TESTSUITE/test-config 26) check domains = +local_domains d in "@"? no (end of list) d in "+local_domains"? no (end of list) accept: condition test failed in ACL "check_recipient" -processing "accept" (TESTSUITE/test-config 24) +processing "accept" (TESTSUITE/test-config 27) check hosts = +relay_hosts search_open: sqlite "TESTSUITE/aux-fixed/sqlitedb" search_find: file="TESTSUITE/aux-fixed/sqlitedb" @@ -157,7 +157,7 @@ lookup failed host in "sqlite;TESTSUITE/aux-fixed/sqlitedb select * from them where id='10.0.0.0'"? no (end of list) host in "+relay_hosts"? no (end of list) accept: condition test failed in ACL "check_recipient" -processing "deny" (TESTSUITE/test-config 25) +processing "deny" (TESTSUITE/test-config 28) message: relay not permitted deny: condition test succeeded in ACL "check_recipient" end of ACL "check_recipient": DENY @@ -166,12 +166,12 @@ LOG: MAIN REJECT H=[10.0.0.0] F= rejected RCPT : relay not permitted SMTP<< rcpt to: using ACL "check_recipient" -processing "accept" (TESTSUITE/test-config 23) +processing "accept" (TESTSUITE/test-config 26) check domains = +local_domains d in "@"? no (end of list) d in "+local_domains"? no (end of list) accept: condition test failed in ACL "check_recipient" -processing "accept" (TESTSUITE/test-config 24) +processing "accept" (TESTSUITE/test-config 27) check hosts = +relay_hosts search_open: sqlite "TESTSUITE/aux-fixed/sqlitedb" cached open @@ -187,7 +187,7 @@ lookup failed host in "sqlite;TESTSUITE/aux-fixed/sqlitedb select * from them where id='10.0.0.0'"? no (end of list) host in "+relay_hosts"? no (end of list) accept: condition test failed in ACL "check_recipient" -processing "deny" (TESTSUITE/test-config 25) +processing "deny" (TESTSUITE/test-config 28) message: relay not permitted deny: condition test succeeded in ACL "check_recipient" end of ACL "check_recipient": DENY @@ -232,12 +232,12 @@ log directory space = nnnnnK inodes = nnnnn check_space = 10240K inodes = 100 SMTP>> 250 OK SMTP<< rcpt to: using ACL "check_recipient" -processing "accept" (TESTSUITE/test-config 23) +processing "accept" (TESTSUITE/test-config 26) check domains = +local_domains d in "@"? no (end of list) d in "+local_domains"? no (end of list) accept: condition test failed in ACL "check_recipient" -processing "accept" (TESTSUITE/test-config 24) +processing "accept" (TESTSUITE/test-config 27) check hosts = +relay_hosts search_open: sqlite "TESTSUITE/aux-fixed/sqlitedb" search_find: file="TESTSUITE/aux-fixed/sqlitedb" @@ -256,12 +256,12 @@ SMTP>> 250 Accepted DSN: orcpt: NULL flags: 0 SMTP<< rcpt to: using ACL "check_recipient" -processing "accept" (TESTSUITE/test-config 23) +processing "accept" (TESTSUITE/test-config 26) check domains = +local_domains d in "@"? no (end of list) d in "+local_domains"? no (end of list) accept: condition test failed in ACL "check_recipient" -processing "accept" (TESTSUITE/test-config 24) +processing "accept" (TESTSUITE/test-config 27) check hosts = +relay_hosts search_open: sqlite "TESTSUITE/aux-fixed/sqlitedb" cached open @@ -489,3 +489,17 @@ dropping to exim gid; retaining priv uid lookup yielded: Ayen Other search_tidyup called >>>>>>>>>>>>>>>> Exim pid=pppp (fresh-exec) terminating with rc=0 >>>>>>>>>>>>>>>> +Exim version x.yz .... +configuration file is TESTSUITE/test-config +admin user +dropping to exim gid; retaining priv uid + search_open: sqlite "NULL" + search_find: file="NULL" + key="select name from them where id='userx';" partial=-1 affix=NULL starflags=0 + LRU list: + internal_search_find: file="NULL" + type=sqlite key="select name from them where id='userx';" + database lookup required for select name from them where id='userx'; + lookup yielded: Ayen Other +search_tidyup called +>>>>>>>>>>>>>>>> Exim pid=pppp (fresh-exec) terminating with rc=0 >>>>>>>>>>>>>>>> diff --git a/test/stdout/2600 b/test/stdout/2600 index 2b11b92df..53d6b9b23 100644 --- a/test/stdout/2600 +++ b/test/stdout/2600 @@ -32,3 +32,5 @@ after" id=newline 221 myhost.test.ex closing connection > Ayen Other > +> Ayen Other +> -- 2.30.2