Applied the patch to add dtx's ClamAV integration module.
authorArt Cancro <ajc@citadel.org>
Sun, 5 Oct 2008 01:20:16 +0000 (01:20 +0000)
committerArt Cancro <ajc@citadel.org>
Sun, 5 Oct 2008 01:20:16 +0000 (01:20 +0000)
citadel/docs/citadel.html
citadel/modules/serv_virus.c [new file with mode: 0644]
citadel/tuiconfig.c
webcit/inetconf.c
webcit/static/t/aide_inetconf.html

index e5e577a45ede9756b58d6449456b04532f31ae6b..bb028372bfd959cbb564e4bb2f092131efa56bf6 100644 (file)
@@ -140,6 +140,12 @@ others<br>
       <td valign="top"><i>IGnet protocol design<br>
       </i></td>
     </tr>
+    <tr>
+      <td valign="top">Edward Flick<br>
+      </td>
+      <td valign="top"><i>ClamAV integration module<br>
+      </i></td>
+    </tr>
   </tbody>
 </table>
 </div>
diff --git a/citadel/modules/serv_virus.c b/citadel/modules/serv_virus.c
new file mode 100644 (file)
index 0000000..258738c
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * $Id$
+ *
+ * This module allows Citadel to use clamd to filter incoming messages
+ * arriving via SMTP.  For more information on clamd, visit
+ * http://clamav.net (the ClamAV project is not in any way
+ * affiliated with the Citadel project).
+ */
+
+#define CLAMD_PORT       "3310"
+
+#include "sysdep.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <pwd.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#include <sys/wait.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/socket.h>
+#include <libcitadel.h>
+#include "citadel.h"
+#include "server.h"
+#include "citserver.h"
+#include "support.h"
+#include "config.h"
+#include "control.h"
+#include "room_ops.h"
+#include "user_ops.h"
+#include "policy.h"
+#include "database.h"
+#include "msgbase.h"
+#include "internet_addressing.h"
+#include "domain.h"
+#include "clientsocket.h"
+
+
+#include "ctdl_module.h"
+
+
+
+/*
+ * Connect to the clamd server and scan a message.
+ */
+int clamd(struct CtdlMessage *msg) {
+       int sock = (-1);
+       int streamsock = (-1);
+       char clamhosts[SIZ];
+       int num_clamhosts;
+       char buf[SIZ];
+        char hostbuf[SIZ];
+        char portbuf[SIZ];
+       int is_virus = 0;
+       int clamhost;
+       char *msgtext;
+       size_t msglen;
+
+       /* Don't care if you're logged in.  You can still spread viruses.
+        */
+       /* if (CC->logged_in) return(0); */
+
+       /* See if we have any clamd hosts configured */
+       num_clamhosts = get_hosts(clamhosts, "clamav");
+       if (num_clamhosts < 1) return(0);
+
+       /* Try them one by one until we get a working one */
+        for (clamhost=0; clamhost<num_clamhosts; ++clamhost) {
+                extract_token(buf, clamhosts, clamhost, '|', sizeof buf);
+                CtdlLogPrintf(CTDL_INFO, "Connecting to clamd at <%s>\n", buf);
+
+                /* Assuming a host:port entry */ 
+                extract_token(hostbuf, buf, 0, ':', sizeof hostbuf);
+                if (extract_token(portbuf, buf, 1, ':', sizeof portbuf)==-1)
+                  /* Didn't specify a port so we'll try the psuedo-standard 3310 */
+                  sock = sock_connect(hostbuf, CLAMD_PORT, "tcp");
+                else
+                  /* Port specified lets try connecting to it! */
+                  sock = sock_connect(hostbuf, portbuf, "tcp");
+
+                if (sock >= 0) CtdlLogPrintf(CTDL_DEBUG, "Connected!\n");
+        }
+
+       if (sock < 0) {
+               /* If the service isn't running, just pass the mail
+                * through.  Potentially throwing away mails isn't good.
+                */
+               return(0);
+       }
+
+       /* Command */
+       CtdlLogPrintf(CTDL_DEBUG, "Transmitting STREAM command\n");
+       sprintf(buf, "STREAM\r\n");
+       sock_write(sock, buf, strlen(buf));
+
+       CtdlLogPrintf(CTDL_DEBUG, "Waiting for PORT number\n");
+        if (sock_getln(sock, buf, sizeof buf) < 0) {
+                goto bail;
+        }
+
+        CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
+       if (strncasecmp(buf, "PORT", 4)!=0) {
+               goto bail;
+       }
+
+        /* Should have received a port number to connect to */
+       extract_token(portbuf, buf, 1, ' ', sizeof portbuf);
+
+       /* Attempt to establish connection to STREAM socket */
+        streamsock = sock_connect(hostbuf, portbuf, "tcp");
+
+       if (streamsock < 0) {
+               /* If the service isn't running, just pass the mail
+                * through.  Potentially throwing away mails isn't good.
+                */
+               return(0);
+        }
+       else {
+               CtdlLogPrintf(CTDL_DEBUG, "STREAM socket connected!\n");
+       }
+
+
+
+       /* Message */
+       CC->redirect_buffer = malloc(SIZ);
+       CC->redirect_len = 0;
+       CC->redirect_alloc = SIZ;
+       CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_ALL, 0, 1, 0);
+       msgtext = CC->redirect_buffer;
+       msglen = CC->redirect_len;
+       CC->redirect_buffer = NULL;
+       CC->redirect_len = 0;
+       CC->redirect_alloc = 0;
+
+       sock_write(streamsock, msgtext, msglen);
+       free(msgtext);
+
+       /* Close the streamsocket connection; this tells clamd
+        * that we're done.
+        */
+       close(streamsock);
+       
+       /* Response */
+       CtdlLogPrintf(CTDL_DEBUG, "Awaiting response\n");
+        if (sock_getln(sock, buf, sizeof buf) < 0) {
+                goto bail;
+        }
+        CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
+       if (strncasecmp(buf, "stream: OK", 10)!=0) {
+               is_virus = 1;
+       }
+
+       if (is_virus) {
+               if (msg->cm_fields['0'] != NULL) {
+                       free(msg->cm_fields['0']);
+               }
+               msg->cm_fields['0'] = strdup("message rejected by virus filter");
+       }
+
+bail:  close(sock);
+       return(is_virus);
+}
+
+
+
+CTDL_MODULE_INIT(virus)
+{
+       if (!threading)
+       {
+               CtdlRegisterMessageHook(clamd, EVT_SMTPSCAN);
+       }
+       
+       /* return our Subversion id for the Log */
+        return "$Id$";
+}
index 7857847226b93ff2b563711f044aa424eea38ce8..15461953e83da2e54421dfd4964611daa49fa163 100644 (file)
@@ -390,7 +390,8 @@ void get_inet_rec_type(CtdlIPC *ipc, char *buf) {
        keyopt(" <4> SpamAssassin   (Address of SpamAssassin server)\n");
        keyopt(" <5> RBL            (domain suffix of spam hunting RBL)\n");
        keyopt(" <6> masq domains   (Domains as which users are allowed to masquerade)\n");
-       sel = intprompt("Which one", 1, 1, 6);
+       keyopt(" <7> ClamAV         (Address of ClamAV clamd server)\n");
+       sel = intprompt("Which one", 1, 1, 7);
        switch(sel) {
                case 1: strcpy(buf, "localhost");
                        return;
@@ -404,6 +405,8 @@ void get_inet_rec_type(CtdlIPC *ipc, char *buf) {
                        return;
                case 6: strcpy(buf, "masqdomain");
                        return;
+               case 7: strcpy(buf, "clamav");
+                       return;
        }
 }
 
index cc04419c73031398e40094d87dec1c544a04d690..f865e7607fd9311df66604310591850584dbed34 100644 (file)
@@ -26,6 +26,7 @@ void display_inetconf(void)
                ic_rbl,
                ic_spamass,
                ic_masq,
+               ic_clamav,
                ic_max
        };
 
