Start
[exim.git] / configs / config.samples / L001
diff --git a/configs/config.samples/L001 b/configs/config.samples/L001
new file mode 100644 (file)
index 0000000..247923c
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * uvscan local_scan() function for Exim (requires at least Exim v4.14)
+ * known to work with VirusScan for Linux v4.16.0 (Scan engine v4.2.40)
+ * but should be OK with other platforms
+ *
+ * this file is free software (license=GNU GPLv2) and comes with no
+ * guarantees--if it breaks, you get to keep the pieces (maybe not the mail)!
+ *
+ * by (ie patches / flames to): mb/local_scan@dcs.qmul.ac.uk, 2003-05-02
+ * (original version on 2002-05-25)
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <string.h>
+#include "local_scan.h"
+
+/*
+ * remember to set LOCAL_SCAN_HAS_OPTIONS=yes in Local/Makefile
+ * otherwise you get stuck with the compile-time defaults
+ */
+
+static uschar *uvscan_binary = US"/usr/local/uvscan/uvscan";
+static uschar *data_directory = US"/usr/local/uvscan";
+
+optionlist local_scan_options[] = { /* alphabetical order */
+       { "data_directory", opt_stringptr, &data_directory },
+       { "uvscan_binary", opt_stringptr, &uvscan_binary }
+};
+
+int local_scan_options_count = sizeof(local_scan_options)/sizeof(optionlist);
+
+/* log headers in rejectlog or not? */
+
+//#define VIRUS_IN_MAIL LOCAL_SCAN_REJECT
+#define VIRUS_IN_MAIL LOCAL_SCAN_REJECT_NOLOGHDR
+
+/*
+ * buffer is used both for file copying and catching uvscan's output
+ * BUFSIZE = 1024 should always be fine
+ */
+
+#define BUFSIZE 1024
+
+/* some number which uvscan doesn't return */
+#define MAGIC 123
+
+/*
+ * some macros to make the main function more obvious
+ * NB bailing out might leave tempfiles hanging around
+ * (and open fds, but no need to be worried about that)
+ */
+
+#define BAIL(btext) { log_write(0, LOG_MAIN, "UVSCAN ERROR: "btext); \
+       *return_text = "local scanning problem: please try again later"; \
+       return LOCAL_SCAN_TEMPREJECT; }
+
+#define DEBUG(dtext) if ((debug_selector & D_local_scan) != 0) \
+               { debug_printf(dtext); sleep(1); }
+       /* sleep useful for running exim -d */
+
+#define RESULT(rtext) header_add(32, \
+       "X-uvscan-result: "rtext" (%s)\n", message_id);
+
+#define FREEZE(ftext) { header_add(32, \
+       "X-uvscan-warning: frozen for manual attention (%s)\n", message_id); \
+       RESULT(ftext); *return_text = ftext; return LOCAL_SCAN_ACCEPT_FREEZE; }
+
+/* OK, enough waffle. On with the show! */
+
+int local_scan(int fd, uschar **return_text)
+{
+       char tf[] = "/tmp/local_scan.XXXXXX"; /* should this be tunable? */
+       int tmpfd, bytesin, bytesout, pid, status, pipe_fd[2];
+       fd_set fds; 
+       static uschar buffer[BUFSIZE];
+
+       DEBUG("entered uvscan local_scan() function");
+
+       /*
+        * I set majordomo to resend using -oMr lsmtp
+         * (and yes, I know majordomo isn't actually using SMTP..)
+        * no point in scanning these beasties twice
+        */
+
+       if(!strcmp(received_protocol, "lsmtp"))
+               return LOCAL_SCAN_ACCEPT;
+
+       /* create a file to copy the data into */
+
+       if ((tmpfd = mkstemp(tf)) == -1)
+               BAIL("mkstemp failed");
+
+       DEBUG("made tmp file");
+
+       /* copy said file BUFSIZE at a time */
+
+       while ((bytesin = read(fd, buffer, BUFSIZE)) > 0) {
+               bytesout = write(tmpfd, buffer, bytesin);
+               if (bytesout < 1)
+                       BAIL("writing to tmp file");
+       }
+       if (bytesin < 0)
+               BAIL("reading from spool file");
+
+       close(tmpfd);
+
+       if(pipe(pipe_fd) == -1)
+               BAIL("making pipe");
+
+       /* fork and scan */     
+
+       if((pid = fork()) == -1)
+               BAIL("couldn't fork");
+
+       if(pid == 0) {
+               close(1); /* close stdout */
+               if(dup2(pipe_fd[1],1) == -1) /* duplicate write as stdout */
+                       BAIL("dup2 (stdout) failed");
+               if(fcntl(1,F_SETFD,0) == -1) /* fd to NOT close on exec() */
+                       BAIL("fcntl (stdout) failed");
+
+               execl(uvscan_binary, uvscan_binary, "--mime", "--secure",
+                       "-d", data_directory, tf, NULL);
+               DEBUG("execl failed");
+               _exit(MAGIC);
+       }
+
+       if(waitpid(pid, &status, 0) < 1)
+               BAIL("couldn't wait for child");
+       
+       DEBUG("about to unlink");
+
+       if(unlink(tf) == -1)
+               FREEZE("couldn't unlink tmp file");
+
+       DEBUG("unlinked :)");
+
+       /*
+        * choose what to do based on the return code of uvscan
+        * RESULT() or FREEZE() according to personal taste
+        */
+
+       if(WIFEXITED(status) != 0)
+               switch(WEXITSTATUS(status)) {
+               case 0: RESULT("clean"); break;
+               case 2: RESULT("driver integrity check failed"); break;
+               case 6: FREEZE("general problem occurred"); break;
+               case 8: RESULT("could not find a driver"); break;
+               case 12: FREEZE("failed to clean file"); break;
+               case 13:
+                       // RESULT("virus detected"); /* were we to accept */
+                       DEBUG("about to read from uvscan process");
+                       FD_ZERO(&fds);
+                       FD_SET(pipe_fd[0], &fds);
+                       if(select(pipe_fd[0]+1, &fds, NULL, NULL, NULL)) {
+                               /* last NULL above means wait forever! */
+                               DEBUG("select returned non-zero");
+                               if((bytesin = read(pipe_fd[0], buffer,
+                                               BUFSIZE - 1)) > 0) {
+                                       buffer[bytesin] = (uschar)0;
+                                       *return_text = buffer + 22;
+                                       /* 22 was empirically found ;) */
+                                       return VIRUS_IN_MAIL;
+                               } else
+                                       BAIL("reading from uvscan process");
+                       }
+                       break;
+               case 15: FREEZE("self-check failed"); break;
+               case 19: FREEZE("virus detected and cleaned"); break;
+               case MAGIC: RESULT("couldn't run uvscan"); break;
+               default:
+                       RESULT("unknown error code");
+                       header_add(32, "X-uvscan-status: %d\n",
+                               WEXITSTATUS(status));
+                       break;
+               }
+       else
+               BAIL("child exited abnormally");
+
+       return LOCAL_SCAN_ACCEPT;
+}