summaryrefslogtreecommitdiffstats
path: root/debian/patches/90_localscan_dlopen.dpatch
blob: 21c96b1b8a6af313aa42d41694bd3fbf837f5515 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
Description: Allow one to use and switch between different local_scan functions
 without recompiling exim.
 http://marc.merlins.org/linux/exim/files/sa-exim-current/ Original patch from
 David Woodhouse, modified first by Derrick 'dman' Hudson and then by Marc
 MERLIN for SA-Exim and minor/major API version tracking
Author: David Woodhouse, Derrick 'dman' Hudson, Marc MERLIN
Origin: other, http://marc.merlins.org/linux/exim/files/sa-exim-current/
Forwarded: https://bugs.exim.org/show_bug.cgi?id=2671
Last-Update: 2022-12-19

--- a/src/EDITME
+++ b/src/EDITME
@@ -874,10 +874,25 @@ HEADERS_CHARSET="ISO-8859-1"
 # as the traditional crypt() function.
 # *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING ***
 
 
 #------------------------------------------------------------------------------
+# On systems which support dynamic loading of shared libraries, Exim can
+# load a local_scan function specified in its config file instead of having
+# to be recompiled with the desired local_scan function. For a full
+# description of the API to this function, see the Exim specification.
+
+DLOPEN_LOCAL_SCAN=yes
+
+# If you set DLOPEN_LOCAL_SCAN, then you need to include -rdynamic in the
+# linker flags.  Without it, the loaded .so won't be able to access any
+# functions from exim.
+
+LDFLAGS += -rdynamic
+CFLAGS += -fvisibility=hidden
+
+#------------------------------------------------------------------------------
 # The default distribution of Exim contains only the plain text form of the
 # documentation. Other forms are available separately. If you want to install
 # the documentation in "info" format, first fetch the Texinfo documentation
 # sources from the ftp directory and unpack them, which should create files
 # with the extension "texinfo" in the doc directory. You may find that the
--- a/src/config.h.defaults
+++ b/src/config.h.defaults
@@ -31,10 +31,12 @@ Do not put spaces between # and the 'def
 #define AUTH_SPA
 #define AUTH_TLS
 
 #define AUTH_VARS                     4
 
+#define DLOPEN_LOCAL_SCAN
+
 #define BIN_DIRECTORY
 
 #define CONFIGURE_FILE
 #define CONFIGURE_FILE_USE_EUID
 #define CONFIGURE_FILE_USE_NODE
