ldap lookups build as dynamic module
authorJeremy Harris <jgh146exb@wizmail.org>
Sun, 18 Aug 2024 17:46:44 +0000 (18:46 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Sun, 18 Aug 2024 19:17:45 +0000 (20:17 +0100)
doc/doc-txt/NewStuff
src/scripts/lookups-Makefile
src/src/EDITME
src/src/drtables.c
src/src/exim.c
src/src/expand.c
src/src/functions.h
src/src/lookups/ldap.c
src/src/lookups/ldap.h [deleted file]
src/src/search.c

index a5a8bd5f32c55c9f33a4189cc69be47b517e8eab..9a34f8ac2a8304ef7dc5640fae7d1f2266beec2f 100644 (file)
@@ -14,8 +14,9 @@ Version 4.98
 
  3. Events smtp:fail:protocol and smtp:fail:syntax
 
 
  3. Events smtp:fail:protocol and smtp:fail:syntax
 
- 4. JSON lookup support, all the router and authenticator drivers, and all the
-    transport drivers except smtp, can now be built as loadable modules
+ 4. JSON and LDAP lookup support, all the router and authenticator drivers,
+    and all the transport drivers except smtp, can now be built as loadable
+    modules
 
 Version 4.98
 ------------
 
 Version 4.98
 ------------
index 8dcac585b086ebfd2ca73055aaa4efba2e4f18dc..40cca603f52886eb9ce13b7264f881d6dab1d41a 100755 (executable)
@@ -163,17 +163,12 @@ exec > "$target"
 sed -n "1,/$tag_marker/p" < "$input"
 
 for name_mod in \
 sed -n "1,/$tag_marker/p" < "$input"
 
 for name_mod in \
-    CDB DBM:dbmdb DNSDB DSEARCH IBASE JSON LMDB LSEARCH MYSQL NIS NISPLUS ORACLE \
-    PASSWD PGSQL REDIS SQLITE TESTDB WHOSON
+    CDB DBM:dbmdb DNSDB DSEARCH IBASE JSON LMDB LDAP LSEARCH MYSQL NIS NISPLUS \
+    ORACLE PASSWD PGSQL REDIS SQLITE TESTDB WHOSON
 do
   emit_module_rule $name_mod
 done
 
 do
   emit_module_rule $name_mod
 done
 
-if want_at_all LDAP
-then
-  OBJ="${OBJ} ldap.o"
-fi
-
 # Because the variable is EXPERIMENTAL_SPF and not LOOKUP_SPF we
 # always include spf.o and compile a dummy if EXPERIMENTAL_SPF is not
 # defined.
 # Because the variable is EXPERIMENTAL_SPF and not LOOKUP_SPF we
 # always include spf.o and compile a dummy if EXPERIMENTAL_SPF is not
 # defined.
index 3353a42394edc2679054f7dcd6e4b410685e51df..aeba7704abf715efd8bb8bffde95f7e2a4e3c14d 100644 (file)
@@ -416,10 +416,8 @@ TRANSPORT_SMTP=yes
 # the dynamic library and not the exim binary will be linked against the
 # library.
 #
 # the dynamic library and not the exim binary will be linked against the
 # library.
 #
-# NOTE: LDAP cannot be built as a module!
-# JSON cannot (yet).
-# Also, PASSWD, DBM and DNSDB can but there is little point since the accesses
-# are always needed by the Exim core.
+# PASSWD, DBM and DNSDB can be build as modules but there is little point since
+# the accesses are always needed by the Exim core.
 #
 # For Redis you need to have hiredis installed on your system
 # (https://github.com/redis/hiredis).
 #
 # For Redis you need to have hiredis installed on your system
 # (https://github.com/redis/hiredis).
@@ -479,6 +477,7 @@ LOOKUP_DNSDB=yes
 # If you don't set any of these, Exim assumes the original University of
 # Michigan (OpenLDAP 1) library.
 
 # If you don't set any of these, Exim assumes the original University of
 # Michigan (OpenLDAP 1) library.
 
+# For building as a modules, set LOOKUP_LDAP_INCLUDE and LOOKUP_LDAP_LIBS
 
 #------------------------------------------------------------------------------
 # The PCRE2 library is required for Exim.  There is no longer an embedded
 
 #------------------------------------------------------------------------------
 # The PCRE2 library is required for Exim.  There is no longer an embedded
@@ -538,6 +537,7 @@ SUPPORT_DANE=yes
 #
 # LOOKUP_INCLUDE += -I/usr/local/include
 # LOOKUP_LIBS += -llmdb
 #
 # LOOKUP_INCLUDE += -I/usr/local/include
 # LOOKUP_LIBS += -llmdb
+# For dynamic-modules builds, use instead LOOKUP_LMDB_INCLUDE & LOOKUP_LMDB_LIBS
 
 
 #------------------------------------------------------------------------------
 
 
 #------------------------------------------------------------------------------
index c490e7f86809b7c28cb4026ebe64a0a89a59de29..1d8e222e2d2e8efe9d6f6fda555cf408a56fe3c4 100644 (file)
@@ -295,7 +295,7 @@ extern lookup_module_info ibase_lookup_module_info;
 #if defined(LOOKUP_JSON) && LOOKUP_JSON!=2
 extern lookup_module_info json_lookup_module_info;
 #endif
 #if defined(LOOKUP_JSON) && LOOKUP_JSON!=2
 extern lookup_module_info json_lookup_module_info;
 #endif
-#if defined(LOOKUP_LDAP)
+#if defined(LOOKUP_LDAP) && LOOKUP_LDAP!=2
 extern lookup_module_info ldap_lookup_module_info;
 #endif
 #if defined(LOOKUP_LSEARCH) && LOOKUP_LSEARCH!=2
 extern lookup_module_info ldap_lookup_module_info;
 #endif
 #if defined(LOOKUP_LSEARCH) && LOOKUP_LSEARCH!=2
@@ -322,7 +322,7 @@ extern lookup_module_info pgsql_lookup_module_info;
 #if defined(LOOKUP_REDIS) && LOOKUP_REDIS!=2
 extern lookup_module_info redis_lookup_module_info;
 #endif
 #if defined(LOOKUP_REDIS) && LOOKUP_REDIS!=2
 extern lookup_module_info redis_lookup_module_info;
 #endif
-#if defined(LOOKUP_LMDB)
+#if defined(LOOKUP_LMDB) && LOOKUP_LMDB!=2
 extern lookup_module_info lmdb_lookup_module_info;
 #endif
 #if defined(SUPPORT_SPF)
 extern lookup_module_info lmdb_lookup_module_info;
 #endif
 #if defined(SUPPORT_SPF)
@@ -378,7 +378,7 @@ addlookupmodule(NULL, &dsearch_lookup_module_info);
 addlookupmodule(NULL, &ibase_lookup_module_info);
 #endif
 
 addlookupmodule(NULL, &ibase_lookup_module_info);
 #endif
 
-#ifdef LOOKUP_LDAP
+#if defined(LOOKUP_LDAP) && LOOKUP_LDAP!=2
 addlookupmodule(NULL, &ldap_lookup_module_info);
 #endif
 
 addlookupmodule(NULL, &ldap_lookup_module_info);
 #endif
 
@@ -418,7 +418,7 @@ addlookupmodule(NULL, &pgsql_lookup_module_info);
 addlookupmodule(NULL, &redis_lookup_module_info);
 #endif
 
 addlookupmodule(NULL, &redis_lookup_module_info);
 #endif
 
-#ifdef LOOKUP_LMDB
+#if defined(LOOKUP_LMDB) && LOOKUP_LMDB!=2
 addlookupmodule(NULL, &lmdb_lookup_module_info);
 #endif
 
 addlookupmodule(NULL, &lmdb_lookup_module_info);
 #endif
 
index 66746e6bb9f2982bae50c6b3eebfdd561f273660..3d0929592b3ca8f46b012f390ef6e0fce62a9da3 100644 (file)
@@ -1043,56 +1043,56 @@ lookup_show_supported(gstring * g)
 {
 gstring * b = NULL, * d = NULL;
 
 {
 gstring * b = NULL, * d = NULL;
 
-#if defined(LOOKUP_LSEARCH)
+#ifdef LOOKUP_LSEARCH
 # if LOOKUP_LSEARCH!=2
   b = string_cat(b, US" lsearch wildlsearch nwildlsearch iplsearch");
 # else
   d = string_cat(d, US" lsearch wildlsearch nwildlsearch iplsearch");
 # endif
 #endif
 # if LOOKUP_LSEARCH!=2
   b = string_cat(b, US" lsearch wildlsearch nwildlsearch iplsearch");
 # else
   d = string_cat(d, US" lsearch wildlsearch nwildlsearch iplsearch");
 # endif
 #endif
-#if defined(LOOKUP_CDB)
+#ifdef LOOKUP_CDB
 # if LOOKUP_CDB!=2
   b = string_cat(b, US" cdb");
 # else
   d = string_cat(d, US" cdb");
 # endif
 #endif
 # if LOOKUP_CDB!=2
   b = string_cat(b, US" cdb");
 # else
   d = string_cat(d, US" cdb");
 # endif
 #endif
-#if defined(LOOKUP_DBM)
+#ifdef LOOKUP_DBM
 # if LOOKUP_DBM!=2
   b = string_cat(b, US" dbm dbmjz dbmnz");
 # else
   d = string_cat(d, US" dbm dbmjz dbmnz");
 # endif
 #endif
 # if LOOKUP_DBM!=2
   b = string_cat(b, US" dbm dbmjz dbmnz");
 # else
   d = string_cat(d, US" dbm dbmjz dbmnz");
 # endif
 #endif
-#if defined(LOOKUP_DNSDB)
+#ifdef LOOKUP_DNSDB
 # if LOOKUP_DNSDB!=2
   b = string_cat(b, US" dnsdb");
 # else
   d = string_cat(d, US" dnsdb");
 # endif
 #endif
 # if LOOKUP_DNSDB!=2
   b = string_cat(b, US" dnsdb");
 # else
   d = string_cat(d, US" dnsdb");
 # endif
 #endif
-#if defined(LOOKUP_DSEARCH)
+#ifdef LOOKUP_DSEARCH
 # if LOOKUP_DSEARCH!=2
   b = string_cat(b, US" dsearch");
 # else
   d = string_cat(d, US" dsearch");
 # endif
 #endif
 # if LOOKUP_DSEARCH!=2
   b = string_cat(b, US" dsearch");
 # else
   d = string_cat(d, US" dsearch");
 # endif
 #endif
-#if defined(LOOKUP_IBASE)
+#ifdef LOOKUP_IBASE
 # if LOOKUP_IBASE!=2
   b = string_cat(b, US" ibase");
 # else
   d = string_cat(d, US" ibase");
 # endif
 #endif
 # if LOOKUP_IBASE!=2
   b = string_cat(b, US" ibase");
 # else
   d = string_cat(d, US" ibase");
 # endif
 #endif
-#if defined(LOOKUP_JSON)
+#ifdef LOOKUP_JSON
 # if LOOKUP_JSON!=2
   b = string_cat(b, US" json");
 # else
   d = string_cat(d, US" json");
 # endif
 #endif
 # if LOOKUP_JSON!=2
   b = string_cat(b, US" json");
 # else
   d = string_cat(d, US" json");
 # endif
 #endif
-#if defined(LOOKUP_LDAP)
+#ifdef LOOKUP_LDAP
 # if LOOKUP_LDAP!=2
   b = string_cat(b, US" ldap ldapdn ldapm");
 # else
 # if LOOKUP_LDAP!=2
   b = string_cat(b, US" ldap ldapdn ldapm");
 # else
@@ -1100,72 +1100,76 @@ gstring * b = NULL, * d = NULL;
 # endif
 #endif
 #ifdef LOOKUP_LMDB
 # endif
 #endif
 #ifdef LOOKUP_LMDB
+# if LOOKUP_LMDB!=2
   b = string_cat(b, US" lmdb");
   b = string_cat(b, US" lmdb");
+# else
+  d = string_cat(d, US" lmdb");
+# endif
 #endif
 #endif
-#if defined(LOOKUP_MYSQL)
+#ifdef LOOKUP_MYSQL
 # if LOOKUP_MYSQL!=2
   b = string_cat(b, US" mysql");
 # else
   d = string_cat(d, US" mysql");
 # endif
 #endif
 # if LOOKUP_MYSQL!=2
   b = string_cat(b, US" mysql");
 # else
   d = string_cat(d, US" mysql");
 # endif
 #endif
-#if defined(LOOKUP_NIS)
+#ifdef LOOKUP_NIS
 # if LOOKUP_NIS!=2
   b = string_cat(b, US" nis nis0");
 # else
   d = string_cat(d, US" nis nis0");
 # endif
 #endif
 # if LOOKUP_NIS!=2
   b = string_cat(b, US" nis nis0");
 # else
   d = string_cat(d, US" nis nis0");
 # endif
 #endif
-#if defined(LOOKUP_NISPLUS)
+#ifdef LOOKUP_NISPLUS
 # if LOOKUP_NISPLUS!=2
   b = string_cat(b, US" nisplus");
 # else
   d = string_cat(d, US" nisplus");
 # endif
 #endif
 # if LOOKUP_NISPLUS!=2
   b = string_cat(b, US" nisplus");
 # else
   d = string_cat(d, US" nisplus");
 # endif
 #endif
-#if defined(LOOKUP_ORACLE)
+#ifdef LOOKUP_ORACLE
 # if LOOKUP_ORACLE!=2
   b = string_cat(b, US" oracle");
 # else
   d = string_cat(d, US" oracle");
 # endif
 #endif
 # if LOOKUP_ORACLE!=2
   b = string_cat(b, US" oracle");
 # else
   d = string_cat(d, US" oracle");
 # endif
 #endif
-#if defined(LOOKUP_PASSWD)
+#ifdef LOOKUP_PASSWD
 # if LOOKUP_PASSWD!=2
   b = string_cat(b, US" passwd");
 # else
   d = string_cat(d, US" passwd");
 # endif
 #endif
 # if LOOKUP_PASSWD!=2
   b = string_cat(b, US" passwd");
 # else
   d = string_cat(d, US" passwd");
 # endif
 #endif
-#if defined(LOOKUP_PGSQL)
+#ifdef LOOKUP_PGSQL
 # if LOOKUP_PGSQL!=2
   b = string_cat(b, US" pgsql");
 # else
   d = string_cat(d, US" pgsql");
 # endif
 #endif
 # if LOOKUP_PGSQL!=2
   b = string_cat(b, US" pgsql");
 # else
   d = string_cat(d, US" pgsql");
 # endif
 #endif
-#if defined(LOOKUP_REDIS)
+#ifdef LOOKUP_REDIS
 # if LOOKUP_REDIS!=2
   b = string_cat(b, US" redis");
 # else
   d = string_cat(d, US" redis");
 # endif
 #endif
 # if LOOKUP_REDIS!=2
   b = string_cat(b, US" redis");
 # else
   d = string_cat(d, US" redis");
 # endif
 #endif
-#if defined(LOOKUP_SQLITE)
+#ifdef LOOKUP_SQLITE
 # if LOOKUP_SQLITE!=2
   b = string_cat(b, US" sqlite");
 # else
   d = string_cat(d, US" sqlite");
 # endif
 #endif
 # if LOOKUP_SQLITE!=2
   b = string_cat(b, US" sqlite");
 # else
   d = string_cat(d, US" sqlite");
 # endif
 #endif
-#if defined(LOOKUP_TESTDB)
+#ifdef LOOKUP_TESTDB
 # if LOOKUP_TESTDB!=2
   b = string_cat(b, US" testdb");
 # else
   d = string_cat(d, US" testdb");
 # endif
 #endif
 # if LOOKUP_TESTDB!=2
   b = string_cat(b, US" testdb");
 # else
   d = string_cat(d, US" testdb");
 # endif
 #endif
-#if defined(LOOKUP_WHOSON)
+#ifdef LOOKUP_WHOSON
 # if LOOKUP_WHOSON!=2
   b = string_cat(b, US" whoson");
 # else
 # if LOOKUP_WHOSON!=2
   b = string_cat(b, US" whoson");
 # else
index 2ed802fd4f8374a3a0604612fb10cb465a81440d..d7b55831f61bad2b83f038a3beae30c667233857 100644 (file)
@@ -30,10 +30,6 @@ typedef unsigned esi_flags;
 # endif
 #endif /*!STAND_ALONE*/
 
 # endif
 #endif /*!STAND_ALONE*/
 
-#ifdef LOOKUP_LDAP
-# include "lookups/ldap.h"
-#endif
-
 #ifdef SUPPORT_CRYPTEQ
 # ifdef CRYPT_H
 #  include <crypt.h>
 #ifdef SUPPORT_CRYPTEQ
 # ifdef CRYPT_H
 #  include <crypt.h>
@@ -2808,13 +2804,14 @@ switch(cond_type = identify_operator(&s, &opname))
     case ECOND_LDAPAUTH:
     #ifdef LOOKUP_LDAP
       {
     case ECOND_LDAPAUTH:
     #ifdef LOOKUP_LDAP
       {
-      /* Just to keep the interface the same */
-      BOOL do_cache;
-      int old_pool = store_pool;
-      store_pool = POOL_SEARCH;
-      rc = eldapauth_find((void *)(-1), NULL, sub[0], Ustrlen(sub[0]), NULL,
-        &expand_string_message, &do_cache);
-      store_pool = old_pool;
+      int stype = search_findtype(US"ldapauth", 8), expand_setup = -1;
+      void * handle = search_open(NULL, stype, 0, NULL, NULL);
+      if (handle)
+       rc= search_find(handle, NULL, sub[0],
+                       -1, NULL, 0, 0, &expand_setup, NULL)
+         ? OK : f.search_find_defer ? DEFER : FAIL;
+      else
+       { expand_string_message = search_error_message; rc = FAIL; }
       }
     goto END_AUTH;
     #else
       }
     goto END_AUTH;
     #else
index 3253043ae29d1a8d287d42e875d926f96a26f627..9ae51d05b6578823135e2e968aa799c38ba3704a 100644 (file)
@@ -491,7 +491,7 @@ extern void    route_tidyup(void);
 extern uschar *router_current_name(void);
 
 extern uschar *search_args(int, uschar *, uschar *, uschar **, const uschar *);
 extern uschar *router_current_name(void);
 
 extern uschar *search_args(int, uschar *, uschar *, uschar **, const uschar *);
-extern uschar *search_find(void *, const uschar *, uschar *, int,
+extern uschar *search_find(void *, const uschar *, const uschar *, int,
                 const uschar *, int, int, int *, const uschar *);
 extern int     search_findtype(const uschar *, int);
 extern int     search_findtype_partial(const uschar *, int *, const uschar **, int *,
                 const uschar *, int, int, int *, const uschar *);
 extern int     search_findtype(const uschar *, int);
 extern int     search_findtype_partial(const uschar *, int *, const uschar **, int *,
index 0c29b6c9a737a791e3decb230ba05d5a01b31396..8a142398e4ed98c4ed10110eeedfe23cec0e374e 100644 (file)
@@ -101,7 +101,10 @@ and eldapm_find(), with a difference in the "search_type" argument.
 
 The case of eldapauth_find() is special in that all it does is do
 authentication, returning OK or FAIL as appropriate. This isn't used as a
 
 The case of eldapauth_find() is special in that all it does is do
 authentication, returning OK or FAIL as appropriate. This isn't used as a
-lookup. Instead, it is called from expand.c as an expansion condition test.
+lookup. Instead, it is called via the generic search interface from expand.c
+as an expansion condition test.  We take a non/NULL return string as OK/FAIL.
+We do not advertise or document it as a general search method,
+but probably could.
 
 The DN from a successful lookup is placed in $ldap_dn. This feature postdates
 the provision of the SEARCH_LDAP_DN facility for returning just the DN as the
 
 The DN from a successful lookup is placed in $ldap_dn. This feature postdates
 the provision of the SEARCH_LDAP_DN facility for returning just the DN as the
@@ -1305,7 +1308,8 @@ return(control_ldap_search(ldap_url, SEARCH_LDAP_DN, result, errmsg));
 
 int
 eldapauth_find(void * handle, const uschar * filename, const uschar * ldap_url,
 
 int
 eldapauth_find(void * handle, const uschar * filename, const uschar * ldap_url,
-  int length, uschar ** result, uschar ** errmsg, uint * do_cache)
+  int length, uschar ** result, uschar ** errmsg, uint * do_cache,
+  const uschar * opts)
 {
 return(control_ldap_search(ldap_url, SEARCH_LDAP_AUTH, result, errmsg));
 }
 {
 return(control_ldap_search(ldap_url, SEARCH_LDAP_AUTH, result, errmsg));
 }
@@ -1559,6 +1563,7 @@ gstring *
 ldap_version_report(gstring * g)
 {
 #ifdef DYNLOOKUP
 ldap_version_report(gstring * g)
 {
 #ifdef DYNLOOKUP
+/*XXX it would be nice to haul a version string for the underlying ldap library */
 g = string_fmt_append(g, "Library version: LDAP: Exim version %s\n", EXIM_VERSION_STR);
 #endif
 return g;
 g = string_fmt_append(g, "Library version: LDAP: Exim version %s\n", EXIM_VERSION_STR);
 #endif
 return g;
@@ -1601,11 +1606,28 @@ static lookup_info ldapm_lookup_info = {
   .version_report = NULL                           /* no version reporting (redundant) */
 };
 
   .version_report = NULL                           /* no version reporting (redundant) */
 };
 
+static lookup_info ldapauth_lookup_info = {
+  .name = US"ldapauth",                        /* lookup name */
+  .type = lookup_querystyle,           /* query-style lookup */
+  .open = eldap_open,                  /* sic */    /* open function */
+  .check = NULL,                       /* check function */
+  .find = eldapauth_find,              /* find function */
+  .close = NULL,                       /* no close function */
+  .tidy = eldap_tidy,                  /* sic */    /* tidy function */
+  .quote = eldap_quote,                        /* sic */    /* quoting function */
+  .version_report = NULL                           /* no version reporting (redundant) */
+};
+
 #ifdef DYNLOOKUP
 #define ldap_lookup_module_info _lookup_module_info
 #endif
 
 #ifdef DYNLOOKUP
 #define ldap_lookup_module_info _lookup_module_info
 #endif
 
-static lookup_info *_lookup_list[] = { &ldap_lookup_info, &ldapdn_lookup_info, &ldapm_lookup_info };
-lookup_module_info ldap_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 3 };
+static lookup_info *_lookup_list[] = {
+  &ldap_lookup_info,
+  &ldapdn_lookup_info,
+  &ldapm_lookup_info,
+  &ldapauth_lookup_info,
+  };
+lookup_module_info ldap_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 4 };
 
 /* End of lookups/ldap.c */
 
 /* End of lookups/ldap.c */
diff --git a/src/src/lookups/ldap.h b/src/src/lookups/ldap.h
deleted file mode 100644 (file)
index 2ce62fc..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-/*************************************************
-*     Exim - an Internet mail transport agent    *
-*************************************************/
-
-/* Copyright (c) University of Cambridge 1995 - 2015 */
-/* See the file NOTICE for conditions of use and distribution. */
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-/* Header for eldapauth_find */
-
-extern int     eldapauth_find(void *, uschar *, const uschar *, int, uschar **,
-                 uschar **, BOOL *);
-
-/* End of lookups/ldap.h */
index 6c28b390eb424d5331d5d2101cc01da9431cefb2..1c501455eb2842f3c6caeadfc1613faeb4bec4ee 100644 (file)
@@ -520,8 +520,8 @@ Returns:       a pointer to a dynamic string containing the answer,
 */
 
 static uschar *
 */
 
 static uschar *
-internal_search_find(void * handle, const uschar * filename, uschar * keystring,
-  BOOL cache_rd, const uschar * opts)
+internal_search_find(void * handle, const uschar * filename,
+  const uschar * keystring, BOOL cache_rd, const uschar * opts)
 {
 tree_node * t = (tree_node *)handle;
 search_cache * c = (search_cache *)(t->data.ptr);
 {
 tree_node * t = (tree_node *)handle;
 search_cache * c = (search_cache *)(t->data.ptr);
@@ -729,7 +729,7 @@ Returns:         a pointer to a dynamic string containing the answer,
 */
 
 uschar *
 */
 
 uschar *
-search_find(void * handle, const uschar * filename, uschar * keystring,
+search_find(void * handle, const uschar * filename, const uschar * keystring,
   int partial, const uschar * affix, int affixlen, int starflags,
   int * expand_setup, const uschar * opts)
 {
   int partial, const uschar * affix, int affixlen, int starflags,
   int * expand_setup, const uschar * opts)
 {
@@ -830,14 +830,16 @@ else if (partial >= 0)
 
   /* Try with the affix on the front, except for a zero-length affix */
 
 
   /* Try with the affix on the front, except for a zero-length affix */
 
-  if (affixlen == 0) keystring2 = keystring; else
+  if (affixlen == 0)
+    keystring2 = string_copy(keystring);
+  else
     {
     keystring2 = store_get(len + affixlen + 1,
          is_tainted(keystring) || is_tainted(affix) ? GET_TAINTED : GET_UNTAINTED);
     Ustrncpy(keystring2, affix, affixlen);
     Ustrcpy(keystring2 + affixlen, keystring);
     DEBUG(D_lookup) debug_printf_indent("trying partial match %s\n", keystring2);
     {
     keystring2 = store_get(len + affixlen + 1,
          is_tainted(keystring) || is_tainted(affix) ? GET_TAINTED : GET_UNTAINTED);
     Ustrncpy(keystring2, affix, affixlen);
     Ustrcpy(keystring2 + affixlen, keystring);
     DEBUG(D_lookup) debug_printf_indent("trying partial match %s\n", keystring2);
-    yield = internal_search_find(handle, filename, keystring2, cache_rd, opts);
+    yield = internal_search_find(handle, filename, CUS keystring2, cache_rd, opts);
     if (f.search_find_defer) return NULL;
     }
 
     if (f.search_find_defer) return NULL;
     }
 
@@ -875,7 +877,7 @@ else if (partial >= 0)
         }
 
       DEBUG(D_lookup) debug_printf_indent("trying partial match %s\n", keystring3);
         }
 
       DEBUG(D_lookup) debug_printf_indent("trying partial match %s\n", keystring3);
-      yield = internal_search_find(handle, filename, keystring3,
+      yield = internal_search_find(handle, filename, CUS keystring3,
                cache_rd, opts);
       if (f.search_find_defer) return NULL;
       if (yield)
                cache_rd, opts);
       if (f.search_find_defer) return NULL;
       if (yield)