From: Jeremy Harris Date: Fri, 3 Apr 2020 13:38:31 +0000 (+0100) Subject: dsearch: filter-matches option X-Git-Url: https://git.exim.org/users/heiko/exim.git/commitdiff_plain/4aaeaddeaa130a227a694d32b7214689e982a39e dsearch: filter-matches option --- diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index 295835dbb..9a7f9113e 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -6780,22 +6780,31 @@ The key may not contain any forward slash characters. If &[lstat()]& succeeds then so does the lookup. .new +.cindex "tainted data" "dsearch result" +The result is regarded as untainted. + Options for the lookup can be given by appending them after the word "dsearch", -separated by a comma. Options, if present, are a comma-separated list with +separated by a comma. Options, if present, are a comma-separated list having each element starting with a tag name and an equals. -The only option currently supported requests an alternate output value of +Two options are supported, for the return value and for filtering match +candidates. +The "ret" option requests an alternate result value of the entire path for the entry. Example: .code ${lookup {passwd} dsearch,ret=full {/etc}} .endd The default result is just the requested entry. - -The matching entry may be a file, directory, -symbolic link, or any other kind of directory entry. -.cindex "tainted data" "dsearch result" -The result is regarded as untainted. +The "filter" option requests that only directory entries of a given type +are matched. The match value is one of "file", "dir" or "subdir" (the latter +not matching "." or ".."). Example: +.code +${lookup {passwd} dsearch,filter=file {/etc}} +.endd +The default matching is for any entry type, including directories +and symlinks. .wen + An example of how this lookup can be used to support virtual domains is given in section &<>&. @@ -8100,8 +8109,8 @@ daemon as in the other SQL databases. .oindex &%sqlite_dbfile%& The preferred way of specifying the file is by using the &%sqlite_dbfile%& option, set to -.wen an absolute path. +.wen 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. @@ -8110,6 +8119,7 @@ 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. +.new Here is a lookup expansion example: .code sqlite_dbfile = /some/thing/sqlitedb @@ -8121,6 +8131,7 @@ In a list, the syntax is similar. For example: domainlist relay_to_domains = sqlite;\ select * from relays where ip='$sender_host_address'; .endd +.wen The only character affected by the &%quote_sqlite%& operator is a single quote, which it doubles. diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff index 9a06feab7..1573f3485 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -48,6 +48,8 @@ Version 4.94 in the lookup, as the filename becomes tainted. The new method keeps the filename separate. 12. An option on the dsearch lookup, to return the full path. +12. Options on the dsearch lookup, to return the full path and to filter + filetypes for matching. diff --git a/src/src/lookups/dsearch.c b/src/src/lookups/dsearch.c index 0509a761b..9bb76cc25 100644 --- a/src/src/lookups/dsearch.c +++ b/src/src/lookups/dsearch.c @@ -65,6 +65,11 @@ return FALSE; *************************************************/ #define RET_FULL BIT(0) +#define FILTER_TYPE BIT(1) +#define FILTER_ALL BIT(1) +#define FILTER_FILE BIT(2) +#define FILTER_DIR BIT(3) +#define FILTER_SUBDIR BIT(4) /* See local README for interface description. We use lstat() instead of scanning the directory, as it is hopefully faster to let the OS do the scanning @@ -99,10 +104,29 @@ if (opts) while ((ele = string_nextinlist(&opts, &sep, NULL, 0))) if (Ustrcmp(ele, "ret=full") == 0) flags |= RET_FULL; + else if (Ustrncmp(ele, "filter=", 7) == 0) + { + ele += 7; + if (Ustrcmp(ele, "file") == 0) + flags |= FILTER_TYPE | FILTER_FILE; + else if (Ustrcmp(ele, "dir") == 0) + flags |= FILTER_TYPE | FILTER_DIR; + else if (Ustrcmp(ele, "subdir") == 0) + flags |= FILTER_TYPE | FILTER_SUBDIR; /* like dir but not "." or ".." */ + } } filename = string_sprintf("%s/%s", dirname, keystring); -if (Ulstat(filename, &statbuf) >= 0) +if ( Ulstat(filename, &statbuf) >= 0 + && ( !(flags & FILTER_TYPE) + || (flags & FILTER_FILE && S_ISREG(statbuf.st_mode)) + || ( flags & (FILTER_DIR | FILTER_SUBDIR) + && S_ISDIR(statbuf.st_mode) + && ( flags & FILTER_DIR + || keystring[0] != '.' + || keystring[1] != '.' + || keystring[1] && keystring[2] + ) ) ) ) { /* Since the filename exists in the filesystem, we can return a non-tainted result. */ diff --git a/test/aux-fixed/2500.dir/regfile b/test/aux-fixed/2500.dir/regfile new file mode 100644 index 000000000..e69de29bb diff --git a/test/scripts/2500-dsearch/2500 b/test/scripts/2500-dsearch/2500 index 14cf31b26..588690373 100644 --- a/test/scripts/2500-dsearch/2500 +++ b/test/scripts/2500-dsearch/2500 @@ -7,7 +7,14 @@ fail: ${lookup{TESTNUM.tst} dsearch{DIR/dir_not_here}{$value}{FAIL}} fail(case): ${lookup{TESTNUM.TST} dsearch{DIR/aux-fixed}{$value}{FAIL}} fail(case): ${lookup{TESTNUM.TST} dsearch{DIR/AUX-fixed}{$value}{FAIL}} fail(path): ${lookup{TESTNUM.tst} dsearch{.}{$value}{OTHER}} -ok,full: ${lookup{TESTNUM.tst} dsearch,ret=full {DIR/aux-fixed}{$value}{FAIL}} +ok,full: ${lookup{TESTNUM.tst} dsearch,ret=full {DIR/aux-fixed}{$value}{FAIL}} +ok,file: ${lookup{TESTNUM.tst} dsearch,filter=file {DIR/aux-fixed}{$value}{FAIL}} +fail,file: ${lookup{TESTNUM.dir} dsearch,filter=file {DIR/aux-fixed}{$value}{FAIL}} +ok,dir: ${lookup{TESTNUM.dir} dsearch,filter=dir {DIR/aux-fixed}{$value}{FAIL}} +fail,dir: ${lookup{TESTNUM.tst} dsearch,filter=dir {DIR/aux-fixed}{$value}{FAIL}} +ok,subdir: ${lookup{TESTNUM.dir} dsearch,filter=subdir {DIR/aux-fixed}{$value}{FAIL}} +fail,subdir:${lookup{..} dsearch,filter=subdir {DIR/aux-fixed}{$value}{FAIL}} +fail,subdir:${lookup{TESTNUM.tst} dsearch,filter=subdir {DIR/aux-fixed}{$value}{FAIL}} **** # 1 diff --git a/test/stderr/2200 b/test/stderr/2200 index 8efc38f64..c8320321a 100644 --- a/test/stderr/2200 +++ b/test/stderr/2200 @@ -43,7 +43,7 @@ search_tidyup called LRU list: internal_search_find: file="NULL" type=dnsdb key="a=shorthost.test.ex" opts=NULL - cached data found but past valid time; database lookup required for a=shorthost.test.ex + cached data found but either wrong opts or dated; database lookup required for a=shorthost.test.ex dnsdb key: shorthost.test.ex lookup yielded: 127.0.0.1 LOG: MAIN diff --git a/test/stderr/2201 b/test/stderr/2201 index 9764069d8..fb618fc64 100644 --- a/test/stderr/2201 +++ b/test/stderr/2201 @@ -176,7 +176,7 @@ search_find: file="NULL" LRU list: internal_search_find: file="NULL" type=dnsdb key="a=shorthost.test.ex" opts=NULL -cached data found but past valid time; database lookup required for a=shorthost.test.ex +cached data found but either wrong opts or dated; database lookup required for a=shorthost.test.ex dnsdb key: shorthost.test.ex lookup yielded: 127.0.0.1 LOG: MAIN diff --git a/test/stderr/2600 b/test/stderr/2600 index 3c4a59271..627e47755 100644 --- a/test/stderr/2600 +++ b/test/stderr/2600 @@ -495,10 +495,10 @@ 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 + key="select name from them where id='userx';" partial=-1 affix=NULL starflags=0 opts=NULL LRU list: internal_search_find: file="NULL" - type=sqlite key="select name from them where id='userx';" + type=sqlite key="select name from them where id='userx';" opts=NULL database lookup required for select name from them where id='userx'; lookup yielded: Ayen Other search_tidyup called diff --git a/test/stdout/2500 b/test/stdout/2500 index d10a5f8a7..ef5b2a18c 100644 --- a/test/stdout/2500 +++ b/test/stdout/2500 @@ -5,4 +5,11 @@ > Failed: failed to open TESTSUITE/AUX-fixed for directory search: No such file or directory > Failed: dirname '.' for dsearch is not absolute > ok,full: TESTSUITE/aux-fixed/2500.tst +> ok,file: 2500.tst +> fail,file: FAIL +> ok,dir: 2500.dir +> fail,dir: FAIL +> ok,subdir: 2500.dir +> fail,subdir:FAIL +> fail,subdir:FAIL >