--- a/src/globals.c
+++ b/src/globals.c
@@ -115,10 +115,14 @@ tls_support tls_out = {
 uschar *dsn_envid              = NULL;
 int     dsn_ret                = 0;
 const pcre2_code  *regex_DSN         = NULL;
 uschar *dsn_advertise_hosts    = NULL;
 
+#ifdef DLOPEN_LOCAL_SCAN
+uschar *local_scan_path        = NULL;
+#endif
+
 #ifndef DISABLE_TLS
 BOOL    gnutls_compat_mode     = FALSE;
 BOOL    gnutls_allow_auto_pkcs11 = FALSE;
 uschar *hosts_require_alpn     = NULL;
 uschar *openssl_options        = NULL;
--- a/src/globals.h
+++ b/src/globals.h
@@ -153,10 +153,14 @@ extern uschar *tls_advertise_hosts;    /
 extern uschar  *dsn_envid;             /* DSN envid string */
 extern int      dsn_ret;               /* DSN ret type*/
 extern const pcre2_code  *regex_DSN;         /* For recognizing DSN settings */
 extern uschar  *dsn_advertise_hosts;   /* host for which TLS is advertised */
 
+#ifdef DLOPEN_LOCAL_SCAN
+extern uschar *local_scan_path;        /* Path to local_scan() library */
+#endif
+
 /* Input-reading functions for messages, so we can use special ones for
 incoming TCP/IP. */
 
 extern int (*lwr_receive_getc)(unsigned);
 extern uschar * (*lwr_receive_getbuf)(unsigned *);
--- a/src/local_scan.c
+++ b/src/local_scan.c
@@ -5,60 +5,136 @@
 /* Copyright (c) University of Cambridge 1995 - 2009 */
 /* Copyright (c) The Exim Maintainers 2021 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 
-/******************************************************************************
-This file contains a template local_scan() function that just returns ACCEPT.
-If you want to implement your own version, you should copy this file to, say
-Local/local_scan.c, and edit the copy. To use your version instead of the
-default, you must set
-
-HAVE_LOCAL_SCAN=yes
-LOCAL_SCAN_SOURCE=Local/local_scan.c
-
-in your Local/Makefile. This makes it easy to copy your version for use with
-subsequent Exim releases.
-
-For a full description of the API to this function, see the Exim specification.
-******************************************************************************/
-
-
 /* This is the only Exim header that you should include. The effect of
 including any other Exim header is not defined, and may change from release to
 release. Use only the documented interface! */
 
 #include "local_scan.h"
 
-
-/* This is a "do-nothing" version of a local_scan() function. The arguments
-are:
-
-  fd             The file descriptor of the open -D file, which contains the
-                   body of the message. The file is open for reading and
-                   writing, but modifying it is dangerous and not recommended.
-
-  return_text    A pointer to an unsigned char* variable which you can set in
-                   order to return a text string. It is initialized to NULL.
-
-The return values of this function are:
-
-  LOCAL_SCAN_ACCEPT
-                 The message is to be accepted. The return_text argument is
-                   saved in $local_scan_data.
-
-  LOCAL_SCAN_REJECT
-                 The message is to be rejected. The returned text is used
-                   in the rejection message.
-
-  LOCAL_SCAN_TEMPREJECT
-                 This specifies a temporary rejection. The returned text
-                   is used in the rejection message.
-*/
+#ifdef DLOPEN_LOCAL_SCAN
+#include <dlfcn.h>
+#include <stdlib.h>
+static int (*local_scan_fn)(int fd, uschar **return_text) = NULL;
+static int load_local_scan_library(void);
+#endif
 
 int
 local_scan(int fd, uschar **return_text)
 {
-return LOCAL_SCAN_ACCEPT;
+
+#ifdef DLOPEN_LOCAL_SCAN
+/* local_scan_path is defined AND not the empty string */
+if (local_scan_path && *local_scan_path)
+  {
+  if (!local_scan_fn)
+    {
+    if (!load_local_scan_library())
+      {
+        char *base_msg , *error_msg , *final_msg ;
+        int final_length = -1 ;
+
+        base_msg=US"Local configuration error - local_scan() library failure\n";
+        error_msg = dlerror() ;
+
+        final_length = strlen(base_msg) + strlen(error_msg) + 1 ;
+        final_msg = (char*)malloc( final_length*sizeof(char) ) ;
+        *final_msg = '\0' ;
+
+        strcat( final_msg , base_msg ) ;
+        strcat( final_msg , error_msg ) ;
+
+        *return_text = final_msg ;
+      return LOCAL_SCAN_TEMPREJECT;
+      }
+    }
+    return local_scan_fn(fd, return_text);
+  }
+else
+#endif
+  return LOCAL_SCAN_ACCEPT;
+}
+
+#ifdef DLOPEN_LOCAL_SCAN
+
+static int load_local_scan_library(void)
+{
+/* No point in keeping local_scan_lib since we'll never dlclose() anyway */
+void *local_scan_lib = NULL;
+int (*local_scan_version_fn)(void);
+int vers_maj;
+int vers_min;
+
+local_scan_lib = dlopen(local_scan_path, RTLD_NOW);
+if (!local_scan_lib)
+  {
+  log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() library open failed - "
+    "message temporarily rejected");
+  return FALSE;
+  }
+
+local_scan_version_fn = dlsym(local_scan_lib, "local_scan_version_major");
+if (!local_scan_version_fn)
+  {
+  dlclose(local_scan_lib);
+  log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() library doesn't contain "
+    "local_scan_version_major() function - message temporarily rejected");
+  return FALSE;
+  }
+
+/* The major number is increased when the ABI is changed in a non
+   backward compatible way. */
+vers_maj = local_scan_version_fn();
+
+local_scan_version_fn = dlsym(local_scan_lib, "local_scan_version_minor");
+if (!local_scan_version_fn)
+  {
+  dlclose(local_scan_lib);
+  log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() library doesn't contain "
+    "local_scan_version_minor() function - message temporarily rejected");
+  return FALSE;
+  }
+
+/* The minor number is increased each time a new feature is added (in a
+   way that doesn't break backward compatibility) -- Marc */
+vers_min = local_scan_version_fn();
+
+
+if (vers_maj != LOCAL_SCAN_ABI_VERSION_MAJOR)
+  {
+  dlclose(local_scan_lib);
+  local_scan_lib = NULL;
+  log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() has an incompatible major"
+    "version number, you need to recompile your module for this version"
+    "of exim (The module was compiled for version %d.%d and this exim provides"
+    "ABI version %d.%d)", vers_maj, vers_min, LOCAL_SCAN_ABI_VERSION_MAJOR,
+    LOCAL_SCAN_ABI_VERSION_MINOR);
+  return FALSE;
+  }
+else if (vers_min > LOCAL_SCAN_ABI_VERSION_MINOR)
+  {
+  dlclose(local_scan_lib);
+  local_scan_lib = NULL;
+  log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() has an incompatible minor"
+    "version number, you need to recompile your module for this version"
+    "of exim (The module was compiled for version %d.%d and this exim provides"
+    "ABI version %d.%d)", vers_maj, vers_min, LOCAL_SCAN_ABI_VERSION_MAJOR,
+    LOCAL_SCAN_ABI_VERSION_MINOR);
+  return FALSE;
+  }
+
+local_scan_fn = dlsym(local_scan_lib, "local_scan");
+if (!local_scan_fn)
+  {
+  dlclose(local_scan_lib);
+  log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() library doesn't contain "
+    "local_scan() function - message temporarily rejected");
+  return FALSE;
+  }
+return TRUE;
 }
 
