[exim-dev] New malware scanner type "sock"

Top Page
Delete this message
Reply to this message
Author: Jeremy Harris
Date:  
To: exim-dev
Subject: [exim-dev] New malware scanner type "sock"
I'm in the process of a major refactor of malware.c and I discover that
we have an internally-developed scanner access method we we never got
around to contributing into the mainline. It's a generic socket
method; we use it for talking to some custom perl.

I've attached it as a patch. Does anyone feel strongly that it should
not go into the exim sourcebase?


(More on the refactor later.  Currently down from 2000 lines to 1800).
-- 
Cheers,
    Jeremy

diff --git a/src/src/malware.c b/src/src/malware.c
index 2b4d282..2803c6b 100644
--- a/src/src/malware.c
+++ b/src/src/malware.c
@@ -1812,6 +1812,203 @@ try_next_server:
     /* ----------------------------------------------------------------------- */



+    /* generic "sock" scanner type ------------------------------------------- */
+    /* This code was derived by Martin Poole from the clamd code contributed by David Saez
+    and the cmdline code
+    */
+    else if (strcmpic(scanner_name,US"sock") == 0) {
+      uschar *sock_options;
+      uschar sock_options_buffer[1024];
+      uschar sock_options_default[] = "/tmp/malware.sock";
+      struct sockaddr_un server;
+      int sock,bread=0;
+      unsigned int port;
+      uschar file_name[1024];
+      uschar commandline[1024];
+      uschar av_buffer[1024];
+      uschar linebuffer[1024];
+      uschar hostname[256];
+      struct hostent *he;
+      struct in_addr in;
+      uschar *sockline_scanner;
+      uschar sockline_scanner_buffer[1024];
+      uschar sockline_scanner_default[] = "%s\n";
+      uschar *sockline_trigger;
+      uschar sockline_trigger_buffer[1024];
+      const pcre *sockline_trigger_re;
+      uschar *sockline_regex;
+      uschar sockline_regex_buffer[1024];
+      const pcre *sockline_regex_re;
+      int result;
+      int trigger = 0;
+      int ovector[30];
+
+      if ((sock_options = string_nextinlist(&av_scanner_work, &sep,
+                                             sock_options_buffer,
+                                             sizeof(sock_options_buffer))) == NULL) {
+        /* no options supplied, use default options */
+        sock_options = sock_options_default;
+      }
+
+      /* find scanner command line */
+      if ((sockline_scanner = string_nextinlist(&av_scanner_work, &sep,
+                                          sockline_scanner_buffer,
+                                          sizeof(sockline_scanner_buffer))) == NULL) {
+        sockline_scanner = sockline_scanner_default;
+      };
+
+      /* find scanner output trigger */
+      if ((sockline_trigger = string_nextinlist(&av_scanner_work, &sep,
+                                          sockline_trigger_buffer,
+                                          sizeof(sockline_trigger_buffer))) == NULL) {
+        /* no trigger regex supplied */
+        log_write(0, LOG_MAIN|LOG_PANIC,
+             "malware acl condition: missing trigger specification for sock scanner type.");
+        return DEFER;
+      };
+
+      /* precompile trigger regex */
+      sockline_trigger_re = pcre_compile(CS sockline_trigger, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
+      if (sockline_trigger_re == NULL) {
+        log_write(0, LOG_MAIN|LOG_PANIC,
+                 "malware acl condition: regular expression error in '%s': %s at offset %d", sockline_trigger, rerror, roffset);
+        return DEFER;
+      };
+
+      /* find scanner name regex */
+      if ((sockline_regex = string_nextinlist(&av_scanner_work, &sep,
+                                             sockline_regex_buffer,
+                                             sizeof(sockline_regex_buffer))) == NULL) {
+        /* no name regex supplied */
+        log_write(0, LOG_MAIN|LOG_PANIC,
+             "malware acl condition: missing virus name regex specification for sock scanner type.");
+        return DEFER;
+      };
+
+      /* precompile name regex */
+      sockline_regex_re = pcre_compile(CS sockline_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
+      if (sockline_regex_re == NULL) {
+        log_write(0, LOG_MAIN|LOG_PANIC,
+                 "malware acl condition: regular expression error in '%s': %s at offset %d", sockline_regex, rerror, roffset);
+        return DEFER;
+      };
+      /* prepare scanner call */
+      (void)string_format(file_name,1024,"%s/scan/%s/%s.eml", spool_directory, message_id, message_id);
+      (void)string_format(commandline,1024, CS sockline_scanner,file_name);
+
+
+      /* socket does not start with '/' -> network socket */
+      if (*sock_options != '/') {
+
+        /* extract host and port part */
+        if( sscanf(CS sock_options, "%s %u", hostname, &port) != 2 ) {
+          log_write(0, LOG_MAIN|LOG_PANIC,
+                    "malware acl condition: sock: invalid socket '%s'", sock_options);
+          return DEFER;
+        };
+
+        /* Lookup the host */
+        if((he = gethostbyname(CS hostname)) == 0) {
+          log_write(0, LOG_MAIN|LOG_PANIC,
+                    "malware acl condition: sock: failed to lookup host '%s'", hostname);
+          return DEFER;
+        }
+
+        in = *(struct in_addr *) he->h_addr_list[0];
+
+        /* Open the Socket */
+        if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
+          log_write(0, LOG_MAIN|LOG_PANIC,
+                    "malware acl condition: sock: unable to acquire socket (%s)",
+                    strerror(errno));
+          return DEFER;
+        }
+
+        if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
+          (void)close(sock);
+          log_write(0, LOG_MAIN|LOG_PANIC,
+                    "malware acl condition: sock: connection to %s, port %u failed (%s)",
+                    inet_ntoa(in), port, strerror(errno));
+          return DEFER;
+        }
+
+      } else {
+        /* open the local socket */
+        if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+          log_write(0, LOG_MAIN|LOG_PANIC,
+                    "malware acl condition: sock: unable to acquire socket (%s)",
+                    strerror(errno));
+          return DEFER;
+        }
+
+        server.sun_family = AF_UNIX;
+        Ustrcpy(server.sun_path, sock_options);
+
+        if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
+          (void)close(sock);
+          log_write(0, LOG_MAIN|LOG_PANIC,
+                    "malware acl condition: sock: unable to connect to UNIX socket %s (%s)",
+                    sock_options, strerror(errno) );
+          return DEFER;
+        }
+      }
+
+      /* Pass the command string to the socket */
+      if (send(sock, commandline, Ustrlen(commandline), 0) < 0) {
+        (void)close(sock);
+        log_write(0, LOG_MAIN|LOG_PANIC,"malware acl condition: sock: unable to write to socket (%s)",
+                  strerror(errno));
+        return DEFER;
+      }
+
+      /*
+        We're done sending, close socket for writing.
+      */
+
+      /* shutdown(sock, SHUT_WR); */
+
+      /* Read the result */
+      memset(av_buffer, 0, sizeof(av_buffer));
+      bread = read(sock, av_buffer, sizeof(av_buffer));
+      (void)close(sock);
+
+      if (!(bread  > 0)) {
+        log_write(0, LOG_MAIN|LOG_PANIC,
+                  "malware acl condition: sock: unable to read from socket (%s)",
+                  strerror(errno));
+        return DEFER;
+      }
+
+      if (bread == sizeof(av_buffer)) {
+        log_write(0, LOG_MAIN|LOG_PANIC,
+                  "malware acl condition: sock: buffer too small");
+        return DEFER;
+      }
+      Ustrcpy( linebuffer, av_buffer );
+
+      /* try trigger match */
+      if (!trigger && regex_match_and_setup(sockline_trigger_re, linebuffer, 0, -1))
+        trigger = 1;
+
+      if (trigger) {
+        Ustrcpy( linebuffer, av_buffer );
+        /* setup default virus name */
+        Ustrcpy(malware_name_buffer,"unknown");
+        malware_name = malware_name_buffer;
+
+    result = pcre_exec(sockline_regex_re, NULL, CS linebuffer, Ustrlen(linebuffer), 0, 0, ovector, 30);
+    if (result >= 2) {
+      pcre_copy_substring(CS linebuffer, ovector, result, 1, CS malware_name_buffer, 255);
+    };
+      }
+      else {
+        /* no virus found */
+        malware_name = NULL;
+      };
+    }
+    /* ----------------------------------------------------------------------- */
+
+
     /* "mksd" scanner type --------------------------------------------------- */
     else if (strcmpic(scanner_name,US"mksd") == 0) {
       uschar *mksd_options;