Generic "sock" malware scanner type, from Martin Poole
authorJeremy Harris <jgh146exb@wizmail.org>
Sun, 2 Feb 2014 22:17:46 +0000 (22:17 +0000)
committerJeremy Harris <jgh146exb@wizmail.org>
Sun, 2 Feb 2014 22:17:46 +0000 (22:17 +0000)
src/src/malware.c

index 88e96911ff6cf4e7205526ccc5ec89b1fc8c2d90..62758580cf1829b9a7ac2e3c9b24299ffbb38d92 100644 (file)
@@ -213,6 +213,11 @@ mksd_errlog_defer(const uschar * str)
 {
   return m_scanner_errlog_defer("mksd", str);
 }
+static int
+sock_errlog_defer(const uschar * str)
+{
+  return m_scanner_errlog_defer("sock", str);
+}
 
 static void
 clmd_errlog(const uschar * str)
@@ -298,37 +303,37 @@ m_tcpsocket_fromdef(const uschar * hostport, uschar ** errstr)
 static int
 m_unixsocket(const uschar * path, uschar ** errstr)
 {
-int sock;
-struct sockaddr_un server;
-
-if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
-  *errstr = "can't open UNIX socket.";
-  return -1;
-}
+  int sock;
+  struct sockaddr_un server;
 
-server.sun_family = AF_UNIX;
-Ustrcpy(server.sun_path, path);
-if (connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0) {
-  int err = errno;
-  (void)close(sock);
-  *errstr =  string_sprintf("unable to connect to UNIX socket (%s): %s",
-               path, strerror(err));
-  return -1;
+  if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+    *errstr = "can't open UNIX socket.";
+    return -1;
   }
-return sock;
+
+  server.sun_family = AF_UNIX;
+  Ustrcpy(server.sun_path, path);
+  if (connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0) {
+    int err = errno;
+    (void)close(sock);
+    *errstr =  string_sprintf("unable to connect to UNIX socket (%s): %s",
+                 path, strerror(err));
+    return -1;
+    }
+  return sock;
 }
 
 static int
 m_sock_send(int sock, uschar * buf, int cnt, uschar ** errstr)
 {
-if (send(sock, buf, cnt, 0) < 0) {
-  int err = errno;
-  (void)close(sock);
-  *errstr = string_sprintf("unable to send to socket (%s): %s",
-        buf, strerror(err));
-  return -1;
-  }
-return sock;
+  if (send(sock, buf, cnt, 0) < 0) {
+    int err = errno;
+    (void)close(sock);
+    *errstr = string_sprintf("unable to send to socket (%s): %s",
+          buf, strerror(err));
+    return -1;
+    }
+  return sock;
 }
 
 /*************************************************
@@ -336,7 +341,7 @@ return sock;
 *************************************************/
 
 typedef enum {M_FPROTD, M_DRWEB, M_AVES, M_FSEC, M_KAVD, M_CMDL,
-               M_SOPHIE, M_CLAMD, M_MKSD} scanner_t;
+               M_SOPHIE, M_CLAMD, M_SOCK, M_MKSD} scanner_t;
 static struct scan
 {
   scanner_t    scancode;
@@ -352,6 +357,7 @@ static struct scan
   { M_CMDL,    "cmdline",      NULL },
   { M_SOPHIE,  "sophie",       "/var/run/sophie" },
   { M_CLAMD,   "clamd",        "/tmp/clamd" },
+  { M_SOCK,    "sock",         "/tmp/malware.sock" },
   { M_MKSD,    "mksd",         NULL },
   { -1,                NULL,           NULL }          /* end-marker */
 };
@@ -1588,11 +1594,104 @@ malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking)
        break;
       } /* clamd */
 
+    case M_SOCK: /* "sock" scanner type ------------------------------------- */
+      /* This code was derived by Martin Poole from the clamd code contributed
+         by David Saez and the cmdline code
+      */
+      {
+       int sock, bread=0;
+       uschar * commandline;
+       uschar av_buffer[1024];
+       uschar * linebuffer;
+       uschar *sockline_scanner;
+       uschar sockline_scanner_default[] = "%s\n";
+       uschar *sockline_trigger;
+       const pcre *sockline_trigger_re;
+       uschar *sockline_regex;
+       const pcre *sockline_regex_re;
+       int result;
+       int ovector[10*3];
+
+       /* find scanner command line */
+       if (!(sockline_scanner = string_nextinlist(&av_scanner_work, &sep,
+                                           NULL, 0)))
+         sockline_scanner = sockline_scanner_default;
+
+       /* find scanner output trigger */
+       if (!(sockline_trigger = string_nextinlist(&av_scanner_work, &sep,
+                                           NULL, 0)))
+         return sock_errlog_defer("missing trigger specification");
+
+       /* precompile trigger regex */
+       sockline_trigger_re = pcre_compile(CS sockline_trigger, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
+       if (sockline_trigger_re == NULL)
+         return sock_errlog_defer(
+           string_sprintf("regular expression error in '%s': %s at offset %d",
+             sockline_trigger, rerror, roffset));
+
+       /* find virus name regex */
+       if (!(sockline_regex = string_nextinlist(&av_scanner_work, &sep,
+                                              NULL, 0)))
+         return sock_errlog_defer("missing virus name regex specification");
+
+       /* precompile name regex */
+       sockline_regex_re = pcre_compile(CS sockline_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
+       if (sockline_regex_re == NULL)
+         return sock_errlog_defer(
+           string_sprintf("regular expression error in '%s': %s at offset %d",
+             sockline_regex, rerror, roffset));
+
+       /* prepare scanner call */
+       commandline = string_sprintf("%s/scan/%s/%s.eml", spool_directory, message_id, message_id);
+       commandline = string_sprintf( CS sockline_scanner, CS commandline);
+
+
+       /* socket does not start with '/' -> network socket */
+       sock = *scanner_options != '/'
+         ? m_tcpsocket_fromdef(scanner_options, &errstr)
+         : m_unixsocket(scanner_options, &errstr);
+       if (sock < 0)
+         return sock_errlog_defer(errstr);
+
+       /* Pass the command string to the socket */
+       if (m_sock_send(sock, commandline, Ustrlen(commandline), &errstr) < 0)
+         return sock_errlog_defer(errstr);
+
+       /* 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))
+         return sock_errlog_defer(
+           string_sprintf("unable to read from socket (%s)", strerror(errno)));
+
+       if (bread == sizeof(av_buffer))
+         return sock_errlog_defer("buffer too small");
+       linebuffer = string_copy(av_buffer);
+
+       /* try trigger match */
+       if (regex_match_and_setup(sockline_trigger_re, linebuffer, 0, -1)) {
+         result = pcre_exec(sockline_regex_re, NULL,
+                             CS av_buffer, Ustrlen(av_buffer), 0, 0,
+                             ovector, nelements(ovector));
+         if (result >= 2)
+           pcre_get_substring(CS av_buffer, ovector, result, 1,
+                             (const char **)&malware_name);
+         else
+           malware_name = US "unknown";
+       }
+       else /* no virus found */
+         malware_name = NULL;
+      }
+
     case M_MKSD: /* "mksd" scanner type ------------------------------------- */
       {
        char *mksd_options_end;
        int mksd_maxproc = 1;  /* default, if no option supplied */
-       struct sockaddr_un server;
        int sock;
        int retval;