Back to index

citadel  8.12
serv_virus.c
Go to the documentation of this file.
00001 /*
00002  * This module allows Citadel to use clamd to filter incoming messages
00003  * arriving via SMTP.  For more information on clamd, visit
00004  * http://clamav.net (the ClamAV project is not in any way
00005  * affiliated with the Citadel project).
00006  *
00007  * Copyright (c) 1987-2012 by the citadel.org team
00008  *
00009  *  This program is open source software; you can redistribute it and/or modify
00010  *  it under the terms of the GNU General Public License version 3.
00011  *  
00012  *  
00013  *
00014  *  This program is distributed in the hope that it will be useful,
00015  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  *  GNU General Public License for more details.
00018  *
00019  *  
00020  *  
00021  *  
00022  */
00023 
00024 #define CLAMD_PORT       "3310"
00025 
00026 #include "sysdep.h"
00027 #include <stdlib.h>
00028 #include <unistd.h>
00029 #include <stdio.h>
00030 #include <fcntl.h>
00031 #include <signal.h>
00032 #include <pwd.h>
00033 #include <errno.h>
00034 #include <sys/types.h>
00035 
00036 #if TIME_WITH_SYS_TIME
00037 # include <sys/time.h>
00038 # include <time.h>
00039 #else
00040 # if HAVE_SYS_TIME_H
00041 #  include <sys/time.h>
00042 # else
00043 #  include <time.h>
00044 # endif
00045 #endif
00046 
00047 #include <sys/wait.h>
00048 #include <string.h>
00049 #include <limits.h>
00050 #include <sys/socket.h>
00051 #include <libcitadel.h>
00052 #include "citadel.h"
00053 #include "server.h"
00054 #include "citserver.h"
00055 #include "support.h"
00056 #include "config.h"
00057 #include "control.h"
00058 #include "user_ops.h"
00059 #include "database.h"
00060 #include "msgbase.h"
00061 #include "internet_addressing.h"
00062 #include "domain.h"
00063 #include "clientsocket.h"
00064 
00065 
00066 #include "ctdl_module.h"
00067 
00068 
00069 
00070 /*
00071  * Connect to the clamd server and scan a message.
00072  */
00073 int clamd(struct CtdlMessage *msg) {
00074        int sock = (-1);
00075        int streamsock = (-1);
00076        char clamhosts[SIZ];
00077        int num_clamhosts;
00078        char buf[SIZ];
00079         char hostbuf[SIZ];
00080         char portbuf[SIZ];
00081        int is_virus = 0;
00082        int clamhost;
00083        StrBuf *msgtext;
00084        CitContext *CCC;
00085 
00086        /* Don't care if you're logged in.  You can still spread viruses.
00087         */
00088        /* if (CC->logged_in) return(0); */
00089 
00090        /* See if we have any clamd hosts configured */
00091        num_clamhosts = get_hosts(clamhosts, "clamav");
00092        if (num_clamhosts < 1) return(0);
00093 
00094        /* Try them one by one until we get a working one */
00095         for (clamhost=0; clamhost<num_clamhosts; ++clamhost) {
00096                 extract_token(buf, clamhosts, clamhost, '|', sizeof buf);
00097                 syslog(LOG_INFO, "Connecting to clamd at <%s>\n", buf);
00098 
00099                 /* Assuming a host:port entry */ 
00100                 extract_token(hostbuf, buf, 0, ':', sizeof hostbuf);
00101                 if (extract_token(portbuf, buf, 1, ':', sizeof portbuf)==-1)
00102                   /* Didn't specify a port so we'll try the psuedo-standard 3310 */
00103                   sock = sock_connect(hostbuf, CLAMD_PORT);
00104                 else
00105                   /* Port specified lets try connecting to it! */
00106                   sock = sock_connect(hostbuf, portbuf);
00107 
00108                 if (sock >= 0) syslog(LOG_DEBUG, "Connected!\n");
00109         }
00110 
00111        if (sock < 0) {
00112               /* If the service isn't running, just pass the mail
00113                * through.  Potentially throwing away mails isn't good.
00114                */
00115               return(0);
00116        }
00117        CCC=CC;
00118        CCC->SBuf.Buf = NewStrBuf();
00119        CCC->sMigrateBuf = NewStrBuf();
00120        CCC->SBuf.ReadWritePointer = NULL;
00121 
00122        /* Command */
00123        syslog(LOG_DEBUG, "Transmitting STREAM command\n");
00124        sprintf(buf, "STREAM\r\n");
00125        sock_write(&sock, buf, strlen(buf));
00126 
00127        syslog(LOG_DEBUG, "Waiting for PORT number\n");
00128         if (sock_getln(&sock, buf, sizeof buf) < 0) {
00129                 goto bail;
00130         }
00131 
00132         syslog(LOG_DEBUG, "<%s\n", buf);
00133        if (strncasecmp(buf, "PORT", 4)!=0) {
00134                goto bail;
00135        }
00136 
00137         /* Should have received a port number to connect to */
00138        extract_token(portbuf, buf, 1, ' ', sizeof portbuf);
00139 
00140        /* Attempt to establish connection to STREAM socket */
00141         streamsock = sock_connect(hostbuf, portbuf);
00142 
00143        if (streamsock < 0) {
00144               /* If the service isn't running, just pass the mail
00145                * through.  Potentially throwing away mails isn't good.
00146                */
00147               FreeStrBuf(&CCC->SBuf.Buf);
00148               FreeStrBuf(&CCC->sMigrateBuf);
00149               return(0);
00150         }
00151        else {
00152                syslog(LOG_DEBUG, "STREAM socket connected!\n");
00153        }
00154 
00155 
00156 
00157        /* Message */
00158        CC->redirect_buffer = NewStrBufPlain(NULL, SIZ);
00159        CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_ALL, 0, 1, 0);
00160        msgtext = CC->redirect_buffer;
00161        CC->redirect_buffer = NULL;
00162 
00163        sock_write(&streamsock, SKEY(msgtext));
00164        FreeStrBuf(&msgtext);
00165 
00166        /* Close the streamsocket connection; this tells clamd
00167         * that we're done.
00168         */
00169        if (streamsock != -1)
00170               close(streamsock);
00171        
00172        /* Response */
00173        syslog(LOG_DEBUG, "Awaiting response\n");
00174         if (sock_getln(&sock, buf, sizeof buf) < 0) {
00175                 goto bail;
00176         }
00177         syslog(LOG_DEBUG, "<%s\n", buf);
00178        if (strncasecmp(buf, "stream: OK", 10)!=0) {
00179               is_virus = 1;
00180        }
00181 
00182        if (is_virus) {
00183               if (msg->cm_fields['0'] != NULL) {
00184                      free(msg->cm_fields['0']);
00185               }
00186               msg->cm_fields['0'] = strdup("message rejected by virus filter");
00187        }
00188 
00189 bail:  close(sock);
00190        FreeStrBuf(&CCC->SBuf.Buf);
00191        FreeStrBuf(&CCC->sMigrateBuf);
00192        return(is_virus);
00193 }
00194 
00195 
00196 
00197 CTDL_MODULE_INIT(virus)
00198 {
00199        if (!threading)
00200        {
00201               CtdlRegisterMessageHook(clamd, EVT_SMTPSCAN);
00202        }
00203        
00204        /* return our module name for the log */
00205         return "virus";
00206 }