#include "mfapi.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h> /* for Solaris */
#include <sys/types.h>
#include <sys/stat.h>
#include <sysexits.h>
#include <unistd.h>
#include <syslog.h>
#include <signal.h>
#include <fcntl.h>
#ifdef __FreeBSD__
#include <sys/wait.h>
#else
#include <wait.h>
#endif
#include <errno.h>
#include <sys/param.h>
#include <sys/time.h>
#include <dirent.h>
#include <ctype.h>
#include <pwd.h>
#include <grp.h>
#include <sys/resource.h>
static char *rcsid[]={
"$Author: wcolburn $",
"$Date: 2003/06/30 16:15:42 $",
"$Revision: 3.30 $",
"$Source: /system/milter/RCS/antivirus.c,v $",
'\0'
};
static char *versionid="$Id: antivirus.c,v 3.30 2003/06/30 16:15:42 wcolburn Exp $";
static char *webpage="http://www.nmt.edu/~wcolburn/antivirus/";
static char *copyright="Copyright (c) 2001 New Mexico Institute of Mining and Technology. All rights reserved.";
/*
** I *DESPERATLY* need to keep track of my contributors!
**
** William D. Colburn (original author)
** Thomas Lussnig (also peeled off his own version)
** James R. Stahr
** Tim Toolan
** Steve Barber
** Sebastien VAUTHEROT
** Pierre DAVID
** Emmanuel Collignon
*/
/*
** compilestamp and sendmailstamp come from the Makefile
*/
extern char *compilestamp;
extern char *sendmailstamp;
/*
** errno comes from errno.h and libc
*/
extern int errno;
/*
** these can be changed from the configure file
*/
static char *HOMEDIR=NULL;
static char *BASEDIR=NULL;
static char *AVSCANDIR =NULL;
static char *QUARANTINE=NULL;
static char *AVSCANNER=NULL;
static char *AVSCANARGS=NULL;
static char *UNPACKER=NULL;
static char *SAFEUSER=NULL;
static char *SAFEGROUP=NULL;
static char *PIDFILE=NULL;
static char *SOCKETNAME=NULL;
static char *TIMEOUT=NULL;
static char *AVPRODUCT=NULL;
static char *AVFAILACTION=NULL;
static char *VIRUSACTION=NULL;
static char *FORMAT=NULL;
static sfsistat avfailcode=0;
static int purgevirus=0;
static int skipwords=0;
static int ignorerror2=0;
static char *avargs[]={NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
/*
** You might want to change these
*/
static char *badext[]={".com",".scr",".vbs",".pif"};
/*
** these are the default values
*/
#define CONF_HOMEDIR "/system/av"
#define CONF_BASEDIR "/system/av/abattoir"
#define CONF_AVSCANDIR "/system/av/decullotage"
#define CONF_QUARANTINE "/system/av/oubliette"
#define CONF_AVSCANNER "/system/av/decullotage/uvscan"
#define CONF_AVSCANARGS ""
#define CONF_UNPACKER "/system/bin/ripmime"
#define CONF_SAFEUSER "nobody"
#define CONF_SAFEGROUP "nobody"
#define CONF_PIDFILE "/system/av/etc/antivirus.pid"
#define CONF_SOCKETNAME "local:/system/av/etc/antivirus.sock"
#define CONF_TIMEOUT ""
#define CONF_AVPRODUCT "mcafee"
#define CONF_AVFAILACTION "tempfail"
#define CONF_VIRUSACTION "quarantine"
/*
** This one is for Mcafee
*/
#define FORMAT_MCAFEE " Found the %s virus !!!"
#define SCANARGS_MCAFEE "--unzip"
/*
** This one is for FSAV (f-prot)
*/
#define FORMAT_FSAV "%*s infection: %s"
/*
** This one is for Sophos
*/
#define FORMAT_SOPHOS ">>> Virus '%[^']s' found in file %*s"
/*
** This one is for clamav
*/
#define FORMAT_CLAMAV " %s FOUND"
#define SCANARGS_CLAMAV "--disable-summary"
/*
** this can be given on the command line
*/
static char *configfile=NULL;
#define DEFAULTCONFIGFILE "/usr/local/etc/antivirus.conf"
struct mlfiPriv
{
char *qid;
char *datafile;
FILE *datafp;
char *workdir;
int headers;
int bodyblocks;
int bodysize;
int hasvirus;
char *viruses;
int childpid;
int childstatus;
FILE *childfp;
DIR *dir;
char *scripts;
#ifdef ANTIVIRUSTIMING
struct timeval start;
#endif /* ANTIVIRUSTIMING */
};
/*
** this is a handy shortcut
*/
#define MLFIPRIV ((struct mlfiPriv *) smfi_getpriv(ctx))
/*
** debugging sucks
*/
static int debug=0;
/*
** forward definitions
*/
void cleanup(SMFICTX *);
sfsistat mlfi_envfrom(SMFICTX *ctx, char **argv)
{
struct mlfiPriv *priv = MLFIPRIV;
char *qid;
char buf[512];
if (debug)
syslog(LOG_ERR,"mlfi_envfrom(0x%x, %s)",(int)ctx,argv[0]);
if (priv)
syslog(LOG_ERR,"mlfi_envfrom(0x%x, %s) given priv 0x%x",
(int)ctx,argv[0],(int)priv);
priv = malloc(sizeof *priv);
if (priv == NULL)
{
syslog(LOG_CRIT,"malloc(): %s",strerror(errno));
return SMFIS_TEMPFAIL;
}
memset(priv, '\0', sizeof *priv);
smfi_setpriv(ctx, priv);
qid=smfi_getsymval(ctx,"i");
if (qid==NULL)
{
syslog(LOG_CRIT,"could not get $i from sendmail");
return(SMFIS_TEMPFAIL);
}
priv->qid=strdup(qid);
if ((priv->qid)==NULL)
{
syslog(LOG_CRIT,"strdup(): %s",strerror(errno));
return(SMFIS_TEMPFAIL);
}
if (debug)
syslog(LOG_ERR,"mlfi_envfrom(0x%x, %s) processing qid %s",
(int)ctx,argv[0],qid);
if (((priv->headers)!=0) || ((priv->bodyblocks)!=0))
{
syslog(LOG_ERR,"mlfi_envfrom() found headers and bodyblocks > 0");
}
snprintf(buf,sizeof(buf),"%s/%s",BASEDIR,priv->qid);
priv->workdir=strdup(buf);
if ((priv->workdir)==NULL)
{
syslog(LOG_CRIT,"strdup(): %s",strerror(errno));
return(SMFIS_TEMPFAIL);
}
snprintf(buf,sizeof(buf),"%s/%s",priv->workdir,qid);
priv->datafile=strdup(buf);
if ((priv->datafile)==NULL)
{
syslog(LOG_CRIT,"strdup(): %s",strerror(errno));
return(SMFIS_TEMPFAIL);
}
if (mkdir(priv->workdir,0700)!=0)
{
syslog(LOG_CRIT,"mkdir(%s): %s",priv->workdir,strerror(errno));
return(SMFIS_TEMPFAIL);
}
/*
** FIXME use open() instead because Solaris is stupid
*/
priv->datafp=fopen(priv->datafile,"w");
if ((priv->datafp)==NULL)
{
syslog(LOG_CRIT,"fopen(%s): %s",priv->datafile,strerror(errno));
return(SMFIS_TEMPFAIL);
}
if (debug)
syslog(LOG_ERR,"mlfi_envfrom(0x%x, %s) => SMFIS_CONTINUE",
(int)ctx,argv[0]);
return(SMFIS_CONTINUE);
}
sfsistat mlfi_envrcpt(SMFICTX *ctx, char **argv)
{
char *rcptaddr = smfi_getsymval(ctx, "{rcpt_addr}");
if (debug)
syslog(LOG_ERR,"mlfi_envrcpt(0x%x, %s)",(int)ctx,argv[0]);
if (rcptaddr && (strcasecmp(rcptaddr,"postmaster")==0))
{
/*
** always accept postmaster mail
**
** any mail that includes postmaster as a recipent in the
** SMTP transaction will be accepted without any processing
**
** DANGER => people can use this against us by including
** postmaster in mail with viruses. I don't consider this
** dangerous enough to implement a solution, so unless it
** starts happening I will ignore it. Problem noted by
** Thomas Lussnig.
*/
/*
** James R. Stahr <stahr@binc.net discovered that the lack of a
** cleanup(ctx) here causes some queue directories not to be
** cleaned up. I had noticed them appearing, but had never
** worried enough about where there were coming from.
*/
cleanup(ctx);
if (debug)
syslog(LOG_ERR,"mlfi_envrcpt(0x%x, %s) => SMFIS_ACCEPT",
(int)ctx,argv[0]);
return SMFIS_ACCEPT;
}
/* continue processing */
if (debug)
syslog(LOG_ERR,"mlfi_envrcpt(0x%x, %s) => SMFIS_CONTINUE",
(int)ctx,argv[0]);
return SMFIS_CONTINUE;
}
sfsistat mlfi_header(SMFICTX *ctx, char *headerf, char *headerv)
{
struct mlfiPriv *priv = MLFIPRIV;
if (debug)
syslog(LOG_ERR,"smfi_header(0x%x, %s, <omitted>)",(int)ctx,headerf);
/*
** FIXME use write() instead because Solaris is stupid
*/
if (fprintf(priv->datafp, "%s: %s\r\n", headerf, headerv)<=0)
return(SMFIS_TEMPFAIL);
(priv->headers)++;
if (debug)
syslog(LOG_ERR,"smfi_header(0x%x, %s, <omitted>) => SMFIS_CONTINUE",
(int)ctx,headerf);
return SMFIS_CONTINUE;
}
sfsistat mlfi_eoh(SMFICTX *ctx)
{
struct mlfiPriv *priv = MLFIPRIV;
if (debug)
syslog(LOG_ERR,"smfi_eoh(0x%x)",(int)ctx);
/*
** FIXME use write() instead because Solaris is stupid
*/
if (fprintf(priv->datafp, "\r\n")<=0)
{
if (debug)
syslog(LOG_ERR,"smfi_eoh(0x%x) => SMFIS_TEMPFAIL",(int)ctx);
return(SMFIS_TEMPFAIL);
}
if (debug)
syslog(LOG_ERR,"smfi_eoh(0x%x) => SMFIS_CONTINUE",(int)ctx);
return SMFIS_CONTINUE;
}
sfsistat mlfi_body(SMFICTX *ctx, unsigned char *bodyp, size_t bodylen)
{
int nwritten;
struct mlfiPriv *priv = MLFIPRIV;
if (debug)
syslog(LOG_ERR,"smfi_body(0x%x, 0x%x, %d)",(int)ctx,(int)bodyp,bodylen);
(priv->bodyblocks)++;
(priv->bodysize)+=bodylen;
/*
** FIXME use write() instead because Solaris is stupid
*/
if ((nwritten = fwrite(bodyp, bodylen, 1, priv->datafp)) != 1)
{
syslog(LOG_CRIT,"mlfi_body(): fwrite(): %s",strerror(errno));
(void) cleanup(ctx);
return SMFIS_TEMPFAIL;
}
if (debug)
syslog(LOG_ERR,"smfi_body(0x%x, 0x%x, %d) => SMFIS_CONTINUE",
(int)ctx,(int)bodyp,bodylen);
return SMFIS_CONTINUE;
}
int externalcommand(SMFICTX *ctx, char *workdir, int fd, char *p, char **av)
{
struct mlfiPriv *priv = MLFIPRIV;
char *ev[]={"PATH=/bin:/usr/bin",NULL};
int i;
int openmax=getdtablesize();
if (debug)
syslog(LOG_ERR,"externalcommand(0x%x, ..., %s, ...)",(int)ctx,p);
if ((priv->childpid)!=0)
{
syslog(LOG_CRIT,"externalcommand() called and priv->childpid is %d",
priv->childpid);
}
priv->childstatus=0;
priv->childpid=fork();
switch(priv->childpid)
{
case -1: /* error */
syslog(LOG_CRIT,"fork(): %s",strerror(errno));
priv->childpid=0;
return(-1);
break;
case 0: /* child */
if (workdir!=NULL)
{
if (chdir(workdir)!=0)
{
syslog(LOG_CRIT,"chdir(%s): %s",workdir,strerror(errno));
}
}
if (fd==EOF)
{
close(0);
close(1);
close(2);
open("/dev/null",O_RDONLY);
open("/dev/null",O_WRONLY);
open("/dev/null",O_WRONLY);
}
else
{
close(0);
open("/dev/null",O_RDONLY);
dup2(fd,1);
dup2(fd,2);
}
for (i=3;i<openmax;i++)
close(i);
execve(p, av, ev);
syslog(LOG_CRIT,"externalcommand() failed to execve() %s",p);
exit(1);
break;
default: /* parent */
waitpid(priv->childpid,&(priv->childstatus),0);
priv->childpid=0;
if (WIFEXITED(priv->childstatus))
{
if (debug)
syslog(LOG_ERR,"externalcommand(0x%x, ..., %s, ...) => %d",
(int)ctx,p,WEXITSTATUS(priv->childstatus));
return(WEXITSTATUS(priv->childstatus));
}
else
{
if (debug)
syslog(LOG_ERR,"externalcommand(0x%x, ..., %s, ...) => -1",
(int)ctx,p);
return(-1);
}
break;
}
if (debug)
syslog(LOG_ERR,"externalcommand(0x%x, ..., %s, ...) => -1",(int)ctx,p);
return(-1);
}
int unpack(SMFICTX *ctx, char *workdir, char *datafile)
{
struct mlfiPriv *priv = MLFIPRIV;
char *p=UNPACKER;
char *av[]={NULL,"--unique_names","-i",NULL,NULL};
union
{
struct dirent d;
char s[(sizeof(struct dirent)+MAXPATHLEN+1)];
} dirent;
struct dirent *direntp;
int count=0;
av[0]=UNPACKER;
av[3]=datafile;
externalcommand(ctx, workdir, EOF, p, av);
/*
** On Solaris you need to compile with:
** -D_POSIX_PTHREAD_SEMANTICS -D_REENTRANT
*/
priv->dir=opendir(priv->workdir);
while((readdir_r(priv->dir,&(dirent.d),&direntp)==0) && (direntp !=NULL))
{
count++;
}
closedir(priv->dir);
priv->dir=NULL;
if (debug)
syslog(LOG_ERR,"unpack() found %d files, returning %d",count,(count>3));
return(count>3);
}
int rmrf(SMFICTX *ctx)
{
struct mlfiPriv *priv = MLFIPRIV;
char *p="/bin/rm";
char *av[]={"rm","-rf",NULL,NULL};
av[2]=priv->workdir;
return(externalcommand(ctx, NULL, EOF, p, av));
}
sfsistat virusscan(SMFICTX *ctx, char *path, char *format)
{
struct mlfiPriv *priv = MLFIPRIV;
char buf[512];
char viruses[512];
char tmp[512];
int retval;
int fd;
int i;
char *word;
char *p=NULL;
char *av[]={NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
/*
** the size of av here is dependant on the sizeof avargs elsewhere
*/
if ((sizeof(av)/sizeof(char *)+3)>(sizeof(avargs)/sizeof(char)))
{
syslog(LOG_ERR,"virusscan() detected that char *avargs[] is larger than char *av[]!\n");
syslog(LOG_ERR,"virusscan() not scanning anything!\n");
syslog(LOG_ERR,"virusscan() DANGER! DANGER! DANGER!\n");
return(SMFIS_CONTINUE);
}
if (debug)
syslog(LOG_ERR,"virusscan(0x%x, ...)",(int)ctx);
snprintf(buf,sizeof(buf),"%s/tmp.XXXXXX",priv->workdir);
fd=mkstemp(buf);
if (fd==EOF)
{
syslog(LOG_CRIT,"mkstemp(): %s",strerror(errno));
return(SMFIS_TEMPFAIL);
}
priv->childfp=fdopen(fd,"rw");
if ((priv->childfp)==NULL)
{
syslog(LOG_CRIT,"fdopen(): %s",strerror(errno));
close(fd);
return(SMFIS_TEMPFAIL);
}
p=path;
av[0]=path;
for (i=0;i<(sizeof(avargs)/sizeof(char *))&&avargs[i];i++)
{
av[i+1]=avargs[i];
}
av[i+1]=priv->workdir;
retval=externalcommand(ctx,AVSCANDIR,fileno(priv->childfp),p,av);
if (debug)
{
syslog(LOG_ERR,"virusscan(0x%x, ...)"
"externalcommand() returned %d on %s",
(int)ctx, retval,priv->workdir);
}
if (ignorerror2 > 0 )
{
if ( retval == 2 )
retval = 0;
}
if (retval>0)
{
fseek(priv->childfp,0,SEEK_SET);
memset(viruses,0,sizeof(buf));
while (fgets(buf,sizeof(buf),priv->childfp)!=NULL)
{
word = buf;
if (skipwords > 0 )
{
word = strchr( word, ' ' );
if ( word == NULL )
word = buf;
}
if (sscanf(word,format,tmp)==1)
{
if (viruses[0])
strncat(viruses," ",sizeof(viruses));
strncat(viruses,tmp,sizeof(viruses));
}
}
fclose(priv->childfp);
priv->childfp=NULL;
if (viruses[0])
priv->viruses=strdup(viruses);
else
{
priv->viruses=strdup("please contact postmaster");
return(avfailcode);
}
return(SMFIS_REJECT);
}
if (retval < 0)
{
syslog(LOG_CRIT,"externalcommand() returned less than zero");
return(avfailcode);
}
if (debug)
syslog(LOG_ERR,"virusscan(0x%x, ...) => SMFIS_CONTINUE",(int)ctx);
return(SMFIS_CONTINUE);
}
/*
** only scan for known bad extensions
** this is wrong but less likely to generate false positives...
*/
static int isbadfilename(char *name)
{
static int nbad=(sizeof(badext)/sizeof(char *));
int i;
size_t flen;
size_t elen;
flen=strlen(name);
for (i=0;i<nbad;i++)
{
elen=strlen(badext[i]);
if (flen > elen)
{
if (strcasecmp((name+(flen-elen)),badext[i])==0)
{
return(1);
}
}
}
return(0);
}
sfsistat extscan(SMFICTX *ctx)
{
struct mlfiPriv *priv = MLFIPRIV;
union
{
struct dirent d;
char s[(sizeof(struct dirent)+MAXPATHLEN+1)];
} dirent;
struct dirent *direntp;
char buf[1024];
memset(buf,0,sizeof(buf));
priv->dir=opendir(priv->workdir);
while((readdir_r(priv->dir,&(dirent.d),&direntp)==0) && (direntp !=NULL))
{
if ((direntp->d_name[0] == '.')
&& ((direntp->d_name[1] == 0)
||((direntp->d_name[1] == '.') && (direntp->d_name[2] == 0))
)
)
continue;
if (isbadfilename(direntp->d_name))
{
if (buf[0])
strncat(buf," ",sizeof(buf));
strncat(buf,direntp->d_name,sizeof(buf));
}
}
closedir(priv->dir);
priv->dir=NULL;
if (buf[0])
{
priv->scripts=strdup(buf);
if ((priv->scripts)==NULL)
{
syslog(LOG_CRIT,"strdup(): %s",strerror(errno));
return(SMFIS_TEMPFAIL);
}
return(SMFIS_REJECT);
}
return(SMFIS_CONTINUE);
}
sfsistat mlfi_eom(SMFICTX *ctx)
{
struct mlfiPriv *priv = MLFIPRIV;
char message[2048];
sfsistat vstat;
#ifdef ANTIVIRUSTIMING
struct timeval stop;
struct timeval total;
#endif /* ANTIVIRUSTIMING */
if (debug)
syslog(LOG_ERR,"smfi_eom(0x%x)",(int)ctx);
if (priv==NULL)
{
syslog(LOG_ERR,"mlfi_eom() priv is NULL in mlfi_eom?");
return(SMFIS_TEMPFAIL);
}
if ((priv->datafp)!=NULL)
{
fclose(priv->datafp);
(priv->datafp)=NULL;
}
else
{
syslog(LOG_ERR,"mlfi_eom() priv->datafp is NULL?");
return(SMFIS_TEMPFAIL);
}
if (((priv->headers)!=0)
&& ((priv->bodyblocks)!=0)
&& ((priv->bodysize)>100))
{
if ((priv->workdir)==NULL)
syslog(LOG_ERR,"mlfi_eom() priv->workdir is NULL");
if ((priv->datafile)==NULL)
syslog(LOG_ERR,"mlfi_eom() priv->datafile is NULL");
#ifdef ANTIVIRUSTIMING
if (gettimeofday(&(priv->start),NULL)!=0)
syslog(LOG_ERR,"gettimeofday(): %s",strerror(errno));
#endif /* ANTIVIRUSTIMING */
if (unpack(ctx,priv->workdir, priv->datafile))
{
vstat=virusscan(ctx,AVSCANNER,FORMAT);
#ifdef ANTIVIRUSTIMING
if (gettimeofday(&stop,NULL)!=0)
syslog(LOG_ERR,"cleanup(0x%x): gettimeofday(): %s",
(int)ctx,strerror(errno));
total.tv_sec=(stop.tv_sec-(priv->start).tv_sec);
if (stop.tv_usec > (priv->start.tv_usec))
{
total.tv_usec=stop.tv_usec-(priv->start).tv_usec;
}
else
{
total.tv_sec--;
total.tv_usec=(1000000 + stop.tv_usec) - (priv->start).tv_usec;
}
syslog(LOG_ERR,"timing to unpack/virusscan %s: %d.%06d",
priv->qid,(int)total.tv_sec,(int)total.tv_usec);
#endif /* ANTIVIRUSTIMING */
switch(vstat)
{
case SMFIS_REJECT:
priv->hasvirus=1;
snprintf(message,sizeof(message),
"virus alert: %s",priv->viruses);
smfi_setreply(ctx, "550", "5.7.1", message);
cleanup(ctx);
if (debug)
syslog(LOG_ERR,"smfi_eom(0x%x) => SMFIS_REJECT",(int)ctx);
return(SMFIS_REJECT);
break;
case SMFIS_CONTINUE:
break;
case SMFIS_TEMPFAIL:
default:
cleanup(ctx);
if (debug)
syslog(LOG_ERR,"smfi_eom(0x%x) => SMFIS_TEMPFAIL",(int)ctx);
return(SMFIS_TEMPFAIL);
break;
}
vstat=extscan(ctx);
switch(vstat)
{
case SMFIS_REJECT:
priv->hasvirus=1;
snprintf(message,sizeof(message),
"illegal extension: %s",priv->scripts);
smfi_setreply(ctx, "550", "5.7.1", message);
cleanup(ctx);
if (debug)
syslog(LOG_ERR,"smfi_eom(0x%x) => SMFIS_REJECT",(int)ctx);
return(SMFIS_REJECT);
break;
case SMFIS_CONTINUE:
break;
case SMFIS_TEMPFAIL:
default:
cleanup(ctx);
if (debug)
syslog(LOG_ERR,"smfi_eom(0x%x) => SMFIS_TEMPFAIL",(int)ctx);
return(SMFIS_TEMPFAIL);
break;
}
}
}
cleanup(ctx);
if (debug)
syslog(LOG_ERR,"smfi_eom(0x%x) => SMFIS_CONTINUE",(int)ctx);
return(SMFIS_CONTINUE);
}
sfsistat mlfi_abort(SMFICTX *ctx)
{
if (debug)
syslog(LOG_ERR,"smfi_abort(0x%x)",(int)ctx);
cleanup(ctx);
if (debug)
syslog(LOG_ERR,"smfi_abort(0x%x) => SMFIS_CONTINUE",(int)ctx);
return(SMFIS_CONTINUE);
}
void cleanup(SMFICTX *ctx)
{
struct mlfiPriv *priv = MLFIPRIV;
char buf[1024];
if (debug)
syslog(LOG_ERR,"cleanup(0x%x)",(int)ctx);
if (priv != NULL)
{
if ((priv->childpid)>0)
{
syslog(LOG_ERR,"cleanup() killing %d\n",priv->childpid);
kill(priv->childpid,SIGABRT);
waitpid(priv->childpid,NULL,0);
priv->childpid=0;
}
if ((priv->childfp)!=NULL)
{
fclose(priv->childfp);
priv->childfp=NULL;
}
if ((priv->datafp)!=NULL)
{
fclose(priv->datafp);
priv->datafp=NULL;
}
if (priv->dir)
{
closedir(priv->dir);
priv->dir=NULL;
}
if(priv->workdir)
{
if ((priv->hasvirus) && (!purgevirus))
{
snprintf(buf,sizeof(buf),"%s/%s",QUARANTINE,priv->qid);
/*
** If your abbatoir and your oubliette are on different
** file systems then you need to do more work in
** order to quarantine things. Compile with -DCROSSFX
**
** Theoretically, we don't leak anything from externalcommand
** so it is safe to call this after we cleaned up from
** it already.
*/
#ifdef CROSSFS
{
char *p="/bin/mv";
char *av[]={"mv",NULL,NULL,NULL};
av[1]=priv->workdir;
av[2]=buf;
externalcommand(ctx, NULL, EOF, p, av);
}
#else /* CROSSFS */
rename(priv->workdir,buf);
#endif /* CROSSFS */
}
else
{
rmrf(ctx);
}
priv->hasvirus=0;
free(priv->workdir);
priv->workdir=NULL;
}
(priv->headers)=0;
(priv->bodyblocks)=0;
(priv->bodysize)=0;
if(priv->datafile)
{
free(priv->datafile);
priv->datafile=NULL;
}
if (priv->viruses)
{
free(priv->viruses);
priv->viruses=NULL;
}
if (priv->scripts)
{
free(priv->scripts);
priv->scripts=NULL;
}
if(priv->qid)
{
free(priv->qid);
priv->qid=NULL;
}
}
free(priv);
smfi_setpriv(ctx, NULL);
if (debug)
syslog(LOG_ERR,"cleanup(0x%x) => (void)",(int)ctx);
}
sfsistat mlfi_close(SMFICTX *ctx)
{
struct mlfiPriv *priv = MLFIPRIV;
if (debug)
syslog(LOG_ERR,"smfi_close(0x%x)",(int)ctx);
if (priv!=NULL)
{
cleanup(ctx);
}
if (debug)
syslog(LOG_ERR,"smfi_close(0x%x) => SMFIS_CONTINUE",(int)ctx);
return SMFIS_CONTINUE;
}
struct smfiDesc smfilter =
{
"antivirus", /* filter name */
SMFI_VERSION, /* version code -- do not change */
0, /* flags */
NULL, /* connection info filter */
NULL, /* SMTP HELO command filter */
mlfi_envfrom, /* envelope sender filter */
mlfi_envrcpt, /* envelope recipient filter */
mlfi_header, /* header filter */
mlfi_eoh, /* end of header */
mlfi_body, /* body block filter */
mlfi_eom, /* end of message */
mlfi_abort, /* message aborted */
mlfi_close, /* connection cleanup */
};
static void
usage(void)
{
fprintf(stderr,"Usage: antivirus [-C <configfile>] [-v] [-V] [-n]\n");
fprintf(stderr," -v: version\n");
fprintf(stderr," -V: verbose version\n");
fprintf(stderr," -n: do not use pid file\n");
}
/*
** inspired by Thomas Lussnig <thomas@bewegungsmelder.de>
*/
int droproot(void)
{
uid_t myuid;
uid_t myeuid;
struct passwd *pwent;
struct group *grent;
myuid=getuid();
myeuid=geteuid();
if ((myuid!=0) && (myeuid!=0))
return(0);
grent=getgrnam(SAFEGROUP);
if (grent==NULL)
{
fprintf(stderr,"droproot(): getgrnam(%s): %s\n",
SAFEGROUP,strerror(errno));
return(-1);
}
if (setregid(grent->gr_gid,grent->gr_gid)!=0)
{
fprintf(stderr,"droproot(): setregid(%d,%d): %s\n",
(int)grent->gr_gid,(int)grent->gr_gid,strerror(errno));
return(-1);
}
if (initgroups(SAFEUSER,grent->gr_gid))
{
fprintf(stderr,"droproot(): initgroups(%s,%d): %s\n",
SAFEUSER,(int)(grent->gr_gid),strerror(errno));
return(-1);
}
pwent=getpwnam(SAFEUSER);
if (pwent==NULL)
{
fprintf(stderr,"droproot(): getpwnam(%s): %s\n",
SAFEUSER,strerror(errno));
return(-1);
}
if (setreuid(pwent->pw_uid,pwent->pw_uid)!=0)
{
fprintf(stderr,"droproot(): setreuid(%d,%d): %s\n",
(int)pwent->pw_uid,(int)pwent->pw_uid,strerror(errno));
return(-1);
}
myuid=getuid();
myeuid=geteuid();
if ((myuid==0) || (myeuid==0))
{
fprintf(stderr,"droproot(): still root after setreuid()?\n");
return(-1);
}
return(0);
}
static int readconfig(void)
{
FILE *fp;
char buf[512];
char fmt[64];
char token[64];
char value[256];
char *ptr;
int scanned;
int set=0;
int line=0;
int i;
fflush(stdout);
if (configfile==NULL)
{
fp=fopen(DEFAULTCONFIGFILE,"r");
if (fp==NULL)
{
if (errno==ENOENT)
return(0);
snprintf(buf,sizeof(buf),"fopen(%s)",configfile);
perror(buf);
return(-1);
}
}
else
{
fp=fopen(configfile,"r");
if (fp==NULL)
{
snprintf(buf,sizeof(buf),"fopen(%s)",configfile);
perror(buf);
return(-1);
}
}
while (fgets(buf,sizeof(buf),fp)!=NULL)
{
line++;
ptr=index(buf,'#');
if (ptr)
*ptr='\0';
snprintf(fmt,sizeof(fmt),"%%%ds %%%dc",sizeof(token),sizeof(value));
bzero(token,sizeof(token));
bzero(value,sizeof(value));
scanned=sscanf(buf,fmt,token,value);
/*
** nuke the trailing whitespace here
*/
i=0;
while (value[i]) i++;
i--;
while ((i>0) && isspace((int)value[i]))
{
value[i]='\0';
i--;
}
if (scanned==1)
{
fprintf(stderr,"Error on line %d in configure file: %s\n",line,buf);
fclose(fp);
return(-1);
}
if (scanned==2)
{
#define DOTOKEN(X,Y) if(strcasecmp(token,(X))==0){if((Y)!=NULL){fprintf(stderr,"readconfig(): %s already defined\n",(X));return(-1);}(Y)=strdup(value);if ((Y)==NULL){perror("malloc()");return(-1);}set++;continue;}
DOTOKEN("HOMEDIR",HOMEDIR);
DOTOKEN("BASEDIR",BASEDIR);
DOTOKEN("AVSCANDIR",AVSCANDIR);
DOTOKEN("QUARANTINE",QUARANTINE);
DOTOKEN("AVSCANNER",AVSCANNER);
DOTOKEN("AVSCANARGS",AVSCANARGS);
DOTOKEN("UNPACKER",UNPACKER);
DOTOKEN("SAFEUSER",SAFEUSER);
DOTOKEN("SAFEGROUP",SAFEGROUP);
DOTOKEN("PIDFILE",PIDFILE);
DOTOKEN("SOCKETNAME",SOCKETNAME);
DOTOKEN("TIMEOUT",TIMEOUT);
DOTOKEN("AVPRODUCT",AVPRODUCT);
DOTOKEN("AVFAILACTION",AVFAILACTION);
DOTOKEN("VIRUSACTION",VIRUSACTION);
fprintf(stderr,"Line %d unrecognized: %s\n",line,buf);
fclose(fp);
return(-1);
}
}
if (set==0)
{
fprintf(stderr,"Warning: %s contained no valid lines\n",configfile);
}
fclose(fp);
return(0);
}
int buildavargs(void)
{
int i=0;
int n=0;
int c=0;
char buf[512];
/*
** put strings into *avargs[] for later use
*/
bzero(buf,sizeof(buf));
if (AVSCANARGS==NULL)
{
fprintf(stderr,"buildavargs(): AVSCANARGS is NULL?\n");
return(-1);
}
if (strlen(AVSCANARGS)==0)
{
/*
** nothing to do
*/
return(0);
}
for (i=0,n=0,c=0;(i<sizeof(buf))&&(AVSCANARGS[i]);i++)
{
if (n==(sizeof(avargs)/sizeof(char *)))
{
fprintf(stderr,"buildavargs(): too many args in AVSCANARGS!\n");
return(-1);
}
if (AVSCANARGS[i]==' ')
{
if (c>0)
{
printf("Found end of word %d\n",n);
avargs[n]=(char *)strdup(buf);
if (avargs[n]==NULL)
{
perror("buildavargs(): strdup()");
return(-1);
}
bzero(buf,sizeof(buf));
n++;
c=0;
}
}
else
{
buf[c]=AVSCANARGS[i];
c++;
}
}
/*
** consume the last word
*/
if (c>0)
{
avargs[n]=(char *)strdup(buf);
if (avargs[n]==NULL)
{
perror("buildavargs(): strdup()");
return(-1);
}
}
return(0);
}
int init(void)
{
if (readconfig()<0)
return(-1);
if (HOMEDIR==NULL) HOMEDIR=CONF_HOMEDIR;
if (BASEDIR==NULL) BASEDIR=CONF_BASEDIR;
if (AVSCANDIR==NULL) AVSCANDIR=CONF_AVSCANDIR;
if (QUARANTINE==NULL) QUARANTINE=CONF_QUARANTINE;
if (AVSCANNER==NULL) AVSCANNER=CONF_AVSCANNER;
if (AVSCANARGS==NULL) AVSCANARGS=CONF_AVSCANARGS;
if (UNPACKER==NULL) UNPACKER=CONF_UNPACKER;
if (SAFEUSER==NULL) SAFEUSER=CONF_SAFEUSER;
if (SAFEGROUP==NULL) SAFEGROUP=CONF_SAFEGROUP;
if (PIDFILE==NULL) PIDFILE=CONF_PIDFILE;
if (SOCKETNAME==NULL) SOCKETNAME=CONF_SOCKETNAME;
if (TIMEOUT==NULL) TIMEOUT=CONF_TIMEOUT;
if (AVPRODUCT==NULL) AVPRODUCT=CONF_AVPRODUCT;
if (AVFAILACTION==NULL) AVFAILACTION=CONF_AVFAILACTION;
if (VIRUSACTION==NULL) VIRUSACTION=CONF_VIRUSACTION;
skipwords = 0;
if (strcasecmp(AVPRODUCT,"mcafee")==0)
{
FORMAT=FORMAT_MCAFEE;
AVSCANARGS=SCANARGS_MCAFEE;
}
else if (strcasecmp(AVPRODUCT,"sophos")==0)
{
FORMAT=FORMAT_SOPHOS;
}
else if ((strcasecmp(AVPRODUCT,"fsav")==0)
|| (strcasecmp(AVPRODUCT,"f-prot")==0))
{
FORMAT=FORMAT_FSAV;
}
else if (strcasecmp(AVPRODUCT,"clamav")==0)
{
FORMAT=FORMAT_CLAMAV;
AVSCANARGS=SCANARGS_CLAMAV;
skipwords = 1;
ignorerror2 = 1;
}
else
{
fprintf(stderr,"init(): unrecognized AVPRODUCT %s\n",AVPRODUCT);
fprintf(stderr,"init(): valid values are: mcafee, fsva, sophos, clamav\n");
return(-1);
}
if (strcasecmp(AVFAILACTION,"continue")==0)
{
avfailcode=SMFIS_CONTINUE;
}
else if (strcasecmp(AVFAILACTION,"tempfail")==0)
{
avfailcode=SMFIS_TEMPFAIL;
}
else if (strcasecmp(AVFAILACTION,"reject")==0)
{
avfailcode=SMFIS_REJECT;
}
else
{
fprintf(stderr,"init(): unrecognized AVFAILACTION %s\n",AVFAILACTION);
fprintf(stderr,"init(): valid values are: continue, tempfail, reject\n");
return(-1);
}
if ((strcasecmp(VIRUSACTION,"quarantine")==0)
||(strcasecmp(VIRUSACTION,"save")==0))
{
purgevirus=0;
}
else if ((strcasecmp(VIRUSACTION,"purge")==0)
|| (strcasecmp(VIRUSACTION,"delete")==0))
{
purgevirus=1;
}
else
{
fprintf(stderr,"init(): unrecognized VIRUSACTION %s\n",VIRUSACTION);
fprintf(stderr,"init(): valid values are: quarantine or purge\n");
return(-1);
}
/*
** build up the *avargs[] array
*/
if (buildavargs()==(-1))
{
fprintf(stderr,"init(): buildavargs() failed\n");
return(-1);
}
return(0);
}
/*
** this is sometimes used in testing
*/
/*
void dumpconfig(void)
{
printf("HOMEDIR %s\n",HOMEDIR);
printf("BASEDIR %s\n",BASEDIR);
printf("AVSCANDIR %s\n",AVSCANDIR);
printf("QUARANTINE %s\n",QUARANTINE);
printf("AVSCANNER %s\n",AVSCANNER);
printf("UNPACKER %s\n",UNPACKER);
printf("SAFEUSER %s\n",SAFEUSER);
printf("SAFEGROUP %s\n",SAFEGROUP);
printf("PIDFILE %s\n",PIDFILE);
printf("SOCKETNAME %s\n",SOCKETNAME);
printf("TIMEOUT %s\n",TIMEOUT);
}
*/
/*
** Original version by Thomas Lussnig <thomas@bewegungsmelder.de>
** Modified by me (wcolburn@nmt.edu)
*/
void versioninfo(int longversion)
{
int i;
printf("\n");
printf("%s\n",copyright);
printf("%s\n",versionid);
printf("\n");
printf("%s\n",webpage);
if (longversion)
{
printf("\n");
for (i=0;rcsid[i];i++)
printf("%s\n",rcsid[i]);
printf("%s\n",compilestamp);
printf("%s\n",sendmailstamp);
}
printf("\n");
}
int main(int argc, char **argv, char **envp)
{
int retval;
char c;
FILE *fp;
const char *args = "p:C:hnvVdD";
extern char *optarg;
char *socketoverride=NULL;
int usepidfile=1;
int daemonize=1;
#ifdef USESETRLIMIT
struct rlimit rlim;
#endif /* USESETRLIMIT */
while ((c = getopt(argc, argv, args)) != (char)EOF)
{
switch (c)
{
case 'd':
debug++;
break;
case 'D':
daemonize=!daemonize;
break;
case 'p':
if (socketoverride!=NULL)
{
fprintf(stderr,"Socket already set\n");
exit(EX_SOFTWARE);
}
socketoverride=strdup(optarg);
if (socketoverride==NULL)
{
perror("malloc()");
exit(EX_SOFTWARE);
}
break;
case 'C':
/*
** configure file
*/
if ((optarg==NULL) || (*optarg=='\0'))
{
fprintf(stderr,"Illegal configure file\n");
exit(EX_SOFTWARE);
}
if (configfile != NULL)
{
fprintf(stderr,"Configure file already set\n");
exit(EX_SOFTWARE);
}
configfile=strdup(optarg);
if (configfile==NULL)
{
perror("malloc()");
exit(EX_SOFTWARE);
}
break;
case 'n':
usepidfile=0;
break;
case 'v':
versioninfo(0);
exit(0);
break;
case 'V':
versioninfo(1);
exit(0);
break;
case 'h':
default:
usage();
exit(0);
break;
}
}
if (init()!=0)
{
fprintf(stderr,"init() failed\n");
exit(EX_SOFTWARE);
}
#ifdef USESETRLIMIT
/*
** Some people need more file descriptors
*/
if (getrlimit(RLIMIT_NOFILE, &rlim)<0)
{
fprintf(stderr,"main(): getrlimit(RLIMIT_NOFILE, ...) failed\n");
}
else
{
/*
** the value 1024 should probably be in the configuration file
*/
if (rlim.rlim_cur<1024)
{
rlim.rlim_cur=1024;
rlim.rlim_max=1024;
if (setrlimit(RLIMIT_NOFILE, &rlim)<0)
{
fprintf(stderr,"main(): setrlimit(RLIMIT_NOFILE, {%d,%d}) failed\n",
(int)rlim.rlim_cur,(int)rlim.rlim_max);
}
}
}
#endif /* USESETRLIMIT */
/*
** allow -p to override the default and the config file
*/
if (socketoverride != NULL)
SOCKETNAME=socketoverride;
if (droproot()!=0)
{
fprintf(stderr,"droproot() failed\n");
exit(EX_SOFTWARE);
}
openlog("antivirus",LOG_PID,LOG_MAIL);
if (mkdir(HOMEDIR,0700)!=0)
{
if (errno != EEXIST)
{
syslog(LOG_CRIT,"mkdir(%s): %s",HOMEDIR,strerror(errno));
fprintf(stderr,"mkdir(%s): %s",HOMEDIR,strerror(errno));
exit(EX_SOFTWARE);
}
}
if (chdir(HOMEDIR)!=0)
{
syslog(LOG_CRIT,"chdir(%s): %s",HOMEDIR,strerror(errno));
fprintf(stderr,"chdir(%s): %s",HOMEDIR,strerror(errno));
exit(EX_SOFTWARE);
}
if (mkdir(BASEDIR,0700)!=0)
{
if (errno != EEXIST)
{
syslog(LOG_CRIT,"mkdir(%s): %s",BASEDIR,strerror(errno));
fprintf(stderr,"mkdir(%s): %s",BASEDIR,strerror(errno));
exit(EX_SOFTWARE);
}
}
if (mkdir(QUARANTINE,0700)!=0)
{
if (errno != EEXIST)
{
syslog(LOG_CRIT,"mkdir(%s): %s",QUARANTINE,strerror(errno));
fprintf(stderr,"mkdir(%s): %s",QUARANTINE,strerror(errno));
exit(EX_SOFTWARE);
}
}
/*
** do the socket
*/
if(smfi_setconn(SOCKETNAME) == MI_FAILURE)
{
fprintf(stderr,"smfi_setconn(%s) failed",SOCKETNAME);
syslog(LOG_CRIT,"smfi_setconn(%s) failed",SOCKETNAME);
exit(EX_SOFTWARE);
}
/*
** If we're using a local socket, make sure it doesn't
** already exist.
**
** Why do they do this after the smfi_setconn() call?
*/
if(strncmp(SOCKETNAME, "unix:", 5) == 0)
{
if (unlink(SOCKETNAME + 5)<0)
{
if (errno!=ENOENT)
perror("unlink()");
}
}
else if(strncmp(SOCKETNAME, "local:", 6) == 0)
{
if (unlink(SOCKETNAME + 6)<0)
{
if (errno!=ENOENT)
perror("unlink()");
}
}
/*
** do the timeout
*/
if (TIMEOUT && (*TIMEOUT != '\0'))
{
if(smfi_settimeout(atoi(TIMEOUT)) == MI_FAILURE)
{
fprintf(stderr,"smfi_settimeout(%s) failed",TIMEOUT);
syslog(LOG_CRIT,"smfi_settimeout(%s) failed",TIMEOUT);
exit(EX_SOFTWARE);
}
}
/*
** background the process
*/
if (daemonize)
{
switch(fork())
{
case -1:
perror("fork()");
exit(1);
break;
case 0: /* child */
close(0);
close(1);
close(2);
open("/dev/null",O_RDONLY);
open("/dev/null",O_WRONLY);
open("/dev/null",O_WRONLY);
setsid();
break;
default: /* parent */
exit(0);
break;
}
}
if (usepidfile)
{
fp=fopen(PIDFILE,"w");
if (fp==NULL)
{
fprintf(stderr,"Could not open %s\n",PIDFILE);
perror("fopen()");
}
else
{
fprintf(fp,"%d\n",(int)getpid());
fclose(fp);
}
}
if (smfi_register(smfilter) == MI_FAILURE)
{
fprintf(stderr, "smfi_register failed\n");
exit(EX_UNAVAILABLE);
}
syslog(LOG_CRIT,"ready to answer queries");
retval = smfi_main();
if (retval != 0)
{
fprintf(stderr,"smfi_main() => %d\n",retval);
}
if (usepidfile)
{
if (truncate(PIDFILE,0)!=0)
perror("truncate()");
}
return(abs(retval));
}
/*
* $Log: antivirus.c,v $
* Revision 3.30 2003/06/30 16:15:42 wcolburn
* (int)value[i] inside isspace()
*
* Revision 3.29 2003/06/27 16:36:45 wcolburn
* took out trailing whitespace because I'm an idiot
*
* Revision 3.28 2003/06/26 19:23:22 wcolburn
* changed the parser to apparantly pickup tokens and multiword values now!
*
* Revision 3.27 2003/06/26 19:17:41 wcolburn
* also check length of string at beginning, just in case! (AVSCANARGS)
*
* Revision 3.26 2003/06/26 19:16:05 wcolburn
* Fix case of AVSCANARGS == ""
*
* Revision 3.25 2003/06/26 18:54:48 wcolburn
* rearranged how --unzip is set for AVSCANARGS
*
* Revision 3.24 2003/06/26 18:48:56 wcolburn
* char *AVSCANARGS, char *avargs[], and the buildavargs() calls so that I
* can pass aribtary arguments to the scanner. Primitive, but it should be
* sufficient.
*
* Revision 3.23 2003/06/25 19:12:56 wcolburn
* took out .zi
*
* Revision 3.22 2003/06/25 17:47:00 wcolburn
* added ".zi" as a temporary AV measure.
*
* Revision 3.21 2002/11/25 16:21:30 wcolburn
* do not scan mesasges less than 100 bytes.
*
* Revision 3.20 2002/05/17 14:03:03 wcolburn
* \r\n in eoh
*
* Revision 3.19 2002/05/17 14:02:47 wcolburn
* \r\n in headers
*
* Revision 3.18 2002/04/23 14:44:02 wcolburn
* some tweaks I forgot, and added --unique-names from Emmanuel Collignon
*
* Revision 3.17 2002/04/19 14:18:33 wcolburn
* USESETULIMIT
*
* Revision 3.16 2002/04/15 22:23:04 wcolburn
* fixed a typo in an error message
*
* Revision 3.15 2002/04/15 22:19:00 wcolburn
* rlimit in main()
*
* Revision 3.14 2002/04/12 16:52:07 wcolburn
* VIRUSACTION to purge or quarantine viruses
*
* Revision 3.13 2002/04/12 16:06:55 wcolburn
* added default in case AVFAILACTION is not set
*
* Revision 3.12 2002/04/12 15:59:44 wcolburn
* AVFAILACTION
*
* Revision 3.11 2002/04/12 15:49:07 wcolburn
* syslog after execve in externalcommand()
*
* Revision 3.10 2002/04/12 15:43:10 wcolburn
* error checking and return value and error message for AVPRODUCT
*
* Revision 3.9 2002/04/12 15:41:16 wcolburn
* AVPRODUCT works correctly now. :)
*
* Revision 3.8 2002/04/12 15:31:34 wcolburn
* AVPRODUCT to select what kind of format string to use. This
* should/could select the name of the scanner, but I don't know any other
* scanner names.
*
* Revision 3.7 2002/04/10 21:20:58 wcolburn
* added another comment
*
* Revision 3.6 2002/04/10 20:44:39 wcolburn
* updated a comment
*
* Revision 3.5 2002/04/10 20:42:50 wcolburn
* "ready to answer queries" syslog message
*
* Revision 3.4 2002/04/10 20:41:56 wcolburn
* daemonize option, suggested by Sebastien VAUTHEROT
*
* Revision 3.3 2002/04/10 20:40:21 wcolburn
* same but for an extention
*
* Revision 3.2 2002/04/10 20:39:46 wcolburn
* correct reply for finding a virus from Sebastien VAUTHEROT
*
* Revision 3.1 2002/04/10 20:37:20 wcolburn
* FREEBSD wait include from Sebastien VAUTHEROT
*
* Revision 3.0 2002/04/10 18:20:37 wcolburn
* The dawn of a new world
*
* Revision 2.136 2002/04/10 14:30:30 wcolburn
* ok, now the dirent and direntp stuff is all "correct" as per the manual
* page. Both of them even, not just one of them!
*
* Revision 2.135 2002/04/10 14:27:22 wcolburn
* not quite
*
* Revision 2.134 2002/04/10 13:55:23 wcolburn
* oops? I need to look back into this dirent thing in extscan.
*
* Revision 2.133 2002/04/09 16:58:34 wcolburn
* save a few cycles checking for bad file names, from Steve Barber
*
* Revision 2.132 2002/04/09 14:56:45 wcolburn
* Added in a call to /bin/mv for Steve Barber and his tmpfs.
*
* Revision 2.131 2002/04/09 14:47:10 wcolburn
* make a union for dirent as in the diffs Steve Barber sent me
* This is mostly stylisting, avoids casting a character array to
* structure.
*
* Revision 2.130 2002/04/09 14:32:14 wcolburn
* Ok, Steve Barber sent me a huge diff file. The rename (last revision
* I checked in) and the Sophos stuff are from him. Actually, he used
* REGEX for sophos, but it doesn't seem to need it.
*
* Revision 2.129 2002/04/09 14:23:23 wcolburn
* more generic AVSANNER and AVSCANDIR
*
* Revision 2.128 2002/04/01 19:54:19 wcolburn
* moved the location of the timing
*
* Revision 2.127 2002/04/01 17:53:00 wcolburn
* moved final timing to have access to qid
*
* Revision 2.126 2002/04/01 17:49:18 wcolburn
* oops, now it is right
*
* Revision 2.125 2002/04/01 17:48:07 wcolburn
* ANTIVIRUSTIMING
*
* Revision 2.124 2002/03/26 21:02:08 wcolburn
* openmax and a few cosmetic things
*
* Revision 2.123 2002/03/26 20:45:59 wcolburn
* took out a comment
*
* Revision 2.122 2002/03/26 17:05:35 wcolburn
* rearranged fd handling in virusscan()
*
* Revision 2.121 2002/03/26 16:55:14 wcolburn
* added Toolan's initgroups() code to droproot()
*
* Revision 2.120 2002/03/26 16:52:35 wcolburn
* took out mlfi_connect() in hopes to make the error-out go away
*
* Revision 2.119 2002/03/26 16:12:46 wcolburn
* close files 3..NOFILE in externalcommand before exec
*
* Revision 2.118 2002/03/26 16:07:04 wcolburn
* took out a misleading newline
*
* Revision 2.117 2002/03/25 22:42:54 wcolburn
* *** empty log message ***
*
* Revision 2.116 2002/03/25 22:06:40 wcolburn
* close fp in readconfig()
*
* Revision 2.115 2002/03/25 22:05:04 wcolburn
* setsid()
*
* Revision 2.114 2002/03/18 16:08:04 wcolburn
* cleaned up code a little for solaris
*
* Revision 2.113 2002/03/04 18:50:32 wcolburn
* fork at beginning to dettach terminal
*
* Revision 2.112 2002/02/28 21:11:16 wcolburn
* added and removed some signal code for children
*
* Revision 2.111 2002/02/28 20:40:37 wcolburn
* fixed comment about f-prot/FSAV
*
* Revision 2.110 2002/02/28 20:38:59 wcolburn
* took out inuse flag
*
* Revision 2.109 2002/02/28 20:36:39 wcolburn
* added an inuse to the private data
*
* Revision 2.108 2002/02/22 20:41:57 wcolburn
* took out a lot of debugging
*
* Revision 2.107 2002/02/22 20:30:37 wcolburn
* more debugging, and changed the fd closures in externalcommand
*
* Revision 2.106 2002/02/22 19:51:50 wcolburn
* corrected spelling in some debug statements
*
* Revision 2.105 2002/02/22 19:44:11 wcolburn
* no chdir() when rmrf()ing
*
* Revision 2.104 2002/02/22 19:31:37 wcolburn
* tried some things but failed
*
* Revision 2.103 2002/02/22 19:15:45 wcolburn
* syslog kill() in cleanup()
*
* Revision 2.102 2002/02/22 19:09:49 wcolburn
* rearranged cleanup() for more sensical ordering?
*
* Revision 2.101 2002/02/22 19:01:35 wcolburn
* messed up the chdir thing because I forgot how it worked. :)
*
* Revision 2.100 2002/02/22 18:55:42 wcolburn
* moved the rmrf() in cleanup() to the top
*
* Revision 2.99 2002/02/22 18:44:53 wcolburn
* took out priv from rmrf() because I don't use it anymore
*
* Revision 2.98 2002/02/22 18:44:31 wcolburn
* chdir to BASEDIR in rmrf()
*
* Revision 2.97 2002/02/22 18:39:55 wcolburn
* priv->childpid=0 in fork() error
*
* Revision 2.96 2002/02/22 18:32:32 wcolburn
* moved the closedir ahead of the rmrf()
*
* Revision 2.95 2002/02/22 18:30:37 wcolburn
* lots of debugging code
*
* Revision 2.94 2002/02/22 17:01:48 wcolburn
* added debug and cl option -d
*
* Revision 2.93 2002/02/21 21:40:14 wcolburn
* took out those cleanup(ctx) calls because every message is failing
*
* Revision 2.92 2002/02/21 21:34:40 wcolburn
* added cleanup(ctx) in a couple of places inside them eom() function
*
* Revision 2.91 2002/02/15 16:22:50 wcolburn
* added a comment
*
* Revision 2.90 2002/02/15 16:21:51 wcolburn
* fix for solaris compatability
*
* Revision 2.89 2002/01/30 15:24:08 wcolburn
* took out double extension scan
*
* Revision 2.88 2002/01/30 15:00:10 wcolburn
* .txt and .tar.bz
*
* Revision 2.87 2002/01/30 14:42:49 wcolburn
* .tar.gz is ok
*
* Revision 2.86 2002/01/29 20:14:03 wcolburn
* spelled extension correctly
*
* Revision 2.85 2002/01/29 20:07:05 wcolburn
* added a directory counter to unpack because my load is too high
*
* Revision 2.84 2002/01/29 19:35:20 wcolburn
* removed unused variable
*
* Revision 2.83 2002/01/29 19:34:54 wcolburn
* always unpack() and always virusscan() because it is just safer.
*
* Revision 2.82 2002/01/29 16:03:43 wcolburn
* oops, "." and ".." are special
*
* Revision 2.81 2002/01/29 16:01:20 wcolburn
* start of sweeping changes to isbadfilename
*
* Revision 2.80 2002/01/29 15:51:00 wcolburn
* rearranged things a little, prevent strdup()ing a NULL in virusscan
*
* Revision 2.79 2002/01/29 15:32:17 wcolburn
* dehacked the uudecode thing, and added a TODO item from Lussnig so that
* I stop losing track of all the things he keeps suggesting.
*
* Revision 2.78 2002/01/28 22:35:52 wcolburn
* --output-file=
*
* Revision 2.77 2002/01/28 22:10:16 wcolburn
* DOH! My extra.dat wasn't readable by my SAFEUSER, so it was
* being ignored and the virus wasn't being caught.
*
* Revision 2.76 2002/01/28 21:59:28 wcolburn
* oops
*
* Revision 2.75 2002/01/28 21:58:41 wcolburn
* rewrite from 2.64
*
* Revision 2.66 2002/01/28 20:48:56 wcolburn
* changed message for extentions
*
* Revision 2.65 2002/01/28 20:47:00 wcolburn
* took out .exe again
*
* Revision 2.64 2002/01/25 19:56:37 wcolburn
* more dynamic extention scanning
*
* Revision 2.63 2002/01/24 17:08:31 wcolburn
* added cleanup(ctx) for postmaster mail
*
* Revision 2.62 2002/01/16 15:46:38 wcolburn
* added pgp to the list of allowed extentions. I am sure it used to be
* there!
*
* Revision 2.61 2001/12/19 20:58:43 wcolburn
* cleaned up, mostly happy with it
*
* Revision 2.60 2001/12/19 18:50:40 wcolburn
* minor tweaks for good error reporting
*
* Revision 2.59 2001/12/19 18:43:21 wcolburn
* fixed silly mistake in socket unlink()
*
* Revision 2.58 2001/12/19 18:41:27 wcolburn
* unlink() wasn't working right?
*
* Revision 2.57 2001/12/19 18:37:56 wcolburn
* possibly a fully working version with new config file handling
*
* Revision 2.56 2001/12/19 17:34:43 wcolburn
* redid the config file reader to make it shorter and simpler
* as per suggestion/example from Thomas Lussnig.
*
* Revision 2.55 2001/12/19 15:57:59 wcolburn
* argv[0] doesn't work, you need smfi_getsymval() to find "postmaster"
*
* Revision 2.54 2001/12/19 15:55:05 wcolburn
* testing out Thomas Lussnig's usage of argv[0] instead of my sm_getsym()
* in mlfi_envrcpt()
*
* Revision 2.53 2001/12/19 15:43:32 wcolburn
* added warning about blindly accepting postmaster mail from Thomas
* Lussnig
*
* Revision 2.52 2001/12/18 16:41:55 wcolburn
* fixed the dollar Log dollar thing to not be wonky
*
* Revision 2.51 2001/12/18 16:40:46 wcolburn
* changed Copyright notice
*
* Revision 2.50 2001/12/18 15:58:33 wcolburn
* added in a dollar Log dollar directive
*
*/
syntax highlighted by Code2HTML, v. 0.9.1