You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

305 lines
10 KiB

  1. Description: Allow one to use and switch between different local_scan functions
  2. without recompiling exim.
  3. http://marc.merlins.org/linux/exim/files/sa-exim-current/ Original patch from
  4. David Woodhouse, modified first by Derrick 'dman' Hudson and then by Marc
  5. MERLIN for SA-Exim and minor/major API version tracking
  6. Author: David Woodhouse, Derrick 'dman' Hudson, Marc MERLIN
  7. Origin: other, http://marc.merlins.org/linux/exim/files/sa-exim-current/
  8. Forwarded: https://bugs.exim.org/show_bug.cgi?id=2671
  9. Last-Update: 2021-07-28
  10. --- a/src/EDITME
  11. +++ b/src/EDITME
  12. @@ -883,6 +883,21 @@ HEADERS_CHARSET="ISO-8859-1"
  13. #------------------------------------------------------------------------------
  14. +# On systems which support dynamic loading of shared libraries, Exim can
  15. +# load a local_scan function specified in its config file instead of having
  16. +# to be recompiled with the desired local_scan function. For a full
  17. +# description of the API to this function, see the Exim specification.
  18. +
  19. +DLOPEN_LOCAL_SCAN=yes
  20. +
  21. +# If you set DLOPEN_LOCAL_SCAN, then you need to include -rdynamic in the
  22. +# linker flags. Without it, the loaded .so won't be able to access any
  23. +# functions from exim.
  24. +
  25. +LDFLAGS += -rdynamic
  26. +CFLAGS += -fvisibility=hidden
  27. +
  28. +#------------------------------------------------------------------------------
  29. # The default distribution of Exim contains only the plain text form of the
  30. # documentation. Other forms are available separately. If you want to install
  31. # the documentation in "info" format, first fetch the Texinfo documentation
  32. --- a/src/config.h.defaults
  33. +++ b/src/config.h.defaults
  34. @@ -35,6 +35,8 @@ Do not put spaces between # and the 'def
  35. #define AUTH_VARS 4
  36. +#define DLOPEN_LOCAL_SCAN
  37. +
  38. #define BIN_DIRECTORY
  39. #define CONFIGURE_FILE
  40. --- a/src/globals.c
  41. +++ b/src/globals.c
  42. @@ -121,6 +121,10 @@ int dsn_ret = 0;
  43. const pcre *regex_DSN = NULL;
  44. uschar *dsn_advertise_hosts = NULL;
  45. +#ifdef DLOPEN_LOCAL_SCAN
  46. +uschar *local_scan_path = NULL;
  47. +#endif
  48. +
  49. #ifndef DISABLE_TLS
  50. BOOL gnutls_compat_mode = FALSE;
  51. BOOL gnutls_allow_auto_pkcs11 = FALSE;
  52. --- a/src/globals.h
  53. +++ b/src/globals.h
  54. @@ -156,6 +156,9 @@ extern int dsn_ret; /
  55. extern const pcre *regex_DSN; /* For recognizing DSN settings */
  56. extern uschar *dsn_advertise_hosts; /* host for which TLS is advertised */
  57. +#ifdef DLOPEN_LOCAL_SCAN
  58. +extern uschar *local_scan_path; /* Path to local_scan() library */
  59. +#endif
  60. /* Input-reading functions for messages, so we can use special ones for
  61. incoming TCP/IP. */
  62. --- a/src/local_scan.c
  63. +++ b/src/local_scan.c
  64. @@ -6,58 +6,133 @@
  65. /* See the file NOTICE for conditions of use and distribution. */
  66. -/******************************************************************************
  67. -This file contains a template local_scan() function that just returns ACCEPT.
  68. -If you want to implement your own version, you should copy this file to, say
  69. -Local/local_scan.c, and edit the copy. To use your version instead of the
  70. -default, you must set
  71. -
  72. -HAVE_LOCAL_SCAN=yes
  73. -LOCAL_SCAN_SOURCE=Local/local_scan.c
  74. -
  75. -in your Local/Makefile. This makes it easy to copy your version for use with
  76. -subsequent Exim releases.
  77. -
  78. -For a full description of the API to this function, see the Exim specification.
  79. -******************************************************************************/
  80. -
  81. -
  82. /* This is the only Exim header that you should include. The effect of
  83. including any other Exim header is not defined, and may change from release to
  84. release. Use only the documented interface! */
  85. #include "local_scan.h"
  86. -
  87. -/* This is a "do-nothing" version of a local_scan() function. The arguments
  88. -are:
  89. -
  90. - fd The file descriptor of the open -D file, which contains the
  91. - body of the message. The file is open for reading and
  92. - writing, but modifying it is dangerous and not recommended.
  93. -
  94. - return_text A pointer to an unsigned char* variable which you can set in
  95. - order to return a text string. It is initialized to NULL.
  96. -
  97. -The return values of this function are:
  98. -
  99. - LOCAL_SCAN_ACCEPT
  100. - The message is to be accepted. The return_text argument is
  101. - saved in $local_scan_data.
  102. -
  103. - LOCAL_SCAN_REJECT
  104. - The message is to be rejected. The returned text is used
  105. - in the rejection message.
  106. -
  107. - LOCAL_SCAN_TEMPREJECT
  108. - This specifies a temporary rejection. The returned text
  109. - is used in the rejection message.
  110. -*/
  111. +#ifdef DLOPEN_LOCAL_SCAN
  112. +#include <dlfcn.h>
  113. +static int (*local_scan_fn)(int fd, uschar **return_text) = NULL;
  114. +static int load_local_scan_library(void);
  115. +#endif
  116. int
  117. local_scan(int fd, uschar **return_text)
  118. {
  119. -return LOCAL_SCAN_ACCEPT;
  120. +
  121. +#ifdef DLOPEN_LOCAL_SCAN
  122. +/* local_scan_path is defined AND not the empty string */
  123. +if (local_scan_path && *local_scan_path)
  124. + {
  125. + if (!local_scan_fn)
  126. + {
  127. + if (!load_local_scan_library())
  128. + {
  129. + char *base_msg , *error_msg , *final_msg ;
  130. + int final_length = -1 ;
  131. +
  132. + base_msg=US"Local configuration error - local_scan() library failure\n";
  133. + error_msg = dlerror() ;
  134. +
  135. + final_length = strlen(base_msg) + strlen(error_msg) + 1 ;
  136. + final_msg = (char*)malloc( final_length*sizeof(char) ) ;
  137. + *final_msg = '\0' ;
  138. +
  139. + strcat( final_msg , base_msg ) ;
  140. + strcat( final_msg , error_msg ) ;
  141. +
  142. + *return_text = final_msg ;
  143. + return LOCAL_SCAN_TEMPREJECT;
  144. + }
  145. + }
  146. + return local_scan_fn(fd, return_text);
  147. + }
  148. +else
  149. +#endif
  150. + return LOCAL_SCAN_ACCEPT;
  151. +}
  152. +
  153. +#ifdef DLOPEN_LOCAL_SCAN
  154. +
  155. +static int load_local_scan_library(void)
  156. +{
  157. +/* No point in keeping local_scan_lib since we'll never dlclose() anyway */
  158. +void *local_scan_lib = NULL;
  159. +int (*local_scan_version_fn)(void);
  160. +int vers_maj;
  161. +int vers_min;
  162. +
  163. +local_scan_lib = dlopen(local_scan_path, RTLD_NOW);
  164. +if (!local_scan_lib)
  165. + {
  166. + log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() library open failed - "
  167. + "message temporarily rejected");
  168. + return FALSE;
  169. + }
  170. +
  171. +local_scan_version_fn = dlsym(local_scan_lib, "local_scan_version_major");
  172. +if (!local_scan_version_fn)
  173. + {
  174. + dlclose(local_scan_lib);
  175. + log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() library doesn't contain "
  176. + "local_scan_version_major() function - message temporarily rejected");
  177. + return FALSE;
  178. + }
  179. +
  180. +/* The major number is increased when the ABI is changed in a non
  181. + backward compatible way. */
  182. +vers_maj = local_scan_version_fn();
  183. +
  184. +local_scan_version_fn = dlsym(local_scan_lib, "local_scan_version_minor");
  185. +if (!local_scan_version_fn)
  186. + {
  187. + dlclose(local_scan_lib);
  188. + log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() library doesn't contain "
  189. + "local_scan_version_minor() function - message temporarily rejected");
  190. + return FALSE;
  191. + }
  192. +
  193. +/* The minor number is increased each time a new feature is added (in a
  194. + way that doesn't break backward compatibility) -- Marc */
  195. +vers_min = local_scan_version_fn();
  196. +
  197. +
  198. +if (vers_maj != LOCAL_SCAN_ABI_VERSION_MAJOR)
  199. + {
  200. + dlclose(local_scan_lib);
  201. + local_scan_lib = NULL;
  202. + log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() has an incompatible major"
  203. + "version number, you need to recompile your module for this version"
  204. + "of exim (The module was compiled for version %d.%d and this exim provides"
  205. + "ABI version %d.%d)", vers_maj, vers_min, LOCAL_SCAN_ABI_VERSION_MAJOR,
  206. + LOCAL_SCAN_ABI_VERSION_MINOR);
  207. + return FALSE;
  208. + }
  209. +else if (vers_min > LOCAL_SCAN_ABI_VERSION_MINOR)
  210. + {
  211. + dlclose(local_scan_lib);
  212. + local_scan_lib = NULL;
  213. + log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() has an incompatible minor"
  214. + "version number, you need to recompile your module for this version"
  215. + "of exim (The module was compiled for version %d.%d and this exim provides"
  216. + "ABI version %d.%d)", vers_maj, vers_min, LOCAL_SCAN_ABI_VERSION_MAJOR,
  217. + LOCAL_SCAN_ABI_VERSION_MINOR);
  218. + return FALSE;
  219. + }
  220. +
  221. +local_scan_fn = dlsym(local_scan_lib, "local_scan");
  222. +if (!local_scan_fn)
  223. + {
  224. + dlclose(local_scan_lib);
  225. + log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() library doesn't contain "
  226. + "local_scan() function - message temporarily rejected");
  227. + return FALSE;
  228. + }
  229. +return TRUE;
  230. }
  231. +#endif /* DLOPEN_LOCAL_SCAN */
  232. +
  233. /* End of local_scan.c */
  234. --- a/src/local_scan.h
  235. +++ b/src/local_scan.h
  236. @@ -27,6 +27,7 @@ settings, and the store functions. */
  237. #include <stdarg.h>
  238. #include <sys/types.h>
  239. +#pragma GCC visibility push(default)
  240. #include "config.h"
  241. #include "mytypes.h"
  242. #include "store.h"
  243. @@ -166,6 +167,9 @@ extern header_line *header_list; /
  244. extern BOOL host_checking; /* Set when checking a host */
  245. extern uschar *interface_address; /* Interface for incoming call */
  246. extern int interface_port; /* Port number for incoming call */
  247. +#ifdef DLOPEN_LOCAL_SCAN
  248. +extern uschar *local_scan_path;
  249. +#endif
  250. extern uschar *message_id; /* Internal id of message being handled */
  251. extern uschar *received_protocol; /* Name of incoming protocol */
  252. extern int recipients_count; /* Number of recipients */
  253. @@ -235,4 +239,6 @@ extern pid_t child_open_exim2_functio
  254. extern pid_t child_open_function(uschar **, uschar **, int, int *, int *, BOOL, const uschar *);
  255. #endif
  256. +#pragma GCC visibility pop
  257. +
  258. /* End of local_scan.h */
  259. --- a/src/readconf.c
  260. +++ b/src/readconf.c
  261. @@ -215,6 +215,9 @@ static optionlist optionlist_config[] =
  262. { "local_from_prefix", opt_stringptr, {&local_from_prefix} },
  263. { "local_from_suffix", opt_stringptr, {&local_from_suffix} },
  264. { "local_interfaces", opt_stringptr, {&local_interfaces} },
  265. +#ifdef DLOPEN_LOCAL_SCAN
  266. + { "local_scan_path", opt_stringptr, &local_scan_path },
  267. +#endif
  268. #ifdef HAVE_LOCAL_SCAN
  269. { "local_scan_timeout", opt_time, {&local_scan_timeout} },
  270. #endif
  271. --- a/src/string.c
  272. +++ b/src/string.c
  273. @@ -418,6 +418,7 @@ return ss;
  274. #if (defined(HAVE_LOCAL_SCAN) || defined(EXPAND_DLFUNC)) \
  275. && !defined(MACRO_PREDEF) && !defined(COMPILE_UTILITY)
  276. +#pragma GCC visibility push(default)
  277. /*************************************************
  278. * Copy and save string *
  279. *************************************************/
  280. @@ -470,6 +471,7 @@ Ustrncpy(ss, s, n);
  281. ss[n] = 0;
  282. return ss;
  283. }
  284. +#pragma GCC visibility pop
  285. #endif