From 2376f2f55f7f8e1df38b44908b6d39181a88a65e Mon Sep 17 00:00:00 2001 From: Tom Kistner Date: Wed, 5 Jan 2005 13:33:58 +0000 Subject: [PATCH] Added patches for remote clamd operation and improved drwebd error handling. Contributed by Alex Miller --- src/src/malware.c | 163 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 161 insertions(+), 2 deletions(-) diff --git a/src/src/malware.c b/src/src/malware.c index 82111557c..b9a641c94 100644 --- a/src/src/malware.c +++ b/src/src/malware.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/malware.c,v 1.3 2004/12/17 14:52:44 ph10 Exp $ */ +/* $Cambridge: exim/src/src/malware.c,v 1.4 2005/01/05 13:33:58 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -24,6 +24,11 @@ int mksd_scan_packed(int sock); #define DRWEBD_RETURN_VIRUSES (1<<0) /* ask daemon return to us viruses names from report */ #define DRWEBD_IS_MAIL (1<<19) /* say to daemon that format is "archive MAIL" */ +#define DERR_READ_ERR (1<<0) /* read error */ +#define DERR_NOMEMORY (1<<2) /* no memory */ +#define DERR_TIMEOUT (1<<9) /* scan timeout has run out */ +#define DERR_BAD_CALL (1<<15) /* wrong command */ + /* Routine to check whether a system is big- or litte-endian. Ripped from http://www.faqs.org/faqs/graphics/fileformats-faq/part4/section-7.html Needed for proper kavdaemon implementation. Sigh. */ @@ -197,6 +202,7 @@ int malware(uschar **listptr) { /* calc file size */ drweb_fd = open(CS scanrequest, O_RDONLY); if (drweb_fd == -1) { + close(sock); log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: drweb: can't open spool file %s: %s", scanrequest, strerror(errno)); @@ -204,6 +210,8 @@ int malware(uschar **listptr) { } fsize = lseek(drweb_fd, 0, SEEK_END); if (fsize == -1) { + close(sock); + close(drweb_fd); log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: drweb: can't seek spool file %s: %s", scanrequest, strerror(errno)); @@ -238,21 +246,23 @@ int malware(uschar **listptr) { if (result == -1) { close(sock); close(drweb_fd); + free(drweb_fbuf); log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: drweb: can't read spool file %s: %s", scanrequest, strerror(errno)); return DEFER; } + close(drweb_fd); /* send file body to socket */ if (send(sock, drweb_fbuf, fsize, 0) < 0) { close(sock); + free(drweb_fbuf); log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: drweb: unable to send file body to socket (%s)", drweb_options); return DEFER; } close(drweb_fd); - free(drweb_fbuf); } else { /* open the drwebd UNIX socket */ @@ -367,6 +377,22 @@ int malware(uschar **listptr) { } } else { + char *drweb_s = NULL; + + if (drweb_rc & DERR_READ_ERR) drweb_s = "read error"; + if (drweb_rc & DERR_NOMEMORY) drweb_s = "no memory"; + if (drweb_rc & DERR_TIMEOUT) drweb_s = "timeout"; + if (drweb_rc & DERR_BAD_CALL) drweb_s = "wrong command"; + /* retcodes DERR_SYMLINK, DERR_NO_REGFILE, DERR_SKIPPED. + * DERR_TOO_BIG, DERR_TOO_COMPRESSED, DERR_SPAM, + * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR + * and others are ignored */ + if (drweb_s) { + log_write(0, LOG_MAIN|LOG_PANIC, + "malware acl condition: drweb: drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s); + close(sock); + return DEFER; + } /* no virus found */ malware_name = NULL; }; @@ -956,6 +982,14 @@ int malware(uschar **listptr) { uschar hostname[256]; struct hostent *he; struct in_addr in; + uschar *clamd_options2; + uschar clamd_options2_buffer[1024]; + uschar clamd_options2_default[] = ""; + uschar av_buffer2[1024]; + uschar *clamav_fbuf; + uschar scanrequest[1024]; + int sockData, clam_fd, result; + unsigned int fsize; if ((clamd_options = string_nextinlist(&av_scanner_work, &sep, clamd_options_buffer, @@ -963,6 +997,11 @@ int malware(uschar **listptr) { /* no options supplied, use default options */ clamd_options = clamd_options_default; } + if ((clamd_options2 = string_nextinlist(&av_scanner_work, &sep, + clamd_options2_buffer, + sizeof(clamd_options2_buffer))) == NULL) { + clamd_options2 = clamd_options2_default; + } /* socket does not start with '/' -> network socket */ if (*clamd_options != '/') { @@ -998,6 +1037,126 @@ int malware(uschar **listptr) { inet_ntoa(in), port, strerror(errno)); return DEFER; } + + if (strcmpic(clamd_options2,US"local") == 0) { + + /* Pass the string to ClamAV (7 = "SCAN \n" + \0) */ + + snprintf(CS file_name,1024,"SCAN %s/scan/%s\n", spool_directory, message_id); + + if (send(sock, file_name, Ustrlen(file_name), 0) < 0) { + close(sock); + log_write(0, LOG_MAIN|LOG_PANIC,"malware acl condition: clamd: unable to write to socket (%s)", + strerror(errno)); + return DEFER; + } + } else { + + /* Pass the string to ClamAV (7 = "STREAM\n") */ + + if (send(sock, "STREAM\n", 7, 0) < 0) { + close(sock); + log_write(0, LOG_MAIN|LOG_PANIC,"malware acl condition: clamd: unable to write to socket (%s)", + strerror(errno)); + return DEFER; + } + memset(av_buffer2, 0, sizeof(av_buffer2)); + bread = read(sock, av_buffer2, sizeof(av_buffer2)); + + if (bread < 0) { + log_write(0, LOG_MAIN|LOG_PANIC, + "malware acl condition: clamd: unable to read PORT from socket (%s)", + strerror(errno)); + return DEFER; + } + + if (bread == sizeof(av_buffer)) { + log_write(0, LOG_MAIN|LOG_PANIC, + "malware acl condition: clamd: buffer too small"); + return DEFER; + } + + if (!(*av_buffer2)) { + log_write(0, LOG_MAIN|LOG_PANIC, + "malware acl condition: clamd: ClamAV returned null"); + return DEFER; + } + + av_buffer2[bread] = '\0'; + if( sscanf(CS av_buffer2, "PORT %hu\n", &port) != 1 ) { + log_write(0, LOG_MAIN|LOG_PANIC, + "malware acl condition: clamd: Expected port information from clamd, got '%s'", av_buffer2); + return DEFER; + }; + + if ( (sockData = ip_socket(SOCK_STREAM, AF_INET)) < 0) { + log_write(0, LOG_MAIN|LOG_PANIC, + "malware acl condition: clamd: unable to acquire socket (%s)", + strerror(errno)); + return DEFER; + } + + if (ip_connect(sockData, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) { + close(sockData); + log_write(0, LOG_MAIN|LOG_PANIC, + "malware acl condition: clamd: connection to %s, port %u failed (%s)", + inet_ntoa(in), port, strerror(errno)); + return DEFER; + } + + snprintf(CS scanrequest, 1024,CS"%s/scan/%s/%s.eml", + spool_directory, message_id, message_id); + + /* calc file size */ + clam_fd = open(CS scanrequest, O_RDONLY); + if (clam_fd == -1) { + log_write(0, LOG_MAIN|LOG_PANIC, + "malware acl condition: clamd: can't open spool file %s: %s", + scanrequest, strerror(errno)); + return DEFER; + } + fsize = lseek(clam_fd, 0, SEEK_END); + if (fsize == -1) { + log_write(0, LOG_MAIN|LOG_PANIC, + "malware acl condition: clamd: can't seek spool file %s: %s", + scanrequest, strerror(errno)); + return DEFER; + } + lseek(clam_fd, 0, SEEK_SET); + + clamav_fbuf = (uschar *) malloc (fsize); + if (!clamav_fbuf) { + close(sockData); + close(clam_fd); + log_write(0, LOG_MAIN|LOG_PANIC, + "malware acl condition: clamd: unable to allocate memory %u for file (%s)", + fsize, scanrequest); + return DEFER; + } + + result = read (clam_fd, clamav_fbuf, fsize); + if (result == -1) { + close(sockData); + close(clam_fd); + free(clamav_fbuf); + log_write(0, LOG_MAIN|LOG_PANIC, + "malware acl condition: clamd: can't read spool file %s: %s", + scanrequest, strerror(errno)); + return DEFER; + } + close(clam_fd); + + /* send file body to socket */ + if (send(sockData, clamav_fbuf, fsize, 0) < 0) { + close(sockData); + free(clamav_fbuf); + log_write(0, LOG_MAIN|LOG_PANIC, + "malware acl condition: clamd: unable to send file body to socket (%s:%u)", hostname, port); + return DEFER; + } + free(clamav_fbuf); + close(sockData); + } } else { /* open the local socket */ -- 2.25.1