summaryrefslogtreecommitdiffstats
path: root/src/klammail/clamdmail.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/klammail/clamdmail.c')
-rwxr-xr-xsrc/klammail/clamdmail.c626
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);
+}