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