Hi,

My methodology for integrating clamscan with qmail is fairly brutal so I am
looking for some feedback - I am sure there is a better way to do it.

The patch below allows you to issue the command:

clamscan --mbox --spool --quiet < somemail

If it's clean it will spit it out again on stdout, if infected it will spit
 it out again on stdout as an attachment to a warning mail fom clamscan.

Files changed are: mbox.c, options.c and manager.c.

If the --spool option is detected, clamscan writes out stdin to a tmp file
 and passes the name to mbox.c. When it gets the return value it calls
 spoolstdin and if the return code is clean it spools the mail straight out,
 if not it hacks a warning message on to the top of it.

The most glaringly obvious drawback presented by what I've done is that it
requires a change to mbox.c in the libclamav library and the way it is
called.  I have had to alter mbox's otherwise very tolerant code to accept
'From:' as well as 'From ' in the first line of the mail and also add an
argument to the function description to allow it to read from a  file name
rather than just eat up stdin. This change works fine with a mail being
spooled direct from a client but I don't know what the consequences are for
scanning mboxes proper (probably not very severe ?!).

If you were to apply the patch below to clamscan, you could then use a
modified version of the qmail-spamc code supplied with SpamAssassin (sample
at the very bottom of the mail) to exec spamc and then clamscan for each mail
coming through your qmail installation (without loading lots of perl
libraries.)

Again, the patch is really just to demonstrate the way I've thought of doing
it (the warning message stuff is harcoded). Ultimately, it should be a
standalone executable just doing what the patch wants to do so as to improve
performance and probably with it's own hacked libclamav.


Am I completely off the mark here?

To patch:
cd /usr/local/src
patch -p4 < filename



--- /usr/local/src/clamav-0.54-orig/libclamav/mbox.c    Thu Apr 10 12:05:05 2003
+++ /usr/local/src/clamav-0.54/libclamav/mbox.c Thu Apr 10 12:03:10 2003
@@ -119,7 +119,7 @@
  * signal is received
  */
 int
