From: Jeremy Harris Date: Sun, 2 Feb 2014 22:17:46 +0000 (+0000) Subject: Generic "sock" malware scanner type, from Martin Poole X-Git-Url: https://git.exim.org/users/jgh/exim.git/commitdiff_plain/33ca8da5a4641a20c2b994ee669b0bf0fd7cb907 Generic "sock" malware scanner type, from Martin Poole --- diff --git a/src/src/malware.c b/src/src/malware.c index 88e96911f..62758580c 100644 --- a/src/src/malware.c +++ b/src/src/malware.c @@ -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;