@@ -42,6 +43,7 @@ void display_inetconf(void)
        ic_keyword[3] = "rbl";
        ic_keyword[4] = "spamassassin";
        ic_keyword[5] = "masqdomain";
+       ic_keyword[6] = "clamav";
 
        ic_boxtitle[0] = _("Local host aliases");
        ic_boxtitle[1] = _("Directory domains");
@@ -49,6 +51,7 @@ void display_inetconf(void)
        ic_boxtitle[3] = _("RBL hosts");
        ic_boxtitle[4] = _("SpamAssassin hosts");
        ic_boxtitle[5] = _("Masqueradable domains");
+       ic_boxtitle[6] = _("ClamAV clamd hosts");
 
        ic_desc[0] = _("(domains for which this host receives mail)");
        ic_desc[1] = _("(domains mapped with the Global Address Book)");
@@ -56,6 +59,7 @@ void display_inetconf(void)
        ic_desc[3] = _("(hosts running a Realtime Blackhole List)");
        ic_desc[4] = _("(hosts running the SpamAssassin service)");
        ic_desc[5] = _("(Domains as which users are allowed to masquerade)");
+       ic_desc[6] = _("(hosts running the ClamAV clamd service)");
 
        for (i=0; i<ic_max; ++i) {
                ic_spec[i] = strdup("");
@@ -217,6 +221,7 @@ typedef enum _e_cfg {
        ic_rbl,
        ic_spamass,
        ic_masq,
+       ic_clamav,
        ic_max
 } ECfg;
 
@@ -233,7 +238,8 @@ ConstStrBuf CfgNames[] = {
        { HKEY("smarthost") },
        { HKEY("rbl") },
        { HKEY("spamassassin") },
-       { HKEY("masqdomain") }
+       { HKEY("masqdomain") },
+       { HKEY("clamav") }
 };
 
        
index fbccd2884d35dcae00aeed2ee519e9275f7d7dc0..4043900ffca9e599a776a95672552633d6177376 100644 (file)
@@ -21,6 +21,7 @@
 
 <?DOBOXED("aide_inet_rbldns", "subject_inet_rbldns")><br />
 <?DOBOXED("aide_inet_spamass", "subject_inet_spamass")><br />
+<?DOBOXED("aide_inet_clamav", "subject_inet_clamav")><br />
 <?DOBOXED("aide_inet_masqdomains", "subject_inet_masqdomains")>