Lookups: sub-path for dsearch
authorJeremy Harris <jgh146exb@wizmail.org>
Sun, 26 May 2024 16:17:50 +0000 (17:17 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Sun, 26 May 2024 17:30:45 +0000 (18:30 +0100)
doc/doc-docbook/spec.xfpt
doc/doc-txt/NewStuff
src/src/lookups/dsearch.c
test/scripts/2500-dsearch/2500
test/stdout/2500

index 8f7abac8946eaa8a37afe313553cbe6ea22ea346..6ee6151455ed27f1989035c71dd17c0b47596343 100644 (file)
@@ -6846,7 +6846,10 @@ by default, but has an option to omit them (see section &<<SECTdbmbuild>>&).
 .cindex "dsearch lookup type"
 The given file must be an absolute directory path; this is searched for an entry
 whose name is the key by calling the &[lstat()]& function.
-The key may not contain any forward slash characters.
+.new
+Unless the options (below) permit a path,
+.wen
+the key may not contain any forward slash characters.
 If &[lstat()]& succeeds then so does the lookup.
 .cindex "tainted data" "dsearch result"
 The result is regarded as untainted.
@@ -6855,7 +6858,7 @@ 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 having
 each element starting with a tag name and an equals.
 
-Two options are supported, for the return value and for filtering match
+Three 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:
@@ -6863,6 +6866,7 @@ the entire path for the entry. Example:
 ${lookup {passwd} dsearch,ret=full {/etc}}
 .endd
 The default result is just the requested entry.
+
 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:
@@ -6872,6 +6876,14 @@ ${lookup {passwd} dsearch,filter=file {/etc}}
 The default matching is for any entry type, including directories
 and symlinks.
 
+The "key" option relaxes the restriction that only a simple path component can
+be searched for, to permit a sequence of path components. Example:
+.code
+${lookup {foo/bar} dsearch,key=path {/etc}}
+.endd
+If this option is used, a ".." component in the key is specifically disallowed.
+The default operation is that the key may only be a single path component.
+
 An example of how this
 lookup can be used to support virtual domains is given in section
 &<<SECTvirtualdomains>>&.
index 1781e05b17080eaa831862ecaa1aa1ca32a4fcde..b0702eea2ec9e652c59176fc8f23c113ebd5b5d2 100644 (file)
@@ -22,6 +22,8 @@ Version 4.98
 
  6. A dns:fail event.
 
+ 7. The dsearch lookup supports search for a sub-path.
+
 Version 4.97
 ------------
 
index 74439bfc874aaf86945822145ce188058563fbdc..22003f4e5dd449abf29f2a43f51c687712e60899 100644 (file)
@@ -70,6 +70,7 @@ return FALSE;
 #define FILTER_FILE    BIT(2)
 #define FILTER_DIR     BIT(3)
 #define FILTER_SUBDIR  BIT(4)
+#define ALLOW_PATH     BIT(5)
 
 /* 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
@@ -85,13 +86,6 @@ int save_errno;
 uschar * filename;
 unsigned flags = 0;
 
-if (Ustrchr(keystring, '/') != 0)
-  {
-  *errmsg = string_sprintf("key for dsearch lookup contains a slash: %s",
-    keystring);
-  return DEFER;
-  }
-
 if (opts)
   {
   int sep = ',';
@@ -110,6 +104,24 @@ if (opts)
       else if (Ustrcmp(ele, "subdir") == 0)
        flags |= FILTER_TYPE | FILTER_SUBDIR;   /* like dir but not "." or ".." */
       }
+    else if (Ustrcmp(ele, "key=path") == 0)
+      flags |= ALLOW_PATH;
+  }
+
+if (flags & ALLOW_PATH)
+  {
+  if (Ustrstr(keystring, "/../") != NULL || Ustrstr(keystring, "/./"))
+    {
+    *errmsg = string_sprintf(
+      "key for dsearch lookup contains bad component: %s", keystring);
+    return DEFER;
+    }
+  }
+else if (Ustrchr(keystring, '/') != NULL)
+  {
+  *errmsg = string_sprintf("key for dsearch lookup contains a slash: %s",
+    keystring);
+  return DEFER;
   }
 
 filename = string_sprintf("%s/%s", dirname, keystring);
@@ -189,3 +201,5 @@ static lookup_info *_lookup_list[] = { &_lookup_info };
 lookup_module_info dsearch_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 1 };
 
 /* End of lookups/dsearch.c */
+/* vi: aw ai sw=2
+*/
index f3937ec9127f064de1ff08b3f7247e409c0df7d5..1690256dae31bef482a31136973b4551cfe149cc 100644 (file)
@@ -18,6 +18,8 @@ ok,subdir:  ${lookup{TESTNUM.dir} dsearch,filter=subdir {DIR/aux-fixed}{$value}{
 fail,subdir(..):${lookup{..}          dsearch,filter=subdir {DIR/aux-fixed}{$value}{FAIL}}
 fail,subdir(.) :${lookup{.}           dsearch,filter=subdir {DIR/aux-fixed}{$value}{FAIL}}
 fail,subdir(f) :${lookup{TESTNUM.tst} dsearch,filter=subdir {DIR/aux-fixed}{$value}{FAIL}}
+fail.path:  ${lookup{TESTNUM.dir/regfile} dsearch          {DIR/aux-fixed}{$value}{FAIL}}
+ok.path:    ${lookup{TESTNUM.dir/regfile} dsearch,key=path {DIR/aux-fixed}{$value}{FAIL}}
 
 cachelayer tests
 fail:       ${lookup{test-data}   dsearch               {DIR/}          {$value}{FAIL}}
index 6daaab658e1f626c6908832a372250d73f9957be..065004bf27860cc179fd3ba9c0c14e738123ac61 100644 (file)
@@ -15,6 +15,8 @@
 > fail,subdir(..):FAIL
 > fail,subdir(.) :FAIL
 > fail,subdir(f) :FAIL
+> Failed: lookup of "2500.dir/regfile" gave DEFER: key for dsearch lookup contains a slash: 2500.dir/regfile
+> ok.path:    2500.dir/regfile
 > 
 > cachelayer tests
 > fail:       FAIL