diff options
Diffstat (limited to 'src/klammail/clamdmail.c')
-rwxr-xr-x | src/klammail/clamdmail.c | 626 |
1 files changed, 626 insertions, 0 deletions
diff --git a/src/klammail/clamdmail.c b/src/klammail/clamdmail.c new file mode 100755 index 0000000..19f985e --- /dev/null +++ b/src/klammail/clamdmail.c @@ -0,0 +1,626 @@ +/* +* +* Clamdmail.c is based loosely on clamdscan.c by Tomasz Kojm. +* +* This program takes a mail message as input from stdin, uses rfc822.c to extract +* attachments to a temp directory, gets clamd to scan them, and +* handles the message appropriately. +* +* Copyright (C) 2003 Robert Hogan <[email protected]> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/time.h> +#include <time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <pwd.h> +#include <clamav.h> + +#include "options.h" +#include "defaults.h" +#include "memory.h" +#include "../version.h" +#include "../../config.h" + +#define BUFFSIZE 1024 + + + +/* this local macro takes care about freeing memory at exit */ +/* +#define mexit(i) if(opt) free_opt(opt); \ + mprintf("*Memory freed. Exit code: %d\n", i); \ + exit(i); +*/ +#define mexit(i) exit(i) + +struct s_info { + int signs; /* number of signatures loaded */ + int dirs; /* number of scanned directories */ + int files; /* number of scanned files */ + int ifiles; /* number of infected files */ + int notremoved; /* number of not removed files (if --remove) */ + int notmoved; /* number of not moved files (if --move) */ + int errors; /* ... of errors */ + long int blocks; /* number of read 16kb blocks */ +}; + + +void help(void); +void printtag(void); +void startclamd(struct optstruct *opt); + +struct s_info claminfo; +short printinfected = 0; +short int mprintf_stdout; + +int clamdscan(struct optstruct *opt) +{ + int ds, dms; + + struct timeval t1, t2; + struct timezone tz; + const char *bndrystore; + const char *tmpdir; + char *tmpfil; + char *pfx; + char *tmper; + char *dir; + struct passwd *user = NULL; + FILE *tmp; + FILE *fs; + int bytes; + char buff[BUFFSIZE]; + struct cl_node *trie = NULL; + int threads = 0; + int fd, fdtmp, ret, no = 0; + unsigned long int size = 0; + long double mb; + const char *virname; + struct cl_engine *engine = NULL; +#ifndef SUPPORT_CLAMAV_V095 + struct cl_limits limits; +#endif + struct stat sb; + + if(optc(opt, 'V')) { + mprintf("clamdmail "KLAMAV_VERSION" \n"); + mexit(0); + } + + if(optc(opt, 'h')) { + free_opt(opt); + help(); + } + + if(optc(opt, 'i')) printinfected = 1; + else printinfected = 0; + + + + memset(&claminfo, 0, sizeof(struct s_info)); + + gettimeofday(&t1, &tz); + + //if(user) + + + char name[19]; + char *tmpnm; + char *mdir; + + if((mdir = getenv("TMPDIR")) == NULL) + #ifdef P_tmpdir + mdir = P_tmpdir; + #else + mdir = "/tmp"; + #endif + + tmpnm = (char*) mcalloc(strlen(mdir) + 1 + 16 + 1 + 7, sizeof(char)); + if(tmpnm == NULL) { + exit(2); + } + + sprintf(tmpnm, "%s/", mdir); + snprintf(name, sizeof(name), "klammailXXXXXX"); + strncat(tmpnm, name, 19); + mkstemp(tmpnm); + + + + fd = open(tmpnm,O_RDWR|O_CREAT, S_IRWXU); + + while((bytes = read(0, buff, BUFFSIZE)) > 0) { + + if(write(fd, buff, bytes) != bytes) { + close(fd); + return CL_EMEM; + } + } + + if(fsync(fd) == -1) { + close(fd); +#ifdef SUPPORT_CLAMAV_V095 + return CL_ETMPFILE; +#else + return CL_EIO; +#endif + } + + close(fd); + + if((fd = open(tmpnm, O_RDONLY)) == -1) { + printf("Can't open file %s\n", tmpnm); + exit(2); + } + + ret = 0; + ret = client(tmpnm, opt, &virname); + + + + /* Clamd isn't running, scan the file ourselves */ + if((ret == 2)) { + /* Clamd isn't running, start it so it is available next time. */ + startclamd(opt); +#ifdef SUPPORT_CLAMAV_V095 + if((engine = cl_engine_new()) == NULL) { + printf("Database initialization error: %s\n", cl_strerror(ret));; + cl_engine_free(engine); + close(fd); + exit(2); + } +#endif + if(optc(opt, 'd')) { +#ifdef SUPPORT_CLAMAV_V095 + if((ret = cl_load(getargc(opt, 'd'), engine, &no, CL_DB_STDOPT))) { +#else + if((ret = cl_load(getargc(opt, 'd'), &engine, &no, CL_DB_STDOPT))) { +#endif + printf("cl_load: %s\n", cl_strerror(ret)); + close(fd); + return 50; + } + }else{ +#ifdef SUPPORT_CLAMAV_V095 + if((ret = cl_load(cl_retdbdir(), engine, &no, CL_DB_STDOPT))) { +#else + if((ret = cl_loaddbdir(cl_retdbdir(), &engine, &no, CL_DB_STDOPT))) { +#endif + printf("cl_loaddbdir: %s\n", cl_strerror(ret)); + close(fd); + exit(2); + } + } + + /* build engine */ +#ifdef SUPPORT_CLAMAV_V095 + if((ret = cl_engine_compile(engine))) { +#else + if((ret = cl_build(engine))) { +#endif + printf("Database initialization error: %s\n", cl_strerror(ret));; +#ifdef SUPPORT_CLAMAV_V095 + cl_engine_free(engine); +#else + cl_free(engine); +#endif + close(fd); + exit(2); + } + +#ifndef SUPPORT_CLAMAV_V095 + /* set up archive limits */ + memset(&limits, 0, sizeof(struct cl_limits)); + limits.maxfiles = 1000; /* max files */ + limits.maxfilesize = 10 * 1048576; /* maximum size of archived/compressed + * file (files exceeding this limit + * will be ignored) + */ + /*limits.maxreclevel = 5;*/ /* maximum recursion level for archives */ + /*limits.maxmailrec = 64;*/ /* maximum recursion level for mail files */ + /*limits.maxratio = 200;*/ /* maximum compression ratio */ + limits.archivememlim = 1; +#endif +#ifdef SUPPORT_CLAMAV_V095 + ret = cl_scandesc(fd, &virname, &size, engine, + CL_SCAN_STDOPT | CL_SCAN_ARCHIVE | CL_SCAN_MAIL | CL_SCAN_OLE2 | CL_SCAN_HTML); + printf("scandesc returned: %i\n", cl_strerror(ret));; +#else + ret = cl_scandesc(fd, &virname, &size, engine, &limits, + CL_SCAN_STDOPT | CL_ARCHIVE | CL_MAIL | CL_OLE2 | CL_SCAN_HTML); +#endif + } + + /* scan descriptor (with archive and mail scanning enabled) */ + close(fd); + fd = open(tmpnm,O_RDWR, S_IRWXU); + + spoolstdin(tmpnm, fd, ret, &virname, &bndrystore, opt); + + if((ret == 2)) +#ifdef SUPPORT_CLAMAV_V095 + cl_engine_free(engine); +#else + cl_free(engine); +#endif + + unlink(tmpnm); + + mexit(0); +} + +int spoolstdin(char *tmpnm, int fd, int ret, char **virname, const char **bndrystore, struct optstruct *opt) +{ + int bytes; + int i, j; + long int size = 0; + char buff[BUFFSIZE]; + char string[1000]; + char To[1000]; + char ReplyTo[1000]; + char DelivTo[1000]; + char From[1000]; + char ToR[1000]; + char FromR[1000]; + char boundary[1000]; + char host[256]; /* could be HOST_NAME_MAX+1 on POSIX 1003.1-2001 */ + struct timeval tv; + struct timezone tz; + char *s; + char *storage; + FILE *tmp; + int pcount = 0; + time_t starttime; + struct tm *tm; + char *out = NULL; + const char *format; + size_t out_length = 0; + time_t when; + int childpid; + + + strcpy (To, "To:"); + strcpy (From, "From:"); + strcpy (ReplyTo, "Reply-To:"); + strcpy (DelivTo, "Delivered"); + + gethostname(host, sizeof(host)); + gettimeofday(&tv, &tz); + time(&when); + tm = localtime(&when); + format = "%a, %_d %b %Y %H:%M:%S %z"; + do { + out_length += 200; + out = (char *) realloc (out, out_length); + out[0] = '\1'; + } while (strftime (out, out_length, format, tm) == 0 && out[0] != '\0'); + + if(ret == CL_VIRUS) { + if(!(optc(opt, 'f'))){ + + lseek(fd, 0, SEEK_SET); + if (!(tmp = fdopen(fd,"r"))){ + mprintf("@Can't open file %s\n", tmpnm); + return 54; + } + + mprintf_stdout = 1; + + while (fgets(string, sizeof(string), tmp)) { + if (strstr(string, To) && !(strstr(string, ReplyTo)) && !(strstr(string,DelivTo))){ + strcpy(To, string); + strcpy(ToR, string); + } + if (strstr(string, From)){ + strcpy(FromR, string); + } + if (strncmp(string, "\n", 1) == 0 && pcount != 0){ + break; + } + ++pcount; + } + + if (strcmp(To,"To:") == 0){ + strcat(To, " "); + strcat(To, getargl(opt, "admin")); + strcat(To, "\n"); + } + strcat(From, " "); + strcat(From, "KlamAV"); + strcat(From, "\n"); + strcat(ReplyTo, " "); + strcat(ReplyTo, "KlamAV"); + strcat(ReplyTo, "\n"); + mprintf("%s", From); + mprintf("%s", ReplyTo); + mprintf("%s", To); + mprintf("Subject: Virus %s found in attached mail by KlamAV.\n", *virname); + mprintf("Date: %s\n", out); + if(!(optl(opt, "quar"))){ + mprintf("MIME-Version: 1.0\n"); + mprintf("Content-Type: multipart/mixed;\n"); + mprintf(" boundary=\"----------=_%d\"\n",tv.tv_sec); + mprintf("X-Virus-Status: Yes\n"); + mprintf("X-Virus-Checker: Scanned by KlamAV %s on %s (virus-found %s);\n\t%s\n", + KLAMAV_VERSION, host, *virname, out); + mprintf("\n"); + mprintf("\n"); + mprintf("------------=_%d\n",tv.tv_sec); + } + mprintf("Content-Type: text/plain;\n"); + mprintf(" charset=\"us-ascii\"\n"); + mprintf("Content-Transfer-Encoding: quoted-printable\n"); + if((optl(opt, "quar"))){ + mprintf("X-Virus-Status: Yes\n"); + mprintf("X-Virus-Checker: Scanned by KlamAV %s on %s (virus-found %s);\n\t%s\n", + KLAMAV_VERSION, host, *virname, out); + mprintf("\n"); + mprintf("KlamAV anti-virus scanner has intercepted and quarantined a message addressed to you.\n"); + } + else + { + mprintf("\n"); + mprintf("KlamAV anti-virus scanner has detected a virus in the attached message.\n"); + } + mprintf("\n"); + mprintf("The following is a summary of the infected message:\n"); + mprintf("\n"); + mprintf("Virus name: %s\n", *virname); + mprintf("%s", FromR); + mprintf("%s", ToR); + mprintf("\n"); + mprintf("Please be aware that a virus spread by email normally forges the \n"); + mprintf("address of the sender. There is a good chance that the infected message\n"); + mprintf("was not received from the sender listed above. \n"); + mprintf("\n"); + if(!(optl(opt, "quar"))){ + mprintf("------------=_%d\n",tv.tv_sec); + mprintf("Content-Type: message/rfc822; x-virus-type=original\n"); + mprintf("Content-Description: Infected Message - Open At Your Own Risk\n"); + mprintf("Content-Disposition: inline\n"); + mprintf("Content-Transfer-Encoding: 8bit\n"); + mprintf("\n"); + fflush(stdout); + + lseek(fd, 0, SEEK_SET); + while((bytes = read(fd, buff, BUFFSIZE)) > 0) { + + if(write(1, buff, bytes) != bytes) { + close(fd); + return CL_EMEM; + } + } + mprintf("------------=_%d--\n",tv.tv_sec); + } + fflush(stdout); + + fclose(tmp); + }else{ + int bcnt = 0; + lseek(fd, 0, SEEK_SET); + if (!(tmp = fdopen(fd,"r"))){ + mprintf("@Can't open file %s\n", tmpnm); + return 54; + } + + mprintf_stdout = 1; + + while (fgets(string, sizeof(string), tmp)) { + if (*string == '\n') { + mprintf("X-Virus-Status: Yes\n"); + mprintf("X-Virus-Checker: Scanned by KlamAV %s on %s (virus-found %s);\n\t%s\n", + KLAMAV_VERSION, host, *virname, out); + break; + } + fputs(string, stdout); + } + while (fgets(string, sizeof(string), tmp)) { + fputs(string, stdout); + } + + if(optl(opt, "tag")) { + if (!(*bndrystore)) + printtag(); + } + fclose(tmp); + } + + if ( ( childpid=fork() ) == -1 ) { + perror("Failed to fork; quitting\n"); + exit(2); + } + + if ( childpid == 0 ) { + char *dialogmessage; + dialogmessage = malloc(77+sizeof(virname)+sizeof(FromR)+sizeof(ToR)+4+84+54); + sprintf(dialogmessage,"KlamAV has found an infected mail with the following details:\nVirus name: %s\n%s\n%s\nThe mail has been handled according to the filter rules set up in your mail client (probably put in your trash/deleted items directory).",*virname,FromR,ToR); + + if (setenv("PATH","/usr/local/sbin:/usr/sbin:/sbin:/usr/bin:/bin:/usr/local/bin:/opt/kde/bin",1) == 0) + execlp("kdialog", "kdialog", "--msgbox", dialogmessage, "--title", "Virus found by KlamAV:", NULL); + free(dialogmessage); + } + } else if(ret == CL_CLEAN) { + int bcnt = 0; + lseek(fd, 0, SEEK_SET); + if (!(tmp = fdopen(fd,"r"))){ + mprintf("@Can't open file %s\n", tmpnm); + return 54; + } + + mprintf_stdout = 1; + + while (fgets(string, sizeof(string), tmp)) { + if (*string == '\n') { + mprintf("X-Virus-Status: No\n"); + mprintf("X-Virus-Checker: Scanned by KlamAV %s on %s (no viruses);\n\t%s\n\n", + KLAMAV_VERSION, host, out); + break; + } + fputs(string, stdout); + } + while (fgets(string, sizeof(string), tmp)) { + fputs(string, stdout); + } + + if(optl(opt, "tag")) + if (!(*bndrystore)) + printtag(); + + fclose(tmp); + } else { + if(!printinfected) + mprintf("stdin: %s\n", cl_strerror(ret)); + } + return ret; +} + +void printtag(void) +{ + + mprintf_stdout = 1; + mprintf("\n"); + mprintf("----------------------------------------------------------------------------\n"); + mprintf(" This message was scanned by\n"); + mprintf(" ClamAV Open Source Anti-Virus Technology\n"); + mprintf(" using KlamAV\n"); + mprintf("\n"); + mprintf(" http://www.clamav.net\n"); + mprintf(" http://klamav.sf.net\n"); + mprintf("----------------------------------------------------------------------------\n"); + mprintf("\n"); + fflush(stdout); + +} + +void help(void) +{ + + mprintf_stdout = 1; + + mprintf("\n"); + mprintf(" KlamAV Mail Processing Client "KLAMAV_VERSION"\n"); + mprintf(" (c) 2004 Robert Hogan <[email protected]>\n"); + mprintf(" Uses a lot of code written by:\n"); + mprintf(" Tomasz Kojm <[email protected]>\n"); + mprintf(" Nigel Horne <[email protected]>\n"); + mprintf(" \n"); + mprintf(" --help -h Show help\n"); + mprintf(" --version -V Print version number and exit\n"); + mprintf(" -f Header Flag Only.\n"); + mprintf(" --tag Tag messages as scanned.\n"); + mprintf(" -d Location of virus definition database.\n"); + + exit(0); +} + +void startclamd(struct optstruct *opt) +{ + + int pfds[2]; + int childpid; + int fd; + char tmpnm[19]; + char conffile[31]; + struct stat sb; + FILE *tmp; + char *fullpath; + char cwd[200]; + char *scancmd; + + snprintf(tmpnm, sizeof(tmpnm), "klammailXXXXXX"); + mkstemp(tmpnm); + + fd = open(tmpnm,O_RDWR|O_CREAT, S_IRWXU); + + + lseek(fd, 0, SEEK_SET); + if (!(tmp = fdopen(fd,"w"))){ + mprintf("@Can't open file \n"); + } + + fprintf(tmp,"LocalSocket /tmp/KlamMailSock\n"); + fprintf(tmp,"MaxDirectoryRecursion 15\n"); + fprintf(tmp,"SelfCheck 900\n"); + //fprintf(tmp,"ScanArchive\n"); + if(optc(opt, 'd')) { + stat(getargc(opt, 'd'), &sb); + switch(sb.st_mode & S_IFMT) { + case S_IFREG: + fprintf(tmp,"DatabaseDirectory %s\n",getargc(opt, 'd')); + break; + case S_IFDIR: + fprintf(tmp,"DatabaseDirectory %s\n",getargc(opt, 'd')); + break; + default: + mprintf("@%s: Not supported database file type\n", getargc(opt, 'd')); + break; + } + }else{ + fprintf(tmp,"DatabaseDirectory /usr/local/share/clamav\n"); + } + //fprintf(tmp,"ScanMail\n"); + if ((strstr(cl_retver(), "0.8")) || (strstr(cl_retver(), "0.7"))) + fprintf(tmp,"FixStaleSocket\n"); + else +#ifdef SUPPORT_CLAMAV_V095 + fprintf(tmp,"FixStaleSocket yes\n"); +#else + fprintf(tmp,"FixStaleSocket TRUE\n"); +#endif + + fflush(tmp); + + fclose(tmp); + close(fd); + + fullpath = (char*) mcalloc(200 + strlen(tmpnm) + 10, sizeof(char)); + + if(!getcwd(cwd, 200)) { + mprintf("@Can't get absolute pathname of current working directory.\n"); + return; + } + sprintf(fullpath, "%s/%s", cwd, tmpnm); + + + if ( pipe(pfds) == -1 ) { + perror("Failed to create pipe; quitting\n"); + exit(1); + } + + if ( ( childpid=fork() ) == -1 ) { + perror("Failed to fork; quitting\n"); + exit(2); + } + + /* instead of STDIN -> clamdmail -> STDOUT, do + STDIN -> clamdmail -> spamc -> STDOUT + (previously incorrect as STDIN -> spamc -> clamdmail -> STDOUT) + */ + if ( childpid == 0 ) { + if (setenv("PATH","/usr/local/sbin:/usr/sbin:/sbin:/usr/bin:/bin:/usr/local/bin:/opt/kde/bin",1) == 0) + execlp("clamd", "clamd", "-c", fullpath, NULL); + } + + sleep(1); + unlink(fullpath); +} |