1 #include "mfapi.h"
2 
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <strings.h> /* for Solaris */
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <sysexits.h>
10 #include <unistd.h>
11 #include <syslog.h>
12 #include <signal.h>
13 #include <fcntl.h>
14 #ifdef __FreeBSD__
15 #include <sys/wait.h>
16 #else
17 #include <wait.h>
18 #endif
19 #include <errno.h>
20 #include <sys/param.h>
21 #include <sys/time.h>
22 #include <dirent.h>
23 #include <ctype.h>
24 #include <pwd.h>
25 #include <grp.h>
26 #include <sys/resource.h>
27 
28 
29 static char *rcsid[]={
30   "$Author: wcolburn $",
31   "$Date: 2003/06/30 16:15:42 $",
32   "$Revision: 3.30 $",
33   "$Source: /system/milter/RCS/antivirus.c,v $",
34   '\0'
35 };
36 static char *versionid="$Id: antivirus.c,v 3.30 2003/06/30 16:15:42 wcolburn Exp $";
37 
38 static char *webpage="http://www.nmt.edu/~wcolburn/antivirus/";
39 
40 static char *copyright="Copyright (c) 2001 New Mexico Institute of Mining and Technology. All rights reserved.";
41 
42 /*
43 ** I *DESPERATLY* need to keep track of my contributors!
44 **
45 ** William D. Colburn (original author)
46 ** Thomas Lussnig (also peeled off his own version)
47 ** James R. Stahr
48 ** Tim Toolan
49 ** Steve Barber
50 ** Sebastien VAUTHEROT
51 ** Pierre DAVID
52 ** Emmanuel Collignon
53 */
54 
55 /*
56 ** compilestamp and sendmailstamp come from the Makefile
57 */
58 extern char *compilestamp;
59 extern char *sendmailstamp;
60 
61 /*
62 ** errno comes from errno.h and libc
63 */
64 extern int errno;
65 
66 /*
67 ** these can be changed from the configure file
68 */
69 static char *HOMEDIR=NULL;
70 static char *BASEDIR=NULL;
71 static char *AVSCANDIR =NULL;
72 static char *QUARANTINE=NULL;
73 static char *AVSCANNER=NULL;
74 static char *AVSCANARGS=NULL;
75 static char *UNPACKER=NULL;
76 static char *SAFEUSER=NULL;
77 static char *SAFEGROUP=NULL;
78 static char *PIDFILE=NULL;
79 static char *SOCKETNAME=NULL;
80 static char *TIMEOUT=NULL;
81 static char *AVPRODUCT=NULL;
82 static char *AVFAILACTION=NULL;
83 static char *VIRUSACTION=NULL;
84 
85 static char *FORMAT=NULL;
86 static sfsistat avfailcode=0;
87 static int purgevirus=0;
88 static int skipwords=0;
89 static int ignorerror2=0;
90 static char *avargs[]={NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
91 
92 /*
93 ** You might want to change these
94 */
95 static char *badext[]={".com",".scr",".vbs",".pif"};
96 
97 /*
98 ** these are the default values
99 */
100 #define CONF_HOMEDIR      "/system/av"
101 #define CONF_BASEDIR      "/system/av/abattoir"
102 #define CONF_AVSCANDIR    "/system/av/decullotage"
103 #define CONF_QUARANTINE   "/system/av/oubliette"
104 #define CONF_AVSCANNER    "/system/av/decullotage/uvscan"
105 #define CONF_AVSCANARGS   ""
106 #define CONF_UNPACKER     "/system/bin/ripmime"
107 #define CONF_SAFEUSER     "nobody"
108 #define CONF_SAFEGROUP    "nobody"
109 #define CONF_PIDFILE      "/system/av/etc/antivirus.pid"
110 #define CONF_SOCKETNAME   "local:/system/av/etc/antivirus.sock"
111 #define CONF_TIMEOUT      ""
112 #define CONF_AVPRODUCT    "mcafee"
113 #define CONF_AVFAILACTION "tempfail"
114 #define CONF_VIRUSACTION  "quarantine"
115 
116 /*
117 ** This one is for Mcafee
118 */
119 #define FORMAT_MCAFEE "        Found the %s virus !!!"
120 #define SCANARGS_MCAFEE "--unzip"
121 
122 /*
123 ** This one is for FSAV (f-prot)
124 */
125 #define FORMAT_FSAV "%*s infection: %s"
126 
127 /*
128 ** This one is for Sophos
129 */
130 #define FORMAT_SOPHOS ">>> Virus '%[^']s' found in file %*s"
131 
132 /*
133 ** This one is for clamav
134 */
135 #define FORMAT_CLAMAV " %s FOUND"
136 #define SCANARGS_CLAMAV "--disable-summary"
137 
138 /*
139 ** this can be given on the command line
140 */
141 static char *configfile=NULL;
142 #define DEFAULTCONFIGFILE "/etc/mail/antivirus.conf"
143 
144 struct mlfiPriv
145 {
146   char *qid;
147   char *datafile;
148   FILE *datafp;
149   char *workdir;
150   int headers;
151   int bodyblocks;
152   int bodysize;
153   int hasvirus;
154   char *viruses;
155   int childpid;
156   int childstatus;
157   FILE *childfp;
158   DIR *dir;
159   char *scripts;
160 #ifdef ANTIVIRUSTIMING
161   struct timeval start;
162 #endif /* ANTIVIRUSTIMING */
163 };
164 
165 /*
166 ** this is a handy shortcut
167 */
168 #define MLFIPRIV        ((struct mlfiPriv *) smfi_getpriv(ctx))
169 
170 /*
171 ** debugging sucks
172 */
173 static int debug=0;
174 
175 /*
176 ** forward definitions
177 */
178 void cleanup(SMFICTX *);
179 
mlfi_envfrom(SMFICTX * ctx,char ** argv)180 sfsistat mlfi_envfrom(SMFICTX *ctx, char **argv)
181 {
182   struct mlfiPriv *priv = MLFIPRIV;
183   char *qid;
184   char buf[512];
185 
186   if (debug)
187     syslog(LOG_ERR,"mlfi_envfrom(0x%x, %s)",(int)ctx,argv[0]);
188 
189   if (priv)
190     syslog(LOG_ERR,"mlfi_envfrom(0x%x, %s) given priv 0x%x",
191 	   (int)ctx,argv[0],(int)priv);
192 
193   priv = malloc(sizeof *priv);
194   if (priv == NULL)
195     {
196       syslog(LOG_CRIT,"malloc(): %s",strerror(errno));
197       return SMFIS_TEMPFAIL;
198     }
199   memset(priv, '\0', sizeof *priv);
200   smfi_setpriv(ctx, priv);
201 
202   qid=smfi_getsymval(ctx,"i");
203   if (qid==NULL)
204     {
205       syslog(LOG_CRIT,"could not get $i from sendmail");
206       return(SMFIS_TEMPFAIL);
207     }
208 
209   priv->qid=strdup(qid);
210   if ((priv->qid)==NULL)
211     {
212       syslog(LOG_CRIT,"strdup(): %s",strerror(errno));
213       return(SMFIS_TEMPFAIL);
214     }
215 
216   if (debug)
217     syslog(LOG_ERR,"mlfi_envfrom(0x%x, %s) processing qid %s",
218 	   (int)ctx,argv[0],qid);
219 
220   if (((priv->headers)!=0) || ((priv->bodyblocks)!=0))
221     {
222       syslog(LOG_ERR,"mlfi_envfrom() found headers and bodyblocks > 0");
223     }
224 
225   snprintf(buf,sizeof(buf),"%s/%s",BASEDIR,priv->qid);
226   priv->workdir=strdup(buf);
227   if ((priv->workdir)==NULL)
228     {
229       syslog(LOG_CRIT,"strdup(): %s",strerror(errno));
230       return(SMFIS_TEMPFAIL);
231     }
232 
233   snprintf(buf,sizeof(buf),"%s/%s",priv->workdir,qid);
234   priv->datafile=strdup(buf);
235   if ((priv->datafile)==NULL)
236     {
237       syslog(LOG_CRIT,"strdup(): %s",strerror(errno));
238       return(SMFIS_TEMPFAIL);
239     }
240 
241   if (mkdir(priv->workdir,0700)!=0)
242     {
243       syslog(LOG_CRIT,"mkdir(%s): %s",priv->workdir,strerror(errno));
244       return(SMFIS_TEMPFAIL);
245     }
246 
247   /*
248   ** FIXME use open() instead because Solaris is stupid
249   */
250   priv->datafp=fopen(priv->datafile,"w");
251   if ((priv->datafp)==NULL)
252     {
253       syslog(LOG_CRIT,"fopen(%s): %s",priv->datafile,strerror(errno));
254       return(SMFIS_TEMPFAIL);
255     }
256 
257   if (debug)
258     syslog(LOG_ERR,"mlfi_envfrom(0x%x, %s) => SMFIS_CONTINUE",
259 	   (int)ctx,argv[0]);
260   return(SMFIS_CONTINUE);
261 }
262 
mlfi_envrcpt(SMFICTX * ctx,char ** argv)263 sfsistat mlfi_envrcpt(SMFICTX *ctx, char **argv)
264 {
265   char *rcptaddr = smfi_getsymval(ctx, "{rcpt_addr}");
266 
267   if (debug)
268     syslog(LOG_ERR,"mlfi_envrcpt(0x%x, %s)",(int)ctx,argv[0]);
269 
270   if (rcptaddr && (strcasecmp(rcptaddr,"postmaster")==0))
271     {
272       /*
273       ** always accept postmaster mail
274       **
275       ** any mail that includes postmaster as a recipent in the
276       ** SMTP transaction will be accepted without any processing
277       **
278       ** DANGER => people can use this against us by including
279       ** postmaster in mail with viruses.  I don't consider this
280       ** dangerous enough to implement a solution, so unless it
281       ** starts happening I will ignore it.  Problem noted by
282       ** Thomas Lussnig.
283       */
284 
285       /*
286       ** James R. Stahr <stahr@binc.net discovered that the lack of a
287       ** cleanup(ctx) here causes some queue directories not to be
288       ** cleaned up.  I had noticed them appearing, but had never
289       ** worried enough about where there were coming from.
290       */
291       cleanup(ctx);
292       if (debug)
293 	syslog(LOG_ERR,"mlfi_envrcpt(0x%x, %s) => SMFIS_ACCEPT",
294 	       (int)ctx,argv[0]);
295       return SMFIS_ACCEPT;
296     }
297 
298   /* continue processing */
299   if (debug)
300     syslog(LOG_ERR,"mlfi_envrcpt(0x%x, %s) => SMFIS_CONTINUE",
301 	   (int)ctx,argv[0]);
302   return SMFIS_CONTINUE;
303 }
304 
mlfi_header(SMFICTX * ctx,char * headerf,char * headerv)305 sfsistat mlfi_header(SMFICTX *ctx, char *headerf, char *headerv)
306 {
307   struct mlfiPriv *priv = MLFIPRIV;
308 
309 
310   if (debug)
311     syslog(LOG_ERR,"smfi_header(0x%x, %s, <omitted>)",(int)ctx,headerf);
312 
313   /*
314   ** FIXME use write() instead because Solaris is stupid
315   */
316   if (fprintf(priv->datafp, "%s: %s\r\n", headerf, headerv)<=0)
317     return(SMFIS_TEMPFAIL);
318 
319   (priv->headers)++;
320   if (debug)
321     syslog(LOG_ERR,"smfi_header(0x%x, %s, <omitted>) => SMFIS_CONTINUE",
322 	   (int)ctx,headerf);
323   return SMFIS_CONTINUE;
324 }
325 
mlfi_eoh(SMFICTX * ctx)326 sfsistat mlfi_eoh(SMFICTX *ctx)
327 {
328   struct mlfiPriv *priv = MLFIPRIV;
329 
330   if (debug)
331     syslog(LOG_ERR,"smfi_eoh(0x%x)",(int)ctx);
332 
333   /*
334   ** FIXME use write() instead because Solaris is stupid
335   */
336   if (fprintf(priv->datafp, "\r\n")<=0)
337     {
338       if (debug)
339 	syslog(LOG_ERR,"smfi_eoh(0x%x) => SMFIS_TEMPFAIL",(int)ctx);
340       return(SMFIS_TEMPFAIL);
341     }
342 
343   if (debug)
344     syslog(LOG_ERR,"smfi_eoh(0x%x) => SMFIS_CONTINUE",(int)ctx);
345 
346   return SMFIS_CONTINUE;
347 }
348 
mlfi_body(SMFICTX * ctx,unsigned char * bodyp,size_t bodylen)349 sfsistat mlfi_body(SMFICTX *ctx, unsigned char *bodyp, size_t bodylen)
350 {
351   int nwritten;
352   struct mlfiPriv *priv = MLFIPRIV;
353 
354   if (debug)
355     syslog(LOG_ERR,"smfi_body(0x%x, 0x%x, %d)",(int)ctx,(int)bodyp,bodylen);
356 
357   (priv->bodyblocks)++;
358   (priv->bodysize)+=bodylen;
359 
360   /*
361   ** FIXME use write() instead because Solaris is stupid
362   */
363   if ((nwritten = fwrite(bodyp, bodylen, 1, priv->datafp)) != 1)
364     {
365       syslog(LOG_CRIT,"mlfi_body(): fwrite(): %s",strerror(errno));
366       (void) cleanup(ctx);
367       return SMFIS_TEMPFAIL;
368     }
369 
370   if (debug)
371     syslog(LOG_ERR,"smfi_body(0x%x, 0x%x, %d) => SMFIS_CONTINUE",
372 	   (int)ctx,(int)bodyp,bodylen);
373   return SMFIS_CONTINUE;
374 }
375 
externalcommand(SMFICTX * ctx,char * workdir,int fd,char * p,char ** av)376 int externalcommand(SMFICTX *ctx, char *workdir, int fd, char *p, char **av)
377 {
378   struct mlfiPriv *priv = MLFIPRIV;
379   char *ev[]={"PATH=/bin:/usr/bin",NULL};
380   int i;
381   int openmax=getdtablesize();
382 
383   if (debug)
384     syslog(LOG_ERR,"externalcommand(0x%x, ..., %s, ...)",(int)ctx,p);
385 
386   if ((priv->childpid)!=0)
387     {
388       syslog(LOG_CRIT,"externalcommand() called and priv->childpid is %d",
389 	     priv->childpid);
390     }
391 
392   priv->childstatus=0;
393   priv->childpid=fork();
394   switch(priv->childpid)
395     {
396     case -1: /* error */
397       syslog(LOG_CRIT,"fork(): %s",strerror(errno));
398       priv->childpid=0;
399       return(-1);
400       break;
401     case 0: /* child */
402       if (workdir!=NULL)
403 	{
404 	  if (chdir(workdir)!=0)
405 	    {
406 	      syslog(LOG_CRIT,"chdir(%s): %s",workdir,strerror(errno));
407 	    }
408 	}
409       if (fd==EOF)
410 	{
411 	  close(0);
412 	  close(1);
413 	  close(2);
414 	  open("/dev/null",O_RDONLY);
415 	  open("/dev/null",O_WRONLY);
416 	  open("/dev/null",O_WRONLY);
417 	}
418       else
419 	{
420 	  close(0);
421 	  open("/dev/null",O_RDONLY);
422 	  dup2(fd,1);
423 	  dup2(fd,2);
424 	}
425 
426       for (i=3;i<openmax;i++)
427 	close(i);
428 
429       execve(p, av, ev);
430       syslog(LOG_CRIT,"externalcommand() failed to execve() %s",p);
431       exit(1);
432       break;
433     default: /* parent */
434       waitpid(priv->childpid,&(priv->childstatus),0);
435       priv->childpid=0;
436       if (WIFEXITED(priv->childstatus))
437 	{
438 	  if (debug)
439 	    syslog(LOG_ERR,"externalcommand(0x%x, ..., %s, ...) => %d",
440 		   (int)ctx,p,WEXITSTATUS(priv->childstatus));
441 	  return(WEXITSTATUS(priv->childstatus));
442 	}
443       else
444 	{
445 	  if (debug)
446 	    syslog(LOG_ERR,"externalcommand(0x%x, ..., %s, ...) => -1",
447 		   (int)ctx,p);
448 	  return(-1);
449 	}
450       break;
451     }
452   if (debug)
453     syslog(LOG_ERR,"externalcommand(0x%x, ..., %s, ...) => -1",(int)ctx,p);
454   return(-1);
455 }
456 
unpack(SMFICTX * ctx,char * workdir,char * datafile)457 int unpack(SMFICTX *ctx, char *workdir, char *datafile)
458 {
459   struct mlfiPriv *priv = MLFIPRIV;
460   char *p=UNPACKER;
461   char *av[]={NULL,"--unique_names","-i",NULL,NULL};
462   union
463   {
464     struct dirent d;
465     char s[(sizeof(struct dirent)+MAXPATHLEN+1)];
466   } dirent;
467   struct dirent *direntp;
468   int count=0;
469 
470   av[0]=UNPACKER;
471   av[3]=datafile;
472 
473   externalcommand(ctx, workdir, EOF, p, av);
474 
475   /*
476   ** On Solaris you need to compile with:
477   **   -D_POSIX_PTHREAD_SEMANTICS -D_REENTRANT
478   */
479   priv->dir=opendir(priv->workdir);
480   while((readdir_r(priv->dir,&(dirent.d),&direntp)==0) && (direntp !=NULL))
481     {
482       count++;
483     }
484   closedir(priv->dir);
485   priv->dir=NULL;
486 
487   if (debug)
488     syslog(LOG_ERR,"unpack() found %d files, returning %d",count,(count>3));
489 
490   return(count>3);
491 }
492 
rmrf(SMFICTX * ctx)493 int rmrf(SMFICTX *ctx)
494 {
495   struct mlfiPriv *priv = MLFIPRIV;
496   char *p="/bin/rm";
497   char *av[]={"rm","-rf",NULL,NULL};
498 
499   av[2]=priv->workdir;
500 
501   return(externalcommand(ctx, NULL, EOF, p, av));
502 }
503 
virusscan(SMFICTX * ctx,char * path,char * format)504 sfsistat virusscan(SMFICTX *ctx, char *path, char *format)
505 {
506   struct mlfiPriv *priv = MLFIPRIV;
507   char buf[512];
508   char viruses[512];
509   char tmp[512];
510   int retval;
511   int fd;
512   int i;
513   char *word;
514   char *p=NULL;
515   char *av[]={NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
516 
517   /*
518   ** the size of av here is dependant on the sizeof avargs elsewhere
519   */
520   if ((sizeof(av)/sizeof(char *)+3)>(sizeof(avargs)/sizeof(char)))
521     {
522       syslog(LOG_ERR,"virusscan() detected that char *avargs[] is larger than char *av[]!\n");
523       syslog(LOG_ERR,"virusscan() not scanning anything!\n");
524       syslog(LOG_ERR,"virusscan() DANGER! DANGER! DANGER!\n");
525       return(SMFIS_CONTINUE);
526     }
527 
528   if (debug)
529     syslog(LOG_ERR,"virusscan(0x%x, ...)",(int)ctx);
530 
531   snprintf(buf,sizeof(buf),"%s/tmp.XXXXXX",priv->workdir);
532   fd=mkstemp(buf);
533   if (fd==EOF)
534     {
535       syslog(LOG_CRIT,"mkstemp(): %s",strerror(errno));
536       return(SMFIS_TEMPFAIL);
537     }
538 
539   priv->childfp=fdopen(fd,"rw");
540   if ((priv->childfp)==NULL)
541     {
542       syslog(LOG_CRIT,"fdopen(): %s",strerror(errno));
543       close(fd);
544       return(SMFIS_TEMPFAIL);
545     }
546 
547   p=path;
548   av[0]=path;
549   for (i=0;i<(sizeof(avargs)/sizeof(char *))&&avargs[i];i++)
550     {
551       av[i+1]=avargs[i];
552     }
553   av[i+1]=priv->workdir;
554 
555   retval=externalcommand(ctx,AVSCANDIR,fileno(priv->childfp),p,av);
556 
557   if (debug)
558     {
559       syslog(LOG_ERR,"virusscan(0x%x, ...)"
560 	     "externalcommand() returned %d on %s",
561 	     (int)ctx, retval,priv->workdir);
562     }
563 
564   if (ignorerror2 > 0 )
565     {
566 	if ( retval == 2 )
567 		retval = 0;
568     }
569 
570   if (retval>0)
571     {
572       fseek(priv->childfp,0,SEEK_SET);
573       memset(viruses,0,sizeof(buf));
574       while (fgets(buf,sizeof(buf),priv->childfp)!=NULL)
575 	{
576 	  word = buf;
577           if (skipwords > 0 )
578 	    {
579               word = strchr( word, ' ' );
580               if ( word == NULL )
581 	        word = buf;
582 	    }
583 	  if (sscanf(word,format,tmp)==1)
584 	    {
585 	      if (viruses[0])
586 		strncat(viruses," ",sizeof(viruses));
587 	      strncat(viruses,tmp,sizeof(viruses));
588 	    }
589 	}
590 
591       fclose(priv->childfp);
592 
593       priv->childfp=NULL;
594       if (viruses[0])
595 	priv->viruses=strdup(viruses);
596       else
597 	{
598 	  priv->viruses=strdup("please contact postmaster");
599 	  return(avfailcode);
600 	}
601       return(SMFIS_REJECT);
602     }
603 
604   if (retval < 0)
605     {
606       syslog(LOG_CRIT,"externalcommand() returned less than zero");
607       return(avfailcode);
608     }
609   if (debug)
610     syslog(LOG_ERR,"virusscan(0x%x, ...) => SMFIS_CONTINUE",(int)ctx);
611   return(SMFIS_CONTINUE);
612 }
613 
614 /*
615 ** only scan for known bad extensions
616 ** this is wrong but less likely to generate false positives...
617 */
isbadfilename(char * name)618 static int isbadfilename(char *name)
619 {
620   static int nbad=(sizeof(badext)/sizeof(char *));
621   int i;
622   size_t flen;
623   size_t elen;
624 
625   flen=strlen(name);
626 
627   for (i=0;i<nbad;i++)
628     {
629       elen=strlen(badext[i]);
630       if (flen > elen)
631 	{
632 	  if (strcasecmp((name+(flen-elen)),badext[i])==0)
633 	    {
634 	      return(1);
635 	    }
636 	}
637     }
638   return(0);
639 }
640 
extscan(SMFICTX * ctx)641 sfsistat extscan(SMFICTX *ctx)
642 {
643   struct mlfiPriv *priv = MLFIPRIV;
644   union
645   {
646     struct dirent d;
647     char s[(sizeof(struct dirent)+MAXPATHLEN+1)];
648   } dirent;
649   struct dirent *direntp;
650   char buf[1024];
651 
652   memset(buf,0,sizeof(buf));
653 
654   priv->dir=opendir(priv->workdir);
655 
656   while((readdir_r(priv->dir,&(dirent.d),&direntp)==0) && (direntp !=NULL))
657     {
658       if ((direntp->d_name[0] == '.')
659 	  && ((direntp->d_name[1] == 0)
660 	      ||((direntp->d_name[1] == '.') && (direntp->d_name[2] == 0))
661 	      )
662 	  )
663  	continue;
664 
665       if (isbadfilename(direntp->d_name))
666 	{
667 	  if (buf[0])
668 	    strncat(buf," ",sizeof(buf));
669 	  strncat(buf,direntp->d_name,sizeof(buf));
670 	}
671     }
672 
673   closedir(priv->dir);
674   priv->dir=NULL;
675 
676   if (buf[0])
677     {
678       priv->scripts=strdup(buf);
679       if ((priv->scripts)==NULL)
680 	{
681 	  syslog(LOG_CRIT,"strdup(): %s",strerror(errno));
682 	  return(SMFIS_TEMPFAIL);
683 	}
684       return(SMFIS_REJECT);
685     }
686   return(SMFIS_CONTINUE);
687 }
688 
mlfi_eom(SMFICTX * ctx)689 sfsistat mlfi_eom(SMFICTX *ctx)
690 {
691   struct mlfiPriv *priv = MLFIPRIV;
692   char message[2048];
693   sfsistat vstat;
694 #ifdef ANTIVIRUSTIMING
695   struct timeval stop;
696   struct timeval total;
697 #endif /* ANTIVIRUSTIMING */
698 
699   if (debug)
700     syslog(LOG_ERR,"smfi_eom(0x%x)",(int)ctx);
701 
702   if (priv==NULL)
703     {
704 
705       syslog(LOG_ERR,"mlfi_eom() priv is NULL in mlfi_eom?");
706       return(SMFIS_TEMPFAIL);
707     }
708 
709   if ((priv->datafp)!=NULL)
710     {
711 
712       fclose(priv->datafp);
713       (priv->datafp)=NULL;
714     }
715   else
716     {
717       syslog(LOG_ERR,"mlfi_eom() priv->datafp is NULL?");
718       return(SMFIS_TEMPFAIL);
719     }
720 
721 
722   if (((priv->headers)!=0)
723       && ((priv->bodyblocks)!=0)
724       && ((priv->bodysize)>100))
725     {
726 
727       if ((priv->workdir)==NULL)
728 	  syslog(LOG_ERR,"mlfi_eom() priv->workdir is NULL");
729       if ((priv->datafile)==NULL)
730 	  syslog(LOG_ERR,"mlfi_eom() priv->datafile is NULL");
731 
732 #ifdef ANTIVIRUSTIMING
733 	  if (gettimeofday(&(priv->start),NULL)!=0)
734 	    syslog(LOG_ERR,"gettimeofday(): %s",strerror(errno));
735 #endif /* ANTIVIRUSTIMING */
736       if (unpack(ctx,priv->workdir, priv->datafile))
737 	{
738 	  vstat=virusscan(ctx,AVSCANNER,FORMAT);
739 
740 #ifdef ANTIVIRUSTIMING
741 	  if (gettimeofday(&stop,NULL)!=0)
742 	    syslog(LOG_ERR,"cleanup(0x%x): gettimeofday(): %s",
743 		   (int)ctx,strerror(errno));
744 
745 	  total.tv_sec=(stop.tv_sec-(priv->start).tv_sec);
746 	  if (stop.tv_usec > (priv->start.tv_usec))
747 	    {
748 	      total.tv_usec=stop.tv_usec-(priv->start).tv_usec;
749 	    }
750 	  else
751 	    {
752 	      total.tv_sec--;
753 	      total.tv_usec=(1000000 + stop.tv_usec) - (priv->start).tv_usec;
754 	    }
755 	  syslog(LOG_ERR,"timing to unpack/virusscan %s: %d.%06d",
756 		 priv->qid,(int)total.tv_sec,(int)total.tv_usec);
757 #endif /* ANTIVIRUSTIMING */
758 
759 	  switch(vstat)
760 	    {
761 	    case SMFIS_REJECT:
762 
763 	      priv->hasvirus=1;
764 	      snprintf(message,sizeof(message),
765 		       "virus alert: %s",priv->viruses);
766  	      smfi_setreply(ctx, "550", "5.7.1", message);
767 	      cleanup(ctx);
768 	      if (debug)
769 		syslog(LOG_ERR,"smfi_eom(0x%x) => SMFIS_REJECT",(int)ctx);
770 	      return(SMFIS_REJECT);
771 	      break;
772 	    case SMFIS_CONTINUE:
773 	      break;
774 	    case SMFIS_TEMPFAIL:
775 	    default:
776 
777 	      cleanup(ctx);
778 	      if (debug)
779 		syslog(LOG_ERR,"smfi_eom(0x%x) => SMFIS_TEMPFAIL",(int)ctx);
780 	      return(SMFIS_TEMPFAIL);
781 	      break;
782 	    }
783 
784 	  vstat=extscan(ctx);
785 	  switch(vstat)
786 	    {
787 	    case SMFIS_REJECT:
788 	      priv->hasvirus=1;
789 	      snprintf(message,sizeof(message),
790 		       "illegal extension: %s",priv->scripts);
791  	      smfi_setreply(ctx, "550", "5.7.1", message);
792 	      cleanup(ctx);
793 	      if (debug)
794 		syslog(LOG_ERR,"smfi_eom(0x%x) => SMFIS_REJECT",(int)ctx);
795 	      return(SMFIS_REJECT);
796 	      break;
797 	    case SMFIS_CONTINUE:
798 	      break;
799 	    case SMFIS_TEMPFAIL:
800 	    default:
801 	      cleanup(ctx);
802 	      if (debug)
803 		syslog(LOG_ERR,"smfi_eom(0x%x) => SMFIS_TEMPFAIL",(int)ctx);
804 	      return(SMFIS_TEMPFAIL);
805 	      break;
806 	    }
807 	}
808     }
809 
810   cleanup(ctx);
811   if (debug)
812     syslog(LOG_ERR,"smfi_eom(0x%x) => SMFIS_CONTINUE",(int)ctx);
813   return(SMFIS_CONTINUE);
814 }
815 
mlfi_abort(SMFICTX * ctx)816 sfsistat mlfi_abort(SMFICTX *ctx)
817 {
818   if (debug)
819     syslog(LOG_ERR,"smfi_abort(0x%x)",(int)ctx);
820   cleanup(ctx);
821   if (debug)
822     syslog(LOG_ERR,"smfi_abort(0x%x) => SMFIS_CONTINUE",(int)ctx);
823   return(SMFIS_CONTINUE);
824 }
825 
cleanup(SMFICTX * ctx)826 void cleanup(SMFICTX *ctx)
827 {
828   struct mlfiPriv *priv = MLFIPRIV;
829   char buf[1024];
830 
831   if (debug)
832     syslog(LOG_ERR,"cleanup(0x%x)",(int)ctx);
833 
834   if (priv != NULL)
835     {
836       if ((priv->childpid)>0)
837 	{
838 	  syslog(LOG_ERR,"cleanup() killing %d\n",priv->childpid);
839 	  kill(priv->childpid,SIGABRT);
840 	  waitpid(priv->childpid,NULL,0);
841 	  priv->childpid=0;
842 	}
843 
844       if ((priv->childfp)!=NULL)
845 	{
846 	  fclose(priv->childfp);
847 	  priv->childfp=NULL;
848 	}
849 
850       if ((priv->datafp)!=NULL)
851 	{
852 	  fclose(priv->datafp);
853 	  priv->datafp=NULL;
854 	}
855 
856       if (priv->dir)
857 	{
858 	  closedir(priv->dir);
859 	  priv->dir=NULL;
860 	}
861 
862       if(priv->workdir)
863 	{
864 	  if ((priv->hasvirus) && (!purgevirus))
865 	    {
866 	      snprintf(buf,sizeof(buf),"%s/%s",QUARANTINE,priv->qid);
867 	      /*
868 	      ** If your abbatoir and your oubliette are on different
869 	      ** file systems then you need to do more work in
870 	      ** order to quarantine things.  Compile with -DCROSSFX
871 	      **
872 	      ** Theoretically, we don't leak anything from externalcommand
873 	      ** so it is safe to call this after we cleaned up from
874 	      ** it already.
875 	      */
876 #ifdef CROSSFS
877 	      {
878 		char *p="/bin/mv";
879 		char *av[]={"mv",NULL,NULL,NULL};
880 
881 		av[1]=priv->workdir;
882 		av[2]=buf;
883 
884 		externalcommand(ctx, NULL, EOF, p, av);
885 	      }
886 #else /* CROSSFS */
887 	      rename(priv->workdir,buf);
888 #endif /* CROSSFS */
889 	    }
890 	  else
891 	    {
892 	      rmrf(ctx);
893 	    }
894 	  priv->hasvirus=0;
895 	  free(priv->workdir);
896 	  priv->workdir=NULL;
897 	}
898 
899       (priv->headers)=0;
900       (priv->bodyblocks)=0;
901       (priv->bodysize)=0;
902 
903       if(priv->datafile)
904 	{
905 	  free(priv->datafile);
906 	  priv->datafile=NULL;
907 	}
908 
909       if (priv->viruses)
910 	{
911 	  free(priv->viruses);
912 	  priv->viruses=NULL;
913 	}
914 
915       if (priv->scripts)
916 	{
917 	  free(priv->scripts);
918 	  priv->scripts=NULL;
919 	}
920 
921       if(priv->qid)
922 	{
923 	  free(priv->qid);
924 	  priv->qid=NULL;
925 	}
926     }
927 
928   free(priv);
929   smfi_setpriv(ctx, NULL);
930 
931   if (debug)
932     syslog(LOG_ERR,"cleanup(0x%x) => (void)",(int)ctx);
933 }
934 
mlfi_close(SMFICTX * ctx)935 sfsistat mlfi_close(SMFICTX *ctx)
936 {
937   struct mlfiPriv *priv = MLFIPRIV;
938 
939   if (debug)
940     syslog(LOG_ERR,"smfi_close(0x%x)",(int)ctx);
941 
942   if (priv!=NULL)
943     {
944       cleanup(ctx);
945     }
946   if (debug)
947     syslog(LOG_ERR,"smfi_close(0x%x) => SMFIS_CONTINUE",(int)ctx);
948   return SMFIS_CONTINUE;
949 }
950 
951 struct smfiDesc smfilter =
952 {
953     "antivirus",    /* filter name */
954     SMFI_VERSION,   /* version code -- do not change */
955     0,              /* flags */
956     NULL,           /* connection info filter */
957     NULL,           /* SMTP HELO command filter */
958     mlfi_envfrom,   /* envelope sender filter */
959     mlfi_envrcpt,   /* envelope recipient filter */
960     mlfi_header,    /* header filter */
961     mlfi_eoh,       /* end of header */
962     mlfi_body,      /* body block filter */
963     mlfi_eom,       /* end of message */
964     mlfi_abort,     /* message aborted */
965     mlfi_close,     /* connection cleanup */
966 };
967 
968 static void
usage(void)969 usage(void)
970 {
971     fprintf(stderr,"Usage: antivirus [-C <configfile>] [-v] [-V] [-n]\n");
972     fprintf(stderr,"  -v: version\n");
973     fprintf(stderr,"  -V: verbose version\n");
974     fprintf(stderr,"  -n: do not use pid file\n");
975 }
976 
977 /*
978 ** inspired by Thomas Lussnig <thomas@bewegungsmelder.de>
979 */
droproot(void)980 int droproot(void)
981 {
982   uid_t myuid;
983   uid_t myeuid;
984   struct passwd *pwent;
985   struct group *grent;
986 
987   myuid=getuid();
988   myeuid=geteuid();
989 
990   if ((myuid!=0) && (myeuid!=0))
991     return(0);
992 
993   grent=getgrnam(SAFEGROUP);
994   if (grent==NULL)
995     {
996       fprintf(stderr,"droproot(): getgrnam(%s): %s\n",
997 	      SAFEGROUP,strerror(errno));
998       return(-1);
999     }
1000   if (setregid(grent->gr_gid,grent->gr_gid)!=0)
1001     {
1002       fprintf(stderr,"droproot(): setregid(%d,%d): %s\n",
1003 	      (int)grent->gr_gid,(int)grent->gr_gid,strerror(errno));
1004       return(-1);
1005     }
1006   if (initgroups(SAFEUSER,grent->gr_gid))
1007     {
1008       fprintf(stderr,"droproot(): initgroups(%s,%d): %s\n",
1009               SAFEUSER,(int)(grent->gr_gid),strerror(errno));
1010       return(-1);
1011     }
1012 
1013   pwent=getpwnam(SAFEUSER);
1014   if (pwent==NULL)
1015     {
1016       fprintf(stderr,"droproot(): getpwnam(%s): %s\n",
1017 	      SAFEUSER,strerror(errno));
1018       return(-1);
1019     }
1020   if (setreuid(pwent->pw_uid,pwent->pw_uid)!=0)
1021     {
1022       fprintf(stderr,"droproot(): setreuid(%d,%d): %s\n",
1023 	      (int)pwent->pw_uid,(int)pwent->pw_uid,strerror(errno));
1024       return(-1);
1025     }
1026 
1027   myuid=getuid();
1028   myeuid=geteuid();
1029 
1030   if ((myuid==0) || (myeuid==0))
1031     {
1032       fprintf(stderr,"droproot(): still root after setreuid()?\n");
1033       return(-1);
1034     }
1035 
1036   return(0);
1037 }
1038 
readconfig(void)1039 static int readconfig(void)
1040 {
1041   FILE *fp;
1042   char buf[512];
1043   char fmt[64];
1044   char token[64];
1045   char value[256];
1046   char *ptr;
1047   int scanned;
1048   int set=0;
1049   int line=0;
1050   int i;
1051 
1052   fflush(stdout);
1053 
1054   if (configfile==NULL)
1055     {
1056       fp=fopen(DEFAULTCONFIGFILE,"r");
1057       if (fp==NULL)
1058 	{
1059 	  if (errno==ENOENT)
1060 	    return(0);
1061 	  snprintf(buf,sizeof(buf),"fopen(%s)",configfile);
1062 	  perror(buf);
1063 	  return(-1);
1064 	}
1065     }
1066   else
1067     {
1068       fp=fopen(configfile,"r");
1069       if (fp==NULL)
1070 	{
1071 	  snprintf(buf,sizeof(buf),"fopen(%s)",configfile);
1072 	  perror(buf);
1073 	  return(-1);
1074 	}
1075     }
1076 
1077   while (fgets(buf,sizeof(buf),fp)!=NULL)
1078     {
1079       line++;
1080       ptr=index(buf,'#');
1081       if (ptr)
1082 	*ptr='\0';
1083 
1084       snprintf(fmt,sizeof(fmt),"%%%ds %%%dc",sizeof(token),sizeof(value));
1085       bzero(token,sizeof(token));
1086       bzero(value,sizeof(value));
1087       scanned=sscanf(buf,fmt,token,value);
1088       /*
1089       ** nuke the trailing whitespace here
1090       */
1091       i=0;
1092       while (value[i]) i++;
1093       i--;
1094       while ((i>0) && isspace((int)value[i]))
1095 	{
1096 	  value[i]='\0';
1097 	  i--;
1098 	}
1099 
1100       if (scanned==1)
1101 	{
1102 	  fprintf(stderr,"Error on line %d in configure file: %s\n",line,buf);
1103 	  fclose(fp);
1104 	  return(-1);
1105 	}
1106       if (scanned==2)
1107 	{
1108 #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;}
1109 	  DOTOKEN("HOMEDIR",HOMEDIR);
1110 	  DOTOKEN("BASEDIR",BASEDIR);
1111 	  DOTOKEN("AVSCANDIR",AVSCANDIR);
1112 	  DOTOKEN("QUARANTINE",QUARANTINE);
1113 	  DOTOKEN("AVSCANNER",AVSCANNER);
1114 	  DOTOKEN("AVSCANARGS",AVSCANARGS);
1115 	  DOTOKEN("UNPACKER",UNPACKER);
1116 	  DOTOKEN("SAFEUSER",SAFEUSER);
1117 	  DOTOKEN("SAFEGROUP",SAFEGROUP);
1118 	  DOTOKEN("PIDFILE",PIDFILE);
1119 	  DOTOKEN("SOCKETNAME",SOCKETNAME);
1120 	  DOTOKEN("TIMEOUT",TIMEOUT);
1121 	  DOTOKEN("AVPRODUCT",AVPRODUCT);
1122 	  DOTOKEN("AVFAILACTION",AVFAILACTION);
1123 	  DOTOKEN("VIRUSACTION",VIRUSACTION);
1124 	  fprintf(stderr,"Line %d unrecognized: %s\n",line,buf);
1125 	  fclose(fp);
1126 	  return(-1);
1127 	}
1128     }
1129 
1130   if (set==0)
1131     {
1132       fprintf(stderr,"Warning: %s contained no valid lines\n",configfile);
1133     }
1134 
1135   fclose(fp);
1136   return(0);
1137 }
1138 
1139 
buildavargs(void)1140 int buildavargs(void)
1141 {
1142   int i=0;
1143   int n=0;
1144   int c=0;
1145   char buf[512];
1146 
1147   /*
1148   ** put strings into *avargs[] for later use
1149   */
1150 
1151   bzero(buf,sizeof(buf));
1152 
1153   if (AVSCANARGS==NULL)
1154     {
1155       fprintf(stderr,"buildavargs(): AVSCANARGS is NULL?\n");
1156       return(-1);
1157     }
1158 
1159   if (strlen(AVSCANARGS)==0)
1160     {
1161       /*
1162       ** nothing to do
1163       */
1164       return(0);
1165     }
1166 
1167   for (i=0,n=0,c=0;(i<sizeof(buf))&&(AVSCANARGS[i]);i++)
1168     {
1169 
1170       if (n==(sizeof(avargs)/sizeof(char *)))
1171 	{
1172 	  fprintf(stderr,"buildavargs(): too many args in AVSCANARGS!\n");
1173 	  return(-1);
1174 	}
1175 
1176       if (AVSCANARGS[i]==' ')
1177 	{
1178 	  if (c>0)
1179 	    {
1180 	      printf("Found end of word %d\n",n);
1181 	      avargs[n]=(char *)strdup(buf);
1182 	      if (avargs[n]==NULL)
1183 		{
1184 		  perror("buildavargs(): strdup()");
1185 		  return(-1);
1186 		}
1187 	      bzero(buf,sizeof(buf));
1188 	      n++;
1189 	      c=0;
1190 	    }
1191 	}
1192       else
1193 	{
1194 	  buf[c]=AVSCANARGS[i];
1195 	  c++;
1196 	}
1197     }
1198   /*
1199   ** consume the last word
1200   */
1201   if (c>0)
1202     {
1203       avargs[n]=(char *)strdup(buf);
1204       if (avargs[n]==NULL)
1205 	{
1206 	  perror("buildavargs(): strdup()");
1207 	  return(-1);
1208 	}
1209     }
1210   return(0);
1211 }
1212 
init(void)1213 int init(void)
1214 {
1215   if (readconfig()<0)
1216     return(-1);
1217 
1218   if (HOMEDIR==NULL) HOMEDIR=CONF_HOMEDIR;
1219   if (BASEDIR==NULL) BASEDIR=CONF_BASEDIR;
1220   if (AVSCANDIR==NULL) AVSCANDIR=CONF_AVSCANDIR;
1221   if (QUARANTINE==NULL) QUARANTINE=CONF_QUARANTINE;
1222   if (AVSCANNER==NULL) AVSCANNER=CONF_AVSCANNER;
1223   if (AVSCANARGS==NULL) AVSCANARGS=CONF_AVSCANARGS;
1224   if (UNPACKER==NULL) UNPACKER=CONF_UNPACKER;
1225   if (SAFEUSER==NULL) SAFEUSER=CONF_SAFEUSER;
1226   if (SAFEGROUP==NULL) SAFEGROUP=CONF_SAFEGROUP;
1227   if (PIDFILE==NULL) PIDFILE=CONF_PIDFILE;
1228   if (SOCKETNAME==NULL) SOCKETNAME=CONF_SOCKETNAME;
1229   if (TIMEOUT==NULL) TIMEOUT=CONF_TIMEOUT;
1230   if (AVPRODUCT==NULL) AVPRODUCT=CONF_AVPRODUCT;
1231   if (AVFAILACTION==NULL) AVFAILACTION=CONF_AVFAILACTION;
1232   if (VIRUSACTION==NULL) VIRUSACTION=CONF_VIRUSACTION;
1233 
1234   skipwords = 0;
1235   if (strcasecmp(AVPRODUCT,"mcafee")==0)
1236     {
1237       FORMAT=FORMAT_MCAFEE;
1238       AVSCANARGS=SCANARGS_MCAFEE;
1239     }
1240   else if (strcasecmp(AVPRODUCT,"sophos")==0)
1241     {
1242       FORMAT=FORMAT_SOPHOS;
1243     }
1244   else if ((strcasecmp(AVPRODUCT,"fsav")==0)
1245 	   || (strcasecmp(AVPRODUCT,"f-prot")==0))
1246     {
1247       FORMAT=FORMAT_FSAV;
1248     }
1249   else if (strcasecmp(AVPRODUCT,"clamav")==0)
1250     {
1251       FORMAT=FORMAT_CLAMAV;
1252       AVSCANARGS=SCANARGS_CLAMAV;
1253       skipwords = 1;
1254       ignorerror2 = 1;
1255     }
1256   else
1257     {
1258       fprintf(stderr,"init(): unrecognized AVPRODUCT %s\n",AVPRODUCT);
1259       fprintf(stderr,"init(): valid values are: mcafee, fsva, sophos, clamav\n");
1260       return(-1);
1261     }
1262 
1263   if (strcasecmp(AVFAILACTION,"continue")==0)
1264     {
1265       avfailcode=SMFIS_CONTINUE;
1266     }
1267   else   if (strcasecmp(AVFAILACTION,"tempfail")==0)
1268     {
1269       avfailcode=SMFIS_TEMPFAIL;
1270     }
1271   else   if (strcasecmp(AVFAILACTION,"reject")==0)
1272     {
1273       avfailcode=SMFIS_REJECT;
1274     }
1275   else
1276     {
1277       fprintf(stderr,"init(): unrecognized AVFAILACTION %s\n",AVFAILACTION);
1278       fprintf(stderr,"init(): valid values are: continue, tempfail, reject\n");
1279       return(-1);
1280     }
1281 
1282   if ((strcasecmp(VIRUSACTION,"quarantine")==0)
1283       ||(strcasecmp(VIRUSACTION,"save")==0))
1284     {
1285       purgevirus=0;
1286     }
1287   else if ((strcasecmp(VIRUSACTION,"purge")==0)
1288 	   || (strcasecmp(VIRUSACTION,"delete")==0))
1289     {
1290       purgevirus=1;
1291     }
1292   else
1293     {
1294       fprintf(stderr,"init(): unrecognized VIRUSACTION %s\n",VIRUSACTION);
1295       fprintf(stderr,"init(): valid values are: quarantine or purge\n");
1296       return(-1);
1297     }
1298 
1299   /*
1300   ** build up the *avargs[] array
1301   */
1302   if (buildavargs()==(-1))
1303     {
1304       fprintf(stderr,"init(): buildavargs() failed\n");
1305       return(-1);
1306     }
1307 
1308 
1309 
1310   return(0);
1311 }
1312 
1313 /*
1314 ** this is sometimes used in testing
1315 */
1316 /*
1317 void dumpconfig(void)
1318 {
1319   printf("HOMEDIR    %s\n",HOMEDIR);
1320   printf("BASEDIR    %s\n",BASEDIR);
1321   printf("AVSCANDIR  %s\n",AVSCANDIR);
1322   printf("QUARANTINE %s\n",QUARANTINE);
1323   printf("AVSCANNER  %s\n",AVSCANNER);
1324   printf("UNPACKER   %s\n",UNPACKER);
1325   printf("SAFEUSER   %s\n",SAFEUSER);
1326   printf("SAFEGROUP  %s\n",SAFEGROUP);
1327   printf("PIDFILE    %s\n",PIDFILE);
1328   printf("SOCKETNAME %s\n",SOCKETNAME);
1329   printf("TIMEOUT    %s\n",TIMEOUT);
1330 }
1331 */
1332 
1333 /*
1334 ** Original version by Thomas Lussnig <thomas@bewegungsmelder.de>
1335 ** Modified by me (wcolburn@nmt.edu)
1336 */
versioninfo(int longversion)1337 void versioninfo(int longversion)
1338 {
1339   int i;
1340 
1341   printf("\n");
1342   printf("%s\n",copyright);
1343   printf("%s\n",versionid);
1344   printf("\n");
1345   printf("%s\n",webpage);
1346   if (longversion)
1347     {
1348       printf("\n");
1349       for (i=0;rcsid[i];i++)
1350 	printf("%s\n",rcsid[i]);
1351 
1352       printf("%s\n",compilestamp);
1353       printf("%s\n",sendmailstamp);
1354     }
1355   printf("\n");
1356 }
1357 
main(int argc,char ** argv,char ** envp)1358 int main(int argc, char **argv, char **envp)
1359 {
1360   int retval;
1361   char c;
1362   FILE *fp;
1363   const char *args = "p:C:hnvVdD";
1364   extern char *optarg;
1365   char *socketoverride=NULL;
1366   int usepidfile=1;
1367   int daemonize=1;
1368 #ifdef USESETRLIMIT
1369   struct rlimit rlim;
1370 #endif /* USESETRLIMIT */
1371 
1372   while ((c = getopt(argc, argv, args)) != (char)EOF)
1373     {
1374       switch (c)
1375         {
1376 	case 'd':
1377 	  debug++;
1378 	  break;
1379 	case 'D':
1380 	  daemonize=!daemonize;
1381 	  break;
1382 	case 'p':
1383 	  if (socketoverride!=NULL)
1384 	    {
1385 	      fprintf(stderr,"Socket already set\n");
1386 	      exit(EX_SOFTWARE);
1387 	    }
1388 	  socketoverride=strdup(optarg);
1389 	  if (socketoverride==NULL)
1390 	    {
1391 	      perror("malloc()");
1392 	      exit(EX_SOFTWARE);
1393 	    }
1394 	  break;
1395 	case 'C':
1396 	  /*
1397 	  ** configure file
1398 	  */
1399 	  if ((optarg==NULL) || (*optarg=='\0'))
1400 	    {
1401 	      fprintf(stderr,"Illegal configure file\n");
1402 	      exit(EX_SOFTWARE);
1403 	    }
1404 	  if (configfile != NULL)
1405 	    {
1406 	      fprintf(stderr,"Configure file already set\n");
1407 	      exit(EX_SOFTWARE);
1408 	    }
1409 	  configfile=strdup(optarg);
1410 	  if (configfile==NULL)
1411 	    {
1412 	      perror("malloc()");
1413 	      exit(EX_SOFTWARE);
1414 	    }
1415 	  break;
1416 	case 'n':
1417 	  usepidfile=0;
1418 	  break;
1419 	case 'v':
1420 	  versioninfo(0);
1421 	  exit(0);
1422 	  break;
1423 	case 'V':
1424 	  versioninfo(1);
1425 	  exit(0);
1426 	  break;
1427         case 'h':
1428         default:
1429 	  usage();
1430 	  exit(0);
1431 	  break;
1432         }
1433     }
1434 
1435   if (init()!=0)
1436     {
1437       fprintf(stderr,"init() failed\n");
1438       exit(EX_SOFTWARE);
1439     }
1440 
1441 
1442 #ifdef USESETRLIMIT
1443   /*
1444   ** Some people need more file descriptors
1445   */
1446   if (getrlimit(RLIMIT_NOFILE, &rlim)<0)
1447     {
1448       fprintf(stderr,"main(): getrlimit(RLIMIT_NOFILE, ...) failed\n");
1449     }
1450   else
1451     {
1452       /*
1453       ** the value 1024 should probably be in the configuration file
1454       */
1455       if (rlim.rlim_cur<1024)
1456 	{
1457 	  rlim.rlim_cur=1024;
1458 	  rlim.rlim_max=1024;
1459 	  if (setrlimit(RLIMIT_NOFILE, &rlim)<0)
1460 	    {
1461 	      fprintf(stderr,"main(): setrlimit(RLIMIT_NOFILE, {%d,%d}) failed\n",
1462 		      (int)rlim.rlim_cur,(int)rlim.rlim_max);
1463 	    }
1464 	}
1465     }
1466 #endif /* USESETRLIMIT */
1467 
1468 
1469   /*
1470   ** allow -p to override the default and the config file
1471   */
1472   if (socketoverride != NULL)
1473     SOCKETNAME=socketoverride;
1474 
1475   if (droproot()!=0)
1476     {
1477       fprintf(stderr,"droproot() failed\n");
1478       exit(EX_SOFTWARE);
1479     }
1480 
1481   openlog("antivirus",LOG_PID,LOG_MAIL);
1482 
1483   if (mkdir(HOMEDIR,0700)!=0)
1484     {
1485       if (errno != EEXIST)
1486 	{
1487 	  syslog(LOG_CRIT,"mkdir(%s): %s",HOMEDIR,strerror(errno));
1488 	  fprintf(stderr,"mkdir(%s): %s",HOMEDIR,strerror(errno));
1489 	  exit(EX_SOFTWARE);
1490 	}
1491     }
1492 
1493   if (chdir(HOMEDIR)!=0)
1494     {
1495       syslog(LOG_CRIT,"chdir(%s): %s",HOMEDIR,strerror(errno));
1496       fprintf(stderr,"chdir(%s): %s",HOMEDIR,strerror(errno));
1497       exit(EX_SOFTWARE);
1498     }
1499 
1500   if (mkdir(BASEDIR,0700)!=0)
1501     {
1502       if (errno != EEXIST)
1503 	{
1504 	  syslog(LOG_CRIT,"mkdir(%s): %s",BASEDIR,strerror(errno));
1505 	  fprintf(stderr,"mkdir(%s): %s",BASEDIR,strerror(errno));
1506 	  exit(EX_SOFTWARE);
1507 	}
1508     }
1509 
1510   if (mkdir(QUARANTINE,0700)!=0)
1511     {
1512       if (errno != EEXIST)
1513 	{
1514 	  syslog(LOG_CRIT,"mkdir(%s): %s",QUARANTINE,strerror(errno));
1515 	  fprintf(stderr,"mkdir(%s): %s",QUARANTINE,strerror(errno));
1516 	  exit(EX_SOFTWARE);
1517 	}
1518     }
1519 
1520   /*
1521   ** do the socket
1522   */
1523   if(smfi_setconn(SOCKETNAME) == MI_FAILURE)
1524     {
1525       fprintf(stderr,"smfi_setconn(%s) failed",SOCKETNAME);
1526       syslog(LOG_CRIT,"smfi_setconn(%s) failed",SOCKETNAME);
1527       exit(EX_SOFTWARE);
1528     }
1529   /*
1530   ** If we're using a local socket, make sure it doesn't
1531   ** already exist.
1532   **
1533   ** Why do they do this after the smfi_setconn() call?
1534   */
1535   if(strncmp(SOCKETNAME, "unix:", 5) == 0)
1536     {
1537       if (unlink(SOCKETNAME + 5)<0)
1538 	{
1539 	  if (errno!=ENOENT)
1540 	    perror("unlink()");
1541 	}
1542     }
1543   else if(strncmp(SOCKETNAME, "local:", 6) == 0)
1544     {
1545       if (unlink(SOCKETNAME + 6)<0)
1546 	{
1547 	  if (errno!=ENOENT)
1548 	    perror("unlink()");
1549 	}
1550     }
1551 
1552   /*
1553   ** do the timeout
1554   */
1555   if (TIMEOUT && (*TIMEOUT != '\0'))
1556     {
1557       if(smfi_settimeout(atoi(TIMEOUT)) == MI_FAILURE)
1558 	{
1559 	  fprintf(stderr,"smfi_settimeout(%s) failed",TIMEOUT);
1560 	  syslog(LOG_CRIT,"smfi_settimeout(%s) failed",TIMEOUT);
1561 	  exit(EX_SOFTWARE);
1562 	}
1563     }
1564 
1565   /*
1566   ** background the process
1567   */
1568   if (daemonize)
1569     {
1570       switch(fork())
1571 	{
1572 	case -1:
1573 	  perror("fork()");
1574 	  exit(1);
1575 	  break;
1576 	case 0: /* child */
1577 	  close(0);
1578 	  close(1);
1579 	  close(2);
1580 	  open("/dev/null",O_RDONLY);
1581 	  open("/dev/null",O_WRONLY);
1582 	  open("/dev/null",O_WRONLY);
1583 	  setsid();
1584 	  break;
1585 	default: /* parent */
1586 	  exit(0);
1587 	  break;
1588 	}
1589     }
1590 
1591   if (usepidfile)
1592     {
1593       fp=fopen(PIDFILE,"w");
1594       if (fp==NULL)
1595 	{
1596 	  fprintf(stderr,"Could not open %s\n",PIDFILE);
1597 	  perror("fopen()");
1598 	}
1599       else
1600 	{
1601 	  fprintf(fp,"%d\n",(int)getpid());
1602 	  fclose(fp);
1603 	}
1604     }
1605 
1606   if (smfi_register(smfilter) == MI_FAILURE)
1607     {
1608       fprintf(stderr, "smfi_register failed\n");
1609       exit(EX_UNAVAILABLE);
1610     }
1611   syslog(LOG_CRIT,"ready to answer queries");
1612   retval = smfi_main();
1613   if (retval != 0)
1614     {
1615       fprintf(stderr,"smfi_main() => %d\n",retval);
1616     }
1617   if (usepidfile)
1618     {
1619       if (truncate(PIDFILE,0)!=0)
1620 	perror("truncate()");
1621     }
1622   return(abs(retval));
1623 }
1624 
1625 /*
1626  * $Log: antivirus.c,v $
1627  * Revision 3.30  2003/06/30 16:15:42  wcolburn
1628  * (int)value[i] inside isspace()
1629  *
1630  * Revision 3.29  2003/06/27 16:36:45  wcolburn
1631  * took out trailing whitespace because I'm an idiot
1632  *
1633  * Revision 3.28  2003/06/26 19:23:22  wcolburn
1634  * changed the parser to apparantly pickup tokens and multiword values now!
1635  *
1636  * Revision 3.27  2003/06/26 19:17:41  wcolburn
1637  * also check length of string at beginning, just in case! (AVSCANARGS)
1638  *
1639  * Revision 3.26  2003/06/26 19:16:05  wcolburn
1640  * Fix case of AVSCANARGS == ""
1641  *
1642  * Revision 3.25  2003/06/26 18:54:48  wcolburn
1643  * rearranged how --unzip is set for AVSCANARGS
1644  *
1645  * Revision 3.24  2003/06/26 18:48:56  wcolburn
1646  * char *AVSCANARGS, char *avargs[], and the buildavargs() calls so that I
1647  * can pass aribtary arguments to the scanner.  Primitive, but it should be
1648  * sufficient.
1649  *
1650  * Revision 3.23  2003/06/25 19:12:56  wcolburn
1651  * took out .zi
1652  *
1653  * Revision 3.22  2003/06/25 17:47:00  wcolburn
1654  * added ".zi" as a temporary AV measure.
1655  *
1656  * Revision 3.21  2002/11/25 16:21:30  wcolburn
1657  * do not scan mesasges less than 100 bytes.
1658  *
1659  * Revision 3.20  2002/05/17 14:03:03  wcolburn
1660  * \r\n in eoh
1661  *
1662  * Revision 3.19  2002/05/17 14:02:47  wcolburn
1663  * \r\n in headers
1664  *
1665  * Revision 3.18  2002/04/23 14:44:02  wcolburn
1666  * some tweaks I forgot, and added --unique-names from Emmanuel Collignon
1667  *
1668  * Revision 3.17  2002/04/19 14:18:33  wcolburn
1669  * USESETULIMIT
1670  *
1671  * Revision 3.16  2002/04/15 22:23:04  wcolburn
1672  * fixed a typo in an error message
1673  *
1674  * Revision 3.15  2002/04/15 22:19:00  wcolburn
1675  * rlimit in main()
1676  *
1677  * Revision 3.14  2002/04/12 16:52:07  wcolburn
1678  * VIRUSACTION to purge or quarantine viruses
1679  *
1680  * Revision 3.13  2002/04/12 16:06:55  wcolburn
1681  * added default in case AVFAILACTION is not set
1682  *
1683  * Revision 3.12  2002/04/12 15:59:44  wcolburn
1684  * AVFAILACTION
1685  *
1686  * Revision 3.11  2002/04/12 15:49:07  wcolburn
1687  * syslog after execve in externalcommand()
1688  *
1689  * Revision 3.10  2002/04/12 15:43:10  wcolburn
1690  * error checking and return value and error message for AVPRODUCT
1691  *
1692  * Revision 3.9  2002/04/12 15:41:16  wcolburn
1693  * AVPRODUCT works correctly now.  :)
1694  *
1695  * Revision 3.8  2002/04/12 15:31:34  wcolburn
1696  * AVPRODUCT to select what kind of format string to use.  This
1697  * should/could select the name of the scanner, but I don't know any other
1698  * scanner names.
1699  *
1700  * Revision 3.7  2002/04/10 21:20:58  wcolburn
1701  * added another comment
1702  *
1703  * Revision 3.6  2002/04/10 20:44:39  wcolburn
1704  * updated a comment
1705  *
1706  * Revision 3.5  2002/04/10 20:42:50  wcolburn
1707  * "ready to answer queries" syslog message
1708  *
1709  * Revision 3.4  2002/04/10 20:41:56  wcolburn
1710  * daemonize option, suggested by Sebastien VAUTHEROT
1711  *
1712  * Revision 3.3  2002/04/10 20:40:21  wcolburn
1713  * same but for an extention
1714  *
1715  * Revision 3.2  2002/04/10 20:39:46  wcolburn
1716  * correct reply for finding a virus from Sebastien VAUTHEROT
1717  *
1718  * Revision 3.1  2002/04/10 20:37:20  wcolburn
1719  * FREEBSD wait include from Sebastien VAUTHEROT
1720  *
1721  * Revision 3.0  2002/04/10 18:20:37  wcolburn
1722  * The dawn of a new world
1723  *
1724  * Revision 2.136  2002/04/10 14:30:30  wcolburn
1725  * ok, now the dirent and direntp stuff is all "correct" as per the manual
1726  * page.  Both of them even, not just one of them!
1727  *
1728  * Revision 2.135  2002/04/10 14:27:22  wcolburn
1729  * not quite
1730  *
1731  * Revision 2.134  2002/04/10 13:55:23  wcolburn
1732  * oops?  I need to look back into this dirent thing in extscan.
1733  *
1734  * Revision 2.133  2002/04/09 16:58:34  wcolburn
1735  * save a few cycles checking for bad file names, from Steve Barber
1736  *
1737  * Revision 2.132  2002/04/09 14:56:45  wcolburn
1738  * Added in a call to /bin/mv for Steve Barber and his tmpfs.
1739  *
1740  * Revision 2.131  2002/04/09 14:47:10  wcolburn
1741  * make a union for dirent as in the diffs Steve Barber sent me
1742  * This is mostly stylisting, avoids casting a character array to
1743  * structure.
1744  *
1745  * Revision 2.130  2002/04/09 14:32:14  wcolburn
1746  * Ok, Steve Barber sent me a huge diff file.  The rename (last revision
1747  * I checked in) and the Sophos stuff are from him.  Actually, he used
1748  * REGEX for sophos, but it doesn't seem to need it.
1749  *
1750  * Revision 2.129  2002/04/09 14:23:23  wcolburn
1751  * more generic AVSANNER and AVSCANDIR
1752  *
1753  * Revision 2.128  2002/04/01 19:54:19  wcolburn
1754  * moved the location of the timing
1755  *
1756  * Revision 2.127  2002/04/01 17:53:00  wcolburn
1757  * moved final timing to have access to qid
1758  *
1759  * Revision 2.126  2002/04/01 17:49:18  wcolburn
1760  * oops, now it is right
1761  *
1762  * Revision 2.125  2002/04/01 17:48:07  wcolburn
1763  * ANTIVIRUSTIMING
1764  *
1765  * Revision 2.124  2002/03/26 21:02:08  wcolburn
1766  * openmax and a few cosmetic things
1767  *
1768  * Revision 2.123  2002/03/26 20:45:59  wcolburn
1769  * took out a comment
1770  *
1771  * Revision 2.122  2002/03/26 17:05:35  wcolburn
1772  * rearranged fd handling in virusscan()
1773  *
1774  * Revision 2.121  2002/03/26 16:55:14  wcolburn
1775  * added Toolan's initgroups() code to droproot()
1776  *
1777  * Revision 2.120  2002/03/26 16:52:35  wcolburn
1778  * took out mlfi_connect() in hopes to make the error-out go away
1779  *
1780  * Revision 2.119  2002/03/26 16:12:46  wcolburn
1781  * close files 3..NOFILE in externalcommand before exec
1782  *
1783  * Revision 2.118  2002/03/26 16:07:04  wcolburn
1784  * took out a misleading newline
1785  *
1786  * Revision 2.117  2002/03/25 22:42:54  wcolburn
1787  * *** empty log message ***
1788  *
1789  * Revision 2.116  2002/03/25 22:06:40  wcolburn
1790  * close fp in readconfig()
1791  *
1792  * Revision 2.115  2002/03/25 22:05:04  wcolburn
1793  * setsid()
1794  *
1795  * Revision 2.114  2002/03/18 16:08:04  wcolburn
1796  * cleaned up code a little for solaris
1797  *
1798  * Revision 2.113  2002/03/04 18:50:32  wcolburn
1799  * fork at beginning to dettach terminal
1800  *
1801  * Revision 2.112  2002/02/28 21:11:16  wcolburn
1802  * added and removed some signal code for children
1803  *
1804  * Revision 2.111  2002/02/28 20:40:37  wcolburn
1805  * fixed comment about f-prot/FSAV
1806  *
1807  * Revision 2.110  2002/02/28 20:38:59  wcolburn
1808  * took out inuse flag
1809  *
1810  * Revision 2.109  2002/02/28 20:36:39  wcolburn
1811  * added an inuse to the private data
1812  *
1813  * Revision 2.108  2002/02/22 20:41:57  wcolburn
1814  * took out a lot of debugging
1815  *
1816  * Revision 2.107  2002/02/22 20:30:37  wcolburn
1817  * more debugging, and changed the fd closures in externalcommand
1818  *
1819  * Revision 2.106  2002/02/22 19:51:50  wcolburn
1820  * corrected spelling in some debug statements
1821  *
1822  * Revision 2.105  2002/02/22 19:44:11  wcolburn
1823  * no chdir() when rmrf()ing
1824  *
1825  * Revision 2.104  2002/02/22 19:31:37  wcolburn
1826  * tried some things but failed
1827  *
1828  * Revision 2.103  2002/02/22 19:15:45  wcolburn
1829  * syslog kill() in cleanup()
1830  *
1831  * Revision 2.102  2002/02/22 19:09:49  wcolburn
1832  * rearranged cleanup() for more sensical ordering?
1833  *
1834  * Revision 2.101  2002/02/22 19:01:35  wcolburn
1835  * messed up the chdir thing because I forgot how it worked.  :)
1836  *
1837  * Revision 2.100  2002/02/22 18:55:42  wcolburn
1838  * moved the rmrf() in cleanup() to the top
1839  *
1840  * Revision 2.99  2002/02/22 18:44:53  wcolburn
1841  * took out priv from rmrf() because I don't use it anymore
1842  *
1843  * Revision 2.98  2002/02/22 18:44:31  wcolburn
1844  * chdir to BASEDIR in rmrf()
1845  *
1846  * Revision 2.97  2002/02/22 18:39:55  wcolburn
1847  * priv->childpid=0 in fork() error
1848  *
1849  * Revision 2.96  2002/02/22 18:32:32  wcolburn
1850  * moved the closedir ahead of the rmrf()
1851  *
1852  * Revision 2.95  2002/02/22 18:30:37  wcolburn
1853  * lots of debugging code
1854  *
1855  * Revision 2.94  2002/02/22 17:01:48  wcolburn
1856  * added debug and cl option -d
1857  *
1858  * Revision 2.93  2002/02/21 21:40:14  wcolburn
1859  * took out those cleanup(ctx) calls because every message is failing
1860  *
1861  * Revision 2.92  2002/02/21 21:34:40  wcolburn
1862  * added cleanup(ctx) in a couple of places inside them eom() function
1863  *
1864  * Revision 2.91  2002/02/15 16:22:50  wcolburn
1865  * added a comment
1866  *
1867  * Revision 2.90  2002/02/15 16:21:51  wcolburn
1868  * fix for solaris compatability
1869  *
1870  * Revision 2.89  2002/01/30 15:24:08  wcolburn
1871  * took out double extension scan
1872  *
1873  * Revision 2.88  2002/01/30 15:00:10  wcolburn
1874  * .txt and .tar.bz
1875  *
1876  * Revision 2.87  2002/01/30 14:42:49  wcolburn
1877  * .tar.gz is ok
1878  *
1879  * Revision 2.86  2002/01/29 20:14:03  wcolburn
1880  * spelled extension correctly
1881  *
1882  * Revision 2.85  2002/01/29 20:07:05  wcolburn
1883  * added a directory counter to unpack because my load is too high
1884  *
1885  * Revision 2.84  2002/01/29 19:35:20  wcolburn
1886  * removed unused variable
1887  *
1888  * Revision 2.83  2002/01/29 19:34:54  wcolburn
1889  * always unpack() and always virusscan() because it is just safer.
1890  *
1891  * Revision 2.82  2002/01/29 16:03:43  wcolburn
1892  * oops, "." and ".." are special
1893  *
1894  * Revision 2.81  2002/01/29 16:01:20  wcolburn
1895  * start of sweeping changes to isbadfilename
1896  *
1897  * Revision 2.80  2002/01/29 15:51:00  wcolburn
1898  * rearranged things a little, prevent strdup()ing a NULL in virusscan
1899  *
1900  * Revision 2.79  2002/01/29 15:32:17  wcolburn
1901  * dehacked the uudecode thing, and added a TODO item from Lussnig so that
1902  * I stop losing track of all the things he keeps suggesting.
1903  *
1904  * Revision 2.78  2002/01/28 22:35:52  wcolburn
1905  * --output-file=
1906  *
1907  * Revision 2.77  2002/01/28 22:10:16  wcolburn
1908  * DOH!  My extra.dat wasn't readable by my SAFEUSER, so it was
1909  * being ignored and the virus wasn't being caught.
1910  *
1911  * Revision 2.76  2002/01/28 21:59:28  wcolburn
1912  * oops
1913  *
1914  * Revision 2.75  2002/01/28 21:58:41  wcolburn
1915  * rewrite from 2.64
1916  *
1917  * Revision 2.66  2002/01/28 20:48:56  wcolburn
1918  * changed message for extentions
1919  *
1920  * Revision 2.65  2002/01/28 20:47:00  wcolburn
1921  * took out .exe again
1922  *
1923  * Revision 2.64  2002/01/25 19:56:37  wcolburn
1924  * more dynamic extention scanning
1925  *
1926  * Revision 2.63  2002/01/24 17:08:31  wcolburn
1927  * added cleanup(ctx) for postmaster mail
1928  *
1929  * Revision 2.62  2002/01/16 15:46:38  wcolburn
1930  * added pgp to the list of allowed extentions.  I am sure it used to be
1931  * there!
1932  *
1933  * Revision 2.61  2001/12/19 20:58:43  wcolburn
1934  * cleaned up, mostly happy with it
1935  *
1936  * Revision 2.60  2001/12/19 18:50:40  wcolburn
1937  * minor tweaks for good error reporting
1938  *
1939  * Revision 2.59  2001/12/19 18:43:21  wcolburn
1940  * fixed silly mistake in socket unlink()
1941  *
1942  * Revision 2.58  2001/12/19 18:41:27  wcolburn
1943  * unlink() wasn't working right?
1944  *
1945  * Revision 2.57  2001/12/19 18:37:56  wcolburn
1946  * possibly a fully working version with new config file handling
1947  *
1948  * Revision 2.56  2001/12/19 17:34:43  wcolburn
1949  * redid the config file reader to make it shorter and simpler
1950  * as per suggestion/example from Thomas Lussnig.
1951  *
1952  * Revision 2.55  2001/12/19 15:57:59  wcolburn
1953  * argv[0] doesn't work, you need smfi_getsymval() to find "postmaster"
1954  *
1955  * Revision 2.54  2001/12/19 15:55:05  wcolburn
1956  * testing out Thomas Lussnig's usage of argv[0] instead of my sm_getsym()
1957  * in mlfi_envrcpt()
1958  *
1959  * Revision 2.53  2001/12/19 15:43:32  wcolburn
1960  * added warning about blindly accepting postmaster mail from Thomas
1961  * Lussnig
1962  *
1963  * Revision 2.52  2001/12/18 16:41:55  wcolburn
1964  * fixed the dollar Log dollar thing to not be wonky
1965  *
1966  * Revision 2.51  2001/12/18 16:40:46  wcolburn
1967  * changed Copyright notice
1968  *
1969  * Revision 2.50  2001/12/18 15:58:33  wcolburn
1970  * added in a dollar Log dollar directive
1971  *
1972  */
1973