-mbox(const char *dir)
+mbox(const char *dir, FILE *fd)
 {
        int retcode;
        char buffer[LINE_LENGTH];
@@ -151,13 +151,13 @@
        /*
         * handle more than one message in the filter. Probably a waste of time
         */
-       while(fgets(buffer, sizeof(buffer), stdin) != NULL) {
+       while(fgets(buffer, sizeof(buffer), fd) != NULL) {
 #ifdef CL_DEBUG
                /*cli_dbgmsg("read: %s", buffer);*/
 #endif
                if(first)
                        /* sanity check */
-                       if(strncmp(buffer, "From ", 5) != 0) {
+                       if((strncmp(buffer, "From ", 5) != 0) && (strncmp(buffer, 
"From:", 5)
!=0)) {
                                cli_errmsg("Not a valid mail message");
                                retcode = -1;
                                break;
@@ -183,7 +183,7 @@
                        for(ptr = strtok_r(buffer, ";\r\n", &strptr); ptr; ptr = 
strtok_r(NULL,
":\r\n", &strptr))
                                messageAddArgument(m, ptr);

-               } else if((!inHeader) && lastLineWasEmpty && (strncmp(buffer, "From ", 
5)
== 0)) {
+               } else if((!inHeader) && lastLineWasEmpty && ((strncmp(buffer, "From 
", 5)
== 0) || (strncmp(buffer, "From:", 5) == 0))) {
                        /*
                         * New message, save the previous message, if any
                         */
--- /usr/local/src/clamav-0.54-orig/clamscan/options.c  Thu Apr 10 12:05:05
2003
+++ /usr/local/src/clamav-0.54/clamscan/options.c       Thu Apr 10 12:03:26 2003
@@ -70,6 +70,7 @@

            {"mbox", 0, 0, 0},
            {"stdout", 0, 0, 0},
+           {"spool", 0, 0, 0},
            {"unzip", 2, 0, 0},
            {"unrar", 2, 0, 0},
            {"unace", 2, 0, 0},
--- /usr/local/src/clamav-0.54-orig/clamscan/manager.c  Thu Apr 10 12:05:05
2003
+++ /usr/local/src/clamav-0.54/clamscan/manager.c       Thu Apr 10 12:03:34 2003
@@ -1,4 +1,4 @@
-/*
+ /*
  *  Copyright (C) 2002 Tomasz Kojm <[EMAIL PROTECTED]>
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -19,6 +19,7 @@
  */

 #include <stdio.h>
+#include <sys/time.h>
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
@@ -177,6 +178,9 @@
        if(optl(opt, "mbox")) {
                const char *tmpdir;
                char *dir;
+               char *virname;
+               FILE *tmp;
+               int fd;

                /* [EMAIL PROTECTED]: BeOS */
 #if !defined(C_CYGWIN) && !defined(C_BEOS)
@@ -211,10 +215,45 @@
                if(user)
                        chown(dir, user->pw_uid, user->pw_gid);

+               if(optl(opt, "spool")) {
+                       int bytes, ret = CL_CLEAN;
+                       long int size = 0;
+                       char buff[BUFFSIZE];
+
+
+                       if((tmp = tmpfile()) == NULL) {
+                               cli_dbgmsg("Can't generate tmpfile().\n");
+                               return CL_ETMPFILE;
+                       }
+                       fd = fileno(tmp);
+
+                       while((bytes = read(0, buff, BUFFSIZE)) > 0) {
+
+                               if(write(fd, buff, bytes) != bytes) {
+                                       cli_dbgmsg("Gzip -> Can't write() file.\n");
+                                       close(fd);
+                                       return CL_EGZIP;
+                               }
+                       }
+
+                       if(fsync(fd) == -1) {
+                               cli_dbgmsg("fsync() failed for descriptor %d\n", fd);
+                               close(fd);
+                               return CL_EFSYNC;
+                       }
+                       if (!(tmp = fdopen(fd,"r"))){
+                               mprintf("@Can't open file %s\n", fd);
+                               return 54;
+                       }
+                       lseek(fd, 0, SEEK_SET);
+
+               }else{
+                       tmp = stdin;
+               }
                /*
                 * Extract the attachments into the temporary directory
                 */
-               ret = mbox(dir);
+               ret = mbox(dir, tmp);

                if(ret == 0) {
                        /* fix permissions of extracted files */
@@ -223,6 +262,9 @@
                        if(ret == 0) /* execute successful */
                                ret = treewalk(dir, trie, threads, user, opt, limits);

+                       if(optl(opt, "spool")) {
+                               ret = spoolstdin(fd, ret);
+                       }
                        /* remove the directory - as clamav */
                        clamav_rmdirs(dir);

@@ -767,6 +809,108 @@
     } else if(ret == CL_CLEAN) {
        if(!printinfected)
            mprintf("stdin: OK\n");
+    } else
+       if(!printinfected)
+           mprintf("stdin: %s\n", cl_perror(ret));
+
+    return ret;
+}
+
+int spoolstdin(int fd, int ret)
+{
+       int bytes;
+       int i, j;
+       char *virname;
+       long int size = 0;
+       char buff[BUFFSIZE];
+       char string[100];
+       char To[100];
+       char ReplyTo[100];
+       char clammsg[100];
+       char MsgID[100];
+       char boundary[100];
+       char date[100];
+       struct timeval tv;
+       char *s;
+
+       FILE *tmp;
+
+       strcpy (To, "To:");
+       strcpy (ReplyTo, "Reply-To:");
+       strcpy (MsgID, "Message-Id:");
+       strcpy (date, "Date:");
+       strcpy (clammsg, ".clamscan>");
+
+
+    if(ret == CL_VIRUS) {
+       lseek(fd, 0, SEEK_SET);
+       if (!(tmp = fdopen(fd,"r"))){
+       mprintf("@Can't open file %s\n", fd);
+       return 54;
+       }
+       while (fgets(string, sizeof(string), tmp)) {
+               if (strstr(string, To) && !(strstr(string, ReplyTo))){
+                       strcpy(To, string);
+               }
+               if (strstr(string, date)){
+                       strcpy(date, string);
+               }
+               if (strstr(string, MsgID)){
+                       strcpy(MsgID, string);
+                       strcpy(strstr(MsgID,">"), clammsg);
+                       break;
+               }
+       }
+       gettimeofday(&tv,(struct timezone *) 0);
+       printf("From: [EMAIL PROTECTED]");
+       printf("%s", To);
+       printf("Subject: Virus found in attached mail by Clamscan.\n");
+       printf("%s", date);
+       printf("MIME-Version: 1.0\n");
+       printf("Content-Type: multipart/mixed;\n");
+       printf("  boundary=\"----------=_%d\"\n",tv.tv_sec);
+       printf("%s\n", MsgID);
+       printf("\n");
+       printf("\n");
+       printf("------------=_%d\n",tv.tv_sec);
+       printf("Content-Type: text/plain\n");
+       printf("charset=\"us-ascii\"\n");
+       printf("Content-Transfer-Encoding: quoted-printable\n");
+       printf("\n");
+       printf("This mail contains a virus. It has been included as an
attachment.\n");
+       printf("\n");
+       printf("------------=_%d\n",tv.tv_sec);
+       printf("Content-Type: message/rfc822; x-virus-type=original\n");
+       printf("Content-Description: original message before clamscan\n");
+       printf("Content-Disposition: inline\n");
+       printf("Content-Transfer-Encoding: 8bit\n");
+       printf("\n");
+       fflush(stdout);
+
+     lseek(fd, 0, SEEK_SET);
+     while((bytes = read(fd, buff, BUFFSIZE)) > 0) {
+
+       if(write(1, buff, bytes) != bytes) {
+           cli_dbgmsg("Gzip -> Can't write() file.\n");
+           close(fd);
+           return CL_EMEM;
+       }
+     }
+       printf("------------=_%d--\n",tv.tv_sec);
+       fflush(stdout);
+       fclose(tmp);
+    } else if(ret == CL_CLEAN) {
+       if(!printinfected)
+           mprintf("stdin: OK\n");
+    lseek(fd, 0, SEEK_SET);
+     while((bytes = read(fd, buff, BUFFSIZE)) > 0) {
+
+       if(write(1, buff, bytes) != bytes) {
+           cli_dbgmsg("Gzip -> Can't write() file.\n");
+           close(fd);
+           return CL_EMEM;
+       }
+     }
     } else
        if(!printinfected)
            mprintf("stdin: %s\n", cl_perror(ret));





modified qmail-spam.c


#include <stdio.h>
#include <fcntl.h>
#include <sys/wait.h>

main()
{
  int dummy2, dummy1, i;
  int pipefd[2];
  int pipefdb[2];
  int childpid;
  int childpidb;
  int wstat;
  int wstatb;
  i = pipe(pipefd);
  if (i < 0) {
    perror("headsort: pipe");
    exit(1);
  }
  i = pipe(pipefdb);
  if (i < 0) {
    perror("headsort: pipe");
    exit(1);
  }

  if (( childpid=fork() ) == 0) {
    if (dup2(pipefd[1], 1) != 1) {
      perror("headsort: dup2(pipefd[1])");
      exit(1);
    }
    close(pipefd[0]);
    close(pipefd[1]);
    execlp("clamscan", "clamscan", "--quiet", "--mbox", "--spool", "-",
 NULL); perror("headsort: exec sort failed");
    exit(1);
  }else if (( childpidb=fork() ) == 0) {
    if (dup2(pipefd[0], 0) != 0) {
      perror("headsort: dup2(pipefd[1])");
      exit(1);
    }
    if (dup2(pipefdb[1], 1) != 1) {
      perror("headsort: dup2(pipefd[1])");
      exit(1);
    }
    close(pipefd[0]);
    close(pipefd[1]);
    close(pipefdb[0]);
    close(pipefdb[1]);
    waitpid(childpid,&wstat,0);
    execlp("spamc", "spamc", NULL);
    perror("headsort: exec head failed");
    exit(1);
  } else {
    if (dup2(pipefdb[0], 0) != 0) {
      perror("headsort: dup2(pipefd[0])");
      exit(1);
    }
    close(pipefd[1]);
    close(pipefd[0]);
    close(pipefdb[0]);
    close(pipefdb[1]);
    waitpid(childpidb,&wstatb,0);
     execlp("qmail-queue", "qmail-queue", NULL);
  }
}

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]


Reply via email to