+#endif /* DLOPEN_LOCAL_SCAN */
+
 /* End of local_scan.c */
--- a/src/local_scan.h
+++ b/src/local_scan.h
@@ -25,10 +25,11 @@ store.c
 /* Some basic types that make some things easier, the Exim configuration
 settings, and the store functions. */
 
 #include <stdarg.h>
 #include <sys/types.h>
+#pragma GCC visibility push(default)
 #include "config.h"
 #include "mytypes.h"
 #include "store.h"
 
 
@@ -164,10 +165,13 @@ extern const uschar *headers_charset;  /
 extern header_line *header_last;       /* Final header */
 extern header_line *header_list;       /* First header */
 extern BOOL    host_checking;          /* Set when checking a host */
 extern uschar *interface_address;      /* Interface for incoming call */
 extern int     interface_port;         /* Port number for incoming call */
+#ifdef DLOPEN_LOCAL_SCAN
+extern uschar *local_scan_path;
+#endif
 extern uschar *message_id;             /* Internal id of message being handled */
 extern uschar *received_protocol;      /* Name of incoming protocol */
 extern int     recipients_count;       /* Number of recipients */
 extern recipient_item *recipients_list;/* List of recipient addresses */
 extern unsigned char *sender_address;  /* Sender address */
@@ -234,6 +238,8 @@ extern uschar * string_copy_taint_functi
 extern pid_t    child_open_exim_function(int *, const uschar *);
 extern pid_t    child_open_exim2_function(int *, uschar *, uschar *, const uschar *);
 extern pid_t    child_open_function(uschar **, uschar **, int, int *, int *, BOOL, const uschar *);
 #endif
 
+#pragma GCC visibility pop
+
 /* End of local_scan.h */
--- a/src/readconf.c
+++ b/src/readconf.c
@@ -210,10 +210,13 @@ static optionlist optionlist_config[] =
 #endif
   { "local_from_check",         opt_bool,        {&local_from_check} },
   { "local_from_prefix",        opt_stringptr,   {&local_from_prefix} },
   { "local_from_suffix",        opt_stringptr,   {&local_from_suffix} },
   { "local_interfaces",         opt_stringptr,   {&local_interfaces} },
+#ifdef DLOPEN_LOCAL_SCAN
+  { "local_scan_path",          opt_stringptr,   &local_scan_path },
+#endif
 #ifdef HAVE_LOCAL_SCAN
   { "local_scan_timeout",       opt_time,        {&local_scan_timeout} },
 #endif
   { "local_sender_retain",      opt_bool,        {&local_sender_retain} },
   { "localhost_number",         opt_stringptr,   {&host_number_string} },
--- a/src/string.c
+++ b/src/string.c
@@ -416,10 +416,11 @@ return ss;
 
 
 
 #if (defined(HAVE_LOCAL_SCAN) || defined(EXPAND_DLFUNC)) \
 	&& !defined(MACRO_PREDEF) && !defined(COMPILE_UTILITY)
+#pragma GCC visibility push(default)
 /*************************************************
 *            Copy and save string                *
 *************************************************/
 
 /*
@@ -461,10 +462,11 @@ Returns:    copy of string in new store
 uschar *
 string_copyn_function(const uschar * s, int n)
 {
 return string_copyn(s, n);
 }
+#pragma GCC visibility pop
 #endif
 
 
 /*************************************************
 *     Copy and save string in malloc'd store     *