Browse code

Squashed commit of the following:

commit 0ddbd1a7117e207b8aa87079568a056691d5eb32
Author: acab <acab@1337ness.(none)>
Date: Thu Nov 27 17:18:04 2008 +0100

to be committed

commit 51f8010a275717b8a56a3ab22d49755899819518
Author: acab <acab@1337ness.(none)>
Date: Thu Nov 27 15:18:40 2008 +0100

mangle body

commit cd38463f4739f4a7778b6bf294cfb2166024656e
Author: acab <acab@1337ness.(none)>
Date: Thu Nov 27 14:43:39 2008 +0100

parse clamd result

commit 7b8a39495e59ac745342455f8e9aab09951d1041
Author: acab <acab@1337ness.(none)>
Date: Thu Nov 27 03:19:45 2008 +0100

handle calloc shifts

commit a58b732af99f9e3f18d2327e977432f1117028ee
Author: acab <acab@1337ness.(none)>
Date: Thu Nov 27 02:07:19 2008 +0100

spam added, local is b0rked

commit 22604b4b037cfdbc92d6b29986652e2f004bd1f3
Author: acab <acab@albe.digitalfuture.it>
Date: Wed Nov 26 19:44:59 2008 +0100

free/close stuff

commit 33a02aed984981d3e80ca4930a482f702624f08f
Author: aCaB <acab@digitalfuture.it>
Date: Wed Nov 26 03:11:29 2008 +0100

skeleton ready

commit 4630d9902ee74b6137abf6526c6a9ad3e41fc597
Author: aCaB <acab@digitalfuture.it>
Date: Tue Nov 25 22:59:41 2008 +0100

need to rebase

commit 083f5f98aecfce2763870f20ae97643d5683613a
Author: aCaB <acab@digitalfuture.it>
Date: Tue Nov 25 22:45:11 2008 +0100

in body()

commit 08f040f67de1264810953415c0a47c95ec9acff0
Author: aCaB <acab@digitalfuture.it>
Date: Tue Nov 25 19:51:15 2008 +0100

clamfi

commit bc08fe8f72580b8be81791a7c03ec38952781af7
Author: aCaB <acab@digitalfuture.it>
Date: Tue Nov 25 16:47:17 2008 +0100

adding milter

commit 34dcebd9294a059f2c45ec9d1817bdb75f423cb3
Author: aCaB <acab@digitalfuture.it>
Date: Tue Nov 25 00:35:11 2008 +0100

netcode works, fix to cfgparser

commit 3cc0997d907e817954328c60e43cdcca0667d6f3
Author: aCaB <acab@digitalfuture.it>
Date: Mon Nov 24 23:46:05 2008 +0100

socket probe

commit 0c3bbd6d03f8df931cb114b07150cd0b7dcd0aff
Author: aCaB <acab@digitalfuture.it>
Date: Mon Nov 24 19:02:06 2008 +0100

nonblock started

commit a1193ff0de0d0a3d9212d833110236b1329c1f2e
Author: aCaB <acab@digitalfuture.it>
Date: Mon Nov 24 11:17:41 2008 +0100

legacy options

commit 90519c59ff8a4f44fceaf84e8c40116254c73045
Author: aCaB <acab@digitalfuture.it>
Date: Sun Nov 23 19:41:26 2008 +0100

sockets pool

commit 897b0c5f82503530ba3fbb2fcc3a9c007488e90a
Author: aCaB <acab@digitalfuture.it>
Date: Sun Nov 23 01:02:59 2008 +0100

Logging done

commit 4cf3a218756271c74782a1649f728e1c6e977ae5
Author: aCaB <acab@digitalfuture.it>
Date: Sat Nov 22 23:31:34 2008 +0100

Config done

commit 93b271579f680286c697476c00d8eec7effb2fc4
Author: aCaB <acab@digitalfuture.it>
Date: Sat Nov 22 22:50:42 2008 +0100

Sanitise config file options

commit bf5e81b8561b258daaef134894761fa80d44f5b1
Author: aCaB <acab@digitalfuture.it>
Date: Sat Nov 22 20:25:06 2008 +0100

cfgparser hacks to allow for milter own conf

commit 8924e93b2567a1f378c2177b4011b67631b49cc1
Author: aCaB <acab@digitalfuture.it>
Date: Sat Nov 22 19:03:45 2008 +0100

command line parser

commit d2180e370f75948b6ce12f89979bda9e92ccd5f8
Author: aCaB <acab@digitalfuture.it>
Date: Sat Nov 22 16:36:37 2008 +0100

restart from scratch

commit 249d6cdbddbea622c949753aafbf48e526e0ef4d
Author: aCaB <acab@digitalfuture.it>
Date: Sun Nov 16 19:39:12 2008 +0100

more diversion

commit b948990103867b59c749da88d6384128c1e5e6d3
Author: aCaB <acab@digitalfuture.it>
Date: Sun Nov 16 17:31:42 2008 +0100

milter separation (milter)

commit 6dc259a5d4f8f6e4428953055510382160ef8df4
Author: aCaB <acab@digitalfuture.it>
Date: Sun Nov 16 17:23:14 2008 +0100

milter separation

git-svn: trunk@4519

aCaB authored on 2008/12/05 01:26:04
Showing 12 changed files
... ...
@@ -31,8 +31,14 @@ clamav_milter_SOURCES = \
31 31
     $(top_srcdir)/shared/getopt.h \
32 32
     $(top_srcdir)/shared/misc.c \
33 33
     $(top_srcdir)/shared/misc.h \
34
-    $(top_srcdir)/shared/network.c \
35
-    $(top_srcdir)/shared/network.h \
34
+    $(top_srcdir)/shared/options.c \
35
+    $(top_srcdir)/shared/options.h \
36
+    connpool.c \
37
+    connpool.h \
38
+    netcode.c \
39
+    netcode.h \
40
+    clamfi.c \
41
+    clamfi.h \
36 42
     clamav-milter.c
37 43
 man_MANS = $(top_builddir)/docs/man/clamav-milter.8
38 44
 
... ...
@@ -76,14 +76,18 @@ am__clamav_milter_SOURCES_DIST = $(top_srcdir)/shared/cfgparser.c \
76 76
 	$(top_srcdir)/shared/cfgparser.h $(top_srcdir)/shared/output.c \
77 77
 	$(top_srcdir)/shared/output.h $(top_srcdir)/shared/getopt.c \
78 78
 	$(top_srcdir)/shared/getopt.h $(top_srcdir)/shared/misc.c \
79
-	$(top_srcdir)/shared/misc.h $(top_srcdir)/shared/network.c \
80
-	$(top_srcdir)/shared/network.h clamav-milter.c
79
+	$(top_srcdir)/shared/misc.h $(top_srcdir)/shared/options.c \
80
+	$(top_srcdir)/shared/options.h connpool.c connpool.h netcode.c \
81
+	netcode.h clamfi.c clamfi.h clamav-milter.c
81 82
 @BUILD_CLAMD_TRUE@@HAVE_MILTER_TRUE@am_clamav_milter_OBJECTS =  \
82 83
 @BUILD_CLAMD_TRUE@@HAVE_MILTER_TRUE@	cfgparser.$(OBJEXT) \
83 84
 @BUILD_CLAMD_TRUE@@HAVE_MILTER_TRUE@	output.$(OBJEXT) \
84 85
 @BUILD_CLAMD_TRUE@@HAVE_MILTER_TRUE@	getopt.$(OBJEXT) \
85 86
 @BUILD_CLAMD_TRUE@@HAVE_MILTER_TRUE@	misc.$(OBJEXT) \
86
-@BUILD_CLAMD_TRUE@@HAVE_MILTER_TRUE@	network.$(OBJEXT) \
87
+@BUILD_CLAMD_TRUE@@HAVE_MILTER_TRUE@	options.$(OBJEXT) \
88
+@BUILD_CLAMD_TRUE@@HAVE_MILTER_TRUE@	connpool.$(OBJEXT) \
89
+@BUILD_CLAMD_TRUE@@HAVE_MILTER_TRUE@	netcode.$(OBJEXT) \
90
+@BUILD_CLAMD_TRUE@@HAVE_MILTER_TRUE@	clamfi.$(OBJEXT) \
87 91
 @BUILD_CLAMD_TRUE@@HAVE_MILTER_TRUE@	clamav-milter.$(OBJEXT)
88 92
 clamav_milter_OBJECTS = $(am_clamav_milter_OBJECTS)
89 93
 clamav_milter_LDADD = $(LDADD)
... ...
@@ -273,8 +277,14 @@ top_srcdir = @top_srcdir@
273 273
 @BUILD_CLAMD_TRUE@@HAVE_MILTER_TRUE@    $(top_srcdir)/shared/getopt.h \
274 274
 @BUILD_CLAMD_TRUE@@HAVE_MILTER_TRUE@    $(top_srcdir)/shared/misc.c \
275 275
 @BUILD_CLAMD_TRUE@@HAVE_MILTER_TRUE@    $(top_srcdir)/shared/misc.h \
276
-@BUILD_CLAMD_TRUE@@HAVE_MILTER_TRUE@    $(top_srcdir)/shared/network.c \
277
-@BUILD_CLAMD_TRUE@@HAVE_MILTER_TRUE@    $(top_srcdir)/shared/network.h \
276
+@BUILD_CLAMD_TRUE@@HAVE_MILTER_TRUE@    $(top_srcdir)/shared/options.c \
277
+@BUILD_CLAMD_TRUE@@HAVE_MILTER_TRUE@    $(top_srcdir)/shared/options.h \
278
+@BUILD_CLAMD_TRUE@@HAVE_MILTER_TRUE@    connpool.c \
279
+@BUILD_CLAMD_TRUE@@HAVE_MILTER_TRUE@    connpool.h \
280
+@BUILD_CLAMD_TRUE@@HAVE_MILTER_TRUE@    netcode.c \
281
+@BUILD_CLAMD_TRUE@@HAVE_MILTER_TRUE@    netcode.h \
282
+@BUILD_CLAMD_TRUE@@HAVE_MILTER_TRUE@    clamfi.c \
283
+@BUILD_CLAMD_TRUE@@HAVE_MILTER_TRUE@    clamfi.h \
278 284
 @BUILD_CLAMD_TRUE@@HAVE_MILTER_TRUE@    clamav-milter.c
279 285
 
280 286
 @BUILD_CLAMD_TRUE@@HAVE_MILTER_TRUE@man_MANS = $(top_builddir)/docs/man/clamav-milter.8
... ...
@@ -370,9 +380,12 @@ distclean-compile:
370 370
 
371 371
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cfgparser.Po@am__quote@
372 372
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clamav-milter.Po@am__quote@
373
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clamfi.Po@am__quote@
374
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/connpool.Po@am__quote@
373 375
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt.Po@am__quote@
374 376
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc.Po@am__quote@
375
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/network.Po@am__quote@
377
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netcode.Po@am__quote@
378
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/options.Po@am__quote@
376 379
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/output.Po@am__quote@
377 380
 
378 381
 .c.o:
... ...
@@ -452,19 +465,19 @@ misc.obj: $(top_srcdir)/shared/misc.c
452 452
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
453 453
 @am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o misc.obj `if test -f '$(top_srcdir)/shared/misc.c'; then $(CYGPATH_W) '$(top_srcdir)/shared/misc.c'; else $(CYGPATH_W) '$(srcdir)/$(top_srcdir)/shared/misc.c'; fi`
454 454
 
455
-network.o: $(top_srcdir)/shared/network.c
456
-@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT network.o -MD -MP -MF $(DEPDIR)/network.Tpo -c -o network.o `test -f '$(top_srcdir)/shared/network.c' || echo '$(srcdir)/'`$(top_srcdir)/shared/network.c
457
-@am__fastdepCC_TRUE@	mv -f $(DEPDIR)/network.Tpo $(DEPDIR)/network.Po
458
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$(top_srcdir)/shared/network.c' object='network.o' libtool=no @AMDEPBACKSLASH@
455
+options.o: $(top_srcdir)/shared/options.c
456
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT options.o -MD -MP -MF $(DEPDIR)/options.Tpo -c -o options.o `test -f '$(top_srcdir)/shared/options.c' || echo '$(srcdir)/'`$(top_srcdir)/shared/options.c
457
+@am__fastdepCC_TRUE@	mv -f $(DEPDIR)/options.Tpo $(DEPDIR)/options.Po
458
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$(top_srcdir)/shared/options.c' object='options.o' libtool=no @AMDEPBACKSLASH@
459 459
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
460
-@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o network.o `test -f '$(top_srcdir)/shared/network.c' || echo '$(srcdir)/'`$(top_srcdir)/shared/network.c
460
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o options.o `test -f '$(top_srcdir)/shared/options.c' || echo '$(srcdir)/'`$(top_srcdir)/shared/options.c
461 461
 
462
-network.obj: $(top_srcdir)/shared/network.c
463
-@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT network.obj -MD -MP -MF $(DEPDIR)/network.Tpo -c -o network.obj `if test -f '$(top_srcdir)/shared/network.c'; then $(CYGPATH_W) '$(top_srcdir)/shared/network.c'; else $(CYGPATH_W) '$(srcdir)/$(top_srcdir)/shared/network.c'; fi`
464
-@am__fastdepCC_TRUE@	mv -f $(DEPDIR)/network.Tpo $(DEPDIR)/network.Po
465
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$(top_srcdir)/shared/network.c' object='network.obj' libtool=no @AMDEPBACKSLASH@
462
+options.obj: $(top_srcdir)/shared/options.c
463
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT options.obj -MD -MP -MF $(DEPDIR)/options.Tpo -c -o options.obj `if test -f '$(top_srcdir)/shared/options.c'; then $(CYGPATH_W) '$(top_srcdir)/shared/options.c'; else $(CYGPATH_W) '$(srcdir)/$(top_srcdir)/shared/options.c'; fi`
464
+@am__fastdepCC_TRUE@	mv -f $(DEPDIR)/options.Tpo $(DEPDIR)/options.Po
465
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$(top_srcdir)/shared/options.c' object='options.obj' libtool=no @AMDEPBACKSLASH@
466 466
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
467
-@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o network.obj `if test -f '$(top_srcdir)/shared/network.c'; then $(CYGPATH_W) '$(top_srcdir)/shared/network.c'; else $(CYGPATH_W) '$(srcdir)/$(top_srcdir)/shared/network.c'; fi`
467
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o options.obj `if test -f '$(top_srcdir)/shared/options.c'; then $(CYGPATH_W) '$(top_srcdir)/shared/options.c'; else $(CYGPATH_W) '$(srcdir)/$(top_srcdir)/shared/options.c'; fi`
468 468
 
469 469
 mostlyclean-libtool:
470 470
 	-rm -f *.lo
... ...
@@ -1,13 +1,11 @@
1 1
 /*
2
- * clamav-milter.c
3
- *	.../clamav-milter/clamav-milter.c
2
+ *  Copyright (C)2008 Sourcefire, Inc.
4 3
  *
5
- *  Copyright (C) 2003-2007 Nigel Horne <njh@bandsman.co.uk>
4
+ *  Author: aCaB <acab@clamav.net>
6 5
  *
7 6
  *  This program is free software; you can redistribute it and/or modify
8
- *  it under the terms of the GNU General Public License as published by
9
- *  the Free Software Foundation; either version 2 of the License, or
10
- *  (at your option) any later version.
7
+ *  it under the terms of the GNU General Public License version 2 as
8
+ *  published by the Free Software Foundation.
11 9
  *
12 10
  *  This program is distributed in the hope that it will be useful,
13 11
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
... ...
@@ -18,7022 +16,249 @@
18 18
  *  along with this program; if not, write to the Free Software
19 19
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 20
  *  MA 02110-1301, USA.
21
- *
22
- * Install into /usr/local/sbin/clamav-milter
23
- * See http://www.elandsys.com/resources/sendmail/libmilter/overview.html
24
- *
25
- * For installation instructions see the file INSTALL that came with this file
26
- *
27
- * NOTE: first character of strings to logg():
28
- *	! Error
29
- *	^ Warning
30
- *	* Verbose
31
- *	# Info, but not logged in foreground
32
- *	Default Info
33 21
  */
34
-static	char	const	rcsid[] = "$Id: clamav-milter.c,v 1.312 2007/02/12 22:24:21 njh Exp $";
35 22
 
36 23
 #if HAVE_CONFIG_H
37 24
 #include "clamav-config.h"
38 25
 #endif
39 26
 
40
-#include "cfgparser.h"
41
-#include "target.h"
42
-#include "str.h"
43
-#include "../libclamav/others.h"
44
-#include "output.h"
45
-#include "clamav.h"
46
-#include "table.h"
47
-#include "network.h"
48
-#include "misc.h"
49
-
50 27
 #include <stdio.h>
51
-#include <sysexits.h>
52
-#ifdef	HAVE_SYS_STAT_H
53
-#include <sys/stat.h>
54
-#endif
55
-#if	HAVE_STDLIB_H
56
-#include <stdlib.h>
57
-#endif
58
-#if	HAVE_MEMORY_H
59
-#include <memory.h>
60
-#endif
61
-#if	HAVE_STRING_H
62
-#include <string.h>
63
-#endif
64
-#ifdef HAVE_STRINGS_H
65
-#include <strings.h>
66
-#endif
67
-#include <sys/wait.h>
68
-#include <assert.h>
69
-#include <sys/socket.h>
70
-#include <netinet/in.h>
71
-#include <net/if.h>
72
-#include <arpa/inet.h>
73
-#include <sys/un.h>
74
-#include <stdarg.h>
75
-#include <errno.h>
76
-#if	HAVE_LIBMILTER_MFAPI_H
77
-#include <libmilter/mfapi.h>
78
-#endif
79
-#include <pthread.h>
80
-#include <sys/time.h>
81
-#include <sys/resource.h>
82
-#include <signal.h>
83
-#include <fcntl.h>
28
+#include <sys/types.h>
29
+#include <unistd.h>
84 30
 #include <pwd.h>
85 31
 #include <grp.h>
86
-#if	HAVE_SYS_PARAM_H
87
-#include <sys/param.h>
88
-#endif
89
-#if	HAVE_RESOLV_H
90
-#include <arpa/nameser.h>	/* for HEADER */
91
-#include <resolv.h>
92
-#endif
93
-#ifdef	HAVE_UNISTD_H
94
-#include <unistd.h>
95
-#endif
96
-#include <ctype.h>
97
-
98
-#if HAVE_MMAP
99
-#if HAVE_SYS_MMAN_H
100
-#include <sys/mman.h>
101
-#else /* HAVE_SYS_MMAN_H */
102
-#undef HAVE_MMAP
103
-#endif
104
-#endif
105
-
106
-#define NONBLOCK_SELECT_MAX_FAILURES	3
107
-#define NONBLOCK_MAX_ATTEMPTS	10
108
-#define	CONNECT_TIMEOUT	5	/* Allow 5 seconds to connect */
109
-
110
-#ifdef	C_LINUX
111
-#include <sys/sendfile.h>	/* FIXME: use sendfile on BSD not Linux */
112
-#include <libintl.h>
113
-#include <locale.h>
114
-
115
-#define	gettext_noop(s)	s
116
-#define	_(s)	gettext(s)
117
-#define	N_(s)	gettext_noop(s)
118
-
119
-#else
120
-
121
-#define	_(s)	s
122
-#define	N_(s)	s
123
-
124
-#endif
125
-
126
-#ifdef	USE_SYSLOG
32
+#include <string.h>
127 33
 #include <syslog.h>
128
-#endif
129
-
130
-#ifdef	WITH_TCPWRAP
131
-#if	HAVE_TCPD_H
132
-#include <tcpd.h>
133
-#endif
134
-
135
-int	allow_severity = LOG_DEBUG;
136
-int	deny_severity = LOG_NOTICE;
137
-#endif
138
-
139
-#ifdef	CL_DEBUG
140
-static	char	console[] = "/dev/console";
141
-#endif
142
-
143
-#if defined(CL_DEBUG) && defined(C_LINUX)
144
-#include <sys/resource.h>
145
-#endif
146
-
147
-#define _GNU_SOURCE
148
-#include <getopt.h>
149
-
150
-#ifndef	SENDMAIL_BIN
151
-#define	SENDMAIL_BIN	"/usr/lib/sendmail"
152
-#endif
153
-
154
-#ifndef HAVE_IN_PORT_T
155
-typedef	unsigned short	in_port_t;
156
-#endif
157
-
158
-#ifndef	HAVE_IN_ADDR_T
159
-typedef	unsigned int	in_addr_t;
160
-#endif
161
-
162
-#ifndef	INET6_ADDRSTRLEN
163
-#ifdef	AF_INET6
164
-#define	INET6_ADDRSTRLEN	40
165
-#else
166
-#define	INET6_ADDRSTRLEN	16
167
-#endif
168
-#endif
169
-
170
-#ifndef	EX_CONFIG	/* HP-UX */
171
-#define	EX_CONFIG	78
172
-#endif
173
-
174
-#define	VERSION_LENGTH	128
175
-#define	DEFAULT_TIMEOUT	120
176
-
177
-#define	NTRIES	5	/* How many times we try to connect to a clamd */
178
-
179
-/*#define	SESSION*/
180
-		/* Keep one command connexion open to clamd, otherwise a new
181
-		 * command connexion is created for each new email
182
-		 *
183
-		 * FIXME: When SESSIONS are open, freshclam can hang when
184
-		 *	notfying clamd of an update. This is most likely to be a
185
-		 *	problem with the implementation of SESSIONS on clamd.
186
-		 *	The problem seems worst on BSD.
187
-		 *
188
-		 * Note that clamd is buggy and can hang or even crash if you
189
-		 *	send SESSION command so be aware
190
-		 */
191
-
192
-/*
193
- * TODO: optional: xmessage on console when virus stopped (SNMP would be real nice!)
194
- *	Having said that, with LogSysLog you can (on Linux) configure the system
195
- *	to get messages on the system console, see syslog.conf(5), also you
196
- *	can use wall(1) in the VirusEvent entry in clamd.conf
197
- * TODO: Decide action (bounce, discard, reject etc.) based on the virus
198
- *	found. Those with faked addresses, such as SCO.A want discarding,
199
- *	others could be bounced properly.
200
- * TODO: Encrypt mails sent to clamd to stop sniffers. Sending by UNIX domain
201
- *	sockets is better
202
- * TODO: Load balancing, allow local machine to talk via UNIX domain socket.
203
- * TODO: allow each To: line in the whitelist file to specify a quarantine email
204
- *	address
205
- * TODO: optionally use zlib to compress data sent to remote hosts
206
- * TODO: Finish IPv6 support (serverIPs array and SPF are IPv4 only)
207
- * TODO: Check domainkeys as well as SPF for phish false positives
208
- */
209
-
210
-struct header_node_t {
211
-	char	*header;
212
-	struct	header_node_t *next;
213
-};
214
-
215
-struct header_list_struct {
216
-	struct	header_node_t *first;
217
-	struct	header_node_t *last;
218
-};
219
-
220
-typedef struct header_list_struct *header_list_t;
221
-
222
-/*
223
- * Local addresses are those not scanned if --local is not set
224
- * 127.0.0.0 is not in this table since that's goverend by --outgoing
225
- * Andy Fiddaman <clam@fiddaman.net> added 169.254.0.0/16
226
- *	(Microsoft default DHCP)
227
- * TODO: compare this with RFC1918/RFC3330
228
- */
229
-#define PACKADDR(a, b, c, d) (((uint32_t)(a) << 24) | ((b) << 16) | ((c) << 8) | (d))
230
-#define MAKEMASK(bits)	((uint32_t)(0xffffffff << (32 - bits)))
231
-
232
-static struct cidr_net {	/* don't make this const because of -I flag */
233
-	uint32_t	base;
234
-	uint32_t	mask;
235
-} localNets[] = {
236
-	/*{ PACKADDR(127,   0,   0,   0), MAKEMASK(8) },	*   127.0.0.0/8 */
237
-	{ PACKADDR(192, 168,   0,   0), MAKEMASK(16) },	/* 192.168.0.0/16 - RFC3330 */
238
-	/*{ PACKADDR(192, 18,   0,   0), MAKEMASK(15) },	* 192.18.0.0/15 - RFC2544 */
239
-	/*{ PACKADDR(192, 0,   2,   0), MAKEMASK(24) },	* 192.0.2.0/24 - RFC3330 */
240
-	{ PACKADDR( 10,   0,   0,   0), MAKEMASK(8) },	/*    10.0.0.0/8 */
241
-	{ PACKADDR(172,  16,   0,   0), MAKEMASK(12) },	/*  172.16.0.0/12 */
242
-	{ PACKADDR(169, 254,   0,   0), MAKEMASK(16) },	/* 169.254.0.0/16 */
243
-	{ 0, 0 },	/* space to put eight more via -I addr */
244
-	{ 0, 0 },
245
-	{ 0, 0 },
246
-	{ 0, 0 },
247
-	{ 0, 0 },
248
-	{ 0, 0 },
249
-	{ 0, 0 },
250
-	{ 0, 0 },
251
-	{ 0, 0 }
252
-};
253
-#define IFLAG_MAX 8
254
-
255
-#ifdef	AF_INET6
256
-typedef struct cidr_net6 {
257
-	struct in6_addr	base;
258
-	int preflen;
259
-} cidr_net6;
260
-static	cidr_net6	localNets6[IFLAG_MAX];
261
-static	int	localNets6_cnt;
262
-#endif
34
+#include <time.h>
35
+#include <libmilter/mfapi.h>
263 36
 
264
-/*
265
- * Each libmilter thread has one of these
266
- */
267
-struct	privdata {
268
-	char	*from;	/* Who sent the message */
269
-	char	*subject;	/* Original subject */
270
-	char	*sender;	/* Secretary - often used in mailing lists */
271
-	char	**to;	/* Who is the message going to */
272
-	char	ip[INET6_ADDRSTRLEN];	/* IP address of the other end */
273
-	int	numTo;	/* Number of people the message is going to */
274
-#ifndef	SESSION
275
-	int	cmdSocket;	/*
276
-				 * Socket to send/get commands e.g. PORT for
277
-				 * dataSocket
278
-				 */
279
-#endif
280
-	int	dataSocket;	/* Socket to send data to clamd */
281
-	char	*filename;	/* Where to store the message in quarantine */
282
-	u_char	*body;		/* body of the message if Sflag is set */
283
-	size_t	bodyLen;	/* number of bytes in body */
284
-	header_list_t headers;	/* Message headers */
285
-	long	numBytes;	/* Number of bytes sent so far */
286
-	char	*received;	/* keep track of received from */
287
-	const	char	*rejectCode;	/* 550 or 554? */
288
-	unsigned	int	discard:1;	/*
289
-				 * looks like the remote end is playing ping
290
-				 * pong with us
291
-				 */
292
-#ifdef	HAVE_RESOLV_H
293
-	unsigned	int	spf_ok:1;
294
-#endif
295
-	int	statusCount;	/* number of X-Virus-Status headers */
296
-	int	serverNumber;	/* Index into serverIPs */
297
-};
37
+#include "clamav.h"
298 38
 
299
-#ifdef	SESSION
300
-static	int		createSession(unsigned int s);
301
-#else
302
-static	int		pingServer(int serverNumber);
303
-static	void		*try_server(void *var);
304
-static	int		active_servers(int *active);
305
-struct	try_server_struct {
306
-	int	sock;
307
-	int	rc;
308
-	struct	sockaddr_in *server;
309
-	int	server_index;
39
+#include "shared/options.h"
40
+#include "shared/output.h"
41
+#include "shared/cfgparser.h"
42
+#include "shared/misc.h"
43
+
44
+#include "connpool.h"
45
+#include "netcode.h"
46
+#include "clamfi.h"
47
+
48
+
49
+struct smfiDesc descr = {
50
+    "ClamAV", 		/* filter name */
51
+    SMFI_VERSION,	/* milter version */
52
+    SMFIF_ADDHDRS|SMFIF_ADDRCPT, /* flags */
53
+    NULL,		/* connection info filter */
54
+    NULL,		/* SMTP HELO command filter */
55
+    NULL,		/* envelope sender filter */
56
+    NULL,		/* envelope recipient filter */
57
+    NULL,		/* header filter */
58
+    NULL,		/* end of header */
59
+    clamfi_body,	/* body block */
60
+    clamfi_eom,		/* end of message */
61
+    NULL,		/* message aborted */
62
+    NULL,		/* connection cleanup */
63
+    NULL,		/* any unrecognized or unimplemented command filter */
64
+    NULL,		/* SMTP DATA command filter */
65
+    NULL		/* negotiation callback */
310 66
 };
311
-#endif
312
-static	int		findServer(void);
313
-static	sfsistat	clamfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr);
314
-#ifdef	CL_DEBUG
315
-static	sfsistat	clamfi_helo(SMFICTX *ctx, char *helostring);
316
-#endif
317
-static	sfsistat	clamfi_envfrom(SMFICTX *ctx, char **argv);
318
-static	sfsistat	clamfi_envrcpt(SMFICTX *ctx, char **argv);
319
-static	sfsistat	clamfi_header(SMFICTX *ctx, char *headerf, char *headerv);
320
-static	sfsistat	clamfi_eoh(SMFICTX *ctx);
321
-static	sfsistat	clamfi_body(SMFICTX *ctx, u_char *bodyp, size_t len);
322
-static	sfsistat	clamfi_eom(SMFICTX *ctx);
323
-static	sfsistat	clamfi_abort(SMFICTX *ctx);
324
-static	sfsistat	clamfi_close(SMFICTX *ctx);
325
-static	void		clamfi_cleanup(SMFICTX *ctx);
326
-static	void		clamfi_free(struct privdata *privdata, int keep);
327
-#ifdef __GNUC__
328
-static	int		clamfi_send(struct privdata *privdata, size_t len, const char *format, ...) __attribute__((format(printf, 3,4)));
329
-#else
330
-static	int		clamfi_send(struct privdata *privdata, size_t len, const char *format, ...);
331
-#endif
332
-static	long		clamd_recv(int sock, char *buf, size_t len);
333
-static	off_t		updateSigFile(void);
334
-static	header_list_t	header_list_new(void);
335
-static	void	header_list_free(header_list_t list);
336
-static	void	header_list_add(header_list_t list, const char *headerf, const char *headerv);
337
-static	void	header_list_print(header_list_t list, FILE *fp);
338
-static	int	connect2clamd(struct privdata *privdata);
339
-static	int	sendToFrom(struct privdata *privdata);
340
-static	int	checkClamd(int log_result);
341
-static	int	sendtemplate(SMFICTX *ctx, const char *filename, FILE *sendmail, const char *virusname);
342
-static	int	qfile(struct privdata *privdata, const char *sendmailId, const char *virusname);
343
-static	int	move(const char *oldfile, const char *newfile);
344
-static	void	setsubject(SMFICTX *ctx, const char *virusname);
345
-/*static	int	clamfi_gethostbyname(const char *hostname, struct hostent *hp, char *buf, size_t len);*/
346
-static	int	add_local_ip(char *address);
347
-static	int	isLocalAddr(in_addr_t addr);
348
-static	int	isLocal(const char *addr);
349
-static	void	clamdIsDown(void);
350
-static	void	*watchdog(void *a);
351
-static	int	check_and_reload_database(void);
352
-static	void	timeoutBlacklist(char *ip_address, int time_of_blacklist, void *v);
353
-static	void	quit(void);
354
-static	void	broadcast(const char *mess);
355
-static	int	loadDatabase(void);
356
-static	int	increment_connexions(void);
357
-static	void	decrement_connexions(void);
358
-static	void	dump_blacklist(char *key, int value, void *v);
359
-static	int	nonblock_connect(int sock, const struct sockaddr_in *sin, const char *hostname);
360
-static	int	connect_error(int sock, const char *hostname);
361
-
362
-#ifdef	SESSION
363
-static	pthread_mutex_t	version_mutex = PTHREAD_MUTEX_INITIALIZER;
364
-static	char	**clamav_versions;	/* max_children elements in the array */
365
-#define	clamav_version	(clamav_versions[0])
366
-#else
367
-static	char	clamav_version[VERSION_LENGTH + 1];
368
-#endif
369
-static	int	fflag = 0;	/* force a scan, whatever */
370
-static	int	oflag = 0;	/* scan messages from our machine? */
371
-static	int	lflag = 0;	/* scan messages from our site? */
372
-static	int	Iflag = 0;	/* Added an IP addr to localNets? */
373
-static	const	char	*progname;	/* our name - usually clamav-milter */
374
-
375
-/* Variables for --external */
376
-static	int	external = 0;	/* scan messages ourself or use clamd? */
377
-static	pthread_mutex_t	engine_mutex = PTHREAD_MUTEX_INITIALIZER;
378
-struct  cl_engine *engine = NULL;
379
-uint64_t maxscansize;
380
-uint64_t maxfilesize;
381
-uint32_t maxreclevel;
382
-uint32_t maxfiles;
383
-
384
-static	struct	cl_stat	dbstat;
385
-static	int	options = CL_SCAN_STDOPT;
386
-
387
-#ifdef	BOUNCE
388
-static	int	bflag = 0;	/*
389
-				 * send a failure (bounce) message to the
390
-				 * sender. This probably isn't a good idea
391
-				 * since most reply addresses will be fake
392
-				 *
393
-				 * TODO: Perhaps we can have an option to
394
-				 * bounce outgoing mail, but not incoming?
395
-				 */
396
-#endif
397
-static	const	char	*iface;	/*
398
-				 * Broadcast a message when a virus is found,
399
-				 * this allows remote network management
400
-				 */
401
-static	int	broadcastSock = -1;
402
-static	int	pflag = 0;	/*
403
-				 * Send a warning to the postmaster only,
404
-				 * this means user's won't be told when someone
405
-				 * sent them a virus
406
-				 */
407
-static	int	qflag = 0;	/*
408
-				 * Send no warnings when a virus is found,
409
-				 * this means that the only log of viruses
410
-				 * found is the syslog, so it's best to
411
-				 * enable LogSyslog in clamd.conf
412
-				 */
413
-static	int	Sflag = 0;	/*
414
-				 * Add a signature to each message that
415
-				 * has been scanned
416
-				 */
417
-static	const	char	*sigFilename;	/*
418
-				 * File where the scanned message signature
419
-				 * can be found
420
-				 */
421
-static	char	*quarantine;	/*
422
-				 * If a virus is found in an email redirect
423
-				 * it to this account
424
-				 */
425
-static	char	*quarantine_dir; /*
426
-				 * Path to store messages before scanning.
427
-				 * Infected ones will be left there.
428
-				 */
429
-static	int	nflag = 0;	/*
430
-				 * Don't add X-Virus-Scanned to header. Patch
431
-				 * from Dirk Meyer <dirk.meyer@dinoex.sub.org>
432
-				 */
433
-static	int	rejectmail = 1;	/*
434
-				 * Send a 550 rejection when a virus is
435
-				 * found
436
-				 */
437
-static	int	hflag = 0;	/*
438
-				 * Include original message headers in
439
-				 * report
440
-				 */
441
-static	int	cl_error = SMFIS_TEMPFAIL; /*
442
-				 * If an error occurs, return
443
-				 * this status. Allows messages
444
-				 * to be passed through
445
-				 * unscanned in the event of
446
-				 * an error. Patch from
447
-				 * Joe Talbott <josepht@cstone.net>
448
-				 */
449
-static	int	readTimeout = DEFAULT_TIMEOUT; /*
450
-				 * number of seconds to wait for clamd to
451
-				 * respond, see ReadTimeout in clamd.conf
452
-				 */
453
-static	long	streamMaxLength = -1;	/* StreamMaxLength from clamd.conf */
454
-static	int	logok = 0;	/*
455
-				 * Add clean items to the log file
456
-				 */
457
-static	const char	*signature = N_("-- \nScanned by ClamAv - http://www.clamav.net\n");
458
-static	time_t	signatureStamp;
459
-static	char	*templateFile;	/* e-mail to be sent when virus detected */
460
-static	char	*templateHeaders;	/* headers to be added to the above */
461
-static	const char	*tmpdir;
462
-
463
-#ifdef	CL_DEBUG
464
-static	int	debug_level = 0;
465
-#endif
466
-
467
-static	pthread_mutex_t	n_children_mutex = PTHREAD_MUTEX_INITIALIZER;
468
-static	pthread_cond_t	n_children_cond = PTHREAD_COND_INITIALIZER;
469
-static	int	n_children = 0;
470
-static	int	max_children = 0;
471
-static	unsigned	int	freshclam_monitor = 10;	/*
472
-							 * how often, in
473
-							 * seconds, to scan for
474
-							 * database updates
475
-							 */
476
-static	int	child_timeout = 300;	/* number of seconds to wait for
477
-					 * a child to die. Set to 0 to
478
-					 * wait forever
479
-					 */
480
-static	int	dont_wait = 0;	/*
481
-				 * If 1 send retry later to the remote end
482
-				 * if max_chilren is exceeded, otherwise we
483
-				 * wait for the number to go down
484
-				 */
485
-static	int	dont_sanitise = 0; /*
486
-				 * Don't check for ";" and "|" chars in 
487
-				 * email addresses.
488
-				 */
489
-static	int	advisory = 0;	/*
490
-				 * Run clamav-milter in advisory mode - viruses
491
-				 * are flagged rather than deleted. Incompatible
492
-				 * with quarantine options
493
-				 */
494
-static	int	detect_forged_local_address;	/*
495
-				 * for incoming only mail servers, drop emails
496
-				 * claiming to be from us that must be false
497
-				 * Requires that -o, -l or -f are NOT given
498
-				 */
499
-static	const	char	*pidFile;
500
-static	struct	cfgstruct	*copt;
501
-static	const	char	*localSocket;	/* milter->clamd comms */
502
-static	in_port_t	tcpSocket;	/* milter->clamd comms */
503
-static	char	*port = NULL;	/* sendmail->milter comms */
504
-
505
-static	const	char	*serverHostNames = "127.0.0.1";
506
-#if	HAVE_IN_ADDR_T
507
-static	in_addr_t	*serverIPs;	/* IPv4 only, in network byte order */
508
-#else
509
-static	long	*serverIPs;	/* IPv4 only, in network byte order */
510
-#endif
511
-static	int	numServers;	/* number of elements in serverIPs array */
512
-#ifndef	SESSION
513
-#define	RETRY_SECS	300	/* How often to retry a server that's down */
514
-static	time_t	*last_failed_pings;	/* For servers that are down. NB: not mutexed */
515
-#endif
516
-static	char	*rootdir;	/* for chroot */
517
-
518
-#ifdef	SESSION
519
-static	struct	session {
520
-	int	sock;	/* fd */
521
-	enum	{ CMDSOCKET_FREE, CMDSOCKET_INUSE, CMDSOCKET_DOWN }	status;
522
-} *sessions;	/* max_children elements in the array */
523
-static	pthread_mutex_t sstatus_mutex = PTHREAD_MUTEX_INITIALIZER;
524
-#endif	/*SESSION*/
525
-
526
-static	pthread_cond_t	watchdog_cond = PTHREAD_COND_INITIALIZER;
527
-
528
-#ifndef	SHUT_RD
529
-#define	SHUT_RD		0
530
-#endif
531
-#ifndef	SHUT_WR
532
-#define	SHUT_WR		1
533
-#endif
534
-
535
-static	const	char	*postmaster = "postmaster";
536
-static	const	char	*from = "MAILER-DAEMON";
537
-static	int	quitting;
538
-static	int	reload;	/* reload database when SIGUSR2 is received */
539
-static	const	char	*report;	/* Report Phishing to this address */
540
-static	const	char	*report_fps;	/* Report Phish FPs to this address */
541
-
542
-static	const	char	*whitelistFile;	/*
543
-					 * file containing destination email
544
-					 * addresses that we don't scan
545
-					 */
546
-static	const	char	*sendmailCF;	/* location of sendmail.cf to verify */
547
-static		int	checkCF = 1;
548
-static	const	char	*pidfile;
549
-static	int	black_hole_mode; /*
550
-				 * Since sendmail calls its milters before it
551
-				 * looks in /etc/aliases we can spend time
552
-				 * looking for malware that's going to be
553
-				 * thrown away even if the message is clean.
554
-				 * Enable this to not scan these messages.
555
-				 * Sadly, because these days sendmail -bv
556
-				 * only works as root, you can't use this with
557
-				 * the User directive, which some won't like
558
-				 * which also may contain the real target name
559
-				 *
560
-				 * smfi_getsymval(ctx, "{rcpt_addr}") only
561
-				 * handles virtuser, it doesn't also deref
562
-				 * the alias table, so it isn't any help
563
-				 */
564
-
565
-static	table_t	*blacklist;	/* never freed */
566
-static	int	blacklist_time;	/* How long to blacklist an IP */
567
-static	pthread_mutex_t	blacklist_mutex = PTHREAD_MUTEX_INITIALIZER;
568
-
569
-#ifdef	CL_DEBUG
570
-#if __GLIBC__ == 2 && __GLIBC_MINOR__ >= 1
571
-#define HAVE_BACKTRACE
572
-#endif
573
-#endif
574
-
575
-static	void	sigsegv(int sig);
576
-static	void	sigusr1(int sig);
577
-static	void	sigusr2(int sig);
578
-
579
-#ifdef HAVE_BACKTRACE
580
-#include <execinfo.h>
581
-
582
-static	void	print_trace(void);
583
-
584
-#define	BACKTRACE_SIZE	200
585
-
586
-#endif
587
-
588
-static	int	verifyIncomingSocketName(const char *sockName);
589
-static	int	isWhitelisted(const char *emailaddress, int to);
590
-static	int	isBlacklisted(const char *ip_address);
591
-static	table_t	*mx(const char *host, table_t *t);
592
-static	sfsistat	black_hole(const struct privdata *privdata);
593
-static	int	useful_header(const char *cmd);
594
-
595
-extern	short	logg_foreground;
596
-
597
-#ifdef HAVE_RESOLV_H
598
-static	table_t	*resolve(const char *host, table_t *t);
599
-static	int	spf(struct privdata *privdata, table_t *prevhosts);
600
-static	void	spf_ip(char *ip, int zero, void *v);
601
-
602
-pthread_mutex_t	res_pool_mutex = PTHREAD_MUTEX_INITIALIZER;
603
-
604
-#ifdef HAVE_LRESOLV_R
605
-res_state res_pool;
606
-uint8_t *res_pool_state;
607
-pthread_cond_t res_pool_cond = PTHREAD_COND_INITIALIZER;
608
-
609
-static int safe_res_query(const char *d, int c, int t, u_char *a, int l) {
610
-	int i = -1, ret;
611
-
612
-	pthread_mutex_lock(&res_pool_mutex);
613
-	while(i==-1) {
614
-		int j;
615
-		for(j=0; j<max_children+1; j++) {
616
-			if(!res_pool_state[j]) continue;
617
-			i = j;
618
-			break;
619
-		}
620
-		if(i!=-1) break;
621
-		pthread_cond_wait(&res_pool_cond, &res_pool_mutex);
622
-	}
623
-	res_pool_state[i]=0;
624
-	pthread_mutex_unlock(&res_pool_mutex);
625
-
626
-	ret = res_nquery(&res_pool[i], d, c, t, a, l);
627
-  
628
-	pthread_mutex_lock(&res_pool_mutex);
629
-	res_pool_state[i]=1;
630
-	pthread_cond_signal(&res_pool_cond);
631
-	pthread_mutex_unlock(&res_pool_mutex);
632
-	return ret;
633
-}
634
-
635
-#else /* !HAVE_LRESOLV_R - non thread safe resolver (old bsd's) */
636
-
637
-static int safe_res_query(const char *d, int c, int t, u_char *a, int l) {
638
-	int ret;
639
-	pthread_mutex_lock(&res_pool_mutex);
640
-	ret = res_query(d, c, t, a, l);
641
-	pthread_mutex_unlock(&res_pool_mutex);
642
-	return ret;
643
-}
644
-
645
-#endif /* HAVE_LRESOLV_R */
646
-
647
-#endif /* HAVE_RESOLV_H */
648
-
649
-static void
650
-help(void)
651
-{
652
-	printf("\n\tclamav-milter version %s\n", get_version());
653
-	puts("\tCopyright (C) 2007 Nigel Horne <njh@clamav.net>\n");
654
-
655
-	puts(_("\t--advisory\t\t-A\tFlag viruses rather than deleting them."));
656
-	puts(_("\t--blacklist-time=SECS\t-k\tTime (in seconds) to blacklist an IP."));
657
-	puts(_("\t--black-hole-mode\t\tDon't scan messages aliased to /dev/null."));
658
-#ifdef	BOUNCE
659
-	puts(_("\t--bounce\t\t-b\tSend a failure message to the sender."));
660
-#endif
661
-	puts(_("\t--broadcast\t\t-B [IFACE]\tBroadcast to a network manager when a virus is found."));
662
-	puts(_("\t--chroot=DIR\t\t-C DIR\tChroot to dir when starting."));
663
-	puts(_("\t--config-file=FILE\t-c FILE\tRead configuration from FILE."));
664
-	puts(_("\t--debug\t\t\t-D\tPrint debug messages."));
665
-	puts(_("\t--detect-forged-local-address\t-L\tReject mails that claim to be from us."));
666
-	puts(_("\t--dont-blacklist\t-K\tDon't blacklist a given IP."));
667
-	puts(_("\t--dont-scan-on-error\t-d\tPass e-mails through unscanned if a system error occurs."));
668
-	puts(_("\t--dont-wait\t\t\tAsk remote end to resend if max-children exceeded."));
669
-	puts(_("\t--dont-sanitise\t\t\tAllow semicolon and pipe characters in email addresses."));
670
-	puts(_("\t--external\t\t-e\tUse an external scanner (usually clamd)."));
671
-	puts(_("\t--freshclam-monitor=SECS\t-M SECS\tHow often to check for database update."));
672
-	puts(_("\t--from=EMAIL\t\t-a EMAIL\tError messages come from here."));
673
-	puts(_("\t--force-scan\t\t-f\tForce scan all messages (overrides (-o and -l)."));
674
-	puts(_("\t--help\t\t\t-h\tThis message."));
675
-	puts(_("\t--headers\t\t-H\tInclude original message headers in the report."));
676
-	puts(_("\t--ignore IPaddr\t\t-I IPaddr\tAdd IPaddr to LAN IP list (see --local)."));
677
-	puts(_("\t--local\t\t\t-l\tScan messages sent from machines on our LAN."));
678
-	puts(_("\t--max-childen\t\t-m\tMaximum number of concurrent scans."));
679
-	puts(_("\t--outgoing\t\t-o\tScan outgoing messages from this machine."));
680
-	puts(_("\t--noreject\t\t-N\tDon't reject viruses, silently throw them away."));
681
-	puts(_("\t--noxheader\t\t-n\tSuppress X-Virus-Scanned/X-Virus-Status headers."));
682
-	puts(_("\t--pidfile=FILE\t\t-i FILE\tLocation of pidfile."));
683
-	puts(_("\t--postmaster\t\t-p EMAIL\tPostmaster address [default=postmaster]."));
684
-	puts(_("\t--postmaster-only\t-P\tSend notifications only to the postmaster."));
685
-	puts(_("\t--quiet\t\t\t-q\tDon't send e-mail notifications of interceptions."));
686
-	puts(_("\t--quarantine=USER\t-Q EMAIL\tQuarantine e-mail account."));
687
-	puts(_("\t--report-phish=EMAIL\t-r EMAIL\tReport phish to this email address."));
688
-	puts(_("\t--report-phish-false-positives=EMAIL\t-R EMAIL\tReport phish false positves to this email address."));
689
-	puts(_("\t--quarantine-dir=DIR\t-U DIR\tDirectory to store infected emails."));
690
-	puts(_("\t--server=SERVER\t\t-s SERVER\tHostname/IP address of server(s) running clamd (when using TCPsocket)."));
691
-	puts(_("\t--sendmail-cf=FILE\t\tLocation of the sendmail.cf file to verify"));
692
-	puts(_("\t--no-check-cf\t\tSkip verification of sendmail.cf"));
693
-	puts(_("\t--sign\t\t\t-S\tAdd a hard-coded signature to each scanned message."));
694
-	puts(_("\t--signature-file=FILE\t-F FILE\tLocation of signature file."));
695
-	puts(_("\t--template-file=FILE\t-t FILE\tLocation of e-mail template file."));
696
-	puts(_("\t--template-headers=FILE\t\tLocation of e-mail headers for template file."));
697
-	puts(_("\t--timeout=SECS\t\t-T SECS\tTimeout waiting to childen to die."));
698
-	puts(_("\t--whitelist-file=FILE\t-W FILE\tLocation of the file of whitelisted addresses"));
699
-	puts(_("\t--version\t\t-V\tPrint the version number of this software."));
700
-#ifdef	CL_DEBUG
701
-	puts(_("\t--debug-level=n\t\t-x n\tSets the debug level to 'n'."));
702
-#endif
703
-	puts(_("\nFor more information type \"man clamav-milter\"."));
704
-	puts(_("For bug reports, please refer to http://www.clamav.net/bugs"));
705
-}
706
-
707
-extern char *optarg;
708
-int
709
-main(int argc, char **argv)
710
-{
711
-	int i, Bflag = 0, server = 0;
712
-	char *cfgfile = NULL;
713
-	const char *wont_blacklist = NULL;
714
-	const struct cfgstruct *cpt;
715
-	char version[VERSION_LENGTH + 1];
716
-	pthread_t tid;
717
-	struct rlimit rlim;
718
-#ifdef	CL_DEBUG
719
-	int consolefd;
720
-#endif
721
-
722
-	/*
723
-	 * The SMFI_VERSION checks are for Sendmail 8.14, which I don't have
724
-	 * yet, so I can't verify them
725
-	 * Patch from Andy Fiddaman <clam@fiddaman.net>
726
-	 */
727
-	struct smfiDesc smfilter = {
728
-		"ClamAv", /* filter name */
729
-		SMFI_VERSION,	/* version code -- leave untouched */
730
-		SMFIF_ADDHDRS|SMFIF_CHGHDRS,	/* flags - we add and delete headers */
731
-		clamfi_connect, /* connexion callback */
732
-#ifdef	CL_DEBUG
733
-		clamfi_helo,	/* HELO filter callback */
734
-#else
735
-		NULL,
736
-#endif
737
-		clamfi_envfrom, /* envelope sender filter callback */
738
-		clamfi_envrcpt, /* envelope recipient filter callback */
739
-		clamfi_header,	/* header filter callback */
740
-		clamfi_eoh,	/* end of header callback */
741
-		clamfi_body,	/* body filter callback */
742
-		clamfi_eom,	/* end of message callback */
743
-		clamfi_abort,	/* message aborted callback */
744
-		clamfi_close,	/* connexion cleanup callback */
745
-#if	SMFI_VERSION > 2
746
-		NULL,		/* Unrecognised command */
747
-#endif
748
-#if	SMFI_VERSION > 3
749
-		NULL,		/* DATA command callback */
750
-#endif
751
-#if	SMFI_VERSION >= 0x01000000
752
-		NULL,		/* Negotiation callback */
753
-#endif
754
-	};
755
-
756
-#if defined(CL_DEBUG) && defined(C_LINUX)
757
-	rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
758
-	if(setrlimit(RLIMIT_CORE, &rlim) < 0)
759
-		perror("setrlimit");
760
-#endif
761
-
762
-	/*
763
-	 * Temporarily enter guessed value into version, will
764
-	 * be overwritten later by the value returned by clamd
765
-	 */
766
-	snprintf(version, sizeof(version) - 1,
767
-		"ClamAV version %s, clamav-milter version %s",
768
-		cl_retver(), get_version());
769
-
770
-	progname = strrchr(argv[0], '/');
771
-	if(progname)
772
-		progname++;
773
-	else
774
-		progname = "clamav-milter";
775
-
776
-#ifdef	C_LINUX
777
-	setlocale(LC_ALL, "");
778
-	bindtextdomain(progname, DATADIR"/clamav-milter/locale");
779
-	textdomain(progname);
780
-#endif
781
-
782
-	for(;;) {
783
-		int opt_index = 0;
784
-#ifdef	BOUNCE
785
-#ifdef	CL_DEBUG
786
-		const char *args = "a:AbB:c:C:dDefF:I:i:k:K:lLm:M:nNop:PqQ:r:R:hHs:St:T:U:VwW:x:z0:1:2";
787
-#else
788
-		const char *args = "a:AbB:c:C:dDefF:I:i:k:K:lLm:M:nNop:PqQ:r:R:hHs:St:T:U:VwW:z0:1:2";
789
-#endif
790
-#else	/*!BOUNCE*/
791
-#ifdef	CL_DEBUG
792
-		const char *args = "a:AB:c:C:dDefF:I:i:k:K:lLm:M:nNop:PqQ:r:R:hHs:St:T:U:VwW:x:z0:1:2";
793
-#else
794
-		const char *args = "a:AB:c:C:dDefF:I:i:k:K:lLm:M:nNop:PqQ:r:R:hHs:St:T:U:VwW:z0:1:2";
795
-#endif
796
-#endif	/*BOUNCE*/
797
-
798
-		static struct option long_options[] = {
799
-			{
800
-				"from", 2, NULL, 'a'
801
-			},
802
-			{
803
-				"advisory", 0, NULL, 'A'
804
-			},
805
-#ifdef	BOUNCE
806
-			{
807
-				"bounce", 0, NULL, 'b'
808
-			},
809
-#endif
810
-			{
811
-				"broadcast", 2, NULL, 'B'
812
-			},
813
-			{
814
-				"config-file", 1, NULL, 'c'
815
-			},
816
-			{
817
-				"chroot", 1, NULL, 'C'
818
-			},
819
-			{
820
-				"detect-forged-local-address", 0, NULL, 'L'
821
-			},
822
-			{
823
-				"dont-blacklist", 1, NULL, 'K'
824
-			},
825
-			{
826
-				"dont-scan-on-error", 0, NULL, 'd'
827
-			},
828
-			{
829
-				"dont-wait", 0, NULL, 'w'
830
-			},
831
-			{
832
-				"dont-sanitise", 0, NULL, 'z'
833
-			},
834
-			{
835
-				"debug", 0, NULL, 'D'
836
-			},
837
-			{
838
-				"external", 0, NULL, 'e'
839
-			},
840
-			{
841
-				"force-scan", 0, NULL, 'f'
842
-			},
843
-			{
844
-				"headers", 0, NULL, 'H'
845
-			},
846
-			{
847
-				"help", 0, NULL, 'h'
848
-			},
849
-			{
850
-				"ignore", 1, NULL, 'I'
851
-			},
852
-			{
853
-				"pidfile", 1, NULL, 'i'
854
-			},
855
-			{
856
-				"blacklist-time", 1, NULL, 'k'
857
-			},
858
-			{
859
-				"local", 0, NULL, 'l'
860
-			},
861
-			{
862
-				"noreject", 0, NULL, 'N'
863
-			},
864
-			{
865
-				"noxheader", 0, NULL, 'n'
866
-			},
867
-			{
868
-				"outgoing", 0, NULL, 'o'
869
-			},
870
-			{
871
-				"postmaster", 1, NULL, 'p'
872
-			},
873
-			{
874
-				"postmaster-only", 0, NULL, 'P',
875
-			},
876
-			{
877
-				"quiet", 0, NULL, 'q'
878
-			},
879
-			{
880
-				"quarantine", 1, NULL, 'Q',
881
-			},
882
-			{
883
-				"report-phish", 1, NULL, 'r'
884
-			},
885
-			{
886
-				"report-phish-false-positives", 1, NULL, 'R'
887
-			},
888
-			{
889
-				"quarantine-dir", 1, NULL, 'U',
890
-			},
891
-			{
892
-				"max-children", 1, NULL, 'm'
893
-			},
894
-			{
895
-				"freshclam-monitor", 1, NULL, 'M'
896
-			},
897
-			{
898
-				"sendmail-cf", 1, NULL, '0'
899
-			},
900
-			{
901
-				"no-check-cf", 0, &checkCF, 0
902
-			},
903
-			{
904
-				"server", 1, NULL, 's'
905
-			},
906
-			{
907
-				"sign", 0, NULL, 'S'
908
-			},
909
-			{
910
-				"signature-file", 1, NULL, 'F'
911
-			},
912
-			{
913
-				"template-file", 1, NULL, 't'
914
-			},
915
-			{
916
-				"template-headers", 1, NULL, '1'
917
-			},
918
-			{
919
-				"timeout", 1, NULL, 'T'
920
-			},
921
-			{
922
-				"whitelist-file", 1, NULL, 'W'
923
-			},
924
-			{
925
-				"version", 0, NULL, 'V'
926
-			},
927
-			{
928
-				"black-hole-mode", 0, NULL, '2'
929
-			},
930
-#ifdef	CL_DEBUG
931
-			{
932
-				"debug-level", 1, NULL, 'x'
933
-			},
934
-#endif
935
-			{
936
-				NULL, 0, NULL, '\0'
937
-			}
938
-		};
939
-
940
-		int ret = getopt_long(argc, argv, args, long_options, &opt_index);
941
-
942
-		if(ret == -1)
943
-			break;
944
-  		else if(ret == 0)
945
-  			continue;
946
-
947
-		switch(ret) {
948
-			case 'a':	/* e-mail errors from here */
949
-				/*
950
-				 * optarg is optional - if you give --from
951
-				 * then the --from is set to the orginal,
952
-				 * probably forged, email address
953
-				 */
954
-				from = optarg;
955
-				break;
956
-			case 'A':
957
-				advisory++;
958
-				break;
959
-#ifdef	BOUNCE
960
-			case 'b':	/* bounce worms/viruses */
961
-				bflag++;
962
-				break;
963
-#endif
964
-			case 'B':	/* broadcast */
965
-				Bflag++;
966
-				if(optarg)
967
-					iface = optarg;
968
-				break;
969
-			case 'c':	/* where is clamd.conf? */
970
-				cfgfile = optarg;
971
-				break;
972
-			case 'C':	/* chroot */
973
-				rootdir = optarg;
974
-				break;
975
-			case 'd':	/* don't scan on error */
976
-				cl_error = SMFIS_ACCEPT;
977
-				break;
978
-			case 'D':	/* enable debug messages */
979
-				cl_debug();
980
-				break;
981
-			case 'e':	/* use clamd */
982
-				external++;
983
-				break;
984
-			case 'f':	/* force the scan */
985
-				fflag++;
986
-				break;
987
-			case 'h':
988
-				help();
989
-				return EX_OK;
990
-			case 'H':
991
-				hflag++;
992
-				break;
993
-			case 'i':	/* pidfile */
994
-				pidfile = optarg;
995
-				break;
996
-			case 'k':	/* blacklist time */
997
-				blacklist_time = atoi(optarg);
998
-				break;
999
-			case 'K':	/* don't black list given IP */
1000
-				wont_blacklist = optarg;
1001
-				break;
1002
-			case 'I':	/* --ignore, -I hostname */
1003
-				/*
1004
-				 * Based on patch by jpd@louisiana.edu
1005
-				 */
1006
-				if(Iflag == IFLAG_MAX) {
1007
-					fprintf(stderr,
1008
-						_("%s: %s, -I may only be given %d times\n"),
1009
-							argv[0], optarg, IFLAG_MAX);
1010
-					return EX_USAGE;
1011
-				}
1012
-				if(!add_local_ip(optarg)) {
1013
-					fprintf(stderr,
1014
-						_("%s: Cannot convert -I%s to IPaddr\n"),
1015
-							argv[0], optarg);
1016
-					return EX_USAGE;
1017
-				}
1018
-				Iflag++;
1019
-				break;
1020
-			case 'l':	/* scan mail from the lan */
1021
-				lflag++;
1022
-				break;
1023
-			case 'L':	/* detect forged local addresses */
1024
-				detect_forged_local_address++;
1025
-				break;
1026
-			case 'm':	/* maximum number of children */
1027
-				max_children = atoi(optarg);
1028
-				break;
1029
-			case 'M':	/* how often to monitor for freshclam */
1030
-				freshclam_monitor = atoi(optarg);
1031
-				break;
1032
-			case 'n':	/* don't add X-Virus-Scanned */
1033
-				nflag++;
1034
-				smfilter.xxfi_flags &= ~(SMFIF_ADDHDRS|SMFIF_CHGHDRS);
1035
-				break;
1036
-			case 'N':	/* Do we reject mail or silently drop it */
1037
-				rejectmail = 0;
1038
-				break;
1039
-			case 'o':	/* scan outgoing mail */
1040
-				oflag++;
1041
-				break;
1042
-			case 'p':	/* postmaster e-mail address */
1043
-				postmaster = optarg;
1044
-				break;
1045
-			case 'P':	/* postmaster only */
1046
-				pflag++;
1047
-				break;
1048
-			case 'q':	/* send NO notification email */
1049
-				qflag++;
1050
-				break;
1051
-			case 'Q':	/* quarantine e-mail address */
1052
-				quarantine = optarg;
1053
-				smfilter.xxfi_flags |= SMFIF_CHGHDRS|SMFIF_ADDRCPT|SMFIF_DELRCPT;
1054
-				break;
1055
-			case 'r':	/* report phishing here */
1056
-				/* e.g. reportphishing@antiphishing.org */
1057
-				report = optarg;
1058
-				break;
1059
-			case 'R':	/* report phishing false positives here */
1060
-				report_fps = optarg;
1061
-				break;
1062
-			case 's':	/* server running clamd */
1063
-				server++;
1064
-				serverHostNames = optarg;
1065
-				break;
1066
-			case 'F':	/* signature file */
1067
-				sigFilename = optarg;
1068
-				signature = NULL;
1069
-				/* fall through */
1070
-			case 'S':	/* sign */
1071
-				smfilter.xxfi_flags |= SMFIF_CHGBODY;
1072
-				Sflag++;
1073
-				break;
1074
-			case 't':	/* e-mail template file */
1075
-				templateFile = optarg;
1076
-				break;
1077
-			case '1':	/* headers for the template file */
1078
-				templateHeaders = optarg;
1079
-				break;
1080
-			case '2':
1081
-				black_hole_mode++;
1082
-				break;
1083
-			case 'T':	/* time to wait for child to die */
1084
-				child_timeout = atoi(optarg);
1085
-				break;
1086
-			case 'U':	/* quarantine path */
1087
-				quarantine_dir = optarg;
1088
-				break;
1089
-			case 'V':
1090
-				puts(version);
1091
-				return EX_OK;
1092
-			case 'w':
1093
-				dont_wait++;
1094
-				break;
1095
-			case 'W':
1096
-				whitelistFile = optarg;
1097
-				break;
1098
-			case 'z':
1099
-				dont_sanitise=1;
1100
-				break;
1101
-			case '0':
1102
-				sendmailCF = optarg;
1103
-				break;
1104
-#ifdef	CL_DEBUG
1105
-			case 'x':
1106
-				debug_level = atoi(optarg);
1107
-				break;
1108
-#endif
1109
-			default:
1110
-#ifdef	CL_DEBUG
1111
-				fprintf(stderr, "Usage: %s [-b] [-c FILE] [-F FILE] [--max-children=num] [-e] [-l] [-o] [-p address] [-P] [-q] [-Q USER] [-s SERVER] [-S] [-x#] [-U PATH] [-M#] socket-addr\n", argv[0]);
1112
-#else
1113
-				fprintf(stderr, "Usage: %s [-b] [-c FILE] [-F FILE] [--max-children=num] [-e] [-l] [-o] [-p address] [-P] [-q] [-Q USER] [-s SERVER] [-S] [-U PATH] [-M#] socket-addr\n", argv[0]);
1114
-#endif
1115
-				return EX_USAGE;
1116
-		}
1117
-	}
1118
-
1119
-	/*
1120
-	 * Check sanity of --external and --server arguments
1121
-	 */
1122
-	if(server && !external) {
1123
-		fprintf(stderr,
1124
-			"%s: --server can only be used with --external\n",
1125
-			argv[0]);
1126
-		return EX_USAGE;
1127
-	}
1128
-#ifdef	SESSION
1129
-	if(!external) {
1130
-		fprintf(stderr,
1131
-			_("%s: SESSIONS mode requires --external\n"), argv[0]);
1132
-		return EX_USAGE;
1133
-	}
1134
-#endif
1135
-
1136
-	/* TODO: support freshclam's daemon notify if --external is not given */
1137
-
1138
-	if(optind == argc) {
1139
-		fprintf(stderr, _("%s: No socket-addr given\n"), argv[0]);
1140
-		return EX_USAGE;
1141
-	}
1142
-	port = argv[optind];
1143
-
1144
-	if(rootdir == NULL)	/* FIXME: Handle CHROOT */
1145
-		if(checkCF && verifyIncomingSocketName(port) < 0) {
1146
-			fprintf(stderr, _("%s: socket-addr (%s) doesn't agree with sendmail.cf\n"), argv[0], port);
1147
-			return EX_CONFIG;
1148
-		}
1149
-
1150
-	if(strncasecmp(port, "inet:", 5) == 0)
1151
-		if(!lflag) {
1152
-			/*
1153
-			 * Barmy but true. It seems that clamfi_connect will,
1154
-			 * in this case, get the IP address of the machine
1155
-			 * running sendmail, not of the machine sending the
1156
-			 * mail, so the remote end will be a local address so
1157
-			 * we must scan by enabling --local
1158
-			 *
1159
-			 * TODO: this is probably not needed if the remote
1160
-			 * machine is localhost, need to check though
1161
-			 */
1162
-			fprintf(stderr, _("%s: when using inet: connexion to sendmail you must enable --local\n"), argv[0]);
1163
-			return EX_USAGE;
1164
-		}
1165
-
1166
-	/*
1167
-	 * Sanity checks on the clamav configuration file
1168
-	 */
1169
-	if(cfgfile == NULL) {
1170
-		cfgfile = cli_malloc(strlen(CONFDIR) + 12);	/* leak */
1171
-		sprintf(cfgfile, "%s/clamd.conf", CONFDIR);
1172
-	}
1173
-	if((copt = getcfg(cfgfile, 1, OPT_CLAMD)) == NULL) {
1174
-		fprintf(stderr, _("%s: Can't parse the config file %s\n"),
1175
-			argv[0], cfgfile);
1176
-		return EX_CONFIG;
1177
-	}
1178
-
1179
-	if(detect_forged_local_address) {
1180
-		if(oflag) {
1181
-			fprintf(stderr, _("%s: --detect-forged-local-addresses is not compatible with --outgoing\n"), argv[0]);
1182
-			return EX_CONFIG;
1183
-		}
1184
-		if(lflag) {
1185
-			fprintf(stderr, _("%s: --detect-forged-local-addresses is not compatible with --local\n"), argv[0]);
1186
-			return EX_CONFIG;
1187
-		}
1188
-		if(fflag) {
1189
-			fprintf(stderr, _("%s: --detect-forged-local-addresses is not compatible with --force\n"), argv[0]);
1190
-			return EX_CONFIG;
1191
-		}
1192
-	}
1193
-
1194
-	if(Bflag) {
1195
-		int on;
1196
-
1197
-		broadcastSock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1198
-		/*
1199
-		 * SO_BROADCAST doesn't sent to all NICs on Linux, it only
1200
-		 * broadcasts on eth0, which is why there's an optional argument
1201
-		 * to --broadcast to say which NIC to broadcast on. You can use
1202
-		 * SO_BINDTODEVICE to get around that, but you need to have
1203
-		 * uid == 0 for that
1204
-		 */
1205
-		on = 1;
1206
-		if(setsockopt(broadcastSock, SOL_SOCKET, SO_BROADCAST, (int *)&on, sizeof(on)) < 0) {
1207
-			perror("setsockopt");
1208
-			return EX_UNAVAILABLE;
1209
-		}
1210
-		shutdown(broadcastSock, SHUT_RD);
1211
-	}
1212
-
1213
-	/*
1214
-	 * Drop privileges
1215
-	 */
1216
-#ifdef	CL_DEBUG
1217
-	/* Save the fd for later, open while we can */
1218
-	consolefd = open(console, O_WRONLY);
1219
-#endif
1220
-
1221
-	if(getuid() == 0) {
1222
-		if(iface) {
1223
-#ifdef	SO_BINDTODEVICE
1224
-			struct ifreq ifr;
1225
-
1226
-			memset(&ifr, '\0', sizeof(struct ifreq));
1227
-			strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1);
1228
-			ifr.ifr_name[sizeof(ifr.ifr_name)-1]='\0';
1229
-			if(setsockopt(broadcastSock, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0) {
1230
-				perror(iface);
1231
-				return EX_CONFIG;
1232
-			}
1233
-#else
1234
-			fprintf(stderr, _("%s: The iface option to --broadcast is not supported on your operating system\n"), argv[0]);
1235
-			return EX_CONFIG;
1236
-#endif
1237
-		}
1238
-
1239
-		if(((cpt = cfgopt(copt, "User")) != NULL) && cpt->enabled) {
1240
-			const struct passwd *user;
1241
-
1242
-			if((user = getpwnam(cpt->strarg)) == NULL) {
1243
-				fprintf(stderr, _("%s: Can't get information about user %s\n"), argv[0], cpt->strarg);
1244
-				return EX_CONFIG;
1245
-			}
1246
-
1247
-			if(cfgopt(copt, "AllowSupplementaryGroups")->enabled) {
1248
-#ifdef HAVE_INITGROUPS
1249
-				if(initgroups(cpt->strarg, user->pw_gid) < 0) {
1250
-					perror(cpt->strarg);
1251
-					return EX_CONFIG;
1252
-				}
1253
-#else
1254
-				fprintf(stderr, _("%s: AllowSupplementaryGroups: initgroups not supported.\n"),
1255
-					argv[0]);
1256
-				return EX_CONFIG;
1257
-#endif
1258
-			} else {
1259
-#ifdef	HAVE_SETGROUPS
1260
-				if(setgroups(1, &user->pw_gid) < 0) {
1261
-					perror(cpt->strarg);
1262
-					return EX_CONFIG;
1263
-				}
1264
-#endif
1265
-			}
1266
-
1267
-			setgid(user->pw_gid);
1268
-
1269
-			if(setuid(user->pw_uid) < 0)
1270
-				perror(cpt->strarg);
1271
-#ifdef	CL_DEBUG
1272
-			else
1273
-				printf(_("Running as user %s (UID %d, GID %d)\n"),
1274
-					cpt->strarg, (int)user->pw_uid,
1275
-					(int)user->pw_gid);
1276
-#endif
1277
-
1278
-			/*
1279
-			 * Note, some O/Ss (e.g. OpenBSD/Fedora Linux) FORCE
1280
-			 * you to run as root in black-hole-mode because
1281
-			 * /var/spool/mqueue is mode 700 owned by root!
1282
-			 * Flames to them, not to me, please.
1283
-			 */
1284
-			if(black_hole_mode && (user->pw_uid != 0)) {
1285
-				int are_trusted;
1286
-				FILE *sendmail;
1287
-				char cmd[128];
1288
-
1289
-				/*
1290
-				 * Determine if we're a "trusted user"
1291
-				 */
1292
-				snprintf(cmd, sizeof(cmd) - 1, "%s -bv root</dev/null 2>&1",
1293
-					SENDMAIL_BIN);
1294
-
1295
-				sendmail = popen(cmd, "r");
1296
-
1297
-				if(sendmail == NULL) {
1298
-					perror(SENDMAIL_BIN);
1299
-					are_trusted = 0;
1300
-				} else {
1301
-					int status;
1302
-					char buf[BUFSIZ];
1303
-
1304
-					while(fgets(buf, sizeof(buf), sendmail) != NULL)
1305
-						;
1306
-					/*
1307
-					 * Can't do
1308
-					 * switch(WEXITSTATUS(pclose(sendmail)))
1309
-					 * because that fails to compile on
1310
-					 * NetBSD2.0
1311
-					 */
1312
-					status = pclose(sendmail);
1313
-					switch(WEXITSTATUS(status)) {
1314
-						case EX_NOUSER:
1315
-							/*
1316
-							 * No root? But at least
1317
-							 * we're trusted enough
1318
-							 * to find out!
1319
-							 */
1320
-							are_trusted = 1;
1321
-							break;
1322
-						default:
1323
-							are_trusted = 0;
1324
-							break;
1325
-						case EX_OK:
1326
-							are_trusted = 1;
1327
-					}
1328
-				}
1329
-				if(!are_trusted) {
1330
-					fprintf(stderr, _("%s: You cannot use black hole mode unless %s is a TrustedUser\n"),
1331
-						argv[0], cpt->strarg);
1332
-					return EX_CONFIG;
1333
-				}
1334
-			}
1335
-		} else
1336
-			printf(_("^%s: running as root is not recommended (check \"User\" in %s)\n"), argv[0], cfgfile);
1337
-	} else if(iface) {
1338
-		fprintf(stderr, _("%s: Only root can set an interface for --broadcast\n"), argv[0]);
1339
-		return EX_USAGE;
1340
-	}
1341
-
1342
-	if(advisory && quarantine) {
1343
-		fprintf(stderr, _("%s: Advisory mode doesn't work with quarantine mode\n"), argv[0]);
1344
-		return EX_USAGE;
1345
-	}
1346
-	if(quarantine_dir) {
1347
-		struct stat statb;
1348
-
1349
-		if(advisory) {
1350
-			fprintf(stderr,
1351
-				_("%s: Advisory mode doesn't work with quarantine directories\n"),
1352
-				argv[0]);
1353
-			return EX_USAGE;
1354
-		}
1355
-		if(strstr(quarantine_dir, "ERROR") != NULL) {
1356
-			fprintf(stderr,
1357
-				_("%s: the quarantine directory must not contain the string 'ERROR'\n"),
1358
-				argv[0]);
1359
-			return EX_USAGE;
1360
-		}
1361
-		if(strstr(quarantine_dir, "FOUND") != NULL) {
1362
-			fprintf(stderr,
1363
-				_("%s: the quarantine directory must not contain the string 'FOUND'\n"),
1364
-				argv[0]);
1365
-			return EX_USAGE;
1366
-		}
1367
-		if(strstr(quarantine_dir, "OK") != NULL) {
1368
-			fprintf(stderr,
1369
-				_("%s: the quarantine directory must not contain the string 'OK'\n"),
1370
-				argv[0]);
1371
-			return EX_USAGE;
1372
-		}
1373
-		if(access(quarantine_dir, W_OK) < 0) {
1374
-			perror(quarantine_dir);
1375
-			return EX_USAGE;
1376
-		}
1377
-		if(stat(quarantine_dir, &statb) < 0) {
1378
-			perror(quarantine_dir);
1379
-			return EX_USAGE;
1380
-		}
1381
-		/*
1382
-		 * Quit if the quarantine directory is publically readable
1383
-		 * or writeable
1384
-		 */
1385
-		if(statb.st_mode & 077) {
1386
-			fprintf(stderr, _("%s: insecure quarantine directory %s (mode 0%o)\n"),
1387
-				argv[0], quarantine_dir, (int)statb.st_mode & 0777);
1388
-			return EX_CONFIG;
1389
-		}
1390
-	}
1391
-
1392
-	if(sigFilename && !updateSigFile())
1393
-		return EX_USAGE;
1394
-
1395
-	if(templateFile && (access(templateFile, R_OK) < 0)) {
1396
-		perror(templateFile);
1397
-		return EX_CONFIG;
1398
-	}
1399
-	if(templateHeaders) {
1400
-		if(templateFile == NULL) {
1401
-			fputs(("%s: --template-headers requires --template-file\n"),
1402
-				stderr);
1403
-			return EX_CONFIG;
1404
-		}
1405
-		if(access(templateHeaders, R_OK) < 0) {
1406
-			perror(templateHeaders);
1407
-			return EX_CONFIG;
1408
-		}
1409
-	}
1410
-	if(whitelistFile && (access(whitelistFile, R_OK) < 0)) {
1411
-		perror(whitelistFile);
1412
-		return EX_CONFIG;
1413
-	}
1414
-
1415
-	/*
1416
-	 * If the --max-children flag isn't set, see if MaxThreads
1417
-	 * is set in the config file. Based on an idea by "Richard G. Roberto"
1418
-	 * <rgr@dedlegend.com>
1419
-	 */
1420
-	if((max_children == 0) && ((cpt = cfgopt(copt, "MaxThreads")) != NULL))
1421
-		max_children = cfgopt(copt, "MaxThreads")->numarg;
1422
-
1423
-#ifdef HAVE_LRESOLV_R
1424
-	/* allocate a pool of resolvers */
1425
-	if(!(res_pool=cli_calloc(max_children+1, sizeof(*res_pool))))
1426
-		return EX_OSERR;
1427
-	if(!(res_pool_state=cli_malloc(max_children+1)))
1428
-		return EX_OSERR;
1429
-	memset(res_pool_state, 1, max_children+1);
1430
-	for(i = 0; i < max_children+1; i++)
1431
-		res_ninit(&res_pool[i]);
1432
-#endif
1433
-
1434
-	if((cpt = cfgopt(copt, "ReadTimeout")) != NULL) {
1435
-		readTimeout = cpt->numarg;
1436
-
1437
-		if(readTimeout < 0) {
1438
-			fprintf(stderr, _("%s: ReadTimeout must not be negative in %s\n"),
1439
-				argv[0], cfgfile);
1440
-			return EX_CONFIG;
1441
-		}
1442
-	}
1443
-
1444
-	if((cpt = cfgopt(copt, "StreamMaxLength")) != NULL) {
1445
-		streamMaxLength = (long)cpt->numarg;
1446
-		if(streamMaxLength < 0L) {
1447
-			fprintf(stderr, _("%s: StreamMaxLength must not be negative in %s\n"),
1448
-			argv[0], cfgfile);
1449
-			return EX_CONFIG;
1450
-		}
1451
-	}
1452
-
1453
-	if(((cpt = cfgopt(copt, "LogSyslog")) != NULL) && cpt->enabled) {
1454
-#if defined(USE_SYSLOG) && !defined(C_AIX)
1455
-		int fac = LOG_LOCAL6;
1456
-#endif
1457
-
1458
-		if(cfgopt(copt, "LogVerbose")->enabled) {
1459
-			logg_verbose = 1;
1460
-#ifdef	CL_DEBUG
1461
-#if	((SENDMAIL_VERSION_A > 8) || ((SENDMAIL_VERSION_A == 8) && (SENDMAIL_VERSION_B >= 13)))
1462
-			if(debug_level >= 15)
1463
-				smfi_setdbg(6);
1464
-#endif
1465
-#endif
1466
-		}
1467
-#if defined(USE_SYSLOG) && !defined(C_AIX)
1468
-		logg_syslog = 1;
1469
-
1470
-		if(((cpt = cfgopt(copt, "LogFacility")) != NULL) && cpt->enabled)
1471
-			if((fac = logg_facility(cpt->strarg)) == -1) {
1472
-				fprintf(stderr, "%s: LogFacility: %s: No such facility\n",
1473
-					argv[0], cpt->strarg);
1474
-				return EX_CONFIG;
1475
-			}
1476
-		openlog(progname, LOG_CONS|LOG_PID, fac);
1477
-#endif
1478
-	} else {
1479
-		if(qflag)
1480
-			fprintf(stderr, _("%s: (-q && !LogSyslog): warning - all interception message methods are off\n"),
1481
-				argv[0]);
1482
-#if defined(USE_SYSLOG) && !defined(C_AIX)
1483
-		logg_syslog = 0;
1484
-#endif
1485
-	}
1486
-	/*
1487
-	 * Get the outgoing socket details - the way to talk to clamd, unless
1488
-	 * we're doing the scanning internally
1489
-	 */
1490
-	if(!external) {
1491
-#ifdef	C_LINUX
1492
-		const char *lang;
1493
-#endif
1494
-
1495
-		if(max_children == 0) {
1496
-			fprintf(stderr, _("%s: --max-children must be given if --external is not given\n"), argv[0]);
1497
-			return EX_CONFIG;
1498
-		}
1499
-		if(freshclam_monitor <= 0) {
1500
-			fprintf(stderr, _("%s: --freshclam_monitor must be at least one second\n"), argv[0]);
1501
-			return EX_CONFIG;
1502
-		}
1503
-#ifdef	C_LINUX
1504
-		lang = getenv("LANG");
1505
-
1506
-		if(lang && (strstr(lang, "UTF-8") != NULL)) {
1507
-			fprintf(stderr, "Your LANG environment variable is set to '%s'\n", lang);
1508
-			fprintf(stderr, "This is known to cause problems for some %s installations.\n", argv[0]);
1509
-			fputs("If you get failures with temporary files, please try again with LANG unset.\n", stderr);
1510
-		}
1511
-#endif
1512
-#if	0
1513
-		if(child_timeout) {
1514
-			fprintf(stderr, _("%s: --timeout must not be given if --external is not given\n"), argv[0]);
1515
-			return EX_CONFIG;
1516
-		}
1517
-#endif
1518
-		if (cl_init(CL_INIT_DEFAULT)!=CL_SUCCESS) {
1519
-			fprintf(stderr, "%s: Failed to initialize libclamav, bailing out.\n", argv[0]);
1520
-			return EX_UNAVAILABLE;
1521
-		}
1522
-		if(loadDatabase() != 0) {
1523
-			/*
1524
-			 * Handle the dont-scan-on-error option, which says
1525
-			 * that we pass on emails, unscanned, if an error has
1526
-			 * occurred
1527
-			 */
1528
-			if(cl_error != SMFIS_ACCEPT)
1529
-				return EX_CONFIG;
1530
-
1531
-			fprintf(stderr, _("%s: No emails will be scanned"),
1532
-				argv[0]);
1533
-		}
1534
-		numServers = 1;
1535
-	} else if(((cpt = cfgopt(copt, "LocalSocket")) != NULL) && cpt->enabled) {
1536
-#ifdef	SESSION
1537
-		struct sockaddr_un sockun;
1538
-#endif
1539
-		char *sockname = NULL;
1540
-
1541
-		if(cfgopt(copt, "TCPSocket")->enabled) {
1542
-			fprintf(stderr, _("%s: You can select one server type only (local/TCP) in %s\n"),
1543
-				argv[0], cfgfile);
1544
-			return EX_CONFIG;
1545
-		}
1546
-		if(server) {
1547
-			fprintf(stderr, _("%s: You cannot use the --server option when using LocalSocket in %s\n"),
1548
-				argv[0], cfgfile);
1549
-			return EX_USAGE;
1550
-		}
1551
-		if(strncasecmp(port, "unix:", 5) == 0)
1552
-			sockname = &port[5];
1553
-		else if(strncasecmp(port, "local:", 6) == 0)
1554
-			sockname = &port[6];
1555
-
1556
-		if(sockname && (strcmp(sockname, cpt->strarg) == 0)) {
1557
-			fprintf(stderr, _("The connexion from sendmail to %s (%s) must not\n"),
1558
-				argv[0], sockname);
1559
-			fprintf(stderr, _("be the same as the connexion to clamd (%s) in %s\n"),
1560
-				cpt->strarg, cfgfile);
1561
-			return EX_CONFIG;
1562
-		}
1563
-		/*
1564
-		 * TODO: check --server hasn't been set
1565
-		 */
1566
-		localSocket = cpt->strarg;
1567
-#ifndef	SESSION
1568
-		if(!pingServer(-1)) {
1569
-			fprintf(stderr, _("Can't talk to clamd server via %s\n"),
1570
-				localSocket);
1571
-			fprintf(stderr, _("Check your entry for LocalSocket in %s\n"),
1572
-				cfgfile);
1573
-			return EX_CONFIG;
1574
-		}
1575
-#endif
1576
-		/*if(quarantine_dir == NULL)
1577
-			fprintf(stderr, _("When using Localsocket in %s\nyou may improve performance if you use the --quarantine-dir option\n"), cfgfile);*/
1578
-
1579
-		umask(077);
1580
-
1581
-		serverIPs = (in_addr_t *)cli_malloc(sizeof(in_addr_t));
1582
-#ifdef	INADDR_LOOPBACK
1583
-		serverIPs[0] = htonl(INADDR_LOOPBACK);
1584
-#else
1585
-		serverIPs[0] = inet_addr("127.0.0.1");
1586
-#endif
1587
-
1588
-#ifdef	SESSION
1589
-		memset((char *)&sockun, 0, sizeof(struct sockaddr_un));
1590
-		sockun.sun_family = AF_UNIX;
1591
-		strncpy(sockun.sun_path, localSocket, sizeof(sockun.sun_path));
1592
-		sockun.sun_path[sizeof(sockun.sun_path)-1]='\0';
1593
-
1594
-		sessions = (struct session *)cli_malloc(sizeof(struct session));
1595
-		if((sessions[0].sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
1596
-			perror(localSocket);
1597
-			fprintf(stderr, _("Can't talk to clamd server via %s\n"),
1598
-				localSocket);
1599
-			fprintf(stderr, _("Check your entry for LocalSocket in %s\n"),
1600
-				cfgfile);
1601
-			return EX_CONFIG;
1602
-		}
1603
-		if(connect(sessions[0].sock, (struct sockaddr *)&sockun, sizeof(struct sockaddr_un)) < 0) {
1604
-			perror(localSocket);
1605
-			return EX_UNAVAILABLE;
1606
-		}
1607
-		if(send(sessions[0].sock, "SESSION\n", 8, 0) < 8) {
1608
-			perror("send");
1609
-			fputs(_("!Can't create a clamd session"), stderr);
1610
-			return EX_UNAVAILABLE;
1611
-		}
1612
-		sessions[0].status = CMDSOCKET_FREE;
1613
-#endif
1614
-		/*
1615
-		 * FIXME: Allow connexion to remote servers by TCP/IP whilst
1616
-		 * connecting to the localserver via a UNIX domain socket
1617
-		 */
1618
-		numServers = 1;
1619
-	} else if(((cpt = cfgopt(copt, "TCPSocket")) != NULL) && cpt->enabled) {
1620
-		int activeServers;
1621
-
1622
-		/*
1623
-		 * TCPSocket is in fact a port number not a full socket
1624
-		 */
1625
-		if(quarantine_dir) {
1626
-			fprintf(stderr, _("%s: --quarantine-dir not supported for TCPSocket - use --quarantine\n"), argv[0]);
1627
-			return EX_CONFIG;
1628
-		}
1629
-
1630
-		tcpSocket = (in_port_t)cpt->numarg;
1631
-
1632
-		/*
1633
-		 * cli_strtok's fieldno counts from 0
1634
-		 */
1635
-		for(;;) {
1636
-			char *hostname = cli_strtok(serverHostNames, numServers, ":");
1637
-			if(hostname == NULL)
1638
-				break;
1639
-#ifdef	MAXHOSTNAMELEN
1640
-			if(strlen(hostname) > MAXHOSTNAMELEN) {
1641
-				fprintf(stderr, _("%s: hostname %s is longer than %d characters\n"),
1642
-					argv[0], hostname, MAXHOSTNAMELEN);
1643
-				return EX_CONFIG;
1644
-			}
1645
-#endif
1646
-			numServers++;
1647
-			free(hostname);
1648
-		}
1649
-
1650
-#ifdef	CL_DEBUG
1651
-		printf("numServers: %d\n", numServers);
1652
-#endif
1653
-
1654
-		serverIPs = (in_addr_t *)cli_malloc(numServers * sizeof(in_addr_t));
1655
-		if(serverIPs == NULL)
1656
-			return EX_OSERR;
1657
-		activeServers = 0;
1658
-
1659
-#ifdef	SESSION
1660
-		/*
1661
-		 * We need to know how many connexion to establish to clamd
1662
-		 */
1663
-		if(max_children == 0) {
1664
-			fprintf(stderr, _("%s: --max-children must be given in sessions mode\n"), argv[0]);
1665
-			return EX_CONFIG;
1666
-		}
1667
-#endif
1668
-
1669
-		if(numServers > max_children) {
1670
-			fprintf(stderr, _("%1$s: --max-children (%2$d) is lower than the number of servers you have (%3$d)\n"),
1671
-				argv[0], max_children, numServers);
1672
-			return EX_CONFIG;
1673
-		}
1674
-
1675
-		for(i = 0; i < numServers; i++) {
1676
-#ifdef	MAXHOSTNAMELEN
1677
-			char hostname[MAXHOSTNAMELEN + 1];
1678
-
1679
-			if(cli_strtokbuf(serverHostNames, i, ":", hostname) == NULL)
1680
-				break;
1681
-#else
1682
-			char *hostname = cli_strtok(serverHostNames, i, ":");
1683
-#endif
1684
-
1685
-			/*
1686
-			 * Translate server's name to IP address
1687
-			 */
1688
-			serverIPs[i] = inet_addr(hostname);
1689
-#ifdef	INADDR_NONE
1690
-			if(serverIPs[i] == INADDR_NONE) {
1691
-#else
1692
-			if(serverIPs[i] == (in_addr_t)-1) {
1693
-#endif
1694
-				const struct hostent *h = gethostbyname(hostname);
1695
-
1696
-				if(h == NULL) {
1697
-					fprintf(stderr, _("%s: Unknown host %s\n"),
1698
-						argv[0], hostname);
1699
-					return EX_USAGE;
1700
-				}
1701
-
1702
-				memcpy((char *)&serverIPs[i], h->h_addr, sizeof(serverIPs[i]));
1703
-			}
1704
-
1705
-#if	defined(NTRIES) && ((NTRIES > 1))
1706
-#ifndef	SESSION
1707
-#ifdef	INADDR_LOOPBACK
1708
-			if(serverIPs[i] == htonl(INADDR_LOOPBACK)) {
1709
-#else
1710
-#if	HAVE_IN_ADDR_T
1711
-			if(serverIPs[i] == (in_addr_t)inet_addr("127.0.0.1")) {
1712
-#else
1713
-			if(serverIPs[i] == (long)inet_addr("127.0.0.1")) {
1714
-#endif
1715
-#endif
1716
-				int tries;
1717
-
1718
-				/*
1719
-				 * Fudge to allow clamd to come up on
1720
-				 * our local machine
1721
-				 */
1722
-				for(tries = 0; tries < NTRIES - 1; tries++) {
1723
-					if(pingServer(i))
1724
-						break;
1725
-					if(checkClamd(1))	/* will try all servers */
1726
-						break;
1727
-					puts(_("Waiting for clamd to come up"));
1728
-					/*
1729
-					 * something to do as the system starts
1730
-					 */
1731
-					sync();
1732
-					sleep(1);
1733
-				}
1734
-				/* Will try one more time */
1735
-			}
1736
-#endif	/* NTRIES > 1 */
1737
-
1738
-			if(pingServer(i))
1739
-				activeServers++;
1740
-			else {
1741
-				printf(_("Can't talk to clamd server %s on port %d\n"),
1742
-					hostname, tcpSocket);
1743
-				if(serverIPs[i] == htonl(INADDR_LOOPBACK)) {
1744
-					if(cfgopt(copt, "TCPAddr")->enabled)
1745
-						printf(_("Check the value for TCPAddr in %s\n"), cfgfile);
1746
-				} else
1747
-					printf(_("Check the value for TCPAddr in clamd.conf on %s\n"), hostname);
1748
-			}
1749
-#endif
1750
-
1751
-#ifndef	MAXHOSTNAMELEN
1752
-			free(hostname);
1753
-#endif
1754
-		}
1755
-#ifdef	SESSION
1756
-		activeServers = numServers;
1757
-
1758
-		sessions = (struct session *)cli_calloc(max_children, sizeof(struct session));
1759
-		for(i = 0; i < (int)max_children; i++)
1760
-			if(createSession(i) < 0)
1761
-				return EX_UNAVAILABLE;
1762
-		if(activeServers == 0) {
1763
-			fprintf(stderr, _("Check your entry for TCPSocket in %s\n"),
1764
-				cfgfile);
1765
-		}
1766
-#else
1767
-		if(activeServers == 0) {
1768
-			fprintf(stderr, _("Check your entry for TCPSocket in %s\n"),
1769
-				cfgfile);
1770
-			fputs(_("Can't find any clamd server\n"), stderr);
1771
-			return EX_CONFIG;
1772
-		}
1773
-		last_failed_pings = (time_t *)cli_calloc(numServers, sizeof(time_t));
1774
-#endif
1775
-	} else {
1776
-		fprintf(stderr, _("%s: You must select server type (local/TCP) in %s\n"),
1777
-			argv[0], cfgfile);
1778
-		return EX_CONFIG;
1779
-	}
1780
-
1781
-#ifdef	SESSION
1782
-	if(!external) {
1783
-		if(clamav_versions == NULL) {
1784
-			clamav_versions = (char **)cli_malloc(sizeof(char *));
1785
-			if(clamav_versions == NULL)
1786
-				return EX_TEMPFAIL;
1787
-			clamav_version = cli_strdup(version);
1788
-		}
1789
-	} else {
1790
-		unsigned int session;
1791
-
1792
-		/*
1793
-		 * We need to know how many connexions to establish to clamd
1794
-		 */
1795
-		if(max_children == 0) {
1796
-			fprintf(stderr, _("%s: --max-children must be given in sessions mode\n"), argv[0]);
1797
-			return EX_CONFIG;
1798
-		}
1799
-
1800
-		clamav_versions = (char **)cli_malloc(max_children * sizeof(char *));
1801
-		if(clamav_versions == NULL)
1802
-			return EX_TEMPFAIL;
1803
-
1804
-		for(session = 0; session < max_children; session++) {
1805
-			clamav_versions[session] = cli_strdup(version);
1806
-			if(clamav_versions[session] == NULL)
1807
-				return EX_TEMPFAIL;
1808
-		}
1809
-	}
1810
-#else
1811
-	strcpy(clamav_version, version);
1812
-#endif
1813
-
1814
-	if(((quarantine_dir == NULL) && localSocket) || !external) {
1815
-		/* set the temporary dir */
1816
-		if((cpt = cfgopt(copt, "TemporaryDirectory")) && cpt->enabled) {
1817
-			tmpdir = cpt->strarg;
1818
-			/*
1819
-			 * FIXME: replace this:
1820
-			  cl_settempdir(tmpdir, (short)(cfgopt(copt, "LeaveTemporaryFiles")->enabled)); *
1821
-			 * with:
1822
-			 * cl_engine_set(engine, CL_ENGINE_TMPDIR, cpt->strarg);
1823
-			 * somewhere...
1824
-			 */
1825
-		} else if((tmpdir = getenv("TMPDIR")) == (char *)NULL)
1826
-			if((tmpdir = getenv("TMP")) == (char *)NULL)
1827
-				if((tmpdir = getenv("TEMP")) == (char *)NULL)
1828
-#ifdef	P_tmpdir
1829
-					tmpdir = P_tmpdir;
1830
-#else
1831
-					tmpdir = "/tmp";
1832
-#endif
1833
-
1834
-		/*
1835
-		 * TODO: investigate mkdtemp on LINUX and possibly others
1836
-		 */
1837
-		tmpdir = cli_gentemp(NULL);
1838
-
1839
-		cli_dbgmsg("Making %s\n", tmpdir);
1840
-
1841
-		if(mkdir(tmpdir, 0700)) {
1842
-			perror(tmpdir);
1843
-			return EX_CANTCREAT;
1844
-		}
1845
-	} else
1846
-		tmpdir = NULL;
1847
-
1848
-	if(report) {
1849
-		if(!cfgopt(copt, "PhishingSignatures")->enabled) {
1850
-			fprintf(stderr, "%s: You have chosen --report-phish, but PhishingSignatures is off in %s\n",
1851
-				argv[0], cfgfile);
1852
-			return EX_USAGE;
1853
-		}
1854
-		if((quarantine_dir == NULL) && (tmpdir == NULL)) {
1855
-			/*
1856
-			 * Limitation: doesn't store message in a temporary
1857
-			 * file, so we won't be able to use mail < file
1858
-			 */
1859
-			fprintf(stderr, "%s: when using --external, --report-phish cannot be used without either LocalSocket or --quarantine-dir\n",
1860
-				argv[0]);
1861
-			return EX_USAGE;
1862
-		}
1863
-		if(lflag) {
1864
-			/*
1865
-			 * Naturally, if you attempt to scan the phish you've
1866
-			 * just reported, it'll be blocked!
1867
-			 */
1868
-			fprintf(stderr, "%s: --report-phish cannot be used with --local\n",
1869
-				argv[0]);
1870
-			return EX_USAGE;
1871
-		}
1872
-	}
1873
-	if(report_fps)
1874
-		if(!cfgopt(copt, "PhishingSignatures")->enabled) {
1875
-			fprintf(stderr, "%s: You have chosen --report-phish-false-positives, but PhishingSignatures is off in %s\n",
1876
-				argv[0], cfgfile);
1877
-			return EX_USAGE;
1878
-		}
1879
-
1880
-	if(cfgopt(copt, "Foreground")->enabled)
1881
-		logg_foreground = 1;
1882
-	else {
1883
-		logg_foreground = 0;
1884
-#ifdef	CL_DEBUG
1885
-		printf(_("When debugging it is recommended that you use Foreground mode in %s\n"), cfgfile);
1886
-		puts(_("\tso that you can see all of the messages"));
1887
-#endif
1888
-
1889
-		switch(fork()) {
1890
-			case -1:
1891
-				perror("fork");
1892
-				return EX_OSERR;
1893
-			case 0:	/* child */
1894
-				break;
1895
-			default:	/* parent */
1896
-				return EX_OK;
1897
-		}
1898
-		close(0);
1899
-		open("/dev/null", O_RDONLY);
1900
-
1901
-		/* initialize logger */
1902
-		logg_lock = cfgopt(copt, "LogFileUnlock")->enabled;
1903
-		logg_time = cfgopt(copt, "LogTime")->enabled;
1904
-		logok = cfgopt(copt, "LogClean")->enabled;
1905
-		logg_size = cfgopt(copt, "LogFileMaxSize")->numarg;
1906
-		logg_verbose = mprintf_verbose = cfgopt(copt, "LogVerbose")->enabled;
1907
-
1908
-		if(cfgopt(copt, "Debug")->enabled) /* enable debug messages in libclamav */
1909
-			cl_debug();
1910
-
1911
-		if((cpt = cfgopt(copt, "LogFile"))->enabled) {
1912
-			time_t currtime;
1913
-
1914
-			logg_file = cpt->strarg;
1915
-			if((strlen(logg_file) < 2) ||
1916
-			   ((logg_file[0] != '/') && (logg_file[0] != '\\') && (logg_file[1] != ':'))) {
1917
-				fprintf(stderr, "ERROR: LogFile requires full path.\n");
1918
-				logg_close();
1919
-				freecfg(copt);
1920
-				return 1;
1921
-			}
1922
-			time(&currtime);
1923
-			close(1);
1924
-			if(logg("#ClamAV-milter started at %s", ctime(&currtime))) {
1925
-				fprintf(stderr, "ERROR: Problem with internal logger. Please check the permissions on the %s file.\n", logg_file);
1926
-				logg_close();
1927
-				freecfg(copt);
1928
-				return 1;
1929
-			}
1930
-		} else {
1931
-#ifdef	CL_DEBUG
1932
-			close(1);
1933
-			logg_file = console;
1934
-			if(consolefd < 0) {
1935
-				perror(console);
1936
-				return EX_OSFILE;
1937
-			}
1938
-			dup(consolefd);
1939
-#else
1940
-			int fds[3];
1941
-			logg_file = NULL;
1942
-			if(chdir("/") < 0)
1943
-				perror("/");
1944
-			fds[0] = open("/dev/null", O_RDONLY);
1945
-			fds[1] = open("/dev/null", O_WRONLY);
1946
-			fds[2] = open("/dev/null", O_WRONLY);
1947
-			for(i = 0; i <= 2; i++) {
1948
-				if(fds[i] == -1 || dup2(fds[i], i) == -1) {
1949
-					fprintf(stderr, "ERROR: failed to daemonize.\n");
1950
-					logg_close();
1951
-					freecfg(copt);
1952
-					return 1;
1953
-				}
1954
-			}
1955
-#endif
1956
-		}
1957
-
1958
-		dup2(1, 2);
1959
-
1960
-#ifdef	CL_DEBUG
1961
-		if(consolefd >= 0)
1962
-			close(consolefd);
1963
-#endif
1964
-
1965
-#ifdef HAVE_SETPGRP
1966
-#ifdef SETPGRP_VOID
1967
-		setpgrp();
1968
-#else
1969
-		setpgrp(0,0);
1970
-#endif
1971
-#else
1972
-#ifdef HAVE_SETSID
1973
-		setsid();
1974
-#endif
1975
-#endif
1976
-	}
1977
-
1978
-	if(cfgopt(copt, "Debug")->enabled)
1979
-		/*
1980
-		 * enable debug messages in libclamav, --debug also does this
1981
-		 */
1982
-		cl_debug();
1983
-
1984
-	atexit(quit);
1985
-
1986
-	if(!external) {
1987
-		if(!cfgopt(copt, "ScanMail")->enabled)
1988
-			printf(_("%s: ScanMail not defined in %s (needed without --external), enabling\n"),
1989
-				argv[0], cfgfile);
1990
-
1991
-		options |= CL_SCAN_MAIL;	/* no choice */
1992
-		/*if(!cfgopt(copt, "ScanRAR")->enabled)
1993
-			options |= CL_SCAN_DISABLERAR;*/
1994
-		if(cfgopt(copt, "ArchiveBlockEncrypted")->enabled)
1995
-			options |= CL_SCAN_BLOCKENCRYPTED;
1996
-		if(cfgopt(copt, "ScanPE")->enabled)
1997
-			options |= CL_SCAN_PE;
1998
-		if(cfgopt(copt, "DetectBrokenExecutables")->enabled)
1999
-			options |= CL_SCAN_BLOCKBROKEN;
2000
-		if(cfgopt(copt, "MailFollowURLs")->enabled)
2001
-			options |= CL_SCAN_MAILURL;
2002
-		if(cfgopt(copt, "ScanOLE2")->enabled)
2003
-			options |= CL_SCAN_OLE2;
2004
-		if(cfgopt(copt, "ScanHTML")->enabled)
2005
-			options |= CL_SCAN_HTML;
2006
-
2007
-		if(((cpt = cfgopt(copt, "MaxScanSize")) != NULL) && cpt->enabled)
2008
-			maxscansize = cpt->numarg;
2009
-		else
2010
-			maxscansize = 104857600;
2011
-		if(((cpt = cfgopt(copt, "MaxFileSize")) != NULL) && cpt->enabled)
2012
-			maxfilesize = cpt->numarg;
2013
-		else
2014
-			maxfilesize = 10485760;
2015
-
2016
-		if(getrlimit(RLIMIT_FSIZE, &rlim) == 0) {
2017
-			if((rlim.rlim_max < maxfilesize) || (rlim.rlim_max < maxscansize))
2018
-				logg("^System limit for file size is lower than maxfilesize or maxscansize\n");
2019
-		} else {
2020
-			logg("^Cannot obtain resource limits for file size\n");
2021
-		}
2022
-
2023
-		if(((cpt = cfgopt(copt, "MaxRecursion")) != NULL) && cpt->enabled)
2024
-			maxreclevel = cpt->numarg;
2025
-		else
2026
-			maxreclevel = 8;
2027
-
2028
-		if(((cpt = cfgopt(copt, "MaxFiles")) != NULL) && cpt->enabled)
2029
-			maxfiles = cpt->numarg;
2030
-		else
2031
-			maxfiles = 1000;
2032
-
2033
-		if(cfgopt(copt, "ScanArchive")->enabled)
2034
-			options |= CL_SCAN_ARCHIVE;
2035
-	}
2036
-
2037
-	pthread_create(&tid, NULL, watchdog, NULL);
2038
-
2039
-	if(((cpt = cfgopt(copt, "PidFile")) != NULL) && cpt->enabled)
2040
-		pidFile = cpt->strarg;
2041
-
2042
-	broadcast(_("Starting clamav-milter"));
2043
-
2044
-	if(rootdir) {
2045
-		if(getuid() == 0) {
2046
-			if(chdir(rootdir) < 0) {
2047
-				perror(rootdir);
2048
-				logg("!chdir %s failed\n", rootdir);
2049
-				return EX_CONFIG;
2050
-			}
2051
-			if(chroot(rootdir) < 0) {
2052
-				perror(rootdir);
2053
-				logg("!chroot %s failed\n", rootdir);
2054
-				return EX_CONFIG;
2055
-			}
2056
-			logg("Chrooted to %s\n", rootdir);
2057
-		} else {
2058
-			logg("!chroot option needs root\n");
2059
-			return EX_CONFIG;
2060
-		}
2061
-	}
2062
-
2063
-	if(pidfile) {
2064
-		/* save the PID */
2065
-		char *p, *q;
2066
-		FILE *fd;
2067
-		const mode_t old_umask = umask(0006);
2068
-
2069
-		if(pidfile[0] != '/') {
2070
-			logg(_("!pidfile: '%s' must be a full pathname"),
2071
-				pidfile);
2072
-
2073
-			return EX_CONFIG;
2074
-		}
2075
-		p = cli_strdup(pidfile);
2076
-		q = strrchr(p, '/');
2077
-		*q = '\0';
2078
-
2079
-		if(rootdir == NULL)
2080
-			if(chdir(p) < 0)	/* safety */
2081
-				perror(p);
2082
-
2083
-		free(p);
2084
-
2085
-		if((fd = fopen(pidfile, "w")) == NULL) {
2086
-			logg(_("!Can't save PID in file %s\n"), pidfile);
2087
-			return EX_CONFIG;
2088
-		}
2089
-#ifdef	C_LINUX
2090
-		/* Ensure that all threads are kill()ed */
2091
-		fprintf(fd, "-%d\n", (int)getpgrp());
2092
-#else
2093
-		fprintf(fd, "%d\n", (int)getpid());
2094
-#endif
2095
-		fclose(fd);
2096
-		umask(old_umask);
2097
-	} else if(tmpdir) {
2098
-		if(rootdir == NULL)
2099
-			if(chdir(tmpdir) < 0) {	/* safety */
2100
-				perror(tmpdir);
2101
-				logg("!chdir %s failed\n", tmpdir);
2102
-			}
2103
-	} else
2104
-		if(rootdir == NULL)
2105
-#ifdef	P_tmpdir
2106
-			if(chdir(P_tmpdir) < 0) {
2107
-				perror(P_tmpdir);
2108
-				logg("!chdir %s failed\n", P_tmpdir);
2109
-			}
2110
-#else
2111
-			if(chdir("/tmp") < 0) {
2112
-				perror("/tmp");
2113
-				logg("!chdir /tmp failed\n", P_tmpdir);
2114
-			}
2115
-#endif
2116
-
2117
-	if(cfgopt(copt, "FixStaleSocket")->enabled) {
2118
-		/*
2119
-		 * Get the incoming socket details - the way sendmail talks to
2120
-		 * us
2121
-		 *
2122
-		 * TODO: There's a security problem here that'll need fixing if
2123
-		 * the User entry of clamd.conf is not used
2124
-		 */
2125
-		if(strncasecmp(port, "unix:", 5) == 0) {
2126
-			if(unlink(&port[5]) < 0)
2127
-				if(errno != ENOENT)
2128
-					perror(&port[5]);
2129
-		} else if(strncasecmp(port, "local:", 6) == 0) {
2130
-			if(unlink(&port[6]) < 0)
2131
-				if(errno != ENOENT)
2132
-					perror(&port[6]);
2133
-		} else if(port[0] == '/') {
2134
-			if(unlink(port) < 0)
2135
-				if(errno != ENOENT)
2136
-					perror(port);
2137
-		}
2138
-	}
2139
-
2140
-	if(smfi_setconn(port) == MI_FAILURE) {
2141
-		cli_errmsg("smfi_setconn failure\n");
2142
-		return EX_SOFTWARE;
2143
-	}
2144
-
2145
-	if(smfi_register(smfilter) == MI_FAILURE) {
2146
-		fprintf(stderr, "smfi_register failure, ensure that you have linked against the correct version of sendmail\n");
2147
-		return EX_UNAVAILABLE;
2148
-	}
2149
-
2150
-#if	((SENDMAIL_VERSION_A > 8) || ((SENDMAIL_VERSION_A == 8) && (SENDMAIL_VERSION_B >= 13)))
2151
-	if(smfi_opensocket(1) == MI_FAILURE) {
2152
-		perror(port);
2153
-		fprintf(stderr, "Can't open/create %s\n", port);
2154
-		return EX_CONFIG;
2155
-	}
2156
-#endif
2157
-
2158
-	signal(SIGPIPE, SIG_IGN);	/* libmilter probably does this as well */
2159
-	signal(SIGXFSZ, SIG_IGN); /* TODO: check if it's safe to call signal() here */
2160
-
2161
-#ifdef	SESSION
2162
-	pthread_mutex_lock(&version_mutex);
2163
-#endif
2164
-	logg(_("Starting %s\n"), clamav_version);
2165
-	logg(_("*Debugging is on\n"));
2166
-
2167
-#ifdef HAVE_RESOLV_H
2168
-#if ! defined(HAVE_LRESOLV_R)
2169
-	if(!(_res.options&RES_INIT))
2170
-		if(res_init() < 0) {
2171
-			fprintf(stderr, "%s: Can't initialise the resolver\n",
2172
-				argv[0]);
2173
-			return EX_UNAVAILABLE;
2174
-		}
2175
-#endif
2176
-	if(blacklist_time) {
2177
-		char name[MAXHOSTNAMELEN + 1];
2178
-
2179
-		if(gethostname(name, sizeof(name)) < 0) {
2180
-			perror("gethostname");
2181
-			return EX_UNAVAILABLE;
2182
-		}
2183
-
2184
-		blacklist = mx(name, NULL);
2185
-		if(blacklist)
2186
-			/* We must never blacklist ourself */
2187
-			tableInsert(blacklist, "127.0.0.1", 0);
2188
-
2189
-		if(wont_blacklist) {
2190
-			char *w;
2191
-
2192
-			i = 0;
2193
-			while((w = cli_strtok(wont_blacklist, i++, ",")) != NULL) {
2194
-				(void)tableInsert(blacklist, w, 0);
2195
-				free(w);
2196
-			}
2197
-		}
2198
-		tableIterate(blacklist, dump_blacklist, NULL);
2199
-	}
2200
-#endif /* HAVE_RESOLV_H */
2201
-
2202
-#ifdef	SESSION
2203
-	pthread_mutex_unlock(&version_mutex);
2204
-#endif
2205
-
2206
-	(void)signal(SIGSEGV, sigsegv);
2207
-	if(!logg_foreground)
2208
-		(void)signal(SIGUSR1, sigusr1);
2209
-	if(!external)
2210
-		(void)signal(SIGUSR2, sigusr2);
2211
-
2212
-	return smfi_main();
2213
-}
2214
-
2215
-#ifdef	SESSION
2216
-/*
2217
- * Use the SESSION command of clamd.
2218
- * Returns -1 for terminal failure, 0 for OK, 1 for nonterminal failure
2219
- * The caller must take care of locking the sessions array
2220
- */
2221
-static int
2222
-createSession(unsigned int s)
2223
-{
2224
-	int ret = 0, fd;
2225
-	const int serverNumber = s % numServers;
2226
-	struct session *session = &sessions[s];
2227
-	const struct protoent *proto;
2228
-	struct sockaddr_in server;
2229
-
2230
-	cli_dbgmsg("createSession session %d, server %d\n", s, serverNumber);
2231
-	assert(s < max_children);
2232
-
2233
-	memset((char *)&server, 0, sizeof(struct sockaddr_in));
2234
-	server.sin_family = AF_INET;
2235
-	server.sin_port = (in_port_t)htons(tcpSocket);
2236
-
2237
-	server.sin_addr.s_addr = serverIPs[serverNumber];
2238
-
2239
-	session->sock = -1;
2240
-	proto = getprotobyname("tcp");
2241
-	if(proto == NULL) {
2242
-		fputs("Unknown prototol tcp, check /etc/protocols\n", stderr);
2243
-		fd = ret = -1;
2244
-	} else if((fd = socket(AF_INET, SOCK_STREAM, proto->p_proto)) < 0) {
2245
-		perror("socket");
2246
-		ret = -1;
2247
-	} else if(connect(fd, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0) {
2248
-		perror("connect");
2249
-		ret = 1;
2250
-	} else if(send(fd, "SESSION\n", 8, 0) < 8) {
2251
-		perror("send");
2252
-		ret = 1;
2253
-	}
2254
-
2255
-	if(ret != 0) {
2256
-#ifdef	MAXHOSTNAMELEN
2257
-		char hostname[MAXHOSTNAMELEN + 1];
2258
-
2259
-		cli_strtokbuf(serverHostNames, serverNumber, ":", hostname);
2260
-		if(strcmp(hostname, "127.0.0.1") == 0)
2261
-			gethostname(hostname, sizeof(hostname));
2262
-#else
2263
-		char *hostname = cli_strtok(serverHostNames, serverNumber, ":");
2264
-#endif
2265
-
2266
-		session->status = CMDSOCKET_DOWN;
2267
-
2268
-		if(fd >= 0)
2269
-			close(fd);
2270
-
2271
-		cli_warnmsg(_("Check clamd server %s - it may be down\n"), hostname);
2272
-#ifndef	MAXHOSTNAMELEN
2273
-		free(hostname);
2274
-#endif
2275
-
2276
-		broadcast(_("Check clamd server - it may be down"));
2277
-	} else
2278
-		session->sock = fd;
2279
-
2280
-	return ret;
2281
-}
2282
-
2283
-#else
2284
-
2285
-/*
2286
- * Verify that the server is where we think it is
2287
- * Returns true or false
2288
- *
2289
- * serverNumber counts from 0, but is only used for TCPSocket
2290
- */
2291
-static int
2292
-pingServer(int serverNumber)
2293
-{
2294
-	char *ptr;
2295
-	int sock;
2296
-	long nbytes;
2297
-	char buf[128];
2298
-
2299
-	if(localSocket) {
2300
-		struct sockaddr_un server;
2301
-
2302
-		memset((char *)&server, 0, sizeof(struct sockaddr_un));
2303
-		server.sun_family = AF_UNIX;
2304
-		strncpy(server.sun_path, localSocket, sizeof(server.sun_path));
2305
-		server.sun_path[sizeof(server.sun_path)-1]='\0';
2306
-
2307
-		if((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
2308
-			perror(localSocket);
2309
-			return 0;
2310
-		}
2311
-		checkClamd(1);
2312
-		if(connect(sock, (struct sockaddr *)&server, sizeof(struct sockaddr_un)) < 0) {
2313
-			perror(localSocket);
2314
-			close(sock);
2315
-			return 0;
2316
-		}
2317
-	} else {
2318
-		struct sockaddr_in server;
2319
-		char *hostname;
2320
-
2321
-		memset((char *)&server, 0, sizeof(struct sockaddr_in));
2322
-		server.sin_family = AF_INET;
2323
-		server.sin_port = (in_port_t)htons(tcpSocket);
2324
-
2325
-		assert(serverIPs != NULL);
2326
-#ifdef	INADDR_NONE
2327
-		assert(serverIPs[0] != INADDR_NONE);
2328
-#else
2329
-		assert(serverIPs[0] != (in_addr_t)-1);
2330
-#endif
2331
-
2332
-		server.sin_addr.s_addr = serverIPs[serverNumber];
2333
-
2334
-		if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
2335
-			perror("socket");
2336
-			return 0;
2337
-		}
2338
-		hostname = cli_strtok(serverHostNames, serverNumber, ":");
2339
-		/*
2340
-		 * FIXME: use non-blocking connect, once the code is
2341
-		 * amalgomated
2342
-		 */
2343
-		if(nonblock_connect(sock, &server, hostname) < 0) {
2344
-			int is_connected = 0;
2345
-
2346
-#if	(!defined(NTRIES)) || ((NTRIES <= 1))
2347
-			if(errno == ECONNREFUSED) {
2348
-				/*
2349
-				 * During startup there is a race condition:
2350
-				 * clamd can start and fork, then rc will start
2351
-				 * clamav-milter before clamd has run accept(2),
2352
-				 * so we fail to connect.
2353
-				 * In case this is the situation here, we wait
2354
-				 * for a couple of seconds and try again. The
2355
-				 * sync() is because during startup the machine
2356
-				 * won't be doing much for most of the time, so
2357
-				 * we may as well do something constructive!
2358
-				 */
2359
-				sync();
2360
-				sleep(2);
2361
-				if(nonblock_connect(sock, &server, hostname) >= 0)
2362
-					is_connected = 1;
2363
-			}
2364
-#endif
2365
-			if(!is_connected) {
2366
-				if(errno != EINPROGRESS)
2367
-					perror(hostname ? hostname : "connect");
2368
-				close(sock);
2369
-				if(hostname)
2370
-					free(hostname);
2371
-				return 0;
2372
-			}
2373
-		}
2374
-		if(hostname)
2375
-			free(hostname);
2376
-	}
2377
-
2378
-	/*
2379
-	 * It would be better to use PING, check for PONG then issue the
2380
-	 * VERSION command, since that would better validate that we're
2381
-	 * talking to clamd, however clamd closes the session after
2382
-	 * sending PONG :-(
2383
-	 * So this code does not really validate that we're talking to clamd
2384
-	 * Needs a fix to clamd
2385
-	 * Also version command is verbose: says "clamd / ClamAV version"
2386
-	 * instead of "clamAV version"
2387
-	 */
2388
-	cli_dbgmsg("pingServer%d: sending VERSION\n", serverNumber);
2389
-	if(send(sock, "VERSION\n", 8, 0) < 8) {
2390
-		perror("send");
2391
-		return close(sock);
2392
-	}
2393
-
2394
-	shutdown(sock, SHUT_WR);
2395
-
2396
-	nbytes = clamd_recv(sock, buf, sizeof(buf) - 1);
2397
-
2398
-	close(sock);
2399
-
2400
-	if(nbytes < 0) {
2401
-		perror("recv");
2402
-		return 0;
2403
-	}
2404
-	if(nbytes == 0)
2405
-		return 0;
2406
-
2407
-	buf[nbytes] = '\0';
2408
-
2409
-	/* Remove the trailing new line from the reply */
2410
-	if((ptr = strchr(buf, '\n')) != NULL)
2411
-		*ptr = '\0';
2412
-
2413
-	/*
2414
-	 * No real validation is done here
2415
-	 *
2416
-	 * TODO: When connecting to more than one server, give a warning
2417
-	 *	if they're running different versions, or if the virus DBs
2418
-	 *	are out of date (say more than a month old)
2419
-	 */
2420
-	snprintf(clamav_version, sizeof(clamav_version) - 1,
2421
-		"%s\n\tclamav-milter version %s",
2422
-		buf, get_version());
2423 67
 
68
+int main(int argc, char **argv) {
69
+    static struct cfgstruct *copt;
70
+    char *my_socket;
71
+    const struct cfgstruct *cpt;
72
+    struct optstruct *opt;
73
+    const char *short_options = "c:hV";
74
+    static struct option long_options[] = {
75
+	{"config-file", required_argument, NULL, 'c'},
76
+	{"help", no_argument, NULL, 'h'},
77
+	{"version", no_argument, NULL, 'V'},
78
+	{NULL, 0, NULL, 0}
79
+    };
80
+    const char *my_conf = CONFDIR "/clamav-milter.conf";
81
+    int ret;
82
+
83
+    opt = opt_parse(argc, argv, short_options, long_options, NULL, NULL);	
84
+    if (!opt) {
85
+	mprintf("!Can't parse the command line\n");
2424 86
 	return 1;
2425
-}
2426
-#endif
2427
-
2428
-/*
2429
- * Find the best server to connect to. No intelligence to this.
2430
- * It is best to weight the order of the servers from most wanted to least
2431
- * wanted
2432
- *
2433
- * Return value is from 0 - index into sessions array
2434
- *
2435
- * If the load balancing fails return the first server in the list, not
2436
- * an error, to be on the safe side
2437
- */
2438
-#ifdef	SESSION
2439
-static int
2440
-findServer(void)
2441
-{
2442
-	unsigned int i, j;
2443
-	struct session *session;
2444
-
2445
-	/*
2446
-	 * FIXME: Sessions code isn't flexible at handling servers
2447
-	 *	appearing and disappearing, e.g. sessions[n_children].sock == -1
2448
-	 */
2449
-	i = 0;
2450
-	pthread_mutex_lock(&n_children_mutex);
2451
-	assert(n_children > 0);
2452
-	assert(n_children <= max_children);
2453
-	j = n_children - 1;
2454
-	pthread_mutex_unlock(&n_children_mutex);
2455
-
2456
-	pthread_mutex_lock(&sstatus_mutex);
2457
-	for(; i < max_children; i++) {
2458
-		const int sess = (j + i) % max_children;
2459
-
2460
-		session = &sessions[sess];
2461
-		cli_dbgmsg("findServer: try server %d\n", sess);
2462
-		if(session->status == CMDSOCKET_FREE) {
2463
-			session->status = CMDSOCKET_INUSE;
2464
-			pthread_mutex_unlock(&sstatus_mutex);
2465
-			return sess;
2466
-		}
2467
-	}
2468
-	pthread_mutex_unlock(&sstatus_mutex);
2469
-
2470
-	/*
2471
-	 * No session free - wait until one comes available. Only
2472
-	 * retries once.
2473
-	 */
2474
-	if(pthread_cond_broadcast(&watchdog_cond) < 0)
2475
-		perror("pthread_cond_broadcast");
2476
-
2477
-	i = 0;
2478
-	session = sessions;
2479
-	pthread_mutex_lock(&sstatus_mutex);
2480
-	for(; i < max_children; i++, session++) {
2481
-		cli_dbgmsg("findServer: try server %d\n", i);
2482
-		if(session->status == CMDSOCKET_FREE) {
2483
-			session->status = CMDSOCKET_INUSE;
2484
-			pthread_mutex_unlock(&sstatus_mutex);
2485
-			return i;
2486
-		}
2487
-	}
2488
-	pthread_mutex_unlock(&sstatus_mutex);
2489
-
2490
-	cli_warnmsg(_("No free clamd sessions\n"));
2491
-
2492
-	return -1;	/* none available - must fail */
2493
-}
2494
-#else
2495
-/*
2496
- * Return value is from 0 - index into serverIPs
2497
- */
2498
-static int
2499
-findServer(void)
2500
-{
2501
-	struct sockaddr_in *servers, *server;
2502
-	int maxsock, i, j, active;
2503
-	int retval;
2504
-	pthread_t *tids;
2505
-	struct try_server_struct *socks;
2506
-	fd_set rfds;
2507
-
2508
-	assert(tcpSocket != 0);
2509
-	assert(numServers > 0);
2510
-
2511
-	if(numServers == 1)
2512
-		return 0;
2513
-
2514
-	if(active_servers(&active) <= 1)
2515
-		return active;
2516
-
2517
-	servers = (struct sockaddr_in *)cli_calloc(numServers, sizeof(struct sockaddr_in));
2518
-	if(servers == NULL)
2519
-		return 0;
2520
-	socks = (struct try_server_struct *)cli_malloc(numServers * sizeof(struct try_server_struct));
2521
-
2522
-	if(max_children > 0) {
2523
-		assert(n_children > 0);
2524
-		assert(n_children <= max_children);
2525
-
2526
-		/*
2527
-		 * Don't worry about no lock - it's doesn't matter if it's
2528
-		 * not really accurate
2529
-		 */
2530
-		j = n_children - 1;	/* look at the next free one */
2531
-		if(j < 0)
2532
-			j = 0;
2533
-	} else
2534
-		/*
2535
-		 * cli_rndnum returns 0..max
2536
-		 */
2537
-		j = cli_rndnum(numServers - 1);
2538
-
2539
-	for(i = 0; i < numServers; i++)
2540
-		socks[i].sock = -1;
2541
-
2542
-	tids = cli_malloc(numServers * sizeof(pthread_t));
2543
-
2544
-	for(i = 0, server = servers; i < numServers; i++, server++) {
2545
-		int sock;
2546
-		int server_index = (i + j) % numServers;
2547
-
2548
-		server->sin_family = AF_INET;
2549
-		server->sin_port = (in_port_t)htons(tcpSocket);
2550
-		server->sin_addr.s_addr = serverIPs[server_index];
2551
-
2552
-		logg("*findServer: try server %d\n", server_index);
2553
-
2554
-		sock = socks[i].sock = socket(AF_INET, SOCK_STREAM, 0);
2555
-
2556
-		if(sock < 0) {
2557
-			perror("socket");
2558
-			while(i--) {
2559
-				pthread_join(tids[i], NULL);
2560
-				if(socks[i].sock >= 0)
2561
-					close(socks[i].sock);
2562
-			}
2563
-			free(socks);
2564
-			free(servers);
2565
-			free(tids);
2566
-			return 0;	/* Use the first server on failure */
2567
-		}
2568
-
2569
-		socks[i].server = server;
2570
-		socks[i].server_index = server_index;
2571
-
2572
-		if(pthread_create(&tids[i], NULL, try_server, &socks[i]) != 0) {
2573
-			perror("pthread_create");
2574
-			j = i;
2575
-			do {
2576
-				if (j!=i) pthread_join(tids[i], NULL);
2577
-				if(socks[i].sock >= 0)
2578
-					close(socks[i].sock);
2579
-			} while(--i >= 0);
2580
-			free(socks);
2581
-			free(servers);
2582
-			free(tids);
2583
-			return 0;	/* Use the first server on failure */
2584
-		}
2585
-	}
2586
-
2587
-	maxsock = -1;
2588
-	FD_ZERO(&rfds);
2589
-
2590
-	for(i = 0; i < numServers; i++) {
2591
-		struct try_server_struct *rc;
2592
-
2593
-		pthread_join(tids[i], (void **)&rc);
2594
-		assert(rc->sock == socks[i].sock);
2595
-		if(rc->rc == 0) {
2596
-			close(rc->sock);
2597
-			socks[i].sock = -1;
2598
-		} else {
2599
-			shutdown(rc->sock, SHUT_WR);
2600
-			FD_SET(rc->sock, &rfds);
2601
-			if(rc->sock > maxsock)
2602
-				maxsock = rc->sock;
2603
-		}
2604
-	}
2605
-
2606
-	free(servers);
2607
-	free(tids);
2608
-
2609
-	if(maxsock == -1) {
2610
-		logg(_("^Couldn't establish a connexion to any clamd server\n"));
2611
-		retval = 0;
2612
-	} else {
2613
-		struct timeval tv;
2614
-
2615
-		tv.tv_sec = readTimeout ? readTimeout : DEFAULT_TIMEOUT;
2616
-		tv.tv_usec = 0;
2617
-
2618
-		retval = select(maxsock + 1, &rfds, NULL, NULL, &tv);
2619
-	}
2620
-
2621
-	if(retval < 0)
2622
-		perror("select");
2623
-
2624
-	for(i = 0; i < numServers; i++)
2625
-		if(socks[i].sock >= 0)
2626
-			close(socks[i].sock);
2627
-
2628
-	if(retval == 0) {
2629
-		free(socks);
2630
-		clamdIsDown();
2631
-		return 0;
2632
-	} else if(retval < 0) {
2633
-		free(socks);
2634
-		logg(_("^findServer: select failed (maxsock = %d)\n"), maxsock);
2635
-		return 0;
2636
-	}
2637
-
2638
-	for(i = 0; i < numServers; i++)
2639
-		if((socks[i].sock >= 0) && (FD_ISSET(socks[i].sock, &rfds))) {
2640
-			const int s = (i + j) % numServers;
2641
-
2642
-			free(socks);
2643
-			logg("*findServer: use server %d\n", s);
2644
-			return s;
2645
-		}
2646
-
2647
-	free(socks);
2648
-	logg(_("^findServer: No response from any server\n"));
87
+    }
88
+
89
+    if(opt_check(opt, "help")) {
90
+	printf("Usage: %s [-c <config-file>]\n\n", argv[0]);
91
+	printf("    --help                   -h       Show this help\n");
92
+	printf("    --version                -V       Show version and exit\n");
93
+	printf("    --config-file <file>     -c       Read configuration from file\n\n");
94
+	opt_free(opt);
2649 95
 	return 0;
2650
-}
2651
-
2652
-/*
2653
- * How many servers are up at the moment? If a server is marked as down,
2654
- *	don't keep on flooding it with requests to see if it's now back up
2655
- * If only one server is active, let the caller know, which server is the
2656
- *	active one
2657
- */
2658
-static int
2659
-active_servers(int *active)
2660
-{
2661
-	int server, count;
2662
-	time_t now = (time_t)0;
2663
-
2664
-	for(count = server = 0; server < numServers; server++)
2665
-		if(last_failed_pings[server] == (time_t)0) {
2666
-			*active = server;
2667
-			count++;
2668
-		} else {
2669
-			if(now == (time_t)0)
2670
-				time(&now);
2671
-			if(now - last_failed_pings[server] >= RETRY_SECS)
2672
-				/* Try this server again next time */
2673
-				last_failed_pings[server] = (time_t)0;
2674
-		}
2675
-
2676
-	if(count != 1)
2677
-		*active = 0;
2678
-	return count;
2679
-}
2680
-
2681
-/*
2682
- * Connecting to remote servers can take some time, so let's connect to
2683
- *	them in parallel. This routine is started as a thread
2684
- */
2685
-static void *
2686
-try_server(void *var)
2687
-{
2688
-	struct try_server_struct *s = (struct try_server_struct *)var;
2689
-	int sock = s->sock;
2690
-	struct sockaddr *server = (struct sockaddr *)s->server;
2691
-	int server_index = s->server_index;
2692
-
2693
-	if(last_failed_pings[server_index]) {
2694
-		s->rc = 0;
2695
-		return var;
2696
-	}
2697
-
2698
-	logg("*try_server: sock %d\n", sock);
2699
-
2700
-	if((connect(sock, server, sizeof(struct sockaddr)) < 0) ||
2701
-	   (send(sock, "PING\n", 5, 0) < 5)) {
2702
-		time(&last_failed_pings[server_index]);
2703
-		s->rc = 0;
2704
-	} else
2705
-		s->rc = 1;
2706
-
2707
-	if(s->rc == 0) {
2708
-#ifdef	MAXHOSTNAMELEN
2709
-		char hostname[MAXHOSTNAMELEN + 1];
2710
-
2711
-		cli_strtokbuf(serverHostNames, server_index, ":", hostname);
2712
-		if(strcmp(hostname, "127.0.0.1") == 0)
2713
-			gethostname(hostname, sizeof(hostname));
2714
-#else
2715
-		char *hostname = cli_strtok(serverHostNames, server_index, ":");
2716
-#endif
2717
-		perror(hostname);
2718
-		logg(_("^Check clamd server %s - it may be down\n"), hostname);
2719
-#ifndef	MAXHOSTNAMELEN
2720
-		free(hostname);
2721
-#endif
2722
-		broadcast(_("Check clamd server - it may be down\n"));
2723
-	}
2724
-
2725
-	return var;
2726
-}
2727
-#endif
2728
-
2729
-/*
2730
- * Sendmail wants to establish a connexion to us
2731
- * TODO: is it possible (desirable?) to determine if the remote machine has been
2732
- *	compromised?
2733
- */
2734
-static sfsistat
2735
-clamfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr)
2736
-{
2737
-#if	defined(HAVE_INET_NTOP) || defined(WITH_TCPWRAP)
2738
-	char ip[INET6_ADDRSTRLEN];
2739
-#endif
2740
-	int t;
2741
-	const char *remoteIP;
2742
-	struct privdata *privdata;
2743
-
2744
-	if(quitting)
2745
-		return cl_error;
2746
-
2747
-	if(ctx == NULL) {
2748
-		logg(_("!clamfi_connect: ctx is null"));
2749
-		return cl_error;
2750
-	}
2751
-	if(hostname == NULL) {
2752
-		logg(_("!clamfi_connect: hostname is null"));
2753
-		return cl_error;
2754
-	}
2755
-	if(smfi_getpriv(ctx) != NULL) {
2756
-		/* More than one connexion command, "can't happen" */
2757
-		cli_warnmsg("clamfi_connect: called more than once\n");
2758
-		clamfi_cleanup(ctx);
2759
-		return cl_error;
2760
-	}
2761
-#ifdef AF_INET6
2762
-	if((hostaddr == NULL) ||
2763
-	   ((hostaddr->sa_family == AF_INET) && (&(((struct sockaddr_in *)(hostaddr))->sin_addr) == NULL)) ||
2764
-	   ((hostaddr->sa_family == AF_INET6) && (&(((struct sockaddr_in6 *)(hostaddr))->sin6_addr) == NULL)))
2765
-#else
2766
-	if((hostaddr == NULL) || (&(((struct sockaddr_in *)(hostaddr))->sin_addr) == NULL))
2767
-#endif
2768
-		/*
2769
-		 * According to the sendmail API hostaddr is NULL if
2770
-		 * "the type is not supported in the current version". What
2771
-		 * the documentation doesn't say is the type of what.
2772
-		 *
2773
-		 * Possibly the input is not a TCP/IP socket e.g. stdin?
2774
-		 */
2775
-		remoteIP = "127.0.0.1";
2776
-	else {
2777
-#ifdef HAVE_INET_NTOP
2778
-		switch(hostaddr->sa_family) {
2779
-			case AF_INET:
2780
-				remoteIP = (const char *)inet_ntop(AF_INET, &((struct sockaddr_in *)(hostaddr))->sin_addr, ip, sizeof(ip));
2781
-				break;
2782
-#ifdef AF_INET6
2783
-			case AF_INET6:
2784
-				remoteIP = (const char *)inet_ntop(AF_INET6, &((struct sockaddr_in6 *)(hostaddr))->sin6_addr, ip, sizeof(ip));
2785
-				break;
2786
-#endif
2787
-			default:
2788
-				logg(_("clamfi_connect: Unexpected sa_family %d\n"),
2789
-					hostaddr->sa_family);
2790
-				return cl_error;
2791
-		}
2792
-
2793
-#else
2794
-		remoteIP = inet_ntoa(((struct sockaddr_in *)(hostaddr))->sin_addr);
2795
-#endif
2796
-
2797
-		if(remoteIP == NULL) {
2798
-			logg(_("clamfi_connect: remoteIP is null"));
2799
-			return cl_error;
2800
-		}
2801
-	}
2802
-
2803
-#ifdef	CL_DEBUG
2804
-	if(debug_level >= 4) {
2805
-		if(hostname[0] == '[')
2806
-			logg(_("clamfi_connect: connexion from %s"), remoteIP);
2807
-		else
2808
-			logg(_("clamfi_connect: connexion from %s [%s]"), hostname, remoteIP);
2809
-	}
2810
-#endif
2811
-
2812
-#ifdef	WITH_TCPWRAP
2813
-	/*
2814
-	 * Support /etc/hosts.allow and /etc/hosts.deny
2815
-	 */
2816
-	if(strncasecmp(port, "inet:", 5) == 0) {
2817
-		const char *hostmail;
2818
-		struct hostent hostent;
2819
-		char buf[BUFSIZ];
2820
-		static pthread_mutex_t wrap_mutex = PTHREAD_MUTEX_INITIALIZER;
2821
-
2822
-		/*
2823
-		 * Using TCP/IP for the sendmail->clamav-milter connexion
2824
-		 */
2825
-		if(((hostmail = smfi_getsymval(ctx, "{if_name}")) == NULL) &&
2826
-		   ((hostmail = smfi_getsymval(ctx, "j")) == NULL)) {
2827
-			logg(_("Can't get sendmail hostname"));
2828
-			return cl_error;
2829
-		}
2830
-		/*
2831
-		 * Use hostmail for error statements, not hostname, suggestion
2832
-		 * by Yar Tikhiy <yar@comp.chem.msu.su>
2833
-		 */
2834
-		if(r_gethostbyname(hostmail, &hostent, buf, sizeof(buf)) != 0) {
2835
-			logg(_("^Access Denied: Host Unknown (%s)"), hostmail);
2836
-			if(hostmail[0] == '[')
2837
-				/*
2838
-				 * A case could be made that it's not clamAV's
2839
-				 * job to check a system's DNS configuration
2840
-				 * and let this message through. However I am
2841
-				 * just too worried about any knock on effects
2842
-				 * to do that...
2843
-				 */
2844
-				cli_warnmsg(_("Can't find entry for IP address %s in DNS - check your DNS setting\n"),
2845
-					hostmail);
2846
-			return cl_error;
2847
-		}
2848
-
2849
-#ifdef HAVE_INET_NTOP
2850
-		if(hostent.h_addr &&
2851
-		   (inet_ntop(AF_INET, (struct in_addr *)hostent.h_addr, ip, sizeof(ip)) == NULL)) {
2852
-			perror(hostent.h_name);
2853
-			/*strcpy(ip, (char *)inet_ntoa(*(struct in_addr *)hostent.h_addr));*/
2854
-			logg(_("^Access Denied: Can't get IP address for (%s)"), hostent.h_name);
2855
-			return cl_error;
2856
-		}
2857
-#else
2858
-		strncpy(ip, (char *)inet_ntoa(*(struct in_addr *)hostent.h_addr), sizeof(ip));
2859
-		ip[sizeof(ip)-1]='\0';
2860
-#endif
2861
-
2862
-		/*
2863
-		 * Ask is this is a allowed name or IP number
2864
-		 *
2865
-		 * hosts_ctl uses strtok so it is not thread safe, see
2866
-		 * hosts_access(3)
2867
-		 */
2868
-		pthread_mutex_lock(&wrap_mutex);
2869
-		if(!hosts_ctl(progname, hostent.h_name, ip, STRING_UNKNOWN)) {
2870
-			pthread_mutex_unlock(&wrap_mutex);
2871
-			logg(_("^Access Denied for %s[%s]"), hostent.h_name, ip);
2872
-			return SMFIS_TEMPFAIL;
2873
-		}
2874
-		pthread_mutex_unlock(&wrap_mutex);
2875
-	}
2876
-#endif	/*WITH_TCPWRAP*/
2877
-
2878
-	if(fflag)
2879
-		/*
2880
-		 * Patch from "Richard G. Roberto" <rgr@dedlegend.com>
2881
-		 * Always scan whereever the message is from
2882
-		 */
2883
-		return SMFIS_CONTINUE;
2884
-
2885
-	if(!oflag)
2886
-		if(strcmp(remoteIP, "127.0.0.1") == 0) {
2887
-			logg(_("*clamfi_connect: not scanning outgoing messages"));
2888
-			return SMFIS_ACCEPT;
2889
-		}
2890
-
2891
-	if((!lflag) && isLocal(remoteIP)) {
2892
-#ifdef	CL_DEBUG
2893
-		logg(_("*clamfi_connect: not scanning local messages\n"));
2894
-#endif
2895
-		return SMFIS_ACCEPT;
2896
-	}
2897
-
2898
-#if	defined(HAVE_INET_NTOP) || defined(WITH_TCPWRAP)
2899
-	if(detect_forged_local_address && !isLocal(ip)) {
2900
-#else
2901
-	if(detect_forged_local_address && !isLocal(remoteIP)) {
2902
-#endif
2903
-		char me[MAXHOSTNAMELEN + 1];
2904
-
2905
-		if(gethostname(me, sizeof(me) - 1) < 0) {
2906
-			logg(_("^clamfi_connect: gethostname failed"));
2907
-			return SMFIS_CONTINUE;
2908
-		}
2909
-		logg("*me '%s' hostname '%s'\n", me, hostname);
2910
-		if(strcasecmp(hostname, me) == 0) {
2911
-			logg(_("Rejected connexion falsely claiming to be from here\n"));
2912
-			smfi_setreply(ctx, "550", "5.7.1", _("You have claimed to be me, but you are not"));
2913
-			broadcast(_("Forged local address detected"));
2914
-			return SMFIS_REJECT;
2915
-		}
2916
-	}
2917
-	if(isBlacklisted(remoteIP)) {
2918
-		char mess[128];
2919
-
2920
-		/*
2921
-		 * TODO: Option to greylist rather than blacklist, by sending
2922
-		 *	a try again code
2923
-		 * TODO: state *which* virus
2924
-		 * TODO: add optional list of IP addresses that won't be
2925
-		 *	blacklisted
2926
-		 */
2927
-		logg("Rejected connexion from blacklisted IP %s\n", remoteIP);
2928
-
2929
-		snprintf(mess, sizeof(mess), _("%s is blacklisted because your machine is infected with a virus"), remoteIP);
2930
-		smfi_setreply(ctx, "550", "5.7.1", mess);
2931
-		broadcast(_("Blacklisted IP detected"));
2932
-
2933
-		/*
2934
-		 * Keep them blacklisted
2935
-		 */
2936
-		pthread_mutex_lock(&blacklist_mutex);
2937
-		(void)tableUpdate(blacklist, remoteIP, (int)time((time_t *)0));
2938
-		pthread_mutex_unlock(&blacklist_mutex);
2939
-
2940
-		return SMFIS_REJECT;
2941
-	}
2942
-
2943
-	if(blacklist_time == 0)
2944
-		return SMFIS_CONTINUE;	/* allocate privdata per message */
2945
-
2946
-	pthread_mutex_lock(&blacklist_mutex);
2947
-	t = tableFind(blacklist, remoteIP);
2948
-	pthread_mutex_unlock(&blacklist_mutex);
2949
-
2950
-	if(t == 0)
2951
-		return SMFIS_CONTINUE;	/* this IP will never be blacklisted */
2952
-
2953
-	privdata = (struct privdata *)cli_calloc(1, sizeof(struct privdata));
2954
-	if(privdata == NULL)
2955
-		return cl_error;
2956
-
2957
-#ifdef	SESSION
2958
-	privdata->dataSocket = -1;
2959
-#else
2960
-	privdata->dataSocket = privdata->cmdSocket = -1;
2961
-#endif
2962
-
2963
-	if(smfi_setpriv(ctx, privdata) == MI_SUCCESS) {
2964
-		strcpy(privdata->ip, remoteIP);
2965
-		return SMFIS_CONTINUE;
2966
-	}
2967
-
2968
-	free(privdata);
2969
-
2970
-	return cl_error;
2971
-}
2972
-
2973
-/*
2974
- * Since sendmail requires that MAIL FROM is called before RCPT TO, it is
2975
- *	safe to assume that this routine is called first, so the n_children
2976
- *	handler is put here
2977
- */
2978
-static sfsistat
2979
-clamfi_envfrom(SMFICTX *ctx, char **argv)
2980
-{
2981
-	struct privdata *privdata;
2982
-	const char *mailaddr = argv[0];
2983
-
2984
-	logg("*clamfi_envfrom: %s\n", argv[0]);
2985
-
2986
-	if(isWhitelisted(argv[0], 0)) {
2987
-		logg(_("*clamfi_envfrom: ignoring whitelisted message"));
2988
-		return SMFIS_ACCEPT;
2989
-	}
2990
-
2991
-	if(strcmp(argv[0], "<>") == 0) {
2992
-		mailaddr = smfi_getsymval(ctx, "{mail_addr}");
2993
-		if(mailaddr == NULL)
2994
-			mailaddr = smfi_getsymval(ctx, "_");
2995
-
2996
-		if(mailaddr && *mailaddr)
2997
-			cli_dbgmsg("Message from \"%s\" has no from field\n", mailaddr);
2998
-		else {
2999
-#if	0
3000
-			if(use_syslog)
3001
-				syslog(LOG_NOTICE, _("Rejected email with empty from field"));
3002
-			smfi_setreply(ctx, "554", "5.7.1", _("You have not said who the email is from"));
3003
-			broadcast(_("Reject email with empty from field"));
3004
-			clamfi_cleanup(ctx);
3005
-			return SMFIS_REJECT;
3006
-#endif
3007
-			mailaddr = "<>";
3008
-		}
3009
-	}
3010
-	privdata = smfi_getpriv(ctx);
3011
-
3012
-	if(privdata == NULL) {
3013
-		privdata = (struct privdata *)cli_calloc(1, sizeof(struct privdata));
3014
-		if(privdata == NULL)
3015
-			return cl_error;
3016
-		if(smfi_setpriv(ctx, privdata) != MI_SUCCESS) {
3017
-			free(privdata);
3018
-			return cl_error;
3019
-		}
3020
-		if(!increment_connexions()) {
3021
-			smfi_setreply(ctx, "451", "4.3.2", _("AV system temporarily overloaded - please try later"));
3022
-			free(privdata);
3023
-			smfi_setpriv(ctx, NULL);
3024
-			return SMFIS_TEMPFAIL;
3025
-		}
3026
-	} else {
3027
-		/* More than one message on this connexion */
3028
-		char ip[INET6_ADDRSTRLEN];
3029
-
3030
-		strcpy(ip, privdata->ip);
3031
-		if(isBlacklisted(ip)) {
3032
-			char mess[80 + INET6_ADDRSTRLEN];
3033
-
3034
-			logg("Rejected email from blacklisted IP %s\n", ip);
3035
-
3036
-			/*
3037
-			 * TODO: Option to greylist rather than blacklist, by
3038
-			 *	sending	a try again code
3039
-			 * TODO: state *which* virus
3040
-			 */
3041
-			sprintf(mess, "Your IP (%s) is blacklisted because your machine is infected with a virus", ip);
3042
-			smfi_setreply(ctx, "550", "5.7.1", mess);
3043
-			broadcast(_("Blacklisted IP detected"));
3044
-
3045
-			/*
3046
-			 * Keep them blacklisted
3047
-			 */
3048
-			pthread_mutex_lock(&blacklist_mutex);
3049
-			(void)tableUpdate(blacklist, ip, (int)time((time_t *)0));
3050
-			pthread_mutex_unlock(&blacklist_mutex);
3051
-
3052
-			return SMFIS_REJECT;
3053
-		}
3054
-		clamfi_free(privdata, 1);
3055
-		strcpy(privdata->ip, ip);
3056
-	}
3057
-
3058
-#ifdef	SESSION
3059
-	privdata->dataSocket = -1;
3060
-#else
3061
-	privdata->dataSocket = privdata->cmdSocket = -1;
3062
-#endif
3063
-
3064
-	/*
3065
-	 * Rejection is via 550 until DATA is received. We know that
3066
-	 * DATA has been sent when either we get a header or the end of
3067
-	 * header statement
3068
-	 */
3069
-	privdata->rejectCode = "550";
3070
-
3071
-	privdata->from = cli_strdup(mailaddr);
3072
-
3073
-	if(hflag) {
3074
-		privdata->headers = header_list_new();
3075
-
3076
-		if(privdata->headers == NULL) {
3077
-			clamfi_free(privdata, 1);
3078
-			return cl_error;
3079
-		}
3080
-	}
3081
-
3082
-	return SMFIS_CONTINUE;
3083
-}
3084
-
3085
-#ifdef	CL_DEBUG
3086
-static sfsistat
3087
-clamfi_helo(SMFICTX *ctx, char *helostring)
3088
-{
3089
-	logg("HELO '%s'\n", helostring);
3090
-
3091
-	return SMFIS_CONTINUE;
3092
-}
3093
-#endif
3094
-
3095
-static sfsistat
3096
-clamfi_envrcpt(SMFICTX *ctx, char **argv)
3097
-{
3098
-	struct privdata *privdata = (struct privdata *)smfi_getpriv(ctx);
3099
-	const char *to, *ptr;
3100
-
3101
-	logg("*clamfi_envrcpt: %s\n", argv[0]);
3102
-
3103
-	if(privdata == NULL)	/* sanity check */
3104
-		return cl_error;
3105
-
3106
-	if(privdata->to == NULL) {
3107
-		privdata->to = cli_malloc(sizeof(char *) * 2);
3108
-
3109
-		assert(privdata->numTo == 0);
3110
-	} else
3111
-		privdata->to = cli_realloc(privdata->to, sizeof(char *) * (privdata->numTo + 2));
3112
-
3113
-	if(privdata->to == NULL)
3114
-		return cl_error;
3115
-
3116
-	to = smfi_getsymval(ctx, "{rcpt_addr}");
3117
-	if(to == NULL)
3118
-		to = argv[0];
3119
-
3120
-	for(ptr = to; !dont_sanitise && *ptr; ptr++)
3121
-		if(strchr("|;", *ptr) != NULL) {
3122
-			smfi_setreply(ctx, "554", "5.7.1", _("Suspicious recipient address blocked"));
3123
-			logg("^Suspicious recipient address blocked: '%s'\n", to);
3124
-			privdata->to[privdata->numTo] = NULL;
3125
-			if(blacklist_time && privdata->ip[0]) {
3126
-				logg(_("Will blacklist %s for %d seconds because of cracking attempt\n"),
3127
-					privdata->ip, blacklist_time);
3128
-				pthread_mutex_lock(&blacklist_mutex);
3129
-				(void)tableUpdate(blacklist, privdata->ip,
3130
-					(int)time((time_t *)0));
3131
-				pthread_mutex_unlock(&blacklist_mutex);
3132
-			}
3133
-			/*
3134
-			 * REJECT rejects this recipient, not the entire email
3135
-			 */
3136
-			return SMFIS_REJECT;
3137
-		}
3138
-
3139
-	privdata->to[privdata->numTo] = cli_strdup(to);
3140
-	privdata->to[++privdata->numTo] = NULL;
3141
-
3142
-	return SMFIS_CONTINUE;
3143
-}
3144
-
3145
-static sfsistat
3146
-clamfi_header(SMFICTX *ctx, char *headerf, char *headerv)
3147
-{
3148
-	struct privdata *privdata = (struct privdata *)smfi_getpriv(ctx);
3149
-
3150
-#ifdef	CL_DEBUG
3151
-	if(debug_level >= 9)
3152
-		logg("*clamfi_header: %s: %s\n", headerf, headerv);
3153
-	else
3154
-		logg("*clamfi_header: %s\n", headerf);
3155
-#else
3156
-	logg("*clamfi_header: %s\n", headerf);
3157
-#endif
3158
-
3159
-	/*
3160
-	 * The DATA instruction from SMTP (RFC2821) must have been sent
3161
-	 */
3162
-	privdata->rejectCode = "554";
3163
-
3164
-	if(hflag)
3165
-		header_list_add(privdata->headers, headerf, headerv);
3166
-	else if((strcasecmp(headerf, "Received") == 0) &&
3167
-		(strncasecmp(headerv, "from ", 5) == 0) &&
3168
-		(strstr(headerv, "localhost") != 0)) {
3169
-		if(privdata->received)
3170
-			free(privdata->received);
3171
-		privdata->received = cli_strdup(headerv);
3172
-	}
3173
-
3174
-	if((strcasecmp(headerf, "Message-ID") == 0) &&
3175
-	   (strncasecmp(headerv, "<MDAEMON", 8) == 0))
3176
-		privdata->discard = 1;
3177
-	else if((strcasecmp(headerf, "Subject") == 0) && headerv) {
3178
-		if(privdata->subject)
3179
-			free(privdata->subject);
3180
-		if(headerv)
3181
-			privdata->subject = cli_strdup(headerv);
3182
-	} else if(strcasecmp(headerf, "X-Virus-Status") == 0)
3183
-		privdata->statusCount++;
3184
-	else if((strcasecmp(headerf, "Sender") == 0) && headerv) {
3185
-		if(privdata->sender)
3186
-			free(privdata->sender);
3187
-		privdata->sender = cli_strdup(headerv);
3188
-	}
3189
-#ifdef	HAVE_RESOLV_H
3190
-	else if((strcasecmp(headerf, "From") == 0) && headerv) {
3191
-		/*
3192
-		 * SPF check against the from header, since the SMTP header
3193
-		 * may be valid. This is not what the SPF spec says, but I
3194
-		 * have seen SPF matches on what are clearly phishes, so by
3195
-		 * checking against the from: header we're less likely to
3196
-		 * FP a real phish
3197
-		 */
3198
-		if(privdata->from)
3199
-			free(privdata->from);
3200
-		privdata->from = cli_strdup(headerv);
3201
-	}
3202
-#endif
3203
-
3204
-	if(!useful_header(headerf)) {
3205
-		logg("*Discarded the header\n");
3206
-		return SMFIS_CONTINUE;
3207
-	}
3208
-
3209
-	if(privdata->dataSocket == -1)
3210
-		/*
3211
-		 * First header - make connexion with clamd
3212
-		 */
3213
-		if(!connect2clamd(privdata)) {
3214
-			clamfi_cleanup(ctx);
3215
-			return cl_error;
3216
-		}
3217
-
3218
-	if(clamfi_send(privdata, 0, "%s: %s\n", headerf, headerv) <= 0) {
3219
-		clamfi_cleanup(ctx);
3220
-		return cl_error;
3221
-	}
3222
-
3223
-	return SMFIS_CONTINUE;
3224
-}
3225
-
3226
-/*
3227
- * At this point DATA will have been received, so we really ought to
3228
- * send 554 back not 550
3229
- */
3230
-static sfsistat
3231
-clamfi_eoh(SMFICTX *ctx)
3232
-{
3233
-	struct privdata *privdata = (struct privdata *)smfi_getpriv(ctx);
3234
-	char **to;
3235
-
3236
-	logg(_("*clamfi_eoh\n"));
3237
-
3238
-	/*
3239
-	 * The DATA instruction from SMTP (RFC2821) must have been sent
3240
-	 */
3241
-	privdata->rejectCode = "554";
3242
-
3243
-	if(privdata->dataSocket == -1)
3244
-		/*
3245
-		 * No headers - make connexion with clamd
3246
-		 */
3247
-		if(!connect2clamd(privdata)) {
3248
-			clamfi_cleanup(ctx);
3249
-			return cl_error;
3250
-		}
3251
-
3252
-#if	0
3253
-	/* Mailing lists often say our own posts are from us */
3254
-	if(detect_forged_local_address && privdata->from &&
3255
-	   (!privdata->sender) && !isWhitelisted(privdata->from, 1)) {
3256
-		char me[MAXHOSTNAMELEN + 1];
3257
-		const char *ptr;
3258
-
3259
-		if(gethostname(me, sizeof(me) - 1) < 0) {
3260
-			if(use_syslog)
3261
-				syslog(LOG_WARNING, _("clamfi_eoh: gethostname failed"));
3262
-			return SMFIS_CONTINUE;
3263
-		}
3264
-		ptr = strstr(privdata->from, me);
3265
-		if(ptr && (ptr != privdata->from) && (*--ptr == '@')) {
3266
-			if(use_syslog)
3267
-				syslog(LOG_NOTICE, _("Rejected email falsely claiming to be from %s"), privdata->from);
3268
-			smfi_setreply(ctx, "554", "5.7.1", _("You have claimed to be from me, but you are not"));
3269
-			broadcast(_("Forged local address detected"));
3270
-			clamfi_cleanup(ctx);
3271
-			return SMFIS_REJECT;
3272
-		}
3273
-	}
3274
-#endif
3275
-
3276
-	if(clamfi_send(privdata, 1, "\n") != 1) {
3277
-		clamfi_cleanup(ctx);
3278
-		return cl_error;
3279
-	}
3280
-
3281
-	if(black_hole_mode) {
3282
-		sfsistat rc = black_hole(privdata);
3283
-
3284
-		if(rc != SMFIS_CONTINUE) {
3285
-			clamfi_cleanup(ctx);
3286
-			return rc;
3287
-		}
3288
-	}
3289
-
3290
-	/*
3291
-	 * See if the e-mail is only going to members of the list
3292
-	 * of users we don't scan for. If it is, don't scan, otherwise
3293
-	 * scan
3294
-	 *
3295
-	 * scan = false
3296
-	 * FORALL recipients
3297
-	 *	IF receipient NOT MEMBER OF white address list
3298
-	 *	THEN
3299
-	 *		scan = true
3300
-	 *	FI
3301
-	 * ENDFOR
3302
-	 */
3303
-	for(to = privdata->to; *to; to++)
3304
-		if(!isWhitelisted(*to, 1))
3305
-			/*
3306
-			 * This recipient is not on the whitelist,
3307
-			 * no need to check any further
3308
-			 */
3309
-			return SMFIS_CONTINUE;
3310
-
3311
-	/*
3312
-	 * Didn't find a recipient who is not on the white list, so all
3313
-	 * must be on the white list, so just accept the e-mail
3314
-	 */
3315
-	logg(_("*clamfi_enveoh: ignoring whitelisted message"));
3316
-	clamfi_cleanup(ctx);
3317
-
3318
-	return SMFIS_ACCEPT;
3319
-}
3320
-
3321
-static sfsistat
3322
-clamfi_body(SMFICTX *ctx, u_char *bodyp, size_t len)
3323
-{
3324
-	struct privdata *privdata = (struct privdata *)smfi_getpriv(ctx);
3325
-	int nbytes;
3326
-
3327
-	logg(_("*clamfi_envbody: %lu bytes"), (unsigned long)len);
3328
-
3329
-	if(len == 0)	/* unlikely */
3330
-		return SMFIS_CONTINUE;
3331
-
3332
-	if(privdata == NULL)	/* sanity check */
3333
-		return cl_error;
3334
-
3335
-	/*
3336
-	 * TODO:
3337
-	 *	If not in external mode, call cli_scanbuff here, at least for
3338
-	 * the first block
3339
-	 */
3340
-	/*
3341
-	 * Lines starting with From are changed to >From, to
3342
-	 *	avoid FP matches in the scanning code, which will speed it up
3343
-	 */
3344
-	if((len >= 6) && cli_memstr((char *)bodyp, len, "\nFrom ", 6)) {
3345
-		const char *ptr = (const char *)bodyp;
3346
-		int left = len;
3347
-
3348
-		nbytes = 0;
3349
-
3350
-		/*
3351
-		 * FIXME: sending one byte at a time down a socket is
3352
-		 *	inefficient
3353
-		 */
3354
-		do {
3355
-			if(*ptr == '\n') {
3356
-				/*
3357
-				 * FIXME: doesn't work if the \nFrom straddles
3358
-				 * multiple calls to clamfi_body
3359
-				 */
3360
-				if(strncmp(ptr, "\nFrom ", 6) == 0) {
3361
-					nbytes += clamfi_send(privdata, 7, "\n>From ");
3362
-					ptr += 6;
3363
-					left -= 6;
3364
-				} else {
3365
-					nbytes += clamfi_send(privdata, 1, "\n");
3366
-					ptr++;
3367
-					left--;
3368
-				}
3369
-			} else {
3370
-				nbytes += clamfi_send(privdata, 1, ptr++);
3371
-				left--;
3372
-			}
3373
-			if(left < 6 && left > 0) {
3374
-				nbytes += clamfi_send(privdata, left, ptr);
3375
-				break;
3376
-			}
3377
-		} while(left > 0);
3378
-	} else
3379
-		nbytes = clamfi_send(privdata, len, (char *)bodyp);
3380
-
3381
-	if(streamMaxLength > 0L) {
3382
-		if(privdata->numBytes > streamMaxLength) {
3383
-			const char *sendmailId = smfi_getsymval(ctx, "i");
3384
-
3385
-			if(sendmailId == NULL)
3386
-				sendmailId = "Unknown";
3387
-			logg(_("%s: Message more than StreamMaxLength (%ld) bytes - not scanned\n"),
3388
-				sendmailId, streamMaxLength);
3389
-			if(!nflag)
3390
-				smfi_addheader(ctx, "X-Virus-Status", _("Not Scanned - StreamMaxLength exceeded"));
3391
-
3392
-			return SMFIS_ACCEPT;	/* clamfi_close will be called */
3393
-		}
3394
-	}
3395
-	if(nbytes < (int)len) {
3396
-		clamfi_cleanup(ctx);	/* not needed, but just to be safe */
3397
-		return cl_error;
3398
-	}
3399
-	if(Sflag) {
3400
-		if(privdata->body) {
3401
-			assert(privdata->bodyLen > 0);
3402
-			privdata->body = cli_realloc(privdata->body, privdata->bodyLen + len);
3403
-			memcpy(&privdata->body[privdata->bodyLen], bodyp, len);
3404
-			privdata->bodyLen += len;
3405
-		} else {
3406
-			assert(privdata->bodyLen == 0);
3407
-			privdata->body = cli_malloc(len);
3408
-			memcpy(privdata->body, bodyp, len);
3409
-			privdata->bodyLen = len;
3410
-		}
3411
-	}
3412
-	return SMFIS_CONTINUE;
3413
-}
3414
-
3415
-static sfsistat
3416
-clamfi_eom(SMFICTX *ctx)
3417
-{
3418
-	int rc = SMFIS_CONTINUE;
3419
-	char *ptr;
3420
-	const char *sendmailId;
3421
-	struct privdata *privdata = (struct privdata *)smfi_getpriv(ctx);
3422
-	char mess[128];
3423
-#ifdef	SESSION
3424
-	struct session *session;
3425
-#endif
3426
-
3427
-	logg("*clamfi_eom\n");
3428
-
3429
-#ifdef	CL_DEBUG
3430
-	assert(privdata != NULL);
3431
-#ifndef	SESSION
3432
-	assert((privdata->cmdSocket >= 0) || (privdata->filename != NULL));
3433
-	assert(!((privdata->cmdSocket >= 0) && (privdata->filename != NULL)));
3434
-#endif
3435
-#endif
3436
-
3437
-	if(external) {
3438
-		shutdown(privdata->dataSocket, SHUT_WR);	/* bug 487 */
3439
-		close(privdata->dataSocket);
3440
-		privdata->dataSocket = -1;
3441
-	}
3442
-
3443
-	if(!nflag) {
3444
-		/*
3445
-		 * remove any existing claims that it's virus free so that
3446
-		 * downstream checkers aren't fooled by a carefully crafted
3447
-		 * virus.
3448
-		 */
3449
-		int i;
3450
-
3451
-		for(i = privdata->statusCount; i > 0; --i)
3452
-			if(smfi_chgheader(ctx, "X-Virus-Status", i, NULL) == MI_FAILURE)
3453
-				logg(_("^Failed to delete X-Virus-Status header %d\n"), i);
3454
-	}
3455
-
3456
-	if(!external) {
3457
-		const char *virname;
3458
-		int ret;
3459
-		struct  cl_engine *cur_engine;
3460
-
3461
-		pthread_mutex_lock(&engine_mutex);
3462
-		ret = cl_engine_addref(engine);
3463
-		cur_engine = engine; /* avoid races */
3464
-		pthread_mutex_unlock(&engine_mutex);
3465
-		if(ret != CL_SUCCESS) {
3466
-			logg("!cl_engine_addref failed\n");
3467
-			clamfi_cleanup(ctx);
3468
-			return cl_error;
3469
-		}
3470
-		switch(cl_scanfile(privdata->filename, &virname, NULL, cur_engine, options)) {
3471
-			case CL_CLEAN:
3472
-				if(logok)
3473
-					logg("#%s: OK\n", privdata->filename);
3474
-				strcpy(mess, "OK");
3475
-				break;
3476
-			case CL_VIRUS:
3477
-				snprintf(mess, sizeof(mess), "%s: %s FOUND", privdata->filename, virname);
3478
-				logg("#%s\n", mess);
3479
-				break;
3480
-			default:
3481
-				snprintf(mess, sizeof(mess), "%s: ERROR", privdata->filename);
3482
-				logg("!%s\n", mess);
3483
-				break;
3484
-		}
3485
-		cl_engine_free(cur_engine); /* drop reference or free */
3486
-
3487
-#ifdef	SESSION
3488
-		session = NULL;
3489
-#endif
3490
-	} else if(privdata->filename) {
3491
-		char cmdbuf[1024];
3492
-		/*
3493
-		 * Create socket to talk to clamd.
3494
-		 */
3495
-#ifndef	SESSION
3496
-		struct sockaddr_un server;
3497
-#endif
3498
-		long nbytes;
3499
-
3500
-		snprintf(cmdbuf, sizeof(cmdbuf) - 1, "SCAN %s", privdata->filename);
3501
-		cli_dbgmsg("clamfi_eom: SCAN %s\n", privdata->filename);
3502
-
3503
-		nbytes = (int)strlen(cmdbuf);
3504
-
3505
-#ifdef	SESSION
3506
-		session = sessions;
3507
-		if(send(session->sock, cmdbuf, nbytes, 0) < nbytes) {
3508
-			perror("send");
3509
-			clamfi_cleanup(ctx);
3510
-			logg(_("failed to send SCAN %s command to clamd\n"), privdata->filename);
3511
-			return cl_error;
3512
-		}
3513
-#else
3514
-		if((privdata->cmdSocket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
3515
-			perror("socket");
3516
-			clamfi_cleanup(ctx);
3517
-			return cl_error;
3518
-		}
3519
-		memset((char *)&server, 0, sizeof(struct sockaddr_un));
3520
-		server.sun_family = AF_UNIX;
3521
-		strncpy(server.sun_path, localSocket, sizeof(server.sun_path));
3522
-		server.sun_path[sizeof(server.sun_path)-1]='\0';
3523
-
3524
-		if(connect(privdata->cmdSocket, (struct sockaddr *)&server, sizeof(struct sockaddr_un)) < 0) {
3525
-			perror(localSocket);
3526
-			clamfi_cleanup(ctx);
3527
-			return cl_error;
3528
-		}
3529
-		if(send(privdata->cmdSocket, cmdbuf, nbytes, 0) < nbytes) {
3530
-			perror("send");
3531
-			clamfi_cleanup(ctx);
3532
-			logg(_("failed to send SCAN command to clamd\n"));
3533
-			return cl_error;
3534
-		}
3535
-
3536
-		shutdown(privdata->cmdSocket, SHUT_WR);
3537
-#endif
3538
-	}
3539
-#ifdef	SESSION
3540
-	else
3541
-		session = &sessions[privdata->serverNumber];
3542
-#endif
3543
-
3544
-	sendmailId = smfi_getsymval(ctx, "i");
3545
-	if(sendmailId == NULL)
3546
-		sendmailId = "Unknown";
3547
-
3548
-	if(external) {
3549
-		int nbytes;
3550
-#ifdef	SESSION
3551
-#ifdef	CL_DEBUG
3552
-		if(debug_level >= 4)
3553
-			cli_dbgmsg(_("Waiting to read status from fd %d\n"),
3554
-				session->sock);
3555
-#endif
3556
-		nbytes = clamd_recv(session->sock, mess, sizeof(mess) - 1);
3557
-#else
3558
-		nbytes = clamd_recv(privdata->cmdSocket, mess, sizeof(mess) - 1);
3559
-#endif
3560
-		if(nbytes > 0) {
3561
-			mess[nbytes] = '\0';
3562
-			if((ptr = strchr(mess, '\n')) != NULL)
3563
-				*ptr = '\0';
3564
-
3565
-			logg(_("*clamfi_eom: read %s\n"), mess);
3566
-		} else {
3567
-#ifdef	MAXHOSTNAMELEN
3568
-			char hostname[MAXHOSTNAMELEN + 1];
3569
-
3570
-			cli_strtokbuf(serverHostNames, privdata->serverNumber, ":", hostname);
3571
-			if(strcmp(hostname, "127.0.0.1") == 0)
3572
-				gethostname(hostname, sizeof(hostname));
3573
-#else
3574
-			char *hostname = cli_strtok(serverHostNames, privdata->serverNumber, ":");
3575
-#endif
3576
-			if(privdata->subject)
3577
-				logg(_("^%s: clamfi_eom: read nothing from clamd on %s, from %s (%s)\n"),
3578
-					sendmailId, hostname, privdata->from,
3579
-					privdata->subject);
3580
-			else
3581
-				logg(_("^%s: clamfi_eom: read nothing from clamd on %s, from %s\n"),
3582
-					sendmailId, hostname, privdata->from);
3583
-
3584
-			if((!nflag) && (cl_error == SMFIS_ACCEPT))
3585
-				smfi_addheader(ctx, "X-Virus-Status", _("Not Scanned - Read timeout exceeded"));
3586
-#ifndef	MAXHOSTNAMELEN
3587
-			free(hostname);
3588
-#endif
3589
-
3590
-#ifdef	CL_DEBUG
3591
-			/*
3592
-			 * Save the file which caused the timeout, for
3593
-			 * debugging
3594
-			 */
3595
-			if(quarantine_dir) {
3596
-				logg(_("Quarantining failed email\n"));
3597
-				qfile(privdata, sendmailId, "scanning-timeout");
3598
-			}
3599
-#endif
3600
-
3601
-			/*
3602
-			 * TODO: if more than one host has been specified, try
3603
-			 * another one - setting cl_error to SMFIS_TEMPFAIL
3604
-			 * helps by forcing a retry
3605
-			 */
3606
-			clamfi_cleanup(ctx);
3607
-
3608
-#ifdef	SESSION
3609
-			pthread_mutex_lock(&sstatus_mutex);
3610
-			session->status = CMDSOCKET_DOWN;
3611
-			pthread_mutex_unlock(&sstatus_mutex);
3612
-#endif
3613
-			return cl_error;
3614
-		}
3615
-
3616
-#ifdef	SESSION
3617
-		pthread_mutex_lock(&sstatus_mutex);
3618
-		if(session->status == CMDSOCKET_INUSE)
3619
-			session->status = CMDSOCKET_FREE;
3620
-		pthread_mutex_unlock(&sstatus_mutex);
3621
-#else
3622
-		close(privdata->cmdSocket);
3623
-		privdata->cmdSocket = -1;
3624
-#endif
3625
-	}
3626
-
3627
-	if(!nflag) {
3628
-		char buf[1024];
3629
-
3630
-		/*
3631
-		 * Include the hostname where the scan took place
3632
-		 */
3633
-		if(localSocket || !external) {
3634
-#ifdef	MAXHOSTNAMELEN
3635
-			char hostname[MAXHOSTNAMELEN + 1];
3636
-#else
3637
-			char hostname[65];
3638
-#endif
3639
-
3640
-			if(gethostname(hostname, sizeof(hostname)) < 0) {
3641
-				const char *j = smfi_getsymval(ctx, "{j}");
3642
-
3643
-				if(j)
3644
-					strncpy(hostname, j, sizeof(hostname) - 1);
3645
-				else
3646
-					strcpy(hostname, _("Error determining host"));
3647
-				hostname[sizeof(hostname)-1]='\0';
3648
-			} else if(strchr(hostname, '.') == NULL) {
3649
-				/*
3650
-				 * Determine fully qualified name
3651
-				 */
3652
-				struct hostent hostent;
3653
-
3654
-				if((r_gethostbyname(hostname, &hostent, buf, sizeof(buf)) == 0) && hostent.h_name) {
3655
-					strncpy(hostname, hostent.h_name, sizeof(hostname));
3656
-					hostname[sizeof(hostname)-1]='\0';
3657
-				}
3658
-			}
3659
-
3660
-#ifdef	SESSION
3661
-			pthread_mutex_lock(&version_mutex);
3662
-			snprintf(buf, sizeof(buf) - 1, "%s on %s",
3663
-				clamav_versions[privdata->serverNumber], hostname);
3664
-			pthread_mutex_unlock(&version_mutex);
3665
-#else
3666
-			snprintf(buf, sizeof(buf) - 1, "%s on %s",
3667
-				clamav_version, hostname);
3668
-#endif
3669
-		} else {
3670
-#ifdef	MAXHOSTNAMELEN
3671
-			char hostname[MAXHOSTNAMELEN + 1];
3672
-
3673
-			if(cli_strtokbuf(serverHostNames, privdata->serverNumber, ":", hostname)) {
3674
-				if(strcmp(hostname, "127.0.0.1") == 0)
3675
-					gethostname(hostname, sizeof(hostname));
3676
-#else
3677
-			char *hostname = cli_strtok(serverHostNames, privdata->serverNumber, ":");
3678
-			if(hostname) {
3679
-#endif
96
+    }
97
+	
98
+    if(opt->filename)
99
+	mprintf("^Ignoring option %s\n", opt->filename);
100
+
101
+    if(opt_check(opt, "version")) {
102
+	printf("clamav-milter %s\n", get_version());
103
+	opt_free(opt);
104
+	return 0;
105
+    }
3680 106
 
3681
-#ifdef	SESSION
3682
-				pthread_mutex_lock(&version_mutex);
3683
-				snprintf(buf, sizeof(buf) - 1, "%s on %s",
3684
-					clamav_versions[privdata->serverNumber], hostname);
3685
-				pthread_mutex_unlock(&version_mutex);
3686
-#else
3687
-				snprintf(buf, sizeof(buf) - 1, "%s on %s",
3688
-					clamav_version, hostname);
3689
-#endif
3690
-#ifndef	MAXHOSTNAMELEN
3691
-				free(hostname);
3692
-#endif
3693
-			} else
3694
-				/* sanity check failed - should issue warning */
3695
-				strcpy(buf, _("Error determining host"));
3696
-		}
3697
-		smfi_addheader(ctx, "X-Virus-Scanned", buf);
3698
-	}
107
+    if(opt_check(opt, "config-file"))
108
+	my_conf = opt_arg(opt, "config-file");
3699 109
 
3700
-	/*
3701
-	 * TODO: it would be useful to add a header if mbox.c/FOLLOWURLS was
3702
-	 * exceeded
3703
-	 */
3704
-#ifdef	HAVE_RESOLV_H
3705
-	if((strstr(mess, "FOUND") != NULL) && (strstr(mess, "Phishing") != NULL)) {
3706
-		table_t *prevhosts = tableCreate();
110
+    if((copt = getcfg(my_conf, 1, OPT_MILTER)) == NULL) {
111
+	printf("%s: cannot parse config file %s\n", argv[0], my_conf);
112
+	opt_free(opt);
113
+	return 1;
114
+    }
3707 115
 
3708
-		if(spf(privdata, prevhosts)) {
3709
-			logg(_("%s: Ignoring %s false positive from %s received from %s\n"),
3710
-				sendmailId, mess, privdata->from, privdata->ip);
3711
-			strcpy(mess, "OK");
3712
-			/*
3713
-			 * Report false positive to ClamAV, works best when
3714
-			 * clamav-milter has had to create a local copy of
3715
-			 * the email, e.g. when --quarantine-dir is on
3716
-			 */
3717
-			if(report_fps &&
3718
-			   (smfi_addrcpt(ctx, report_fps) == MI_FAILURE)) {
3719
-				if(privdata->filename) {
3720
-					char cmd[1024];
116
+    opt_free(opt);
3721 117
 
3722
-					snprintf(cmd, sizeof(cmd) - 1,
3723
-						"mail -s \"False Positive: %s\" %s < %s",
3724
-						mess, report_fps,
3725
-						privdata->filename);
3726
-					if(system(cmd) == 0)
3727
-						logg(_("#Reported phishing false positive to %s"), report_fps);
3728
-					else
3729
-						logg(_("^Couldn't report false positive to %s\n"), report_fps);
3730
-				} else
3731
-					/*
3732
-					 * Most likely this is because we're
3733
-					 * attempting to add a recipient on
3734
-					 * another host
3735
-					 */
3736
-					logg(_("^Can't set phish FP header\n"));
3737
-			}
3738
-		}
3739
-		tableDestroy(prevhosts);
118
+    if(geteuid() == 0 && (cpt = cfgopt(copt, "User"))->enabled) {
119
+        struct passwd *user = NULL;
120
+	if((user = getpwnam(cpt->strarg)) == NULL) {
121
+	    fprintf(stderr, "ERROR: Can't get information about user %s.\n", cpt->strarg);
122
+	    freecfg(copt);
123
+	    return 1;
3740 124
 	}
3741
-#endif
3742
-	if(strstr(mess, "ERROR") != NULL) {
3743
-		if(strstr(mess, "Size limit reached") != NULL) {
3744
-			/*
3745
-			 * Clamd has stopped on StreamMaxLength before us
3746
-			 */
3747
-			logg(_("%s: Message more than StreamMaxLength (%ld) bytes - not scanned"),
3748
-				sendmailId, streamMaxLength);
3749
-			if(!nflag)
3750
-				smfi_addheader(ctx, "X-Virus-Status", _("Not Scanned - StreamMaxLength exceeded"));
3751
-			clamfi_cleanup(ctx);	/* not needed, but just to be safe */
3752
-			return SMFIS_ACCEPT;
3753
-		}
3754
-		if(!nflag)
3755
-			smfi_addheader(ctx, "X-Virus-Status", _("Not Scanned"));
3756
-
3757
-		logg("!%s: %s\n", sendmailId, mess);
3758
-		rc = cl_error;
3759
-	} else if((ptr = strstr(mess, "FOUND")) != NULL) {
3760
-		/*
3761
-		 * FIXME: This will give false positives if the
3762
-		 *	word "FOUND" is in the email, e.g. the
3763
-		 *	quarantine directory is /tmp/VIRUSES-FOUND
3764
-		 */
3765
-		int i;
3766
-		char **to, *virusname, *err;
3767
-		char reject[1024];
3768
-
3769
-		/*
3770
-		 * Remove the "FOUND" word, and the space before it
3771
-		 */
3772
-		*--ptr = '\0';
3773
-
3774
-		/* skip over 'stream/filename: ' at the start */
3775
-		if((virusname = strchr(mess, ':')) != NULL)
3776
-			virusname = &virusname[2];
3777
-		else
3778
-			virusname = mess;
3779
-
3780
-		if(!nflag) {
3781
-			char buf[129];
3782 125
 
3783
-			snprintf(buf, sizeof(buf) - 1, "%s %s", _("Infected with"), virusname);
3784
-			smfi_addheader(ctx, "X-Virus-Status", buf);
3785
-		}
3786
-
3787
-		if(quarantine_dir)
3788
-			qfile(privdata, sendmailId, virusname);
3789
-
3790
-		/*
3791
-		 * Setup err as a list of recipients
3792
-		 */
3793
-		err = (char *)cli_malloc(1024);
3794
-
3795
-		if(err == NULL) {
3796
-			clamfi_cleanup(ctx);
3797
-			return cl_error;
3798
-		}
3799
-
3800
-		/*
3801
-		 * Use snprintf rather than printf since we don't know
3802
-		 * the length of privdata->from and may get a buffer
3803
-		 * overrun
3804
-		 */
3805
-		snprintf(err, 1023, _("Intercepted virus from %s to"),
3806
-			privdata->from);
3807
-
3808
-		ptr = strchr(err, '\0');
3809
-
3810
-		i = 1024;
3811
-
3812
-		for(to = privdata->to; *to; to++) {
3813
-			/*
3814
-			 * Re-alloc if we are about run out of buffer
3815
-			 * space
3816
-			 *
3817
-			 * TODO: Only append *to if it's a valid, local
3818
-			 *	email address
3819
-			 */
3820
-			if(&ptr[strlen(*to) + 2] >= &err[i]) {
3821
-				i += 1024;
3822
-				err = cli_realloc(err, i);
3823
-				if(err == NULL) {
3824
-					clamfi_cleanup(ctx);
3825
-					return cl_error;
3826
-				}
3827
-				ptr = strchr(err, '\0');
3828
-			}
3829
-			ptr = cli_strrcpy(ptr, " ");
3830
-			ptr = cli_strrcpy(ptr, *to);
3831
-		}
3832
-		(void)strcpy(ptr, "\n");
3833
-
3834
-		/* Include the sendmail queue ID in the log */
3835
-		logg("%s: %s %s", sendmailId, mess, err);
3836
-		free(err);
3837
-
3838
-		if(!qflag) {
3839
-			char cmd[128];
3840
-			FILE *sendmail;
3841
-
3842
-			/*
3843
-			 * If the oflag is given this sendmail command
3844
-			 * will cause the mail being generated here to be
3845
-			 * scanned. So if oflag is given we just put the
3846
-			 * item in the queue so there's no scanning of two
3847
-			 * messages at once. It'll still be scanned, but
3848
-			 * not at the same time as the incoming message
3849
-			 *
3850
-			 * FIXME: there is a race condition here when sendmail
3851
-			 * and clamav-milter run on the same machine. If the
3852
-			 * system is very overloaded this sendmail can
3853
-			 * take a long time to start - and may even fail
3854
-			 * is the LA is > REFUSE_LA. In all the time we're
3855
-			 * taking to start this sendmail, the sendmail that's
3856
-			 * started us may timeout waiting for a response and
3857
-			 * let the virus through (albeit tagged with
3858
-			 * X-Virus-Status: Infected) because we haven't
3859
-			 * sent SMFIS_DISCARD or SMFIS_REJECT
3860
-			 *
3861
-			 * -i flag, suggested by Michal Jaegermann
3862
-			 *	<michal@harddata.com>
3863
-			 */
3864
-			snprintf(cmd, sizeof(cmd) - 1,
3865
-				(oflag || fflag) ? "%s -t -i -odq" : "%s -t -i",
3866
-				SENDMAIL_BIN);
3867
-
3868
-			cli_dbgmsg("Calling %s\n", cmd);
3869
-			sendmail = popen(cmd, "w");
3870
-
3871
-			if(sendmail) {
3872
-				if(from && from[0])
3873
-					fprintf(sendmail, "From: %s\n", from);
3874
-				else
3875
-					fprintf(sendmail, "From: %s\n", privdata->from);
3876
-#ifdef	BOUNCE
3877
-				if(bflag && privdata->from) {
3878
-					fprintf(sendmail, "To: %s\n", privdata->from);
3879
-					fprintf(sendmail, "Cc: %s\n", postmaster);
3880
-				} else
3881
-#endif
3882
-					fprintf(sendmail, "To: %s\n", postmaster);
3883
-
3884
-				if((!pflag) && privdata->to)
3885
-					for(to = privdata->to; *to; to++)
3886
-						fprintf(sendmail, "Cc: %s\n", *to);
3887
-				/*
3888
-				 * Auto-submitted is still a draft, keep an
3889
-				 * eye on its format
3890
-				 */
3891
-				fputs("Auto-Submitted: auto-submitted (antivirus notify)\n", sendmail);
3892
-				/* "Sergey Y. Afonin" <asy@kraft-s.ru> */
3893
-				if((ptr = smfi_getsymval(ctx, "{_}")) != NULL)
3894
-					fprintf(sendmail,
3895
-						"X-Infected-Received-From: %s\n",
3896
-						ptr);
3897
-				fputs(_("Subject: Virus intercepted\n"), sendmail);
3898
-
3899
-				if(templateHeaders) {
3900
-					/*
3901
-					 * For example, to state the character
3902
-					 * set of the message:
3903
-					 *	Content-Type: text/plain; charset=koi8-r
3904
-					 *
3905
-					 * Based on a suggestion by Denis
3906
-					 *	Eremenko <moonshade@mail.kz>
3907
-					 */
3908
-					FILE *fin = fopen(templateHeaders, "r");
3909
-
3910
-					if(fin == NULL) {
3911
-						perror(templateHeaders);
3912
-						logg(_("!Can't open e-mail template header file %s"),
3913
-								templateHeaders);
3914
-					} else {
3915
-						int c;
3916
-						int lastc = EOF;
3917
-
3918
-						while((c = getc(fin)) != EOF) {
3919
-							putc(c, sendmail);
3920
-							lastc = c;
3921
-						}
3922
-						fclose(fin);
3923
-						/*
3924
-						 * File not new line terminated
3925
-						 */
3926
-						if(lastc != '\n')
3927
-							fputs(_("\n"), sendmail);
3928
-					}
3929
-				}
3930
-
3931
-				fputs(_("\n"), sendmail);
3932
-
3933
-				if((templateFile == NULL) ||
3934
-				   (sendtemplate(ctx, templateFile, sendmail, virusname) < 0)) {
3935
-					/*
3936
-					 * Use our own hardcoded template
3937
-					 */
3938
-#ifdef	BOUNCE
3939
-					if(bflag)
3940
-						fputs(_("A message you sent to\n"), sendmail);
3941
-					else if(pflag)
126
+	if(cfgopt(copt, "AllowSupplementaryGroups")->enabled) {
127
+#ifdef HAVE_INITGROUPS
128
+	    if(initgroups(cpt->strarg, user->pw_gid)) {
129
+		fprintf(stderr, "ERROR: initgroups() failed.\n");
130
+		freecfg(copt);
131
+		return 1;
132
+	    }
3942 133
 #else
3943
-					if(pflag)
134
+	    mprintf("!AllowSupplementaryGroups: initgroups() is not available, please disable AllowSupplementaryGroups\n");
135
+	    freecfg(copt);
136
+	    return 1;
3944 137
 #endif
3945
-						/*
3946
-						 * The message is only going to
3947
-						 * the postmaster, so include
3948
-						 * some useful information
3949
-						 */
3950
-						fprintf(sendmail, _("The message %1$s sent from %2$s to\n"),
3951
-							sendmailId, privdata->from);
3952
-					else
3953
-						fprintf(sendmail, _("A message sent from %s to\n"),
3954
-							privdata->from);
3955
-
3956
-					for(to = privdata->to; *to; to++)
3957
-						fprintf(sendmail, "\t%s\n", *to);
3958
-					fprintf(sendmail, _("contained %s and has not been accepted for delivery.\n"), virusname);
3959
-
3960
-					if(quarantine_dir != NULL)
3961
-						fprintf(sendmail, _("\nThe message in question has been quarantined as %s\n"), privdata->filename);
3962
-
3963
-					if(hflag) {
3964
-						fprintf(sendmail, _("\nThe message was received by %1$s from %2$s via %3$s\n\n"),
3965
-							smfi_getsymval(ctx, "j"), privdata->from,
3966
-							smfi_getsymval(ctx, "_"));
3967
-						fputs(_("For your information, the original message headers were:\n\n"), sendmail);
3968
-						header_list_print(privdata->headers, sendmail);
3969
-					} else if(privdata->received)
3970
-						/*
3971
-						 * TODO: parse this to find
3972
-						 * real infected machine.
3973
-						 * Need to decide how to find
3974
-						 * if it's a dynamic IP from a
3975
-						 * dial up account in which
3976
-						 * case there may not be much
3977
-						 * we can do if that DHCP has
3978
-						 * set the hostname...
3979
-						 */
3980
-						fprintf(sendmail, _("\nThe infected machine is likely to be here:\n%s\t\n"),
3981
-							privdata->received);
3982
-
3983
-				}
3984
-
3985
-				cli_dbgmsg("Waiting for %s to finish\n", cmd);
3986
-				if(pclose(sendmail) != 0)
3987
-					logg(_("%s: Failed to notify clamAV interception - see dead.letter\n"), sendmailId);
3988
-			} else
3989
-				logg(_("^Can't execute '%s' to send virus notice"), cmd);
3990
-		}
3991
-
3992
-		if(report && (quarantine == NULL) && (!advisory) &&
3993
-		   (strstr(virusname, "Phishing") != NULL)) {
3994
-			/*
3995
-			 * Report phishing to an agency
3996
-			 */
3997
-			for(to = privdata->to; *to; to++) {
3998
-				smfi_delrcpt(ctx, *to);
3999
-				smfi_addheader(ctx, "X-Original-To", *to);
4000
-			}
4001
-			if(smfi_addrcpt(ctx, report) == MI_FAILURE) {
4002
-				/* It's a remote site */
4003
-				if(privdata->filename) {
4004
-					char cmd[1024];
4005
-
4006
-					snprintf(cmd, sizeof(cmd) - 1,
4007
-						"mail -s \"%s\" %s < %s",
4008
-						virusname, report,
4009
-						privdata->filename);
4010
-					if(system(cmd) == 0)
4011
-						logg(_("#Reported phishing to %s"), report);
4012
-					else
4013
-						logg(_("^Couldn't report to %s\n"), report);
4014
-					if((!rejectmail) || privdata->discard)
4015
-						rc = SMFIS_DISCARD;
4016
-					else
4017
-						rc = SMFIS_REJECT;
4018
-				} else {
4019
-					logg(_("^Can't set anti-phish header\n"));
4020
-					rc = (privdata->discard) ? SMFIS_DISCARD : SMFIS_REJECT;
4021
-				}
4022
-			} else {
4023
-				setsubject(ctx, "Phishing attempt trapped by ClamAV and redirected");
4024
-
4025
-				logg("Redirected phish to %s\n", report);
4026
-			}
4027
-		} else if(quarantine) {
4028
-			for(to = privdata->to; *to; to++) {
4029
-				smfi_delrcpt(ctx, *to);
4030
-				smfi_addheader(ctx, "X-Original-To", *to);
4031
-			}
4032
-			/*
4033
-			 * NOTE: on a closed relay this will not work
4034
-			 * if the recipient is a remote address
4035
-			 */
4036
-			if(smfi_addrcpt(ctx, quarantine) == MI_FAILURE) {
4037
-				logg(_("^Can't set quarantine user %s"), quarantine);
4038
-				rc = (privdata->discard) ? SMFIS_DISCARD : SMFIS_REJECT;
4039
-			} else {
4040
-				if(report &&
4041
-				   strstr(virusname, "Phishing") != NULL)
4042
-					(void)smfi_addrcpt(ctx, report);
4043
-				setsubject(ctx, virusname);
4044
-
4045
-				logg("Redirected virus to %s", quarantine);
4046
-			}
4047
-		} else if(advisory)
4048
-			setsubject(ctx, virusname);
4049
-		else if(rejectmail) {
4050
-			if(privdata->discard)
4051
-				rc = SMFIS_DISCARD;
4052
-			else
4053
-				rc = SMFIS_REJECT;	/* Delete the e-mail */
4054
-		} else
4055
-			rc = SMFIS_DISCARD;
4056
-
4057
-		if(quarantine_dir) {
4058
-			/*
4059
-			 * Cleanup filename here otherwise clamfi_free() will
4060
-			 * delete the file that we wish to keep because it
4061
-			 * is infected
4062
-			 */
4063
-			free(privdata->filename);
4064
-			privdata->filename = NULL;
4065
-		}
4066
-
4067
-		/*
4068
-		 * Don't drop the message if it's been forwarded to a
4069
-		 * quarantine email
4070
-		 */
4071
-		snprintf(reject, sizeof(reject) - 1, _("virus %s detected by ClamAV - http://www.clamav.net"), virusname);
4072
-		smfi_setreply(ctx, (const char *)privdata->rejectCode, "5.7.1", reject);
4073
-		broadcast(mess);
4074
-
4075
-		if(blacklist_time && privdata->ip[0]) {
4076
-			logg(_("Will blacklist %s for %d seconds because of %s\n"),
4077
-				privdata->ip, blacklist_time, virusname);
4078
-			pthread_mutex_lock(&blacklist_mutex);
4079
-			(void)tableUpdate(blacklist, privdata->ip,
4080
-				(int)time((time_t *)0));
4081
-			pthread_mutex_unlock(&blacklist_mutex);
4082
-		}
4083
-	} else if((strstr(mess, "OK") == NULL) && (strstr(mess, "Empty file") == NULL)) {
4084
-		if(!nflag)
4085
-			smfi_addheader(ctx, "X-Virus-Status", _("Unknown"));
4086
-		logg(_("!%s: incorrect message \"%s\" from clamd"),
4087
-				sendmailId, mess);
4088
-		rc = cl_error;
4089 138
 	} else {
4090
-		if(!nflag)
4091
-			smfi_addheader(ctx, "X-Virus-Status", _("Clean"));
4092
-
4093
-		/* Include the sendmail queue ID in the log */
4094
-		if(logok)
4095
-			logg(_("%s: clean message from %s\n"),
4096
-				sendmailId,
4097
-				(privdata->from) ? privdata->from : _("an unknown sender"));
4098
-
4099
-		if(privdata->body) {
4100
-			/*
4101
-			 * Add a signature that all has been scanned OK
4102
-			 *
4103
-			 * Note that this is simple minded and isn't aware of
4104
-			 *	any MIME segments in the message. In practice
4105
-			 *	this means that the message will only display
4106
-			 *	on users' terminals if the message is
4107
-			 *	plain/text
4108
-			 */
4109
-			off_t len = updateSigFile();
4110
-
4111
-			if(len) {
4112
-				assert(Sflag != 0);
4113
-
4114
-				privdata->body = cli_realloc(privdata->body, privdata->bodyLen + len);
4115
-				if(privdata->body) {
4116
-					memcpy(&privdata->body[privdata->bodyLen], signature, len);
4117
-					smfi_replacebody(ctx, privdata->body, privdata->bodyLen + len);
4118
-				}
4119
-			}
4120
-		}
4121
-	}
4122
-
4123
-	return rc;
4124
-}
4125
-
4126
-static sfsistat
4127
-clamfi_abort(SMFICTX *ctx)
4128
-{
4129
-	logg("*clamfi_abort\n");
4130
-
4131
-	clamfi_cleanup(ctx);
4132
-	decrement_connexions();
4133
-
4134
-	logg("*clamfi_abort returns\n");
4135
-
4136
-	return cl_error;
4137
-}
4138
-
4139
-static sfsistat
4140
-clamfi_close(SMFICTX *ctx)
4141
-{
4142
-	logg("*clamfi_close\n");
4143
-
4144
-	clamfi_cleanup(ctx);
4145
-	decrement_connexions();
4146
-
4147
-	return SMFIS_CONTINUE;
4148
-}
4149
-
4150
-static void
4151
-clamfi_cleanup(SMFICTX *ctx)
4152
-{
4153
-	struct privdata *privdata = (struct privdata *)smfi_getpriv(ctx);
4154
-
4155
-	cli_dbgmsg("clamfi_cleanup\n");
4156
-
4157
-	if(privdata) {
4158
-		clamfi_free(privdata, 0);
4159
-		smfi_setpriv(ctx, NULL);
4160
-	}
4161
-}
4162
-
4163
-static void
4164
-clamfi_free(struct privdata *privdata, int keep)
4165
-{
4166
-	cli_dbgmsg("clamfi_free\n");
4167
-
4168
-	if(privdata) {
4169
-#ifdef	SESSION
4170
-		struct session *session;
4171
-#endif
4172
-		if(privdata->body)
4173
-			free(privdata->body);
4174
-
4175
-		if(privdata->dataSocket >= 0)
4176
-			close(privdata->dataSocket);
4177
-
4178
-		if(privdata->filename != NULL) {
4179
-			/*
4180
-			 * Don't print an error if the file hasn't been
4181
-			 * created yet
4182
-			 */
4183
-			if((unlink(privdata->filename) < 0) && (errno != ENOENT)) {
4184
-				perror(privdata->filename);
4185
-				logg(_("!Can't remove clean file %s"),
4186
-					privdata->filename);
4187
-			}
4188
-			free(privdata->filename);
4189
-		}
4190
-
4191
-		if(privdata->from) {
4192
-#ifdef	CL_DEBUG
4193
-			if(debug_level >= 9)
4194
-				cli_dbgmsg("Free privdata->from\n");
4195
-#endif
4196
-			free(privdata->from);
4197
-		}
4198
-
4199
-		if(privdata->subject)
4200
-			free(privdata->subject);
4201
-		if(privdata->sender)
4202
-			free(privdata->sender);
4203
-
4204
-		if(privdata->to) {
4205
-			char **to;
4206
-
4207
-			for(to = privdata->to; *to; to++) {
4208
-#ifdef	CL_DEBUG
4209
-				if(debug_level >= 9)
4210
-					cli_dbgmsg("Free *privdata->to\n");
4211
-#endif
4212
-				free(*to);
4213
-			}
4214
-#ifdef	CL_DEBUG
4215
-			if(debug_level >= 9)
4216
-				cli_dbgmsg("Free privdata->to\n");
4217
-#endif
4218
-			free(privdata->to);
4219
-		}
4220
-
4221
-		if(external) {
4222
-#ifdef	SESSION
4223
-			session = &sessions[privdata->serverNumber];
4224
-			pthread_mutex_lock(&sstatus_mutex);
4225
-			if(session->status == CMDSOCKET_INUSE) {
4226
-				/*
4227
-				 * Probably we've got here because
4228
-				 * StreamMaxLength has been reached
4229
-				 */
4230
-#if	0
4231
-				pthread_mutex_unlock(&sstatus_mutex);
4232
-				if(readTimeout) {
4233
-					char buf[64];
4234
-					const int fd = session->sock;
4235
-
4236
-					cli_dbgmsg("clamfi_free: flush server %d fd %d\n",
4237
-						privdata->serverNumber, fd);
4238
-
4239
-					/*
4240
-					 * FIXME: whenever this code gets
4241
-					 *	executed, all of the PINGs fail
4242
-					 *	in the next watchdog cycle
4243
-					 */
4244
-					while(clamd_recv(fd, buf, sizeof(buf)) > 0)
4245
-						;
4246
-				}
4247
-				pthread_mutex_lock(&sstatus_mutex);
4248
-#endif
4249
-				/* Force a reset */
4250
-				session->status = CMDSOCKET_DOWN;
4251
-			}
4252
-			pthread_mutex_unlock(&sstatus_mutex);
4253
-#else
4254
-			if(privdata->cmdSocket >= 0) {
4255
-#if	0
4256
-				char buf[64];
4257
-
4258
-				/*
4259
-				 * Flush the remote end so that clamd doesn't
4260
-				 * get a SIGPIPE
4261
-				 */
4262
-				if(readTimeout)
4263
-					while(clamd_recv(privdata->cmdSocket, buf, sizeof(buf)) > 0)
4264
-						;
4265
-#endif
4266
-				close(privdata->cmdSocket);
4267
-			}
4268
-#endif
4269
-		}
4270
-
4271
-		if(privdata->headers)
4272
-			header_list_free(privdata->headers);
4273
-
4274
-#ifdef	CL_DEBUG
4275
-		if(debug_level >= 9)
4276
-			cli_dbgmsg("Free privdata\n");
4277
-#endif
4278
-		if(privdata->received)
4279
-			free(privdata->received);
4280
-
4281
-		if(keep) {
4282
-			memset(privdata, '\0', sizeof(struct privdata));
4283
-#ifdef	SESSION
4284
-			privdata->dataSocket = -1;
4285
-#else
4286
-			privdata->dataSocket = privdata->cmdSocket = -1;
4287
-#endif
4288
-		} else
4289
-			free(privdata);
4290
-	}
4291
-
4292
-	cli_dbgmsg("clamfi_free returns\n");
4293
-}
4294
-
4295
-/*
4296
- * Returns < 0 for failure, otherwise the number of bytes sent
4297
- */
4298
-static int
4299
-clamfi_send(struct privdata *privdata, size_t len, const char *format, ...)
4300
-{
4301
-	char output[BUFSIZ];
4302
-	const char *ptr;
4303
-	int ret = 0;
4304
-	assert(format != NULL);
4305
-
4306
-	if(len > 0)
4307
-		/*
4308
-		 * It isn't a NUL terminated string. We have a set number of
4309
-		 * bytes to output.
4310
-		 */
4311
-		ptr = format;
4312
-	else {
4313
-		va_list argp;
4314
-
4315
-		va_start(argp, format);
4316
-		vsnprintf(output, sizeof(output) - 1, format, argp);
4317
-		va_end(argp);
4318
-
4319
-		len = strlen(output);
4320
-		ptr = output;
4321
-	}
4322
-#ifdef	CL_DEBUG
4323
-	if(debug_level >= 9) {
4324
-		time_t t;
4325
-		const struct tm *tm;
4326
-
4327
-		time(&t);
4328
-		tm = localtime(&t);
4329
-
4330
-		cli_dbgmsg("%d:%d:%d clamfi_send: len=%u bufsiz=%u, fd=%d\n",
4331
-			tm->tm_hour, tm->tm_min, tm->tm_sec, len,
4332
-			sizeof(output), privdata->dataSocket);
4333
-	}
4334
-#endif
4335
-
4336
-	while(len > 0) {
4337
-		const int nbytes = (privdata->filename) ?
4338
-			write(privdata->dataSocket, ptr, len) :
4339
-			send(privdata->dataSocket, ptr, len, 0);
4340
-
4341
-		assert(privdata->dataSocket >= 0);
4342
-
4343
-		if(nbytes == -1) {
4344
-			if(privdata->filename) {
4345
-#ifdef HAVE_STRERROR_R
4346
-				char buf[32];
4347
-
4348
-				perror(privdata->filename);
4349
-				strerror_r(errno, buf, sizeof(buf));
4350
-				logg(_("!write failure (%lu bytes) to %s: %s\n"),
4351
-					(unsigned long)len, privdata->filename, buf);
4352
-#else
4353
-				perror(privdata->filename);
4354
-				logg(_("!write failure (%lu bytes) to %s: %s\n"),
4355
-					(unsigned long)len, privdata->filename,
4356
-					strerror(errno));
4357
-#endif
4358
-			} else {
4359
-				if(errno == EINTR)
4360
-					continue;
4361
-				perror("send");
4362
-#ifdef HAVE_STRERROR_R
4363
-				{
4364
-					char buf[32];
4365
-					strerror_r(errno, buf, sizeof(buf));
4366
-					logg(_("!write failure (%lu bytes) to clamd: %s\n"),
4367
-						(unsigned long)len, buf);
4368
-				}
4369
-#else
4370
-				logg(_("!write failure (%lu bytes) to clamd: %s\n"),
4371
-					(unsigned long)len, strerror(errno));
139
+#ifdef HAVE_SETGROUPS
140
+	    if(setgroups(1, &user->pw_gid)) {
141
+		fprintf(stderr, "ERROR: setgroups() failed.\n");
142
+		freecfg(copt);
143
+		return 1;
144
+	    }
4372 145
 #endif
4373
-				checkClamd(1);
4374
-			}
4375
-
4376
-			return -1;
4377
-		}
4378
-		ret += nbytes;
4379
-		len -= nbytes;
4380
-		ptr = &ptr[nbytes];
4381
-
4382
-		if(streamMaxLength > 0L) {
4383
-			privdata->numBytes += nbytes;
4384
-			if(privdata->numBytes >= streamMaxLength)
4385
-				break;
4386
-		}
4387 146
 	}
4388
-	return ret;
4389
-}
4390 147
 
4391
-/*
4392
- * Like strcpy, but return the END of the destination, allowing a quicker
4393
- * means of adding to the end of a string than strcat
4394
- */
4395
-#if	0
4396
-static char *
4397
-strrcpy(char *dest, const char *source)
4398
-{
4399
-	/* Pre assertions */
4400
-	assert(dest != NULL);
4401
-	assert(source != NULL);
4402
-	assert(dest != source);
4403
-
4404
-	while((*dest++ = *source++) != '\0')
4405
-		;
4406
-	return(--dest);
4407
-}
4408
-#endif
4409
-
4410
-/*
4411
- * Read from clamav - timeout if necessary
4412
- */
4413
-static long
4414
-clamd_recv(int sock, char *buf, size_t len)
4415
-{
4416
-	struct timeval tv;
4417
-	long ret;
4418
-
4419
-	assert(sock >= 0);
4420
-
4421
-	if(readTimeout == 0) {
4422
-		do
4423
-			/* TODO: Needs a test for ssize_t in configure */
4424
-			ret = (long)recv(sock, buf, len, 0);
4425
-		while((ret < 0) && (errno == EINTR));
4426
-
4427
-		return ret;
148
+	if(setgid(user->pw_gid)) {
149
+	    fprintf(stderr, "ERROR: setgid(%d) failed.\n", (int) user->pw_gid);
150
+	    freecfg(copt);
151
+	    return 1;
4428 152
 	}
4429 153
 
4430
-	tv.tv_sec = readTimeout;
4431
-	tv.tv_usec = 0;
4432
-
4433
-	for(;;) {
4434
-		fd_set rfds;
4435
-
4436
-		FD_ZERO(&rfds);
4437
-		FD_SET(sock, &rfds);
4438
-
4439
-		switch(select(sock + 1, &rfds, NULL, NULL, &tv)) {
4440
-			case -1:
4441
-				if(errno == EINTR)
4442
-					/* FIXME: work out time left */
4443
-					continue;
4444
-				perror("select");
4445
-				return -1;
4446
-			case 0:
4447
-				logg(_("!No data received from clamd in %d seconds\n"), readTimeout);
4448
-				return 0;
4449
-		}
4450
-		break;
154
+	if(setuid(user->pw_uid)) {
155
+	    fprintf(stderr, "ERROR: setuid(%d) failed.\n", (int) user->pw_uid);
156
+	    freecfg(copt);
157
+	    return 1;
4451 158
 	}
159
+    }
4452 160
 
4453
-	do
4454
-		ret = recv(sock, buf, len, 0);
4455
-	while((ret < 0) && (errno == EINTR));
4456
-
4457
-	return ret;
4458
-}
4459
-
4460
-/*
4461
- * Read in the signature file
4462
- */
4463
-static off_t
4464
-updateSigFile(void)
4465
-{
4466
-	struct stat statb;
4467
-	int fd;
4468
-
4469
-	if(sigFilename == NULL)
4470
-		/* nothing to read */
4471
-		return 0;
161
+    logg_lock = !cfgopt(copt, "LogFileUnlock")->enabled;
162
+    logg_time = cfgopt(copt, "LogTime")->enabled;
163
+    logg_size = cfgopt(copt, "LogFileMaxSize")->numarg;
164
+    logg_verbose = mprintf_verbose = cfgopt(copt, "LogVerbose")->enabled;
4472 165
 
4473
-	if(stat(sigFilename, &statb) < 0) {
4474
-		perror(sigFilename);
4475
-		logg(_("Can't stat %s"), sigFilename);
4476
-		return 0;
166
+    if((cpt = cfgopt(copt, "LogFile"))->enabled) {
167
+	time_t currtime;
168
+	logg_file = cpt->strarg;
169
+	if(strlen(logg_file) < 2 || logg_file[0] != '/') {
170
+	    fprintf(stderr, "ERROR: LogFile requires full path.\n");
171
+	    logg_close();
172
+	    freecfg(copt);
173
+	    return 1;
4477 174
 	}
4478
-
4479
-	if(statb.st_mtime <= signatureStamp)
4480
-		return statb.st_size;	/* not changed */
4481
-
4482
-	fd = open(sigFilename, O_RDONLY);
4483
-	if(fd < 0) {
4484
-		perror(sigFilename);
4485
-		logg(_("Can't open %s"), sigFilename);
4486
-		return 0;
175
+	time(&currtime);
176
+	if(logg("#+++ Started at %s", ctime(&currtime))) {
177
+	    fprintf(stderr, "ERROR: Can't initialize the internal logger\n");
178
+	    logg_close();
179
+	    freecfg(copt);
180
+	    return 1;
4487 181
 	}
182
+    } else
183
+	logg_file = NULL;
4488 184
 
4489
-	signatureStamp = statb.st_mtime;
4490
-
4491
-	signature = cli_realloc((void *)signature, statb.st_size);
4492
-	if(signature)
4493
-		cli_readn(fd, (void *)signature, statb.st_size);
4494
-	close(fd);
4495
-
4496
-	return statb.st_size;
4497
-}
4498
-
4499
-static header_list_t
4500
-header_list_new(void)
4501
-{
4502
-	header_list_t ret;
4503
-
4504
-	ret = (header_list_t)cli_malloc(sizeof(struct header_list_struct));
4505
-	if(ret) {
4506
-		ret->first = NULL;
4507
-		ret->last = NULL;
4508
-	}
4509
-	return ret;
4510
-}
4511
-
4512
-static void
4513
-header_list_free(header_list_t list)
4514
-{
4515
-	struct header_node_t *iter;
4516
-
4517
-	if(list == NULL)
4518
-		return;
4519
-
4520
-	iter = list->first;
4521
-	while(iter) {
4522
-		struct header_node_t *iter2 = iter->next;
4523
-		free(iter->header);
4524
-		free(iter);
4525
-		iter = iter2;
4526
-	}
4527
-	free(list);
4528
-}
4529
-
4530
-static void
4531
-header_list_add(header_list_t list, const char *headerf, const char *headerv)
4532
-{
4533
-	char *header;
4534
-	size_t len;
4535
-	struct header_node_t *new_node;
4536
-
4537
-	if(list == NULL)
4538
-		return;
4539
-
4540
-	len = (size_t)(strlen(headerf) + strlen(headerv) + 3);
4541
-
4542
-	header = (char *)cli_malloc(len);
4543
-	if(header == NULL)
4544
-		return;
4545
-
4546
-	sprintf(header, "%s: %s", headerf, headerv);
4547
-	new_node = (struct header_node_t *)cli_malloc(sizeof(struct header_node_t));
4548
-	if(new_node == NULL) {
4549
-		free(header);
4550
-		return;
4551
-	}
4552
-	new_node->header = header;
4553
-	new_node->next = NULL;
4554
-	if(list->first == NULL)
4555
-		list->first = new_node;
4556
-	if(list->last)
4557
-		list->last->next = new_node;
4558
-
4559
-	list->last = new_node;
4560
-}
4561
-
4562
-static void
4563
-header_list_print(const header_list_t list, FILE *fp)
4564
-{
4565
-	const struct header_node_t *iter;
4566
-
4567
-	if(list == NULL)
4568
-		return;
185
+#if defined(USE_SYSLOG) && !defined(C_AIX)
186
+    if(cfgopt(copt, "LogSyslog")->enabled) {
187
+	int fac;
4569 188
 
4570
-	for(iter = list->first; iter; iter = iter->next) {
4571
-		if(strncmp(iter->header, "From ", 5) == 0)
4572
-			putc('>', fp);
4573
-		fprintf(fp, "%s\n", iter->header);
189
+	cpt = cfgopt(copt, "LogFacility");
190
+	if((fac = logg_facility(cpt->strarg)) == -1) {
191
+	    logg("!LogFacility: %s: No such facility.\n", cpt->strarg);
192
+	    logg_close();
193
+	    freecfg(copt);
194
+	    return 1;
4574 195
 	}
4575
-}
4576
-
4577
-/*
4578
- * Establish a connexion to clamd
4579
- *	Returns success (1) or failure (0)
4580
- */
4581
-static int
4582
-connect2clamd(struct privdata *privdata)
4583
-{
4584
-	assert(privdata != NULL);
4585
-	assert(privdata->dataSocket == -1);
4586
-	assert(privdata->from != NULL);
4587
-	assert(privdata->to != NULL);
4588
-
4589
-	logg("*connect2clamd\n");
4590
-
4591
-	if(quarantine_dir || tmpdir) {	/* store message in a temporary file */
4592
-		int ntries = 5;
4593
-		const char *dir = (tmpdir) ? tmpdir : quarantine_dir;
4594 196
 
4595
-		/*
4596
-		 * TODO: investigate mkdtemp on LINUX and possibly others
4597
-		 */
4598
-#ifdef	C_AIX
4599
-		/*
4600
-		 * Patch by Andy Feldt <feldt@nhn.ou.edu>, AIX 5.2 sets errno
4601
-		 * to ENOENT often and sometimes sets errno to 0 (after a
4602
-		 * database reload) for the mkdir call
4603
-		 */
4604
-		if((mkdir(dir, 0700) < 0) && (errno != EEXIST) && (errno > 0) &&
4605
-		    (errno != ENOENT)) {
4606
-#else
4607
-		if((mkdir(dir, 0700) < 0) && (errno != EEXIST)) {
4608
-#endif
4609
-			perror(dir);
4610
-			logg(_("mkdir %s failed"), dir);
4611
-			return 0;
4612
-		}
4613
-		privdata->filename = (char *)cli_malloc(strlen(dir) + 12);
4614
-
4615
-		if(privdata->filename == NULL)
4616
-			return 0;
4617
-
4618
-		do {
4619
-			sprintf(privdata->filename, "%s/msg.XXXXXX", dir);
4620
-#if	defined(C_LINUX) || defined(C_BSD) || defined(HAVE_MKSTEMP) || defined(C_SOLARIS)
4621
-			privdata->dataSocket = mkstemp(privdata->filename);
4622
-#else
4623
-			if(mktemp(privdata->filename) == NULL) {
4624
-				logg(_("mktemp %s failed"), privdata->filename);
4625
-				return 0;
4626
-			}
4627
-			privdata->dataSocket = open(privdata->filename, O_CREAT|O_EXCL|O_WRONLY|O_TRUNC, 0600);
4628
-#endif
4629
-		} while((--ntries > 0) && (privdata->dataSocket < 0));
4630
-
4631
-		if(privdata->dataSocket < 0) {
4632
-			perror(privdata->filename);
4633
-			logg(_("Temporary quarantine file %s creation failed"),
4634
-				privdata->filename);
4635
-			free(privdata->filename);
4636
-			privdata->filename = NULL;
4637
-			return 0;
4638
-		}
4639
-		privdata->serverNumber = 0;
4640
-		cli_dbgmsg("Saving message to %s to scan later\n", privdata->filename);
4641
-	} else {	/* communicate to clamd */
4642
-		int freeServer, nbytes;
4643
-		in_port_t p;
4644
-		struct sockaddr_in reply;
4645
-		char buf[64];
4646
-
4647
-#ifdef	SESSION
4648
-		struct session *session;
4649
-#else
4650
-		assert(privdata->cmdSocket == -1);
4651
-#endif
4652
-
4653
-		/*
4654
-		 * Create socket to talk to clamd. It will tell us the port to
4655
-		 * use to send the data. That will require another socket.
4656
-		 */
4657
-		if(localSocket) {
4658
-#ifndef	SESSION
4659
-			struct sockaddr_un server;
4660
-
4661
-			memset((char *)&server, 0, sizeof(struct sockaddr_un));
4662
-			server.sun_family = AF_UNIX;
4663
-			strncpy(server.sun_path, localSocket, sizeof(server.sun_path));
4664
-			server.sun_path[sizeof(server.sun_path)-1]='\0';
4665
-
4666
-			if((privdata->cmdSocket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
4667
-				perror("socket");
4668
-				return 0;
4669
-			}
4670
-			if(connect(privdata->cmdSocket, (struct sockaddr *)&server, sizeof(struct sockaddr_un)) < 0) {
4671
-				perror(localSocket);
4672
-				return 0;
4673
-			}
4674
-			privdata->serverNumber = 0;
197
+	openlog("clamd", LOG_PID, fac);
198
+	logg_syslog = 1;
199
+    }
4675 200
 #endif
4676
-			freeServer = 0;
4677
-		} else {	/* TCP/IP */
4678
-#ifdef	SESSION
4679
-			freeServer = findServer();
4680
-			if(freeServer < 0)
4681
-				return 0;
4682
-			assert(freeServer < (int)max_children);
4683
-#else
4684
-			struct sockaddr_in server;
4685
-
4686
-			memset((char *)&server, 0, sizeof(struct sockaddr_in));
4687
-			server.sin_family = AF_INET;
4688
-			server.sin_port = (in_port_t)htons(tcpSocket);
4689
-
4690
-			assert(serverIPs != NULL);
4691
-
4692
-			freeServer = findServer();
4693
-			if(freeServer < 0)
4694
-				return 0;
4695
-			assert(freeServer < (int)numServers);
4696
-
4697
-			server.sin_addr.s_addr = serverIPs[freeServer];
4698
-
4699
-			if((privdata->cmdSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
4700
-				perror("socket");
4701
-				return 0;
4702
-			}
4703
-			if(connect(privdata->cmdSocket, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0) {
4704
-				char *hostname = cli_strtok(serverHostNames, freeServer, ":");
4705
-
4706
-				perror(hostname ? hostname : "connect");
4707
-				close(privdata->cmdSocket);
4708
-				privdata->cmdSocket = -1;
4709
-				if(hostname)
4710
-					free(hostname);
4711
-				time(&last_failed_pings[freeServer]);
4712
-				return 0;
4713
-			}
4714
-			last_failed_pings[freeServer] = (time_t)0;
4715
-#endif
4716
-			privdata->serverNumber = freeServer;
4717
-		}
4718
-
4719
-#ifdef	SESSION
4720
-		if(serverIPs[freeServer] == (int)inet_addr("127.0.0.1")) {
4721
-			privdata->filename = cli_gentemp(NULL);
4722
-			if(privdata->filename) {
4723
-				cli_dbgmsg("connect2clamd(%d): creating %s\n", freeServer, privdata->filename);
4724
-#ifdef	O_TEXT
4725
-				privdata->dataSocket = open(privdata->filename, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC|O_TEXT, 0600);
4726
-#else
4727
-				privdata->dataSocket = open(privdata->filename, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0600);
4728
-#endif
4729
-				if(privdata->dataSocket < 0) {
4730
-					perror(privdata->filename);
4731
-					free(privdata->filename);
4732
-					privdata->filename = NULL;
4733
-				} else
4734
-					return sendToFrom(privdata);
4735
-			}
4736
-		}
4737
-		cli_dbgmsg("connect2clamd(%d): STREAM\n", freeServer);
4738
-
4739
-		session = &sessions[freeServer];
4740
-		if((session->sock < 0) || (send(session->sock, "STREAM\n", 7, 0) < 7)) {
4741
-			perror("send");
4742
-			pthread_mutex_lock(&sstatus_mutex);
4743
-			session->status = CMDSOCKET_DOWN;
4744
-			pthread_mutex_unlock(&sstatus_mutex);
4745
-			logg(_("!failed to send STREAM command clamd server %d"),
4746
-				freeServer);
4747
-
4748
-			return 0;
4749
-		}
4750
-#else
4751
-		if(send(privdata->cmdSocket, "STREAM\n", 7, 0) < 7) {
4752
-			perror("send");
4753
-			logg(_("!failed to send STREAM command clamd"));
4754
-			return 0;
4755
-		}
4756
-		shutdown(privdata->cmdSocket, SHUT_WR);
4757
-#endif
4758
-
4759
-		/*
4760
-		 * Create socket that we'll use to send the data to clamd
4761
-		 */
4762
-		if((privdata->dataSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
4763
-			perror("socket");
4764
-			logg(_("!failed to create TCPSocket to talk to clamd\n"));
4765
-			return 0;
4766
-		}
4767
-
4768
-		shutdown(privdata->dataSocket, SHUT_RD);
4769
-
4770
-#ifdef	SESSION
4771
-		nbytes = clamd_recv(session->sock, buf, sizeof(buf));
4772
-		if(nbytes <= 0) {
4773
-			if(nbytes < 0) {
4774
-				perror("recv");
4775
-				logg(_("!recv failed from clamd getting PORT\n"));
4776
-			} else
4777
-				logg(_("!EOF from clamd getting PORT\n"));
4778
-
4779
-			pthread_mutex_lock(&sstatus_mutex);
4780
-			session->status = CMDSOCKET_DOWN;
4781
-			return pthread_mutex_unlock(&sstatus_mutex);
4782
-		}
4783
-#else
4784
-		nbytes = clamd_recv(privdata->cmdSocket, buf, sizeof(buf));
4785
-		if(nbytes <= 0) {
4786
-			if(nbytes < 0) {
4787
-				perror("recv");
4788
-				logg(_("!recv failed from clamd getting PORT\n"));
4789
-			} else
4790
-				logg(_("!EOF from clamd getting PORT\n"));
4791
-
4792
-			return 0;
4793
-		}
4794
-#endif
4795
-		buf[nbytes] = '\0';
4796
-#ifdef	CL_DEBUG
4797
-		if(debug_level >= 4)
4798
-			cli_dbgmsg("Received: %s\n", buf);
4799
-#endif
4800
-		if(sscanf(buf, "PORT %hu\n", &p) != 1) {
4801
-			logg(_("!Expected port information from clamd, got '%s'\n"),
4802
-				buf);
4803
-#ifdef	SESSION
4804
-			session->status = CMDSOCKET_DOWN;
4805
-			pthread_mutex_unlock(&sstatus_mutex);
4806
-#endif
4807
-			return 0;
4808
-		}
4809
-
4810
-		memset((char *)&reply, 0, sizeof(struct sockaddr_in));
4811
-		reply.sin_family = AF_INET;
4812
-		reply.sin_port = (in_port_t)htons(p);
4813
-
4814
-		assert(serverIPs != NULL);
4815
-
4816
-		reply.sin_addr.s_addr = serverIPs[freeServer];
4817
-
4818
-#ifdef	CL_DEBUG
4819
-		if(debug_level >= 4)
4820
-#ifdef	SESSION
4821
-			cli_dbgmsg(_("Connecting to local port %d - data %d cmd %d\n"),
4822
-				p, privdata->dataSocket, session->sock);
4823
-#else
4824
-			cli_dbgmsg(_("Connecting to local port %d - data %d cmd %d\n"),
4825
-				p, privdata->dataSocket, privdata->cmdSocket);
4826
-#endif
4827
-#endif
4828
-
4829
-		if(connect(privdata->dataSocket, (struct sockaddr *)&reply, sizeof(struct sockaddr_in)) < 0) {
4830
-			perror("connect");
4831
-
4832
-			cli_dbgmsg("Failed to connect to port %d given by clamd\n",
4833
-				p);
4834
-			/* 0.4 - use better error message */
4835
-#ifdef HAVE_STRERROR_R
4836
-			strerror_r(errno, buf, sizeof(buf));
4837
-			logg(_("!Failed to connect to port %d given by clamd: %s"),
4838
-					p, buf);
4839
-#else
4840
-			logg(_("!Failed to connect to port %d given by clamd: %s"), p, strerror(errno));
4841
-#endif
4842
-#ifdef	SESSION
4843
-			pthread_mutex_lock(&sstatus_mutex);
4844
-			session->status = CMDSOCKET_DOWN;
4845
-			pthread_mutex_unlock(&sstatus_mutex);
4846
-#endif
4847
-			return 0;
4848
-		}
4849
-	}
4850
-
4851
-	if(!sendToFrom(privdata))
4852
-		return 0;
4853
-
4854
-	cli_dbgmsg("connect2clamd: serverNumber = %d\n", privdata->serverNumber);
4855 201
 
202
+    umask(0007);
203
+    if(!(my_socket = cfgopt(copt, "MilterSocket")->strarg)) {
204
+	logg("!Please configure the MilterSocket directive\n");
205
+	logg_close();
206
+	freecfg(copt);
4856 207
 	return 1;
4857
-}
4858
-
4859
-/*
4860
- * Combine the To and From into one clamfi_send to save bandwidth
4861
- * when sending using TCP/IP to connect to a remote clamd, by band
4862
- * width here I mean number of packets
4863
- */
4864
-static int
4865
-sendToFrom(struct privdata *privdata)
4866
-{
4867
-	char **to;
4868
-	char *msg;
4869
-	int length;
4870
-
4871
-	length = strlen(privdata->from) + 34;
4872
-	for(to = privdata->to; *to; to++)
4873
-		length += strlen(*to) + 5;
4874
-
4875
-	msg = cli_malloc(length + 1);
4876
-
4877
-	if(msg) {
4878
-		sprintf(msg, "Received: by clamav-milter\nFrom: %s\n",
4879
-			privdata->from);
4880
-
4881
-		for(to = privdata->to; *to; to++) {
4882
-			char *eom = strchr(msg, '\0');
4883
-
4884
-			sprintf(eom, "To: %s\n", *to);
4885
-		}
4886
-		if(clamfi_send(privdata, length, msg) != length) {
4887
-			free(msg);
4888
-			return 0;
4889
-		}
4890
-		free(msg);
4891
-	} else {
4892
-		if(clamfi_send(privdata, 0,
4893
-		    "Received: by clamav-milter\nFrom: %s\n",
4894
-		    privdata->from) <= 0)
4895
-			return 0;
4896
-
4897
-		for(to = privdata->to; *to; to++)
4898
-			if(clamfi_send(privdata, 0, "To: %s\n", *to) <= 0)
4899
-				return 0;
4900
-	}
4901
-
208
+    }
209
+    if(smfi_setconn(my_socket) == MI_FAILURE) {
210
+	logg("!smfi_setconn failed\n");
211
+	logg_close();
212
+	freecfg(copt);
4902 213
 	return 1;
4903
-}
4904
-
4905
-/*
4906
- * If possible, check if clamd has died, and, if requested, report if it has
4907
- * Returns true if OK or unknown, otherwise false
4908
- */
4909
-static int
4910
-checkClamd(int log_result)
4911
-{
4912
-	pid_t pid;
4913
-	int fd, nbytes;
4914
-	char buf[9];
4915
-
4916
-	if(!localSocket) {
4917
-		/* communicating via TCP, is one of the servers localhost? */
4918
-		int i, onlocal;
4919
-
4920
-		onlocal = 0;
4921
-		for(i = 0; i < numServers; i++)
4922
-#ifdef	INADDR_LOOPBACK
4923
-			if(serverIPs[0] == htonl(INADDR_LOOPBACK)) {
4924
-#else
4925
-			if(serverIPs[0] == inet_addr("127.0.0.1")) {
4926
-#endif
4927
-				onlocal = 1;
4928
-				break;
4929
-			}
4930
-
4931
-		if(!onlocal) {
4932
-			/* No local clamd, use pingServer() to tell */
4933
-			for(i = 0; i < numServers; i++)
4934
-				if(serverIPs[i] && pingServer(i))
4935
-					return 1;
4936
-			if(log_result)
4937
-				logg(_("!Can't find any clamd server\n"));
4938
-			return 0;
4939
-		}
4940
-	}
4941
-
4942
-	if(pidFile == NULL)
4943
-		return 1;	/* PidFile directive missing from clamd.conf */
4944
-
4945
-	fd = open(pidFile, O_RDONLY);
4946
-	if(fd < 0) {
4947
-		if(log_result) {
4948
-			perror(pidFile);
4949
-			logg(_("!Can't open %s\n"), pidFile);
4950
-		}
4951
-		return 1;	/* unknown */
4952
-	}
4953
-	nbytes = read(fd, buf, sizeof(buf) - 1);
4954
-	if(nbytes < 0)
4955
-		perror(pidFile);
4956
-	else
4957
-		buf[nbytes] = '\0';
4958
-	close(fd);
4959
-	pid = atoi(buf);
4960
-	if((kill(pid, 0) < 0) && (errno == ESRCH)) {
4961
-		if(log_result) {
4962
-			perror("clamd");
4963
-			logg(_("!Clamd (pid %d) seems to have died\n"), (int)pid);
4964
-		}
4965
-		return 0;	/* down */
4966
-	}
4967
-	return 1;	/* up */
4968
-}
4969
-
4970
-/*
4971
- * Send a templated message about an intercepted message. Very basic for
4972
- * now, just to prove it works, will enhance the flexability later, only
4973
- * supports %v and $sendmail_variables$ at present.
4974
- *
4975
- * TODO: more template features
4976
- * TODO: allow filename to start with a '|' taken to mean the output of
4977
- *	a program
4978
- */
4979
-static int
4980
-sendtemplate(SMFICTX *ctx, const char *filename, FILE *sendmail, const char *virusname)
4981
-{
4982
-	FILE *fin = fopen(filename, "r");
4983
-	struct stat statb;
4984
-	char *buf, *ptr /* , *ptr2 */;
4985
-	struct privdata *privdata = (struct privdata *)smfi_getpriv(ctx);
4986
-
4987
-	if(fin == NULL) {
4988
-		perror(filename);
4989
-		logg(_("!Can't open e-mail template file %s"), filename);
4990
-		return -1;
4991
-	}
4992
-
4993
-	if(fstat(fileno(fin), &statb) < 0) {
4994
-		/* File disappeared in race condition? */
4995
-		perror(filename);
4996
-		logg(_("!Can't stat e-mail template file %s"), filename);
4997
-		fclose(fin);
4998
-		return -1;
4999
-	}
5000
-	buf = cli_malloc(statb.st_size + 1);
5001
-	if(buf == NULL) {
5002
-		fclose(fin);
5003
-		logg(_("!Out of memory"));
5004
-		return -1;
5005
-	}
5006
-	if(fread(buf, sizeof(char), statb.st_size, fin) != (size_t)statb.st_size) {
5007
-		perror(filename);
5008
-		logg(_("!Error reading e-mail template file %s"),
5009
-			filename);
5010
-		fclose(fin);
5011
-		free(buf);
5012
-		return -1;
5013
-	}
5014
-	fclose(fin);
5015
-	buf[statb.st_size] = '\0';
5016
-
5017
-	for(ptr = buf; *ptr; ptr++)
5018
-		switch(*ptr) {
5019
-			case '%': /* clamAV variable */
5020
-				switch(*++ptr) {
5021
-					case 'v':	/* virus name */
5022
-						fputs(virusname, sendmail);
5023
-						break;
5024
-					case '%':
5025
-						putc('%', sendmail);
5026
-						break;
5027
-					case 'h':	/* headers */
5028
-						if(privdata)
5029
-							header_list_print(privdata->headers, sendmail);
5030
-						break;
5031
-					case '\0':
5032
-						putc('%', sendmail);
5033
-						--ptr;
5034
-						continue;
5035
-					default:
5036
-						logg(_("!%s: Unknown clamAV variable \"%c\"\n"),
5037
-							filename, *ptr);
5038
-						break;
5039
-				}
5040
-				break;
5041
-			case '$': /* sendmail string */ {
5042
-				const char *val;
5043
-				char *end = strchr(++ptr, '$');
5044
-
5045
-				if(end == NULL) {
5046
-					logg(_("!%s: Unterminated sendmail variable \"%s\"\n"),
5047
-						filename, ptr);
5048
-					continue;
5049
-				}
5050
-				*end = '\0';
5051
-
5052
-				val = smfi_getsymval(ctx, ptr);
5053
-				if(val == NULL) {
5054
-					fputs(ptr, sendmail);
5055
-						logg(_("!%s: Unknown sendmail variable \"%s\"\n"),
5056
-							filename, ptr);
5057
-				} else
5058
-					fputs(val, sendmail);
5059
-				ptr = end;
5060
-				break;
5061
-			}
5062
-			case '\\':
5063
-				if(*++ptr == '\0') {
5064
-					--ptr;
5065
-					continue;
5066
-				}
5067
-				putc(*ptr, sendmail);
5068
-				break;
5069
-			default:
5070
-				putc(*ptr, sendmail);
5071
-		}
5072
-
5073
-	free(buf);
5074
-
5075
-	return 0;
5076
-}
5077
-
5078
-/*
5079
- * Keep the infected file in quarantine, return success (0) or failure
5080
- *
5081
- * It's quicker if the quarantine directory is on the same filesystem
5082
- *	as the temporary directory
5083
- */
5084
-static int
5085
-qfile(struct privdata *privdata, const char *sendmailId, const char *virusname)
5086
-{
5087
-	int MM, YY, DD;
5088
-	time_t t;
5089
-	size_t len;
5090
-	char *newname, *ptr;
5091
-	const struct tm *tm;
5092
-
5093
-	assert(privdata != NULL);
5094
-
5095
-	if((privdata->filename == NULL) || (virusname == NULL))
5096
-		return -1;
5097
-
5098
-	cli_dbgmsg("qfile filename '%s' sendmailId '%s' virusname '%s'\n", privdata->filename, sendmailId, virusname);
5099
-
5100
-	len = strlen(quarantine_dir);
5101
-
5102
-	newname = cli_malloc(len + strlen(sendmailId) + strlen(virusname) + 10);
5103
-
5104
-	if(newname == NULL)
5105
-		return -1;
5106
-
5107
-	t = time((time_t *)0);
5108
-	tm = localtime(&t);
5109
-	MM = tm->tm_mon + 1;
5110
-	YY = tm->tm_year - 100;
5111
-	DD = tm->tm_mday;
5112
-
5113
-	sprintf(newname, "%s/%02d%02d%02d", quarantine_dir, YY, MM, DD);
5114
-#ifdef	C_AIX
5115
-	if((mkdir(newname, 0700) < 0) && (errno != EEXIST) && (errno > 0) &&
5116
-	    (errno != ENOENT)) {
5117
-#else
5118
-	if((mkdir(newname, 0700) < 0) && (errno != EEXIST)) {
5119
-#endif
5120
-		perror(newname);
5121
-		logg(_("!mkdir %s failed\n"), newname);
5122
-		return -1;
5123
-	}
5124
-	sprintf(newname, "%s/%02d%02d%02d/%s.%s",
5125
-		quarantine_dir, YY, MM, DD, sendmailId, virusname);
5126
-
5127
-	/*
5128
-	 * Strip out funnies that may be in the name of the virus, such as '/'
5129
-	 * that would cause the quarantine to fail to save since the name
5130
-	 * of the virus is included in the filename
5131
-	 */
5132
-	for(ptr = &newname[len + 8]; *ptr; ptr++) {
5133
-#ifdef	C_DARWIN
5134
-		*ptr &= '\177';
5135
-#endif
5136
-#if	defined(MSDOS) || defined(C_WINDOWS) || defined(C_OS2)
5137
-		if(strchr("/*?<>|\\\"+=,;:\t ", *ptr))
5138
-#else
5139
-		if(*ptr == '/')
5140
-#endif
5141
-			*ptr = '_';
5142
-	}
5143
-	cli_dbgmsg("qfile move '%s' to '%s'\n", privdata->filename, newname);
5144
-
5145
-	if(move(privdata->filename, newname) < 0) {
5146
-		logg(_("^Can't rename %1$s to %2$s\n"),
5147
-			privdata->filename, newname);
5148
-		free(newname);
5149
-		return -1;
5150
-	}
5151
-	free(privdata->filename);
5152
-	privdata->filename = newname;
5153
-
5154
-	logg(_("Email quarantined as %s\n"), newname);
5155
-
5156
-	return 0;
5157
-}
5158
-
5159
-/*
5160
- * Move oldfile to newfile using the fastest possible method
5161
- */
5162
-static int
5163
-move(const char *oldfile, const char *newfile)
5164
-{
5165
-	int ret, c;
5166
-	FILE *fin, *fout;
5167
-#ifdef	C_LINUX
5168
-	struct stat statb;
5169
-	int in, out;
5170
-	off_t offset;
5171
-#endif
5172
-
5173
-	ret = rename(oldfile, newfile);
5174
-	if(ret >= 0)
5175
-		return 0;
5176
-
5177
-	if((ret < 0) && (errno != EXDEV)) {
5178
-		perror(newfile);
5179
-		return -1;
5180
-	}
5181
-
5182
-#ifdef	C_LINUX	/* >= 2.2 */
5183
-	in = open(oldfile, O_RDONLY);
5184
-	if(in < 0) {
5185
-		perror(oldfile);
5186
-		return -1;
5187
-	}
5188
-
5189
-	if(fstat(in, &statb) < 0) {
5190
-		perror(oldfile);
5191
-		close(in);
5192
-		return -1;
5193
-	}
5194
-	out = open(newfile, O_WRONLY|O_CREAT, 0600);
5195
-	if(out < 0) {
5196
-		perror(newfile);
5197
-		close(in);
5198
-		return -1;
5199
-	}
5200
-	offset = (off_t)0;
5201
-	ret = sendfile(out, in, &offset, statb.st_size);
5202
-	close(in);
5203
-	if(ret < 0) {
5204
-		/*
5205
-		 * Fall back if sendfile fails, which will happen on Linux
5206
-		 * 2.6 :-(. FreeBSD works correctly, so the ifdef should be
5207
-		 * fixed
5208
-		 */
5209
-		close(out);
5210
-		unlink(newfile);
5211
-
5212
-		fin = fopen(oldfile, "r");
5213
-		if(fin == NULL)
5214
-			return -1;
5215
-
5216
-		fout = fopen(newfile, "w");
5217
-		if(fout == NULL) {
5218
-			fclose(fin);
5219
-			return -1;
5220
-		}
5221
-		while((c = getc(fin)) != EOF)
5222
-			putc(c, fout);
5223
-
5224
-		fclose(fin);
5225
-		fclose(fout);
5226
-	} else
5227
-		close(out);
5228
-#else
5229
-	fin = fopen(oldfile, "r");
5230
-	if(fin == NULL)
5231
-		return -1;
5232
-
5233
-	fout = fopen(newfile, "w");
5234
-	if(fout == NULL) {
5235
-		fclose(fin);
5236
-		return -1;
5237
-	}
5238
-	while((c = getc(fin)) != EOF)
5239
-		putc(c, fout);
5240
-
5241
-	fclose(fin);
5242
-	fclose(fout);
5243
-#endif
5244
-
5245
-	cli_dbgmsg("removing %s\n", oldfile);
5246
-
5247
-	return unlink(oldfile);
5248
-}
5249
-
5250
-/*
5251
- * Store the name of the virus in the subject of the e-mail
5252
- */
5253
-static void
5254
-setsubject(SMFICTX *ctx, const char *virusname)
5255
-{
5256
-	struct privdata *privdata = (struct privdata *)smfi_getpriv(ctx);
5257
-	char subject[128];
5258
-
5259
-	if(privdata->subject)
5260
-		smfi_addheader(ctx, "X-Original-Subject", privdata->subject);
5261
-
5262
-	snprintf(subject, sizeof(subject) - 1, _("[Virus] %s"), virusname);
5263
-	if(privdata->subject)
5264
-		smfi_chgheader(ctx, "Subject", 1, subject);
5265
-	else
5266
-		smfi_addheader(ctx, "Subject", subject);
5267
-}
5268
-
5269
-#if	0
5270
-/*
5271
- * TODO: gethostbyname_r is non-standard so different operating
5272
- * systems do it in different ways. Need more examples
5273
- * Perhaps we could use res_search()?
5274
- * Perhaps we could use http://www.chiark.greenend.org.uk/~ian/adns/
5275
- *
5276
- * Returns 0 for success
5277
- */
5278
-static int
5279
-clamfi_gethostbyname(const char *hostname, struct hostent *hp, char *buf, size_t len)
5280
-{
5281
-#if	defined(HAVE_GETHOSTBYNAME_R_6)
5282
-	/* e.g. Linux */
5283
-	struct hostent *hp2;
5284
-	int ret = -1;
5285
-
5286
-	if((hostname == NULL) || (hp == NULL))
5287
-		return -1;
5288
-	if(gethostbyname_r(hostname, hp, buf, len, &hp2, &ret) < 0)
5289
-		return ret;
5290
-#elif	defined(HAVE_GETHOSTBYNAME_R_5)
5291
-	/* e.g. BSD, Solaris, Cygwin */
5292
-	int ret = -1;
5293
-
5294
-	if((hostname == NULL) || (hp == NULL))
5295
-		return -1;
5296
-	if(gethostbyname_r(hostname, hp, buf, len, &ret) == NULL)
5297
-		return ret;
5298
-#elif	defined(HAVE_GETHOSTBYNAME_R_3)
5299
-	/* e.g. HP/UX, AIX */
5300
-	if((hostname == NULL) || (hp == NULL))
5301
-		return -1;
5302
-	if(gethostbyname_r(hostname, &hp, (struct hostent_data *)buf) < 0)
5303
-		return h_errno;
5304
-#else
5305
-	/* Single thread the code */
5306
-	struct hostent *hp2;
5307
-	static pthread_mutex_t hostent_mutex = PTHREAD_MUTEX_INITIALIZER;
5308
-
5309
-	if((hostname == NULL) || (hp == NULL))
5310
-		return -1;
5311
-
5312
-	pthread_mutex_lock(&hostent_mutex);
5313
-	if((hp2 = gethostbyname(hostname)) == NULL) {
5314
-		pthread_mutex_unlock(&hostent_mutex);
5315
-		return h_errno;
5316
-	}
5317
-	memcpy(hp, hp2, sizeof(struct hostent));
5318
-	pthread_mutex_unlock(&hostent_mutex);
5319
-#endif
5320
-
5321
-	return 0;
5322
-}
5323
-#endif
5324
-
5325
-/*
5326
- * Handle the -I flag
5327
- */
5328
-static int
5329
-add_local_ip(char *address)
5330
-{
5331
-	char *opt, *pref;
5332
-	int preflen;
5333
-	int retval;
5334
-	struct in_addr ignoreIP;
5335
-#ifdef	AF_INET6
5336
-	struct in6_addr ignoreIP6;
5337
-#endif
5338
-
5339
-	opt = cli_strdup(address);
5340
-	if(opt == NULL)
5341
-		return 0;
5342
-
5343
-	pref = strchr(opt, '/'); /* search for "/prefix" */
5344
-	if(pref)
5345
-		*pref = '\0';
5346
-#ifdef HAVE_INET_NTOP
5347
-	/* IPv4 address ? */
5348
-	if(inet_pton(AF_INET, opt, &ignoreIP) > 0) {
5349
-#else
5350
-	if(inet_aton(address, &ignoreIP)) {
5351
-#endif
5352
-		struct cidr_net *net;
5353
-
5354
-		for(net = (struct cidr_net *)localNets; net->base; net++)
5355
-			;
5356
-		if(pref && *(pref+1))
5357
-			preflen = atoi(pref+1);
5358
-		else
5359
-			preflen = 32;
5360
-
5361
-		net->base = ntohl(ignoreIP.s_addr);
5362
-		net->mask = MAKEMASK(preflen);
5363
-
5364
-		retval = 1;
5365
-	}
5366
-
5367
-#ifdef HAVE_INET_NTOP
5368
-#ifdef AF_INET6
5369
-	else if(inet_pton(AF_INET6, opt, &ignoreIP6) > 0) {
5370
-		/* IPv6 address ? */
5371
-		localNets6[localNets6_cnt].base = ignoreIP6;
5372
-
5373
-		if(pref && *(pref+1))
5374
-			preflen = atoi (pref+1);
5375
-		else
5376
-			preflen = 128;
5377
-		localNets6[localNets6_cnt].preflen = preflen;
5378
-		localNets6_cnt++;
5379
-
5380
-		retval = 1;
5381
-	}
5382
-#endif
5383
-#endif
5384
-	else
5385
-		retval = 0;
5386
-
5387
-	free(opt);
5388
-	return retval;
5389
-}
5390
-
5391
-/*
5392
- * Determine if an IPv6 email address is "local". The address is the
5393
- *	human readable version. Calls isLocalAddr if the given address is
5394
- *	IPv4
5395
- */
5396
-static int
5397
-isLocal(const char *addr)
5398
-{
5399
-	struct in_addr ip;
5400
-#ifdef	AF_INET6
5401
-	struct in6_addr ip6;
5402
-#endif
5403
-
5404
-#ifdef HAVE_INET_NTOP
5405
-	if(inet_pton(AF_INET, addr, &ip) > 0)
5406
-		return isLocalAddr(ip.s_addr);
5407
-#ifdef AF_INET6
5408
-	else if(inet_pton (AF_INET6, addr, &ip6) > 0) {
5409
-		int i;
5410
-		const cidr_net6 *pnet6 = localNets6;
5411
-
5412
-		for (i = 0; i < localNets6_cnt; i++) {
5413
-			int match = 1;
5414
-			int j;
5415
-
5416
-			for(j = 0; match && j < (pnet6->preflen >> 3); j++)
5417
-				if(pnet6->base.s6_addr[j] != ip6.s6_addr[j])
5418
-					match = 0;
5419
-			if(match && (j < 16)) {
5420
-				uint8_t mask = (uint8_t)(0xff << (8 - (pnet6->preflen & 7)) & 0xFF);
5421
-
5422
-				if((pnet6->base.s6_addr[j] & mask) != (ip6.s6_addr[j] & mask))
5423
-					match = 0;
5424
-			}
5425
-			if(match)
5426
-				return 1;	/* isLocal */
5427
-			pnet6++;
5428
-		 }
5429
-	}
5430
-#endif	/* AF_INET6 */
5431
-#endif	/* HAVE_INET_NTOP */
5432
-	return isLocalAddr(inet_addr(addr));
5433
-}
5434
-
5435
-/*
5436
- * David Champion <dgc@uchicago.edu>
5437
- *
5438
- * Check whether addr is on network by applying netmasks.
5439
- * addr must be a 32-bit integer-packed IPv4 address in network order.
5440
- * For example:
5441
- *	struct in_addr IPAddress;
5442
- *	isLocal = isLocalAddr(IPAddress.s_addr);
5443
- */
5444
-static int
5445
-isLocalAddr(in_addr_t addr)
5446
-{
5447
-	const struct cidr_net *net;
5448
-
5449
-	for(net = localNets; net->base; net++)
5450
-		if((net->base & net->mask) == (ntohl(addr) & net->mask))
5451
-			return 1;
5452
-
5453
-	return 0;	/* is non-local */
5454
-}
5455
-
5456
-/*
5457
- * Can't connect to any clamd server. This is serious, we need to inform
5458
- * someone. In the absence of SNMP the best way is by e-mail. We
5459
- * don't want to flood so there's a need to restrict to
5460
- * no more than say one message every 15 minutes
5461
- */
5462
-static void
5463
-clamdIsDown(void)
5464
-{
5465
-	static time_t lasttime;
5466
-	time_t thistime, diff;
5467
-	static pthread_mutex_t time_mutex = PTHREAD_MUTEX_INITIALIZER;
5468
-
5469
-	logg(_("!No response from any clamd server - your AV system is not scanning emails\n"));
5470
-
5471
-	time(&thistime);
5472
-	pthread_mutex_lock(&time_mutex);
5473
-	diff = thistime - lasttime;
5474
-	pthread_mutex_unlock(&time_mutex);
5475
-
5476
-	if(diff >= (time_t)(15 * 60)) {
5477
-		char cmd[128];
5478
-		FILE *sendmail;
5479
-
5480
-		snprintf(cmd, sizeof(cmd) - 1, "%s -t -i", SENDMAIL_BIN);
5481
-
5482
-		sendmail = popen(cmd, "w");
5483
-
5484
-		if(sendmail) {
5485
-			fprintf(sendmail, "To: %s\n", postmaster);
5486
-			fprintf(sendmail, "From: %s\n", postmaster);
5487
-			fputs(_("Subject: ClamAV Down\n"), sendmail);
5488
-			fputs("Priority: High\n\n", sendmail);
5489
-
5490
-			fputs(_("This is an automatic message\n\n"), sendmail);
5491
-
5492
-			if(numServers == 1)
5493
-				fputs(_("The clamd program cannot be contacted.\n"), sendmail);
5494
-			else
5495
-				fputs(_("No clamd server can be contacted.\n"), sendmail);
5496
-
5497
-			fputs(_("Emails may not be being scanned, please check your servers.\n"), sendmail);
5498
-
5499
-			if(pclose(sendmail) == 0) {
5500
-				pthread_mutex_lock(&time_mutex);
5501
-				time(&lasttime);
5502
-				pthread_mutex_unlock(&time_mutex);
5503
-			}
5504
-		}
5505
-	}
5506
-}
5507
-
5508
-#ifdef	SESSION
5509
-/*
5510
- * Thread to monitor the links to clamd sessions. Any marked as being in
5511
- * an error state because of previous I/O errors are restarted, and a heartbeat
5512
- * is sent the others
5513
- *
5514
- * It is woken up when the milter goes idle, when there are no free servers
5515
- * available and once every readTimeout-1 seconds
5516
- *
5517
- * TODO: reload the whiteList file if it's been changed
5518
- *
5519
- * TODO: localSocket support
5520
- */
5521
-static void *
5522
-watchdog(void *a)
5523
-{
5524
-	static pthread_mutex_t watchdog_mutex = PTHREAD_MUTEX_INITIALIZER;
5525
-
5526
-	while(!quitting) {
5527
-		unsigned int i;
5528
-		struct timespec ts;
5529
-		struct timeval tp;
5530
-		struct session *session;
5531
-
5532
-		gettimeofday(&tp, NULL);
5533
-
5534
-		ts.tv_sec = tp.tv_sec + freshclam_monitor;
5535
-		ts.tv_nsec = tp.tv_usec * 1000;
5536
-		cli_dbgmsg("watchdog sleeps\n");
5537
-		pthread_mutex_lock(&watchdog_mutex);
5538
-		/*
5539
-		 * Sometimes this returns EPIPE which isn't listed as a
5540
-		 * return value in the Linux man page for pthread_cond_timedwait
5541
-		 * so I'm not sure why it happens
5542
-		 */
5543
-		switch(pthread_cond_timedwait(&watchdog_cond, &watchdog_mutex, &ts)) {
5544
-			case ETIMEDOUT:
5545
-			case 0:
5546
-				break;
5547
-			default:
5548
-				perror("pthread_cond_timedwait");
5549
-		}
5550
-		pthread_mutex_unlock(&watchdog_mutex);
5551
-
5552
-		cli_dbgmsg("watchdog wakes\n");
5553
-
5554
-		if(check_and_reload_database() != 0) {
5555
-			if(cl_error != SMFIS_ACCEPT) {
5556
-				smfi_stop();
5557
-				return NULL;
5558
-			}
5559
-			logg(_("!No emails will be scanned"));
5560
-		}
5561
-
5562
-		i = 0;
5563
-		session = sessions;
5564
-		pthread_mutex_lock(&sstatus_mutex);
5565
-		for(; i < max_children; i++, session++) {
5566
-			const int sock = session->sock;
5567
-
5568
-			/*
5569
-			 * Check all free sessions are still usable
5570
-			 * This could take some time with many free
5571
-			 * sessions to slow remote servers, so only do this
5572
-			 * when the system is quiet (not 100% accurate when
5573
-			 * determining this since n_children isn't locked but
5574
-			 * that doesn't really matter)
5575
-			 */
5576
-			cli_dbgmsg("watchdog: check server %d\n", i);
5577
-			if((n_children == 0) &&
5578
-			   (session->status == CMDSOCKET_FREE) &&
5579
-			   (clamav_versions != NULL)) {
5580
-				if(send(sock, "VERSION\n", 8, 0) == 8) {
5581
-					char buf[81];
5582
-					const int nbytes = clamd_recv(sock, buf, sizeof(buf) - 1);
5583
-
5584
-					if(nbytes <= 0)
5585
-						session->status = CMDSOCKET_DOWN;
5586
-					else {
5587
-						buf[nbytes] = '\0';
5588
-						if(strncmp(buf, "ClamAV ", 7) == 0) {
5589
-							/* Remove the trailing new line from the reply */
5590
-							char *ptr;
5591
-
5592
-							if((ptr = strchr(buf, '\n')) != NULL)
5593
-								*ptr = '\0';
5594
-							pthread_mutex_lock(&version_mutex);
5595
-							if(clamav_versions[i] == NULL)
5596
-								clamav_versions[i] = cli_strdup(buf);
5597
-							else if(strcmp(buf, clamav_versions[i]) != 0) {
5598
-								logg("New version received for server %d: '%s'\n", i, buf);
5599
-								free(clamav_versions[i]);
5600
-								clamav_versions[i] = cli_strdup(buf);
5601
-							}
5602
-							pthread_mutex_unlock(&version_mutex);
5603
-						} else {
5604
-							cli_warnmsg("watchdog: expected \"ClamAV\", got \"%s\"\n", buf);
5605
-							session->status = CMDSOCKET_DOWN;
5606
-						}
5607
-					}
5608
-				} else {
5609
-					perror("send");
5610
-					session->status = CMDSOCKET_DOWN;
5611
-				}
5612
-
5613
-				if(session->status == CMDSOCKET_DOWN)
5614
-					cli_warnmsg("Session %d has gone down\n", i);
5615
-			}
5616
-			/*
5617
-			 * Reset all all dead sessions
5618
-			 */
5619
-			if(session->status == CMDSOCKET_DOWN) {
5620
-				/*
5621
-				 * The END command probably won't get through,
5622
-				 * but let's give it a go anyway
5623
-				 */
5624
-				if(sock >= 0) {
5625
-					send(sock, "END\n", 4, 0);
5626
-					close(sock);
5627
-				}
5628
-
5629
-				cli_dbgmsg("Trying to restart session %d\n", i);
5630
-				if(createSession(i) == 0) {
5631
-					session->status = CMDSOCKET_FREE;
5632
-					cli_warnmsg("Session %d restarted OK\n", i);
5633
-				}
5634
-			}
5635
-		}
5636
-		for(i = 0; i < max_children; i++)
5637
-			if(sessions[i].status != CMDSOCKET_DOWN)
5638
-				break;
5639
-
5640
-		if(i == max_children)
5641
-			clamdIsDown();
5642
-		pthread_mutex_unlock(&sstatus_mutex);
5643
-
5644
-		/* Garbage collect IP addresses no longer blacklisted */
5645
-		if(blacklist) {
5646
-			pthread_mutex_lock(&blacklist_mutex);
5647
-			tableIterate(blacklist, timeoutBlacklist, NULL);
5648
-			pthread_mutex_unlock(&blacklist_mutex);
5649
-		}
5650
-	}
5651
-	cli_dbgmsg("watchdog quits\n");
5652
-	return NULL;
5653
-}
5654
-#else	/*!SESSION*/
5655
-/*
5656
- * Reload the database from time to time, when using the internal scanner
5657
- *
5658
- * TODO: reload the whiteList file if it's been changed
5659
- */
5660
-/*ARGSUSED*/
5661
-static void *
5662
-watchdog(void *a)
5663
-{
5664
-	static pthread_mutex_t watchdog_mutex = PTHREAD_MUTEX_INITIALIZER;
5665
-
5666
-	if((!blacklist_time) && external)
5667
-		return NULL;	/* no need for this thread */
5668
-
5669
-	while(!quitting) {
5670
-		struct timespec ts;
5671
-		struct timeval tp;
5672
-
5673
-		gettimeofday(&tp, NULL);
5674
-
5675
-		ts.tv_sec = tp.tv_sec + freshclam_monitor;
5676
-		ts.tv_nsec = tp.tv_usec * 1000;
5677
-		cli_dbgmsg("watchdog sleeps\n");
5678
-
5679
-		pthread_mutex_lock(&watchdog_mutex);
5680
-		/*
5681
-		 * Sometimes this returns EPIPE which isn't listed as a
5682
-		 * return value in the Linux man page for pthread_cond_timedwait
5683
-		 * so I'm not sure why it happens
5684
-		 */
5685
-		switch(pthread_cond_timedwait(&watchdog_cond, &watchdog_mutex, &ts)) {
5686
-			case ETIMEDOUT:
5687
-			case 0:
5688
-				break;
5689
-			default:
5690
-				perror("pthread_cond_timedwait");
5691
-		}
5692
-		pthread_mutex_unlock(&watchdog_mutex);
5693
-		cli_dbgmsg("watchdog wakes\n");
5694
-
5695
-		/*
5696
-		 * TODO: sanity check that if n_children == 0, that
5697
-		 * root->refcount == 0. Unfortunatly root->refcount isn't
5698
-		 * thread-safe, since it's governed by a mutex that we can't
5699
-		 * see, and there's no access to it via an approved method
5700
-		 */
5701
-		if(check_and_reload_database() != 0) {
5702
-			if(cl_error != SMFIS_ACCEPT) {
5703
-				smfi_stop();
5704
-				return NULL;
5705
-			}
5706
-			logg(_("!No emails will be scanned"));
5707
-		}
5708
-		/* Garbage collect IP addresses no longer blacklisted */
5709
-		if(blacklist) {
5710
-			pthread_mutex_lock(&blacklist_mutex);
5711
-			tableIterate(blacklist, timeoutBlacklist, NULL);
5712
-			pthread_mutex_unlock(&blacklist_mutex);
5713
-		}
5714
-	}
5715
-	cli_dbgmsg("watchdog quits\n");
5716
-	return NULL;
5717
-}
5718
-#endif
5719
-
5720
-/*
5721
- * Check to see if the database needs to be reloaded
5722
- *	Return 0 for success
5723
- */
5724
-static int
5725
-check_and_reload_database(void)
5726
-{
5727
-	int rc;
5728
-
5729
-	if(external)
5730
-		return 0;
5731
-
5732
-	if(reload) {
5733
-		rc = 1;
5734
-		reload = 0;
5735
-	} else
5736
-		rc = cl_statchkdir(&dbstat);
5737
-
5738
-	switch(rc) {
5739
-		case 1:
5740
-			logg("^Database has changed, loading updated database\n");
5741
-			cl_statfree(&dbstat);
5742
-			rc = loadDatabase();
5743
-			if(rc != 0) {
5744
-				logg("!Failed to load updated database\n");
5745
-				return rc;
5746
-			}
5747
-			break;
5748
-		case 0:
5749
-			logg("*Database has not changed\n");
5750
-			break;
5751
-		default:
5752
-			logg("Database error %d - %s is stopping\n",
5753
-				rc, progname);
5754
-			return 1;
5755
-	}
5756
-	return 0;	/* all OK */
5757
-}
5758
-
5759
-static void
5760
-timeoutBlacklist(char *ip_address, int time_of_blacklist, void *v)
5761
-{
5762
-	if(time_of_blacklist == 0)	/* Must not blacklist this IP address */
5763
-		return;
5764
-	if((time((time_t *)0) - time_of_blacklist) > blacklist_time)
5765
-		tableRemove(blacklist, ip_address);
5766
-}
5767
-
5768
-static void
5769
-quit(void)
5770
-{
5771
-	quitting++;
5772
-
5773
-#ifdef	SESSION
5774
-	pthread_mutex_lock(&version_mutex);
5775
-#endif
5776
-	logg(_("Stopping %s\n"), clamav_version);
5777
-#ifdef	SESSION
5778
-	pthread_mutex_unlock(&version_mutex);
5779
-#endif
5780
-
5781
-	if(!external) {
5782
-		pthread_mutex_lock(&engine_mutex);
5783
-		if(engine)
5784
-			cl_engine_free(engine);
5785
-		pthread_mutex_unlock(&engine_mutex);
5786
-	} else {
5787
-#ifdef	SESSION
5788
-		int i = 0;
5789
-		struct session *session = sessions;
5790
-
5791
-		pthread_mutex_lock(&sstatus_mutex);
5792
-		for(; i < ((localSocket != NULL) ? 1 : (int)max_children); i++) {
5793
-			/*
5794
-			 * Check all free sessions are still usable
5795
-			 * This could take some time with many free
5796
-			 * sessions to slow remote servers, so only do this
5797
-			 * when the system is quiet (not 100% accurate when
5798
-			 * determining this since n_children isn't locked but
5799
-			 * that doesn't really matter)
5800
-			 */
5801
-			cli_dbgmsg("quit: close server %d\n", i);
5802
-			if(session->status == CMDSOCKET_FREE) {
5803
-				const int sock = session->sock;
5804
-
5805
-				send(sock, "END\n", 4, 0);
5806
-				shutdown(sock, SHUT_WR);
5807
-				session->status = CMDSOCKET_DOWN;
5808
-				pthread_mutex_unlock(&sstatus_mutex);
5809
-				close(sock);
5810
-				pthread_mutex_lock(&sstatus_mutex);
5811
-			}
5812
-			session++;
5813
-		}
5814
-		pthread_mutex_unlock(&sstatus_mutex);
5815
-#endif
5816
-	}
5817
-
5818
-	if(tmpdir)
5819
-		if(rmdir(tmpdir) < 0)
5820
-			perror(tmpdir);
5821
-
5822
-	broadcast(_("Stopping clamav-milter"));
5823
-
5824
-	if(pidfile)
5825
-		if(unlink(pidfile) < 0)
5826
-			perror(pidfile);
5827
-
214
+    }
215
+    if(smfi_register(descr) == MI_FAILURE) {
216
+	logg("!smfi_register failed\n");
5828 217
 	logg_close();
5829
-}
5830
-
5831
-static void
5832
-broadcast(const char *mess)
5833
-{
5834
-	struct sockaddr_in s;
5835
-
5836
-	if(broadcastSock < 0)
5837
-		return;
5838
-
5839
-	memset(&s, '\0', sizeof(struct sockaddr_in));
5840
-	s.sin_family = AF_INET;
5841
-	s.sin_port = (in_port_t)htons(tcpSocket ? tcpSocket : 3310);
5842
-	s.sin_addr.s_addr = htonl(INADDR_BROADCAST);
5843
-
5844
-	cli_dbgmsg("broadcast %s to %d\n", mess, broadcastSock);
5845
-	if(sendto(broadcastSock, mess, strlen(mess), 0, (struct sockaddr *)&s, sizeof(struct sockaddr_in)) < 0)
5846
-		perror("sendto");
5847
-}
5848
-
5849
-/*
5850
- * Load a new database into the internal scanner
5851
- */
5852
-static int
5853
-loadDatabase(void)
5854
-{
5855
-	int ret;
5856
-	unsigned int signatures, dboptions;
5857
-	char *daily;
5858
-	struct cl_cvd *d;
5859
-	const struct cfgstruct *cpt;
5860
-	static const char *dbdir;
5861
-
5862
-	assert(!external);
5863
-
5864
-	if(dbdir == NULL) {
5865
-		/*
5866
-		 * First time through, find out in which directory the signature
5867
-		 * databases are
5868
-		 */
5869
-		if((cpt = cfgopt(copt, "DatabaseDirectory")) && cpt->enabled)
5870
-			dbdir = cpt->strarg;
5871
-		else
5872
-			dbdir = cl_retdbdir();
5873
-	}
5874
-
5875
-	daily = cli_malloc(strlen(dbdir) + 11);
5876
-	sprintf(daily, "%s/daily.cvd", dbdir);
5877
-	if(access(daily, R_OK) < 0)
5878
-		sprintf(daily, "%s/daily.cld", dbdir);
5879
-
5880
-
5881
-	cli_dbgmsg("loadDatabase: check %s for updates\n", daily);
5882
-
5883
-	d = cl_cvdhead(daily);
5884
-
5885
-	if(d) {
5886
-		char *ptr;
5887
-		time_t t = d->stime;
5888
-		char buf[26];
5889
-
5890
-		snprintf(clamav_version, VERSION_LENGTH,
5891
-			"ClamAV %s/%u/%s", get_version(), d->version,
5892
-			cli_ctime(&t, buf, sizeof(buf)));
5893
-
5894
-		/* Remove ctime's trailing \n */
5895
-		if((ptr = strchr(clamav_version, '\n')) != NULL)
5896
-			*ptr = '\0';
5897
-
5898
-		cl_cvdfree(d);
5899
-	} else
5900
-		snprintf(clamav_version, VERSION_LENGTH,
5901
-			"ClamAV version %s, clamav-milter version %s",
5902
-			cl_retver(), get_version());
5903
-
5904
-	free(daily);
5905
-
5906
-#ifdef	SESSION
5907
-	pthread_mutex_lock(&version_mutex);
5908
-	if(clamav_versions == NULL) {
5909
-		clamav_versions = (char **)cli_malloc(sizeof(char *));
5910
-		if(clamav_versions == NULL) {
5911
-			pthread_mutex_unlock(&version_mutex);
5912
-			return -1;
5913
-		}
5914
-		clamav_version = cli_malloc(VERSION_LENGTH + 1);
5915
-		if(clamav_version == NULL) {
5916
-			free(clamav_versions);
5917
-			clamav_versions = NULL;
5918
-			pthread_mutex_unlock(&version_mutex);
5919
-			return -1;
5920
-		}
5921
-	}
5922
-	pthread_mutex_unlock(&version_mutex);
5923
-#endif
5924
-	signatures = 0;
5925
-	pthread_mutex_lock(&engine_mutex);
5926
-	if(engine) cl_engine_free(engine);
5927
-	engine = cl_engine_new();
5928
-	if (!engine) {
5929
-		logg("!Can't initialize antivirus engine\n");
5930
-		pthread_mutex_unlock(&engine_mutex);
5931
-		return -1;
5932
-	}
5933
-	if(!cfgopt(copt, "PhishingSignatures")->enabled) {
5934
-		logg("Not loading phishing signatures.\n");
5935
-		dboptions = 0;
5936
-	} else
5937
-		dboptions = CL_DB_PHISHING;
5938
-	if((ret = cl_engine_set(engine, CL_ENGINE_MAX_SCANSIZE, &maxscansize))) {
5939
-		logg("!cli_engine_set(CL_ENGINE_MAX_SCANSIZE) failed: %s\n", cl_strerror(ret));
5940
-		cl_engine_free(engine);
5941
-		pthread_mutex_unlock(&engine_mutex);
5942
-		return -1;
5943
-	}
5944
-	if((ret = cl_engine_set(engine, CL_ENGINE_MAX_FILESIZE, &maxfilesize))) {
5945
-		logg("!cli_engine_set(CL_ENGINE_MAX_FILESIZE) failed: %s\n", cl_strerror(ret));
5946
-		cl_engine_free(engine);
5947
-		pthread_mutex_unlock(&engine_mutex);
5948
-		return -1;
5949
-	}
5950
-	ret = cl_load(dbdir, engine, &signatures, dboptions);
5951
-	if(ret != CL_SUCCESS) {
5952
-		logg("!%s\n", cl_strerror(ret));
5953
-		cl_engine_free(engine);
5954
-		pthread_mutex_unlock(&engine_mutex);
5955
-		return -1;
5956
-	}
5957
-	ret = cl_engine_compile(engine);
5958
-	if(ret != CL_SUCCESS) {
5959
-		logg("!Database initialization error: %s\n", cl_strerror(ret));
5960
-		cl_engine_free(engine);
5961
-		pthread_mutex_unlock(&engine_mutex);
5962
-		return -1;
5963
-	}
5964
-	pthread_mutex_unlock(&engine_mutex);
5965
-#ifdef	SESSION
5966
-	pthread_mutex_lock(&version_mutex);
5967
-#endif
5968
-	logg( _("Loaded %s\n"), clamav_version);
5969
-#ifdef	SESSION
5970
-	pthread_mutex_unlock(&version_mutex);
5971
-#endif
5972
-	logg(_("ClamAV: Protecting against %u viruses\n"), signatures);
5973
-	logg("#Database correctly (re)loaded (%u viruses)\n");
5974
-	return cl_statinidir(dbdir, &dbstat);
5975
-}
5976
-
5977
-static void
5978
-sigsegv(int sig)
5979
-{
5980
-	signal(SIGSEGV, SIG_DFL);
5981
-
5982
-#ifdef HAVE_BACKTRACE
5983
-	print_trace();
5984
-#endif
5985
-
5986
-	logg("!Segmentation fault :-( Bye.., notify bugs@clamav.net\n");
5987
-
5988
-	quitting++;
5989
-	smfi_stop();
5990
-}
5991
-
5992
-extern FILE *logg_fd;
5993
-static void
5994
-sigusr1(int sig)
5995
-{
5996
-
5997
-	signal(SIGUSR1, sigusr1);
5998
-
5999
-	if(!(cfgopt(copt, "LogFile"))->enabled)
6000
-		return;
6001
-
6002
-	logg("SIGUSR1 caught: re-opening log file\n");
218
+	freecfg(copt);
219
+	return 1;
220
+    }
221
+    cpt = cfgopt(copt, "FixStaleSocket");
222
+    if(smfi_opensocket(cpt->enabled) == MI_FAILURE) {
223
+	logg("!Failed to create socket %s\n", my_socket);
6003 224
 	logg_close();
6004
-	logg("*Log file re-opened\n");
6005
-	dup2(fileno(logg_fd), 2);
6006
-}
6007
-
6008
-static void
6009
-sigusr2(int sig)
6010
-{
6011
-	signal(SIGUSR2, sigusr2);
6012
-
6013
-	logg("^SIGUSR2 caught: scheduling database reload\n");
6014
-	reload++;
6015
-}
6016
-
6017
-#ifdef HAVE_BACKTRACE
6018
-static void
6019
-print_trace(void)
6020
-{
6021
-	void *array[BACKTRACE_SIZE];
6022
-	size_t size, i;
6023
-	char **strings;
6024
-	pid_t pid = getpid();
6025
-
6026
-	size = backtrace(array, BACKTRACE_SIZE);
6027
-	strings = backtrace_symbols(array, size);
6028
-
6029
-	logg("*Backtrace of pid %d:\n", pid);
6030
-
6031
-	for(i = 0; i < size; i++)
6032
-		logg("bt[%u]: %s", i, strings[i]);
6033
-
6034
-	/* TODO: dump the current email */
6035
-
6036
-	free(strings);
6037
-}
6038
-#endif
6039
-
6040
-/*
6041
- * Check that the correct port name has been given, i.e. that the
6042
- * input socket to clamav-milter from sendmail, is the same that
6043
- * sendmail has been configured to use as it's output socket
6044
- * Return:	<0 invalid
6045
- *		=0 valid
6046
- *		>0 unknown
6047
- *
6048
- * You wouldn't believe the amount of time I used to waste chasing bug reports
6049
- *	from people who's sendmail.cf didn't tally with the arguments given to
6050
- *	clamav-milter before I put this check in, which is why bug 726 must
6051
- *	never be acted upon.
6052
- *
6053
- * FIXME: return different codes for "the value is wrong" and "sendmail.cf"
6054
- *	hasn't been set up, though that's not so easy to work out.
6055
- */
6056
-static int
6057
-verifyIncomingSocketName(const char *sockName)
6058
-{
6059
-#if HAVE_MMAP
6060
-	int fd, ret;
6061
-	char *ptr;
6062
-	size_t size;
6063
-	struct stat statb;
6064
-
6065
-	if(strncmp(sockName, "inet:", 5) == 0)
6066
-		/*
6067
-		 * clamav-milter is running on a different machine from sendmail
6068
-		 */
6069
-		return 1;
6070
-
6071
-	if(sendmailCF)
6072
-		fd = open(sendmailCF, O_RDONLY);
6073
-	else {
6074
-		fd = open("/etc/mail/sendmail.cf", O_RDONLY);
6075
-		if(fd < 0)
6076
-			fd = open("/etc/sendmail.cf", O_RDONLY);
6077
-	}
6078
-
6079
-	if(fd < 0)
6080
-		return 1;
6081
-
6082
-	if(fstat(fd, &statb) < 0) {
6083
-		close(fd);
6084
-		return 1;
6085
-	}
6086
-
6087
-	size = statb.st_size;
6088
-
6089
-	if(size == 0) {
6090
-		close(fd);
6091
-		return -1;
6092
-	}
6093
-
6094
-	ptr = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
6095
-	if(ptr == MAP_FAILED) {
6096
-		perror("mmap");
6097
-		close(fd);
6098
-		return -1;
6099
-	}
6100
-
6101
-	ret = (cli_memstr(ptr, size, sockName, strlen(sockName)) != NULL) ? 1 : -1;
6102
-
6103
-	munmap(ptr, size);
6104
-	close(fd);
6105
-
6106
-	return ret;
6107
-#else	/*!HAVE_MMAP*/
225
+	freecfg(copt);
6108 226
 	return 1;
6109
-#endif
6110
-}
6111
-
6112
-/*
6113
- * If the given email address is whitelisted don't scan emails to them,
6114
- *	the addresses are in angle brackets e.g. <foo@bar.com>.
6115
- *
6116
- * TODO: Allow regular expressions in the addresses
6117
- * TODO: Syntax check the contents of the files
6118
- * TODO: Allow emails of the form "name <address>"
6119
- * TODO: Allow emails not of the form "<address>", i.e. no angle brackets
6120
- * TODO: Assume that if a '@' is missing from the address, that all emails
6121
- *	to that domain are to be whitelisted
6122
- */
6123
-static int
6124
-isWhitelisted(const char *emailaddress, int to)
6125
-{
6126
-	static table_t *to_whitelist, *from_whitelist;	/* never freed */
6127
-	table_t *table;
6128
-
6129
-	logg("*isWhitelisted %s\n", emailaddress);
6130
-
6131
-	/*
6132
-	 * Don't scan messages to the quarantine email address
6133
-	 */
6134
-	if(quarantine && (strcasecmp(quarantine, emailaddress) == 0))
6135
-		return 1;
6136
-
6137
-	if((to_whitelist == NULL) && whitelistFile) {
6138
-		FILE *fin;
6139
-		char buf[BUFSIZ + 1];
6140
-
6141
-		fin = fopen(whitelistFile, "r");
6142
-
6143
-		if(fin == NULL) {
6144
-			perror(whitelistFile);
6145
-			logg(_("!Can't open whitelist file %s"), whitelistFile);
6146
-			return 0;
6147
-		}
6148
-		to_whitelist = tableCreate();
6149
-		from_whitelist = tableCreate();
6150
-
6151
-		if((to_whitelist == NULL) || (from_whitelist == NULL)) {
6152
-			logg(_("!Can't create whitelist table"));
6153
-			if(to_whitelist) {
6154
-				tableDestroy(to_whitelist);
6155
-				to_whitelist = NULL;
6156
-			} else {
6157
-				tableDestroy(from_whitelist);
6158
-				from_whitelist = NULL;
6159
-			}
6160
-			fclose(fin);
6161
-			return 0;
6162
-		}
6163
-
6164
-		while(fgets(buf, sizeof(buf), fin) != NULL) {
6165
-			const char *ptr;
6166
-
6167
-			/* comment line? */
6168
-			switch(buf[0]) {
6169
-				case '#':
6170
-				case '/':
6171
-				case ':':
6172
-					continue;
6173
-			}
6174
-			if(cli_chomp(buf) > 0) {
6175
-				if((ptr = strchr(buf, ':')) != NULL) {
6176
-					do
6177
-						ptr++;
6178
-					while(*ptr && isspace(*ptr));
6179
-
6180
-					if(*ptr == '\0') {
6181
-						logg("*Ignoring bad line '%s'\n",
6182
-							buf);
6183
-						continue;
6184
-					}
6185
-				} else
6186
-					ptr = buf;
6187
-
6188
-				if(strncasecmp(buf, "From:", 5) == 0)
6189
-					table = from_whitelist;
6190
-				else
6191
-					table = to_whitelist;
6192
-
6193
-				(void)tableInsert(table, ptr, 1);
6194
-			}
6195
-		}
6196
-		fclose(fin);
6197
-	}
6198
-	table = (to) ? to_whitelist : from_whitelist;
6199
-
6200
-	if(table && (tableFind(table, emailaddress) == 1))
6201
-		/*
6202
-		 * This recipient is on the whitelist
6203
-		 */
6204
-		return 1;
227
+    }
6205 228
 
6206
-	return 0;
6207
-}
6208
-
6209
-/*
6210
- * Blacklist IP addresses that send malware. Often in the phishing world, one
6211
- * phish is quickly followed by another. IP addresses are blacklisted for one
6212
- * minute. We can't blacklist for longer since DHCP means we could hit innocent
6213
- * parties, and in theory malware could go through a smart host and affect
6214
- * innocent parties
6215
- *
6216
- * Note that sites which can't be blacklisted will have their timestamp set
6217
- * to 0, since that can never be less than blacklist_time seconds from now
6218
- */
6219
-static int
6220
-isBlacklisted(const char *ip_address)
6221
-{
6222
-	time_t t;
6223
-
6224
-	if(blacklist_time == 0)
6225
-		/* Blacklisting not being used */
6226
-		return 0;
6227
-
6228
-	logg("*isBlacklisted %s\n", ip_address);
6229
-
6230
-	if(isLocal(ip_address))
6231
-		return 0;
6232
-
6233
-	pthread_mutex_lock(&blacklist_mutex);
6234
-	if(blacklist == NULL) {
6235
-		blacklist = tableCreate();
6236
-
6237
-		pthread_mutex_unlock(&blacklist_mutex);
6238
-
6239
-		if(blacklist == NULL)
6240
-			logg(_("!Can't create blacklist table"));
6241
-		return 0;
6242
-	}
6243
-	t = tableFind(blacklist, ip_address);
6244
-	pthread_mutex_unlock(&blacklist_mutex);
6245
-
6246
-	if(t == (time_t)-1)
6247
-		/* IP address is not blacklisted */
6248
-		return 0;
6249
-
6250
-	if(t == (time_t)0)
6251
-		/* IP cannot be blacklisted */
6252
-		return 0;
6253
-
6254
-	if((time((time_t *)0) - t) <= blacklist_time)
6255
-		return 1;
6256
-
6257
-	/* timedout: remove the IP from the blacklist */
6258
-	pthread_mutex_lock(&blacklist_mutex);
6259
-	tableRemove(blacklist, ip_address);
6260
-	pthread_mutex_unlock(&blacklist_mutex);
6261
-
6262
-	return 0;
6263
-}
6264
-
6265
-#ifdef	HAVE_RESOLV_H
6266
-/*
6267
- * Determine our MX peers, they must never be blacklisted
6268
- * See RFC1034 for the definition of the record formats
6269
- *
6270
- * This is only ever called once, which is wrong, but the overheard of calling
6271
- * this from the watchdog isn't worth it
6272
- */
6273
-static table_t *
6274
-mx(const char *host, table_t *t)
6275
-{
6276
-	u_char *p, *end;
6277
-	const HEADER *hp;
6278
-	int len, i;
6279
-	union {
6280
-		HEADER h;
6281
-		u_char u[PACKETSZ];
6282
-	} q;
6283
-	char buf[BUFSIZ];
6284
-
6285
-	if(t == NULL) {
6286
-		t = tableCreate();
6287
-
6288
-		if(t == NULL)
6289
-			return NULL;
6290
-	}
6291
-
6292
-	len = safe_res_query(host, C_IN, T_MX, (u_char *)&q, sizeof(q));
6293
-	if(len < 0)
6294
-		return t;	/* Host has no MX records */
6295
-
6296
-	if((unsigned int)len > sizeof(q))
6297
-		return t;
6298
-
6299
-	hp = &(q.h);
6300
-	p = q.u + HFIXEDSZ;
6301
-	end = q.u + len;
6302
-
6303
-	for(i = ntohs(hp->qdcount); i--; p += len + QFIXEDSZ)
6304
-		if((len = dn_skipname(p, end)) < 0)
6305
-			return t;
6306
-
6307
-	i = ntohs(hp->ancount);
6308
-
6309
-	while((--i >= 0) && (p < end)) {
6310
-		in_addr_t addr;
6311
-		u_short type, pref;
6312
-		u_long ttl;	/* unused */
6313
-
6314
-		if((len = dn_expand(q.u, end, p, buf, sizeof(buf) - 1)) < 0)
6315
-			break;
6316
-		p += len;
6317
-		GETSHORT(type, p);
6318
-		p += INT16SZ;
6319
-		GETLONG(ttl, p);
6320
-		GETSHORT(len, p);
6321
-		if(type != T_MX) {
6322
-			p += len;
6323
-			continue;
6324
-		}
6325
-		GETSHORT(pref, p);
6326
-		if((len = dn_expand(q.u, end, p, buf, sizeof(buf) - 1)) < 0)
6327
-			break;
6328
-		p += len;
6329
-		addr = inet_addr(buf);
6330
-#ifdef	INADDR_NONE
6331
-		if(addr != INADDR_NONE) {
6332
-#else
6333
-		if(addr != (in_addr_t)-1) {
6334
-#endif
6335
-			(void)tableInsert(t, buf, 0);
6336
-		} else
6337
-			t = resolve(buf, t);
6338
-	}
6339
-	return t;
6340
-}
6341
-
6342
-/*
6343
- * If the MX record points to a name, we need to resolve that name. This routine
6344
- * does that
6345
- */
6346
-static table_t *
6347
-resolve(const char *host, table_t *t)
6348
-{
6349
-	u_char *p, *end;
6350
-	const HEADER *hp;
6351
-	int len, i;
6352
-	union {
6353
-		HEADER h;
6354
-		u_char u[PACKETSZ];
6355
-	} q;
6356
-	char buf[BUFSIZ];
6357
-
6358
-	if((host == NULL) || (*host == '\0'))
6359
-		return t;
6360
-
6361
-	len = safe_res_query(host, C_IN, T_A, (u_char *)&q, sizeof(q));
6362
-	if(len < 0)
6363
-		return t;	/* Host has no A records */
6364
-
6365
-	if((unsigned int)len > sizeof(q))
6366
-		return t;
6367
-
6368
-	hp = &(q.h);
6369
-	p = q.u + HFIXEDSZ;
6370
-	end = q.u + len;
6371
-
6372
-	for(i = ntohs(hp->qdcount); i--; p += len + QFIXEDSZ)
6373
-		if((len = dn_skipname(p, end)) < 0)
6374
-			return t;
6375
-
6376
-	i = ntohs(hp->ancount);
6377
-
6378
-	while((--i >= 0) && (p < end)) {
6379
-		u_short type;
6380
-		u_long ttl;
6381
-		const char *ip;
6382
-		struct in_addr addr;
6383
-
6384
-		if((len = dn_expand(q.u, end, p, buf, sizeof(buf) - 1)) < 0)
6385
-			return t;
6386
-		p += len;
6387
-		GETSHORT(type, p);
6388
-		p += INT16SZ;
6389
-		GETLONG(ttl, p);	/* unused */
6390
-		GETSHORT(len, p);
6391
-		if(type != T_A) {
6392
-			p += len;
6393
-			continue;
6394
-		}
6395
-		memcpy(&addr, p, sizeof(struct in_addr));
6396
-		p += 4;	/* Should check len == 4 */
6397
-		ip = inet_ntoa(addr);
6398
-		if(ip) {
6399
-			if(t == NULL) {
6400
-				t = tableCreate();
6401
-
6402
-				if(t == NULL)
6403
-					return NULL;
6404
-			}
6405
-			(void)tableInsert(t, ip, 0);
6406
-		}
6407
-	}
6408
-	return t;
6409
-}
6410
-
6411
-/*
6412
- * Validate SPF records to help to stop Phish false positives
6413
- * http://www.openspf.org/SPF_Record_Syntax
6414
- *
6415
- * Currently only handles ip4, a and mx fields in the DNS record
6416
- * Having said that, this is NOT a replacement for spf-milter, it is NOT
6417
- *	an SPF system, we ONLY use SPF records to reduce phish false positives
6418
- * TODO: IPv6?
6419
- * TODO: cache queries?
6420
- *
6421
- * INPUT: prevhosts, a list of hosts already searched: stops include loops
6422
- *	e.g. mercado.com includes medrcadosw.com which includes mercado.com,
6423
- *	causing a loop
6424
- * Return 1 if SPF says this email is from a legitimate source
6425
- *	0 for fail or unknown
6426
- */
6427
-static int
6428
-spf(struct privdata *privdata, table_t *prevhosts)
6429
-{
6430
-	char *host, *ptr;
6431
-	u_char *p, *end;
6432
-	const HEADER *hp;
6433
-	int len, i;
6434
-	union {
6435
-		HEADER h;
6436
-		u_char u[PACKETSZ];
6437
-	} q;
6438
-	char buf[BUFSIZ];
6439
-
6440
-	if(privdata->spf_ok)
6441
-		return 1;
6442
-	if(privdata->ip[0] == '\0')
6443
-		return 0;
6444
-	if(strcmp(privdata->ip, "127.0.0.1") == 0) {
6445
-		/* Loopback always pass SPF */
6446
-		privdata->spf_ok = 1;
6447
-		return 1;
6448
-	}
6449
-	if(isLocal(privdata->ip)) {
6450
-		/* Local addresses always pass SPF */
6451
-		privdata->spf_ok = 1;
6452
-		return 1;
6453
-	}
6454
-
6455
-	if(privdata->from == NULL)
6456
-		return 0;
6457
-	if((host = strrchr(privdata->from, '@')) == NULL)
6458
-		return 0;
6459
-
6460
-	host = cli_strdup(++host);
6461
-
6462
-	if(host == NULL)
6463
-		return 0;
6464
-
6465
-	ptr = strchr(host, '>');
6466
-
6467
-	if(ptr)
6468
-		*ptr = '\0';
6469
-
6470
-	logg("*SPF query '%s'\n", host);
6471
-	len = safe_res_query(host, C_IN, T_TXT, (u_char *)&q, sizeof(q));
6472
-	if(len < 0) {
6473
-		free(host);
6474
-		return 0;	/* Host has no TXT records */
6475
-	}
6476
-
6477
-	if((unsigned int)len > sizeof(q)) {
6478
-		free(host);
6479
-		return 0;
6480
-	}
6481
-
6482
-	hp = &(q.h);
6483
-	p = q.u + HFIXEDSZ;
6484
-	end = q.u + len;
6485
-
6486
-	for(i = ntohs(hp->qdcount); i--; p += len + QFIXEDSZ)
6487
-		if((len = dn_skipname(p, end)) < 0) {
6488
-			free(host);
6489
-			return 0;
6490
-		}
6491
-
6492
-	i = ntohs(hp->ancount);
6493
-
6494
-	while((--i >= 0) && (p < end) && !privdata->spf_ok) {
6495
-		u_short type;
6496
-		u_long ttl;
6497
-		char txt[BUFSIZ];
6498
-
6499
-		if((len = dn_expand(q.u, end, p, buf, sizeof(buf) - 1)) < 0) {
6500
-			free(host);
6501
-			return 0;
6502
-		}
6503
-		p += len;
6504
-		GETSHORT(type, p);
6505
-		p += INT16SZ;
6506
-		GETLONG(ttl, p);	/* unused */
6507
-		GETSHORT(len, p);
6508
-		if(type != T_TXT) {
6509
-			p += len;
6510
-			continue;
6511
-		}
6512
-		strncpy(txt, (const char *)&p[1], sizeof(txt) - 1);
6513
-		txt[sizeof(txt)-1]='\0';
6514
-		txt[len - 1] = '\0';
6515
-		if((strncmp(txt, "v=spf1 ", 7) == 0) || (strncmp(txt, "spf2.0/pra ", 11) == 0)) {
6516
-			int j;
6517
-			char *record;
6518
-			struct in_addr remote_ip;	/* IP connecting to us */
6519
-
6520
-			logg("*%s(%s): SPF record %s\n",
6521
-				host, privdata->ip, txt);
6522
-#ifdef HAVE_INET_NTOP
6523
-			/* IPv4 address ? */
6524
-			if(inet_pton(AF_INET, privdata->ip, &remote_ip) <= 0) {
6525
-				p += len;
6526
-				continue;
6527
-			}
6528
-#else
6529
-			if(inet_aton(privdata->ip, &remote_ip) == 0) {
6530
-				p += len;
6531
-				continue;
6532
-			}
6533
-#endif
6534
-
6535
-			j = 1;	/* strtok 0 would give the v= part */
6536
-			while((record = cli_strtok(txt, j++, " ")) != NULL) {
6537
-				if(strncmp(record, "ip4:", 4) == 0) {
6538
-					int preflen;
6539
-					char *ip, *pref;
6540
-					uint32_t mask;
6541
-					struct in_addr spf_range;	/* acceptable range of IPs */
6542
-
6543
-					ip = &record[4];
6544
-
6545
-					pref = strchr(ip, '/');
6546
-					preflen = 32;
6547
-					if(pref) {
6548
-						*pref++ = '\0';
6549
-						if(*pref)
6550
-							preflen = atoi(pref);
6551
-					}
6552
-
6553
-#ifdef HAVE_INET_NTOP
6554
-					/* IPv4 address ? */
6555
-					if(inet_pton(AF_INET, ip, &spf_range) <= 0) {
6556
-						free(record);
6557
-						continue;
6558
-					}
6559
-#else
6560
-					if(inet_aton(ip, &spf_range) == 0) {
6561
-						free(record);
6562
-						continue;
6563
-					}
6564
-#endif
6565
-					mask = MAKEMASK(preflen);
6566
-					if((ntohl(remote_ip.s_addr) & mask) == (ntohl(spf_range.s_addr) & mask)) {
6567
-						if(privdata->subject)
6568
-							logg("#SPF ip4 pass (%s) %s is valid for %s\n",
6569
-								privdata->subject, ip, host);
6570
-						else
6571
-							logg("#SPF ip4 pass %s is valid for %s\n", ip, host);
6572
-						privdata->spf_ok = 1;
6573
-					}
6574
-				} else if(strcmp(record, "mx") == 0) {
6575
-					table_t *t = mx(host, NULL);
6576
-
6577
-					if(t) {
6578
-						tableIterate(t, spf_ip,
6579
-							(void *)privdata);
6580
-						tableDestroy(t);
6581
-					}
6582
-				} else if(strcmp(record, "a") == 0) {
6583
-					table_t *t = resolve(host, NULL);
6584
-
6585
-					if(t) {
6586
-						tableIterate(t, spf_ip,
6587
-							(void *)privdata);
6588
-						tableDestroy(t);
6589
-					}
6590
-				} else if(strncmp(record, "a:", 2) == 0) {
6591
-					const char *ahost = &record[2];
229
+    maxfilesize = cfgopt(copt, "MaxFileSize")->numarg;
230
+    readtimeout = cfgopt(copt, "ReadTimeout")->numarg;
6592 231
 
6593
-					if(*ahost && (strcmp(ahost, host) != 0)) {
6594
-						table_t *t = resolve(ahost, NULL);
6595
-
6596
-						if(t) {
6597
-							tableIterate(t, spf_ip,
6598
-								(void *)privdata);
6599
-							tableDestroy(t);
6600
-						}
6601
-					}
6602
-				} else if(strncmp(record, "mx:", 3) == 0) {
6603
-					const char *mxhost = &record[3];
6604
-
6605
-					if(*mxhost && (strcmp(mxhost, host) != 0)) {
6606
-						table_t *t = mx(mxhost, NULL);
6607
-
6608
-						if(t) {
6609
-							tableIterate(t, spf_ip,
6610
-								(void *)privdata);
6611
-							tableDestroy(t);
6612
-						}
6613
-					}
6614
-				} else if(strncmp(record, "include:", 8) == 0) {
6615
-					const char *inchost = &record[8];
6616
-
6617
-					/*
6618
-					 * Ensure we haven't already looked at
6619
-					 *	the host that's to be included
6620
-					 */
6621
-					if(*inchost &&
6622
-					   (strcmp(inchost, host) != 0) &&
6623
-					   (tableFind(prevhosts, inchost) == -1)) {
6624
-						char *real_from = privdata->from;
6625
-						privdata->from = cli_malloc(strlen(inchost) + 3);
6626
-						sprintf(privdata->from, "n@%s", inchost);
6627
-						tableInsert(prevhosts, host, 0);
6628
-						spf(privdata, prevhosts);
6629
-						free(privdata->from);
6630
-						privdata->from = real_from;
6631
-					}
6632
-				}
6633
-				free(record);
6634
-				if(privdata->spf_ok)
6635
-					break;
6636
-			}
6637
-		}
6638
-		p += len;
6639
-	}
6640
-	free(host);
6641
-
6642
-	return privdata->spf_ok;
6643
-}
6644
-
6645
-static void
6646
-spf_ip(char *ip, int zero, void *v)
6647
-{
6648
-	struct privdata *privdata = (struct privdata *)v;
6649
-
6650
-	if(strcmp(ip, privdata->ip) == 0) {
6651
-		if(privdata->subject)
6652
-			logg("#SPF mx/a pass (%s) %s\n", privdata->subject, ip);
6653
-		else
6654
-			logg("#SPF mx/a pass %s\n", ip);
6655
-		privdata->spf_ok = 1;
6656
-	}
6657
-}
6658
-
6659
-#else	/*!HAVE_RESOLV_H */
6660
-static table_t *
6661
-mx(const char *host, table_t *t)
6662
-{
6663
-	logg(_("^MX peers will not be immune from being blacklisted"));
6664
-
6665
-	if(blacklist == NULL)
6666
-		blacklist = tableCreate();
6667
-	return NULL;
6668
-}
6669
-#endif	/* HAVE_RESOLV_H */
6670
-
6671
-static sfsistat
6672
-black_hole(const struct privdata *privdata)
6673
-{
6674
-	int must_scan;
6675
-	char **to;
6676
-
6677
-	to = privdata->to;
6678
-	must_scan = (*to) ? 0 : 1;
6679
-
6680
-	for(; *to; to++) {
6681
-		pid_t pid, w;
6682
-		int pv[2], status;
6683
-		FILE *sendmail;
6684
-		char buf[BUFSIZ];
6685
-
6686
-		logg("*Calling \"%s -bv %s\"\n", SENDMAIL_BIN, *to);
6687
-
6688
-		if(pipe(pv) < 0) {
6689
-			perror("pipe");
6690
-			logg(_("!Can't create pipe\n"));
6691
-			must_scan = 1;
6692
-			break;
6693
-		}
6694
-		pid = fork();
6695
-		if(pid == 0) {
6696
-			close(1);
6697
-			close(pv[0]);
6698
-			dup2(pv[1], 1);
6699
-			close(pv[1]);
6700
-
6701
-			/*
6702
-			 * Avoid calling popen() since *to isn't trusted
6703
-			 */
6704
-			execl(SENDMAIL_BIN, "sendmail", "-bv", *to, NULL);
6705
-			perror(SENDMAIL_BIN);
6706
-			logg("Can't execl %s\n", SENDMAIL_BIN);
6707
-			_exit(errno ? errno : 1);
6708
-		}
6709
-		if(pid == -1) {
6710
-			perror("fork");
6711
-			logg(_("!Can't fork\n"));
6712
-			close(pv[0]);
6713
-			close(pv[1]);
6714
-			must_scan = 1;
6715
-			break;
6716
-		}
6717
-		close(pv[1]);
6718
-		sendmail = fdopen(pv[0], "r");
6719
-
6720
-		if(sendmail == NULL) {
6721
-			logg("fdopen failed\n");
6722
-			close(pv[0]);
6723
-			must_scan = 1;
6724
-			break;
6725
-		}
6726
-
6727
-		while(fgets(buf, sizeof(buf), sendmail) != NULL) {
6728
-			if(cli_chomp(buf) == 0)
6729
-				continue;
6730
-
6731
-			logg("*sendmail output: %s\n", buf);
6732
-
6733
-			if(strstr(buf, "... deliverable: mailer ")) {
6734
-				const char *p = strstr(buf, ", user ");
6735
-
6736
-				if(strcmp(&p[7], "/dev/null") != 0) {
6737
-					must_scan = 1;
6738
-					break;
6739
-				}
6740
-			}
6741
-		}
6742
-		fclose(sendmail);
6743
-
6744
-		status = -1;
6745
-		do
6746
-			w = wait(&status);
6747
-		while((w != pid) && (w != -1));
6748
-
6749
-		if(w == -1)
6750
-			status = -1;
6751
-		else
6752
-			status = WEXITSTATUS(status);
6753
-
6754
-		switch(status) {
6755
-			case EX_NOUSER:
6756
-			case EX_OK:
6757
-				break;
6758
-			default:
6759
-				logg(_("^Can't execute '%s' to expand '%s' (error %d)\n"),
6760
-					SENDMAIL_BIN, *to, WEXITSTATUS(status));
6761
-				must_scan = 1;
6762
-		}
6763
-		if(must_scan)
6764
-			break;
6765
-	}
6766
-	if(!must_scan) {
6767
-		/* All recipients map to /dev/null */
6768
-		to = privdata->to;
6769
-		if(*to)
6770
-			logg("Discarded, since all recipients (e.g. \"%s\") are /dev/null\n", *to);
6771
-		else
6772
-			logg("Discarded, since all recipients are /dev/null\n");
6773
-		return SMFIS_DISCARD;
6774
-	}
6775
-	return SMFIS_CONTINUE;
6776
-}
6777
-
6778
-/* See also libclamav/mbox.c */
6779
-static int
6780
-useful_header(const char *cmd)
6781
-{
6782
-	if(strcasecmp(cmd, "From") == 0)
6783
-		return 1;
6784
-	if(strcasecmp(cmd, "Received") == 0)
6785
-		return 1;
6786
-	if(strcasecmp(cmd, "Content-Type") == 0)
6787
-		return 1;
6788
-	if(strcasecmp(cmd, "Content-Transfer-Encoding") == 0)
6789
-		return 1;
6790
-	if(strcasecmp(cmd, "Content-Disposition") == 0)
6791
-		return 1;
6792
-	if(strcasecmp(cmd, "De") == 0)
6793
-		return 1;
6794
-
6795
-	return 0;
6796
-}
6797
-
6798
-static int
6799
-increment_connexions(void)
6800
-{
6801
-	if(max_children > 0) {
6802
-		int rc = 0;
6803
-
6804
-		pthread_mutex_lock(&n_children_mutex);
6805
-
6806
-		/*
6807
-		 * Wait a while since sendmail doesn't like it if we
6808
-		 * take too long replying. Effectively this means that
6809
-		 * max_children is more of a hint than a rule
6810
-		 */
6811
-		if(n_children >= max_children) {
6812
-			struct timespec timeout;
6813
-			struct timeval now;
6814
-			struct timezone tz;
6815
-
6816
-			logg((dont_wait) ?
6817
-					_("hit max-children limit (%u >= %u)\n") :
6818
-					_("hit max-children limit (%u >= %u): waiting for some to exit\n"),
6819
-				n_children, max_children);
6820
-
6821
-			if(dont_wait) {
6822
-				pthread_mutex_unlock(&n_children_mutex);
6823
-				return 0;
6824
-			}
6825
-			/*
6826
-			 * Wait for an amount of time for a child to go
6827
-			 *
6828
-			 * Use pthread_cond_timedwait rather than
6829
-			 * pthread_cond_wait since the sendmail which
6830
-			 * calls us will have a timeout that we don't
6831
-			 * want to exceed, stops sendmail getting
6832
-			 * fidgety.
6833
-			 *
6834
-			 * Patch from Damian Menscher
6835
-			 * <menscher@uiuc.edu> to ensure it wakes up
6836
-			 * when a child goes away
6837
-			 */
6838
-			gettimeofday(&now, &tz);
6839
-			do {
6840
-				logg(_("n_children %d: waiting %d seconds for some to exit\n"),
6841
-					n_children, child_timeout);
6842
-
6843
-				if(child_timeout == 0) {
6844
-					pthread_cond_wait(&n_children_cond, &n_children_mutex);
6845
-					rc = 0;
6846
-				} else {
6847
-					timeout.tv_sec = now.tv_sec + child_timeout;
6848
-					timeout.tv_nsec = 0;
6849
-
6850
-					rc = pthread_cond_timedwait(&n_children_cond, &n_children_mutex, &timeout);
6851
-				}
6852
-			} while((n_children >= max_children) && (rc != ETIMEDOUT));
6853
-			logg(_("Finished waiting, n_children = %d\n"), n_children);
6854
-		}
6855
-		n_children++;
6856
-
6857
-		logg("*>n_children = %d\n", n_children);
6858
-		pthread_mutex_unlock(&n_children_mutex);
232
+    cpool_init(copt);
233
+    if (!cp) {
234
+	logg("!Failed to init the socket pool\n");
235
+	logg_close();
236
+	freecfg(copt);
237
+	return 1;
238
+    }	
6859 239
 
6860
-		if(child_timeout && (rc == ETIMEDOUT))
6861
-			logg(_("Timeout waiting for a child to die\n"));
240
+    if(!cfgopt(copt, "Foreground")->enabled) {
241
+	if(daemonize() == -1) {
242
+	    logg("!daemonize() failed\n");
243
+	    cpool_free();
244
+	    logg_close();
245
+	    return 1;
6862 246
 	}
247
+	if(chdir("/") == -1)
248
+	    logg("^Can't change current working directory to root\n");
249
+    }
6863 250
 
6864
-	return 1;
6865
-}
251
+    freecfg(copt);
6866 252
 
6867
-static void
6868
-decrement_connexions(void)
6869
-{
6870
-	if(max_children > 0) {
6871
-		pthread_mutex_lock(&n_children_mutex);
6872
-		logg("*decrement_connexions: n_children = %d\n", n_children);
6873
-		/*
6874
-		 * Deliberately errs on the side of broadcasting too many times
6875
-		 */
6876
-		if(n_children > 0)
6877
-			if(--n_children == 0) {
6878
-				logg("*%s is idle\n", progname);
6879
-				if(pthread_cond_broadcast(&watchdog_cond) < 0)
6880
-					perror("pthread_cond_broadcast");
6881
-			}
6882
-#ifdef	CL_DEBUG
6883
-		logg("*pthread_cond_broadcast\n");
6884
-#endif
6885
-		if(pthread_cond_broadcast(&n_children_cond) < 0)
6886
-			perror("pthread_cond_broadcast");
6887
-		logg("*<n_children = %d\n", n_children);
6888
-		pthread_mutex_unlock(&n_children_mutex);
6889
-	}
6890
-}
253
+    ret = smfi_main();
6891 254
 
6892
-static void
6893
-dump_blacklist(char *key, int value, void *v)
6894
-{
6895
-	logg(_("Won't blacklist %s\n"), key);
255
+    logg_close();
256
+    cpool_free();
257
+    return ret;
6896 258
 }
6897
-
6898 259
 /*
6899
- * Non-blocking connect, based on an idea by Everton da Silva Marques
6900
- *	 <everton.marques@gmail.com>
6901
- * FIXME: There are lots of copies of this code :-(
260
+ * Local Variables:
261
+ * mode: c
262
+ * c-basic-offset: 4
263
+ * tab-width: 8
264
+ * End: 
265
+ * vim: set cindent smartindent autoindent softtabstop=4 shiftwidth=4 tabstop=8: 
6902 266
  */
6903
-static int
6904
-nonblock_connect(int sock, const struct sockaddr_in *sin, const char *hostname)
6905
-{
6906
-	int select_failures;	/* Max. of unexpected select() failures */
6907
-	int attempts;
6908
-	struct timeval timeout;	/* When we should time out */
6909
-	int numfd;		/* Highest fdset fd plus 1 */
6910
-	long flags;
6911
-
6912
-	gettimeofday(&timeout, 0);	/* store when we started to connect */
6913
-
6914
-	if(hostname == NULL)
6915
-		hostname = "clamav-milter";	/* It's only used in debug messages */
6916
-
6917
-#ifdef	F_GETFL
6918
-	flags = fcntl(sock, F_GETFL, 0);
6919
-
6920
-	if(flags == -1L)
6921
-		logg("^getfl: %s\n", strerror(errno));
6922
-	else if(fcntl(sock, F_SETFL, (long)(flags | O_NONBLOCK)) < 0)
6923
-		logg("^setfl: %s\n", strerror(errno));
6924
-#else
6925
-	flags = -1L;
6926
-#endif
6927
-	if(connect(sock, (const struct sockaddr *)sin, sizeof(struct sockaddr_in)) != 0)
6928
-		switch(errno) {
6929
-			case EALREADY:
6930
-			case EINPROGRESS:
6931
-				logg("*%s: connect: %s\n", hostname,
6932
-					strerror(errno));
6933
-				break; /* wait for connection */
6934
-			case EISCONN:
6935
-				return 0; /* connected */
6936
-			default:
6937
-				logg("^%s: connect: %s\n", hostname,
6938
-					strerror(errno));
6939
-#ifdef	F_SETFL
6940
-				if(flags != -1L)
6941
-					if(fcntl(sock, F_SETFL, flags))
6942
-						logg("^f_setfl: %s\n", strerror(errno));
6943
-#endif
6944
-				return -1; /* failed */
6945
-		}
6946
-	else {
6947
-#ifdef	F_SETFL
6948
-		if(flags != -1L)
6949
-			if(fcntl(sock, F_SETFL, flags))
6950
-				logg("^f_setfl: %s\n", strerror(errno));
6951
-#endif
6952
-		return connect_error(sock, hostname);
6953
-	}
6954
-
6955
-	numfd = (int)sock + 1;
6956
-	select_failures = NONBLOCK_SELECT_MAX_FAILURES;
6957
-	attempts = 1;
6958
-	timeout.tv_sec += CONNECT_TIMEOUT;
6959
-
6960
-	for (;;) {
6961
-		int n, t;
6962
-		fd_set fds;
6963
-		struct timeval now, waittime;
6964
-
6965
-		/* Force timeout if we ran out of time */
6966
-		gettimeofday(&now, 0);
6967
-		t = (now.tv_sec == timeout.tv_sec) ?
6968
-			(now.tv_usec > timeout.tv_usec) :
6969
-			(now.tv_sec > timeout.tv_sec);
6970
-
6971
-		if(t) {
6972
-			logg("^%s: connect timeout (%d secs)\n",
6973
-				hostname, CONNECT_TIMEOUT);
6974
-			break;
6975
-		}
6976
-
6977
-		/* Calculate how long to wait */
6978
-		waittime.tv_sec = timeout.tv_sec - now.tv_sec;
6979
-		waittime.tv_usec = timeout.tv_usec - now.tv_usec;
6980
-		if(waittime.tv_usec < 0) {
6981
-			waittime.tv_sec--;
6982
-			waittime.tv_usec += 1000000;
6983
-		}
6984
-
6985
-		/* Init fds with 'sock' as the only fd */
6986
-		FD_ZERO(&fds);
6987
-		FD_SET(sock, &fds);
6988
-
6989
-		n = select(numfd, 0, &fds, 0, &waittime);
6990
-		if(n < 0) {
6991
-			logg("^%s: select attempt %d %s\n",
6992
-				hostname, select_failures, strerror(errno));
6993
-			if(--select_failures >= 0)
6994
-				continue; /* not timed-out, try again */
6995
-			break; /* failed */
6996
-		}
6997
-
6998
-		logg("*%s: select = %d\n", hostname, n);
6999
-
7000
-		if(n) {
7001
-#ifdef	F_SETFL
7002
-			if(flags != -1L)
7003
-				if(fcntl(sock, F_SETFL, flags))
7004
-					logg("^f_setfl: %s\n", strerror(errno));
7005
-#endif
7006
-			return connect_error(sock, hostname);
7007
-		}
7008
-
7009
-		/* timeout */
7010
-		if(attempts++ == NONBLOCK_MAX_ATTEMPTS) {
7011
-			logg("^timeout connecting to %s\n", hostname);
7012
-			break;
7013
-		}
7014
-	}
7015
-
7016
-#ifdef	F_SETFL
7017
-	if(flags != -1L)
7018
-		if(fcntl(sock, F_SETFL, flags))
7019
-			logg("^f_setfl: %s\n", strerror(errno));
7020
-#endif
7021
-	return -1; /* failed */
7022
-}
7023
-
7024
-static int
7025
-connect_error(int sock, const char *hostname)
7026
-{
7027
-#ifdef	SO_ERROR
7028
-	int optval;
7029
-	socklen_t optlen = sizeof(optval);
7030
-
7031
-	getsockopt(sock, SOL_SOCKET, SO_ERROR, &optval, &optlen);
7032
-
7033
-	if(optval) {
7034
-		logg("^%s: %s\n", hostname, strerror(optval));
7035
-		return -1;
7036
-	}
7037
-#endif
7038
-	return 0;
7039
-}
7040 267
new file mode 100644
... ...
@@ -0,0 +1,6951 @@
0
+/*
1
+ * clamav-milter.c
2
+ *	.../clamav-milter/clamav-milter.c
3
+ *
4
+ *  Copyright (C) 2003-2007 Nigel Horne <njh@bandsman.co.uk>
5
+ *
6
+ *  This program is free software; you can redistribute it and/or modify
7
+ *  it under the terms of the GNU General Public License as published by
8
+ *  the Free Software Foundation; either version 2 of the License, or
9
+ *  (at your option) any later version.
10
+ *
11
+ *  This program is distributed in the hope that it will be useful,
12
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ *  GNU General Public License for more details.
15
+ *
16
+ *  You should have received a copy of the GNU General Public License
17
+ *  along with this program; if not, write to the Free Software
18
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
+ *  MA 02110-1301, USA.
20
+ *
21
+ * Install into /usr/local/sbin/clamav-milter
22
+ * See http://www.elandsys.com/resources/sendmail/libmilter/overview.html
23
+ *
24
+ * For installation instructions see the file INSTALL that came with this file
25
+ *
26
+ * NOTE: first character of strings to logg():
27
+ *	! Error
28
+ *	^ Warning
29
+ *	* Verbose
30
+ *	# Info, but not logged in foreground
31
+ *	Default Info
32
+ */
33
+static	char	const	rcsid[] = "$Id: clamav-milter.c,v 1.312 2007/02/12 22:24:21 njh Exp $";
34
+
35
+#if HAVE_CONFIG_H
36
+#include "clamav-config.h"
37
+#endif
38
+
39
+#include "cfgparser.h"
40
+#include "target.h"
41
+#include "str.h"
42
+#include "../libclamav/others.h"
43
+#include "output.h"
44
+#include "clamav.h"
45
+#include "table.h"
46
+#include "network.h"
47
+#include "misc.h"
48
+
49
+#include <stdio.h>
50
+#include <sysexits.h>
51
+#ifdef	HAVE_SYS_STAT_H
52
+#include <sys/stat.h>
53
+#endif
54
+#if	HAVE_STDLIB_H
55
+#include <stdlib.h>
56
+#endif
57
+#if	HAVE_MEMORY_H
58
+#include <memory.h>
59
+#endif
60
+#if	HAVE_STRING_H
61
+#include <string.h>
62
+#endif
63
+#ifdef HAVE_STRINGS_H
64
+#include <strings.h>
65
+#endif
66
+#include <sys/wait.h>
67
+#include <assert.h>
68
+#include <sys/socket.h>
69
+#include <netinet/in.h>
70
+#include <net/if.h>
71
+#include <arpa/inet.h>
72
+#include <sys/un.h>
73
+#include <stdarg.h>
74
+#include <errno.h>
75
+#if	HAVE_LIBMILTER_MFAPI_H
76
+#include <libmilter/mfapi.h>
77
+#endif
78
+#include <pthread.h>
79
+#include <sys/time.h>
80
+#include <sys/resource.h>
81
+#include <signal.h>
82
+#include <fcntl.h>
83
+#include <pwd.h>
84
+#include <grp.h>
85
+#if	HAVE_SYS_PARAM_H
86
+#include <sys/param.h>
87
+#endif
88
+#if	HAVE_RESOLV_H
89
+#include <arpa/nameser.h>	/* for HEADER */
90
+#include <resolv.h>
91
+#endif
92
+#ifdef	HAVE_UNISTD_H
93
+#include <unistd.h>
94
+#endif
95
+#include <ctype.h>
96
+
97
+#if HAVE_MMAP
98
+#if HAVE_SYS_MMAN_H
99
+#include <sys/mman.h>
100
+#else /* HAVE_SYS_MMAN_H */
101
+#undef HAVE_MMAP
102
+#endif
103
+#endif
104
+
105
+#define NONBLOCK_SELECT_MAX_FAILURES	3
106
+#define NONBLOCK_MAX_ATTEMPTS	10
107
+#define	CONNECT_TIMEOUT	5	/* Allow 5 seconds to connect */
108
+
109
+#ifdef	C_LINUX
110
+#include <sys/sendfile.h>	/* FIXME: use sendfile on BSD not Linux */
111
+#include <libintl.h>
112
+#include <locale.h>
113
+
114
+#define	gettext_noop(s)	s
115
+#define	_(s)	gettext(s)
116
+#define	N_(s)	gettext_noop(s)
117
+
118
+#else
119
+
120
+#define	_(s)	s
121
+#define	N_(s)	s
122
+
123
+#endif
124
+
125
+#ifdef	USE_SYSLOG
126
+#include <syslog.h>
127
+#endif
128
+
129
+#ifdef	WITH_TCPWRAP
130
+#if	HAVE_TCPD_H
131
+#include <tcpd.h>
132
+#endif
133
+
134
+int	allow_severity = LOG_DEBUG;
135
+int	deny_severity = LOG_NOTICE;
136
+#endif
137
+
138
+#ifdef	CL_DEBUG
139
+static	char	console[] = "/dev/console";
140
+#endif
141
+
142
+#if defined(CL_DEBUG) && defined(C_LINUX)
143
+#include <sys/resource.h>
144
+#endif
145
+
146
+#define _GNU_SOURCE
147
+#include <getopt.h>
148
+
149
+#ifndef	SENDMAIL_BIN
150
+#define	SENDMAIL_BIN	"/usr/lib/sendmail"
151
+#endif
152
+
153
+#ifndef HAVE_IN_PORT_T
154
+typedef	unsigned short	in_port_t;
155
+#endif
156
+
157
+#ifndef	HAVE_IN_ADDR_T
158
+typedef	unsigned int	in_addr_t;
159
+#endif
160
+
161
+#ifndef	INET6_ADDRSTRLEN
162
+#ifdef	AF_INET6
163
+#define	INET6_ADDRSTRLEN	40
164
+#else
165
+#define	INET6_ADDRSTRLEN	16
166
+#endif
167
+#endif
168
+
169
+#ifndef	EX_CONFIG	/* HP-UX */
170
+#define	EX_CONFIG	78
171
+#endif
172
+
173
+#define	VERSION_LENGTH	128
174
+#define	DEFAULT_TIMEOUT	120
175
+
176
+#define	NTRIES	5	/* How many times we try to connect to a clamd */
177
+
178
+/*#define	SESSION*/
179
+		/* Keep one command connexion open to clamd, otherwise a new
180
+		 * command connexion is created for each new email
181
+		 *
182
+		 * FIXME: When SESSIONS are open, freshclam can hang when
183
+		 *	notfying clamd of an update. This is most likely to be a
184
+		 *	problem with the implementation of SESSIONS on clamd.
185
+		 *	The problem seems worst on BSD.
186
+		 *
187
+		 * Note that clamd is buggy and can hang or even crash if you
188
+		 *	send SESSION command so be aware
189
+		 */
190
+
191
+/*
192
+ * TODO: optional: xmessage on console when virus stopped (SNMP would be real nice!)
193
+ *	Having said that, with LogSysLog you can (on Linux) configure the system
194
+ *	to get messages on the system console, see syslog.conf(5), also you
195
+ *	can use wall(1) in the VirusEvent entry in clamd.conf
196
+ * TODO: Decide action (bounce, discard, reject etc.) based on the virus
197
+ *	found. Those with faked addresses, such as SCO.A want discarding,
198
+ *	others could be bounced properly.
199
+ * TODO: Encrypt mails sent to clamd to stop sniffers. Sending by UNIX domain
200
+ *	sockets is better
201
+ * TODO: Load balancing, allow local machine to talk via UNIX domain socket.
202
+ * TODO: allow each To: line in the whitelist file to specify a quarantine email
203
+ *	address
204
+ * TODO: optionally use zlib to compress data sent to remote hosts
205
+ * TODO: Finish IPv6 support (serverIPs array and SPF are IPv4 only)
206
+ * TODO: Check domainkeys as well as SPF for phish false positives
207
+ */
208
+
209
+struct header_node_t {
210
+	char	*header;
211
+	struct	header_node_t *next;
212
+};
213
+
214
+struct header_list_struct {
215
+	struct	header_node_t *first;
216
+	struct	header_node_t *last;
217
+};
218
+
219
+typedef struct header_list_struct *header_list_t;
220
+
221
+/*
222
+ * Local addresses are those not scanned if --local is not set
223
+ * 127.0.0.0 is not in this table since that's goverend by --outgoing
224
+ * Andy Fiddaman <clam@fiddaman.net> added 169.254.0.0/16
225
+ *	(Microsoft default DHCP)
226
+ * TODO: compare this with RFC1918/RFC3330
227
+ */
228
+#define PACKADDR(a, b, c, d) (((uint32_t)(a) << 24) | ((b) << 16) | ((c) << 8) | (d))
229
+#define MAKEMASK(bits)	((uint32_t)(0xffffffff << (32 - bits)))
230
+
231
+static struct cidr_net {	/* don't make this const because of -I flag */
232
+	uint32_t	base;
233
+	uint32_t	mask;
234
+} localNets[] = {
235
+	/*{ PACKADDR(127,   0,   0,   0), MAKEMASK(8) },	*   127.0.0.0/8 */
236
+	{ PACKADDR(192, 168,   0,   0), MAKEMASK(16) },	/* 192.168.0.0/16 - RFC3330 */
237
+	/*{ PACKADDR(192, 18,   0,   0), MAKEMASK(15) },	* 192.18.0.0/15 - RFC2544 */
238
+	/*{ PACKADDR(192, 0,   2,   0), MAKEMASK(24) },	* 192.0.2.0/24 - RFC3330 */
239
+	{ PACKADDR( 10,   0,   0,   0), MAKEMASK(8) },	/*    10.0.0.0/8 */
240
+	{ PACKADDR(172,  16,   0,   0), MAKEMASK(12) },	/*  172.16.0.0/12 */
241
+	{ PACKADDR(169, 254,   0,   0), MAKEMASK(16) },	/* 169.254.0.0/16 */
242
+	{ 0, 0 },	/* space to put eight more via -I addr */
243
+	{ 0, 0 },
244
+	{ 0, 0 },
245
+	{ 0, 0 },
246
+	{ 0, 0 },
247
+	{ 0, 0 },
248
+	{ 0, 0 },
249
+	{ 0, 0 },
250
+	{ 0, 0 }
251
+};
252
+#define IFLAG_MAX 8
253
+
254
+#ifdef	AF_INET6
255
+typedef struct cidr_net6 {
256
+	struct in6_addr	base;
257
+	int preflen;
258
+} cidr_net6;
259
+static	cidr_net6	localNets6[IFLAG_MAX];
260
+static	int	localNets6_cnt;
261
+#endif
262
+
263
+/*
264
+ * Each libmilter thread has one of these
265
+ */
266
+struct	privdata {
267
+	char	*from;	/* Who sent the message */
268
+	char	*subject;	/* Original subject */
269
+	char	*sender;	/* Secretary - often used in mailing lists */
270
+	char	**to;	/* Who is the message going to */
271
+	char	ip[INET6_ADDRSTRLEN];	/* IP address of the other end */
272
+	int	numTo;	/* Number of people the message is going to */
273
+#ifndef	SESSION
274
+	int	cmdSocket;	/*
275
+				 * Socket to send/get commands e.g. PORT for
276
+				 * dataSocket
277
+				 */
278
+#endif
279
+	int	dataSocket;	/* Socket to send data to clamd */
280
+	char	*filename;	/* Where to store the message in quarantine */
281
+	u_char	*body;		/* body of the message if Sflag is set */
282
+	size_t	bodyLen;	/* number of bytes in body */
283
+	header_list_t headers;	/* Message headers */
284
+	long	numBytes;	/* Number of bytes sent so far */
285
+	char	*received;	/* keep track of received from */
286
+	const	char	*rejectCode;	/* 550 or 554? */
287
+	unsigned	int	discard:1;	/*
288
+				 * looks like the remote end is playing ping
289
+				 * pong with us
290
+				 */
291
+#ifdef	HAVE_RESOLV_H
292
+	unsigned	int	spf_ok:1;
293
+#endif
294
+	int	statusCount;	/* number of X-Virus-Status headers */
295
+	int	serverNumber;	/* Index into serverIPs */
296
+};
297
+
298
+#ifdef	SESSION
299
+static	int		createSession(unsigned int s);
300
+#else
301
+static	int		pingServer(int serverNumber);
302
+static	void		*try_server(void *var);
303
+static	int		active_servers(int *active);
304
+struct	try_server_struct {
305
+	int	sock;
306
+	int	rc;
307
+	struct	sockaddr_in *server;
308
+	int	server_index;
309
+};
310
+#endif
311
+static	int		findServer(void);
312
+static	sfsistat	clamfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr);
313
+#ifdef	CL_DEBUG
314
+static	sfsistat	clamfi_helo(SMFICTX *ctx, char *helostring);
315
+#endif
316
+static	sfsistat	clamfi_envfrom(SMFICTX *ctx, char **argv);
317
+static	sfsistat	clamfi_envrcpt(SMFICTX *ctx, char **argv);
318
+static	sfsistat	clamfi_header(SMFICTX *ctx, char *headerf, char *headerv);
319
+static	sfsistat	clamfi_eoh(SMFICTX *ctx);
320
+static	sfsistat	clamfi_body(SMFICTX *ctx, u_char *bodyp, size_t len);
321
+static	sfsistat	clamfi_eom(SMFICTX *ctx);
322
+static	sfsistat	clamfi_abort(SMFICTX *ctx);
323
+static	sfsistat	clamfi_close(SMFICTX *ctx);
324
+static	void		clamfi_cleanup(SMFICTX *ctx);
325
+static	void		clamfi_free(struct privdata *privdata, int keep);
326
+#ifdef __GNUC__
327
+static	int		clamfi_send(struct privdata *privdata, size_t len, const char *format, ...) __attribute__((format(printf, 3,4)));
328
+#else
329
+static	int		clamfi_send(struct privdata *privdata, size_t len, const char *format, ...);
330
+#endif
331
+static	long		clamd_recv(int sock, char *buf, size_t len);
332
+static	off_t		updateSigFile(void);
333
+static	header_list_t	header_list_new(void);
334
+static	void	header_list_free(header_list_t list);
335
+static	void	header_list_add(header_list_t list, const char *headerf, const char *headerv);
336
+static	void	header_list_print(header_list_t list, FILE *fp);
337
+static	int	connect2clamd(struct privdata *privdata);
338
+static	int	sendToFrom(struct privdata *privdata);
339
+static	int	checkClamd(int log_result);
340
+static	int	sendtemplate(SMFICTX *ctx, const char *filename, FILE *sendmail, const char *virusname);
341
+static	int	qfile(struct privdata *privdata, const char *sendmailId, const char *virusname);
342
+static	int	move(const char *oldfile, const char *newfile);
343
+static	void	setsubject(SMFICTX *ctx, const char *virusname);
344
+/*static	int	clamfi_gethostbyname(const char *hostname, struct hostent *hp, char *buf, size_t len);*/
345
+static	int	add_local_ip(char *address);
346
+static	int	isLocalAddr(in_addr_t addr);
347
+static	int	isLocal(const char *addr);
348
+static	void	clamdIsDown(void);
349
+static	void	*watchdog(void *a);
350
+static	int	check_and_reload_database(void);
351
+static	void	timeoutBlacklist(char *ip_address, int time_of_blacklist, void *v);
352
+static	void	quit(void);
353
+static	void	broadcast(const char *mess);
354
+static	int	loadDatabase(void);
355
+static	int	increment_connexions(void);
356
+static	void	decrement_connexions(void);
357
+static	void	dump_blacklist(char *key, int value, void *v);
358
+static	int	nonblock_connect(int sock, const struct sockaddr_in *sin, const char *hostname);
359
+static	int	connect_error(int sock, const char *hostname);
360
+
361
+#ifdef	SESSION
362
+static	pthread_mutex_t	version_mutex = PTHREAD_MUTEX_INITIALIZER;
363
+static	char	**clamav_versions;	/* max_children elements in the array */
364
+#define	clamav_version	(clamav_versions[0])
365
+#else
366
+static	char	clamav_version[VERSION_LENGTH + 1];
367
+#endif
368
+static	int	fflag = 0;	/* force a scan, whatever */
369
+static	int	oflag = 0;	/* scan messages from our machine? */
370
+static	int	lflag = 0;	/* scan messages from our site? */
371
+static	int	Iflag = 0;	/* Added an IP addr to localNets? */
372
+static	const	char	*progname;	/* our name - usually clamav-milter */
373
+
374
+/* Variables for --external */
375
+static	int	external = 0;	/* scan messages ourself or use clamd? */
376
+static	pthread_mutex_t	engine_mutex = PTHREAD_MUTEX_INITIALIZER;
377
+struct  cl_engine *engine = NULL;
378
+uint64_t maxscansize;
379
+uint64_t maxfilesize;
380
+uint32_t maxreclevel;
381
+uint32_t maxfiles;
382
+
383
+static	struct	cl_stat	dbstat;
384
+static	int	options = CL_SCAN_STDOPT;
385
+
386
+#ifdef	BOUNCE
387
+static	int	bflag = 0;	/*
388
+				 * send a failure (bounce) message to the
389
+				 * sender. This probably isn't a good idea
390
+				 * since most reply addresses will be fake
391
+				 *
392
+				 * TODO: Perhaps we can have an option to
393
+				 * bounce outgoing mail, but not incoming?
394
+				 */
395
+#endif
396
+static	const	char	*iface;	/*
397
+				 * Broadcast a message when a virus is found,
398
+				 * this allows remote network management
399
+				 */
400
+static	int	broadcastSock = -1;
401
+static	int	pflag = 0;	/*
402
+				 * Send a warning to the postmaster only,
403
+				 * this means user's won't be told when someone
404
+				 * sent them a virus
405
+				 */
406
+static	int	qflag = 0;	/*
407
+				 * Send no warnings when a virus is found,
408
+				 * this means that the only log of viruses
409
+				 * found is the syslog, so it's best to
410
+				 * enable LogSyslog in clamd.conf
411
+				 */
412
+static	int	Sflag = 0;	/*
413
+				 * Add a signature to each message that
414
+				 * has been scanned
415
+				 */
416
+static	const	char	*sigFilename;	/*
417
+				 * File where the scanned message signature
418
+				 * can be found
419
+				 */
420
+static	char	*quarantine;	/*
421
+				 * If a virus is found in an email redirect
422
+				 * it to this account
423
+				 */
424
+static	char	*quarantine_dir; /*
425
+				 * Path to store messages before scanning.
426
+				 * Infected ones will be left there.
427
+				 */
428
+static	int	nflag = 0;	/*
429
+				 * Don't add X-Virus-Scanned to header. Patch
430
+				 * from Dirk Meyer <dirk.meyer@dinoex.sub.org>
431
+				 */
432
+static	int	rejectmail = 1;	/*
433
+				 * Send a 550 rejection when a virus is
434
+				 * found
435
+				 */
436
+static	int	hflag = 0;	/*
437
+				 * Include original message headers in
438
+				 * report
439
+				 */
440
+static	int	cl_error = SMFIS_TEMPFAIL; /*
441
+				 * If an error occurs, return
442
+				 * this status. Allows messages
443
+				 * to be passed through
444
+				 * unscanned in the event of
445
+				 * an error. Patch from
446
+				 * Joe Talbott <josepht@cstone.net>
447
+				 */
448
+static	int	readTimeout = DEFAULT_TIMEOUT; /*
449
+				 * number of seconds to wait for clamd to
450
+				 * respond, see ReadTimeout in clamd.conf
451
+				 */
452
+static	long	streamMaxLength = 10*1024*1024;	/* StreamMaxLength from clamd.conf */
453
+static	int	logok = 0;	/*
454
+				 * Add clean items to the log file
455
+				 */
456
+static	const char	*signature = N_("-- \nScanned by ClamAv - http://www.clamav.net\n");
457
+static	time_t	signatureStamp;
458
+static	char	*templateFile;	/* e-mail to be sent when virus detected */
459
+static	char	*templateHeaders;	/* headers to be added to the above */
460
+static	const char	*tmpdir;
461
+
462
+#ifdef	CL_DEBUG
463
+static	int	debug_level = 0;
464
+#endif
465
+
466
+static	pthread_mutex_t	n_children_mutex = PTHREAD_MUTEX_INITIALIZER;
467
+static	pthread_cond_t	n_children_cond = PTHREAD_COND_INITIALIZER;
468
+static	int	n_children = 0;
469
+static	int	max_children = 0;
470
+static	unsigned	int	freshclam_monitor = 10;	/*
471
+							 * how often, in
472
+							 * seconds, to scan for
473
+							 * database updates
474
+							 */
475
+static	int	child_timeout = 300;	/* number of seconds to wait for
476
+					 * a child to die. Set to 0 to
477
+					 * wait forever
478
+					 */
479
+static	int	dont_wait = 0;	/*
480
+				 * If 1 send retry later to the remote end
481
+				 * if max_chilren is exceeded, otherwise we
482
+				 * wait for the number to go down
483
+				 */
484
+static	int	dont_sanitise = 0; /*
485
+				 * Don't check for ";" and "|" chars in 
486
+				 * email addresses.
487
+				 */
488
+static	int	advisory = 0;	/*
489
+				 * Run clamav-milter in advisory mode - viruses
490
+				 * are flagged rather than deleted. Incompatible
491
+				 * with quarantine options
492
+				 */
493
+static	int	detect_forged_local_address;	/*
494
+				 * for incoming only mail servers, drop emails
495
+				 * claiming to be from us that must be false
496
+				 * Requires that -o, -l or -f are NOT given
497
+				 */
498
+static	struct	cfgstruct	*copt;
499
+static	const	char	*localSocket;	/* milter->clamd comms */
500
+static	in_port_t	tcpSocket;	/* milter->clamd comms */
501
+static	char	*port = NULL;	/* sendmail->milter comms */
502
+
503
+static	const	char	*serverHostNames = "127.0.0.1";
504
+#if	HAVE_IN_ADDR_T
505
+static	in_addr_t	*serverIPs;	/* IPv4 only, in network byte order */
506
+#else
507
+static	long	*serverIPs;	/* IPv4 only, in network byte order */
508
+#endif
509
+static	int	numServers;	/* number of elements in serverIPs array */
510
+#ifndef	SESSION
511
+#define	RETRY_SECS	300	/* How often to retry a server that's down */
512
+static	time_t	*last_failed_pings;	/* For servers that are down. NB: not mutexed */
513
+#endif
514
+static	char	*rootdir;	/* for chroot */
515
+
516
+#ifdef	SESSION
517
+static	struct	session {
518
+	int	sock;	/* fd */
519
+	enum	{ CMDSOCKET_FREE, CMDSOCKET_INUSE, CMDSOCKET_DOWN }	status;
520
+} *sessions;	/* max_children elements in the array */
521
+static	pthread_mutex_t sstatus_mutex = PTHREAD_MUTEX_INITIALIZER;
522
+#endif	/*SESSION*/
523
+
524
+static	pthread_cond_t	watchdog_cond = PTHREAD_COND_INITIALIZER;
525
+
526
+#ifndef	SHUT_RD
527
+#define	SHUT_RD		0
528
+#endif
529
+#ifndef	SHUT_WR
530
+#define	SHUT_WR		1
531
+#endif
532
+
533
+static	const	char	*postmaster = "postmaster";
534
+static	const	char	*from = "MAILER-DAEMON";
535
+static	int	quitting;
536
+static	int	reload;	/* reload database when SIGUSR2 is received */
537
+static	const	char	*report;	/* Report Phishing to this address */
538
+static	const	char	*report_fps;	/* Report Phish FPs to this address */
539
+
540
+static	const	char	*whitelistFile;	/*
541
+					 * file containing destination email
542
+					 * addresses that we don't scan
543
+					 */
544
+static	const	char	*sendmailCF;	/* location of sendmail.cf to verify */
545
+static		int	checkCF = 1;
546
+static	const	char	*pidfile;
547
+static	int	black_hole_mode; /*
548
+				 * Since sendmail calls its milters before it
549
+				 * looks in /etc/aliases we can spend time
550
+				 * looking for malware that's going to be
551
+				 * thrown away even if the message is clean.
552
+				 * Enable this to not scan these messages.
553
+				 * Sadly, because these days sendmail -bv
554
+				 * only works as root, you can't use this with
555
+				 * the User directive, which some won't like
556
+				 * which also may contain the real target name
557
+				 *
558
+				 * smfi_getsymval(ctx, "{rcpt_addr}") only
559
+				 * handles virtuser, it doesn't also deref
560
+				 * the alias table, so it isn't any help
561
+				 */
562
+
563
+static	table_t	*blacklist;	/* never freed */
564
+static	int	blacklist_time;	/* How long to blacklist an IP */
565
+static	pthread_mutex_t	blacklist_mutex = PTHREAD_MUTEX_INITIALIZER;
566
+
567
+#ifdef	CL_DEBUG
568
+#if __GLIBC__ == 2 && __GLIBC_MINOR__ >= 1
569
+#define HAVE_BACKTRACE
570
+#endif
571
+#endif
572
+
573
+static	void	sigsegv(int sig);
574
+static	void	sigusr1(int sig);
575
+static	void	sigusr2(int sig);
576
+
577
+#ifdef HAVE_BACKTRACE
578
+#include <execinfo.h>
579
+
580
+static	void	print_trace(void);
581
+
582
+#define	BACKTRACE_SIZE	200
583
+
584
+#endif
585
+
586
+static	int	verifyIncomingSocketName(const char *sockName);
587
+static	int	isWhitelisted(const char *emailaddress, int to);
588
+static	int	isBlacklisted(const char *ip_address);
589
+static	table_t	*mx(const char *host, table_t *t);
590
+static	sfsistat	black_hole(const struct privdata *privdata);
591
+static	int	useful_header(const char *cmd);
592
+
593
+extern	short	logg_foreground;
594
+
595
+#ifdef HAVE_RESOLV_H
596
+static	table_t	*resolve(const char *host, table_t *t);
597
+static	int	spf(struct privdata *privdata, table_t *prevhosts);
598
+static	void	spf_ip(char *ip, int zero, void *v);
599
+
600
+pthread_mutex_t	res_pool_mutex = PTHREAD_MUTEX_INITIALIZER;
601
+
602
+#ifdef HAVE_LRESOLV_R
603
+res_state res_pool;
604
+uint8_t *res_pool_state;
605
+pthread_cond_t res_pool_cond = PTHREAD_COND_INITIALIZER;
606
+
607
+static int safe_res_query(const char *d, int c, int t, u_char *a, int l) {
608
+	int i = -1, ret;
609
+
610
+	pthread_mutex_lock(&res_pool_mutex);
611
+	while(i==-1) {
612
+		int j;
613
+		for(j=0; j<max_children+1; j++) {
614
+			if(!res_pool_state[j]) continue;
615
+			i = j;
616
+			break;
617
+		}
618
+		if(i!=-1) break;
619
+		pthread_cond_wait(&res_pool_cond, &res_pool_mutex);
620
+	}
621
+	res_pool_state[i]=0;
622
+	pthread_mutex_unlock(&res_pool_mutex);
623
+
624
+	ret = res_nquery(&res_pool[i], d, c, t, a, l);
625
+  
626
+	pthread_mutex_lock(&res_pool_mutex);
627
+	res_pool_state[i]=1;
628
+	pthread_cond_signal(&res_pool_cond);
629
+	pthread_mutex_unlock(&res_pool_mutex);
630
+	return ret;
631
+}
632
+
633
+#else /* !HAVE_LRESOLV_R - non thread safe resolver (old bsd's) */
634
+
635
+static int safe_res_query(const char *d, int c, int t, u_char *a, int l) {
636
+	int ret;
637
+	pthread_mutex_lock(&res_pool_mutex);
638
+	ret = res_query(d, c, t, a, l);
639
+	pthread_mutex_unlock(&res_pool_mutex);
640
+	return ret;
641
+}
642
+
643
+#endif /* HAVE_LRESOLV_R */
644
+
645
+#endif /* HAVE_RESOLV_H */
646
+
647
+static void
648
+help(void)
649
+{
650
+	printf("\n\tclamav-milter version %s\n", get_version());
651
+	puts("\tCopyright (C) 2007 Nigel Horne <njh@clamav.net>\n");
652
+
653
+	puts(_("\t--advisory\t\t-A\tFlag viruses rather than deleting them."));
654
+	puts(_("\t--blacklist-time=SECS\t-k\tTime (in seconds) to blacklist an IP."));
655
+	puts(_("\t--black-hole-mode\t\tDon't scan messages aliased to /dev/null."));
656
+#ifdef	BOUNCE
657
+	puts(_("\t--bounce\t\t-b\tSend a failure message to the sender."));
658
+#endif
659
+	puts(_("\t--broadcast\t\t-B [IFACE]\tBroadcast to a network manager when a virus is found."));
660
+	puts(_("\t--chroot=DIR\t\t-C DIR\tChroot to dir when starting."));
661
+	puts(_("\t--config-file=FILE\t-c FILE\tRead configuration from FILE."));
662
+	puts(_("\t--debug\t\t\t-D\tPrint debug messages."));
663
+	puts(_("\t--detect-forged-local-address\t-L\tReject mails that claim to be from us."));
664
+	puts(_("\t--dont-blacklist\t-K\tDon't blacklist a given IP."));
665
+	puts(_("\t--dont-scan-on-error\t-d\tPass e-mails through unscanned if a system error occurs."));
666
+	puts(_("\t--dont-wait\t\t\tAsk remote end to resend if max-children exceeded."));
667
+	puts(_("\t--dont-sanitise\t\t\tAllow semicolon and pipe characters in email addresses."));
668
+	puts(_("\t--external\t\t-e\tUse an external scanner (usually clamd)."));
669
+	puts(_("\t--freshclam-monitor=SECS\t-M SECS\tHow often to check for database update."));
670
+	puts(_("\t--from=EMAIL\t\t-a EMAIL\tError messages come from here."));
671
+	puts(_("\t--force-scan\t\t-f\tForce scan all messages (overrides (-o and -l)."));
672
+	puts(_("\t--help\t\t\t-h\tThis message."));
673
+	puts(_("\t--headers\t\t-H\tInclude original message headers in the report."));
674
+	puts(_("\t--ignore IPaddr\t\t-I IPaddr\tAdd IPaddr to LAN IP list (see --local)."));
675
+	puts(_("\t--local\t\t\t-l\tScan messages sent from machines on our LAN."));
676
+	puts(_("\t--max-childen\t\t-m\tMaximum number of concurrent scans."));
677
+	puts(_("\t--outgoing\t\t-o\tScan outgoing messages from this machine."));
678
+	puts(_("\t--noreject\t\t-N\tDon't reject viruses, silently throw them away."));
679
+	puts(_("\t--noxheader\t\t-n\tSuppress X-Virus-Scanned/X-Virus-Status headers."));
680
+	puts(_("\t--pidfile=FILE\t\t-i FILE\tLocation of pidfile."));
681
+	puts(_("\t--postmaster\t\t-p EMAIL\tPostmaster address [default=postmaster]."));
682
+	puts(_("\t--postmaster-only\t-P\tSend notifications only to the postmaster."));
683
+	puts(_("\t--quiet\t\t\t-q\tDon't send e-mail notifications of interceptions."));
684
+	puts(_("\t--quarantine=USER\t-Q EMAIL\tQuarantine e-mail account."));
685
+	puts(_("\t--report-phish=EMAIL\t-r EMAIL\tReport phish to this email address."));
686
+	puts(_("\t--report-phish-false-positives=EMAIL\t-R EMAIL\tReport phish false positves to this email address."));
687
+	puts(_("\t--quarantine-dir=DIR\t-U DIR\tDirectory to store infected emails."));
688
+	puts(_("\t--server=SERVER\t\t-s SERVER\tHostname/IP address of server(s) running clamd (when using TCPsocket)."));
689
+	puts(_("\t--sendmail-cf=FILE\t\tLocation of the sendmail.cf file to verify"));
690
+	puts(_("\t--no-check-cf\t\tSkip verification of sendmail.cf"));
691
+	puts(_("\t--sign\t\t\t-S\tAdd a hard-coded signature to each scanned message."));
692
+	puts(_("\t--signature-file=FILE\t-F FILE\tLocation of signature file."));
693
+	puts(_("\t--template-file=FILE\t-t FILE\tLocation of e-mail template file."));
694
+	puts(_("\t--template-headers=FILE\t\tLocation of e-mail headers for template file."));
695
+	puts(_("\t--timeout=SECS\t\t-T SECS\tTimeout waiting to childen to die."));
696
+	puts(_("\t--whitelist-file=FILE\t-W FILE\tLocation of the file of whitelisted addresses"));
697
+	puts(_("\t--version\t\t-V\tPrint the version number of this software."));
698
+#ifdef	CL_DEBUG
699
+	puts(_("\t--debug-level=n\t\t-x n\tSets the debug level to 'n'."));
700
+#endif
701
+	puts(_("\nFor more information type \"man clamav-milter\"."));
702
+	puts(_("For bug reports, please refer to http://www.clamav.net/bugs"));
703
+}
704
+
705
+extern char *optarg;
706
+int
707
+main(int argc, char **argv)
708
+{
709
+	int i, Bflag = 0, server = 0;
710
+	char *cfgfile = NULL;
711
+	const char *wont_blacklist = NULL;
712
+	const struct cfgstruct *cpt;
713
+	char version[VERSION_LENGTH + 1];
714
+	pthread_t tid;
715
+	struct rlimit rlim;
716
+#ifdef	CL_DEBUG
717
+	int consolefd;
718
+#endif
719
+
720
+	/*
721
+	 * The SMFI_VERSION checks are for Sendmail 8.14, which I don't have
722
+	 * yet, so I can't verify them
723
+	 * Patch from Andy Fiddaman <clam@fiddaman.net>
724
+	 */
725
+	struct smfiDesc smfilter = {
726
+		"ClamAv", /* filter name */
727
+		SMFI_VERSION,	/* version code -- leave untouched */
728
+		SMFIF_ADDHDRS|SMFIF_CHGHDRS,	/* flags - we add and delete headers */
729
+		clamfi_connect, /* connexion callback */
730
+#ifdef	CL_DEBUG
731
+		clamfi_helo,	/* HELO filter callback */
732
+#else
733
+		NULL,
734
+#endif
735
+		clamfi_envfrom, /* envelope sender filter callback */
736
+		clamfi_envrcpt, /* envelope recipient filter callback */
737
+		clamfi_header,	/* header filter callback */
738
+		clamfi_eoh,	/* end of header callback */
739
+		clamfi_body,	/* body filter callback */
740
+		clamfi_eom,	/* end of message callback */
741
+		clamfi_abort,	/* message aborted callback */
742
+		clamfi_close,	/* connexion cleanup callback */
743
+#if	SMFI_VERSION > 2
744
+		NULL,		/* Unrecognised command */
745
+#endif
746
+#if	SMFI_VERSION > 3
747
+		NULL,		/* DATA command callback */
748
+#endif
749
+#if	SMFI_VERSION >= 0x01000000
750
+		NULL,		/* Negotiation callback */
751
+#endif
752
+	};
753
+
754
+#if defined(CL_DEBUG) && defined(C_LINUX)
755
+	rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
756
+	if(setrlimit(RLIMIT_CORE, &rlim) < 0)
757
+		perror("setrlimit");
758
+#endif
759
+
760
+	/*
761
+	 * Temporarily enter guessed value into version, will
762
+	 * be overwritten later by the value returned by clamd
763
+	 */
764
+	snprintf(version, sizeof(version) - 1,
765
+		"ClamAV version %s, clamav-milter version %s",
766
+		cl_retver(), get_version());
767
+
768
+	progname = strrchr(argv[0], '/');
769
+	if(progname)
770
+		progname++;
771
+	else
772
+		progname = "clamav-milter";
773
+
774
+#ifdef	C_LINUX
775
+	setlocale(LC_ALL, "");
776
+	bindtextdomain(progname, DATADIR"/clamav-milter/locale");
777
+	textdomain(progname);
778
+#endif
779
+
780
+	for(;;) {
781
+		int opt_index = 0;
782
+#ifdef	BOUNCE
783
+#ifdef	CL_DEBUG
784
+		const char *args = "a:AbB:c:C:dDefF:I:i:k:K:lLm:M:nNop:PqQ:r:R:hHs:St:T:U:VwW:x:z0:1:2";
785
+#else
786
+		const char *args = "a:AbB:c:C:dDefF:I:i:k:K:lLm:M:nNop:PqQ:r:R:hHs:St:T:U:VwW:z0:1:2";
787
+#endif
788
+#else	/*!BOUNCE*/
789
+#ifdef	CL_DEBUG
790
+		const char *args = "a:AB:c:C:dDefF:I:i:k:K:lLm:M:nNop:PqQ:r:R:hHs:St:T:U:VwW:x:z0:1:2";
791
+#else
792
+		const char *args = "a:AB:c:C:dDefF:I:i:k:K:lLm:M:nNop:PqQ:r:R:hHs:St:T:U:VwW:z0:1:2";
793
+#endif
794
+#endif	/*BOUNCE*/
795
+
796
+		static struct option long_options[] = {
797
+			{
798
+				"from", 2, NULL, 'a'
799
+			},
800
+			{
801
+				"advisory", 0, NULL, 'A'
802
+			},
803
+#ifdef	BOUNCE
804
+			{
805
+				"bounce", 0, NULL, 'b'
806
+			},
807
+#endif
808
+			{
809
+				"broadcast", 2, NULL, 'B'
810
+			},
811
+			{
812
+				"config-file", 1, NULL, 'c'
813
+			},
814
+			{
815
+				"chroot", 1, NULL, 'C'
816
+			},
817
+			{
818
+				"detect-forged-local-address", 0, NULL, 'L'
819
+			},
820
+			{
821
+				"dont-blacklist", 1, NULL, 'K'
822
+			},
823
+			{
824
+				"dont-scan-on-error", 0, NULL, 'd'
825
+			},
826
+			{
827
+				"dont-wait", 0, NULL, 'w'
828
+			},
829
+			{
830
+				"dont-sanitise", 0, NULL, 'z'
831
+			},
832
+			{
833
+				"debug", 0, NULL, 'D'
834
+			},
835
+			{
836
+				"external", 0, NULL, 'e'
837
+			},
838
+			{
839
+				"force-scan", 0, NULL, 'f'
840
+			},
841
+			{
842
+				"headers", 0, NULL, 'H'
843
+			},
844
+			{
845
+				"help", 0, NULL, 'h'
846
+			},
847
+			{
848
+				"ignore", 1, NULL, 'I'
849
+			},
850
+			{
851
+				"pidfile", 1, NULL, 'i'
852
+			},
853
+			{
854
+				"blacklist-time", 1, NULL, 'k'
855
+			},
856
+			{
857
+				"local", 0, NULL, 'l'
858
+			},
859
+			{
860
+				"noreject", 0, NULL, 'N'
861
+			},
862
+			{
863
+				"noxheader", 0, NULL, 'n'
864
+			},
865
+			{
866
+				"outgoing", 0, NULL, 'o'
867
+			},
868
+			{
869
+				"postmaster", 1, NULL, 'p'
870
+			},
871
+			{
872
+				"postmaster-only", 0, NULL, 'P',
873
+			},
874
+			{
875
+				"quiet", 0, NULL, 'q'
876
+			},
877
+			{
878
+				"quarantine", 1, NULL, 'Q',
879
+			},
880
+			{
881
+				"report-phish", 1, NULL, 'r'
882
+			},
883
+			{
884
+				"report-phish-false-positives", 1, NULL, 'R'
885
+			},
886
+			{
887
+				"quarantine-dir", 1, NULL, 'U',
888
+			},
889
+			{
890
+				"max-children", 1, NULL, 'm'
891
+			},
892
+			{
893
+				"freshclam-monitor", 1, NULL, 'M'
894
+			},
895
+			{
896
+				"sendmail-cf", 1, NULL, '0'
897
+			},
898
+			{
899
+				"no-check-cf", 0, &checkCF, 0
900
+			},
901
+			{
902
+				"server", 1, NULL, 's'
903
+			},
904
+			{
905
+				"sign", 0, NULL, 'S'
906
+			},
907
+			{
908
+				"signature-file", 1, NULL, 'F'
909
+			},
910
+			{
911
+				"template-file", 1, NULL, 't'
912
+			},
913
+			{
914
+				"template-headers", 1, NULL, '1'
915
+			},
916
+			{
917
+				"timeout", 1, NULL, 'T'
918
+			},
919
+			{
920
+				"whitelist-file", 1, NULL, 'W'
921
+			},
922
+			{
923
+				"version", 0, NULL, 'V'
924
+			},
925
+			{
926
+				"black-hole-mode", 0, NULL, '2'
927
+			},
928
+#ifdef	CL_DEBUG
929
+			{
930
+				"debug-level", 1, NULL, 'x'
931
+			},
932
+#endif
933
+			{
934
+				NULL, 0, NULL, '\0'
935
+			}
936
+		};
937
+
938
+		int ret = getopt_long(argc, argv, args, long_options, &opt_index);
939
+
940
+		if(ret == -1)
941
+			break;
942
+  		else if(ret == 0)
943
+  			continue;
944
+
945
+		switch(ret) {
946
+			case 'a':	/* e-mail errors from here */
947
+				/*
948
+				 * optarg is optional - if you give --from
949
+				 * then the --from is set to the orginal,
950
+				 * probably forged, email address
951
+				 */
952
+				from = optarg;
953
+				break;
954
+			case 'A':
955
+				advisory++;
956
+				break;
957
+#ifdef	BOUNCE
958
+			case 'b':	/* bounce worms/viruses */
959
+				bflag++;
960
+				break;
961
+#endif
962
+			case 'B':	/* broadcast */
963
+				Bflag++;
964
+				if(optarg)
965
+					iface = optarg;
966
+				break;
967
+			case 'c':	/* where is clamd.conf? */
968
+				cfgfile = optarg;
969
+				break;
970
+			case 'C':	/* chroot */
971
+				rootdir = optarg;
972
+				break;
973
+			case 'd':	/* don't scan on error */
974
+				cl_error = SMFIS_ACCEPT;
975
+				break;
976
+			case 'D':	/* enable debug messages */
977
+				cl_debug();
978
+				break;
979
+			case 'e':	/* use clamd */
980
+				external++;
981
+				break;
982
+			case 'f':	/* force the scan */
983
+				fflag++;
984
+				break;
985
+			case 'h':
986
+				help();
987
+				return EX_OK;
988
+			case 'H':
989
+				hflag++;
990
+				break;
991
+			case 'i':	/* pidfile */
992
+				pidfile = optarg;
993
+				break;
994
+			case 'k':	/* blacklist time */
995
+				blacklist_time = atoi(optarg);
996
+				break;
997
+			case 'K':	/* don't black list given IP */
998
+				wont_blacklist = optarg;
999
+				break;
1000
+			case 'I':	/* --ignore, -I hostname */
1001
+				/*
1002
+				 * Based on patch by jpd@louisiana.edu
1003
+				 */
1004
+				if(Iflag == IFLAG_MAX) {
1005
+					fprintf(stderr,
1006
+						_("%s: %s, -I may only be given %d times\n"),
1007
+							argv[0], optarg, IFLAG_MAX);
1008
+					return EX_USAGE;
1009
+				}
1010
+				if(!add_local_ip(optarg)) {
1011
+					fprintf(stderr,
1012
+						_("%s: Cannot convert -I%s to IPaddr\n"),
1013
+							argv[0], optarg);
1014
+					return EX_USAGE;
1015
+				}
1016
+				Iflag++;
1017
+				break;
1018
+			case 'l':	/* scan mail from the lan */
1019
+				lflag++;
1020
+				break;
1021
+			case 'L':	/* detect forged local addresses */
1022
+				detect_forged_local_address++;
1023
+				break;
1024
+			case 'm':	/* maximum number of children */
1025
+				max_children = atoi(optarg);
1026
+				break;
1027
+			case 'M':	/* how often to monitor for freshclam */
1028
+				freshclam_monitor = atoi(optarg);
1029
+				break;
1030
+			case 'n':	/* don't add X-Virus-Scanned */
1031
+				nflag++;
1032
+				smfilter.xxfi_flags &= ~(SMFIF_ADDHDRS|SMFIF_CHGHDRS);
1033
+				break;
1034
+			case 'N':	/* Do we reject mail or silently drop it */
1035
+				rejectmail = 0;
1036
+				break;
1037
+			case 'o':	/* scan outgoing mail */
1038
+				oflag++;
1039
+				break;
1040
+			case 'p':	/* postmaster e-mail address */
1041
+				postmaster = optarg;
1042
+				break;
1043
+			case 'P':	/* postmaster only */
1044
+				pflag++;
1045
+				break;
1046
+			case 'q':	/* send NO notification email */
1047
+				qflag++;
1048
+				break;
1049
+			case 'Q':	/* quarantine e-mail address */
1050
+				quarantine = optarg;
1051
+				smfilter.xxfi_flags |= SMFIF_CHGHDRS|SMFIF_ADDRCPT|SMFIF_DELRCPT;
1052
+				break;
1053
+			case 'r':	/* report phishing here */
1054
+				/* e.g. reportphishing@antiphishing.org */
1055
+				report = optarg;
1056
+				break;
1057
+			case 'R':	/* report phishing false positives here */
1058
+				report_fps = optarg;
1059
+				break;
1060
+			case 's':	/* server running clamd */
1061
+				server++;
1062
+				serverHostNames = optarg;
1063
+				break;
1064
+			case 'F':	/* signature file */
1065
+				sigFilename = optarg;
1066
+				signature = NULL;
1067
+				/* fall through */
1068
+			case 'S':	/* sign */
1069
+				smfilter.xxfi_flags |= SMFIF_CHGBODY;
1070
+				Sflag++;
1071
+				break;
1072
+			case 't':	/* e-mail template file */
1073
+				templateFile = optarg;
1074
+				break;
1075
+			case '1':	/* headers for the template file */
1076
+				templateHeaders = optarg;
1077
+				break;
1078
+			case '2':
1079
+				black_hole_mode++;
1080
+				break;
1081
+			case 'T':	/* time to wait for child to die */
1082
+				child_timeout = atoi(optarg);
1083
+				break;
1084
+			case 'U':	/* quarantine path */
1085
+				quarantine_dir = optarg;
1086
+				break;
1087
+			case 'V':
1088
+				puts(version);
1089
+				return EX_OK;
1090
+			case 'w':
1091
+				dont_wait++;
1092
+				break;
1093
+			case 'W':
1094
+				whitelistFile = optarg;
1095
+				break;
1096
+			case 'z':
1097
+				dont_sanitise=1;
1098
+				break;
1099
+			case '0':
1100
+				sendmailCF = optarg;
1101
+				break;
1102
+#ifdef	CL_DEBUG
1103
+			case 'x':
1104
+				debug_level = atoi(optarg);
1105
+				break;
1106
+#endif
1107
+			default:
1108
+#ifdef	CL_DEBUG
1109
+				fprintf(stderr, "Usage: %s [-b] [-c FILE] [-F FILE] [--max-children=num] [-e] [-l] [-o] [-p address] [-P] [-q] [-Q USER] [-s SERVER] [-S] [-x#] [-U PATH] [-M#] socket-addr\n", argv[0]);
1110
+#else
1111
+				fprintf(stderr, "Usage: %s [-b] [-c FILE] [-F FILE] [--max-children=num] [-e] [-l] [-o] [-p address] [-P] [-q] [-Q USER] [-s SERVER] [-S] [-U PATH] [-M#] socket-addr\n", argv[0]);
1112
+#endif
1113
+				return EX_USAGE;
1114
+		}
1115
+	}
1116
+
1117
+	/*
1118
+	 * Check sanity of --external and --server arguments
1119
+	 */
1120
+	if(server && !external) {
1121
+		fprintf(stderr,
1122
+			"%s: --server can only be used with --external\n",
1123
+			argv[0]);
1124
+		return EX_USAGE;
1125
+	}
1126
+#ifdef	SESSION
1127
+	if(!external) {
1128
+		fprintf(stderr,
1129
+			_("%s: SESSIONS mode requires --external\n"), argv[0]);
1130
+		return EX_USAGE;
1131
+	}
1132
+#endif
1133
+
1134
+	/* TODO: support freshclam's daemon notify if --external is not given */
1135
+
1136
+	if(optind == argc) {
1137
+		fprintf(stderr, _("%s: No socket-addr given\n"), argv[0]);
1138
+		return EX_USAGE;
1139
+	}
1140
+	port = argv[optind];
1141
+
1142
+	if(rootdir == NULL)	/* FIXME: Handle CHROOT */
1143
+		if(checkCF && verifyIncomingSocketName(port) < 0) {
1144
+			fprintf(stderr, _("%s: socket-addr (%s) doesn't agree with sendmail.cf\n"), argv[0], port);
1145
+			return EX_CONFIG;
1146
+		}
1147
+
1148
+	if(strncasecmp(port, "inet:", 5) == 0)
1149
+		if(!lflag) {
1150
+			/*
1151
+			 * Barmy but true. It seems that clamfi_connect will,
1152
+			 * in this case, get the IP address of the machine
1153
+			 * running sendmail, not of the machine sending the
1154
+			 * mail, so the remote end will be a local address so
1155
+			 * we must scan by enabling --local
1156
+			 *
1157
+			 * TODO: this is probably not needed if the remote
1158
+			 * machine is localhost, need to check though
1159
+			 */
1160
+			fprintf(stderr, _("%s: when using inet: connexion to sendmail you must enable --local\n"), argv[0]);
1161
+			return EX_USAGE;
1162
+		}
1163
+
1164
+	/*
1165
+	 * Sanity checks on the clamav configuration file
1166
+	 */
1167
+	if(cfgfile == NULL) {
1168
+		cfgfile = cli_malloc(strlen(CONFDIR) + 12);	/* leak */
1169
+		sprintf(cfgfile, "%s/clamd.conf", CONFDIR);
1170
+	}
1171
+	if((copt = getcfg(cfgfile, 1)) == NULL) {
1172
+		fprintf(stderr, _("%s: Can't parse the config file %s\n"),
1173
+			argv[0], cfgfile);
1174
+		return EX_CONFIG;
1175
+	}
1176
+
1177
+	if(detect_forged_local_address) {
1178
+		if(oflag) {
1179
+			fprintf(stderr, _("%s: --detect-forged-local-addresses is not compatible with --outgoing\n"), argv[0]);
1180
+			return EX_CONFIG;
1181
+		}
1182
+		if(lflag) {
1183
+			fprintf(stderr, _("%s: --detect-forged-local-addresses is not compatible with --local\n"), argv[0]);
1184
+			return EX_CONFIG;
1185
+		}
1186
+		if(fflag) {
1187
+			fprintf(stderr, _("%s: --detect-forged-local-addresses is not compatible with --force\n"), argv[0]);
1188
+			return EX_CONFIG;
1189
+		}
1190
+	}
1191
+
1192
+	if(Bflag) {
1193
+		int on;
1194
+
1195
+		broadcastSock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1196
+		/*
1197
+		 * SO_BROADCAST doesn't sent to all NICs on Linux, it only
1198
+		 * broadcasts on eth0, which is why there's an optional argument
1199
+		 * to --broadcast to say which NIC to broadcast on. You can use
1200
+		 * SO_BINDTODEVICE to get around that, but you need to have
1201
+		 * uid == 0 for that
1202
+		 */
1203
+		on = 1;
1204
+		if(setsockopt(broadcastSock, SOL_SOCKET, SO_BROADCAST, (int *)&on, sizeof(on)) < 0) {
1205
+			perror("setsockopt");
1206
+			return EX_UNAVAILABLE;
1207
+		}
1208
+		shutdown(broadcastSock, SHUT_RD);
1209
+	}
1210
+
1211
+	/*
1212
+	 * Drop privileges
1213
+	 */
1214
+#ifdef	CL_DEBUG
1215
+	/* Save the fd for later, open while we can */
1216
+	consolefd = open(console, O_WRONLY);
1217
+#endif
1218
+
1219
+	if(getuid() == 0) {
1220
+		if(iface) {
1221
+#ifdef	SO_BINDTODEVICE
1222
+			struct ifreq ifr;
1223
+
1224
+			memset(&ifr, '\0', sizeof(struct ifreq));
1225
+			strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1);
1226
+			ifr.ifr_name[sizeof(ifr.ifr_name)-1]='\0';
1227
+			if(setsockopt(broadcastSock, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0) {
1228
+				perror(iface);
1229
+				return EX_CONFIG;
1230
+			}
1231
+#else
1232
+			fprintf(stderr, _("%s: The iface option to --broadcast is not supported on your operating system\n"), argv[0]);
1233
+			return EX_CONFIG;
1234
+#endif
1235
+		}
1236
+
1237
+		if(((cpt = cfgopt(copt, "User")) != NULL) && cpt->enabled) {
1238
+			const struct passwd *user;
1239
+
1240
+			if((user = getpwnam(cpt->strarg)) == NULL) {
1241
+				fprintf(stderr, _("%s: Can't get information about user %s\n"), argv[0], cpt->strarg);
1242
+				return EX_CONFIG;
1243
+			}
1244
+
1245
+			if(cfgopt(copt, "AllowSupplementaryGroups")->enabled) {
1246
+#ifdef HAVE_INITGROUPS
1247
+				if(initgroups(cpt->strarg, user->pw_gid) < 0) {
1248
+					perror(cpt->strarg);
1249
+					return EX_CONFIG;
1250
+				}
1251
+#else
1252
+				fprintf(stderr, _("%s: AllowSupplementaryGroups: initgroups not supported.\n"),
1253
+					argv[0]);
1254
+				return EX_CONFIG;
1255
+#endif
1256
+			} else {
1257
+#ifdef	HAVE_SETGROUPS
1258
+				if(setgroups(1, &user->pw_gid) < 0) {
1259
+					perror(cpt->strarg);
1260
+					return EX_CONFIG;
1261
+				}
1262
+#endif
1263
+			}
1264
+
1265
+			setgid(user->pw_gid);
1266
+
1267
+			if(setuid(user->pw_uid) < 0)
1268
+				perror(cpt->strarg);
1269
+#ifdef	CL_DEBUG
1270
+			else
1271
+				printf(_("Running as user %s (UID %d, GID %d)\n"),
1272
+					cpt->strarg, (int)user->pw_uid,
1273
+					(int)user->pw_gid);
1274
+#endif
1275
+
1276
+			/*
1277
+			 * Note, some O/Ss (e.g. OpenBSD/Fedora Linux) FORCE
1278
+			 * you to run as root in black-hole-mode because
1279
+			 * /var/spool/mqueue is mode 700 owned by root!
1280
+			 * Flames to them, not to me, please.
1281
+			 */
1282
+			if(black_hole_mode && (user->pw_uid != 0)) {
1283
+				int are_trusted;
1284
+				FILE *sendmail;
1285
+				char cmd[128];
1286
+
1287
+				/*
1288
+				 * Determine if we're a "trusted user"
1289
+				 */
1290
+				snprintf(cmd, sizeof(cmd) - 1, "%s -bv root</dev/null 2>&1",
1291
+					SENDMAIL_BIN);
1292
+
1293
+				sendmail = popen(cmd, "r");
1294
+
1295
+				if(sendmail == NULL) {
1296
+					perror(SENDMAIL_BIN);
1297
+					are_trusted = 0;
1298
+				} else {
1299
+					int status;
1300
+					char buf[BUFSIZ];
1301
+
1302
+					while(fgets(buf, sizeof(buf), sendmail) != NULL)
1303
+						;
1304
+					/*
1305
+					 * Can't do
1306
+					 * switch(WEXITSTATUS(pclose(sendmail)))
1307
+					 * because that fails to compile on
1308
+					 * NetBSD2.0
1309
+					 */
1310
+					status = pclose(sendmail);
1311
+					switch(WEXITSTATUS(status)) {
1312
+						case EX_NOUSER:
1313
+							/*
1314
+							 * No root? But at least
1315
+							 * we're trusted enough
1316
+							 * to find out!
1317
+							 */
1318
+							are_trusted = 1;
1319
+							break;
1320
+						default:
1321
+							are_trusted = 0;
1322
+							break;
1323
+						case EX_OK:
1324
+							are_trusted = 1;
1325
+					}
1326
+				}
1327
+				if(!are_trusted) {
1328
+					fprintf(stderr, _("%s: You cannot use black hole mode unless %s is a TrustedUser\n"),
1329
+						argv[0], cpt->strarg);
1330
+					return EX_CONFIG;
1331
+				}
1332
+			}
1333
+		} else
1334
+			printf(_("^%s: running as root is not recommended (check \"User\" in %s)\n"), argv[0], cfgfile);
1335
+	} else if(iface) {
1336
+		fprintf(stderr, _("%s: Only root can set an interface for --broadcast\n"), argv[0]);
1337
+		return EX_USAGE;
1338
+	}
1339
+
1340
+	if(advisory && quarantine) {
1341
+		fprintf(stderr, _("%s: Advisory mode doesn't work with quarantine mode\n"), argv[0]);
1342
+		return EX_USAGE;
1343
+	}
1344
+	if(quarantine_dir) {
1345
+		struct stat statb;
1346
+
1347
+		if(advisory) {
1348
+			fprintf(stderr,
1349
+				_("%s: Advisory mode doesn't work with quarantine directories\n"),
1350
+				argv[0]);
1351
+			return EX_USAGE;
1352
+		}
1353
+		if(strstr(quarantine_dir, "ERROR") != NULL) {
1354
+			fprintf(stderr,
1355
+				_("%s: the quarantine directory must not contain the string 'ERROR'\n"),
1356
+				argv[0]);
1357
+			return EX_USAGE;
1358
+		}
1359
+		if(strstr(quarantine_dir, "FOUND") != NULL) {
1360
+			fprintf(stderr,
1361
+				_("%s: the quarantine directory must not contain the string 'FOUND'\n"),
1362
+				argv[0]);
1363
+			return EX_USAGE;
1364
+		}
1365
+		if(strstr(quarantine_dir, "OK") != NULL) {
1366
+			fprintf(stderr,
1367
+				_("%s: the quarantine directory must not contain the string 'OK'\n"),
1368
+				argv[0]);
1369
+			return EX_USAGE;
1370
+		}
1371
+		if(access(quarantine_dir, W_OK) < 0) {
1372
+			perror(quarantine_dir);
1373
+			return EX_USAGE;
1374
+		}
1375
+		if(stat(quarantine_dir, &statb) < 0) {
1376
+			perror(quarantine_dir);
1377
+			return EX_USAGE;
1378
+		}
1379
+		/*
1380
+		 * Quit if the quarantine directory is publically readable
1381
+		 * or writeable
1382
+		 */
1383
+		if(statb.st_mode & 077) {
1384
+			fprintf(stderr, _("%s: insecure quarantine directory %s (mode 0%o)\n"),
1385
+				argv[0], quarantine_dir, (int)statb.st_mode & 0777);
1386
+			return EX_CONFIG;
1387
+		}
1388
+	}
1389
+
1390
+	if(sigFilename && !updateSigFile())
1391
+		return EX_USAGE;
1392
+
1393
+	if(templateFile && (access(templateFile, R_OK) < 0)) {
1394
+		perror(templateFile);
1395
+		return EX_CONFIG;
1396
+	}
1397
+	if(templateHeaders) {
1398
+		if(templateFile == NULL) {
1399
+			fputs(("%s: --template-headers requires --template-file\n"),
1400
+				stderr);
1401
+			return EX_CONFIG;
1402
+		}
1403
+		if(access(templateHeaders, R_OK) < 0) {
1404
+			perror(templateHeaders);
1405
+			return EX_CONFIG;
1406
+		}
1407
+	}
1408
+	if(whitelistFile && (access(whitelistFile, R_OK) < 0)) {
1409
+		perror(whitelistFile);
1410
+		return EX_CONFIG;
1411
+	}
1412
+
1413
+	/*
1414
+	 * If the --max-children flag isn't set, see if MaxThreads
1415
+	 * is set in the config file. Based on an idea by "Richard G. Roberto"
1416
+	 * <rgr@dedlegend.com>
1417
+	 */
1418
+	if((max_children == 0) && ((cpt = cfgopt(copt, "MaxThreads")) != NULL))
1419
+		max_children = cfgopt(copt, "MaxThreads")->numarg;
1420
+
1421
+#ifdef HAVE_LRESOLV_R
1422
+	/* allocate a pool of resolvers */
1423
+	if(!(res_pool=cli_calloc(max_children+1, sizeof(*res_pool))))
1424
+		return EX_OSERR;
1425
+	if(!(res_pool_state=cli_malloc(max_children+1)))
1426
+		return EX_OSERR;
1427
+	memset(res_pool_state, 1, max_children+1);
1428
+	for(i = 0; i < max_children+1; i++)
1429
+		res_ninit(&res_pool[i]);
1430
+#endif
1431
+
1432
+	if((cpt = cfgopt(copt, "ReadTimeout")) != NULL) {
1433
+		readTimeout = cpt->numarg;
1434
+
1435
+		if(readTimeout < 0) {
1436
+			fprintf(stderr, _("%s: ReadTimeout must not be negative in %s\n"),
1437
+				argv[0], cfgfile);
1438
+			return EX_CONFIG;
1439
+		}
1440
+	}
1441
+
1442
+	if((cpt = cfgopt(copt, "StreamMaxLength")) != NULL) {
1443
+		streamMaxLength = (long)cpt->numarg;
1444
+		if(streamMaxLength < 0L) {
1445
+			fprintf(stderr, _("%s: StreamMaxLength must not be negative in %s\n"),
1446
+			argv[0], cfgfile);
1447
+			return EX_CONFIG;
1448
+		}
1449
+	}
1450
+
1451
+	if(((cpt = cfgopt(copt, "LogSyslog")) != NULL) && cpt->enabled) {
1452
+#if defined(USE_SYSLOG) && !defined(C_AIX)
1453
+		int fac = LOG_LOCAL6;
1454
+#endif
1455
+
1456
+		if(cfgopt(copt, "LogVerbose")->enabled) {
1457
+			logg_verbose = 1;
1458
+#ifdef	CL_DEBUG
1459
+#if	((SENDMAIL_VERSION_A > 8) || ((SENDMAIL_VERSION_A == 8) && (SENDMAIL_VERSION_B >= 13)))
1460
+			if(debug_level >= 15)
1461
+				smfi_setdbg(6);
1462
+#endif
1463
+#endif
1464
+		}
1465
+#if defined(USE_SYSLOG) && !defined(C_AIX)
1466
+		logg_syslog = 1;
1467
+
1468
+		if(((cpt = cfgopt(copt, "LogFacility")) != NULL) && cpt->enabled)
1469
+			if((fac = logg_facility(cpt->strarg)) == -1) {
1470
+				fprintf(stderr, "%s: LogFacility: %s: No such facility\n",
1471
+					argv[0], cpt->strarg);
1472
+				return EX_CONFIG;
1473
+			}
1474
+		openlog(progname, LOG_CONS|LOG_PID, fac);
1475
+#endif
1476
+	} else {
1477
+		if(qflag)
1478
+			fprintf(stderr, _("%s: (-q && !LogSyslog): warning - all interception message methods are off\n"),
1479
+				argv[0]);
1480
+#if defined(USE_SYSLOG) && !defined(C_AIX)
1481
+		logg_syslog = 0;
1482
+#endif
1483
+	}
1484
+	/*
1485
+	 * Get the outgoing socket details - the way to talk to clamd, unless
1486
+	 * we're doing the scanning internally
1487
+	 */
1488
+	if(!external) {
1489
+#ifdef	C_LINUX
1490
+		const char *lang;
1491
+#endif
1492
+
1493
+		if(max_children == 0) {
1494
+			fprintf(stderr, _("%s: --max-children must be given if --external is not given\n"), argv[0]);
1495
+			return EX_CONFIG;
1496
+		}
1497
+		if(freshclam_monitor <= 0) {
1498
+			fprintf(stderr, _("%s: --freshclam_monitor must be at least one second\n"), argv[0]);
1499
+			return EX_CONFIG;
1500
+		}
1501
+#ifdef	C_LINUX
1502
+		lang = getenv("LANG");
1503
+
1504
+		if(lang && (strstr(lang, "UTF-8") != NULL)) {
1505
+			fprintf(stderr, "Your LANG environment variable is set to '%s'\n", lang);
1506
+			fprintf(stderr, "This is known to cause problems for some %s installations.\n", argv[0]);
1507
+			fputs("If you get failures with temporary files, please try again with LANG unset.\n", stderr);
1508
+		}
1509
+#endif
1510
+#if	0
1511
+		if(child_timeout) {
1512
+			fprintf(stderr, _("%s: --timeout must not be given if --external is not given\n"), argv[0]);
1513
+			return EX_CONFIG;
1514
+		}
1515
+#endif
1516
+		if (cl_init(CL_INIT_DEFAULT)!=CL_SUCCESS) {
1517
+			fprintf(stderr, "%s: Failed to initialize libclamav, bailing out.\n", argv[0]);
1518
+			return EX_UNAVAILABLE;
1519
+		}
1520
+		if(loadDatabase() != 0) {
1521
+			/*
1522
+			 * Handle the dont-scan-on-error option, which says
1523
+			 * that we pass on emails, unscanned, if an error has
1524
+			 * occurred
1525
+			 */
1526
+			if(cl_error != SMFIS_ACCEPT)
1527
+				return EX_CONFIG;
1528
+
1529
+			fprintf(stderr, _("%s: No emails will be scanned"),
1530
+				argv[0]);
1531
+		}
1532
+		numServers = 1;
1533
+	} else if(((cpt = cfgopt(copt, "LocalSocket")) != NULL) && cpt->enabled) {
1534
+#ifdef	SESSION
1535
+		struct sockaddr_un sockun;
1536
+#endif
1537
+		char *sockname = NULL;
1538
+
1539
+		if(cfgopt(copt, "TCPSocket")->enabled) {
1540
+			fprintf(stderr, _("%s: You can select one server type only (local/TCP) in %s\n"),
1541
+				argv[0], cfgfile);
1542
+			return EX_CONFIG;
1543
+		}
1544
+		if(server) {
1545
+			fprintf(stderr, _("%s: You cannot use the --server option when using LocalSocket in %s\n"),
1546
+				argv[0], cfgfile);
1547
+			return EX_USAGE;
1548
+		}
1549
+		if(strncasecmp(port, "unix:", 5) == 0)
1550
+			sockname = &port[5];
1551
+		else if(strncasecmp(port, "local:", 6) == 0)
1552
+			sockname = &port[6];
1553
+
1554
+		if(sockname && (strcmp(sockname, cpt->strarg) == 0)) {
1555
+			fprintf(stderr, _("The connexion from sendmail to %s (%s) must not\n"),
1556
+				argv[0], sockname);
1557
+			fprintf(stderr, _("be the same as the connexion to clamd (%s) in %s\n"),
1558
+				cpt->strarg, cfgfile);
1559
+			return EX_CONFIG;
1560
+		}
1561
+		/*
1562
+		 * TODO: check --server hasn't been set
1563
+		 */
1564
+		localSocket = cpt->strarg;
1565
+#ifndef	SESSION
1566
+		if(!pingServer(-1)) {
1567
+			fprintf(stderr, _("Can't talk to clamd server via %s\n"),
1568
+				localSocket);
1569
+			fprintf(stderr, _("Check your entry for LocalSocket in %s\n"),
1570
+				cfgfile);
1571
+			return EX_CONFIG;
1572
+		}
1573
+#endif
1574
+		/*if(quarantine_dir == NULL)
1575
+			fprintf(stderr, _("When using Localsocket in %s\nyou may improve performance if you use the --quarantine-dir option\n"), cfgfile);*/
1576
+
1577
+		umask(077);
1578
+
1579
+		serverIPs = (in_addr_t *)cli_malloc(sizeof(in_addr_t));
1580
+#ifdef	INADDR_LOOPBACK
1581
+		serverIPs[0] = htonl(INADDR_LOOPBACK);
1582
+#else
1583
+		serverIPs[0] = inet_addr("127.0.0.1");
1584
+#endif
1585
+
1586
+#ifdef	SESSION
1587
+		memset((char *)&sockun, 0, sizeof(struct sockaddr_un));
1588
+		sockun.sun_family = AF_UNIX;
1589
+		strncpy(sockun.sun_path, localSocket, sizeof(sockun.sun_path));
1590
+		sockun.sun_path[sizeof(sockun.sun_path)-1]='\0';
1591
+
1592
+		sessions = (struct session *)cli_malloc(sizeof(struct session));
1593
+		if((sessions[0].sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
1594
+			perror(localSocket);
1595
+			fprintf(stderr, _("Can't talk to clamd server via %s\n"),
1596
+				localSocket);
1597
+			fprintf(stderr, _("Check your entry for LocalSocket in %s\n"),
1598
+				cfgfile);
1599
+			return EX_CONFIG;
1600
+		}
1601
+		if(connect(sessions[0].sock, (struct sockaddr *)&sockun, sizeof(struct sockaddr_un)) < 0) {
1602
+			perror(localSocket);
1603
+			return EX_UNAVAILABLE;
1604
+		}
1605
+		if(send(sessions[0].sock, "SESSION\n", 8, 0) < 8) {
1606
+			perror("send");
1607
+			fputs(_("!Can't create a clamd session"), stderr);
1608
+			return EX_UNAVAILABLE;
1609
+		}
1610
+		sessions[0].status = CMDSOCKET_FREE;
1611
+#endif
1612
+		/*
1613
+		 * FIXME: Allow connexion to remote servers by TCP/IP whilst
1614
+		 * connecting to the localserver via a UNIX domain socket
1615
+		 */
1616
+		numServers = 1;
1617
+	} else if(((cpt = cfgopt(copt, "TCPSocket")) != NULL) && cpt->enabled) {
1618
+		int activeServers;
1619
+
1620
+		/*
1621
+		 * TCPSocket is in fact a port number not a full socket
1622
+		 */
1623
+		if(quarantine_dir) {
1624
+			fprintf(stderr, _("%s: --quarantine-dir not supported for TCPSocket - use --quarantine\n"), argv[0]);
1625
+			return EX_CONFIG;
1626
+		}
1627
+
1628
+		tcpSocket = (in_port_t)cpt->numarg;
1629
+
1630
+		/*
1631
+		 * cli_strtok's fieldno counts from 0
1632
+		 */
1633
+		for(;;) {
1634
+			char *hostname = cli_strtok(serverHostNames, numServers, ":");
1635
+			if(hostname == NULL)
1636
+				break;
1637
+#ifdef	MAXHOSTNAMELEN
1638
+			if(strlen(hostname) > MAXHOSTNAMELEN) {
1639
+				fprintf(stderr, _("%s: hostname %s is longer than %d characters\n"),
1640
+					argv[0], hostname, MAXHOSTNAMELEN);
1641
+				return EX_CONFIG;
1642
+			}
1643
+#endif
1644
+			numServers++;
1645
+			free(hostname);
1646
+		}
1647
+
1648
+#ifdef	CL_DEBUG
1649
+		printf("numServers: %d\n", numServers);
1650
+#endif
1651
+
1652
+		serverIPs = (in_addr_t *)cli_malloc(numServers * sizeof(in_addr_t));
1653
+		if(serverIPs == NULL)
1654
+			return EX_OSERR;
1655
+		activeServers = 0;
1656
+
1657
+#ifdef	SESSION
1658
+		/*
1659
+		 * We need to know how many connexion to establish to clamd
1660
+		 */
1661
+		if(max_children == 0) {
1662
+			fprintf(stderr, _("%s: --max-children must be given in sessions mode\n"), argv[0]);
1663
+			return EX_CONFIG;
1664
+		}
1665
+#endif
1666
+
1667
+		if(numServers > max_children) {
1668
+			fprintf(stderr, _("%1$s: --max-children (%2$d) is lower than the number of servers you have (%3$d)\n"),
1669
+				argv[0], max_children, numServers);
1670
+			return EX_CONFIG;
1671
+		}
1672
+
1673
+		for(i = 0; i < numServers; i++) {
1674
+#ifdef	MAXHOSTNAMELEN
1675
+			char hostname[MAXHOSTNAMELEN + 1];
1676
+
1677
+			if(cli_strtokbuf(serverHostNames, i, ":", hostname) == NULL)
1678
+				break;
1679
+#else
1680
+			char *hostname = cli_strtok(serverHostNames, i, ":");
1681
+#endif
1682
+
1683
+			/*
1684
+			 * Translate server's name to IP address
1685
+			 */
1686
+			serverIPs[i] = inet_addr(hostname);
1687
+#ifdef	INADDR_NONE
1688
+			if(serverIPs[i] == INADDR_NONE) {
1689
+#else
1690
+			if(serverIPs[i] == (in_addr_t)-1) {
1691
+#endif
1692
+				const struct hostent *h = gethostbyname(hostname);
1693
+
1694
+				if(h == NULL) {
1695
+					fprintf(stderr, _("%s: Unknown host %s\n"),
1696
+						argv[0], hostname);
1697
+					return EX_USAGE;
1698
+				}
1699
+
1700
+				memcpy((char *)&serverIPs[i], h->h_addr, sizeof(serverIPs[i]));
1701
+			}
1702
+
1703
+#if	defined(NTRIES) && ((NTRIES > 1))
1704
+#ifndef	SESSION
1705
+#ifdef	INADDR_LOOPBACK
1706
+			if(serverIPs[i] == htonl(INADDR_LOOPBACK)) {
1707
+#else
1708
+#if	HAVE_IN_ADDR_T
1709
+			if(serverIPs[i] == (in_addr_t)inet_addr("127.0.0.1")) {
1710
+#else
1711
+			if(serverIPs[i] == (long)inet_addr("127.0.0.1")) {
1712
+#endif
1713
+#endif
1714
+				int tries;
1715
+
1716
+				/*
1717
+				 * Fudge to allow clamd to come up on
1718
+				 * our local machine
1719
+				 */
1720
+				for(tries = 0; tries < NTRIES - 1; tries++) {
1721
+					if(pingServer(i))
1722
+						break;
1723
+					if(checkClamd(1))	/* will try all servers */
1724
+						break;
1725
+					puts(_("Waiting for clamd to come up"));
1726
+					/*
1727
+					 * something to do as the system starts
1728
+					 */
1729
+					sync();
1730
+					sleep(1);
1731
+				}
1732
+				/* Will try one more time */
1733
+			}
1734
+#endif	/* NTRIES > 1 */
1735
+
1736
+			if(pingServer(i))
1737
+				activeServers++;
1738
+			else {
1739
+				printf(_("Can't talk to clamd server %s on port %d\n"),
1740
+					hostname, tcpSocket);
1741
+				if(serverIPs[i] == htonl(INADDR_LOOPBACK)) {
1742
+					if(cfgopt(copt, "TCPAddr")->enabled)
1743
+						printf(_("Check the value for TCPAddr in %s\n"), cfgfile);
1744
+				} else
1745
+					printf(_("Check the value for TCPAddr in clamd.conf on %s\n"), hostname);
1746
+			}
1747
+#endif
1748
+
1749
+#ifndef	MAXHOSTNAMELEN
1750
+			free(hostname);
1751
+#endif
1752
+		}
1753
+#ifdef	SESSION
1754
+		activeServers = numServers;
1755
+
1756
+		sessions = (struct session *)cli_calloc(max_children, sizeof(struct session));
1757
+		for(i = 0; i < (int)max_children; i++)
1758
+			if(createSession(i) < 0)
1759
+				return EX_UNAVAILABLE;
1760
+		if(activeServers == 0) {
1761
+			fprintf(stderr, _("Check your entry for TCPSocket in %s\n"),
1762
+				cfgfile);
1763
+		}
1764
+#else
1765
+		if(activeServers == 0) {
1766
+			fprintf(stderr, _("Check your entry for TCPSocket in %s\n"),
1767
+				cfgfile);
1768
+			fputs(_("Can't find any clamd server\n"), stderr);
1769
+			return EX_CONFIG;
1770
+		}
1771
+		last_failed_pings = (time_t *)cli_calloc(numServers, sizeof(time_t));
1772
+#endif
1773
+	} else {
1774
+		fprintf(stderr, _("%s: You must select server type (local/TCP) in %s\n"),
1775
+			argv[0], cfgfile);
1776
+		return EX_CONFIG;
1777
+	}
1778
+
1779
+#ifdef	SESSION
1780
+	if(!external) {
1781
+		if(clamav_versions == NULL) {
1782
+			clamav_versions = (char **)cli_malloc(sizeof(char *));
1783
+			if(clamav_versions == NULL)
1784
+				return EX_TEMPFAIL;
1785
+			clamav_version = cli_strdup(version);
1786
+		}
1787
+	} else {
1788
+		unsigned int session;
1789
+
1790
+		/*
1791
+		 * We need to know how many connexions to establish to clamd
1792
+		 */
1793
+		if(max_children == 0) {
1794
+			fprintf(stderr, _("%s: --max-children must be given in sessions mode\n"), argv[0]);
1795
+			return EX_CONFIG;
1796
+		}
1797
+
1798
+		clamav_versions = (char **)cli_malloc(max_children * sizeof(char *));
1799
+		if(clamav_versions == NULL)
1800
+			return EX_TEMPFAIL;
1801
+
1802
+		for(session = 0; session < max_children; session++) {
1803
+			clamav_versions[session] = cli_strdup(version);
1804
+			if(clamav_versions[session] == NULL)
1805
+				return EX_TEMPFAIL;
1806
+		}
1807
+	}
1808
+#else
1809
+	strcpy(clamav_version, version);
1810
+#endif
1811
+
1812
+	if(((quarantine_dir == NULL) && localSocket) || !external) {
1813
+		/* set the temporary dir */
1814
+		if((cpt = cfgopt(copt, "TemporaryDirectory")) && cpt->enabled)
1815
+			tmpdir = cpt->strarg;
1816
+		else 
1817
+			tmpdir = cli_gentemp(NULL);
1818
+
1819
+		logg("#Making %s\n", tmpdir);
1820
+
1821
+		if(mkdir(tmpdir, 0700)) {
1822
+			perror(tmpdir);
1823
+			return EX_CANTCREAT;
1824
+		}
1825
+	} else
1826
+		tmpdir = NULL;
1827
+
1828
+	if(report) {
1829
+		if(!cfgopt(copt, "PhishingSignatures")->enabled) {
1830
+			fprintf(stderr, "%s: You have chosen --report-phish, but PhishingSignatures is off in %s\n",
1831
+				argv[0], cfgfile);
1832
+			return EX_USAGE;
1833
+		}
1834
+		if((quarantine_dir == NULL) && (tmpdir == NULL)) {
1835
+			/*
1836
+			 * Limitation: doesn't store message in a temporary
1837
+			 * file, so we won't be able to use mail < file
1838
+			 */
1839
+			fprintf(stderr, "%s: when using --external, --report-phish cannot be used without either LocalSocket or --quarantine-dir\n",
1840
+				argv[0]);
1841
+			return EX_USAGE;
1842
+		}
1843
+		if(lflag) {
1844
+			/*
1845
+			 * Naturally, if you attempt to scan the phish you've
1846
+			 * just reported, it'll be blocked!
1847
+			 */
1848
+			fprintf(stderr, "%s: --report-phish cannot be used with --local\n",
1849
+				argv[0]);
1850
+			return EX_USAGE;
1851
+		}
1852
+	}
1853
+	if(report_fps)
1854
+		if(!cfgopt(copt, "PhishingSignatures")->enabled) {
1855
+			fprintf(stderr, "%s: You have chosen --report-phish-false-positives, but PhishingSignatures is off in %s\n",
1856
+				argv[0], cfgfile);
1857
+			return EX_USAGE;
1858
+		}
1859
+
1860
+	if(cfgopt(copt, "Foreground")->enabled)
1861
+		logg_foreground = 1;
1862
+	else {
1863
+		logg_foreground = 0;
1864
+#ifdef	CL_DEBUG
1865
+		printf(_("When debugging it is recommended that you use Foreground mode in %s\n"), cfgfile);
1866
+		puts(_("\tso that you can see all of the messages"));
1867
+#endif
1868
+
1869
+		switch(fork()) {
1870
+			case -1:
1871
+				perror("fork");
1872
+				return EX_OSERR;
1873
+			case 0:	/* child */
1874
+				break;
1875
+			default:	/* parent */
1876
+				return EX_OK;
1877
+		}
1878
+		close(0);
1879
+		open("/dev/null", O_RDONLY);
1880
+
1881
+		/* initialize logger */
1882
+		logg_time = cfgopt(copt, "LogTime")->enabled;
1883
+		logok = cfgopt(copt, "LogClean")->enabled;
1884
+		logg_size = cfgopt(copt, "LogFileMaxSize")->numarg;
1885
+		logg_verbose = mprintf_verbose = cfgopt(copt, "LogVerbose")->enabled;
1886
+
1887
+		if(cfgopt(copt, "Debug")->enabled) /* enable debug messages in libclamav */
1888
+			cl_debug();
1889
+
1890
+		if((cpt = cfgopt(copt, "LogFile"))->enabled) {
1891
+			time_t currtime;
1892
+
1893
+			logg_file = cpt->strarg;
1894
+			if((strlen(logg_file) < 2) ||
1895
+			   ((logg_file[0] != '/') && (logg_file[0] != '\\') && (logg_file[1] != ':'))) {
1896
+				fprintf(stderr, "ERROR: LogFile requires full path.\n");
1897
+				logg_close();
1898
+				freecfg(copt);
1899
+				return 1;
1900
+			}
1901
+			time(&currtime);
1902
+			close(1);
1903
+			if(logg("#ClamAV-milter started at %s", ctime(&currtime))) {
1904
+				fprintf(stderr, "ERROR: Problem with internal logger. Please check the permissions on the %s file.\n", logg_file);
1905
+				logg_close();
1906
+				freecfg(copt);
1907
+				return 1;
1908
+			}
1909
+		} else {
1910
+#ifdef	CL_DEBUG
1911
+			close(1);
1912
+			logg_file = console;
1913
+			if(consolefd < 0) {
1914
+				perror(console);
1915
+				return EX_OSFILE;
1916
+			}
1917
+			dup(consolefd);
1918
+#else
1919
+			int fds[3];
1920
+			logg_file = NULL;
1921
+			if(chdir("/") < 0)
1922
+				perror("/");
1923
+			fds[0] = open("/dev/null", O_RDONLY);
1924
+			fds[1] = open("/dev/null", O_WRONLY);
1925
+			fds[2] = open("/dev/null", O_WRONLY);
1926
+			for(i = 0; i <= 2; i++) {
1927
+				if(fds[i] == -1 || dup2(fds[i], i) == -1) {
1928
+					fprintf(stderr, "ERROR: failed to daemonize.\n");
1929
+					logg_close();
1930
+					freecfg(copt);
1931
+					return 1;
1932
+				}
1933
+			}
1934
+#endif
1935
+		}
1936
+
1937
+		dup2(1, 2);
1938
+
1939
+#ifdef	CL_DEBUG
1940
+		if(consolefd >= 0)
1941
+			close(consolefd);
1942
+#endif
1943
+
1944
+#ifdef HAVE_SETPGRP
1945
+#ifdef SETPGRP_VOID
1946
+		setpgrp();
1947
+#else
1948
+		setpgrp(0,0);
1949
+#endif
1950
+#else
1951
+#ifdef HAVE_SETSID
1952
+		setsid();
1953
+#endif
1954
+#endif
1955
+	}
1956
+
1957
+	if(cfgopt(copt, "Debug")->enabled)
1958
+		/*
1959
+		 * enable debug messages in libclamav, --debug also does this
1960
+		 */
1961
+		cl_debug();
1962
+
1963
+	atexit(quit);
1964
+
1965
+	if(!external) {
1966
+		if(!cfgopt(copt, "ScanMail")->enabled)
1967
+			printf(_("%s: ScanMail not defined in %s (needed without --external), enabling\n"),
1968
+				argv[0], cfgfile);
1969
+
1970
+		options |= CL_SCAN_MAIL;	/* no choice */
1971
+		/*if(!cfgopt(copt, "ScanRAR")->enabled)
1972
+			options |= CL_SCAN_DISABLERAR;*/
1973
+		if(cfgopt(copt, "ArchiveBlockEncrypted")->enabled)
1974
+			options |= CL_SCAN_BLOCKENCRYPTED;
1975
+		if(cfgopt(copt, "ScanPE")->enabled)
1976
+			options |= CL_SCAN_PE;
1977
+		if(cfgopt(copt, "DetectBrokenExecutables")->enabled)
1978
+			options |= CL_SCAN_BLOCKBROKEN;
1979
+		if(cfgopt(copt, "MailFollowURLs")->enabled)
1980
+			options |= CL_SCAN_MAILURL;
1981
+		if(cfgopt(copt, "ScanOLE2")->enabled)
1982
+			options |= CL_SCAN_OLE2;
1983
+		if(cfgopt(copt, "ScanHTML")->enabled)
1984
+			options |= CL_SCAN_HTML;
1985
+
1986
+		if(((cpt = cfgopt(copt, "MaxScanSize")) != NULL) && cpt->enabled)
1987
+			maxscansize = cpt->numarg;
1988
+		else
1989
+			maxscansize = 104857600;
1990
+		if(((cpt = cfgopt(copt, "MaxFileSize")) != NULL) && cpt->enabled)
1991
+			maxfilesize = cpt->numarg;
1992
+		else
1993
+			maxfilesize = 10485760;
1994
+
1995
+		if(getrlimit(RLIMIT_FSIZE, &rlim) == 0) {
1996
+			if((rlim.rlim_max < maxfilesize) || (rlim.rlim_max < maxscansize))
1997
+				logg("^System limit for file size is lower than maxfilesize or maxscansize\n");
1998
+		} else {
1999
+			logg("^Cannot obtain resource limits for file size\n");
2000
+		}
2001
+
2002
+		if(((cpt = cfgopt(copt, "MaxRecursion")) != NULL) && cpt->enabled)
2003
+			maxreclevel = cpt->numarg;
2004
+		else
2005
+			maxreclevel = 8;
2006
+
2007
+		if(((cpt = cfgopt(copt, "MaxFiles")) != NULL) && cpt->enabled)
2008
+			maxfiles = cpt->numarg;
2009
+		else
2010
+			maxfiles = 1000;
2011
+
2012
+		if(cfgopt(copt, "ScanArchive")->enabled)
2013
+			options |= CL_SCAN_ARCHIVE;
2014
+	}
2015
+
2016
+	pthread_create(&tid, NULL, watchdog, NULL);
2017
+
2018
+	broadcast(_("Starting clamav-milter"));
2019
+
2020
+	if(rootdir) {
2021
+		if(getuid() == 0) {
2022
+			if(chdir(rootdir) < 0) {
2023
+				perror(rootdir);
2024
+				logg("!chdir %s failed\n", rootdir);
2025
+				return EX_CONFIG;
2026
+			}
2027
+			if(chroot(rootdir) < 0) {
2028
+				perror(rootdir);
2029
+				logg("!chroot %s failed\n", rootdir);
2030
+				return EX_CONFIG;
2031
+			}
2032
+			logg("Chrooted to %s\n", rootdir);
2033
+		} else {
2034
+			logg("!chroot option needs root\n");
2035
+			return EX_CONFIG;
2036
+		}
2037
+	}
2038
+
2039
+	if(pidfile) {
2040
+		/* save the PID */
2041
+		char *p, *q;
2042
+		FILE *fd;
2043
+		const mode_t old_umask = umask(0006);
2044
+
2045
+		if(pidfile[0] != '/') {
2046
+			logg(_("!pidfile: '%s' must be a full pathname"),
2047
+				pidfile);
2048
+
2049
+			return EX_CONFIG;
2050
+		}
2051
+		p = cli_strdup(pidfile);
2052
+		q = strrchr(p, '/');
2053
+		*q = '\0';
2054
+
2055
+		if(rootdir == NULL)
2056
+			if(chdir(p) < 0)	/* safety */
2057
+				perror(p);
2058
+
2059
+		free(p);
2060
+
2061
+		if((fd = fopen(pidfile, "w")) == NULL) {
2062
+			logg(_("!Can't save PID in file %s\n"), pidfile);
2063
+			return EX_CONFIG;
2064
+		}
2065
+#ifdef	C_LINUX
2066
+		/* Ensure that all threads are kill()ed */
2067
+		fprintf(fd, "-%d\n", (int)getpgrp());
2068
+#else
2069
+		fprintf(fd, "%d\n", (int)getpid());
2070
+#endif
2071
+		fclose(fd);
2072
+		umask(old_umask);
2073
+	} else if(tmpdir) {
2074
+		if(rootdir == NULL)
2075
+			if(chdir(tmpdir) < 0) {	/* safety */
2076
+				perror(tmpdir);
2077
+				logg("!chdir %s failed\n", tmpdir);
2078
+			}
2079
+	} else
2080
+		if(rootdir == NULL)
2081
+#ifdef	P_tmpdir
2082
+			if(chdir(P_tmpdir) < 0) {
2083
+				perror(P_tmpdir);
2084
+				logg("!chdir %s failed\n", P_tmpdir);
2085
+			}
2086
+#else
2087
+			if(chdir("/tmp") < 0) {
2088
+				perror("/tmp");
2089
+				logg("!chdir /tmp failed\n", P_tmpdir);
2090
+			}
2091
+#endif
2092
+
2093
+	if(smfi_setconn(port) == MI_FAILURE) {
2094
+		logg("!smfi_setconn failure\n");
2095
+		return EX_SOFTWARE;
2096
+	}
2097
+
2098
+	if(smfi_register(smfilter) == MI_FAILURE) {
2099
+		fprintf(stderr, "smfi_register failure, ensure that you have linked against the correct version of sendmail\n");
2100
+		return EX_UNAVAILABLE;
2101
+	}
2102
+
2103
+#if	((SENDMAIL_VERSION_A > 8) || ((SENDMAIL_VERSION_A == 8) && (SENDMAIL_VERSION_B >= 13)))
2104
+	if(smfi_opensocket(1) == MI_FAILURE) {
2105
+		perror(port);
2106
+		fprintf(stderr, "Can't open/create %s\n", port);
2107
+		return EX_CONFIG;
2108
+	}
2109
+#endif
2110
+
2111
+	signal(SIGPIPE, SIG_IGN);	/* libmilter probably does this as well */
2112
+	signal(SIGXFSZ, SIG_IGN); /* TODO: check if it's safe to call signal() here */
2113
+
2114
+#ifdef	SESSION
2115
+	pthread_mutex_lock(&version_mutex);
2116
+#endif
2117
+	logg(_("Starting %s\n"), clamav_version);
2118
+	logg(_("*Debugging is on\n"));
2119
+
2120
+#ifdef HAVE_RESOLV_H
2121
+#if ! defined(HAVE_LRESOLV_R)
2122
+	if(!(_res.options&RES_INIT))
2123
+		if(res_init() < 0) {
2124
+			fprintf(stderr, "%s: Can't initialise the resolver\n",
2125
+				argv[0]);
2126
+			return EX_UNAVAILABLE;
2127
+		}
2128
+#endif
2129
+	if(blacklist_time) {
2130
+		char name[MAXHOSTNAMELEN + 1];
2131
+
2132
+		if(gethostname(name, sizeof(name)) < 0) {
2133
+			perror("gethostname");
2134
+			return EX_UNAVAILABLE;
2135
+		}
2136
+
2137
+		blacklist = mx(name, NULL);
2138
+		if(blacklist)
2139
+			/* We must never blacklist ourself */
2140
+			tableInsert(blacklist, "127.0.0.1", 0);
2141
+
2142
+		if(wont_blacklist) {
2143
+			char *w;
2144
+
2145
+			i = 0;
2146
+			while((w = cli_strtok(wont_blacklist, i++, ",")) != NULL) {
2147
+				(void)tableInsert(blacklist, w, 0);
2148
+				free(w);
2149
+			}
2150
+		}
2151
+		tableIterate(blacklist, dump_blacklist, NULL);
2152
+	}
2153
+#endif /* HAVE_RESOLV_H */
2154
+
2155
+#ifdef	SESSION
2156
+	pthread_mutex_unlock(&version_mutex);
2157
+#endif
2158
+
2159
+	(void)signal(SIGSEGV, sigsegv);
2160
+	if(!logg_foreground)
2161
+		(void)signal(SIGUSR1, sigusr1);
2162
+	if(!external)
2163
+		(void)signal(SIGUSR2, sigusr2);
2164
+
2165
+	return smfi_main();
2166
+}
2167
+
2168
+#ifdef	SESSION
2169
+/*
2170
+ * Use the SESSION command of clamd.
2171
+ * Returns -1 for terminal failure, 0 for OK, 1 for nonterminal failure
2172
+ * The caller must take care of locking the sessions array
2173
+ */
2174
+static int
2175
+createSession(unsigned int s)
2176
+{
2177
+	int ret = 0, fd;
2178
+	const int serverNumber = s % numServers;
2179
+	struct session *session = &sessions[s];
2180
+	const struct protoent *proto;
2181
+	struct sockaddr_in server;
2182
+
2183
+	logg("#createSession session %d, server %d\n", s, serverNumber);
2184
+	assert(s < max_children);
2185
+
2186
+	memset((char *)&server, 0, sizeof(struct sockaddr_in));
2187
+	server.sin_family = AF_INET;
2188
+	server.sin_port = (in_port_t)htons(tcpSocket);
2189
+
2190
+	server.sin_addr.s_addr = serverIPs[serverNumber];
2191
+
2192
+	session->sock = -1;
2193
+	proto = getprotobyname("tcp");
2194
+	if(proto == NULL) {
2195
+		fputs("Unknown prototol tcp, check /etc/protocols\n", stderr);
2196
+		fd = ret = -1;
2197
+	} else if((fd = socket(AF_INET, SOCK_STREAM, proto->p_proto)) < 0) {
2198
+		perror("socket");
2199
+		ret = -1;
2200
+	} else if(connect(fd, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0) {
2201
+		perror("connect");
2202
+		ret = 1;
2203
+	} else if(send(fd, "SESSION\n", 8, 0) < 8) {
2204
+		perror("send");
2205
+		ret = 1;
2206
+	}
2207
+
2208
+	if(ret != 0) {
2209
+#ifdef	MAXHOSTNAMELEN
2210
+		char hostname[MAXHOSTNAMELEN + 1];
2211
+
2212
+		cli_strtokbuf(serverHostNames, serverNumber, ":", hostname);
2213
+		if(strcmp(hostname, "127.0.0.1") == 0)
2214
+			gethostname(hostname, sizeof(hostname));
2215
+#else
2216
+		char *hostname = cli_strtok(serverHostNames, serverNumber, ":");
2217
+#endif
2218
+
2219
+		session->status = CMDSOCKET_DOWN;
2220
+
2221
+		if(fd >= 0)
2222
+			close(fd);
2223
+
2224
+		logg(_("^Check clamd server %s - it may be down\n"), hostname);
2225
+#ifndef	MAXHOSTNAMELEN
2226
+		free(hostname);
2227
+#endif
2228
+
2229
+		broadcast(_("Check clamd server - it may be down"));
2230
+	} else
2231
+		session->sock = fd;
2232
+
2233
+	return ret;
2234
+}
2235
+
2236
+#else
2237
+
2238
+/*
2239
+ * Verify that the server is where we think it is
2240
+ * Returns true or false
2241
+ *
2242
+ * serverNumber counts from 0, but is only used for TCPSocket
2243
+ */
2244
+static int
2245
+pingServer(int serverNumber)
2246
+{
2247
+	char *ptr;
2248
+	int sock;
2249
+	long nbytes;
2250
+	char buf[128];
2251
+
2252
+	if(localSocket) {
2253
+		struct sockaddr_un server;
2254
+
2255
+		memset((char *)&server, 0, sizeof(struct sockaddr_un));
2256
+		server.sun_family = AF_UNIX;
2257
+		strncpy(server.sun_path, localSocket, sizeof(server.sun_path));
2258
+		server.sun_path[sizeof(server.sun_path)-1]='\0';
2259
+
2260
+		if((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
2261
+			perror(localSocket);
2262
+			return 0;
2263
+		}
2264
+		checkClamd(1);
2265
+		if(connect(sock, (struct sockaddr *)&server, sizeof(struct sockaddr_un)) < 0) {
2266
+			perror(localSocket);
2267
+			close(sock);
2268
+			return 0;
2269
+		}
2270
+	} else {
2271
+		struct sockaddr_in server;
2272
+		char *hostname;
2273
+
2274
+		memset((char *)&server, 0, sizeof(struct sockaddr_in));
2275
+		server.sin_family = AF_INET;
2276
+		server.sin_port = (in_port_t)htons(tcpSocket);
2277
+
2278
+		assert(serverIPs != NULL);
2279
+#ifdef	INADDR_NONE
2280
+		assert(serverIPs[0] != INADDR_NONE);
2281
+#else
2282
+		assert(serverIPs[0] != (in_addr_t)-1);
2283
+#endif
2284
+
2285
+		server.sin_addr.s_addr = serverIPs[serverNumber];
2286
+
2287
+		if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
2288
+			perror("socket");
2289
+			return 0;
2290
+		}
2291
+		hostname = cli_strtok(serverHostNames, serverNumber, ":");
2292
+		/*
2293
+		 * FIXME: use non-blocking connect, once the code is
2294
+		 * amalgomated
2295
+		 */
2296
+		if(nonblock_connect(sock, &server, hostname) < 0) {
2297
+			int is_connected = 0;
2298
+
2299
+#if	(!defined(NTRIES)) || ((NTRIES <= 1))
2300
+			if(errno == ECONNREFUSED) {
2301
+				/*
2302
+				 * During startup there is a race condition:
2303
+				 * clamd can start and fork, then rc will start
2304
+				 * clamav-milter before clamd has run accept(2),
2305
+				 * so we fail to connect.
2306
+				 * In case this is the situation here, we wait
2307
+				 * for a couple of seconds and try again. The
2308
+				 * sync() is because during startup the machine
2309
+				 * won't be doing much for most of the time, so
2310
+				 * we may as well do something constructive!
2311
+				 */
2312
+				sync();
2313
+				sleep(2);
2314
+				if(nonblock_connect(sock, &server, hostname) >= 0)
2315
+					is_connected = 1;
2316
+			}
2317
+#endif
2318
+			if(!is_connected) {
2319
+				if(errno != EINPROGRESS)
2320
+					perror(hostname ? hostname : "connect");
2321
+				close(sock);
2322
+				if(hostname)
2323
+					free(hostname);
2324
+				return 0;
2325
+			}
2326
+		}
2327
+		if(hostname)
2328
+			free(hostname);
2329
+	}
2330
+
2331
+	/*
2332
+	 * It would be better to use PING, check for PONG then issue the
2333
+	 * VERSION command, since that would better validate that we're
2334
+	 * talking to clamd, however clamd closes the session after
2335
+	 * sending PONG :-(
2336
+	 * So this code does not really validate that we're talking to clamd
2337
+	 * Needs a fix to clamd
2338
+	 * Also version command is verbose: says "clamd / ClamAV version"
2339
+	 * instead of "clamAV version"
2340
+	 */
2341
+	logg("#pingServer%d: sending VERSION\n", serverNumber);
2342
+	if(send(sock, "VERSION\n", 8, 0) < 8) {
2343
+		perror("send");
2344
+		return close(sock);
2345
+	}
2346
+
2347
+	shutdown(sock, SHUT_WR);
2348
+
2349
+	nbytes = clamd_recv(sock, buf, sizeof(buf) - 1);
2350
+
2351
+	close(sock);
2352
+
2353
+	if(nbytes < 0) {
2354
+		perror("recv");
2355
+		return 0;
2356
+	}
2357
+	if(nbytes == 0)
2358
+		return 0;
2359
+
2360
+	buf[nbytes] = '\0';
2361
+
2362
+	/* Remove the trailing new line from the reply */
2363
+	if((ptr = strchr(buf, '\n')) != NULL)
2364
+		*ptr = '\0';
2365
+
2366
+	/*
2367
+	 * No real validation is done here
2368
+	 *
2369
+	 * TODO: When connecting to more than one server, give a warning
2370
+	 *	if they're running different versions, or if the virus DBs
2371
+	 *	are out of date (say more than a month old)
2372
+	 */
2373
+	snprintf(clamav_version, sizeof(clamav_version) - 1,
2374
+		"%s\n\tclamav-milter version %s",
2375
+		buf, get_version());
2376
+
2377
+	return 1;
2378
+}
2379
+#endif
2380
+
2381
+/*
2382
+ * Find the best server to connect to. No intelligence to this.
2383
+ * It is best to weight the order of the servers from most wanted to least
2384
+ * wanted
2385
+ *
2386
+ * Return value is from 0 - index into sessions array
2387
+ *
2388
+ * If the load balancing fails return the first server in the list, not
2389
+ * an error, to be on the safe side
2390
+ */
2391
+#ifdef	SESSION
2392
+static int
2393
+findServer(void)
2394
+{
2395
+	unsigned int i, j;
2396
+	struct session *session;
2397
+
2398
+	/*
2399
+	 * FIXME: Sessions code isn't flexible at handling servers
2400
+	 *	appearing and disappearing, e.g. sessions[n_children].sock == -1
2401
+	 */
2402
+	i = 0;
2403
+	pthread_mutex_lock(&n_children_mutex);
2404
+	assert(n_children > 0);
2405
+	assert(n_children <= max_children);
2406
+	j = n_children - 1;
2407
+	pthread_mutex_unlock(&n_children_mutex);
2408
+
2409
+	pthread_mutex_lock(&sstatus_mutex);
2410
+	for(; i < max_children; i++) {
2411
+		const int sess = (j + i) % max_children;
2412
+
2413
+		session = &sessions[sess];
2414
+		logg("#findServer: try server %d\n", sess);
2415
+		if(session->status == CMDSOCKET_FREE) {
2416
+			session->status = CMDSOCKET_INUSE;
2417
+			pthread_mutex_unlock(&sstatus_mutex);
2418
+			return sess;
2419
+		}
2420
+	}
2421
+	pthread_mutex_unlock(&sstatus_mutex);
2422
+
2423
+	/*
2424
+	 * No session free - wait until one comes available. Only
2425
+	 * retries once.
2426
+	 */
2427
+	if(pthread_cond_broadcast(&watchdog_cond) < 0)
2428
+		perror("pthread_cond_broadcast");
2429
+
2430
+	i = 0;
2431
+	session = sessions;
2432
+	pthread_mutex_lock(&sstatus_mutex);
2433
+	for(; i < max_children; i++, session++) {
2434
+		logg("#findServer: try server %d\n", i);
2435
+		if(session->status == CMDSOCKET_FREE) {
2436
+			session->status = CMDSOCKET_INUSE;
2437
+			pthread_mutex_unlock(&sstatus_mutex);
2438
+			return i;
2439
+		}
2440
+	}
2441
+	pthread_mutex_unlock(&sstatus_mutex);
2442
+
2443
+	logg(_("^No free clamd sessions\n"));
2444
+
2445
+	return -1;	/* none available - must fail */
2446
+}
2447
+#else
2448
+/*
2449
+ * Return value is from 0 - index into serverIPs
2450
+ */
2451
+static int
2452
+findServer(void)
2453
+{
2454
+	struct sockaddr_in *servers, *server;
2455
+	int maxsock, i, j, active;
2456
+	int retval;
2457
+	pthread_t *tids;
2458
+	struct try_server_struct *socks;
2459
+	fd_set rfds;
2460
+
2461
+	assert(tcpSocket != 0);
2462
+	assert(numServers > 0);
2463
+
2464
+	if(numServers == 1)
2465
+		return 0;
2466
+
2467
+	if(active_servers(&active) <= 1)
2468
+		return active;
2469
+
2470
+	servers = (struct sockaddr_in *)cli_calloc(numServers, sizeof(struct sockaddr_in));
2471
+	if(servers == NULL)
2472
+		return 0;
2473
+	socks = (struct try_server_struct *)cli_malloc(numServers * sizeof(struct try_server_struct));
2474
+
2475
+	if(max_children > 0) {
2476
+		assert(n_children > 0);
2477
+		assert(n_children <= max_children);
2478
+
2479
+		/*
2480
+		 * Don't worry about no lock - it's doesn't matter if it's
2481
+		 * not really accurate
2482
+		 */
2483
+		j = n_children - 1;	/* look at the next free one */
2484
+		if(j < 0)
2485
+			j = 0;
2486
+	} else
2487
+		/*
2488
+		 * cli_rndnum returns 0..max
2489
+		 */
2490
+		j = cli_rndnum(numServers - 1);
2491
+
2492
+	for(i = 0; i < numServers; i++)
2493
+		socks[i].sock = -1;
2494
+
2495
+	tids = cli_malloc(numServers * sizeof(pthread_t));
2496
+
2497
+	for(i = 0, server = servers; i < numServers; i++, server++) {
2498
+		int sock;
2499
+		int server_index = (i + j) % numServers;
2500
+
2501
+		server->sin_family = AF_INET;
2502
+		server->sin_port = (in_port_t)htons(tcpSocket);
2503
+		server->sin_addr.s_addr = serverIPs[server_index];
2504
+
2505
+		logg("*findServer: try server %d\n", server_index);
2506
+
2507
+		sock = socks[i].sock = socket(AF_INET, SOCK_STREAM, 0);
2508
+
2509
+		if(sock < 0) {
2510
+			perror("socket");
2511
+			while(i--) {
2512
+				pthread_join(tids[i], NULL);
2513
+				if(socks[i].sock >= 0)
2514
+					close(socks[i].sock);
2515
+			}
2516
+			free(socks);
2517
+			free(servers);
2518
+			free(tids);
2519
+			return 0;	/* Use the first server on failure */
2520
+		}
2521
+
2522
+		socks[i].server = server;
2523
+		socks[i].server_index = server_index;
2524
+
2525
+		if(pthread_create(&tids[i], NULL, try_server, &socks[i]) != 0) {
2526
+			perror("pthread_create");
2527
+			j = i;
2528
+			do {
2529
+				if (j!=i) pthread_join(tids[i], NULL);
2530
+				if(socks[i].sock >= 0)
2531
+					close(socks[i].sock);
2532
+			} while(--i >= 0);
2533
+			free(socks);
2534
+			free(servers);
2535
+			free(tids);
2536
+			return 0;	/* Use the first server on failure */
2537
+		}
2538
+	}
2539
+
2540
+	maxsock = -1;
2541
+	FD_ZERO(&rfds);
2542
+
2543
+	for(i = 0; i < numServers; i++) {
2544
+		struct try_server_struct *rc;
2545
+
2546
+		pthread_join(tids[i], (void **)&rc);
2547
+		assert(rc->sock == socks[i].sock);
2548
+		if(rc->rc == 0) {
2549
+			close(rc->sock);
2550
+			socks[i].sock = -1;
2551
+		} else {
2552
+			shutdown(rc->sock, SHUT_WR);
2553
+			FD_SET(rc->sock, &rfds);
2554
+			if(rc->sock > maxsock)
2555
+				maxsock = rc->sock;
2556
+		}
2557
+	}
2558
+
2559
+	free(servers);
2560
+	free(tids);
2561
+
2562
+	if(maxsock == -1) {
2563
+		logg(_("^Couldn't establish a connexion to any clamd server\n"));
2564
+		retval = 0;
2565
+	} else {
2566
+		struct timeval tv;
2567
+
2568
+		tv.tv_sec = readTimeout ? readTimeout : DEFAULT_TIMEOUT;
2569
+		tv.tv_usec = 0;
2570
+
2571
+		retval = select(maxsock + 1, &rfds, NULL, NULL, &tv);
2572
+	}
2573
+
2574
+	if(retval < 0)
2575
+		perror("select");
2576
+
2577
+	for(i = 0; i < numServers; i++)
2578
+		if(socks[i].sock >= 0)
2579
+			close(socks[i].sock);
2580
+
2581
+	if(retval == 0) {
2582
+		free(socks);
2583
+		clamdIsDown();
2584
+		return 0;
2585
+	} else if(retval < 0) {
2586
+		free(socks);
2587
+		logg(_("^findServer: select failed (maxsock = %d)\n"), maxsock);
2588
+		return 0;
2589
+	}
2590
+
2591
+	for(i = 0; i < numServers; i++)
2592
+		if((socks[i].sock >= 0) && (FD_ISSET(socks[i].sock, &rfds))) {
2593
+			const int s = (i + j) % numServers;
2594
+
2595
+			free(socks);
2596
+			logg("*findServer: use server %d\n", s);
2597
+			return s;
2598
+		}
2599
+
2600
+	free(socks);
2601
+	logg(_("^findServer: No response from any server\n"));
2602
+	return 0;
2603
+}
2604
+
2605
+/*
2606
+ * How many servers are up at the moment? If a server is marked as down,
2607
+ *	don't keep on flooding it with requests to see if it's now back up
2608
+ * If only one server is active, let the caller know, which server is the
2609
+ *	active one
2610
+ */
2611
+static int
2612
+active_servers(int *active)
2613
+{
2614
+	int server, count;
2615
+	time_t now = (time_t)0;
2616
+
2617
+	for(count = server = 0; server < numServers; server++)
2618
+		if(last_failed_pings[server] == (time_t)0) {
2619
+			*active = server;
2620
+			count++;
2621
+		} else {
2622
+			if(now == (time_t)0)
2623
+				time(&now);
2624
+			if(now - last_failed_pings[server] >= RETRY_SECS)
2625
+				/* Try this server again next time */
2626
+				last_failed_pings[server] = (time_t)0;
2627
+		}
2628
+
2629
+	if(count != 1)
2630
+		*active = 0;
2631
+	return count;
2632
+}
2633
+
2634
+/*
2635
+ * Connecting to remote servers can take some time, so let's connect to
2636
+ *	them in parallel. This routine is started as a thread
2637
+ */
2638
+static void *
2639
+try_server(void *var)
2640
+{
2641
+	struct try_server_struct *s = (struct try_server_struct *)var;
2642
+	int sock = s->sock;
2643
+	struct sockaddr *server = (struct sockaddr *)s->server;
2644
+	int server_index = s->server_index;
2645
+
2646
+	if(last_failed_pings[server_index]) {
2647
+		s->rc = 0;
2648
+		return var;
2649
+	}
2650
+
2651
+	logg("*try_server: sock %d\n", sock);
2652
+
2653
+	if((connect(sock, server, sizeof(struct sockaddr)) < 0) ||
2654
+	   (send(sock, "PING\n", 5, 0) < 5)) {
2655
+		time(&last_failed_pings[server_index]);
2656
+		s->rc = 0;
2657
+	} else
2658
+		s->rc = 1;
2659
+
2660
+	if(s->rc == 0) {
2661
+#ifdef	MAXHOSTNAMELEN
2662
+		char hostname[MAXHOSTNAMELEN + 1];
2663
+
2664
+		cli_strtokbuf(serverHostNames, server_index, ":", hostname);
2665
+		if(strcmp(hostname, "127.0.0.1") == 0)
2666
+			gethostname(hostname, sizeof(hostname));
2667
+#else
2668
+		char *hostname = cli_strtok(serverHostNames, server_index, ":");
2669
+#endif
2670
+		perror(hostname);
2671
+		logg(_("^Check clamd server %s - it may be down\n"), hostname);
2672
+#ifndef	MAXHOSTNAMELEN
2673
+		free(hostname);
2674
+#endif
2675
+		broadcast(_("Check clamd server - it may be down\n"));
2676
+	}
2677
+
2678
+	return var;
2679
+}
2680
+#endif
2681
+
2682
+/*
2683
+ * Sendmail wants to establish a connexion to us
2684
+ * TODO: is it possible (desirable?) to determine if the remote machine has been
2685
+ *	compromised?
2686
+ */
2687
+static sfsistat
2688
+clamfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr)
2689
+{
2690
+#if	defined(HAVE_INET_NTOP) || defined(WITH_TCPWRAP)
2691
+	char ip[INET6_ADDRSTRLEN];
2692
+#endif
2693
+	int t;
2694
+	const char *remoteIP;
2695
+	struct privdata *privdata;
2696
+
2697
+	if(quitting)
2698
+		return cl_error;
2699
+
2700
+	if(ctx == NULL) {
2701
+		logg(_("!clamfi_connect: ctx is null"));
2702
+		return cl_error;
2703
+	}
2704
+	if(hostname == NULL) {
2705
+		logg(_("!clamfi_connect: hostname is null"));
2706
+		return cl_error;
2707
+	}
2708
+	if(smfi_getpriv(ctx) != NULL) {
2709
+		/* More than one connexion command, "can't happen" */
2710
+		logg("^clamfi_connect: called more than once\n");
2711
+		clamfi_cleanup(ctx);
2712
+		return cl_error;
2713
+	}
2714
+#ifdef AF_INET6
2715
+	if((hostaddr == NULL) ||
2716
+	   ((hostaddr->sa_family == AF_INET) && (&(((struct sockaddr_in *)(hostaddr))->sin_addr) == NULL)) ||
2717
+	   ((hostaddr->sa_family == AF_INET6) && (&(((struct sockaddr_in6 *)(hostaddr))->sin6_addr) == NULL)))
2718
+#else
2719
+	if((hostaddr == NULL) || (&(((struct sockaddr_in *)(hostaddr))->sin_addr) == NULL))
2720
+#endif
2721
+		/*
2722
+		 * According to the sendmail API hostaddr is NULL if
2723
+		 * "the type is not supported in the current version". What
2724
+		 * the documentation doesn't say is the type of what.
2725
+		 *
2726
+		 * Possibly the input is not a TCP/IP socket e.g. stdin?
2727
+		 */
2728
+		remoteIP = "127.0.0.1";
2729
+	else {
2730
+#ifdef HAVE_INET_NTOP
2731
+		switch(hostaddr->sa_family) {
2732
+			case AF_INET:
2733
+				remoteIP = (const char *)inet_ntop(AF_INET, &((struct sockaddr_in *)(hostaddr))->sin_addr, ip, sizeof(ip));
2734
+				break;
2735
+#ifdef AF_INET6
2736
+			case AF_INET6:
2737
+				remoteIP = (const char *)inet_ntop(AF_INET6, &((struct sockaddr_in6 *)(hostaddr))->sin6_addr, ip, sizeof(ip));
2738
+				break;
2739
+#endif
2740
+			default:
2741
+				logg(_("clamfi_connect: Unexpected sa_family %d\n"),
2742
+					hostaddr->sa_family);
2743
+				return cl_error;
2744
+		}
2745
+
2746
+#else
2747
+		remoteIP = inet_ntoa(((struct sockaddr_in *)(hostaddr))->sin_addr);
2748
+#endif
2749
+
2750
+		if(remoteIP == NULL) {
2751
+			logg(_("clamfi_connect: remoteIP is null"));
2752
+			return cl_error;
2753
+		}
2754
+	}
2755
+
2756
+#ifdef	CL_DEBUG
2757
+	if(debug_level >= 4) {
2758
+		if(hostname[0] == '[')
2759
+			logg(_("clamfi_connect: connexion from %s"), remoteIP);
2760
+		else
2761
+			logg(_("clamfi_connect: connexion from %s [%s]"), hostname, remoteIP);
2762
+	}
2763
+#endif
2764
+
2765
+#ifdef	WITH_TCPWRAP
2766
+	/*
2767
+	 * Support /etc/hosts.allow and /etc/hosts.deny
2768
+	 */
2769
+	if(strncasecmp(port, "inet:", 5) == 0) {
2770
+		const char *hostmail;
2771
+		struct hostent hostent;
2772
+		char buf[BUFSIZ];
2773
+		static pthread_mutex_t wrap_mutex = PTHREAD_MUTEX_INITIALIZER;
2774
+
2775
+		/*
2776
+		 * Using TCP/IP for the sendmail->clamav-milter connexion
2777
+		 */
2778
+		if(((hostmail = smfi_getsymval(ctx, "{if_name}")) == NULL) &&
2779
+		   ((hostmail = smfi_getsymval(ctx, "j")) == NULL)) {
2780
+			logg(_("Can't get sendmail hostname"));
2781
+			return cl_error;
2782
+		}
2783
+		/*
2784
+		 * Use hostmail for error statements, not hostname, suggestion
2785
+		 * by Yar Tikhiy <yar@comp.chem.msu.su>
2786
+		 */
2787
+		if(r_gethostbyname(hostmail, &hostent, buf, sizeof(buf)) != 0) {
2788
+			logg(_("^Access Denied: Host Unknown (%s)"), hostmail);
2789
+			if(hostmail[0] == '[')
2790
+				/*
2791
+				 * A case could be made that it's not clamAV's
2792
+				 * job to check a system's DNS configuration
2793
+				 * and let this message through. However I am
2794
+				 * just too worried about any knock on effects
2795
+				 * to do that...
2796
+				 */
2797
+				logg(_("^Can't find entry for IP address %s in DNS - check your DNS setting\n"),
2798
+					hostmail);
2799
+			return cl_error;
2800
+		}
2801
+
2802
+#ifdef HAVE_INET_NTOP
2803
+		if(hostent.h_addr &&
2804
+		   (inet_ntop(AF_INET, (struct in_addr *)hostent.h_addr, ip, sizeof(ip)) == NULL)) {
2805
+			perror(hostent.h_name);
2806
+			/*strcpy(ip, (char *)inet_ntoa(*(struct in_addr *)hostent.h_addr));*/
2807
+			logg(_("^Access Denied: Can't get IP address for (%s)"), hostent.h_name);
2808
+			return cl_error;
2809
+		}
2810
+#else
2811
+		strncpy(ip, (char *)inet_ntoa(*(struct in_addr *)hostent.h_addr), sizeof(ip));
2812
+		ip[sizeof(ip)-1]='\0';
2813
+#endif
2814
+
2815
+		/*
2816
+		 * Ask is this is a allowed name or IP number
2817
+		 *
2818
+		 * hosts_ctl uses strtok so it is not thread safe, see
2819
+		 * hosts_access(3)
2820
+		 */
2821
+		pthread_mutex_lock(&wrap_mutex);
2822
+		if(!hosts_ctl(progname, hostent.h_name, ip, STRING_UNKNOWN)) {
2823
+			pthread_mutex_unlock(&wrap_mutex);
2824
+			logg(_("^Access Denied for %s[%s]"), hostent.h_name, ip);
2825
+			return SMFIS_TEMPFAIL;
2826
+		}
2827
+		pthread_mutex_unlock(&wrap_mutex);
2828
+	}
2829
+#endif	/*WITH_TCPWRAP*/
2830
+
2831
+	if(fflag)
2832
+		/*
2833
+		 * Patch from "Richard G. Roberto" <rgr@dedlegend.com>
2834
+		 * Always scan whereever the message is from
2835
+		 */
2836
+		return SMFIS_CONTINUE;
2837
+
2838
+	if(!oflag)
2839
+		if(strcmp(remoteIP, "127.0.0.1") == 0) {
2840
+			logg(_("*clamfi_connect: not scanning outgoing messages"));
2841
+			return SMFIS_ACCEPT;
2842
+		}
2843
+
2844
+	if((!lflag) && isLocal(remoteIP)) {
2845
+#ifdef	CL_DEBUG
2846
+		logg(_("*clamfi_connect: not scanning local messages\n"));
2847
+#endif
2848
+		return SMFIS_ACCEPT;
2849
+	}
2850
+
2851
+#if	defined(HAVE_INET_NTOP) || defined(WITH_TCPWRAP)
2852
+	if(detect_forged_local_address && !isLocal(ip)) {
2853
+#else
2854
+	if(detect_forged_local_address && !isLocal(remoteIP)) {
2855
+#endif
2856
+		char me[MAXHOSTNAMELEN + 1];
2857
+
2858
+		if(gethostname(me, sizeof(me) - 1) < 0) {
2859
+			logg(_("^clamfi_connect: gethostname failed"));
2860
+			return SMFIS_CONTINUE;
2861
+		}
2862
+		logg("*me '%s' hostname '%s'\n", me, hostname);
2863
+		if(strcasecmp(hostname, me) == 0) {
2864
+			logg(_("Rejected connexion falsely claiming to be from here\n"));
2865
+			smfi_setreply(ctx, "550", "5.7.1", _("You have claimed to be me, but you are not"));
2866
+			broadcast(_("Forged local address detected"));
2867
+			return SMFIS_REJECT;
2868
+		}
2869
+	}
2870
+	if(isBlacklisted(remoteIP)) {
2871
+		char mess[128];
2872
+
2873
+		/*
2874
+		 * TODO: Option to greylist rather than blacklist, by sending
2875
+		 *	a try again code
2876
+		 * TODO: state *which* virus
2877
+		 * TODO: add optional list of IP addresses that won't be
2878
+		 *	blacklisted
2879
+		 */
2880
+		logg("Rejected connexion from blacklisted IP %s\n", remoteIP);
2881
+
2882
+		snprintf(mess, sizeof(mess), _("%s is blacklisted because your machine is infected with a virus"), remoteIP);
2883
+		smfi_setreply(ctx, "550", "5.7.1", mess);
2884
+		broadcast(_("Blacklisted IP detected"));
2885
+
2886
+		/*
2887
+		 * Keep them blacklisted
2888
+		 */
2889
+		pthread_mutex_lock(&blacklist_mutex);
2890
+		(void)tableUpdate(blacklist, remoteIP, (int)time((time_t *)0));
2891
+		pthread_mutex_unlock(&blacklist_mutex);
2892
+
2893
+		return SMFIS_REJECT;
2894
+	}
2895
+
2896
+	if(blacklist_time == 0)
2897
+		return SMFIS_CONTINUE;	/* allocate privdata per message */
2898
+
2899
+	pthread_mutex_lock(&blacklist_mutex);
2900
+	t = tableFind(blacklist, remoteIP);
2901
+	pthread_mutex_unlock(&blacklist_mutex);
2902
+
2903
+	if(t == 0)
2904
+		return SMFIS_CONTINUE;	/* this IP will never be blacklisted */
2905
+
2906
+	privdata = (struct privdata *)cli_calloc(1, sizeof(struct privdata));
2907
+	if(privdata == NULL)
2908
+		return cl_error;
2909
+
2910
+#ifdef	SESSION
2911
+	privdata->dataSocket = -1;
2912
+#else
2913
+	privdata->dataSocket = privdata->cmdSocket = -1;
2914
+#endif
2915
+
2916
+	if(smfi_setpriv(ctx, privdata) == MI_SUCCESS) {
2917
+		strcpy(privdata->ip, remoteIP);
2918
+		return SMFIS_CONTINUE;
2919
+	}
2920
+
2921
+	free(privdata);
2922
+
2923
+	return cl_error;
2924
+}
2925
+
2926
+/*
2927
+ * Since sendmail requires that MAIL FROM is called before RCPT TO, it is
2928
+ *	safe to assume that this routine is called first, so the n_children
2929
+ *	handler is put here
2930
+ */
2931
+static sfsistat
2932
+clamfi_envfrom(SMFICTX *ctx, char **argv)
2933
+{
2934
+	struct privdata *privdata;
2935
+	const char *mailaddr = argv[0];
2936
+
2937
+	logg("*clamfi_envfrom: %s\n", argv[0]);
2938
+
2939
+	if(isWhitelisted(argv[0], 0)) {
2940
+		logg(_("*clamfi_envfrom: ignoring whitelisted message"));
2941
+		return SMFIS_ACCEPT;
2942
+	}
2943
+
2944
+	if(strcmp(argv[0], "<>") == 0) {
2945
+		mailaddr = smfi_getsymval(ctx, "{mail_addr}");
2946
+		if(mailaddr == NULL)
2947
+			mailaddr = smfi_getsymval(ctx, "_");
2948
+
2949
+		if(mailaddr && *mailaddr)
2950
+			logg("#Message from \"%s\" has no from field\n", mailaddr);
2951
+		else {
2952
+#if	0
2953
+			if(use_syslog)
2954
+				syslog(LOG_NOTICE, _("Rejected email with empty from field"));
2955
+			smfi_setreply(ctx, "554", "5.7.1", _("You have not said who the email is from"));
2956
+			broadcast(_("Reject email with empty from field"));
2957
+			clamfi_cleanup(ctx);
2958
+			return SMFIS_REJECT;
2959
+#endif
2960
+			mailaddr = "<>";
2961
+		}
2962
+	}
2963
+	privdata = smfi_getpriv(ctx);
2964
+
2965
+	if(privdata == NULL) {
2966
+		privdata = (struct privdata *)cli_calloc(1, sizeof(struct privdata));
2967
+		if(privdata == NULL)
2968
+			return cl_error;
2969
+		if(smfi_setpriv(ctx, privdata) != MI_SUCCESS) {
2970
+			free(privdata);
2971
+			return cl_error;
2972
+		}
2973
+		if(!increment_connexions()) {
2974
+			smfi_setreply(ctx, "451", "4.3.2", _("AV system temporarily overloaded - please try later"));
2975
+			free(privdata);
2976
+			smfi_setpriv(ctx, NULL);
2977
+			return SMFIS_TEMPFAIL;
2978
+		}
2979
+	} else {
2980
+		/* More than one message on this connexion */
2981
+		char ip[INET6_ADDRSTRLEN];
2982
+
2983
+		strcpy(ip, privdata->ip);
2984
+		if(isBlacklisted(ip)) {
2985
+			char mess[80 + INET6_ADDRSTRLEN];
2986
+
2987
+			logg("Rejected email from blacklisted IP %s\n", ip);
2988
+
2989
+			/*
2990
+			 * TODO: Option to greylist rather than blacklist, by
2991
+			 *	sending	a try again code
2992
+			 * TODO: state *which* virus
2993
+			 */
2994
+			sprintf(mess, "Your IP (%s) is blacklisted because your machine is infected with a virus", ip);
2995
+			smfi_setreply(ctx, "550", "5.7.1", mess);
2996
+			broadcast(_("Blacklisted IP detected"));
2997
+
2998
+			/*
2999
+			 * Keep them blacklisted
3000
+			 */
3001
+			pthread_mutex_lock(&blacklist_mutex);
3002
+			(void)tableUpdate(blacklist, ip, (int)time((time_t *)0));
3003
+			pthread_mutex_unlock(&blacklist_mutex);
3004
+
3005
+			return SMFIS_REJECT;
3006
+		}
3007
+		clamfi_free(privdata, 1);
3008
+		strcpy(privdata->ip, ip);
3009
+	}
3010
+
3011
+#ifdef	SESSION
3012
+	privdata->dataSocket = -1;
3013
+#else
3014
+	privdata->dataSocket = privdata->cmdSocket = -1;
3015
+#endif
3016
+
3017
+	/*
3018
+	 * Rejection is via 550 until DATA is received. We know that
3019
+	 * DATA has been sent when either we get a header or the end of
3020
+	 * header statement
3021
+	 */
3022
+	privdata->rejectCode = "550";
3023
+
3024
+	privdata->from = cli_strdup(mailaddr);
3025
+
3026
+	if(hflag) {
3027
+		privdata->headers = header_list_new();
3028
+
3029
+		if(privdata->headers == NULL) {
3030
+			clamfi_free(privdata, 1);
3031
+			return cl_error;
3032
+		}
3033
+	}
3034
+
3035
+	return SMFIS_CONTINUE;
3036
+}
3037
+
3038
+#ifdef	CL_DEBUG
3039
+static sfsistat
3040
+clamfi_helo(SMFICTX *ctx, char *helostring)
3041
+{
3042
+	logg("HELO '%s'\n", helostring);
3043
+
3044
+	return SMFIS_CONTINUE;
3045
+}
3046
+#endif
3047
+
3048
+static sfsistat
3049
+clamfi_envrcpt(SMFICTX *ctx, char **argv)
3050
+{
3051
+	struct privdata *privdata = (struct privdata *)smfi_getpriv(ctx);
3052
+	const char *to, *ptr;
3053
+
3054
+	logg("*clamfi_envrcpt: %s\n", argv[0]);
3055
+
3056
+	if(privdata == NULL)	/* sanity check */
3057
+		return cl_error;
3058
+
3059
+	if(privdata->to == NULL) {
3060
+		privdata->to = cli_malloc(sizeof(char *) * 2);
3061
+
3062
+		assert(privdata->numTo == 0);
3063
+	} else
3064
+		privdata->to = cli_realloc(privdata->to, sizeof(char *) * (privdata->numTo + 2));
3065
+
3066
+	if(privdata->to == NULL)
3067
+		return cl_error;
3068
+
3069
+	to = smfi_getsymval(ctx, "{rcpt_addr}");
3070
+	if(to == NULL)
3071
+		to = argv[0];
3072
+
3073
+	for(ptr = to; !dont_sanitise && *ptr; ptr++)
3074
+		if(strchr("|;", *ptr) != NULL) {
3075
+			smfi_setreply(ctx, "554", "5.7.1", _("Suspicious recipient address blocked"));
3076
+			logg("^Suspicious recipient address blocked: '%s'\n", to);
3077
+			privdata->to[privdata->numTo] = NULL;
3078
+			if(blacklist_time && privdata->ip[0]) {
3079
+				logg(_("Will blacklist %s for %d seconds because of cracking attempt\n"),
3080
+					privdata->ip, blacklist_time);
3081
+				pthread_mutex_lock(&blacklist_mutex);
3082
+				(void)tableUpdate(blacklist, privdata->ip,
3083
+					(int)time((time_t *)0));
3084
+				pthread_mutex_unlock(&blacklist_mutex);
3085
+			}
3086
+			/*
3087
+			 * REJECT rejects this recipient, not the entire email
3088
+			 */
3089
+			return SMFIS_REJECT;
3090
+		}
3091
+
3092
+	privdata->to[privdata->numTo] = cli_strdup(to);
3093
+	privdata->to[++privdata->numTo] = NULL;
3094
+
3095
+	return SMFIS_CONTINUE;
3096
+}
3097
+
3098
+static sfsistat
3099
+clamfi_header(SMFICTX *ctx, char *headerf, char *headerv)
3100
+{
3101
+	struct privdata *privdata = (struct privdata *)smfi_getpriv(ctx);
3102
+
3103
+#ifdef	CL_DEBUG
3104
+	if(debug_level >= 9)
3105
+		logg("*clamfi_header: %s: %s\n", headerf, headerv);
3106
+	else
3107
+		logg("*clamfi_header: %s\n", headerf);
3108
+#else
3109
+	logg("*clamfi_header: %s\n", headerf);
3110
+#endif
3111
+
3112
+	/*
3113
+	 * The DATA instruction from SMTP (RFC2821) must have been sent
3114
+	 */
3115
+	privdata->rejectCode = "554";
3116
+
3117
+	if(hflag)
3118
+		header_list_add(privdata->headers, headerf, headerv);
3119
+	else if((strcasecmp(headerf, "Received") == 0) &&
3120
+		(strncasecmp(headerv, "from ", 5) == 0) &&
3121
+		(strstr(headerv, "localhost") != 0)) {
3122
+		if(privdata->received)
3123
+			free(privdata->received);
3124
+		privdata->received = cli_strdup(headerv);
3125
+	}
3126
+
3127
+	if((strcasecmp(headerf, "Message-ID") == 0) &&
3128
+	   (strncasecmp(headerv, "<MDAEMON", 8) == 0))
3129
+		privdata->discard = 1;
3130
+	else if((strcasecmp(headerf, "Subject") == 0) && headerv) {
3131
+		if(privdata->subject)
3132
+			free(privdata->subject);
3133
+		if(headerv)
3134
+			privdata->subject = cli_strdup(headerv);
3135
+	} else if(strcasecmp(headerf, "X-Virus-Status") == 0)
3136
+		privdata->statusCount++;
3137
+	else if((strcasecmp(headerf, "Sender") == 0) && headerv) {
3138
+		if(privdata->sender)
3139
+			free(privdata->sender);
3140
+		privdata->sender = cli_strdup(headerv);
3141
+	}
3142
+#ifdef	HAVE_RESOLV_H
3143
+	else if((strcasecmp(headerf, "From") == 0) && headerv) {
3144
+		/*
3145
+		 * SPF check against the from header, since the SMTP header
3146
+		 * may be valid. This is not what the SPF spec says, but I
3147
+		 * have seen SPF matches on what are clearly phishes, so by
3148
+		 * checking against the from: header we're less likely to
3149
+		 * FP a real phish
3150
+		 */
3151
+		if(privdata->from)
3152
+			free(privdata->from);
3153
+		privdata->from = cli_strdup(headerv);
3154
+	}
3155
+#endif
3156
+
3157
+	if(!useful_header(headerf)) {
3158
+		logg("*Discarded the header\n");
3159
+		return SMFIS_CONTINUE;
3160
+	}
3161
+
3162
+	if(privdata->dataSocket == -1)
3163
+		/*
3164
+		 * First header - make connexion with clamd
3165
+		 */
3166
+		if(!connect2clamd(privdata)) {
3167
+			clamfi_cleanup(ctx);
3168
+			return cl_error;
3169
+		}
3170
+
3171
+	if(clamfi_send(privdata, 0, "%s: %s\n", headerf, headerv) <= 0) {
3172
+		clamfi_cleanup(ctx);
3173
+		return cl_error;
3174
+	}
3175
+
3176
+	return SMFIS_CONTINUE;
3177
+}
3178
+
3179
+/*
3180
+ * At this point DATA will have been received, so we really ought to
3181
+ * send 554 back not 550
3182
+ */
3183
+static sfsistat
3184
+clamfi_eoh(SMFICTX *ctx)
3185
+{
3186
+	struct privdata *privdata = (struct privdata *)smfi_getpriv(ctx);
3187
+	char **to;
3188
+
3189
+	logg(_("*clamfi_eoh\n"));
3190
+
3191
+	/*
3192
+	 * The DATA instruction from SMTP (RFC2821) must have been sent
3193
+	 */
3194
+	privdata->rejectCode = "554";
3195
+
3196
+	if(privdata->dataSocket == -1)
3197
+		/*
3198
+		 * No headers - make connexion with clamd
3199
+		 */
3200
+		if(!connect2clamd(privdata)) {
3201
+			clamfi_cleanup(ctx);
3202
+			return cl_error;
3203
+		}
3204
+
3205
+#if	0
3206
+	/* Mailing lists often say our own posts are from us */
3207
+	if(detect_forged_local_address && privdata->from &&
3208
+	   (!privdata->sender) && !isWhitelisted(privdata->from, 1)) {
3209
+		char me[MAXHOSTNAMELEN + 1];
3210
+		const char *ptr;
3211
+
3212
+		if(gethostname(me, sizeof(me) - 1) < 0) {
3213
+			if(use_syslog)
3214
+				syslog(LOG_WARNING, _("clamfi_eoh: gethostname failed"));
3215
+			return SMFIS_CONTINUE;
3216
+		}
3217
+		ptr = strstr(privdata->from, me);
3218
+		if(ptr && (ptr != privdata->from) && (*--ptr == '@')) {
3219
+			if(use_syslog)
3220
+				syslog(LOG_NOTICE, _("Rejected email falsely claiming to be from %s"), privdata->from);
3221
+			smfi_setreply(ctx, "554", "5.7.1", _("You have claimed to be from me, but you are not"));
3222
+			broadcast(_("Forged local address detected"));
3223
+			clamfi_cleanup(ctx);
3224
+			return SMFIS_REJECT;
3225
+		}
3226
+	}
3227
+#endif
3228
+
3229
+	if(clamfi_send(privdata, 1, "\n") != 1) {
3230
+		clamfi_cleanup(ctx);
3231
+		return cl_error;
3232
+	}
3233
+
3234
+	if(black_hole_mode) {
3235
+		sfsistat rc = black_hole(privdata);
3236
+
3237
+		if(rc != SMFIS_CONTINUE) {
3238
+			clamfi_cleanup(ctx);
3239
+			return rc;
3240
+		}
3241
+	}
3242
+
3243
+	/*
3244
+	 * See if the e-mail is only going to members of the list
3245
+	 * of users we don't scan for. If it is, don't scan, otherwise
3246
+	 * scan
3247
+	 *
3248
+	 * scan = false
3249
+	 * FORALL recipients
3250
+	 *	IF receipient NOT MEMBER OF white address list
3251
+	 *	THEN
3252
+	 *		scan = true
3253
+	 *	FI
3254
+	 * ENDFOR
3255
+	 */
3256
+	for(to = privdata->to; *to; to++)
3257
+		if(!isWhitelisted(*to, 1))
3258
+			/*
3259
+			 * This recipient is not on the whitelist,
3260
+			 * no need to check any further
3261
+			 */
3262
+			return SMFIS_CONTINUE;
3263
+
3264
+	/*
3265
+	 * Didn't find a recipient who is not on the white list, so all
3266
+	 * must be on the white list, so just accept the e-mail
3267
+	 */
3268
+	logg(_("*clamfi_enveoh: ignoring whitelisted message"));
3269
+	clamfi_cleanup(ctx);
3270
+
3271
+	return SMFIS_ACCEPT;
3272
+}
3273
+
3274
+static sfsistat
3275
+clamfi_body(SMFICTX *ctx, u_char *bodyp, size_t len)
3276
+{
3277
+	struct privdata *privdata = (struct privdata *)smfi_getpriv(ctx);
3278
+	int nbytes;
3279
+
3280
+	logg(_("*clamfi_envbody: %lu bytes"), (unsigned long)len);
3281
+
3282
+	if(len == 0)	/* unlikely */
3283
+		return SMFIS_CONTINUE;
3284
+
3285
+	if(privdata == NULL)	/* sanity check */
3286
+		return cl_error;
3287
+
3288
+	/*
3289
+	 * TODO:
3290
+	 *	If not in external mode, call cli_scanbuff here, at least for
3291
+	 * the first block
3292
+	 */
3293
+	/*
3294
+	 * Lines starting with From are changed to >From, to
3295
+	 *	avoid FP matches in the scanning code, which will speed it up
3296
+	 */
3297
+	if((len >= 6) && cli_memstr((char *)bodyp, len, "\nFrom ", 6)) {
3298
+		const char *ptr = (const char *)bodyp;
3299
+		int left = len;
3300
+
3301
+		nbytes = 0;
3302
+
3303
+		/*
3304
+		 * FIXME: sending one byte at a time down a socket is
3305
+		 *	inefficient
3306
+		 */
3307
+		do {
3308
+			if(*ptr == '\n') {
3309
+				/*
3310
+				 * FIXME: doesn't work if the \nFrom straddles
3311
+				 * multiple calls to clamfi_body
3312
+				 */
3313
+				if(strncmp(ptr, "\nFrom ", 6) == 0) {
3314
+					nbytes += clamfi_send(privdata, 7, "\n>From ");
3315
+					ptr += 6;
3316
+					left -= 6;
3317
+				} else {
3318
+					nbytes += clamfi_send(privdata, 1, "\n");
3319
+					ptr++;
3320
+					left--;
3321
+				}
3322
+			} else {
3323
+				nbytes += clamfi_send(privdata, 1, ptr++);
3324
+				left--;
3325
+			}
3326
+			if(left < 6 && left > 0) {
3327
+				nbytes += clamfi_send(privdata, left, ptr);
3328
+				break;
3329
+			}
3330
+		} while(left > 0);
3331
+	} else
3332
+		nbytes = clamfi_send(privdata, len, (char *)bodyp);
3333
+
3334
+	if(streamMaxLength > 0L) {
3335
+		if(privdata->numBytes > streamMaxLength) {
3336
+			const char *sendmailId = smfi_getsymval(ctx, "i");
3337
+
3338
+			if(sendmailId == NULL)
3339
+				sendmailId = "Unknown";
3340
+			logg(_("%s: Message more than StreamMaxLength (%ld) bytes - not scanned\n"),
3341
+				sendmailId, streamMaxLength);
3342
+			if(!nflag)
3343
+				smfi_addheader(ctx, "X-Virus-Status", _("Not Scanned - StreamMaxLength exceeded"));
3344
+
3345
+			return SMFIS_ACCEPT;	/* clamfi_close will be called */
3346
+		}
3347
+	}
3348
+	if(nbytes < (int)len) {
3349
+		clamfi_cleanup(ctx);	/* not needed, but just to be safe */
3350
+		return cl_error;
3351
+	}
3352
+	if(Sflag) {
3353
+		if(privdata->body) {
3354
+			assert(privdata->bodyLen > 0);
3355
+			privdata->body = cli_realloc(privdata->body, privdata->bodyLen + len);
3356
+			memcpy(&privdata->body[privdata->bodyLen], bodyp, len);
3357
+			privdata->bodyLen += len;
3358
+		} else {
3359
+			assert(privdata->bodyLen == 0);
3360
+			privdata->body = cli_malloc(len);
3361
+			memcpy(privdata->body, bodyp, len);
3362
+			privdata->bodyLen = len;
3363
+		}
3364
+	}
3365
+	return SMFIS_CONTINUE;
3366
+}
3367
+
3368
+static sfsistat
3369
+clamfi_eom(SMFICTX *ctx)
3370
+{
3371
+	int rc = SMFIS_CONTINUE;
3372
+	char *ptr;
3373
+	const char *sendmailId;
3374
+	struct privdata *privdata = (struct privdata *)smfi_getpriv(ctx);
3375
+	char mess[128];
3376
+#ifdef	SESSION
3377
+	struct session *session;
3378
+#endif
3379
+
3380
+	logg("*clamfi_eom\n");
3381
+
3382
+#ifdef	CL_DEBUG
3383
+	assert(privdata != NULL);
3384
+#ifndef	SESSION
3385
+	assert((privdata->cmdSocket >= 0) || (privdata->filename != NULL));
3386
+	assert(!((privdata->cmdSocket >= 0) && (privdata->filename != NULL)));
3387
+#endif
3388
+#endif
3389
+
3390
+	if(external) {
3391
+		shutdown(privdata->dataSocket, SHUT_WR);	/* bug 487 */
3392
+		close(privdata->dataSocket);
3393
+		privdata->dataSocket = -1;
3394
+	}
3395
+
3396
+	if(!nflag) {
3397
+		/*
3398
+		 * remove any existing claims that it's virus free so that
3399
+		 * downstream checkers aren't fooled by a carefully crafted
3400
+		 * virus.
3401
+		 */
3402
+		int i;
3403
+
3404
+		for(i = privdata->statusCount; i > 0; --i)
3405
+			if(smfi_chgheader(ctx, "X-Virus-Status", i, NULL) == MI_FAILURE)
3406
+				logg(_("^Failed to delete X-Virus-Status header %d\n"), i);
3407
+	}
3408
+
3409
+	if(!external) {
3410
+		const char *virname;
3411
+		int ret;
3412
+		struct  cl_engine *cur_engine;
3413
+
3414
+		pthread_mutex_lock(&engine_mutex);
3415
+		ret = cl_engine_addref(engine);
3416
+		cur_engine = engine; /* avoid races */
3417
+		pthread_mutex_unlock(&engine_mutex);
3418
+		if(ret != CL_SUCCESS) {
3419
+			logg("!cl_engine_addref failed\n");
3420
+			clamfi_cleanup(ctx);
3421
+			return cl_error;
3422
+		}
3423
+		switch(cl_scanfile(privdata->filename, &virname, NULL, cur_engine, options)) {
3424
+			case CL_CLEAN:
3425
+				if(logok)
3426
+					logg("#%s: OK\n", privdata->filename);
3427
+				strcpy(mess, "OK");
3428
+				break;
3429
+			case CL_VIRUS:
3430
+				snprintf(mess, sizeof(mess), "%s: %s FOUND", privdata->filename, virname);
3431
+				logg("#%s\n", mess);
3432
+				break;
3433
+			default:
3434
+				snprintf(mess, sizeof(mess), "%s: ERROR", privdata->filename);
3435
+				logg("!%s\n", mess);
3436
+				break;
3437
+		}
3438
+		cl_engine_free(cur_engine); /* drop reference or free */
3439
+
3440
+#ifdef	SESSION
3441
+		session = NULL;
3442
+#endif
3443
+	} else if(privdata->filename) {
3444
+		char cmdbuf[1024];
3445
+		/*
3446
+		 * Create socket to talk to clamd.
3447
+		 */
3448
+#ifndef	SESSION
3449
+		struct sockaddr_un server;
3450
+#endif
3451
+		long nbytes;
3452
+
3453
+		snprintf(cmdbuf, sizeof(cmdbuf) - 1, "SCAN %s", privdata->filename);
3454
+		logg("#clamfi_eom: SCAN %s\n", privdata->filename);
3455
+
3456
+		nbytes = (int)strlen(cmdbuf);
3457
+
3458
+#ifdef	SESSION
3459
+		session = sessions;
3460
+		if(send(session->sock, cmdbuf, nbytes, 0) < nbytes) {
3461
+			perror("send");
3462
+			clamfi_cleanup(ctx);
3463
+			logg(_("failed to send SCAN %s command to clamd\n"), privdata->filename);
3464
+			return cl_error;
3465
+		}
3466
+#else
3467
+		if((privdata->cmdSocket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
3468
+			perror("socket");
3469
+			clamfi_cleanup(ctx);
3470
+			return cl_error;
3471
+		}
3472
+		memset((char *)&server, 0, sizeof(struct sockaddr_un));
3473
+		server.sun_family = AF_UNIX;
3474
+		strncpy(server.sun_path, localSocket, sizeof(server.sun_path));
3475
+		server.sun_path[sizeof(server.sun_path)-1]='\0';
3476
+
3477
+		if(connect(privdata->cmdSocket, (struct sockaddr *)&server, sizeof(struct sockaddr_un)) < 0) {
3478
+			perror(localSocket);
3479
+			clamfi_cleanup(ctx);
3480
+			return cl_error;
3481
+		}
3482
+		if(send(privdata->cmdSocket, cmdbuf, nbytes, 0) < nbytes) {
3483
+			perror("send");
3484
+			clamfi_cleanup(ctx);
3485
+			logg(_("failed to send SCAN command to clamd\n"));
3486
+			return cl_error;
3487
+		}
3488
+
3489
+		shutdown(privdata->cmdSocket, SHUT_WR);
3490
+#endif
3491
+	}
3492
+#ifdef	SESSION
3493
+	else
3494
+		session = &sessions[privdata->serverNumber];
3495
+#endif
3496
+
3497
+	sendmailId = smfi_getsymval(ctx, "i");
3498
+	if(sendmailId == NULL)
3499
+		sendmailId = "Unknown";
3500
+
3501
+	if(external) {
3502
+		int nbytes;
3503
+#ifdef	SESSION
3504
+#ifdef	CL_DEBUG
3505
+		if(debug_level >= 4)
3506
+			logg(_("#Waiting to read status from fd %d\n"),
3507
+				session->sock);
3508
+#endif
3509
+		nbytes = clamd_recv(session->sock, mess, sizeof(mess) - 1);
3510
+#else
3511
+		nbytes = clamd_recv(privdata->cmdSocket, mess, sizeof(mess) - 1);
3512
+#endif
3513
+		if(nbytes > 0) {
3514
+			mess[nbytes] = '\0';
3515
+			if((ptr = strchr(mess, '\n')) != NULL)
3516
+				*ptr = '\0';
3517
+
3518
+			logg(_("*clamfi_eom: read %s\n"), mess);
3519
+		} else {
3520
+#ifdef	MAXHOSTNAMELEN
3521
+			char hostname[MAXHOSTNAMELEN + 1];
3522
+
3523
+			cli_strtokbuf(serverHostNames, privdata->serverNumber, ":", hostname);
3524
+			if(strcmp(hostname, "127.0.0.1") == 0)
3525
+				gethostname(hostname, sizeof(hostname));
3526
+#else
3527
+			char *hostname = cli_strtok(serverHostNames, privdata->serverNumber, ":");
3528
+#endif
3529
+			if(privdata->subject)
3530
+				logg(_("^%s: clamfi_eom: read nothing from clamd on %s, from %s (%s)\n"),
3531
+					sendmailId, hostname, privdata->from,
3532
+					privdata->subject);
3533
+			else
3534
+				logg(_("^%s: clamfi_eom: read nothing from clamd on %s, from %s\n"),
3535
+					sendmailId, hostname, privdata->from);
3536
+
3537
+			if((!nflag) && (cl_error == SMFIS_ACCEPT))
3538
+				smfi_addheader(ctx, "X-Virus-Status", _("Not Scanned - Read timeout exceeded"));
3539
+#ifndef	MAXHOSTNAMELEN
3540
+			free(hostname);
3541
+#endif
3542
+
3543
+#ifdef	CL_DEBUG
3544
+			/*
3545
+			 * Save the file which caused the timeout, for
3546
+			 * debugging
3547
+			 */
3548
+			if(quarantine_dir) {
3549
+				logg(_("Quarantining failed email\n"));
3550
+				qfile(privdata, sendmailId, "scanning-timeout");
3551
+			}
3552
+#endif
3553
+
3554
+			/*
3555
+			 * TODO: if more than one host has been specified, try
3556
+			 * another one - setting cl_error to SMFIS_TEMPFAIL
3557
+			 * helps by forcing a retry
3558
+			 */
3559
+			clamfi_cleanup(ctx);
3560
+
3561
+#ifdef	SESSION
3562
+			pthread_mutex_lock(&sstatus_mutex);
3563
+			session->status = CMDSOCKET_DOWN;
3564
+			pthread_mutex_unlock(&sstatus_mutex);
3565
+#endif
3566
+			return cl_error;
3567
+		}
3568
+
3569
+#ifdef	SESSION
3570
+		pthread_mutex_lock(&sstatus_mutex);
3571
+		if(session->status == CMDSOCKET_INUSE)
3572
+			session->status = CMDSOCKET_FREE;
3573
+		pthread_mutex_unlock(&sstatus_mutex);
3574
+#else
3575
+		close(privdata->cmdSocket);
3576
+		privdata->cmdSocket = -1;
3577
+#endif
3578
+	}
3579
+
3580
+	if(!nflag) {
3581
+		char buf[1024];
3582
+
3583
+		/*
3584
+		 * Include the hostname where the scan took place
3585
+		 */
3586
+		if(localSocket || !external) {
3587
+#ifdef	MAXHOSTNAMELEN
3588
+			char hostname[MAXHOSTNAMELEN + 1];
3589
+#else
3590
+			char hostname[65];
3591
+#endif
3592
+
3593
+			if(gethostname(hostname, sizeof(hostname)) < 0) {
3594
+				const char *j = smfi_getsymval(ctx, "{j}");
3595
+
3596
+				if(j)
3597
+					strncpy(hostname, j, sizeof(hostname) - 1);
3598
+				else
3599
+					strcpy(hostname, _("Error determining host"));
3600
+				hostname[sizeof(hostname)-1]='\0';
3601
+			} else if(strchr(hostname, '.') == NULL) {
3602
+				/*
3603
+				 * Determine fully qualified name
3604
+				 */
3605
+				struct hostent hostent;
3606
+
3607
+				if((r_gethostbyname(hostname, &hostent, buf, sizeof(buf)) == 0) && hostent.h_name) {
3608
+					strncpy(hostname, hostent.h_name, sizeof(hostname));
3609
+					hostname[sizeof(hostname)-1]='\0';
3610
+				}
3611
+			}
3612
+
3613
+#ifdef	SESSION
3614
+			pthread_mutex_lock(&version_mutex);
3615
+			snprintf(buf, sizeof(buf) - 1, "%s on %s",
3616
+				clamav_versions[privdata->serverNumber], hostname);
3617
+			pthread_mutex_unlock(&version_mutex);
3618
+#else
3619
+			snprintf(buf, sizeof(buf) - 1, "%s on %s",
3620
+				clamav_version, hostname);
3621
+#endif
3622
+		} else {
3623
+#ifdef	MAXHOSTNAMELEN
3624
+			char hostname[MAXHOSTNAMELEN + 1];
3625
+
3626
+			if(cli_strtokbuf(serverHostNames, privdata->serverNumber, ":", hostname)) {
3627
+				if(strcmp(hostname, "127.0.0.1") == 0)
3628
+					gethostname(hostname, sizeof(hostname));
3629
+#else
3630
+			char *hostname = cli_strtok(serverHostNames, privdata->serverNumber, ":");
3631
+			if(hostname) {
3632
+#endif
3633
+
3634
+#ifdef	SESSION
3635
+				pthread_mutex_lock(&version_mutex);
3636
+				snprintf(buf, sizeof(buf) - 1, "%s on %s",
3637
+					clamav_versions[privdata->serverNumber], hostname);
3638
+				pthread_mutex_unlock(&version_mutex);
3639
+#else
3640
+				snprintf(buf, sizeof(buf) - 1, "%s on %s",
3641
+					clamav_version, hostname);
3642
+#endif
3643
+#ifndef	MAXHOSTNAMELEN
3644
+				free(hostname);
3645
+#endif
3646
+			} else
3647
+				/* sanity check failed - should issue warning */
3648
+				strcpy(buf, _("Error determining host"));
3649
+		}
3650
+		smfi_addheader(ctx, "X-Virus-Scanned", buf);
3651
+	}
3652
+
3653
+	/*
3654
+	 * TODO: it would be useful to add a header if mbox.c/FOLLOWURLS was
3655
+	 * exceeded
3656
+	 */
3657
+#ifdef	HAVE_RESOLV_H
3658
+	if((strstr(mess, "FOUND") != NULL) && (strstr(mess, "Phishing") != NULL)) {
3659
+		table_t *prevhosts = tableCreate();
3660
+
3661
+		if(spf(privdata, prevhosts)) {
3662
+			logg(_("%s: Ignoring %s false positive from %s received from %s\n"),
3663
+				sendmailId, mess, privdata->from, privdata->ip);
3664
+			strcpy(mess, "OK");
3665
+			/*
3666
+			 * Report false positive to ClamAV, works best when
3667
+			 * clamav-milter has had to create a local copy of
3668
+			 * the email, e.g. when --quarantine-dir is on
3669
+			 */
3670
+			if(report_fps &&
3671
+			   (smfi_addrcpt(ctx, report_fps) == MI_FAILURE)) {
3672
+				if(privdata->filename) {
3673
+					char cmd[1024];
3674
+
3675
+					snprintf(cmd, sizeof(cmd) - 1,
3676
+						"mail -s \"False Positive: %s\" %s < %s",
3677
+						mess, report_fps,
3678
+						privdata->filename);
3679
+					if(system(cmd) == 0)
3680
+						logg(_("#Reported phishing false positive to %s"), report_fps);
3681
+					else
3682
+						logg(_("^Couldn't report false positive to %s\n"), report_fps);
3683
+				} else
3684
+					/*
3685
+					 * Most likely this is because we're
3686
+					 * attempting to add a recipient on
3687
+					 * another host
3688
+					 */
3689
+					logg(_("^Can't set phish FP header\n"));
3690
+			}
3691
+		}
3692
+		tableDestroy(prevhosts);
3693
+	}
3694
+#endif
3695
+	if(strstr(mess, "ERROR") != NULL) {
3696
+		if(strstr(mess, "Size limit reached") != NULL) {
3697
+			/*
3698
+			 * Clamd has stopped on StreamMaxLength before us
3699
+			 */
3700
+			logg(_("%s: Message more than StreamMaxLength (%ld) bytes - not scanned"),
3701
+				sendmailId, streamMaxLength);
3702
+			if(!nflag)
3703
+				smfi_addheader(ctx, "X-Virus-Status", _("Not Scanned - StreamMaxLength exceeded"));
3704
+			clamfi_cleanup(ctx);	/* not needed, but just to be safe */
3705
+			return SMFIS_ACCEPT;
3706
+		}
3707
+		if(!nflag)
3708
+			smfi_addheader(ctx, "X-Virus-Status", _("Not Scanned"));
3709
+
3710
+		logg("!%s: %s\n", sendmailId, mess);
3711
+		rc = cl_error;
3712
+	} else if((ptr = strstr(mess, "FOUND")) != NULL) {
3713
+		/*
3714
+		 * FIXME: This will give false positives if the
3715
+		 *	word "FOUND" is in the email, e.g. the
3716
+		 *	quarantine directory is /tmp/VIRUSES-FOUND
3717
+		 */
3718
+		int i;
3719
+		char **to, *virusname, *err;
3720
+		char reject[1024];
3721
+
3722
+		/*
3723
+		 * Remove the "FOUND" word, and the space before it
3724
+		 */
3725
+		*--ptr = '\0';
3726
+
3727
+		/* skip over 'stream/filename: ' at the start */
3728
+		if((virusname = strchr(mess, ':')) != NULL)
3729
+			virusname = &virusname[2];
3730
+		else
3731
+			virusname = mess;
3732
+
3733
+		if(!nflag) {
3734
+			char buf[129];
3735
+
3736
+			snprintf(buf, sizeof(buf) - 1, "%s %s", _("Infected with"), virusname);
3737
+			smfi_addheader(ctx, "X-Virus-Status", buf);
3738
+		}
3739
+
3740
+		if(quarantine_dir)
3741
+			qfile(privdata, sendmailId, virusname);
3742
+
3743
+		/*
3744
+		 * Setup err as a list of recipients
3745
+		 */
3746
+		err = (char *)cli_malloc(1024);
3747
+
3748
+		if(err == NULL) {
3749
+			clamfi_cleanup(ctx);
3750
+			return cl_error;
3751
+		}
3752
+
3753
+		/*
3754
+		 * Use snprintf rather than printf since we don't know
3755
+		 * the length of privdata->from and may get a buffer
3756
+		 * overrun
3757
+		 */
3758
+		snprintf(err, 1023, _("Intercepted virus from %s to"),
3759
+			privdata->from);
3760
+
3761
+		ptr = strchr(err, '\0');
3762
+
3763
+		i = 1024;
3764
+
3765
+		for(to = privdata->to; *to; to++) {
3766
+			/*
3767
+			 * Re-alloc if we are about run out of buffer
3768
+			 * space
3769
+			 *
3770
+			 * TODO: Only append *to if it's a valid, local
3771
+			 *	email address
3772
+			 */
3773
+			if(&ptr[strlen(*to) + 2] >= &err[i]) {
3774
+				i += 1024;
3775
+				err = cli_realloc(err, i);
3776
+				if(err == NULL) {
3777
+					clamfi_cleanup(ctx);
3778
+					return cl_error;
3779
+				}
3780
+				ptr = strchr(err, '\0');
3781
+			}
3782
+			ptr = cli_strrcpy(ptr, " ");
3783
+			ptr = cli_strrcpy(ptr, *to);
3784
+		}
3785
+		(void)strcpy(ptr, "\n");
3786
+
3787
+		/* Include the sendmail queue ID in the log */
3788
+		logg("%s: %s %s", sendmailId, mess, err);
3789
+		free(err);
3790
+
3791
+		if(!qflag) {
3792
+			char cmd[128];
3793
+			FILE *sendmail;
3794
+
3795
+			/*
3796
+			 * If the oflag is given this sendmail command
3797
+			 * will cause the mail being generated here to be
3798
+			 * scanned. So if oflag is given we just put the
3799
+			 * item in the queue so there's no scanning of two
3800
+			 * messages at once. It'll still be scanned, but
3801
+			 * not at the same time as the incoming message
3802
+			 *
3803
+			 * FIXME: there is a race condition here when sendmail
3804
+			 * and clamav-milter run on the same machine. If the
3805
+			 * system is very overloaded this sendmail can
3806
+			 * take a long time to start - and may even fail
3807
+			 * is the LA is > REFUSE_LA. In all the time we're
3808
+			 * taking to start this sendmail, the sendmail that's
3809
+			 * started us may timeout waiting for a response and
3810
+			 * let the virus through (albeit tagged with
3811
+			 * X-Virus-Status: Infected) because we haven't
3812
+			 * sent SMFIS_DISCARD or SMFIS_REJECT
3813
+			 *
3814
+			 * -i flag, suggested by Michal Jaegermann
3815
+			 *	<michal@harddata.com>
3816
+			 */
3817
+			snprintf(cmd, sizeof(cmd) - 1,
3818
+				(oflag || fflag) ? "%s -t -i -odq" : "%s -t -i",
3819
+				SENDMAIL_BIN);
3820
+
3821
+			logg("#Calling %s\n", cmd);
3822
+			sendmail = popen(cmd, "w");
3823
+
3824
+			if(sendmail) {
3825
+				if(from && from[0])
3826
+					fprintf(sendmail, "From: %s\n", from);
3827
+				else
3828
+					fprintf(sendmail, "From: %s\n", privdata->from);
3829
+#ifdef	BOUNCE
3830
+				if(bflag && privdata->from) {
3831
+					fprintf(sendmail, "To: %s\n", privdata->from);
3832
+					fprintf(sendmail, "Cc: %s\n", postmaster);
3833
+				} else
3834
+#endif
3835
+					fprintf(sendmail, "To: %s\n", postmaster);
3836
+
3837
+				if((!pflag) && privdata->to)
3838
+					for(to = privdata->to; *to; to++)
3839
+						fprintf(sendmail, "Cc: %s\n", *to);
3840
+				/*
3841
+				 * Auto-submitted is still a draft, keep an
3842
+				 * eye on its format
3843
+				 */
3844
+				fputs("Auto-Submitted: auto-submitted (antivirus notify)\n", sendmail);
3845
+				/* "Sergey Y. Afonin" <asy@kraft-s.ru> */
3846
+				if((ptr = smfi_getsymval(ctx, "{_}")) != NULL)
3847
+					fprintf(sendmail,
3848
+						"X-Infected-Received-From: %s\n",
3849
+						ptr);
3850
+				fputs(_("Subject: Virus intercepted\n"), sendmail);
3851
+
3852
+				if(templateHeaders) {
3853
+					/*
3854
+					 * For example, to state the character
3855
+					 * set of the message:
3856
+					 *	Content-Type: text/plain; charset=koi8-r
3857
+					 *
3858
+					 * Based on a suggestion by Denis
3859
+					 *	Eremenko <moonshade@mail.kz>
3860
+					 */
3861
+					FILE *fin = fopen(templateHeaders, "r");
3862
+
3863
+					if(fin == NULL) {
3864
+						perror(templateHeaders);
3865
+						logg(_("!Can't open e-mail template header file %s"),
3866
+								templateHeaders);
3867
+					} else {
3868
+						int c;
3869
+						int lastc = EOF;
3870
+
3871
+						while((c = getc(fin)) != EOF) {
3872
+							putc(c, sendmail);
3873
+							lastc = c;
3874
+						}
3875
+						fclose(fin);
3876
+						/*
3877
+						 * File not new line terminated
3878
+						 */
3879
+						if(lastc != '\n')
3880
+							fputs(_("\n"), sendmail);
3881
+					}
3882
+				}
3883
+
3884
+				fputs(_("\n"), sendmail);
3885
+
3886
+				if((templateFile == NULL) ||
3887
+				   (sendtemplate(ctx, templateFile, sendmail, virusname) < 0)) {
3888
+					/*
3889
+					 * Use our own hardcoded template
3890
+					 */
3891
+#ifdef	BOUNCE
3892
+					if(bflag)
3893
+						fputs(_("A message you sent to\n"), sendmail);
3894
+					else if(pflag)
3895
+#else
3896
+					if(pflag)
3897
+#endif
3898
+						/*
3899
+						 * The message is only going to
3900
+						 * the postmaster, so include
3901
+						 * some useful information
3902
+						 */
3903
+						fprintf(sendmail, _("The message %1$s sent from %2$s to\n"),
3904
+							sendmailId, privdata->from);
3905
+					else
3906
+						fprintf(sendmail, _("A message sent from %s to\n"),
3907
+							privdata->from);
3908
+
3909
+					for(to = privdata->to; *to; to++)
3910
+						fprintf(sendmail, "\t%s\n", *to);
3911
+					fprintf(sendmail, _("contained %s and has not been accepted for delivery.\n"), virusname);
3912
+
3913
+					if(quarantine_dir != NULL)
3914
+						fprintf(sendmail, _("\nThe message in question has been quarantined as %s\n"), privdata->filename);
3915
+
3916
+					if(hflag) {
3917
+						fprintf(sendmail, _("\nThe message was received by %1$s from %2$s via %3$s\n\n"),
3918
+							smfi_getsymval(ctx, "j"), privdata->from,
3919
+							smfi_getsymval(ctx, "_"));
3920
+						fputs(_("For your information, the original message headers were:\n\n"), sendmail);
3921
+						header_list_print(privdata->headers, sendmail);
3922
+					} else if(privdata->received)
3923
+						/*
3924
+						 * TODO: parse this to find
3925
+						 * real infected machine.
3926
+						 * Need to decide how to find
3927
+						 * if it's a dynamic IP from a
3928
+						 * dial up account in which
3929
+						 * case there may not be much
3930
+						 * we can do if that DHCP has
3931
+						 * set the hostname...
3932
+						 */
3933
+						fprintf(sendmail, _("\nThe infected machine is likely to be here:\n%s\t\n"),
3934
+							privdata->received);
3935
+
3936
+				}
3937
+
3938
+				logg("#Waiting for %s to finish\n", cmd);
3939
+				if(pclose(sendmail) != 0)
3940
+					logg(_("%s: Failed to notify clamAV interception - see dead.letter\n"), sendmailId);
3941
+			} else
3942
+				logg(_("^Can't execute '%s' to send virus notice"), cmd);
3943
+		}
3944
+
3945
+		if(report && (quarantine == NULL) && (!advisory) &&
3946
+		   (strstr(virusname, "Phishing") != NULL)) {
3947
+			/*
3948
+			 * Report phishing to an agency
3949
+			 */
3950
+			for(to = privdata->to; *to; to++) {
3951
+				smfi_delrcpt(ctx, *to);
3952
+				smfi_addheader(ctx, "X-Original-To", *to);
3953
+			}
3954
+			if(smfi_addrcpt(ctx, report) == MI_FAILURE) {
3955
+				/* It's a remote site */
3956
+				if(privdata->filename) {
3957
+					char cmd[1024];
3958
+
3959
+					snprintf(cmd, sizeof(cmd) - 1,
3960
+						"mail -s \"%s\" %s < %s",
3961
+						virusname, report,
3962
+						privdata->filename);
3963
+					if(system(cmd) == 0)
3964
+						logg(_("#Reported phishing to %s"), report);
3965
+					else
3966
+						logg(_("^Couldn't report to %s\n"), report);
3967
+					if((!rejectmail) || privdata->discard)
3968
+						rc = SMFIS_DISCARD;
3969
+					else
3970
+						rc = SMFIS_REJECT;
3971
+				} else {
3972
+					logg(_("^Can't set anti-phish header\n"));
3973
+					rc = (privdata->discard) ? SMFIS_DISCARD : SMFIS_REJECT;
3974
+				}
3975
+			} else {
3976
+				setsubject(ctx, "Phishing attempt trapped by ClamAV and redirected");
3977
+
3978
+				logg("Redirected phish to %s\n", report);
3979
+			}
3980
+		} else if(quarantine) {
3981
+			for(to = privdata->to; *to; to++) {
3982
+				smfi_delrcpt(ctx, *to);
3983
+				smfi_addheader(ctx, "X-Original-To", *to);
3984
+			}
3985
+			/*
3986
+			 * NOTE: on a closed relay this will not work
3987
+			 * if the recipient is a remote address
3988
+			 */
3989
+			if(smfi_addrcpt(ctx, quarantine) == MI_FAILURE) {
3990
+				logg(_("^Can't set quarantine user %s"), quarantine);
3991
+				rc = (privdata->discard) ? SMFIS_DISCARD : SMFIS_REJECT;
3992
+			} else {
3993
+				if(report &&
3994
+				   strstr(virusname, "Phishing") != NULL)
3995
+					(void)smfi_addrcpt(ctx, report);
3996
+				setsubject(ctx, virusname);
3997
+
3998
+				logg("Redirected virus to %s", quarantine);
3999
+			}
4000
+		} else if(advisory)
4001
+			setsubject(ctx, virusname);
4002
+		else if(rejectmail) {
4003
+			if(privdata->discard)
4004
+				rc = SMFIS_DISCARD;
4005
+			else
4006
+				rc = SMFIS_REJECT;	/* Delete the e-mail */
4007
+		} else
4008
+			rc = SMFIS_DISCARD;
4009
+
4010
+		if(quarantine_dir) {
4011
+			/*
4012
+			 * Cleanup filename here otherwise clamfi_free() will
4013
+			 * delete the file that we wish to keep because it
4014
+			 * is infected
4015
+			 */
4016
+			free(privdata->filename);
4017
+			privdata->filename = NULL;
4018
+		}
4019
+
4020
+		/*
4021
+		 * Don't drop the message if it's been forwarded to a
4022
+		 * quarantine email
4023
+		 */
4024
+		snprintf(reject, sizeof(reject) - 1, _("virus %s detected by ClamAV - http://www.clamav.net"), virusname);
4025
+		smfi_setreply(ctx, (const char *)privdata->rejectCode, "5.7.1", reject);
4026
+		broadcast(mess);
4027
+
4028
+		if(blacklist_time && privdata->ip[0]) {
4029
+			logg(_("Will blacklist %s for %d seconds because of %s\n"),
4030
+				privdata->ip, blacklist_time, virusname);
4031
+			pthread_mutex_lock(&blacklist_mutex);
4032
+			(void)tableUpdate(blacklist, privdata->ip,
4033
+				(int)time((time_t *)0));
4034
+			pthread_mutex_unlock(&blacklist_mutex);
4035
+		}
4036
+	} else if((strstr(mess, "OK") == NULL) && (strstr(mess, "Empty file") == NULL)) {
4037
+		if(!nflag)
4038
+			smfi_addheader(ctx, "X-Virus-Status", _("Unknown"));
4039
+		logg(_("!%s: incorrect message \"%s\" from clamd"),
4040
+				sendmailId, mess);
4041
+		rc = cl_error;
4042
+	} else {
4043
+		if(!nflag)
4044
+			smfi_addheader(ctx, "X-Virus-Status", _("Clean"));
4045
+
4046
+		/* Include the sendmail queue ID in the log */
4047
+		if(logok)
4048
+			logg(_("%s: clean message from %s\n"),
4049
+				sendmailId,
4050
+				(privdata->from) ? privdata->from : _("an unknown sender"));
4051
+
4052
+		if(privdata->body) {
4053
+			/*
4054
+			 * Add a signature that all has been scanned OK
4055
+			 *
4056
+			 * Note that this is simple minded and isn't aware of
4057
+			 *	any MIME segments in the message. In practice
4058
+			 *	this means that the message will only display
4059
+			 *	on users' terminals if the message is
4060
+			 *	plain/text
4061
+			 */
4062
+			off_t len = updateSigFile();
4063
+
4064
+			if(len) {
4065
+				assert(Sflag != 0);
4066
+
4067
+				privdata->body = cli_realloc(privdata->body, privdata->bodyLen + len);
4068
+				if(privdata->body) {
4069
+					memcpy(&privdata->body[privdata->bodyLen], signature, len);
4070
+					smfi_replacebody(ctx, privdata->body, privdata->bodyLen + len);
4071
+				}
4072
+			}
4073
+		}
4074
+	}
4075
+
4076
+	return rc;
4077
+}
4078
+
4079
+static sfsistat
4080
+clamfi_abort(SMFICTX *ctx)
4081
+{
4082
+	logg("*clamfi_abort\n");
4083
+
4084
+	clamfi_cleanup(ctx);
4085
+	decrement_connexions();
4086
+
4087
+	logg("*clamfi_abort returns\n");
4088
+
4089
+	return cl_error;
4090
+}
4091
+
4092
+static sfsistat
4093
+clamfi_close(SMFICTX *ctx)
4094
+{
4095
+	logg("*clamfi_close\n");
4096
+
4097
+	clamfi_cleanup(ctx);
4098
+	decrement_connexions();
4099
+
4100
+	return SMFIS_CONTINUE;
4101
+}
4102
+
4103
+static void
4104
+clamfi_cleanup(SMFICTX *ctx)
4105
+{
4106
+	struct privdata *privdata = (struct privdata *)smfi_getpriv(ctx);
4107
+
4108
+	logg("#clamfi_cleanup\n");
4109
+
4110
+	if(privdata) {
4111
+		clamfi_free(privdata, 0);
4112
+		smfi_setpriv(ctx, NULL);
4113
+	}
4114
+}
4115
+
4116
+static void
4117
+clamfi_free(struct privdata *privdata, int keep)
4118
+{
4119
+	logg("#clamfi_free\n");
4120
+
4121
+	if(privdata) {
4122
+#ifdef	SESSION
4123
+		struct session *session;
4124
+#endif
4125
+		if(privdata->body)
4126
+			free(privdata->body);
4127
+
4128
+		if(privdata->dataSocket >= 0)
4129
+			close(privdata->dataSocket);
4130
+
4131
+		if(privdata->filename != NULL) {
4132
+			/*
4133
+			 * Don't print an error if the file hasn't been
4134
+			 * created yet
4135
+			 */
4136
+			if((unlink(privdata->filename) < 0) && (errno != ENOENT)) {
4137
+				perror(privdata->filename);
4138
+				logg(_("!Can't remove clean file %s"),
4139
+					privdata->filename);
4140
+			}
4141
+			free(privdata->filename);
4142
+		}
4143
+
4144
+		if(privdata->from) {
4145
+#ifdef	CL_DEBUG
4146
+			if(debug_level >= 9)
4147
+				logg("#Free privdata->from\n");
4148
+#endif
4149
+			free(privdata->from);
4150
+		}
4151
+
4152
+		if(privdata->subject)
4153
+			free(privdata->subject);
4154
+		if(privdata->sender)
4155
+			free(privdata->sender);
4156
+
4157
+		if(privdata->to) {
4158
+			char **to;
4159
+
4160
+			for(to = privdata->to; *to; to++) {
4161
+#ifdef	CL_DEBUG
4162
+				if(debug_level >= 9)
4163
+					logg("#Free *privdata->to\n");
4164
+#endif
4165
+				free(*to);
4166
+			}
4167
+#ifdef	CL_DEBUG
4168
+			if(debug_level >= 9)
4169
+				logg("#Free privdata->to\n");
4170
+#endif
4171
+			free(privdata->to);
4172
+		}
4173
+
4174
+		if(external) {
4175
+#ifdef	SESSION
4176
+			session = &sessions[privdata->serverNumber];
4177
+			pthread_mutex_lock(&sstatus_mutex);
4178
+			if(session->status == CMDSOCKET_INUSE) {
4179
+				/*
4180
+				 * Probably we've got here because
4181
+				 * StreamMaxLength has been reached
4182
+				 */
4183
+#if	0
4184
+				pthread_mutex_unlock(&sstatus_mutex);
4185
+				if(readTimeout) {
4186
+					char buf[64];
4187
+					const int fd = session->sock;
4188
+
4189
+					logg("#clamfi_free: flush server %d fd %d\n",
4190
+						privdata->serverNumber, fd);
4191
+
4192
+					/*
4193
+					 * FIXME: whenever this code gets
4194
+					 *	executed, all of the PINGs fail
4195
+					 *	in the next watchdog cycle
4196
+					 */
4197
+					while(clamd_recv(fd, buf, sizeof(buf)) > 0)
4198
+						;
4199
+				}
4200
+				pthread_mutex_lock(&sstatus_mutex);
4201
+#endif
4202
+				/* Force a reset */
4203
+				session->status = CMDSOCKET_DOWN;
4204
+			}
4205
+			pthread_mutex_unlock(&sstatus_mutex);
4206
+#else
4207
+			if(privdata->cmdSocket >= 0) {
4208
+#if	0
4209
+				char buf[64];
4210
+
4211
+				/*
4212
+				 * Flush the remote end so that clamd doesn't
4213
+				 * get a SIGPIPE
4214
+				 */
4215
+				if(readTimeout)
4216
+					while(clamd_recv(privdata->cmdSocket, buf, sizeof(buf)) > 0)
4217
+						;
4218
+#endif
4219
+				close(privdata->cmdSocket);
4220
+			}
4221
+#endif
4222
+		}
4223
+
4224
+		if(privdata->headers)
4225
+			header_list_free(privdata->headers);
4226
+
4227
+#ifdef	CL_DEBUG
4228
+		if(debug_level >= 9)
4229
+			logg("#Free privdata\n");
4230
+#endif
4231
+		if(privdata->received)
4232
+			free(privdata->received);
4233
+
4234
+		if(keep) {
4235
+			memset(privdata, '\0', sizeof(struct privdata));
4236
+#ifdef	SESSION
4237
+			privdata->dataSocket = -1;
4238
+#else
4239
+			privdata->dataSocket = privdata->cmdSocket = -1;
4240
+#endif
4241
+		} else
4242
+			free(privdata);
4243
+	}
4244
+
4245
+	logg("#clamfi_free returns\n");
4246
+}
4247
+
4248
+/*
4249
+ * Returns < 0 for failure, otherwise the number of bytes sent
4250
+ */
4251
+static int
4252
+clamfi_send(struct privdata *privdata, size_t len, const char *format, ...)
4253
+{
4254
+	char output[BUFSIZ];
4255
+	const char *ptr;
4256
+	int ret = 0;
4257
+	assert(format != NULL);
4258
+
4259
+	if(len > 0)
4260
+		/*
4261
+		 * It isn't a NUL terminated string. We have a set number of
4262
+		 * bytes to output.
4263
+		 */
4264
+		ptr = format;
4265
+	else {
4266
+		va_list argp;
4267
+
4268
+		va_start(argp, format);
4269
+		vsnprintf(output, sizeof(output) - 1, format, argp);
4270
+		va_end(argp);
4271
+
4272
+		len = strlen(output);
4273
+		ptr = output;
4274
+	}
4275
+#ifdef	CL_DEBUG
4276
+	if(debug_level >= 9) {
4277
+		time_t t;
4278
+		const struct tm *tm;
4279
+
4280
+		time(&t);
4281
+		tm = localtime(&t);
4282
+
4283
+		logg("#%d:%d:%d clamfi_send: len=%u bufsiz=%u, fd=%d\n",
4284
+			tm->tm_hour, tm->tm_min, tm->tm_sec, len,
4285
+			sizeof(output), privdata->dataSocket);
4286
+	}
4287
+#endif
4288
+
4289
+	while(len > 0) {
4290
+		const int nbytes = (privdata->filename) ?
4291
+			write(privdata->dataSocket, ptr, len) :
4292
+			send(privdata->dataSocket, ptr, len, 0);
4293
+
4294
+		assert(privdata->dataSocket >= 0);
4295
+
4296
+		if(nbytes == -1) {
4297
+			if(privdata->filename) {
4298
+#ifdef HAVE_STRERROR_R
4299
+				char buf[32];
4300
+
4301
+				perror(privdata->filename);
4302
+				strerror_r(errno, buf, sizeof(buf));
4303
+				logg(_("!write failure (%lu bytes) to %s: %s\n"),
4304
+					(unsigned long)len, privdata->filename, buf);
4305
+#else
4306
+				perror(privdata->filename);
4307
+				logg(_("!write failure (%lu bytes) to %s: %s\n"),
4308
+					(unsigned long)len, privdata->filename,
4309
+					strerror(errno));
4310
+#endif
4311
+			} else {
4312
+				if(errno == EINTR)
4313
+					continue;
4314
+				perror("send");
4315
+#ifdef HAVE_STRERROR_R
4316
+				{
4317
+					char buf[32];
4318
+					strerror_r(errno, buf, sizeof(buf));
4319
+					logg(_("!write failure (%lu bytes) to clamd: %s\n"),
4320
+						(unsigned long)len, buf);
4321
+				}
4322
+#else
4323
+				logg(_("!write failure (%lu bytes) to clamd: %s\n"),
4324
+					(unsigned long)len, strerror(errno));
4325
+#endif
4326
+				checkClamd(1);
4327
+			}
4328
+
4329
+			return -1;
4330
+		}
4331
+		ret += nbytes;
4332
+		len -= nbytes;
4333
+		ptr = &ptr[nbytes];
4334
+
4335
+		if(streamMaxLength > 0L) {
4336
+			privdata->numBytes += nbytes;
4337
+			if(privdata->numBytes >= streamMaxLength)
4338
+				break;
4339
+		}
4340
+	}
4341
+	return ret;
4342
+}
4343
+
4344
+/*
4345
+ * Like strcpy, but return the END of the destination, allowing a quicker
4346
+ * means of adding to the end of a string than strcat
4347
+ */
4348
+#if	0
4349
+static char *
4350
+strrcpy(char *dest, const char *source)
4351
+{
4352
+	/* Pre assertions */
4353
+	assert(dest != NULL);
4354
+	assert(source != NULL);
4355
+	assert(dest != source);
4356
+
4357
+	while((*dest++ = *source++) != '\0')
4358
+		;
4359
+	return(--dest);
4360
+}
4361
+#endif
4362
+
4363
+/*
4364
+ * Read from clamav - timeout if necessary
4365
+ */
4366
+static long
4367
+clamd_recv(int sock, char *buf, size_t len)
4368
+{
4369
+	struct timeval tv;
4370
+	long ret;
4371
+
4372
+	assert(sock >= 0);
4373
+
4374
+	if(readTimeout == 0) {
4375
+		do
4376
+			/* TODO: Needs a test for ssize_t in configure */
4377
+			ret = (long)recv(sock, buf, len, 0);
4378
+		while((ret < 0) && (errno == EINTR));
4379
+
4380
+		return ret;
4381
+	}
4382
+
4383
+	tv.tv_sec = readTimeout;
4384
+	tv.tv_usec = 0;
4385
+
4386
+	for(;;) {
4387
+		fd_set rfds;
4388
+
4389
+		FD_ZERO(&rfds);
4390
+		FD_SET(sock, &rfds);
4391
+
4392
+		switch(select(sock + 1, &rfds, NULL, NULL, &tv)) {
4393
+			case -1:
4394
+				if(errno == EINTR)
4395
+					/* FIXME: work out time left */
4396
+					continue;
4397
+				perror("select");
4398
+				return -1;
4399
+			case 0:
4400
+				logg(_("!No data received from clamd in %d seconds\n"), readTimeout);
4401
+				return 0;
4402
+		}
4403
+		break;
4404
+	}
4405
+
4406
+	do
4407
+		ret = recv(sock, buf, len, 0);
4408
+	while((ret < 0) && (errno == EINTR));
4409
+
4410
+	return ret;
4411
+}
4412
+
4413
+/*
4414
+ * Read in the signature file
4415
+ */
4416
+static off_t
4417
+updateSigFile(void)
4418
+{
4419
+	struct stat statb;
4420
+	int fd;
4421
+
4422
+	if(sigFilename == NULL)
4423
+		/* nothing to read */
4424
+		return 0;
4425
+
4426
+	if(stat(sigFilename, &statb) < 0) {
4427
+		perror(sigFilename);
4428
+		logg(_("Can't stat %s"), sigFilename);
4429
+		return 0;
4430
+	}
4431
+
4432
+	if(statb.st_mtime <= signatureStamp)
4433
+		return statb.st_size;	/* not changed */
4434
+
4435
+	fd = open(sigFilename, O_RDONLY);
4436
+	if(fd < 0) {
4437
+		perror(sigFilename);
4438
+		logg(_("Can't open %s"), sigFilename);
4439
+		return 0;
4440
+	}
4441
+
4442
+	signatureStamp = statb.st_mtime;
4443
+
4444
+	signature = cli_realloc((void *)signature, statb.st_size);
4445
+	if(signature)
4446
+		cli_readn(fd, (void *)signature, statb.st_size);
4447
+	close(fd);
4448
+
4449
+	return statb.st_size;
4450
+}
4451
+
4452
+static header_list_t
4453
+header_list_new(void)
4454
+{
4455
+	header_list_t ret;
4456
+
4457
+	ret = (header_list_t)cli_malloc(sizeof(struct header_list_struct));
4458
+	if(ret) {
4459
+		ret->first = NULL;
4460
+		ret->last = NULL;
4461
+	}
4462
+	return ret;
4463
+}
4464
+
4465
+static void
4466
+header_list_free(header_list_t list)
4467
+{
4468
+	struct header_node_t *iter;
4469
+
4470
+	if(list == NULL)
4471
+		return;
4472
+
4473
+	iter = list->first;
4474
+	while(iter) {
4475
+		struct header_node_t *iter2 = iter->next;
4476
+		free(iter->header);
4477
+		free(iter);
4478
+		iter = iter2;
4479
+	}
4480
+	free(list);
4481
+}
4482
+
4483
+static void
4484
+header_list_add(header_list_t list, const char *headerf, const char *headerv)
4485
+{
4486
+	char *header;
4487
+	size_t len;
4488
+	struct header_node_t *new_node;
4489
+
4490
+	if(list == NULL)
4491
+		return;
4492
+
4493
+	len = (size_t)(strlen(headerf) + strlen(headerv) + 3);
4494
+
4495
+	header = (char *)cli_malloc(len);
4496
+	if(header == NULL)
4497
+		return;
4498
+
4499
+	sprintf(header, "%s: %s", headerf, headerv);
4500
+	new_node = (struct header_node_t *)cli_malloc(sizeof(struct header_node_t));
4501
+	if(new_node == NULL) {
4502
+		free(header);
4503
+		return;
4504
+	}
4505
+	new_node->header = header;
4506
+	new_node->next = NULL;
4507
+	if(list->first == NULL)
4508
+		list->first = new_node;
4509
+	if(list->last)
4510
+		list->last->next = new_node;
4511
+
4512
+	list->last = new_node;
4513
+}
4514
+
4515
+static void
4516
+header_list_print(const header_list_t list, FILE *fp)
4517
+{
4518
+	const struct header_node_t *iter;
4519
+
4520
+	if(list == NULL)
4521
+		return;
4522
+
4523
+	for(iter = list->first; iter; iter = iter->next) {
4524
+		if(strncmp(iter->header, "From ", 5) == 0)
4525
+			putc('>', fp);
4526
+		fprintf(fp, "%s\n", iter->header);
4527
+	}
4528
+}
4529
+
4530
+/*
4531
+ * Establish a connexion to clamd
4532
+ *	Returns success (1) or failure (0)
4533
+ */
4534
+static int
4535
+connect2clamd(struct privdata *privdata)
4536
+{
4537
+	assert(privdata != NULL);
4538
+	assert(privdata->dataSocket == -1);
4539
+	assert(privdata->from != NULL);
4540
+	assert(privdata->to != NULL);
4541
+
4542
+	logg("*connect2clamd\n");
4543
+
4544
+	if(quarantine_dir || tmpdir) {	/* store message in a temporary file */
4545
+		int ntries = 5;
4546
+		const char *dir = (tmpdir) ? tmpdir : quarantine_dir;
4547
+
4548
+		/*
4549
+		 * TODO: investigate mkdtemp on LINUX and possibly others
4550
+		 */
4551
+#ifdef	C_AIX
4552
+		/*
4553
+		 * Patch by Andy Feldt <feldt@nhn.ou.edu>, AIX 5.2 sets errno
4554
+		 * to ENOENT often and sometimes sets errno to 0 (after a
4555
+		 * database reload) for the mkdir call
4556
+		 */
4557
+		if((mkdir(dir, 0700) < 0) && (errno != EEXIST) && (errno > 0) &&
4558
+		    (errno != ENOENT)) {
4559
+#else
4560
+		if((mkdir(dir, 0700) < 0) && (errno != EEXIST)) {
4561
+#endif
4562
+			perror(dir);
4563
+			logg(_("mkdir %s failed"), dir);
4564
+			return 0;
4565
+		}
4566
+		privdata->filename = (char *)cli_malloc(strlen(dir) + 12);
4567
+
4568
+		if(privdata->filename == NULL)
4569
+			return 0;
4570
+
4571
+		do {
4572
+			sprintf(privdata->filename, "%s/msg.XXXXXX", dir);
4573
+#if	defined(C_LINUX) || defined(C_BSD) || defined(HAVE_MKSTEMP) || defined(C_SOLARIS)
4574
+			privdata->dataSocket = mkstemp(privdata->filename);
4575
+#else
4576
+			if(mktemp(privdata->filename) == NULL) {
4577
+				logg(_("mktemp %s failed"), privdata->filename);
4578
+				return 0;
4579
+			}
4580
+			privdata->dataSocket = open(privdata->filename, O_CREAT|O_EXCL|O_WRONLY|O_TRUNC, 0600);
4581
+#endif
4582
+		} while((--ntries > 0) && (privdata->dataSocket < 0));
4583
+
4584
+		if(privdata->dataSocket < 0) {
4585
+			perror(privdata->filename);
4586
+			logg(_("Temporary quarantine file %s creation failed"),
4587
+				privdata->filename);
4588
+			free(privdata->filename);
4589
+			privdata->filename = NULL;
4590
+			return 0;
4591
+		}
4592
+		privdata->serverNumber = 0;
4593
+		logg("#Saving message to %s to scan later\n", privdata->filename);
4594
+	} else {	/* communicate to clamd */
4595
+		int freeServer, nbytes;
4596
+		in_port_t p;
4597
+		struct sockaddr_in reply;
4598
+		char buf[64];
4599
+
4600
+#ifdef	SESSION
4601
+		struct session *session;
4602
+#else
4603
+		assert(privdata->cmdSocket == -1);
4604
+#endif
4605
+
4606
+		/*
4607
+		 * Create socket to talk to clamd. It will tell us the port to
4608
+		 * use to send the data. That will require another socket.
4609
+		 */
4610
+		if(localSocket) {
4611
+#ifndef	SESSION
4612
+			struct sockaddr_un server;
4613
+
4614
+			memset((char *)&server, 0, sizeof(struct sockaddr_un));
4615
+			server.sun_family = AF_UNIX;
4616
+			strncpy(server.sun_path, localSocket, sizeof(server.sun_path));
4617
+			server.sun_path[sizeof(server.sun_path)-1]='\0';
4618
+
4619
+			if((privdata->cmdSocket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
4620
+				perror("socket");
4621
+				return 0;
4622
+			}
4623
+			if(connect(privdata->cmdSocket, (struct sockaddr *)&server, sizeof(struct sockaddr_un)) < 0) {
4624
+				perror(localSocket);
4625
+				return 0;
4626
+			}
4627
+			privdata->serverNumber = 0;
4628
+#endif
4629
+			freeServer = 0;
4630
+		} else {	/* TCP/IP */
4631
+#ifdef	SESSION
4632
+			freeServer = findServer();
4633
+			if(freeServer < 0)
4634
+				return 0;
4635
+			assert(freeServer < (int)max_children);
4636
+#else
4637
+			struct sockaddr_in server;
4638
+
4639
+			memset((char *)&server, 0, sizeof(struct sockaddr_in));
4640
+			server.sin_family = AF_INET;
4641
+			server.sin_port = (in_port_t)htons(tcpSocket);
4642
+
4643
+			assert(serverIPs != NULL);
4644
+
4645
+			freeServer = findServer();
4646
+			if(freeServer < 0)
4647
+				return 0;
4648
+			assert(freeServer < (int)numServers);
4649
+
4650
+			server.sin_addr.s_addr = serverIPs[freeServer];
4651
+
4652
+			if((privdata->cmdSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
4653
+				perror("socket");
4654
+				return 0;
4655
+			}
4656
+			if(connect(privdata->cmdSocket, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0) {
4657
+				char *hostname = cli_strtok(serverHostNames, freeServer, ":");
4658
+
4659
+				perror(hostname ? hostname : "connect");
4660
+				close(privdata->cmdSocket);
4661
+				privdata->cmdSocket = -1;
4662
+				if(hostname)
4663
+					free(hostname);
4664
+				time(&last_failed_pings[freeServer]);
4665
+				return 0;
4666
+			}
4667
+			last_failed_pings[freeServer] = (time_t)0;
4668
+#endif
4669
+			privdata->serverNumber = freeServer;
4670
+		}
4671
+
4672
+#ifdef	SESSION
4673
+		if(serverIPs[freeServer] == (int)inet_addr("127.0.0.1")) {
4674
+			privdata->filename = cli_gentemp(NULL);
4675
+			if(privdata->filename) {
4676
+				logg("#connect2clamd(%d): creating %s\n", freeServer, privdata->filename);
4677
+#ifdef	O_TEXT
4678
+				privdata->dataSocket = open(privdata->filename, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC|O_TEXT, 0600);
4679
+#else
4680
+				privdata->dataSocket = open(privdata->filename, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0600);
4681
+#endif
4682
+				if(privdata->dataSocket < 0) {
4683
+					perror(privdata->filename);
4684
+					free(privdata->filename);
4685
+					privdata->filename = NULL;
4686
+				} else
4687
+					return sendToFrom(privdata);
4688
+			}
4689
+		}
4690
+		logg("#connect2clamd(%d): STREAM\n", freeServer);
4691
+
4692
+		session = &sessions[freeServer];
4693
+		if((session->sock < 0) || (send(session->sock, "STREAM\n", 7, 0) < 7)) {
4694
+			perror("send");
4695
+			pthread_mutex_lock(&sstatus_mutex);
4696
+			session->status = CMDSOCKET_DOWN;
4697
+			pthread_mutex_unlock(&sstatus_mutex);
4698
+			logg(_("!failed to send STREAM command clamd server %d"),
4699
+				freeServer);
4700
+
4701
+			return 0;
4702
+		}
4703
+#else
4704
+		if(send(privdata->cmdSocket, "STREAM\n", 7, 0) < 7) {
4705
+			perror("send");
4706
+			logg(_("!failed to send STREAM command clamd"));
4707
+			return 0;
4708
+		}
4709
+		shutdown(privdata->cmdSocket, SHUT_WR);
4710
+#endif
4711
+
4712
+		/*
4713
+		 * Create socket that we'll use to send the data to clamd
4714
+		 */
4715
+		if((privdata->dataSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
4716
+			perror("socket");
4717
+			logg(_("!failed to create TCPSocket to talk to clamd\n"));
4718
+			return 0;
4719
+		}
4720
+
4721
+		shutdown(privdata->dataSocket, SHUT_RD);
4722
+
4723
+#ifdef	SESSION
4724
+		nbytes = clamd_recv(session->sock, buf, sizeof(buf));
4725
+		if(nbytes <= 0) {
4726
+			if(nbytes < 0) {
4727
+				perror("recv");
4728
+				logg(_("!recv failed from clamd getting PORT\n"));
4729
+			} else
4730
+				logg(_("!EOF from clamd getting PORT\n"));
4731
+
4732
+			pthread_mutex_lock(&sstatus_mutex);
4733
+			session->status = CMDSOCKET_DOWN;
4734
+			return pthread_mutex_unlock(&sstatus_mutex);
4735
+		}
4736
+#else
4737
+		nbytes = clamd_recv(privdata->cmdSocket, buf, sizeof(buf));
4738
+		if(nbytes <= 0) {
4739
+			if(nbytes < 0) {
4740
+				perror("recv");
4741
+				logg(_("!recv failed from clamd getting PORT\n"));
4742
+			} else
4743
+				logg(_("!EOF from clamd getting PORT\n"));
4744
+
4745
+			return 0;
4746
+		}
4747
+#endif
4748
+		buf[nbytes] = '\0';
4749
+#ifdef	CL_DEBUG
4750
+		if(debug_level >= 4)
4751
+			logg("#Received: %s\n", buf);
4752
+#endif
4753
+		if(sscanf(buf, "PORT %hu\n", &p) != 1) {
4754
+			logg(_("!Expected port information from clamd, got '%s'\n"),
4755
+				buf);
4756
+#ifdef	SESSION
4757
+			session->status = CMDSOCKET_DOWN;
4758
+			pthread_mutex_unlock(&sstatus_mutex);
4759
+#endif
4760
+			return 0;
4761
+		}
4762
+
4763
+		memset((char *)&reply, 0, sizeof(struct sockaddr_in));
4764
+		reply.sin_family = AF_INET;
4765
+		reply.sin_port = (in_port_t)htons(p);
4766
+
4767
+		assert(serverIPs != NULL);
4768
+
4769
+		reply.sin_addr.s_addr = serverIPs[freeServer];
4770
+
4771
+#ifdef	CL_DEBUG
4772
+		if(debug_level >= 4)
4773
+#ifdef	SESSION
4774
+			logg(_("#Connecting to local port %d - data %d cmd %d\n"),
4775
+				p, privdata->dataSocket, session->sock);
4776
+#else
4777
+			logg(_("#Connecting to local port %d - data %d cmd %d\n"),
4778
+				p, privdata->dataSocket, privdata->cmdSocket);
4779
+#endif
4780
+#endif
4781
+
4782
+		if(connect(privdata->dataSocket, (struct sockaddr *)&reply, sizeof(struct sockaddr_in)) < 0) {
4783
+			perror("connect");
4784
+
4785
+			logg("#Failed to connect to port %d given by clamd\n",
4786
+				p);
4787
+			/* 0.4 - use better error message */
4788
+#ifdef HAVE_STRERROR_R
4789
+			strerror_r(errno, buf, sizeof(buf));
4790
+			logg(_("!Failed to connect to port %d given by clamd: %s"),
4791
+					p, buf);
4792
+#else
4793
+			logg(_("!Failed to connect to port %d given by clamd: %s"), p, strerror(errno));
4794
+#endif
4795
+#ifdef	SESSION
4796
+			pthread_mutex_lock(&sstatus_mutex);
4797
+			session->status = CMDSOCKET_DOWN;
4798
+			pthread_mutex_unlock(&sstatus_mutex);
4799
+#endif
4800
+			return 0;
4801
+		}
4802
+	}
4803
+
4804
+	if(!sendToFrom(privdata))
4805
+		return 0;
4806
+
4807
+	logg("#connect2clamd: serverNumber = %d\n", privdata->serverNumber);
4808
+
4809
+	return 1;
4810
+}
4811
+
4812
+/*
4813
+ * Combine the To and From into one clamfi_send to save bandwidth
4814
+ * when sending using TCP/IP to connect to a remote clamd, by band
4815
+ * width here I mean number of packets
4816
+ */
4817
+static int
4818
+sendToFrom(struct privdata *privdata)
4819
+{
4820
+	char **to;
4821
+	char *msg;
4822
+	int length;
4823
+
4824
+	length = strlen(privdata->from) + 34;
4825
+	for(to = privdata->to; *to; to++)
4826
+		length += strlen(*to) + 5;
4827
+
4828
+	msg = cli_malloc(length + 1);
4829
+
4830
+	if(msg) {
4831
+		sprintf(msg, "Received: by clamav-milter\nFrom: %s\n",
4832
+			privdata->from);
4833
+
4834
+		for(to = privdata->to; *to; to++) {
4835
+			char *eom = strchr(msg, '\0');
4836
+
4837
+			sprintf(eom, "To: %s\n", *to);
4838
+		}
4839
+		if(clamfi_send(privdata, length, msg) != length) {
4840
+			free(msg);
4841
+			return 0;
4842
+		}
4843
+		free(msg);
4844
+	} else {
4845
+		if(clamfi_send(privdata, 0,
4846
+		    "Received: by clamav-milter\nFrom: %s\n",
4847
+		    privdata->from) <= 0)
4848
+			return 0;
4849
+
4850
+		for(to = privdata->to; *to; to++)
4851
+			if(clamfi_send(privdata, 0, "To: %s\n", *to) <= 0)
4852
+				return 0;
4853
+	}
4854
+
4855
+	return 1;
4856
+}
4857
+
4858
+/*
4859
+ * If possible, check if clamd has died, and, if requested, report if it has
4860
+ * Returns true if OK or unknown, otherwise false
4861
+ */
4862
+static int
4863
+checkClamd(int log_result)
4864
+{
4865
+	pid_t pid;
4866
+	int fd, nbytes;
4867
+	char buf[9];
4868
+
4869
+	int i, onlocal;
4870
+
4871
+	for(i = 0; i < numServers; i++) {
4872
+		if(serverIPs[i] && pingServer(i))
4873
+			return 1;
4874
+	}
4875
+
4876
+	if(log_result)
4877
+		logg(_("!Can't find any clamd server\n"));
4878
+	return 0;
4879
+}
4880
+
4881
+/*
4882
+ * Send a templated message about an intercepted message. Very basic for
4883
+ * now, just to prove it works, will enhance the flexability later, only
4884
+ * supports %v and $sendmail_variables$ at present.
4885
+ *
4886
+ * TODO: more template features
4887
+ * TODO: allow filename to start with a '|' taken to mean the output of
4888
+ *	a program
4889
+ */
4890
+static int
4891
+sendtemplate(SMFICTX *ctx, const char *filename, FILE *sendmail, const char *virusname)
4892
+{
4893
+	FILE *fin = fopen(filename, "r");
4894
+	struct stat statb;
4895
+	char *buf, *ptr /* , *ptr2 */;
4896
+	struct privdata *privdata = (struct privdata *)smfi_getpriv(ctx);
4897
+
4898
+	if(fin == NULL) {
4899
+		perror(filename);
4900
+		logg(_("!Can't open e-mail template file %s"), filename);
4901
+		return -1;
4902
+	}
4903
+
4904
+	if(fstat(fileno(fin), &statb) < 0) {
4905
+		/* File disappeared in race condition? */
4906
+		perror(filename);
4907
+		logg(_("!Can't stat e-mail template file %s"), filename);
4908
+		fclose(fin);
4909
+		return -1;
4910
+	}
4911
+	buf = cli_malloc(statb.st_size + 1);
4912
+	if(buf == NULL) {
4913
+		fclose(fin);
4914
+		logg(_("!Out of memory"));
4915
+		return -1;
4916
+	}
4917
+	if(fread(buf, sizeof(char), statb.st_size, fin) != (size_t)statb.st_size) {
4918
+		perror(filename);
4919
+		logg(_("!Error reading e-mail template file %s"),
4920
+			filename);
4921
+		fclose(fin);
4922
+		free(buf);
4923
+		return -1;
4924
+	}
4925
+	fclose(fin);
4926
+	buf[statb.st_size] = '\0';
4927
+
4928
+	for(ptr = buf; *ptr; ptr++)
4929
+		switch(*ptr) {
4930
+			case '%': /* clamAV variable */
4931
+				switch(*++ptr) {
4932
+					case 'v':	/* virus name */
4933
+						fputs(virusname, sendmail);
4934
+						break;
4935
+					case '%':
4936
+						putc('%', sendmail);
4937
+						break;
4938
+					case 'h':	/* headers */
4939
+						if(privdata)
4940
+							header_list_print(privdata->headers, sendmail);
4941
+						break;
4942
+					case '\0':
4943
+						putc('%', sendmail);
4944
+						--ptr;
4945
+						continue;
4946
+					default:
4947
+						logg(_("!%s: Unknown clamAV variable \"%c\"\n"),
4948
+							filename, *ptr);
4949
+						break;
4950
+				}
4951
+				break;
4952
+			case '$': /* sendmail string */ {
4953
+				const char *val;
4954
+				char *end = strchr(++ptr, '$');
4955
+
4956
+				if(end == NULL) {
4957
+					logg(_("!%s: Unterminated sendmail variable \"%s\"\n"),
4958
+						filename, ptr);
4959
+					continue;
4960
+				}
4961
+				*end = '\0';
4962
+
4963
+				val = smfi_getsymval(ctx, ptr);
4964
+				if(val == NULL) {
4965
+					fputs(ptr, sendmail);
4966
+						logg(_("!%s: Unknown sendmail variable \"%s\"\n"),
4967
+							filename, ptr);
4968
+				} else
4969
+					fputs(val, sendmail);
4970
+				ptr = end;
4971
+				break;
4972
+			}
4973
+			case '\\':
4974
+				if(*++ptr == '\0') {
4975
+					--ptr;
4976
+					continue;
4977
+				}
4978
+				putc(*ptr, sendmail);
4979
+				break;
4980
+			default:
4981
+				putc(*ptr, sendmail);
4982
+		}
4983
+
4984
+	free(buf);
4985
+
4986
+	return 0;
4987
+}
4988
+
4989
+/*
4990
+ * Keep the infected file in quarantine, return success (0) or failure
4991
+ *
4992
+ * It's quicker if the quarantine directory is on the same filesystem
4993
+ *	as the temporary directory
4994
+ */
4995
+static int
4996
+qfile(struct privdata *privdata, const char *sendmailId, const char *virusname)
4997
+{
4998
+	int MM, YY, DD;
4999
+	time_t t;
5000
+	size_t len;
5001
+	char *newname, *ptr;
5002
+	const struct tm *tm;
5003
+
5004
+	assert(privdata != NULL);
5005
+
5006
+	if((privdata->filename == NULL) || (virusname == NULL))
5007
+		return -1;
5008
+
5009
+	logg("#qfile filename '%s' sendmailId '%s' virusname '%s'\n", privdata->filename, sendmailId, virusname);
5010
+
5011
+	len = strlen(quarantine_dir);
5012
+
5013
+	newname = cli_malloc(len + strlen(sendmailId) + strlen(virusname) + 10);
5014
+
5015
+	if(newname == NULL)
5016
+		return -1;
5017
+
5018
+	t = time((time_t *)0);
5019
+	tm = localtime(&t);
5020
+	MM = tm->tm_mon + 1;
5021
+	YY = tm->tm_year - 100;
5022
+	DD = tm->tm_mday;
5023
+
5024
+	sprintf(newname, "%s/%02d%02d%02d", quarantine_dir, YY, MM, DD);
5025
+#ifdef	C_AIX
5026
+	if((mkdir(newname, 0700) < 0) && (errno != EEXIST) && (errno > 0) &&
5027
+	    (errno != ENOENT)) {
5028
+#else
5029
+	if((mkdir(newname, 0700) < 0) && (errno != EEXIST)) {
5030
+#endif
5031
+		perror(newname);
5032
+		logg(_("!mkdir %s failed\n"), newname);
5033
+		return -1;
5034
+	}
5035
+	sprintf(newname, "%s/%02d%02d%02d/%s.%s",
5036
+		quarantine_dir, YY, MM, DD, sendmailId, virusname);
5037
+
5038
+	/*
5039
+	 * Strip out funnies that may be in the name of the virus, such as '/'
5040
+	 * that would cause the quarantine to fail to save since the name
5041
+	 * of the virus is included in the filename
5042
+	 */
5043
+	for(ptr = &newname[len + 8]; *ptr; ptr++) {
5044
+#ifdef	C_DARWIN
5045
+		*ptr &= '\177';
5046
+#endif
5047
+#if	defined(MSDOS) || defined(C_WINDOWS) || defined(C_OS2)
5048
+		if(strchr("/*?<>|\\\"+=,;:\t ", *ptr))
5049
+#else
5050
+		if(*ptr == '/')
5051
+#endif
5052
+			*ptr = '_';
5053
+	}
5054
+	logg("#qfile move '%s' to '%s'\n", privdata->filename, newname);
5055
+
5056
+	if(move(privdata->filename, newname) < 0) {
5057
+		logg(_("^Can't rename %1$s to %2$s\n"),
5058
+			privdata->filename, newname);
5059
+		free(newname);
5060
+		return -1;
5061
+	}
5062
+	free(privdata->filename);
5063
+	privdata->filename = newname;
5064
+
5065
+	logg(_("Email quarantined as %s\n"), newname);
5066
+
5067
+	return 0;
5068
+}
5069
+
5070
+/*
5071
+ * Move oldfile to newfile using the fastest possible method
5072
+ */
5073
+static int
5074
+move(const char *oldfile, const char *newfile)
5075
+{
5076
+	int ret, c;
5077
+	FILE *fin, *fout;
5078
+#ifdef	C_LINUX
5079
+	struct stat statb;
5080
+	int in, out;
5081
+	off_t offset;
5082
+#endif
5083
+
5084
+	ret = rename(oldfile, newfile);
5085
+	if(ret >= 0)
5086
+		return 0;
5087
+
5088
+	if((ret < 0) && (errno != EXDEV)) {
5089
+		perror(newfile);
5090
+		return -1;
5091
+	}
5092
+
5093
+#ifdef	C_LINUX	/* >= 2.2 */
5094
+	in = open(oldfile, O_RDONLY);
5095
+	if(in < 0) {
5096
+		perror(oldfile);
5097
+		return -1;
5098
+	}
5099
+
5100
+	if(fstat(in, &statb) < 0) {
5101
+		perror(oldfile);
5102
+		close(in);
5103
+		return -1;
5104
+	}
5105
+	out = open(newfile, O_WRONLY|O_CREAT, 0600);
5106
+	if(out < 0) {
5107
+		perror(newfile);
5108
+		close(in);
5109
+		return -1;
5110
+	}
5111
+	offset = (off_t)0;
5112
+	ret = sendfile(out, in, &offset, statb.st_size);
5113
+	close(in);
5114
+	if(ret < 0) {
5115
+		/*
5116
+		 * Fall back if sendfile fails, which will happen on Linux
5117
+		 * 2.6 :-(. FreeBSD works correctly, so the ifdef should be
5118
+		 * fixed
5119
+		 */
5120
+		close(out);
5121
+		unlink(newfile);
5122
+
5123
+		fin = fopen(oldfile, "r");
5124
+		if(fin == NULL)
5125
+			return -1;
5126
+
5127
+		fout = fopen(newfile, "w");
5128
+		if(fout == NULL) {
5129
+			fclose(fin);
5130
+			return -1;
5131
+		}
5132
+		while((c = getc(fin)) != EOF)
5133
+			putc(c, fout);
5134
+
5135
+		fclose(fin);
5136
+		fclose(fout);
5137
+	} else
5138
+		close(out);
5139
+#else
5140
+	fin = fopen(oldfile, "r");
5141
+	if(fin == NULL)
5142
+		return -1;
5143
+
5144
+	fout = fopen(newfile, "w");
5145
+	if(fout == NULL) {
5146
+		fclose(fin);
5147
+		return -1;
5148
+	}
5149
+	while((c = getc(fin)) != EOF)
5150
+		putc(c, fout);
5151
+
5152
+	fclose(fin);
5153
+	fclose(fout);
5154
+#endif
5155
+
5156
+	logg("#removing %s\n", oldfile);
5157
+
5158
+	return unlink(oldfile);
5159
+}
5160
+
5161
+/*
5162
+ * Store the name of the virus in the subject of the e-mail
5163
+ */
5164
+static void
5165
+setsubject(SMFICTX *ctx, const char *virusname)
5166
+{
5167
+	struct privdata *privdata = (struct privdata *)smfi_getpriv(ctx);
5168
+	char subject[128];
5169
+
5170
+	if(privdata->subject)
5171
+		smfi_addheader(ctx, "X-Original-Subject", privdata->subject);
5172
+
5173
+	snprintf(subject, sizeof(subject) - 1, _("[Virus] %s"), virusname);
5174
+	if(privdata->subject)
5175
+		smfi_chgheader(ctx, "Subject", 1, subject);
5176
+	else
5177
+		smfi_addheader(ctx, "Subject", subject);
5178
+}
5179
+
5180
+#if	0
5181
+/*
5182
+ * TODO: gethostbyname_r is non-standard so different operating
5183
+ * systems do it in different ways. Need more examples
5184
+ * Perhaps we could use res_search()?
5185
+ * Perhaps we could use http://www.chiark.greenend.org.uk/~ian/adns/
5186
+ *
5187
+ * Returns 0 for success
5188
+ */
5189
+static int
5190
+clamfi_gethostbyname(const char *hostname, struct hostent *hp, char *buf, size_t len)
5191
+{
5192
+#if	defined(HAVE_GETHOSTBYNAME_R_6)
5193
+	/* e.g. Linux */
5194
+	struct hostent *hp2;
5195
+	int ret = -1;
5196
+
5197
+	if((hostname == NULL) || (hp == NULL))
5198
+		return -1;
5199
+	if(gethostbyname_r(hostname, hp, buf, len, &hp2, &ret) < 0)
5200
+		return ret;
5201
+#elif	defined(HAVE_GETHOSTBYNAME_R_5)
5202
+	/* e.g. BSD, Solaris, Cygwin */
5203
+	int ret = -1;
5204
+
5205
+	if((hostname == NULL) || (hp == NULL))
5206
+		return -1;
5207
+	if(gethostbyname_r(hostname, hp, buf, len, &ret) == NULL)
5208
+		return ret;
5209
+#elif	defined(HAVE_GETHOSTBYNAME_R_3)
5210
+	/* e.g. HP/UX, AIX */
5211
+	if((hostname == NULL) || (hp == NULL))
5212
+		return -1;
5213
+	if(gethostbyname_r(hostname, &hp, (struct hostent_data *)buf) < 0)
5214
+		return h_errno;
5215
+#else
5216
+	/* Single thread the code */
5217
+	struct hostent *hp2;
5218
+	static pthread_mutex_t hostent_mutex = PTHREAD_MUTEX_INITIALIZER;
5219
+
5220
+	if((hostname == NULL) || (hp == NULL))
5221
+		return -1;
5222
+
5223
+	pthread_mutex_lock(&hostent_mutex);
5224
+	if((hp2 = gethostbyname(hostname)) == NULL) {
5225
+		pthread_mutex_unlock(&hostent_mutex);
5226
+		return h_errno;
5227
+	}
5228
+	memcpy(hp, hp2, sizeof(struct hostent));
5229
+	pthread_mutex_unlock(&hostent_mutex);
5230
+#endif
5231
+
5232
+	return 0;
5233
+}
5234
+#endif
5235
+
5236
+/*
5237
+ * Handle the -I flag
5238
+ */
5239
+static int
5240
+add_local_ip(char *address)
5241
+{
5242
+	char *opt, *pref;
5243
+	int preflen;
5244
+	int retval;
5245
+	struct in_addr ignoreIP;
5246
+#ifdef	AF_INET6
5247
+	struct in6_addr ignoreIP6;
5248
+#endif
5249
+
5250
+	opt = cli_strdup(address);
5251
+	if(opt == NULL)
5252
+		return 0;
5253
+
5254
+	pref = strchr(opt, '/'); /* search for "/prefix" */
5255
+	if(pref)
5256
+		*pref = '\0';
5257
+#ifdef HAVE_INET_NTOP
5258
+	/* IPv4 address ? */
5259
+	if(inet_pton(AF_INET, opt, &ignoreIP) > 0) {
5260
+#else
5261
+	if(inet_aton(address, &ignoreIP)) {
5262
+#endif
5263
+		struct cidr_net *net;
5264
+
5265
+		for(net = (struct cidr_net *)localNets; net->base; net++)
5266
+			;
5267
+		if(pref && *(pref+1))
5268
+			preflen = atoi(pref+1);
5269
+		else
5270
+			preflen = 32;
5271
+
5272
+		net->base = ntohl(ignoreIP.s_addr);
5273
+		net->mask = MAKEMASK(preflen);
5274
+
5275
+		retval = 1;
5276
+	}
5277
+
5278
+#ifdef HAVE_INET_NTOP
5279
+#ifdef AF_INET6
5280
+	else if(inet_pton(AF_INET6, opt, &ignoreIP6) > 0) {
5281
+		/* IPv6 address ? */
5282
+		localNets6[localNets6_cnt].base = ignoreIP6;
5283
+
5284
+		if(pref && *(pref+1))
5285
+			preflen = atoi (pref+1);
5286
+		else
5287
+			preflen = 128;
5288
+		localNets6[localNets6_cnt].preflen = preflen;
5289
+		localNets6_cnt++;
5290
+
5291
+		retval = 1;
5292
+	}
5293
+#endif
5294
+#endif
5295
+	else
5296
+		retval = 0;
5297
+
5298
+	free(opt);
5299
+	return retval;
5300
+}
5301
+
5302
+/*
5303
+ * Determine if an IPv6 email address is "local". The address is the
5304
+ *	human readable version. Calls isLocalAddr if the given address is
5305
+ *	IPv4
5306
+ */
5307
+static int
5308
+isLocal(const char *addr)
5309
+{
5310
+	struct in_addr ip;
5311
+#ifdef	AF_INET6
5312
+	struct in6_addr ip6;
5313
+#endif
5314
+
5315
+#ifdef HAVE_INET_NTOP
5316
+	if(inet_pton(AF_INET, addr, &ip) > 0)
5317
+		return isLocalAddr(ip.s_addr);
5318
+#ifdef AF_INET6
5319
+	else if(inet_pton (AF_INET6, addr, &ip6) > 0) {
5320
+		int i;
5321
+		const cidr_net6 *pnet6 = localNets6;
5322
+
5323
+		for (i = 0; i < localNets6_cnt; i++) {
5324
+			int match = 1;
5325
+			int j;
5326
+
5327
+			for(j = 0; match && j < (pnet6->preflen >> 3); j++)
5328
+				if(pnet6->base.s6_addr[j] != ip6.s6_addr[j])
5329
+					match = 0;
5330
+			if(match && (j < 16)) {
5331
+				uint8_t mask = (uint8_t)(0xff << (8 - (pnet6->preflen & 7)) & 0xFF);
5332
+
5333
+				if((pnet6->base.s6_addr[j] & mask) != (ip6.s6_addr[j] & mask))
5334
+					match = 0;
5335
+			}
5336
+			if(match)
5337
+				return 1;	/* isLocal */
5338
+			pnet6++;
5339
+		 }
5340
+	}
5341
+#endif	/* AF_INET6 */
5342
+#endif	/* HAVE_INET_NTOP */
5343
+	return isLocalAddr(inet_addr(addr));
5344
+}
5345
+
5346
+/*
5347
+ * David Champion <dgc@uchicago.edu>
5348
+ *
5349
+ * Check whether addr is on network by applying netmasks.
5350
+ * addr must be a 32-bit integer-packed IPv4 address in network order.
5351
+ * For example:
5352
+ *	struct in_addr IPAddress;
5353
+ *	isLocal = isLocalAddr(IPAddress.s_addr);
5354
+ */
5355
+static int
5356
+isLocalAddr(in_addr_t addr)
5357
+{
5358
+	const struct cidr_net *net;
5359
+
5360
+	for(net = localNets; net->base; net++)
5361
+		if((net->base & net->mask) == (ntohl(addr) & net->mask))
5362
+			return 1;
5363
+
5364
+	return 0;	/* is non-local */
5365
+}
5366
+
5367
+/*
5368
+ * Can't connect to any clamd server. This is serious, we need to inform
5369
+ * someone. In the absence of SNMP the best way is by e-mail. We
5370
+ * don't want to flood so there's a need to restrict to
5371
+ * no more than say one message every 15 minutes
5372
+ */
5373
+static void
5374
+clamdIsDown(void)
5375
+{
5376
+	static time_t lasttime;
5377
+	time_t thistime, diff;
5378
+	static pthread_mutex_t time_mutex = PTHREAD_MUTEX_INITIALIZER;
5379
+
5380
+	logg(_("!No response from any clamd server - your AV system is not scanning emails\n"));
5381
+
5382
+	time(&thistime);
5383
+	pthread_mutex_lock(&time_mutex);
5384
+	diff = thistime - lasttime;
5385
+	pthread_mutex_unlock(&time_mutex);
5386
+
5387
+	if(diff >= (time_t)(15 * 60)) {
5388
+		char cmd[128];
5389
+		FILE *sendmail;
5390
+
5391
+		snprintf(cmd, sizeof(cmd) - 1, "%s -t -i", SENDMAIL_BIN);
5392
+
5393
+		sendmail = popen(cmd, "w");
5394
+
5395
+		if(sendmail) {
5396
+			fprintf(sendmail, "To: %s\n", postmaster);
5397
+			fprintf(sendmail, "From: %s\n", postmaster);
5398
+			fputs(_("Subject: ClamAV Down\n"), sendmail);
5399
+			fputs("Priority: High\n\n", sendmail);
5400
+
5401
+			fputs(_("This is an automatic message\n\n"), sendmail);
5402
+
5403
+			if(numServers == 1)
5404
+				fputs(_("The clamd program cannot be contacted.\n"), sendmail);
5405
+			else
5406
+				fputs(_("No clamd server can be contacted.\n"), sendmail);
5407
+
5408
+			fputs(_("Emails may not be being scanned, please check your servers.\n"), sendmail);
5409
+
5410
+			if(pclose(sendmail) == 0) {
5411
+				pthread_mutex_lock(&time_mutex);
5412
+				time(&lasttime);
5413
+				pthread_mutex_unlock(&time_mutex);
5414
+			}
5415
+		}
5416
+	}
5417
+}
5418
+
5419
+#ifdef	SESSION
5420
+/*
5421
+ * Thread to monitor the links to clamd sessions. Any marked as being in
5422
+ * an error state because of previous I/O errors are restarted, and a heartbeat
5423
+ * is sent the others
5424
+ *
5425
+ * It is woken up when the milter goes idle, when there are no free servers
5426
+ * available and once every readTimeout-1 seconds
5427
+ *
5428
+ * TODO: reload the whiteList file if it's been changed
5429
+ *
5430
+ * TODO: localSocket support
5431
+ */
5432
+static void *
5433
+watchdog(void *a)
5434
+{
5435
+	static pthread_mutex_t watchdog_mutex = PTHREAD_MUTEX_INITIALIZER;
5436
+
5437
+	while(!quitting) {
5438
+		unsigned int i;
5439
+		struct timespec ts;
5440
+		struct timeval tp;
5441
+		struct session *session;
5442
+
5443
+		gettimeofday(&tp, NULL);
5444
+
5445
+		ts.tv_sec = tp.tv_sec + freshclam_monitor;
5446
+		ts.tv_nsec = tp.tv_usec * 1000;
5447
+		logg("#watchdog sleeps\n");
5448
+		pthread_mutex_lock(&watchdog_mutex);
5449
+		/*
5450
+		 * Sometimes this returns EPIPE which isn't listed as a
5451
+		 * return value in the Linux man page for pthread_cond_timedwait
5452
+		 * so I'm not sure why it happens
5453
+		 */
5454
+		switch(pthread_cond_timedwait(&watchdog_cond, &watchdog_mutex, &ts)) {
5455
+			case ETIMEDOUT:
5456
+			case 0:
5457
+				break;
5458
+			default:
5459
+				perror("pthread_cond_timedwait");
5460
+		}
5461
+		pthread_mutex_unlock(&watchdog_mutex);
5462
+
5463
+		logg("#watchdog wakes\n");
5464
+
5465
+		if(check_and_reload_database() != 0) {
5466
+			if(cl_error != SMFIS_ACCEPT) {
5467
+				smfi_stop();
5468
+				return NULL;
5469
+			}
5470
+			logg(_("!No emails will be scanned"));
5471
+		}
5472
+
5473
+		i = 0;
5474
+		session = sessions;
5475
+		pthread_mutex_lock(&sstatus_mutex);
5476
+		for(; i < max_children; i++, session++) {
5477
+			const int sock = session->sock;
5478
+
5479
+			/*
5480
+			 * Check all free sessions are still usable
5481
+			 * This could take some time with many free
5482
+			 * sessions to slow remote servers, so only do this
5483
+			 * when the system is quiet (not 100% accurate when
5484
+			 * determining this since n_children isn't locked but
5485
+			 * that doesn't really matter)
5486
+			 */
5487
+			logg("#watchdog: check server %d\n", i);
5488
+			if((n_children == 0) &&
5489
+			   (session->status == CMDSOCKET_FREE) &&
5490
+			   (clamav_versions != NULL)) {
5491
+				if(send(sock, "VERSION\n", 8, 0) == 8) {
5492
+					char buf[81];
5493
+					const int nbytes = clamd_recv(sock, buf, sizeof(buf) - 1);
5494
+
5495
+					if(nbytes <= 0)
5496
+						session->status = CMDSOCKET_DOWN;
5497
+					else {
5498
+						buf[nbytes] = '\0';
5499
+						if(strncmp(buf, "ClamAV ", 7) == 0) {
5500
+							/* Remove the trailing new line from the reply */
5501
+							char *ptr;
5502
+
5503
+							if((ptr = strchr(buf, '\n')) != NULL)
5504
+								*ptr = '\0';
5505
+							pthread_mutex_lock(&version_mutex);
5506
+							if(clamav_versions[i] == NULL)
5507
+								clamav_versions[i] = cli_strdup(buf);
5508
+							else if(strcmp(buf, clamav_versions[i]) != 0) {
5509
+								logg("New version received for server %d: '%s'\n", i, buf);
5510
+								free(clamav_versions[i]);
5511
+								clamav_versions[i] = cli_strdup(buf);
5512
+							}
5513
+							pthread_mutex_unlock(&version_mutex);
5514
+						} else {
5515
+							logg("^watchdog: expected \"ClamAV\", got \"%s\"\n", buf);
5516
+							session->status = CMDSOCKET_DOWN;
5517
+						}
5518
+					}
5519
+				} else {
5520
+					perror("send");
5521
+					session->status = CMDSOCKET_DOWN;
5522
+				}
5523
+
5524
+				if(session->status == CMDSOCKET_DOWN)
5525
+					logg("^Session %d has gone down\n", i);
5526
+			}
5527
+			/*
5528
+			 * Reset all all dead sessions
5529
+			 */
5530
+			if(session->status == CMDSOCKET_DOWN) {
5531
+				/*
5532
+				 * The END command probably won't get through,
5533
+				 * but let's give it a go anyway
5534
+				 */
5535
+				if(sock >= 0) {
5536
+					send(sock, "END\n", 4, 0);
5537
+					close(sock);
5538
+				}
5539
+
5540
+				logg("#Trying to restart session %d\n", i);
5541
+				if(createSession(i) == 0) {
5542
+					session->status = CMDSOCKET_FREE;
5543
+					logg("^Session %d restarted OK\n", i);
5544
+				}
5545
+			}
5546
+		}
5547
+		for(i = 0; i < max_children; i++)
5548
+			if(sessions[i].status != CMDSOCKET_DOWN)
5549
+				break;
5550
+
5551
+		if(i == max_children)
5552
+			clamdIsDown();
5553
+		pthread_mutex_unlock(&sstatus_mutex);
5554
+
5555
+		/* Garbage collect IP addresses no longer blacklisted */
5556
+		if(blacklist) {
5557
+			pthread_mutex_lock(&blacklist_mutex);
5558
+			tableIterate(blacklist, timeoutBlacklist, NULL);
5559
+			pthread_mutex_unlock(&blacklist_mutex);
5560
+		}
5561
+	}
5562
+	logg("#watchdog quits\n");
5563
+	return NULL;
5564
+}
5565
+#else	/*!SESSION*/
5566
+/*
5567
+ * Reload the database from time to time, when using the internal scanner
5568
+ *
5569
+ * TODO: reload the whiteList file if it's been changed
5570
+ */
5571
+/*ARGSUSED*/
5572
+static void *
5573
+watchdog(void *a)
5574
+{
5575
+	static pthread_mutex_t watchdog_mutex = PTHREAD_MUTEX_INITIALIZER;
5576
+
5577
+	if((!blacklist_time) && external)
5578
+		return NULL;	/* no need for this thread */
5579
+
5580
+	while(!quitting) {
5581
+		struct timespec ts;
5582
+		struct timeval tp;
5583
+
5584
+		gettimeofday(&tp, NULL);
5585
+
5586
+		ts.tv_sec = tp.tv_sec + freshclam_monitor;
5587
+		ts.tv_nsec = tp.tv_usec * 1000;
5588
+		logg("#watchdog sleeps\n");
5589
+
5590
+		pthread_mutex_lock(&watchdog_mutex);
5591
+		/*
5592
+		 * Sometimes this returns EPIPE which isn't listed as a
5593
+		 * return value in the Linux man page for pthread_cond_timedwait
5594
+		 * so I'm not sure why it happens
5595
+		 */
5596
+		switch(pthread_cond_timedwait(&watchdog_cond, &watchdog_mutex, &ts)) {
5597
+			case ETIMEDOUT:
5598
+			case 0:
5599
+				break;
5600
+			default:
5601
+				perror("pthread_cond_timedwait");
5602
+		}
5603
+		pthread_mutex_unlock(&watchdog_mutex);
5604
+		logg("#watchdog wakes\n");
5605
+
5606
+		/*
5607
+		 * TODO: sanity check that if n_children == 0, that
5608
+		 * root->refcount == 0. Unfortunatly root->refcount isn't
5609
+		 * thread-safe, since it's governed by a mutex that we can't
5610
+		 * see, and there's no access to it via an approved method
5611
+		 */
5612
+		if(check_and_reload_database() != 0) {
5613
+			if(cl_error != SMFIS_ACCEPT) {
5614
+				smfi_stop();
5615
+				return NULL;
5616
+			}
5617
+			logg(_("!No emails will be scanned"));
5618
+		}
5619
+		/* Garbage collect IP addresses no longer blacklisted */
5620
+		if(blacklist) {
5621
+			pthread_mutex_lock(&blacklist_mutex);
5622
+			tableIterate(blacklist, timeoutBlacklist, NULL);
5623
+			pthread_mutex_unlock(&blacklist_mutex);
5624
+		}
5625
+	}
5626
+	logg("#watchdog quits\n");
5627
+	return NULL;
5628
+}
5629
+#endif
5630
+
5631
+/*
5632
+ * Check to see if the database needs to be reloaded
5633
+ *	Return 0 for success
5634
+ */
5635
+static int
5636
+check_and_reload_database(void)
5637
+{
5638
+	int rc;
5639
+
5640
+	if(external)
5641
+		return 0;
5642
+
5643
+	if(reload) {
5644
+		rc = 1;
5645
+		reload = 0;
5646
+	} else
5647
+		rc = cl_statchkdir(&dbstat);
5648
+
5649
+	switch(rc) {
5650
+		case 1:
5651
+			logg("^Database has changed, loading updated database\n");
5652
+			cl_statfree(&dbstat);
5653
+			rc = loadDatabase();
5654
+			if(rc != 0) {
5655
+				logg("!Failed to load updated database\n");
5656
+				return rc;
5657
+			}
5658
+			break;
5659
+		case 0:
5660
+			logg("*Database has not changed\n");
5661
+			break;
5662
+		default:
5663
+			logg("Database error %d - %s is stopping\n",
5664
+				rc, progname);
5665
+			return 1;
5666
+	}
5667
+	return 0;	/* all OK */
5668
+}
5669
+
5670
+static void
5671
+timeoutBlacklist(char *ip_address, int time_of_blacklist, void *v)
5672
+{
5673
+	if(time_of_blacklist == 0)	/* Must not blacklist this IP address */
5674
+		return;
5675
+	if((time((time_t *)0) - time_of_blacklist) > blacklist_time)
5676
+		tableRemove(blacklist, ip_address);
5677
+}
5678
+
5679
+static void
5680
+quit(void)
5681
+{
5682
+	quitting++;
5683
+
5684
+#ifdef	SESSION
5685
+	pthread_mutex_lock(&version_mutex);
5686
+#endif
5687
+	logg(_("Stopping %s\n"), clamav_version);
5688
+#ifdef	SESSION
5689
+	pthread_mutex_unlock(&version_mutex);
5690
+#endif
5691
+
5692
+	if(!external) {
5693
+		pthread_mutex_lock(&engine_mutex);
5694
+		if(engine)
5695
+			cl_engine_free(engine);
5696
+		pthread_mutex_unlock(&engine_mutex);
5697
+	} else {
5698
+#ifdef	SESSION
5699
+		int i = 0;
5700
+		struct session *session = sessions;
5701
+
5702
+		pthread_mutex_lock(&sstatus_mutex);
5703
+		for(; i < ((localSocket != NULL) ? 1 : (int)max_children); i++) {
5704
+			/*
5705
+			 * Check all free sessions are still usable
5706
+			 * This could take some time with many free
5707
+			 * sessions to slow remote servers, so only do this
5708
+			 * when the system is quiet (not 100% accurate when
5709
+			 * determining this since n_children isn't locked but
5710
+			 * that doesn't really matter)
5711
+			 */
5712
+			logg("#quit: close server %d\n", i);
5713
+			if(session->status == CMDSOCKET_FREE) {
5714
+				const int sock = session->sock;
5715
+
5716
+				send(sock, "END\n", 4, 0);
5717
+				shutdown(sock, SHUT_WR);
5718
+				session->status = CMDSOCKET_DOWN;
5719
+				pthread_mutex_unlock(&sstatus_mutex);
5720
+				close(sock);
5721
+				pthread_mutex_lock(&sstatus_mutex);
5722
+			}
5723
+			session++;
5724
+		}
5725
+		pthread_mutex_unlock(&sstatus_mutex);
5726
+#endif
5727
+	}
5728
+
5729
+	if(tmpdir)
5730
+		if(rmdir(tmpdir) < 0)
5731
+			perror(tmpdir);
5732
+
5733
+	broadcast(_("Stopping clamav-milter"));
5734
+
5735
+	if(pidfile)
5736
+		if(unlink(pidfile) < 0)
5737
+			perror(pidfile);
5738
+
5739
+	logg_close();
5740
+}
5741
+
5742
+static void
5743
+broadcast(const char *mess)
5744
+{
5745
+	struct sockaddr_in s;
5746
+
5747
+	if(broadcastSock < 0)
5748
+		return;
5749
+
5750
+	memset(&s, '\0', sizeof(struct sockaddr_in));
5751
+	s.sin_family = AF_INET;
5752
+	s.sin_port = (in_port_t)htons(tcpSocket ? tcpSocket : 3310);
5753
+	s.sin_addr.s_addr = htonl(INADDR_BROADCAST);
5754
+
5755
+	logg("#broadcast %s to %d\n", mess, broadcastSock);
5756
+	if(sendto(broadcastSock, mess, strlen(mess), 0, (struct sockaddr *)&s, sizeof(struct sockaddr_in)) < 0)
5757
+		perror("sendto");
5758
+}
5759
+
5760
+/*
5761
+ * Load a new database into the internal scanner
5762
+ */
5763
+static int
5764
+loadDatabase(void)
5765
+{
5766
+	int ret;
5767
+	unsigned int signatures, dboptions;
5768
+	char *daily;
5769
+	struct cl_cvd *d;
5770
+	const struct cfgstruct *cpt;
5771
+	static const char *dbdir;
5772
+
5773
+	assert(!external);
5774
+
5775
+	if(dbdir == NULL) {
5776
+		/*
5777
+		 * First time through, find out in which directory the signature
5778
+		 * databases are
5779
+		 */
5780
+		if((cpt = cfgopt(copt, "DatabaseDirectory")) && cpt->enabled)
5781
+			dbdir = cpt->strarg;
5782
+		else
5783
+			dbdir = cl_retdbdir();
5784
+	}
5785
+
5786
+	daily = cli_malloc(strlen(dbdir) + 11);
5787
+	sprintf(daily, "%s/daily.cvd", dbdir);
5788
+	if(access(daily, R_OK) < 0)
5789
+		sprintf(daily, "%s/daily.cld", dbdir);
5790
+
5791
+
5792
+	logg("#loadDatabase: check %s for updates\n", daily);
5793
+
5794
+	d = cl_cvdhead(daily);
5795
+
5796
+	if(d) {
5797
+		char *ptr;
5798
+		time_t t = d->stime;
5799
+		char buf[26];
5800
+
5801
+		snprintf(clamav_version, VERSION_LENGTH,
5802
+			"ClamAV %s/%u/%s", get_version(), d->version,
5803
+			cli_ctime(&t, buf, sizeof(buf)));
5804
+
5805
+		/* Remove ctime's trailing \n */
5806
+		if((ptr = strchr(clamav_version, '\n')) != NULL)
5807
+			*ptr = '\0';
5808
+
5809
+		cl_cvdfree(d);
5810
+	} else
5811
+		snprintf(clamav_version, VERSION_LENGTH,
5812
+			"ClamAV version %s, clamav-milter version %s",
5813
+			cl_retver(), get_version());
5814
+
5815
+	free(daily);
5816
+
5817
+#ifdef	SESSION
5818
+	pthread_mutex_lock(&version_mutex);
5819
+	if(clamav_versions == NULL) {
5820
+		clamav_versions = (char **)cli_malloc(sizeof(char *));
5821
+		if(clamav_versions == NULL) {
5822
+			pthread_mutex_unlock(&version_mutex);
5823
+			return -1;
5824
+		}
5825
+		clamav_version = cli_malloc(VERSION_LENGTH + 1);
5826
+		if(clamav_version == NULL) {
5827
+			free(clamav_versions);
5828
+			clamav_versions = NULL;
5829
+			pthread_mutex_unlock(&version_mutex);
5830
+			return -1;
5831
+		}
5832
+	}
5833
+	pthread_mutex_unlock(&version_mutex);
5834
+#endif
5835
+	signatures = 0;
5836
+	pthread_mutex_lock(&engine_mutex);
5837
+	if(engine) cl_engine_free(engine);
5838
+	engine = cl_engine_new();
5839
+	if (!engine) {
5840
+		logg("!Can't initialize antivirus engine\n");
5841
+		pthread_mutex_unlock(&engine_mutex);
5842
+		return -1;
5843
+	}
5844
+	if(!cfgopt(copt, "PhishingSignatures")->enabled) {
5845
+		logg("Not loading phishing signatures.\n");
5846
+		dboptions = 0;
5847
+	} else
5848
+		dboptions = CL_DB_PHISHING;
5849
+	if((ret = cl_engine_set(engine, CL_ENGINE_MAX_SCANSIZE, &maxscansize))) {
5850
+		logg("!cli_engine_set(CL_ENGINE_MAX_SCANSIZE) failed: %s\n", cl_strerror(ret));
5851
+		cl_engine_free(engine);
5852
+		pthread_mutex_unlock(&engine_mutex);
5853
+		return -1;
5854
+	}
5855
+	if((ret = cl_engine_set(engine, CL_ENGINE_MAX_FILESIZE, &maxfilesize))) {
5856
+		logg("!cli_engine_set(CL_ENGINE_MAX_FILESIZE) failed: %s\n", cl_strerror(ret));
5857
+		cl_engine_free(engine);
5858
+		pthread_mutex_unlock(&engine_mutex);
5859
+		return -1;
5860
+	}
5861
+	ret = cl_load(dbdir, engine, &signatures, dboptions);
5862
+	if(ret != CL_SUCCESS) {
5863
+		logg("!%s\n", cl_strerror(ret));
5864
+		cl_engine_free(engine);
5865
+		pthread_mutex_unlock(&engine_mutex);
5866
+		return -1;
5867
+	}
5868
+	ret = cl_engine_compile(engine);
5869
+	if(ret != CL_SUCCESS) {
5870
+		logg("!Database initialization error: %s\n", cl_strerror(ret));
5871
+		cl_engine_free(engine);
5872
+		pthread_mutex_unlock(&engine_mutex);
5873
+		return -1;
5874
+	}
5875
+	pthread_mutex_unlock(&engine_mutex);
5876
+#ifdef	SESSION
5877
+	pthread_mutex_lock(&version_mutex);
5878
+#endif
5879
+	logg( _("Loaded %s\n"), clamav_version);
5880
+#ifdef	SESSION
5881
+	pthread_mutex_unlock(&version_mutex);
5882
+#endif
5883
+	logg(_("ClamAV: Protecting against %u viruses\n"), signatures);
5884
+	logg("#Database correctly (re)loaded (%u viruses)\n");
5885
+	return cl_statinidir(dbdir, &dbstat);
5886
+}
5887
+
5888
+static void
5889
+sigsegv(int sig)
5890
+{
5891
+	signal(SIGSEGV, SIG_DFL);
5892
+
5893
+#ifdef HAVE_BACKTRACE
5894
+	print_trace();
5895
+#endif
5896
+
5897
+	logg("!Segmentation fault :-( Bye.., notify bugs@clamav.net\n");
5898
+
5899
+	quitting++;
5900
+	smfi_stop();
5901
+}
5902
+
5903
+extern FILE *logg_fd;
5904
+static void
5905
+sigusr1(int sig)
5906
+{
5907
+
5908
+	signal(SIGUSR1, sigusr1);
5909
+
5910
+	if(!(cfgopt(copt, "LogFile"))->enabled)
5911
+		return;
5912
+
5913
+	logg("SIGUSR1 caught: re-opening log file\n");
5914
+	logg_close();
5915
+	logg("*Log file re-opened\n");
5916
+	dup2(fileno(logg_fd), 2);
5917
+}
5918
+
5919
+static void
5920
+sigusr2(int sig)
5921
+{
5922
+	signal(SIGUSR2, sigusr2);
5923
+
5924
+	logg("^SIGUSR2 caught: scheduling database reload\n");
5925
+	reload++;
5926
+}
5927
+
5928
+#ifdef HAVE_BACKTRACE
5929
+static void
5930
+print_trace(void)
5931
+{
5932
+	void *array[BACKTRACE_SIZE];
5933
+	size_t size, i;
5934
+	char **strings;
5935
+	pid_t pid = getpid();
5936
+
5937
+	size = backtrace(array, BACKTRACE_SIZE);
5938
+	strings = backtrace_symbols(array, size);
5939
+
5940
+	logg("*Backtrace of pid %d:\n", pid);
5941
+
5942
+	for(i = 0; i < size; i++)
5943
+		logg("bt[%u]: %s", i, strings[i]);
5944
+
5945
+	/* TODO: dump the current email */
5946
+
5947
+	free(strings);
5948
+}
5949
+#endif
5950
+
5951
+/*
5952
+ * Check that the correct port name has been given, i.e. that the
5953
+ * input socket to clamav-milter from sendmail, is the same that
5954
+ * sendmail has been configured to use as it's output socket
5955
+ * Return:	<0 invalid
5956
+ *		=0 valid
5957
+ *		>0 unknown
5958
+ *
5959
+ * You wouldn't believe the amount of time I used to waste chasing bug reports
5960
+ *	from people who's sendmail.cf didn't tally with the arguments given to
5961
+ *	clamav-milter before I put this check in, which is why bug 726 must
5962
+ *	never be acted upon.
5963
+ *
5964
+ * FIXME: return different codes for "the value is wrong" and "sendmail.cf"
5965
+ *	hasn't been set up, though that's not so easy to work out.
5966
+ */
5967
+static int
5968
+verifyIncomingSocketName(const char *sockName)
5969
+{
5970
+#if HAVE_MMAP
5971
+	int fd, ret;
5972
+	char *ptr;
5973
+	size_t size;
5974
+	struct stat statb;
5975
+
5976
+	if(strncmp(sockName, "inet:", 5) == 0)
5977
+		/*
5978
+		 * clamav-milter is running on a different machine from sendmail
5979
+		 */
5980
+		return 1;
5981
+
5982
+	if(sendmailCF)
5983
+		fd = open(sendmailCF, O_RDONLY);
5984
+	else {
5985
+		fd = open("/etc/mail/sendmail.cf", O_RDONLY);
5986
+		if(fd < 0)
5987
+			fd = open("/etc/sendmail.cf", O_RDONLY);
5988
+	}
5989
+
5990
+	if(fd < 0)
5991
+		return 1;
5992
+
5993
+	if(fstat(fd, &statb) < 0) {
5994
+		close(fd);
5995
+		return 1;
5996
+	}
5997
+
5998
+	size = statb.st_size;
5999
+
6000
+	if(size == 0) {
6001
+		close(fd);
6002
+		return -1;
6003
+	}
6004
+
6005
+	ptr = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
6006
+	if(ptr == MAP_FAILED) {
6007
+		perror("mmap");
6008
+		close(fd);
6009
+		return -1;
6010
+	}
6011
+
6012
+	ret = (cli_memstr(ptr, size, sockName, strlen(sockName)) != NULL) ? 1 : -1;
6013
+
6014
+	munmap(ptr, size);
6015
+	close(fd);
6016
+
6017
+	return ret;
6018
+#else	/*!HAVE_MMAP*/
6019
+	return 1;
6020
+#endif
6021
+}
6022
+
6023
+/*
6024
+ * If the given email address is whitelisted don't scan emails to them,
6025
+ *	the addresses are in angle brackets e.g. <foo@bar.com>.
6026
+ *
6027
+ * TODO: Allow regular expressions in the addresses
6028
+ * TODO: Syntax check the contents of the files
6029
+ * TODO: Allow emails of the form "name <address>"
6030
+ * TODO: Allow emails not of the form "<address>", i.e. no angle brackets
6031
+ * TODO: Assume that if a '@' is missing from the address, that all emails
6032
+ *	to that domain are to be whitelisted
6033
+ */
6034
+static int
6035
+isWhitelisted(const char *emailaddress, int to)
6036
+{
6037
+	static table_t *to_whitelist, *from_whitelist;	/* never freed */
6038
+	table_t *table;
6039
+
6040
+	logg("*isWhitelisted %s\n", emailaddress);
6041
+
6042
+	/*
6043
+	 * Don't scan messages to the quarantine email address
6044
+	 */
6045
+	if(quarantine && (strcasecmp(quarantine, emailaddress) == 0))
6046
+		return 1;
6047
+
6048
+	if((to_whitelist == NULL) && whitelistFile) {
6049
+		FILE *fin;
6050
+		char buf[BUFSIZ + 1];
6051
+
6052
+		fin = fopen(whitelistFile, "r");
6053
+
6054
+		if(fin == NULL) {
6055
+			perror(whitelistFile);
6056
+			logg(_("!Can't open whitelist file %s"), whitelistFile);
6057
+			return 0;
6058
+		}
6059
+		to_whitelist = tableCreate();
6060
+		from_whitelist = tableCreate();
6061
+
6062
+		if((to_whitelist == NULL) || (from_whitelist == NULL)) {
6063
+			logg(_("!Can't create whitelist table"));
6064
+			if(to_whitelist) {
6065
+				tableDestroy(to_whitelist);
6066
+				to_whitelist = NULL;
6067
+			} else {
6068
+				tableDestroy(from_whitelist);
6069
+				from_whitelist = NULL;
6070
+			}
6071
+			fclose(fin);
6072
+			return 0;
6073
+		}
6074
+
6075
+		while(fgets(buf, sizeof(buf), fin) != NULL) {
6076
+			const char *ptr;
6077
+
6078
+			/* comment line? */
6079
+			switch(buf[0]) {
6080
+				case '#':
6081
+				case '/':
6082
+				case ':':
6083
+					continue;
6084
+			}
6085
+			if(cli_chomp(buf) > 0) {
6086
+				if((ptr = strchr(buf, ':')) != NULL) {
6087
+					do
6088
+						ptr++;
6089
+					while(*ptr && isspace(*ptr));
6090
+
6091
+					if(*ptr == '\0') {
6092
+						logg("*Ignoring bad line '%s'\n",
6093
+							buf);
6094
+						continue;
6095
+					}
6096
+				} else
6097
+					ptr = buf;
6098
+
6099
+				if(strncasecmp(buf, "From:", 5) == 0)
6100
+					table = from_whitelist;
6101
+				else
6102
+					table = to_whitelist;
6103
+
6104
+				(void)tableInsert(table, ptr, 1);
6105
+			}
6106
+		}
6107
+		fclose(fin);
6108
+	}
6109
+	table = (to) ? to_whitelist : from_whitelist;
6110
+
6111
+	if(table && (tableFind(table, emailaddress) == 1))
6112
+		/*
6113
+		 * This recipient is on the whitelist
6114
+		 */
6115
+		return 1;
6116
+
6117
+	return 0;
6118
+}
6119
+
6120
+/*
6121
+ * Blacklist IP addresses that send malware. Often in the phishing world, one
6122
+ * phish is quickly followed by another. IP addresses are blacklisted for one
6123
+ * minute. We can't blacklist for longer since DHCP means we could hit innocent
6124
+ * parties, and in theory malware could go through a smart host and affect
6125
+ * innocent parties
6126
+ *
6127
+ * Note that sites which can't be blacklisted will have their timestamp set
6128
+ * to 0, since that can never be less than blacklist_time seconds from now
6129
+ */
6130
+static int
6131
+isBlacklisted(const char *ip_address)
6132
+{
6133
+	time_t t;
6134
+
6135
+	if(blacklist_time == 0)
6136
+		/* Blacklisting not being used */
6137
+		return 0;
6138
+
6139
+	logg("*isBlacklisted %s\n", ip_address);
6140
+
6141
+	if(isLocal(ip_address))
6142
+		return 0;
6143
+
6144
+	pthread_mutex_lock(&blacklist_mutex);
6145
+	if(blacklist == NULL) {
6146
+		blacklist = tableCreate();
6147
+
6148
+		pthread_mutex_unlock(&blacklist_mutex);
6149
+
6150
+		if(blacklist == NULL)
6151
+			logg(_("!Can't create blacklist table"));
6152
+		return 0;
6153
+	}
6154
+	t = tableFind(blacklist, ip_address);
6155
+	pthread_mutex_unlock(&blacklist_mutex);
6156
+
6157
+	if(t == (time_t)-1)
6158
+		/* IP address is not blacklisted */
6159
+		return 0;
6160
+
6161
+	if(t == (time_t)0)
6162
+		/* IP cannot be blacklisted */
6163
+		return 0;
6164
+
6165
+	if((time((time_t *)0) - t) <= blacklist_time)
6166
+		return 1;
6167
+
6168
+	/* timedout: remove the IP from the blacklist */
6169
+	pthread_mutex_lock(&blacklist_mutex);
6170
+	tableRemove(blacklist, ip_address);
6171
+	pthread_mutex_unlock(&blacklist_mutex);
6172
+
6173
+	return 0;
6174
+}
6175
+
6176
+#ifdef	HAVE_RESOLV_H
6177
+/*
6178
+ * Determine our MX peers, they must never be blacklisted
6179
+ * See RFC1034 for the definition of the record formats
6180
+ *
6181
+ * This is only ever called once, which is wrong, but the overheard of calling
6182
+ * this from the watchdog isn't worth it
6183
+ */
6184
+static table_t *
6185
+mx(const char *host, table_t *t)
6186
+{
6187
+	u_char *p, *end;
6188
+	const HEADER *hp;
6189
+	int len, i;
6190
+	union {
6191
+		HEADER h;
6192
+		u_char u[PACKETSZ];
6193
+	} q;
6194
+	char buf[BUFSIZ];
6195
+
6196
+	if(t == NULL) {
6197
+		t = tableCreate();
6198
+
6199
+		if(t == NULL)
6200
+			return NULL;
6201
+	}
6202
+
6203
+	len = safe_res_query(host, C_IN, T_MX, (u_char *)&q, sizeof(q));
6204
+	if(len < 0)
6205
+		return t;	/* Host has no MX records */
6206
+
6207
+	if((unsigned int)len > sizeof(q))
6208
+		return t;
6209
+
6210
+	hp = &(q.h);
6211
+	p = q.u + HFIXEDSZ;
6212
+	end = q.u + len;
6213
+
6214
+	for(i = ntohs(hp->qdcount); i--; p += len + QFIXEDSZ)
6215
+		if((len = dn_skipname(p, end)) < 0)
6216
+			return t;
6217
+
6218
+	i = ntohs(hp->ancount);
6219
+
6220
+	while((--i >= 0) && (p < end)) {
6221
+		in_addr_t addr;
6222
+		u_short type, pref;
6223
+		u_long ttl;	/* unused */
6224
+
6225
+		if((len = dn_expand(q.u, end, p, buf, sizeof(buf) - 1)) < 0)
6226
+			break;
6227
+		p += len;
6228
+		GETSHORT(type, p);
6229
+		p += INT16SZ;
6230
+		GETLONG(ttl, p);
6231
+		GETSHORT(len, p);
6232
+		if(type != T_MX) {
6233
+			p += len;
6234
+			continue;
6235
+		}
6236
+		GETSHORT(pref, p);
6237
+		if((len = dn_expand(q.u, end, p, buf, sizeof(buf) - 1)) < 0)
6238
+			break;
6239
+		p += len;
6240
+		addr = inet_addr(buf);
6241
+#ifdef	INADDR_NONE
6242
+		if(addr != INADDR_NONE) {
6243
+#else
6244
+		if(addr != (in_addr_t)-1) {
6245
+#endif
6246
+			(void)tableInsert(t, buf, 0);
6247
+		} else
6248
+			t = resolve(buf, t);
6249
+	}
6250
+	return t;
6251
+}
6252
+
6253
+/*
6254
+ * If the MX record points to a name, we need to resolve that name. This routine
6255
+ * does that
6256
+ */
6257
+static table_t *
6258
+resolve(const char *host, table_t *t)
6259
+{
6260
+	u_char *p, *end;
6261
+	const HEADER *hp;
6262
+	int len, i;
6263
+	union {
6264
+		HEADER h;
6265
+		u_char u[PACKETSZ];
6266
+	} q;
6267
+	char buf[BUFSIZ];
6268
+
6269
+	if((host == NULL) || (*host == '\0'))
6270
+		return t;
6271
+
6272
+	len = safe_res_query(host, C_IN, T_A, (u_char *)&q, sizeof(q));
6273
+	if(len < 0)
6274
+		return t;	/* Host has no A records */
6275
+
6276
+	if((unsigned int)len > sizeof(q))
6277
+		return t;
6278
+
6279
+	hp = &(q.h);
6280
+	p = q.u + HFIXEDSZ;
6281
+	end = q.u + len;
6282
+
6283
+	for(i = ntohs(hp->qdcount); i--; p += len + QFIXEDSZ)
6284
+		if((len = dn_skipname(p, end)) < 0)
6285
+			return t;
6286
+
6287
+	i = ntohs(hp->ancount);
6288
+
6289
+	while((--i >= 0) && (p < end)) {
6290
+		u_short type;
6291
+		u_long ttl;
6292
+		const char *ip;
6293
+		struct in_addr addr;
6294
+
6295
+		if((len = dn_expand(q.u, end, p, buf, sizeof(buf) - 1)) < 0)
6296
+			return t;
6297
+		p += len;
6298
+		GETSHORT(type, p);
6299
+		p += INT16SZ;
6300
+		GETLONG(ttl, p);	/* unused */
6301
+		GETSHORT(len, p);
6302
+		if(type != T_A) {
6303
+			p += len;
6304
+			continue;
6305
+		}
6306
+		memcpy(&addr, p, sizeof(struct in_addr));
6307
+		p += 4;	/* Should check len == 4 */
6308
+		ip = inet_ntoa(addr);
6309
+		if(ip) {
6310
+			if(t == NULL) {
6311
+				t = tableCreate();
6312
+
6313
+				if(t == NULL)
6314
+					return NULL;
6315
+			}
6316
+			(void)tableInsert(t, ip, 0);
6317
+		}
6318
+	}
6319
+	return t;
6320
+}
6321
+
6322
+/*
6323
+ * Validate SPF records to help to stop Phish false positives
6324
+ * http://www.openspf.org/SPF_Record_Syntax
6325
+ *
6326
+ * Currently only handles ip4, a and mx fields in the DNS record
6327
+ * Having said that, this is NOT a replacement for spf-milter, it is NOT
6328
+ *	an SPF system, we ONLY use SPF records to reduce phish false positives
6329
+ * TODO: IPv6?
6330
+ * TODO: cache queries?
6331
+ *
6332
+ * INPUT: prevhosts, a list of hosts already searched: stops include loops
6333
+ *	e.g. mercado.com includes medrcadosw.com which includes mercado.com,
6334
+ *	causing a loop
6335
+ * Return 1 if SPF says this email is from a legitimate source
6336
+ *	0 for fail or unknown
6337
+ */
6338
+static int
6339
+spf(struct privdata *privdata, table_t *prevhosts)
6340
+{
6341
+	char *host, *ptr;
6342
+	u_char *p, *end;
6343
+	const HEADER *hp;
6344
+	int len, i;
6345
+	union {
6346
+		HEADER h;
6347
+		u_char u[PACKETSZ];
6348
+	} q;
6349
+	char buf[BUFSIZ];
6350
+
6351
+	if(privdata->spf_ok)
6352
+		return 1;
6353
+	if(privdata->ip[0] == '\0')
6354
+		return 0;
6355
+	if(strcmp(privdata->ip, "127.0.0.1") == 0) {
6356
+		/* Loopback always pass SPF */
6357
+		privdata->spf_ok = 1;
6358
+		return 1;
6359
+	}
6360
+	if(isLocal(privdata->ip)) {
6361
+		/* Local addresses always pass SPF */
6362
+		privdata->spf_ok = 1;
6363
+		return 1;
6364
+	}
6365
+
6366
+	if(privdata->from == NULL)
6367
+		return 0;
6368
+	if((host = strrchr(privdata->from, '@')) == NULL)
6369
+		return 0;
6370
+
6371
+	host = cli_strdup(++host);
6372
+
6373
+	if(host == NULL)
6374
+		return 0;
6375
+
6376
+	ptr = strchr(host, '>');
6377
+
6378
+	if(ptr)
6379
+		*ptr = '\0';
6380
+
6381
+	logg("*SPF query '%s'\n", host);
6382
+	len = safe_res_query(host, C_IN, T_TXT, (u_char *)&q, sizeof(q));
6383
+	if(len < 0) {
6384
+		free(host);
6385
+		return 0;	/* Host has no TXT records */
6386
+	}
6387
+
6388
+	if((unsigned int)len > sizeof(q)) {
6389
+		free(host);
6390
+		return 0;
6391
+	}
6392
+
6393
+	hp = &(q.h);
6394
+	p = q.u + HFIXEDSZ;
6395
+	end = q.u + len;
6396
+
6397
+	for(i = ntohs(hp->qdcount); i--; p += len + QFIXEDSZ)
6398
+		if((len = dn_skipname(p, end)) < 0) {
6399
+			free(host);
6400
+			return 0;
6401
+		}
6402
+
6403
+	i = ntohs(hp->ancount);
6404
+
6405
+	while((--i >= 0) && (p < end) && !privdata->spf_ok) {
6406
+		u_short type;
6407
+		u_long ttl;
6408
+		char txt[BUFSIZ];
6409
+
6410
+		if((len = dn_expand(q.u, end, p, buf, sizeof(buf) - 1)) < 0) {
6411
+			free(host);
6412
+			return 0;
6413
+		}
6414
+		p += len;
6415
+		GETSHORT(type, p);
6416
+		p += INT16SZ;
6417
+		GETLONG(ttl, p);	/* unused */
6418
+		GETSHORT(len, p);
6419
+		if(type != T_TXT) {
6420
+			p += len;
6421
+			continue;
6422
+		}
6423
+		strncpy(txt, (const char *)&p[1], sizeof(txt) - 1);
6424
+		txt[sizeof(txt)-1]='\0';
6425
+		txt[len - 1] = '\0';
6426
+		if((strncmp(txt, "v=spf1 ", 7) == 0) || (strncmp(txt, "spf2.0/pra ", 11) == 0)) {
6427
+			int j;
6428
+			char *record;
6429
+			struct in_addr remote_ip;	/* IP connecting to us */
6430
+
6431
+			logg("*%s(%s): SPF record %s\n",
6432
+				host, privdata->ip, txt);
6433
+#ifdef HAVE_INET_NTOP
6434
+			/* IPv4 address ? */
6435
+			if(inet_pton(AF_INET, privdata->ip, &remote_ip) <= 0) {
6436
+				p += len;
6437
+				continue;
6438
+			}
6439
+#else
6440
+			if(inet_aton(privdata->ip, &remote_ip) == 0) {
6441
+				p += len;
6442
+				continue;
6443
+			}
6444
+#endif
6445
+
6446
+			j = 1;	/* strtok 0 would give the v= part */
6447
+			while((record = cli_strtok(txt, j++, " ")) != NULL) {
6448
+				if(strncmp(record, "ip4:", 4) == 0) {
6449
+					int preflen;
6450
+					char *ip, *pref;
6451
+					uint32_t mask;
6452
+					struct in_addr spf_range;	/* acceptable range of IPs */
6453
+
6454
+					ip = &record[4];
6455
+
6456
+					pref = strchr(ip, '/');
6457
+					preflen = 32;
6458
+					if(pref) {
6459
+						*pref++ = '\0';
6460
+						if(*pref)
6461
+							preflen = atoi(pref);
6462
+					}
6463
+
6464
+#ifdef HAVE_INET_NTOP
6465
+					/* IPv4 address ? */
6466
+					if(inet_pton(AF_INET, ip, &spf_range) <= 0) {
6467
+						free(record);
6468
+						continue;
6469
+					}
6470
+#else
6471
+					if(inet_aton(ip, &spf_range) == 0) {
6472
+						free(record);
6473
+						continue;
6474
+					}
6475
+#endif
6476
+					mask = MAKEMASK(preflen);
6477
+					if((ntohl(remote_ip.s_addr) & mask) == (ntohl(spf_range.s_addr) & mask)) {
6478
+						if(privdata->subject)
6479
+							logg("#SPF ip4 pass (%s) %s is valid for %s\n",
6480
+								privdata->subject, ip, host);
6481
+						else
6482
+							logg("#SPF ip4 pass %s is valid for %s\n", ip, host);
6483
+						privdata->spf_ok = 1;
6484
+					}
6485
+				} else if(strcmp(record, "mx") == 0) {
6486
+					table_t *t = mx(host, NULL);
6487
+
6488
+					if(t) {
6489
+						tableIterate(t, spf_ip,
6490
+							(void *)privdata);
6491
+						tableDestroy(t);
6492
+					}
6493
+				} else if(strcmp(record, "a") == 0) {
6494
+					table_t *t = resolve(host, NULL);
6495
+
6496
+					if(t) {
6497
+						tableIterate(t, spf_ip,
6498
+							(void *)privdata);
6499
+						tableDestroy(t);
6500
+					}
6501
+				} else if(strncmp(record, "a:", 2) == 0) {
6502
+					const char *ahost = &record[2];
6503
+
6504
+					if(*ahost && (strcmp(ahost, host) != 0)) {
6505
+						table_t *t = resolve(ahost, NULL);
6506
+
6507
+						if(t) {
6508
+							tableIterate(t, spf_ip,
6509
+								(void *)privdata);
6510
+							tableDestroy(t);
6511
+						}
6512
+					}
6513
+				} else if(strncmp(record, "mx:", 3) == 0) {
6514
+					const char *mxhost = &record[3];
6515
+
6516
+					if(*mxhost && (strcmp(mxhost, host) != 0)) {
6517
+						table_t *t = mx(mxhost, NULL);
6518
+
6519
+						if(t) {
6520
+							tableIterate(t, spf_ip,
6521
+								(void *)privdata);
6522
+							tableDestroy(t);
6523
+						}
6524
+					}
6525
+				} else if(strncmp(record, "include:", 8) == 0) {
6526
+					const char *inchost = &record[8];
6527
+
6528
+					/*
6529
+					 * Ensure we haven't already looked at
6530
+					 *	the host that's to be included
6531
+					 */
6532
+					if(*inchost &&
6533
+					   (strcmp(inchost, host) != 0) &&
6534
+					   (tableFind(prevhosts, inchost) == -1)) {
6535
+						char *real_from = privdata->from;
6536
+						privdata->from = cli_malloc(strlen(inchost) + 3);
6537
+						sprintf(privdata->from, "n@%s", inchost);
6538
+						tableInsert(prevhosts, host, 0);
6539
+						spf(privdata, prevhosts);
6540
+						free(privdata->from);
6541
+						privdata->from = real_from;
6542
+					}
6543
+				}
6544
+				free(record);
6545
+				if(privdata->spf_ok)
6546
+					break;
6547
+			}
6548
+		}
6549
+		p += len;
6550
+	}
6551
+	free(host);
6552
+
6553
+	return privdata->spf_ok;
6554
+}
6555
+
6556
+static void
6557
+spf_ip(char *ip, int zero, void *v)
6558
+{
6559
+	struct privdata *privdata = (struct privdata *)v;
6560
+
6561
+	if(strcmp(ip, privdata->ip) == 0) {
6562
+		if(privdata->subject)
6563
+			logg("#SPF mx/a pass (%s) %s\n", privdata->subject, ip);
6564
+		else
6565
+			logg("#SPF mx/a pass %s\n", ip);
6566
+		privdata->spf_ok = 1;
6567
+	}
6568
+}
6569
+
6570
+#else	/*!HAVE_RESOLV_H */
6571
+static table_t *
6572
+mx(const char *host, table_t *t)
6573
+{
6574
+	logg(_("^MX peers will not be immune from being blacklisted"));
6575
+
6576
+	if(blacklist == NULL)
6577
+		blacklist = tableCreate();
6578
+	return NULL;
6579
+}
6580
+#endif	/* HAVE_RESOLV_H */
6581
+
6582
+static sfsistat
6583
+black_hole(const struct privdata *privdata)
6584
+{
6585
+	int must_scan;
6586
+	char **to;
6587
+
6588
+	to = privdata->to;
6589
+	must_scan = (*to) ? 0 : 1;
6590
+
6591
+	for(; *to; to++) {
6592
+		pid_t pid, w;
6593
+		int pv[2], status;
6594
+		FILE *sendmail;
6595
+		char buf[BUFSIZ];
6596
+
6597
+		logg("*Calling \"%s -bv %s\"\n", SENDMAIL_BIN, *to);
6598
+
6599
+		if(pipe(pv) < 0) {
6600
+			perror("pipe");
6601
+			logg(_("!Can't create pipe\n"));
6602
+			must_scan = 1;
6603
+			break;
6604
+		}
6605
+		pid = fork();
6606
+		if(pid == 0) {
6607
+			close(1);
6608
+			close(pv[0]);
6609
+			dup2(pv[1], 1);
6610
+			close(pv[1]);
6611
+
6612
+			/*
6613
+			 * Avoid calling popen() since *to isn't trusted
6614
+			 */
6615
+			execl(SENDMAIL_BIN, "sendmail", "-bv", *to, NULL);
6616
+			perror(SENDMAIL_BIN);
6617
+			logg("Can't execl %s\n", SENDMAIL_BIN);
6618
+			_exit(errno ? errno : 1);
6619
+		}
6620
+		if(pid == -1) {
6621
+			perror("fork");
6622
+			logg(_("!Can't fork\n"));
6623
+			close(pv[0]);
6624
+			close(pv[1]);
6625
+			must_scan = 1;
6626
+			break;
6627
+		}
6628
+		close(pv[1]);
6629
+		sendmail = fdopen(pv[0], "r");
6630
+
6631
+		if(sendmail == NULL) {
6632
+			logg("fdopen failed\n");
6633
+			close(pv[0]);
6634
+			must_scan = 1;
6635
+			break;
6636
+		}
6637
+
6638
+		while(fgets(buf, sizeof(buf), sendmail) != NULL) {
6639
+			if(cli_chomp(buf) == 0)
6640
+				continue;
6641
+
6642
+			logg("*sendmail output: %s\n", buf);
6643
+
6644
+			if(strstr(buf, "... deliverable: mailer ")) {
6645
+				const char *p = strstr(buf, ", user ");
6646
+
6647
+				if(strcmp(&p[7], "/dev/null") != 0) {
6648
+					must_scan = 1;
6649
+					break;
6650
+				}
6651
+			}
6652
+		}
6653
+		fclose(sendmail);
6654
+
6655
+		status = -1;
6656
+		do
6657
+			w = wait(&status);
6658
+		while((w != pid) && (w != -1));
6659
+
6660
+		if(w == -1)
6661
+			status = -1;
6662
+		else
6663
+			status = WEXITSTATUS(status);
6664
+
6665
+		switch(status) {
6666
+			case EX_NOUSER:
6667
+			case EX_OK:
6668
+				break;
6669
+			default:
6670
+				logg(_("^Can't execute '%s' to expand '%s' (error %d)\n"),
6671
+					SENDMAIL_BIN, *to, WEXITSTATUS(status));
6672
+				must_scan = 1;
6673
+		}
6674
+		if(must_scan)
6675
+			break;
6676
+	}
6677
+	if(!must_scan) {
6678
+		/* All recipients map to /dev/null */
6679
+		to = privdata->to;
6680
+		if(*to)
6681
+			logg("Discarded, since all recipients (e.g. \"%s\") are /dev/null\n", *to);
6682
+		else
6683
+			logg("Discarded, since all recipients are /dev/null\n");
6684
+		return SMFIS_DISCARD;
6685
+	}
6686
+	return SMFIS_CONTINUE;
6687
+}
6688
+
6689
+/* See also libclamav/mbox.c */
6690
+static int
6691
+useful_header(const char *cmd)
6692
+{
6693
+	if(strcasecmp(cmd, "From") == 0)
6694
+		return 1;
6695
+	if(strcasecmp(cmd, "Received") == 0)
6696
+		return 1;
6697
+	if(strcasecmp(cmd, "Content-Type") == 0)
6698
+		return 1;
6699
+	if(strcasecmp(cmd, "Content-Transfer-Encoding") == 0)
6700
+		return 1;
6701
+	if(strcasecmp(cmd, "Content-Disposition") == 0)
6702
+		return 1;
6703
+	if(strcasecmp(cmd, "De") == 0)
6704
+		return 1;
6705
+
6706
+	return 0;
6707
+}
6708
+
6709
+static int
6710
+increment_connexions(void)
6711
+{
6712
+	if(max_children > 0) {
6713
+		int rc = 0;
6714
+
6715
+		pthread_mutex_lock(&n_children_mutex);
6716
+
6717
+		/*
6718
+		 * Wait a while since sendmail doesn't like it if we
6719
+		 * take too long replying. Effectively this means that
6720
+		 * max_children is more of a hint than a rule
6721
+		 */
6722
+		if(n_children >= max_children) {
6723
+			struct timespec timeout;
6724
+			struct timeval now;
6725
+			struct timezone tz;
6726
+
6727
+			logg((dont_wait) ?
6728
+					_("hit max-children limit (%u >= %u)\n") :
6729
+					_("hit max-children limit (%u >= %u): waiting for some to exit\n"),
6730
+				n_children, max_children);
6731
+
6732
+			if(dont_wait) {
6733
+				pthread_mutex_unlock(&n_children_mutex);
6734
+				return 0;
6735
+			}
6736
+			/*
6737
+			 * Wait for an amount of time for a child to go
6738
+			 *
6739
+			 * Use pthread_cond_timedwait rather than
6740
+			 * pthread_cond_wait since the sendmail which
6741
+			 * calls us will have a timeout that we don't
6742
+			 * want to exceed, stops sendmail getting
6743
+			 * fidgety.
6744
+			 *
6745
+			 * Patch from Damian Menscher
6746
+			 * <menscher@uiuc.edu> to ensure it wakes up
6747
+			 * when a child goes away
6748
+			 */
6749
+			gettimeofday(&now, &tz);
6750
+			do {
6751
+				logg(_("n_children %d: waiting %d seconds for some to exit\n"),
6752
+					n_children, child_timeout);
6753
+
6754
+				if(child_timeout == 0) {
6755
+					pthread_cond_wait(&n_children_cond, &n_children_mutex);
6756
+					rc = 0;
6757
+				} else {
6758
+					timeout.tv_sec = now.tv_sec + child_timeout;
6759
+					timeout.tv_nsec = 0;
6760
+
6761
+					rc = pthread_cond_timedwait(&n_children_cond, &n_children_mutex, &timeout);
6762
+				}
6763
+			} while((n_children >= max_children) && (rc != ETIMEDOUT));
6764
+			logg(_("Finished waiting, n_children = %d\n"), n_children);
6765
+		}
6766
+		n_children++;
6767
+
6768
+		logg("*>n_children = %d\n", n_children);
6769
+		pthread_mutex_unlock(&n_children_mutex);
6770
+
6771
+		if(child_timeout && (rc == ETIMEDOUT))
6772
+			logg(_("Timeout waiting for a child to die\n"));
6773
+	}
6774
+
6775
+	return 1;
6776
+}
6777
+
6778
+static void
6779
+decrement_connexions(void)
6780
+{
6781
+	if(max_children > 0) {
6782
+		pthread_mutex_lock(&n_children_mutex);
6783
+		logg("*decrement_connexions: n_children = %d\n", n_children);
6784
+		/*
6785
+		 * Deliberately errs on the side of broadcasting too many times
6786
+		 */
6787
+		if(n_children > 0)
6788
+			if(--n_children == 0) {
6789
+				logg("*%s is idle\n", progname);
6790
+				if(pthread_cond_broadcast(&watchdog_cond) < 0)
6791
+					perror("pthread_cond_broadcast");
6792
+			}
6793
+#ifdef	CL_DEBUG
6794
+		logg("*pthread_cond_broadcast\n");
6795
+#endif
6796
+		if(pthread_cond_broadcast(&n_children_cond) < 0)
6797
+			perror("pthread_cond_broadcast");
6798
+		logg("*<n_children = %d\n", n_children);
6799
+		pthread_mutex_unlock(&n_children_mutex);
6800
+	}
6801
+}
6802
+
6803
+static void
6804
+dump_blacklist(char *key, int value, void *v)
6805
+{
6806
+	logg(_("Won't blacklist %s\n"), key);
6807
+}
6808
+
6809
+/*
6810
+ * Non-blocking connect, based on an idea by Everton da Silva Marques
6811
+ *	 <everton.marques@gmail.com>
6812
+ * FIXME: There are lots of copies of this code :-(
6813
+ */
6814
+static int
6815
+nonblock_connect(int sock, const struct sockaddr_in *sin, const char *hostname)
6816
+{
6817
+	int select_failures;	/* Max. of unexpected select() failures */
6818
+	int attempts;
6819
+	struct timeval timeout;	/* When we should time out */
6820
+	int numfd;		/* Highest fdset fd plus 1 */
6821
+	long flags;
6822
+
6823
+	gettimeofday(&timeout, 0);	/* store when we started to connect */
6824
+
6825
+	if(hostname == NULL)
6826
+		hostname = "clamav-milter";	/* It's only used in debug messages */
6827
+
6828
+#ifdef	F_GETFL
6829
+	flags = fcntl(sock, F_GETFL, 0);
6830
+
6831
+	if(flags == -1L)
6832
+		logg("^getfl: %s\n", strerror(errno));
6833
+	else if(fcntl(sock, F_SETFL, (long)(flags | O_NONBLOCK)) < 0)
6834
+		logg("^setfl: %s\n", strerror(errno));
6835
+#else
6836
+	flags = -1L;
6837
+#endif
6838
+	if(connect(sock, (const struct sockaddr *)sin, sizeof(struct sockaddr_in)) != 0)
6839
+		switch(errno) {
6840
+			case EALREADY:
6841
+			case EINPROGRESS:
6842
+				logg("*%s: connect: %s\n", hostname,
6843
+					strerror(errno));
6844
+				break; /* wait for connection */
6845
+			case EISCONN:
6846
+				return 0; /* connected */
6847
+			default:
6848
+				logg("^%s: connect: %s\n", hostname,
6849
+					strerror(errno));
6850
+#ifdef	F_SETFL
6851
+				if(flags != -1L)
6852
+					if(fcntl(sock, F_SETFL, flags))
6853
+						logg("^f_setfl: %s\n", strerror(errno));
6854
+#endif
6855
+				return -1; /* failed */
6856
+		}
6857
+	else {
6858
+#ifdef	F_SETFL
6859
+		if(flags != -1L)
6860
+			if(fcntl(sock, F_SETFL, flags))
6861
+				logg("^f_setfl: %s\n", strerror(errno));
6862
+#endif
6863
+		return connect_error(sock, hostname);
6864
+	}
6865
+
6866
+	numfd = (int)sock + 1;
6867
+	select_failures = NONBLOCK_SELECT_MAX_FAILURES;
6868
+	attempts = 1;
6869
+	timeout.tv_sec += CONNECT_TIMEOUT;
6870
+
6871
+	for (;;) {
6872
+		int n, t;
6873
+		fd_set fds;
6874
+		struct timeval now, waittime;
6875
+
6876
+		/* Force timeout if we ran out of time */
6877
+		gettimeofday(&now, 0);
6878
+		t = (now.tv_sec == timeout.tv_sec) ?
6879
+			(now.tv_usec > timeout.tv_usec) :
6880
+			(now.tv_sec > timeout.tv_sec);
6881
+
6882
+		if(t) {
6883
+			logg("^%s: connect timeout (%d secs)\n",
6884
+				hostname, CONNECT_TIMEOUT);
6885
+			break;
6886
+		}
6887
+
6888
+		/* Calculate how long to wait */
6889
+		waittime.tv_sec = timeout.tv_sec - now.tv_sec;
6890
+		waittime.tv_usec = timeout.tv_usec - now.tv_usec;
6891
+		if(waittime.tv_usec < 0) {
6892
+			waittime.tv_sec--;
6893
+			waittime.tv_usec += 1000000;
6894
+		}
6895
+
6896
+		/* Init fds with 'sock' as the only fd */
6897
+		FD_ZERO(&fds);
6898
+		FD_SET(sock, &fds);
6899
+
6900
+		n = select(numfd, 0, &fds, 0, &waittime);
6901
+		if(n < 0) {
6902
+			logg("^%s: select attempt %d %s\n",
6903
+				hostname, select_failures, strerror(errno));
6904
+			if(--select_failures >= 0)
6905
+				continue; /* not timed-out, try again */
6906
+			break; /* failed */
6907
+		}
6908
+
6909
+		logg("*%s: select = %d\n", hostname, n);
6910
+
6911
+		if(n) {
6912
+#ifdef	F_SETFL
6913
+			if(flags != -1L)
6914
+				if(fcntl(sock, F_SETFL, flags))
6915
+					logg("^f_setfl: %s\n", strerror(errno));
6916
+#endif
6917
+			return connect_error(sock, hostname);
6918
+		}
6919
+
6920
+		/* timeout */
6921
+		if(attempts++ == NONBLOCK_MAX_ATTEMPTS) {
6922
+			logg("^timeout connecting to %s\n", hostname);
6923
+			break;
6924
+		}
6925
+	}
6926
+
6927
+#ifdef	F_SETFL
6928
+	if(flags != -1L)
6929
+		if(fcntl(sock, F_SETFL, flags))
6930
+			logg("^f_setfl: %s\n", strerror(errno));
6931
+#endif
6932
+	return -1; /* failed */
6933
+}
6934
+
6935
+static int
6936
+connect_error(int sock, const char *hostname)
6937
+{
6938
+#ifdef	SO_ERROR
6939
+	int optval;
6940
+	socklen_t optlen = sizeof(optval);
6941
+
6942
+	getsockopt(sock, SOL_SOCKET, SO_ERROR, &optval, &optlen);
6943
+
6944
+	if(optval) {
6945
+		logg("^%s: %s\n", hostname, strerror(optval));
6946
+		return -1;
6947
+	}
6948
+#endif
6949
+	return 0;
6950
+}
0 6951
new file mode 100644
... ...
@@ -0,0 +1,215 @@
0
+/*
1
+ *  Copyright (C)2008 Sourcefire, Inc.
2
+ *
3
+ *  Author: aCaB <acab@clamav.net>
4
+ *
5
+ *  This program is free software; you can redistribute it and/or modify
6
+ *  it under the terms of the GNU General Public License version 2 as
7
+ *  published by the Free Software Foundation.
8
+ *
9
+ *  This program is distributed in the hope that it will be useful,
10
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
+ *  GNU General Public License for more details.
13
+ *
14
+ *  You should have received a copy of the GNU General Public License
15
+ *  along with this program; if not, write to the Free Software
16
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17
+ *  MA 02110-1301, USA.
18
+ */
19
+
20
+#if HAVE_CONFIG_H
21
+#include "clamav-config.h"
22
+#endif
23
+
24
+#include <stdlib.h>
25
+#include <string.h>
26
+#include <sys/types.h>
27
+#include <unistd.h>
28
+
29
+#include <libmilter/mfapi.h>
30
+
31
+#include "shared/output.h"
32
+
33
+#include "connpool.h"
34
+#include "netcode.h"
35
+
36
+uint64_t maxfilesize;
37
+
38
+#define CLAMFIBUFSZ 1424
39
+
40
+struct CLAMFI {
41
+    char buffer[CLAMFIBUFSZ];
42
+    int local;
43
+    int main;
44
+    int alt;
45
+    unsigned int altsz;
46
+    unsigned int bufsz;
47
+};
48
+
49
+
50
+#define FREECF freecf(ctx, cf)
51
+
52
+static void freecf(SMFICTX *ctx, struct CLAMFI *cf) {
53
+    close(cf->main);
54
+    close(cf->alt);
55
+    smfi_setpriv(ctx, NULL);
56
+    free(cf);
57
+}
58
+
59
+
60
+static sfsistat sendchunk(struct CLAMFI *cf, unsigned char *bodyp, size_t len, SMFICTX *ctx) {
61
+    if(cf->altsz > maxfilesize)
62
+	return SMFIS_CONTINUE; /* FIXME: SMFIS_SKIP needs negotiation (only for _body() */
63
+
64
+    if(cf->altsz + len > maxfilesize)
65
+	len = maxfilesize - cf->altsz;
66
+
67
+    if(cf->local) {
68
+	while(len) {
69
+	    int n = write(cf->alt, bodyp, len);
70
+
71
+	    if (n==-1) {
72
+		logg("!clamfi_body: Failed to write temporary file\n");
73
+		FREECF;
74
+		return SMFIS_TEMPFAIL;
75
+	    }
76
+	    len -= n;
77
+	    bodyp += n;
78
+	}
79
+    } else {
80
+	int sendfailed = 0;
81
+
82
+	if(len < CLAMFIBUFSZ - cf->bufsz) {
83
+	    memcpy(&cf->buffer[cf->bufsz], bodyp, len);
84
+	    cf->bufsz += len;
85
+	} else if(len < CLAMFIBUFSZ) {
86
+	    memcpy(&cf->buffer[cf->bufsz], bodyp, CLAMFIBUFSZ - cf->bufsz);
87
+	    sendfailed = nc_send(cf->alt, cf->buffer, CLAMFIBUFSZ);
88
+	    len -= (CLAMFIBUFSZ - cf->bufsz);
89
+	    memcpy(cf->buffer, &bodyp[CLAMFIBUFSZ - cf->bufsz], len);
90
+	    cf->bufsz = len;
91
+	} else {
92
+	    sendfailed = nc_send(cf->alt, cf->buffer, cf->bufsz);
93
+	    sendfailed += nc_send(cf->alt, bodyp, len);
94
+	    cf->bufsz = 0;
95
+	}
96
+	if(sendfailed) {
97
+	    logg("!clamfi_body: Streaming failed\n");
98
+	    FREECF;
99
+	    return SMFIS_TEMPFAIL;
100
+	}
101
+    }
102
+    cf->altsz += len;
103
+    return SMFIS_CONTINUE;
104
+}
105
+
106
+
107
+sfsistat clamfi_body(SMFICTX *ctx, unsigned char *bodyp, size_t len) {
108
+    struct CLAMFI *cf;
109
+
110
+    if(!(cf = (struct CLAMFI *)smfi_getpriv(ctx))) {
111
+	sfsistat ret;
112
+	cf = (struct CLAMFI *)malloc(sizeof(*cf));
113
+	if(!cf) {
114
+	    logg("!clamfi_body: Failed to allocate CLAMFI struct\n");
115
+	    return SMFIS_TEMPFAIL;
116
+	}
117
+	cf->altsz = 0;
118
+	cf->bufsz = 0;
119
+	if(nc_connect_rand(&cf->main, &cf->alt, &cf->local)) {
120
+	    logg("!clamfi_body: Failed to initiate streaming/fdpassing\n");
121
+	    free(cf);
122
+	    return SMFIS_TEMPFAIL;
123
+	}
124
+	smfi_setpriv(ctx, (void *)cf);
125
+	if((ret = sendchunk(cf, (unsigned char *)"From clamav-milter\n", 19, ctx)) != SMFIS_CONTINUE)
126
+	    return ret;
127
+    }
128
+    return sendchunk(cf, bodyp, len, ctx);
129
+}
130
+
131
+
132
+sfsistat clamfi_eom(SMFICTX *ctx) {
133
+    struct CLAMFI *cf;
134
+    char *reply;
135
+    int len, ret;
136
+
137
+    if(!(cf = (struct CLAMFI *)smfi_getpriv(ctx)))
138
+	return SMFIS_CONTINUE; /* whatever */
139
+
140
+    if(cf->local) {
141
+	struct iovec iov[1];
142
+	struct msghdr msg;
143
+	struct cmsghdr *cmsg;
144
+	unsigned char fdbuf[CMSG_SPACE(sizeof(int))];
145
+	char dummy[]="";
146
+
147
+	if(nc_send(cf->main, "nFILDES\n", 8)) {
148
+	    logg("!clamfi_eom: FD scan request failed\n");
149
+	    FREECF;
150
+	    return SMFIS_TEMPFAIL;
151
+	}
152
+
153
+	lseek(cf->alt, 0, SEEK_SET);
154
+	iov[0].iov_base = dummy;
155
+	iov[0].iov_len = 1;
156
+	memset(&msg, 0, sizeof(msg));
157
+	msg.msg_control = fdbuf;
158
+	msg.msg_iov = iov;
159
+	msg.msg_iovlen = 1;
160
+	msg.msg_controllen = CMSG_LEN(sizeof(int));
161
+	cmsg = CMSG_FIRSTHDR(&msg);
162
+	cmsg->cmsg_len = CMSG_LEN(sizeof(int));
163
+	cmsg->cmsg_level = SOL_SOCKET;
164
+	cmsg->cmsg_type = SCM_RIGHTS;
165
+	*(int *)CMSG_DATA(cmsg) = cf->alt;
166
+	if(sendmsg(cf->main, &msg, 0) == -1) {
167
+	    /* FIXME: nonblock code needed (?) */
168
+	    logg("!clamfi_eom: FD send failed\n");
169
+	    FREECF;
170
+	    return SMFIS_TEMPFAIL;
171
+	}
172
+    } else {
173
+	if(cf->bufsz && nc_send(cf->alt, cf->buffer, cf->bufsz)) {
174
+	    logg("!clamfi_eom: Flushing failed\n");
175
+	    FREECF;
176
+	    return SMFIS_TEMPFAIL;
177
+	}
178
+	close(cf->alt);
179
+    }
180
+
181
+    reply = nc_recv(cf->main);
182
+
183
+    if(cf->local) close(cf->alt);
184
+    close(cf->main);
185
+    close(cf->alt);
186
+    smfi_setpriv(ctx, NULL);
187
+    free(cf);
188
+
189
+    if(!reply) {
190
+	logg("!clamfi_eom: no reply to scan request\n");
191
+	return SMFIS_TEMPFAIL;
192
+    }
193
+    len = strlen(reply);
194
+    if(len>5 && !strcmp(reply + len - 5, ": OK\n"))
195
+	ret = SMFIS_ACCEPT;
196
+    else if (len>7 && !strcmp(reply + len - 7, " FOUND\n"))
197
+	ret = SMFIS_REJECT;
198
+    else {
199
+	logg("!clamfi_eom: unknown reply from clamd\n");
200
+	ret = SMFIS_TEMPFAIL;
201
+    }
202
+
203
+    free(reply);
204
+    return ret;
205
+}
206
+
207
+/*
208
+ * Local Variables:
209
+ * mode: c
210
+ * c-basic-offset: 4
211
+ * tab-width: 8
212
+ * End: 
213
+ * vim: set cindent smartindent autoindent softtabstop=4 shiftwidth=4 tabstop=8: 
214
+ */
0 215
new file mode 100644
... ...
@@ -0,0 +1,11 @@
0
+#ifndef _CLAMFI_H
1
+#define _CLAMFI_H
2
+
3
+#include <libmilter/mfapi.h>
4
+
5
+uint64_t maxfilesize;
6
+
7
+sfsistat clamfi_body(SMFICTX *ctx, unsigned char *bodyp, size_t len);
8
+sfsistat clamfi_eom(SMFICTX *ctx);
9
+
10
+#endif
0 11
new file mode 100644
... ...
@@ -0,0 +1,324 @@
0
+/*
1
+ *  Copyright (C)2008 Sourcefire, Inc.
2
+ *
3
+ *  Author: aCaB <acab@clamav.net>
4
+ *
5
+ *  This program is free software; you can redistribute it and/or modify
6
+ *  it under the terms of the GNU General Public License version 2 as
7
+ *  published by the Free Software Foundation.
8
+ *
9
+ *  This program is distributed in the hope that it will be useful,
10
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
+ *  GNU General Public License for more details.
13
+ *
14
+ *  You should have received a copy of the GNU General Public License
15
+ *  along with this program; if not, write to the Free Software
16
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17
+ *  MA 02110-1301, USA.
18
+ */
19
+
20
+#if HAVE_CONFIG_H
21
+#include "clamav-config.h"
22
+#endif
23
+
24
+#include <stdlib.h>
25
+#include <string.h>
26
+#include <strings.h>
27
+#include <sys/socket.h>
28
+#include <netinet/in.h>
29
+#include <arpa/inet.h>
30
+#include <sys/types.h>
31
+#include <sys/socket.h>
32
+#include <sys/un.h>
33
+#include <netdb.h>
34
+#include <unistd.h>
35
+#include <time.h>
36
+
37
+#include "shared/cfgparser.h"
38
+#include "shared/output.h"
39
+
40
+#include "connpool.h"
41
+#include "netcode.h"
42
+
43
+#ifdef HAVE_GETADDRINFO
44
+#define SETGAI(k, v) {(k)->gai = (void *)(v);} while(0)
45
+#define FREESRV(k) { if((k).gai) freeaddrinfo((k).gai); else if((k).server) free((k).server); } while(0)
46
+#else
47
+#include <netdb.h>
48
+#define SETGAI
49
+#define FREESRV(k) { if ((k).server) free((k).server); } while(0)
50
+#endif
51
+
52
+struct CPOOL *cp = NULL;
53
+
54
+static int cpool_addunix(char *path) {
55
+    struct sockaddr_un *srv;
56
+    struct CP_ENTRY *cpe = &cp->pool[cp->entries-1];
57
+
58
+    if(strlen(path)<2 || *path!='/') {
59
+	logg("!Unix clamd socket must be an absolute path\n");
60
+	return 1;
61
+    }
62
+    if(!(srv = (struct sockaddr_un *)malloc(sizeof(*srv)))) {
63
+	logg("!Out of memory allocating unix socket space\n");
64
+	return 1;
65
+    }
66
+
67
+    srv->sun_family = AF_UNIX;
68
+    strncpy(srv->sun_path, path, sizeof(srv->sun_path));
69
+    srv->sun_path[sizeof(srv->sun_path)-1]='\0';
70
+    cpe->type = 0;
71
+    cpe->dead = 1;
72
+    cpe->local = 1;
73
+    cpe->last_poll = 0;
74
+    cpe->server = (struct sockaddr *)srv;
75
+    cpe->socklen = sizeof(*srv);
76
+    SETGAI(cpe, NULL);
77
+    if(!cp->local_cpe) cp->local_cpe = cpe;
78
+    logg("*Local socket unix:%s added to the pool (slot %d)\n", srv->sun_path, cp->entries);
79
+    return 0;
80
+}
81
+
82
+
83
+static int islocal(struct sockaddr *sa, socklen_t addrlen) {
84
+    int s = socket(sa->sa_family, SOCK_STREAM, 0);
85
+    int ret;
86
+    if (!s) return 0;
87
+    ret = (bind(s, sa, addrlen) == 0);
88
+    close(s);
89
+    return ret;
90
+}
91
+
92
+
93
+#ifdef HAVE_GETADDRINFO
94
+static int cpool_addtcp(char *addr, char *port) {
95
+    struct addrinfo hints, *res, *res2;;
96
+    struct CP_ENTRY *cpe = (struct CP_ENTRY *)&cp->pool[cp->entries-1];
97
+
98
+    memset(&hints, 0, sizeof(hints));
99
+#ifdef SUPPORT_IPv6
100
+    hints.ai_family = AF_UNSPEC;
101
+#else
102
+    hints.ai_family = AF_INET;
103
+#endif
104
+    hints.ai_socktype = SOCK_STREAM;
105
+
106
+    if(getaddrinfo(addr, port ? port : "3310", &hints, &res)) {
107
+	logg("^Can't resolve hostname %s\n", addr ? addr : "");
108
+	return 1;
109
+    }
110
+    cpe->type = 1;
111
+    cpe->dead = 1;
112
+
113
+    memset(&hints, 0, sizeof(hints));
114
+    hints.ai_flags = AI_PASSIVE;
115
+    hints.ai_socktype = SOCK_STREAM;
116
+#ifdef SUPPORT_IPv6
117
+    hints.ai_family = AF_UNSPEC;
118
+#else
119
+    hints.ai_family = AF_INET;
120
+#endif
121
+    if(!getaddrinfo(addr, NULL, &hints, &res2)) {
122
+	cpe->local = islocal(res2->ai_addr, res2->ai_addrlen);
123
+	freeaddrinfo(res2);
124
+    } else cpe->local = 0;
125
+    cpe->last_poll = 0;
126
+    cpe->server = res->ai_addr;
127
+    cpe->socklen = res->ai_addrlen;
128
+    SETGAI(cpe, res);
129
+    logg("*%s socket tcp:%s:%s added to the pool (slot %d)\n", cpe->local ? "Local" : "Remote", addr ? addr : "localhost", port ? port : "3310", cp->entries);
130
+    return 0;
131
+}
132
+#else
133
+static int cpool_addtcp(char *addr, char *port) {
134
+    struct sockaddr_in *srv;
135
+    struct CP_ENTRY *cpe = (struct CP_ENTRY *)&cp->pool[cp->entries-1];
136
+    int nport = 3310;
137
+
138
+    if(port) {
139
+	nport = atoi(port);
140
+	if (nport<=0 || nport>65535) {
141
+	    logg("!Bad port for clamd socket (%d)\n", nport);
142
+	    return 1;
143
+	}
144
+    }
145
+    if(!(srv = malloc(sizeof(*srv)))) {
146
+	logg("!Out of memory allocating unix socket space\n");
147
+	return 1;
148
+    }
149
+
150
+    srv->sin_family = AF_INET;
151
+
152
+    if (addr) {
153
+	struct hostent *h;
154
+	if(!(h=gethostbyname(addr))) {
155
+	    logg("^Can't resolve tcp socket hostname %s\n", addr);
156
+	    free(srv);
157
+	    return 1;
158
+	}
159
+	memcpy(&srv->sin_addr.s_addr, h->h_addr_list[0], 4);
160
+    } else {
161
+	srv->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
162
+    }
163
+    cpe->type = 1;
164
+    cpe->dead = 1;
165
+    srv->sin_port = htons(INADDR_ANY);
166
+    cpe->local = islocal(srv, sizeof(srv));
167
+    srv->sin_port = htons(nport);
168
+    cpe->last_poll = 0;
169
+    cpe->server = (struct sockaddr *)srv;
170
+    cpe->socklen = sizeof(*srv);
171
+    logg("*%s socket tcp:%s:%u added to the pool (slot %d)\n", cpe->local ? "Local" : "Remote", addr ? addr : "localhost", nport, cp->entries);
172
+    return 0;
173
+}    
174
+#endif
175
+
176
+
177
+int addslot(void) {
178
+    struct CP_ENTRY *cpe;
179
+
180
+    if(!(cpe = realloc(cp->pool, (cp->entries + 1) * sizeof(struct CP_ENTRY)))) {
181
+	logg("!Out of memory while initializing the connection pool\n");
182
+	cpool_free();
183
+	return 1;
184
+    }
185
+    if(cp->local_cpe)
186
+	cp->local_cpe = (struct CP_ENTRY *)((char *)cp->local_cpe + ((char *)cpe - (char *)cp->pool));
187
+    memset(&cpe[cp->entries], 0, sizeof(*cpe));
188
+    cp->pool = cpe;
189
+    cp->entries++;
190
+    return 0;
191
+}
192
+    
193
+
194
+void cpool_probe(void) {
195
+    unsigned int i, dead=0;
196
+    struct CP_ENTRY *cpe = cp->pool;
197
+    time_t lastpoll = time(NULL) - 5*60;
198
+
199
+    for(i=1; i<=cp->entries; i++) {
200
+	if(cpe->dead && lastpoll > cpe->last_poll) {
201
+	    nc_ping_entry(cpe);
202
+	    logg("*Probe for slot %u returned: %s\n", i, cpe->dead ? "failed" : "success");
203
+	}
204
+	dead += cpe->dead;
205
+	cpe++;
206
+    }
207
+    cp->alive = cp->entries - dead;
208
+    if(!cp->alive)
209
+	logg("^No clamd server appears to be available, trying again in 5 minutes.\n");
210
+}
211
+
212
+
213
+void cpool_init(struct cfgstruct *copt) {
214
+    const struct cfgstruct *cpt;
215
+    int failed = 0;
216
+
217
+    if(!(cp=calloc(sizeof(*cp), 1))) {
218
+	logg("!Out of memory while initializing the connection pool");
219
+	return;
220
+    }
221
+
222
+    cp->local_cpe = NULL;
223
+
224
+    if((cpt = cfgopt(copt, "ClamdSocket"))->enabled) {
225
+	while(cpt) {
226
+	    char *socktype = cpt->strarg;
227
+
228
+	    if(addslot()) return;
229
+	    if(!strncasecmp(socktype, "unix:", 5)) {
230
+		failed = cpool_addunix(socktype+5);
231
+	    } else if(!strncasecmp(socktype, "tcp:", 4)) {
232
+		char *port = strrchr(socktype+4, ':');
233
+		if(port) {
234
+		    *port='\0';
235
+		    port++;
236
+		}
237
+		failed = cpool_addtcp(socktype+4, port);
238
+	    } else {
239
+		logg("!Failed to parse ClamdSocket directive '%s'\n", socktype);
240
+		failed = 1;
241
+	    }
242
+	    if(failed) break;
243
+	    cpt = (struct cfgstruct *) cpt->nextarg;
244
+	}
245
+	if(failed) {
246
+	    cpool_free();
247
+	    return;
248
+	}
249
+    }
250
+
251
+#ifdef MILTER_LEGACY
252
+    if((cpt = cfgopt(copt, "LocalSocket"))->enabled) {
253
+	if(addslot()) return;
254
+	if(cpool_addunix(cpt->strarg)) {
255
+	    cpool_free();
256
+	    return;
257
+	}
258
+    }
259
+
260
+    if((cpt = cfgopt(copt, "TCPSocket"))->enabled) {
261
+	char *addr = NULL;
262
+	char port[5];
263
+
264
+	if(addslot()) return;
265
+	snprintf(port, 5, "%d", cpt->numarg);
266
+	port[5] = 0;
267
+	if((cpt = cfgopt(copt, "TCPAddr"))->enabled)
268
+	    addr = cpt->strarg;
269
+	if(cpool_addtcp(addr, port)) {
270
+	    cpool_free();
271
+	    return;
272
+	}
273
+    }
274
+#endif
275
+
276
+    if(!cp->entries) {
277
+	logg("!No ClamdSocket specified\n");
278
+	cpool_free();
279
+	return;
280
+    }
281
+    cpool_probe();
282
+    srand(time(NULL)); /* FIXME: naive ? */
283
+}
284
+
285
+
286
+void cpool_free(void) {
287
+    unsigned int i;
288
+    for(i=0; i<cp->entries; i++)
289
+	FREESRV(cp->pool[i]);
290
+    free(cp->pool);
291
+    free(cp);
292
+}
293
+
294
+
295
+struct CP_ENTRY *cpool_get_rand(void) {
296
+    unsigned int start, i;
297
+    struct CP_ENTRY *cpe;
298
+
299
+    if(!cp->alive) {
300
+	logg("!No sockets are alive. Probe forced...\n");
301
+	/* FIXME: yeah, actually do force smthng here */
302
+	return NULL;
303
+    }
304
+    start = rand() % cp->entries;
305
+    for(i=0; i<cp->entries; i++) {
306
+	cpe = &cp->pool[(i+start) % cp->entries];
307
+	if(cpe->dead) continue;
308
+	if(cpe->local && cp->local_cpe && !cp->local_cpe->dead)
309
+	    return cp->local_cpe;
310
+	return cpe;
311
+    }
312
+    return NULL;
313
+}
314
+
315
+
316
+/*
317
+ * Local Variables:
318
+ * mode: c
319
+ * c-basic-offset: 4
320
+ * tab-width: 8
321
+ * End: 
322
+ * vim: set cindent smartindent autoindent softtabstop=4 shiftwidth=4 tabstop=8: 
323
+ */
0 324
new file mode 100644
... ...
@@ -0,0 +1,48 @@
0
+#ifndef _CONNPOOL_H
1
+#define _CONNPOOL_H
2
+
3
+#if HAVE_CONFIG_H
4
+#include "clamav-config.h"
5
+#endif
6
+
7
+#include <sys/socket.h>
8
+#include <netinet/in.h>
9
+#include <netinet/ip.h>
10
+
11
+#include "shared/cfgparser.h"
12
+
13
+struct CP_ENTRY {
14
+    uint8_t type;
15
+    uint8_t dead;
16
+    uint8_t local;
17
+    time_t last_poll;
18
+    struct sockaddr *server;
19
+    socklen_t socklen;
20
+#ifdef HAVE_GETADDRINFO
21
+    void *gai;
22
+#endif
23
+};
24
+
25
+struct CPOOL {
26
+    unsigned int entries;
27
+    unsigned int alive;
28
+    struct CP_ENTRY *local_cpe;
29
+    struct CP_ENTRY *pool;
30
+};
31
+
32
+void cpool_init(struct cfgstruct *copt);
33
+void cpool_free(void);
34
+struct CP_ENTRY *cpool_get_rand(void);
35
+
36
+extern struct CPOOL *cp;
37
+
38
+#endif
39
+
40
+/*
41
+ * Local Variables:
42
+ * mode: c
43
+ * c-basic-offset: 4
44
+ * tab-width: 8
45
+ * End: 
46
+ * vim: set cindent smartindent autoindent softtabstop=4 shiftwidth=4 tabstop=8: 
47
+ */
0 48
new file mode 100644
... ...
@@ -0,0 +1,274 @@
0
+/*
1
+ *  Copyright (C)2008 Sourcefire, Inc.
2
+ *
3
+ *  Author: aCaB <acab@clamav.net>
4
+ *
5
+ *  This program is free software; you can redistribute it and/or modify
6
+ *  it under the terms of the GNU General Public License version 2 as
7
+ *  published by the Free Software Foundation.
8
+ *
9
+ *  This program is distributed in the hope that it will be useful,
10
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
+ *  GNU General Public License for more details.
13
+ *
14
+ *  You should have received a copy of the GNU General Public License
15
+ *  along with this program; if not, write to the Free Software
16
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17
+ *  MA 02110-1301, USA.
18
+ */
19
+
20
+#if HAVE_CONFIG_H
21
+#include "clamav-config.h"
22
+#endif
23
+
24
+#include <stdio.h>
25
+#include <string.h>
26
+#include <stdlib.h>
27
+#include <sys/types.h>
28
+#include <sys/socket.h>
29
+#include <unistd.h>
30
+#include <fcntl.h>
31
+#include <sys/time.h>
32
+#include <sys/select.h>
33
+#include <time.h>
34
+#include <errno.h>
35
+
36
+#include "shared/output.h"
37
+#include "netcode.h"
38
+
39
+
40
+/* FIXME: for connect and send */
41
+#define TIMEOUT 60
42
+/* for recv */
43
+long readtimeout;
44
+
45
+
46
+int nc_socket(struct CP_ENTRY *cpe) {
47
+    int flags, s = socket(cpe->server->sa_family, SOCK_STREAM, 0);
48
+
49
+    if (s == -1) return -1;
50
+    flags = fcntl(s, F_GETFL, 0);
51
+    if (flags == -1) {
52
+	close(s);
53
+	return -1;
54
+    }
55
+    flags |= O_NONBLOCK;
56
+    if (fcntl(s, F_SETFL, flags) == -1) {
57
+	close(s);
58
+	return -1;
59
+    }
60
+    return s;
61
+}
62
+
63
+
64
+int nc_connect(int s, struct CP_ENTRY *cpe) {
65
+    time_t timeout = time(NULL) + TIMEOUT;
66
+    int res = connect(s, cpe->server, cpe->socklen);
67
+    struct timeval tv;
68
+
69
+    if (!res) return 0;
70
+    if (errno != EINPROGRESS) {
71
+	close(s);
72
+	return -1;
73
+    }
74
+
75
+    tv.tv_sec = TIMEOUT;
76
+    tv.tv_usec = 0;
77
+    while(1) {
78
+	fd_set fds;
79
+	int s_err;
80
+	socklen_t s_len = sizeof(s_err);
81
+
82
+	FD_ZERO(&fds);
83
+	FD_SET(s, &fds);
84
+	res = select(s+1, NULL, &fds, NULL, &tv);
85
+	if(res < 1) {
86
+	    time_t now;
87
+	    if (res == -1 && errno == EINTR && ((now = time(NULL)) < timeout)) {
88
+		tv.tv_sec = timeout - now;
89
+		tv.tv_usec = 0;
90
+		continue;
91
+	    }
92
+	    close(s);
93
+	    return -1;
94
+	}
95
+	if (getsockopt(s, SOL_SOCKET, SO_ERROR, &s_err, &s_len) || s_err) {
96
+	    logg("!getsockopt failed: %s\n", strerror(s_err));
97
+	    close(s);
98
+	    return -1;
99
+	}
100
+	return 0;
101
+    }
102
+}
103
+
104
+
105
+int nc_send(int s, const void *buf, size_t len) {
106
+    while(len) {
107
+	int res = send(s, buf, len, 0);
108
+	time_t timeout = time(NULL) + TIMEOUT;
109
+	struct timeval tv;
110
+
111
+	if(res!=-1) {
112
+	    len-=res;
113
+	    buf+=res;
114
+	    timeout = time(NULL) + TIMEOUT;
115
+	    continue;
116
+	}
117
+	if(errno != EAGAIN && errno != EWOULDBLOCK) {
118
+	    close(s);
119
+	    return 1;
120
+	}
121
+
122
+	tv.tv_sec = TIMEOUT;
123
+	tv.tv_usec = 0;
124
+	while(1) {
125
+	    fd_set fds;
126
+	    int s_err;
127
+	    socklen_t s_len = sizeof(s_err);
128
+
129
+	    FD_ZERO(&fds);
130
+	    FD_SET(s, &fds);
131
+	    res = select(s+1, NULL, &fds, NULL, &tv);
132
+	    if(res < 1) {
133
+		time_t now;
134
+		if (res == -1 && errno == EINTR && ((now = time(NULL)) < timeout)) {
135
+		    tv.tv_sec = timeout - now;
136
+		    tv.tv_usec = 0;
137
+		    continue;
138
+		}
139
+		close(s);
140
+		return 1;
141
+	    }
142
+	    if (getsockopt(s, SOL_SOCKET, SO_ERROR, &s_err, &s_len)) {
143
+		close(s);
144
+		return 1;
145
+	    }
146
+	    len-=s_len;
147
+	    buf+=s_len;
148
+	    break;
149
+	}
150
+    }
151
+    return 0;
152
+}
153
+
154
+
155
+char *nc_recv(int s) {
156
+    char buf[BUFSIZ], *ret=NULL;
157
+    time_t timeout = time(NULL) + readtimeout;
158
+    struct timeval tv;
159
+    fd_set fds;
160
+    int res;
161
+
162
+    tv.tv_sec = readtimeout;
163
+    tv.tv_usec = 0;
164
+
165
+    FD_ZERO(&fds);
166
+    FD_SET(s, &fds);
167
+    while(1) {
168
+	res = select(s+1, &fds, NULL, NULL, &tv);
169
+	if(res<1) {
170
+	    time_t now;
171
+	    if (res == -1 && errno == EINTR && ((now = time(NULL)) < timeout)) {
172
+		tv.tv_sec = timeout - now;
173
+		tv.tv_usec = 0;
174
+	    continue;
175
+	    }
176
+	    close(s);
177
+	    return NULL;
178
+	}
179
+	break;
180
+    }
181
+    /* FIXME: check for EOL@EObuf ? */
182
+    res = recv(s, buf, sizeof(buf), 0);
183
+    if (res==-1 || !(ret = (char *)malloc(res+1))) {
184
+	close(s);
185
+	return NULL;
186
+    }
187
+    memcpy(ret, buf, res);
188
+    ret[res]='\0';
189
+    return ret;
190
+}
191
+
192
+
193
+int nc_connect_entry(struct CP_ENTRY *cpe) {
194
+    int s = nc_socket(cpe);
195
+    if(s==-1) return -1;
196
+    return nc_connect(s, cpe) ? -1 : s;
197
+}
198
+
199
+
200
+void nc_ping_entry(struct CP_ENTRY *cpe) {
201
+    int s = nc_connect_entry(cpe);
202
+    char *reply;
203
+    if (s!=-1 && !nc_send(s, "nPING\n", 6) && (reply = nc_recv(s))) {
204
+	cpe->dead = strcmp(reply, "PONG\n")!=0;
205
+	free(reply);
206
+	close(s);
207
+    } else cpe->dead = 1;
208
+}
209
+
210
+
211
+int nc_connect_rand(int *main, int *alt, int *local) {
212
+    struct CP_ENTRY *cpe = cpool_get_rand();
213
+
214
+    if(!cpe) return 1;
215
+    *local = cpe->local;
216
+    if ((*main = nc_connect_entry(cpe)) == -1) return 1; /* FIXME : this should be delayed till eom if local */
217
+    if(*local) {
218
+	char tmpn[] = "/tmp/clamav-milter-XXXXXX"; 
219
+	if((*alt = mkstemp(tmpn))==-1) { /* FIXME */
220
+	    logg("!Failed to create temporary file\n");
221
+	    close(*main);
222
+	    return 1;
223
+	}
224
+	unlink(tmpn);
225
+    } else {
226
+	char *reply=NULL, *port;
227
+	int nport;
228
+	struct CP_ENTRY new_cpe;
229
+	union {
230
+	    struct sockaddr_in sa4;
231
+	    struct sockaddr_in6 sa6;
232
+	} sa;
233
+
234
+	if(nc_send(*main, "nSTREAM\n", 8) || !(reply = nc_recv(*main)) || !(port = strstr(reply, "PORT"))) {
235
+	    logg("!Failed to communicate with clamd\n");
236
+	    if(reply) free(reply);
237
+	    close(*main);
238
+	    return 1;
239
+	}
240
+	port+=5;
241
+	sscanf(port, "%d", &nport);
242
+	free(reply);
243
+	if(cpe->server->sa_family == AF_INET && cpe->socklen == sizeof(struct sockaddr_in)) {
244
+	    memcpy(&sa, cpe->server, sizeof(struct sockaddr_in));
245
+	    sa.sa4.sin_port = htons(nport);
246
+	    new_cpe.socklen = sizeof(struct sockaddr_in);
247
+	} else if(cpe->server->sa_family == AF_INET6 && cpe->socklen == sizeof(struct sockaddr_in6)) {
248
+	    memcpy(&sa, cpe->server, sizeof(struct sockaddr_in6));
249
+	    sa.sa6.sin6_port = htons(nport);
250
+	    new_cpe.socklen = sizeof(struct sockaddr_in6);
251
+	} else {
252
+	    logg("!WTF WHY AM I DOING HERE???");
253
+	    close(*main);
254
+	    return 1;
255
+	}
256
+	new_cpe.server = (struct sockaddr *)&sa;
257
+	if ((*alt = nc_connect_entry(&new_cpe)) == -1) {
258
+	    logg("!Failed to communicate with clamd for streaming\n");
259
+	    close(*main);
260
+	    return 1;
261
+	}
262
+    }
263
+    return 0;
264
+}
265
+
266
+/*
267
+ * Local Variables:
268
+ * mode: c
269
+ * c-basic-offset: 4
270
+ * tab-width: 8
271
+ * End: 
272
+ * vim: set cindent smartindent autoindent softtabstop=4 shiftwidth=4 tabstop=8: 
273
+ */
0 274
new file mode 100644
... ...
@@ -0,0 +1,13 @@
0
+#ifndef _NETCODE_H
1
+#define _NETCODE_H
2
+
3
+#include "connpool.h"
4
+
5
+void nc_ping_entry(struct CP_ENTRY *cpe);
6
+int nc_connect_rand(int *main, int *alt, int *local);
7
+int nc_send(int s, const void *buf, size_t len);
8
+char *nc_recv(int s);
9
+
10
+extern long readtimeout;
11
+
12
+#endif
0 13
new file mode 100644
... ...
@@ -0,0 +1,166 @@
0
+##
1
+## Example config file for the clamav-milter
2
+## /* FIXME : NOT DONE YET */ Please read the clamav-milter.conf(5) manual before editing this file.
3
+##
4
+
5
+# Comment or remove the line below.
6
+Example
7
+
8
+
9
+##
10
+## Main options
11
+##
12
+
13
+# Define the interface through which we communicate with sendmail
14
+# This option is mandatory! Possible formats are:
15
+# [[unix|local]:]/path/to/file - to specify a unix domain socket
16
+# inet:port@[hostname|ip-address] - to specify an ipv4 socket
17
+# inet6:port@[hostname|ip-address] - to specify an ipv6 socket
18
+#Default: no default
19
+##MilterSocket /tmp/clamav-milter.socket
20
+##MilterSocket inet:7357
21
+
22
+# Remove stale socket after unclean shutdown.
23
+# Default: yes
24
+#FixStaleSocket yes
25
+
26
+# Maximum number of threads running at the same time.
27
+# Default: 10
28
+##MaxThreads 20
29
+
30
+# Run as another user (clamav-milter must be started by root for this option to work)
31
+# Default: don't drop privileges
32
+##User clamav
33
+
34
+# Initialize supplementary group access (clamd must be started by root).
35
+# Default: no
36
+##AllowSupplementaryGroups no
37
+
38
+# Waiting for data from clamd will timeout after this time (seconds).
39
+# Value of 0 disables the timeout.
40
+# Default: 120
41
+##ReadTimeout 300
42
+
43
+# Don't fork into background.
44
+# Default: no
45
+##Foreground yes
46
+
47
+
48
+##
49
+## Clamd options
50
+##
51
+
52
+# Define the clamd socket to connect to for scanning.
53
+# If not set (the default), clamav-milter uses internal mode.
54
+# This option is mandatory! Syntax:
55
+# ClamdSocket unix:path
56
+# ClamdSocket tcp:host:port
57
+# The first syntax specifies a local unix socket (needs an bsolute path) e.g.:
58
+#     ClamdSocket unix:/var/run/clamd/clamd.socket
59
+# The second syntax specifies a tcp local or remote tcp socket: the
60
+# host can be a hostname or an ip address; the ":port" field is only required
61
+# for IPv6 addresses, otherwise it defaults to 3310
62
+#     ClamdSocket tcp:192.168.0.1
63
+#
64
+# This option can be repeated several times with different sockets or even
65
+# with the same socket: clamd servers will be selected in a round-robin fashion.
66
+#
67
+# Default: no default
68
+##ClamdSocket tcp:scanner.mydomain:7357
69
+
70
+# WARNING: The following options are deprecated and may go away soon.
71
+# Please use ClamdSocket instead!
72
+# Default: disabled
73
+#LocalSocket
74
+#TCPSocket
75
+#TCPAddr
76
+
77
+
78
+##
79
+## Logging options
80
+##
81
+
82
+# Uncomment this option to enable logging.
83
+# LogFile must be writable for the user running daemon.
84
+# A full path is required.
85
+# Default: disabled
86
+##LogFile /tmp/clamav-milter.log
87
+
88
+# By default the log file is locked for writing - the lock protects against
89
+# running clamav-milter multiple times.
90
+# This option disables log file locking.
91
+# Default: no
92
+##LogFileUnlock yes
93
+
94
+# Maximum size of the log file.
95
+# Value of 0 disables the limit.
96
+# You may use 'M' or 'm' for megabytes (1M = 1m = 1048576 bytes)
97
+# and 'K' or 'k' for kilobytes (1K = 1k = 1024 bytes). To specify the size
98
+# in bytes just don't use modifiers.
99
+# Default: 1M
100
+##LogFileMaxSize 2M
101
+
102
+# Log time with each message.
103
+# Default: no
104
+##LogTime yes
105
+
106
+# Also log clean files. Useful in debugging but drastically increases the
107
+# log size.
108
+# Default: no
109
+##LogClean yes
110
+
111
+# Use system logger (can work together with LogFile).
112
+# Default: no
113
+##LogSyslog yes
114
+
115
+# Specify the type of syslog messages - please refer to 'man syslog'
116
+# for facility names.
117
+# Default: LOG_LOCAL6
118
+##LogFacility LOG_MAIL
119
+
120
+# Enable verbose logging.
121
+# Default: no
122
+##LogVerbose yes
123
+
124
+
125
+##
126
+## Limits
127
+##
128
+
129
+# Files larger than this value won't be scanned.
130
+# Default: 25M
131
+##MaxFileSize 150M
132
+
133
+# WARNING: The following two options are deprecated and may go away soon.
134
+# Please use MaxFile size instead!
135
+# For compatibility reasons the minimum value among MaxFileSize,
136
+# MaxScanSize and StreamMaxLength will be used.
137
+#MaxScanSize
138
+#StreamMaxLength
139
+
140
+
141
+##
142
+## Deprecated options
143
+##
144
+
145
+# The following deprecated options are only kept for compatibility
146
+# reaosns and may go away soon. These do not affect clamav-milter
147
+# in any way, except for a small warning emitted on startup.
148
+
149
+#ArchiveBlockEncrypted
150
+#DatabaseDirectory
151
+#Debug
152
+#DetectBrokenExecutables
153
+#LeaveTemporaryFiles
154
+#MailFollowURLs
155
+#MaxRecursion
156
+#MaxFiles
157
+#PhishingSignatures
158
+#PidFile
159
+#ScanArchive
160
+#ScanHTML
161
+#ScanMail
162
+#ScanOLE2
163
+#ScanPE
164
+#TemporaryDirectory
165
+
... ...
@@ -135,7 +135,7 @@ struct cfgoption cfg_options[] = {
135 135
 
136 136
     /* Deprecated options */
137 137
     {"MailMaxRecursion", OPT_NUM, 64, NULL, 0, OPT_CLAMD | OPT_DEPRECATED},
138
-    {"ArchiveMaxFileSize", OPT_COMPSIZE, 10485760, NULL, 0, OPT_CLAMD | OPT_DEPRECATED},
138
+    {"ArchiveMaxScanSize", OPT_COMPSIZE, 10485760, NULL, 0, OPT_CLAMD | OPT_DEPRECATED},
139 139
     {"ArchiveMaxRecursion", OPT_NUM, 8, NULL, 0, OPT_CLAMD | OPT_DEPRECATED},
140 140
     {"ArchiveMaxFiles", OPT_NUM, 1000, NULL, 0, OPT_CLAMD | OPT_DEPRECATED},
141 141
     {"ArchiveMaxCompressionRatio", OPT_NUM, 250, NULL, 0, OPT_CLAMD | OPT_DEPRECATED},
... ...
@@ -165,7 +165,7 @@ struct cfgoption cfg_options[] = {
165 165
     {"ScanMail", OPT_BOOL, 1, NULL, 0, OPT_MILTER | OPT_DEPRECATED},
166 166
     {"ScanOLE2", OPT_BOOL, 1, NULL, 0, OPT_MILTER | OPT_DEPRECATED},
167 167
     {"ScanPE", OPT_BOOL, 1, NULL, 0, OPT_MILTER | OPT_DEPRECATED},
168
-    {"StreamMaxLength", OPT_COMPSIZE, 10485760, NULL, 0, OPT_MILTER | OPT_DEPRECATED},
168
+    {"StreamMaxLength", OPT_COMPSIZE, 10485760, NULL, OPT_MILTER | OPT_DEPRECATED},
169 169
     {"TCPAddr", OPT_QUOTESTR, -1, NULL, 0, OPT_MILTER | OPT_DEPRECATED},
170 170
     {"TCPSocket", OPT_NUM, -1, NULL, 0, OPT_MILTER | OPT_DEPRECATED},
171 171
     {"TemporaryDirectory", OPT_QUOTESTR, -1, NULL, 0, OPT_MILTER | OPT_DEPRECATED},