b151ef55 |
/*
* clamav-milter.c
* .../clamav-milter/clamav-milter.c
*
* Copyright (C) 2003 Nigel Horne <njh@bandsman.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* |
f02c3c82 |
* Install into /usr/local/sbin/clamav-milter |
b151ef55 |
*
* See http://www.nmt.edu/~wcolburn/sendmail-8.12.5/libmilter/docs/sample.html
* |
71f25024 |
* For installation instructions see the file INSTALL that came with this file |
cbd7686b |
* |
b5d15e64 |
* Change History:
* $Log: clamav-milter.c,v $ |
63e6694b |
* Revision 1.141 2004/10/09 22:10:08 nigelhorne
* BINDTODEVICE fix was broken
* |
2285693f |
* Revision 1.140 2004/10/07 15:36:43 nigelhorne
* Remove scanmail requirement
* |
8064efd5 |
* Revision 1.139 2004/10/04 12:37:11 nigelhorne
* Fix quarantine files being saved twice
* |
da7f01e3 |
* Revision 1.138 2004/10/04 10:53:58 nigelhorne
* Better SMTP message when virus is found
* |
1941819d |
* Revision 1.137 2004/10/02 17:54:06 nigelhorne
* Fix crash if %h is used in a template and --headers is not set
* |
b61a0781 |
* Revision 1.136 2004/09/30 19:18:30 nigelhorne
* Allow --from with no e-mail address
* |
3eacc10d |
* Revision 1.135 2004/09/28 14:44:35 nigelhorne
* Handle operating systems that don't support SO_BINDTODEVICE
* |
e5bb3b37 |
* Revision 1.134 2004/09/27 17:08:31 nigelhorne
* Add iface option to --broadcast
* |
78a78fcc |
* Revision 1.133 2004/09/27 12:43:23 nigelhorne
* Added --broadcast
* |
d7e0b4e6 |
* Revision 1.132 2004/09/25 15:47:19 nigelhorne
* Honour LogFacility
* |
a37e51d5 |
* Revision 1.131 2004/09/20 12:46:05 nigelhorne
* Up issued
* |
c857005e |
* Revision 1.130 2004/09/20 09:09:23 nigelhorne
* Update clamav.conf references
* |
74e42636 |
* Revision 1.129 2004/09/17 15:40:02 nigelhorne
* Handle sendmail variables after clamav variables
* |
58ae48c5 |
* Revision 1.128 2004/09/15 08:46:33 nigelhorne
* Added --max-children to --help
* |
7e8d6a11 |
* Revision 1.127 2004/09/14 07:57:29 nigelhorne
* Session code no longer needs all servers to be up on startup
* |
41190088 |
* Revision 1.126 2004/09/13 21:46:30 nigelhorne
* Session code tidy
* |
99c9dc2a |
* Revision 1.125 2004/09/13 17:39:31 nigelhorne
* User pthread_cond_broadcast rather than pthread_cond_signal
* |
85563fe5 |
* Revision 1.124 2004/09/13 13:14:34 nigelhorne
* Updated SESSION Code
* |
f5d23f2e |
* Revision 1.123 2004/09/12 14:23:47 nigelhorne
* Added SESSION Code
* |
4e136199 |
* Revision 1.122 2004/09/08 16:03:36 nigelhorne
* i18n
* |
95ec27d0 |
* Revision 1.121 2004/08/26 10:22:00 nigelhorne
* Fix overflow To:
* |
0ac43d02 |
* Revision 1.120 2004/08/25 11:44:56 nigelhorne
* Tidy
* |
8eb408e5 |
* Revision 1.119 2004/08/13 10:21:38 nigelhorne
* Single thread through tcp_wrappers
* |
31d36bd3 |
* Revision 1.118 2004/08/12 12:18:45 nigelhorne
* Fixed from
* |
503b2956 |
* Revision 1.117 2004/08/11 10:34:07 nigelhorne
* Better isLocal handler
* |
7d7092de |
* Revision 1.116 2004/08/07 13:10:33 nigelhorne
* Better load balancing
* |
79fa8528 |
* Revision 1.115 2004/08/06 10:08:31 nigelhorne
* Quarantined files now include the virus in the name
* |
6d113e37 |
* Revision 1.114 2004/08/05 07:44:28 nigelhorne
* Better Template Handling
* |
de23431a |
* Revision 1.113 2004/07/30 14:34:56 nigelhorne
* Handle changed clamd message
* |
ab5f138c |
* Revision 1.112 2004/07/29 15:24:47 nigelhorne
* Don't say waiting for some to exit if dont_wait is set
* |
71c63c46 |
* Revision 1.111 2004/07/29 06:38:05 nigelhorne
* GETHOSTBYNAME_R_6
* |
0b79c1b7 |
* Revision 1.110 2004/07/26 13:23:27 nigelhorne
* Remove stream: from template %v
* |
fce82877 |
* Revision 1.109 2004/07/25 11:51:42 nigelhorne
* Fix crash if 1st host dies
* |
0b29053e |
* Revision 1.108 2004/07/22 15:45:03 nigelhorne
* Up-issue
* |
e800fc78 |
* Revision 1.107 2004/07/22 09:14:32 nigelhorne
* Use gethostbyname_r when available
* |
88ad6ea4 |
* Revision 1.106 2004/07/21 21:23:29 nigelhorne
* Mutex gethostbyname result
* |
18f9be43 |
* Revision 1.105 2004/07/21 17:46:06 nigelhorne
* Added note about needing MILTER support in sendmail
* |
ced83bee |
* Revision 1.104 2004/07/14 10:17:05 nigelhorne
* Added dont-wait and advisory options
* |
384e5f09 |
* Revision 1.103 2004/07/08 22:22:39 nigelhorne
* Don't pass empty arguments to inet_ntop
* |
ad31761c |
* Revision 1.102 2004/06/29 15:26:14 nigelhorne
* Support the --timeout argument
* |
2d4560bb |
* Revision 1.101 2004/06/29 10:04:47 nigelhorne
* Up issued
* |
e6bda975 |
* Revision 1.100 2004/06/29 08:27:02 nigelhorne
* Up issued
* |
d80b0903 |
* Revision 1.99 2004/06/28 08:30:18 nigelhorne
* Don't error when creating the quarantine directory if it already exists
* |
c175cbda |
* Revision 1.98 2004/06/22 04:09:12 nigelhorne
* Avoid unlocking unlocked mutex in clamfi_abort
* |
73ffa07e |
* Revision 1.97 2004/06/16 08:08:47 nigelhorne
* Allow access to sendmail variables in the template file
* |
a0a82124 |
* Revision 1.96 2004/06/14 14:34:15 nigelhorne
* Added support for Windows SFU 3.5
* |
1d8066c0 |
* Revision 1.95 2004/06/14 09:05:49 nigelhorne
* Up-issued
* |
819c7c41 |
* Revision 1.94 2004/06/13 02:11:25 kojm
* improve output
* |
74e6c404 |
* Revision 1.93 2004/06/08 21:44:59 nigelhorne
* Ensure --from takes an argument
* |
fe645903 |
* Revision 1.92 2004/06/03 13:14:08 nigelhorne
* Up-issued
* |
39378e7f |
* Revision 1.91 2004/05/25 16:24:21 nigelhorne
* X-Virus-Scanned wasn't being added in maxstreamlength was exceeded
* |
71f25024 |
* Revision 1.90 2004/05/24 17:05:18 nigelhorne
* Include the hostname of the scanner in the headers
* |
f7925922 |
* Revision 1.89 2004/05/21 09:14:57 nigelhorne
* Handle --from, print error message if write to quarantine fails
* |
b90c8762 |
* Revision 1.88 2004/05/16 08:25:09 nigelhorne
* Up issue
* |
d69535ae |
* Revision 1.87 2004/05/09 17:39:04 nigelhorne
* Waiting threads weren't being woken up
* |
0cb97bcb |
* Revision 1.86 2004/05/06 11:25:20 nigelhorne
* Some work on maxStreamLength
* |
c63633c4 |
* Revision 1.85 2004/04/29 07:35:27 nigelhorne
* Change from realloc to cli_realloc - keep assignment
* |
a121491a |
* Revision 1.84 2004/04/28 14:28:29 nigelhorne
* Various updates
* |
679ff9e7 |
* Revision 1.83 2004/04/25 12:56:35 nigelhorne
* Added --pidfile
* |
c51c112c |
* Revision 1.82 2004/04/23 09:13:30 nigelhorne
* Better quarantine email subject
* |
97727aa0 |
* Revision 1.81 2004/04/22 16:47:04 nigelhorne
* Various changes
* |
62f5a55b |
* Revision 1.80 2004/04/21 15:27:02 nigelhorne
* Various changes
* |
40c9a608 |
* Revision 1.79 2004/04/20 14:15:01 nigelhorne
* Sorted out X- headers and handle hostaddr == NULL
* |
6ad582fe |
* Revision 1.78 2004/04/20 08:13:15 nigelhorne
* Print better message if hostaddr is null
* |
063b6c33 |
* Revision 1.77 2004/04/19 22:11:20 nigelhorne
* Many changes
* |
f26b5918 |
* Revision 1.76 2004/04/19 13:28:41 nigelhorne
* Started coding e-mail template support
* |
1392cf22 |
* Revision 1.75 2004/04/19 13:28:00 nigelhorne
* Started coding e-mail template support
* |
dd68be92 |
* Revision 1.74 2004/04/17 20:39:04 nigelhorne
* Add the virus name into the 550 rejection if sent
* |
0a9dafb1 |
* Revision 1.73 2004/04/15 09:53:25 nigelhorne
* Handle systems without inet_ntop
* |
0a867b46 |
* Revision 1.72 2004/04/09 08:36:53 nigelhorne
* Handle clamd giving up on StreamMaxLength before clamav-milter
* |
5691e168 |
* Revision 1.71 2004/04/08 13:14:07 nigelhorne
* Code tidy up
* |
7cc3891c |
* Revision 1.70 2004/04/06 22:43:43 kojm
* reverse strlcpy/strlcat patch
* |
fdc3273d |
* Revision 1.69 2004/04/06 12:14:52 kojm
* use strlcpy/strlcat
* |
cc511c7a |
* Revision 1.68 2004/04/03 04:47:22 nigelhorne
* Honour StreamMaxLength
* |
9873b3c1 |
* Revision 1.67 2004/04/01 15:34:00 nigelhorne
* ThreadTimeout has been renamed ReadTimeout
* |
c96f7461 |
* Revision 1.66 2004/03/31 20:48:03 nigelhorne
* Config file has changed
* |
6489ba59 |
* Revision 1.65 2004/03/27 21:44:21 nigelhorne
* Attempt to handle clamd quick timeout for slow remote sites
* |
ea815cbc |
* Revision 1.64 2004/03/26 11:10:27 nigelhorne
* Added debug information
* |
5f911bde |
* Revision 1.63 2004/03/20 12:30:00 nigelhorne
* strerror_r is confused on Linux
* |
3f29c0de |
* Revision 1.62 2004/03/17 19:46:49 nigelhorne
* Upissue to 0.70@
* |
51255149 |
* Revision 1.61 2004/03/15 19:54:12 kojm
* 0.70-rc
* |
5c0d3a77 |
* Revision 1.60 2004/03/10 11:31:03 nigelhorne
* Use HAVE_STRERROR_R
* |
cbd7686b |
* Revision 1.59 2004/03/07 15:11:15 nigelhorne
* Added more information to headers flag
* |
8f1f6fb8 |
* Revision 1.58 2004/03/03 09:14:55 nigelhorne
* Change way check for TCPwrappers on TCP/IP
* |
6e945c41 |
* Revision 1.57 2004/02/27 15:27:11 nigelhorne
* call checkClamd on start
* |
ce14cc31 |
* Revision 1.56 2004/02/27 09:23:56 nigelhorne
* Don't use TCP wrappers when UNIX domain sockets are used
* |
e27af651 |
* Revision 1.55 2004/02/22 22:53:50 nigelhorne
* Handle ERROR message from clamd
* |
f02c3c82 |
* Revision 1.54 2004/02/22 17:27:40 nigelhorne
* Updated installation instructions now that privileges are dropped
* |
79f846a3 |
* Revision 1.53 2004/02/21 11:03:23 nigelhorne
* Error if quarantine-dir is publically accessable
* |
062dddc9 |
* Revision 1.52 2004/02/20 17:07:24 nigelhorne
* Added checkClamd
* |
64d9c7a1 |
* Revision 1.51 2004/02/20 09:50:42 nigelhorne
* Removed warnings added by new configuration script
* |
35d98cf9 |
* Revision 1.50 2004/02/19 10:00:26 nigelhorne
* Rework TCPWrappers support
* |
3bc05064 |
* Revision 1.49 2004/02/18 13:30:34 nigelhorne
* Added dont-long-clean argument
* |
f8a7079c |
* Revision 1.48 2004/02/18 10:06:51 nigelhorne
* Fix FreeBSD
* |
64d5612d |
* Revision 1.47 2004/02/16 11:55:24 nigelhorne
* Added clamfi_free which helps with the tidying up
* |
7ac60058 |
* Revision 1.46 2004/02/16 09:39:22 nigelhorne
* Upissued to 0.67
* |
b7f54bf2 |
* Revision 1.45 2004/02/14 17:20:38 nigelhorne
* Add TCPwrappers support
* |
d9b674b2 |
* Revision 1.44 2004/02/09 11:05:33 nigelhorne
* Added Hflag
* |
02e79d0c |
* Revision 1.43 2004/02/07 12:16:20 nigelhorne
* Added config.h
* |
8ad6dd05 |
* Revision 1.42 2004/02/02 13:44:31 nigelhorne
* Include the ID of the message when warnings are sent to the postmaster-only
* |
a7164828 |
* Revision 1.41 2004/01/29 12:52:35 nigelhorne
* Added --noreject flag
* |
ca59d8d0 |
* Revision 1.40 2004/01/28 15:55:59 nigelhorne
* Fixed compilation error with --enable-debug
* |
6a914c6e |
* Revision 1.39 2004/01/26 14:12:42 nigelhorne
* Corrected endian problem (ntohs instead of htons)
* |
244be94f |
* Revision 1.38 2004/01/25 14:23:51 nigelhorne
* Support multiple clamd servers
* |
bbc9d684 |
* Revision 1.37 2004/01/24 18:09:39 nigelhorne
* Allow clamd server name as well as IPaddress in -s option
* |
462b5251 |
* Revision 1.36 2004/01/12 15:30:53 nigelhorne
* FixStaleSocket no longer complains on ENOENT
* |
cdbe607d |
* Revision 1.35 2004/01/10 16:22:14 nigelhorne
* Added OpenBSD instructions and --signature-file
* |
e2bb746e |
* Revision 1.34 2003/12/31 14:46:35 nigelhorne
* Include the sendmail queue ID in the log
* |
3166c010 |
* Revision 1.33 2003/12/27 17:28:56 nigelhorne
* Moved --sign data to private area
* |
e6bffccb |
* Revision 1.32 2003/12/22 14:05:31 nigelhorne
* Added --sign option
* |
5dc96b9f |
* Revision 1.31 2003/12/13 16:43:21 nigelhorne
* Upissue
* |
85c1dbfd |
* Revision 1.30 2003/12/12 13:42:47 nigelhorne
* alls to clamfi_cleanup were missing
* |
d2efb3fa |
* Revision 1.29 2003/12/10 12:00:39 nigelhorne
* Timeout on waiting for data from clamd
* |
b696653a |
* Revision 1.28 2003/12/09 09:22:14 nigelhorne
* Use the location of sendmail discovered by configure
* |
709d1342 |
* Revision 1.27 2003/12/05 19:14:07 nigelhorne
* Set umask; handle unescaped From in mailboxes
* |
65a8d561 |
* Revision 1.26 2003/12/02 06:37:26 nigelhorne
* Use setsid if setpgrp not present
* |
3454ad43 |
* Revision 1.25 2003/11/30 06:12:06 nigelhorne
* Added --quarantine-dir option
* |
4ba69cb6 |
* Revision 1.24 2003/11/29 11:51:19 nigelhorne
* Fix problem of possible confused pointers if large number of recipients given
* |
2a1ff3e4 |
* Revision 1.23 2003/11/25 05:56:43 nigelhorne
* Handle empty hostname or hostaddr
* |
434012b8 |
* Revision 1.22 2003/11/24 04:48:44 nigelhorne
* Support AllowSupplementaryGroups
* |
bb9979e6 |
* Revision 1.21 2003/11/22 11:47:45 nigelhorne |
79f846a3 |
* Drop root priviliges and support quarantine |
bb9979e6 |
* |
0b6bce84 |
* Revision 1.20 2003/11/19 16:32:22 nigelhorne
* Close cmdSocket earlier
* |
c2ac5345 |
* Revision 1.19 2003/11/17 04:48:30 nigelhorne
* Up issue to version 0.65
* |
a6f07599 |
* Revision 1.18 2003/11/11 08:19:20 nigelhorne
* Handle % characters in e-mail addresses
* |
3a805bfd |
* Revision 1.17 2003/11/05 15:41:11 nigelhorne
* Tidyup pthread_cond_timewait call
* |
b0a42ec6 |
* Revision 1.16 2003/10/31 13:33:40 nigelhorne
* Added dont scan on error flag
* |
390a7661 |
* Revision 1.15 2003/10/22 19:44:01 nigelhorne
* more calls to pthread_cond_broadcast
* |
f0d6f5a1 |
* Revision 1.14 2003/10/12 08:37:21 nigelhorne
* Uses VERSION command to get version information
* |
dd0d5a8c |
* Revision 1.13 2003/10/11 15:42:15 nigelhorne
* Don't call clamdscan
* |
3a0b4e5b |
* Revision 1.12 2003/10/05 17:30:04 nigelhorne
* Only fix old socket when FixStaleSocket is set
* |
31268a5c |
* Revision 1.11 2003/10/05 13:57:47 nigelhorne
* Fixed handling of MaxThreads
* |
b14e9e77 |
* Revision 1.10 2003/10/03 11:54:53 nigelhorne
* Added white list of recipients
* |
ecb8e6b4 |
* Revision 1.9 2003/09/30 11:53:55 nigelhorne
* clamfi_envfrom was returning EX_TEMPFAIL in some places rather than SMFIS_TEMPFAIL
* |
c15d1d2c |
* Revision 1.8 2003/09/29 06:20:17 nigelhorne
* max_children now overrides MaxThreads
* |
80a72b0d |
* Revision 1.7 2003/09/29 06:07:49 nigelhorne
* Ensure remoteIP is set before usage
* |
b5d15e64 |
* Revision 1.6 2003/09/28 16:37:23 nigelhorne
* Added -f flag use MaxThreads if --max-children not set |
b151ef55 |
*/ |
63e6694b |
static char const rcsid[] = "$Id: clamav-milter.c,v 1.141 2004/10/09 22:10:08 nigelhorne Exp $"; |
b151ef55 |
|
63e6694b |
#define CM_VERSION "0.80j" |
b151ef55 |
/*#define CONFDIR "/usr/local/etc"*/
|
02e79d0c |
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
|
b151ef55 |
#include "defaults.h" |
c96f7461 |
#include "cfgparser.h" |
b151ef55 |
#include "../target.h" |
244be94f |
#include "str.h" |
64d9c7a1 |
#include "../libclamav/others.h" |
e6bda975 |
#include "../libclamav/strrcpy.h" |
64d9c7a1 |
#include "clamav.h" |
b151ef55 |
#ifndef CL_DEBUG
#define NDEBUG
#endif
#include <stdio.h>
#include <sysexits.h> |
709d1342 |
#include <sys/types.h>
#include <sys/stat.h> |
b151ef55 |
#include <syslog.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <assert.h> |
d7e0b4e6 |
#include <sys/socket.h> |
fce82877 |
#include <netinet/in.h> |
e5bb3b37 |
#include <net/if.h> |
b151ef55 |
#include <arpa/inet.h>
#include <sys/un.h>
#include <stdarg.h>
#include <errno.h>
#include <libmilter/mfapi.h>
#include <pthread.h>
#include <sys/time.h>
#include <signal.h> |
1f025849 |
#include <regex.h>
#include <fcntl.h> |
bb9979e6 |
#include <pwd.h> |
434012b8 |
#include <grp.h> |
bbc9d684 |
#include <netdb.h> |
b151ef55 |
|
4e136199 |
#ifdef C_LINUX
#include <libintl.h> |
d7e0b4e6 |
#include <locale.h> |
4e136199 |
#define gettext_noop(s) s
#define _(s) gettext(s)
#define N_(s) gettext_noop(s)
#else
#define _(s) s
#define N_(s) s
#endif
|
b7f54bf2 |
#ifdef WITH_TCPWRAP
#include <tcpd.h> |
35d98cf9 |
int allow_severity = LOG_DEBUG; |
e27af651 |
int deny_severity = LOG_NOTICE; |
35d98cf9 |
|
b7f54bf2 |
#endif
|
244be94f |
#if defined(CL_DEBUG) && defined(C_LINUX)
#include <sys/resource.h>
#endif
|
b151ef55 |
#define _GNU_SOURCE |
4e136199 |
#include <getopt.h> |
b151ef55 |
|
b696653a |
#ifndef SENDMAIL_BIN
#define SENDMAIL_BIN "/usr/lib/sendmail"
#endif
|
b7f54bf2 |
#ifndef HAVE_IN_PORT_T
typedef unsigned short in_port_t;
#endif
|
78a78fcc |
#ifndef HAVE_IN_ADDR_T
typedef unsigned int in_addr_t;
#endif
|
85563fe5 |
/*
* Do not define SESSION in a production environment - it has been known to put |
d7e0b4e6 |
* clamd into a loop when clamav-milter is restarted and sending STREAM often
* returns EPIPE |
85563fe5 |
*
* It is however OK for testing: code is now in place to reopen as session
* that has gone bad, and it would be useful to find out the set of |
c857005e |
* circumstances that causes clamd to loop |
85563fe5 |
*/ |
f5d23f2e |
/*#define SESSION /*
* Keep one command connection open to clamd, otherwise a new
* command connection is created for each new email
*/
|
b151ef55 |
/*
* TODO: optional: xmessage on console when virus stopped (SNMP would be real nice!) |
434012b8 |
* Having said that, with LogSysLog you can (on Linux) configure the system
* to get messages on the system console, see syslog.conf(5), also you |
c857005e |
* can use wall(1) in the VirusEvent entry in clamd.conf |
b151ef55 |
* TODO: build with libclamav.so rather than libclamav.a |
9873b3c1 |
* TODO: Support LogTime and Logfile from the conf file |
02e79d0c |
* TODO: Warn if TCPAddr doesn't allow connection from us
* TODO: Decide action (bounce, discard, reject etc.) based on the virus
* found. Those with faked addresses, such as SCO.A want discarding,
* others could be bounced properly. |
1392cf22 |
* TODO: Encrypt mails sent to clamd to stop sniffers |
063b6c33 |
* TODO: Test with IPv6 |
b151ef55 |
*/
|
d9b674b2 |
struct header_node_t {
char *header;
struct header_node_t *next;
};
struct header_list_struct {
struct header_node_t *first;
struct header_node_t *last;
};
typedef struct header_list_struct *header_list_t;
|
b151ef55 |
/* |
503b2956 |
* Local addresses are those not scanned if --local is not set
* 127.0.0.0 is not in this table since that's goverend by --outgoing
* Andy Fiddaman <clam@fiddaman.net> added 69.254.0.0/16
* (Microsoft default DHCP)
* |
c857005e |
* TODO: read this table in from a file (clamd.conf?) |
503b2956 |
*/
#define PACKADDR(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) |
3eacc10d |
#define MAKEMASK(bits) ((uint32_t)(0xffffffff << (bits))) |
503b2956 |
static const struct cidr_net {
uint32_t base;
uint32_t mask;
} localNets[] = {
/*{ PACKADDR(127, 0, 0, 0), MAKEMASK(24) }, /* 127.0.0.0/24 */
{ PACKADDR(192, 168, 0, 0), MAKEMASK(16) }, /* 192.168.0.0/16 */
{ PACKADDR( 10, 0, 0, 0), MAKEMASK(24) }, /* 10.0.0.0/24 */
{ PACKADDR(172, 16, 0, 0), MAKEMASK(20) }, /* 172.16.0.0/20 */
{ PACKADDR(169, 254, 0, 0), MAKEMASK(16) }, /* 169.254.0.0/16 */
{ 0, 0 }
};
/* |
b151ef55 |
* Each thread has one of these
*/
struct privdata {
char *from; /* Who sent the message */
char **to; /* Who is the message going to */
int numTo; /* Number of people the message is going to */ |
f5d23f2e |
#ifndef SESSION |
b151ef55 |
int cmdSocket; /*
* Socket to send/get commands e.g. PORT for
* dataSocket
*/ |
f5d23f2e |
#endif |
b151ef55 |
int dataSocket; /* Socket to send data to clamd */ |
3454ad43 |
char *filename; /* Where to store the message in quarantine */ |
3166c010 |
u_char *body; /* body of the message if Sflag is set */
size_t bodyLen; /* number of bytes in body */ |
d9b674b2 |
header_list_t headers; /* Message headers */ |
5691e168 |
long numBytes; /* Number of bytes sent so far */ |
063b6c33 |
char *received; /* keep track of received from */ |
a121491a |
const char *rejectCode; /* 550 or 554? */ |
d69535ae |
char *messageID; /* sendmailID */ |
a121491a |
int discard; /*
* looks like the remote end is playing ping
* pong with us
*/ |
39378e7f |
int serverNumber; /* Index into serverIPs */ |
b151ef55 |
};
|
85563fe5 |
#ifdef SESSION
static int createSession(int session);
#else |
244be94f |
static int pingServer(int serverNumber); |
85563fe5 |
#endif |
244be94f |
static int findServer(void); |
b151ef55 |
static sfsistat clamfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr);
static sfsistat clamfi_envfrom(SMFICTX *ctx, char **argv);
static sfsistat clamfi_envrcpt(SMFICTX *ctx, char **argv);
static sfsistat clamfi_header(SMFICTX *ctx, char *headerf, char *headerv);
static sfsistat clamfi_eoh(SMFICTX *ctx);
static sfsistat clamfi_body(SMFICTX *ctx, u_char *bodyp, size_t len);
static sfsistat clamfi_eom(SMFICTX *ctx);
static sfsistat clamfi_abort(SMFICTX *ctx);
static sfsistat clamfi_close(SMFICTX *ctx);
static void clamfi_cleanup(SMFICTX *ctx); |
64d5612d |
static void clamfi_free(struct privdata *privdata); |
0cb97bcb |
static int clamfi_send(struct privdata *privdata, size_t len, const char *format, ...); |
cdbe607d |
static int clamd_recv(int sock, char *buf, size_t len);
static off_t updateSigFile(void); |
d9b674b2 |
static header_list_t header_list_new(void);
static void header_list_free(header_list_t list);
static void header_list_add(header_list_t list, const char *headerf, const char *headerv);
static void header_list_print(header_list_t list, FILE *fp); |
6489ba59 |
static int connect2clamd(struct privdata *privdata); |
062dddc9 |
static void checkClamd(void); |
0b79c1b7 |
static int sendtemplate(SMFICTX *ctx, const char *filename, FILE *sendmail, const char *virusname); |
79fa8528 |
static int qfile(struct privdata *privdata, const char *virusname); |
ced83bee |
static void setsubject(SMFICTX *ctx, const char *virusname); |
6d113e37 |
static int clamfi_gethostbyname(const char *hostname, struct hostent *hp, char *buf, size_t len); |
503b2956 |
static int isLocalAddr(in_addr_t addr); |
85563fe5 |
static void clamdIsDown(void);
#ifdef SESSION
static void *watchdog(void *a);
#endif |
d7e0b4e6 |
static int logg_facility(const char *name); |
78a78fcc |
static void quit(void);
static void broadcast(const char *mess); |
b151ef55 |
|
f0d6f5a1 |
static char clamav_version[128]; |
b5d15e64 |
static int fflag = 0; /* force a scan, whatever */ |
b151ef55 |
static int oflag = 0; /* scan messages from our machine? */
static int lflag = 0; /* scan messages from our site? */
static int bflag = 0; /*
* send a failure (bounce) message to the
* sender. This probably isn't a good idea
* since most reply addresses will be fake
*/ |
e5bb3b37 |
static const char *iface; /* |
78a78fcc |
* Broadcast a message when a virus is found,
* this allows remote network management
*/ |
e5bb3b37 |
static int broadcastSock = -1; |
1c3f1ce1 |
static int pflag = 0; /*
* Send a warning to the postmaster only,
* this means user's won't be told when someone
* sent them a virus
*/
static int qflag = 0; /*
* Send no warnings when a virus is found,
* this means that the only log of viruses
* found is the syslog, so it's best to |
c857005e |
* enable LogSyslog in clamd.conf |
1c3f1ce1 |
*/ |
e6bffccb |
static int Sflag = 0; /*
* Add a signature to each message that
* has been scanned
*/ |
cdbe607d |
static const char *sigFilename; /*
* File where the scanned message signature
* can be found
*/ |
3454ad43 |
static char *quarantine; /* |
bb9979e6 |
* If a virus is found in an email redirect
* it to this account
*/ |
3454ad43 |
static char *quarantine_dir; /*
* Path to store messages before scanning.
* Infected ones will be left there.
*/ |
3613bd91 |
static int nflag = 0; /*
* Don't add X-Virus-Scanned to header. Patch
* from Dirk Meyer <dirk.meyer@dinoex.sub.org>
*/ |
a7164828 |
static int rejectmail = 1; /*
* Send a 550 rejection when a virus is
* found
*/ |
d9b674b2 |
static int hflag = 0; /*
* Include original message headers in
* report
*/ |
b0a42ec6 |
static int cl_error = SMFIS_TEMPFAIL; /*
* If an error occurs, return
* this status. Allows messages
* to be passed through
* unscanned in the event of
* an error. Patch from
* Joe Talbott <josepht@cstone.net>
*/ |
cc511c7a |
static int readTimeout = CL_DEFAULT_SCANTIMEOUT; /* |
d2efb3fa |
* number of seconds to wait for clamd to |
c857005e |
* respond, see ReadTimeout in clamd.conf |
d2efb3fa |
*/ |
c857005e |
static long streamMaxLength = -1; /* StreamMaxLength from clamd.conf */ |
3bc05064 |
static int logClean = 1; /*
* Add clean items to the log file
*/ |
4e136199 |
static char *signature = N_("-- \nScanned by ClamAv - http://www.clamav.net\n"); |
cdbe607d |
static time_t signatureStamp; |
1392cf22 |
static char *templatefile; /* e-mail to be sent when virus detected */ |
b151ef55 |
#ifdef CL_DEBUG
static int debug_level = 0;
#endif
static pthread_mutex_t n_children_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t n_children_cond = PTHREAD_COND_INITIALIZER;
static unsigned int n_children = 0;
static unsigned int max_children = 0; |
ad31761c |
static int child_timeout = 60; /* number of seconds to wait for
* a child to die. Set to 0 to
* wait forever
*/ |
ced83bee |
static int dont_wait = 0; /*
* If 1 send retry later to the remote end
* if max_chilren is exceeded, otherwise we
* wait for the number to go down
*/
static int advisory = 0; /*
* Run clamav-milter in advisory mode - viruses
* are flagged rather than deleted. Incompatible
* with quarantine options
*/ |
31d36bd3 |
static short use_syslog = 0; |
062dddc9 |
static const char *pidFile; |
b151ef55 |
static int logVerbose = 0;
static struct cfgstruct *copt; |
ce14cc31 |
static const char *localSocket; /* milter->clamd comms */
static in_port_t tcpSocket; /* milter->clamd comms */
static char *port = NULL; /* sendmail->milter comms */ |
f5d23f2e |
|
244be94f |
static const char *serverHostNames = "127.0.0.1";
static long *serverIPs; /* IPv4 only */ |
85563fe5 |
static int numServers; /* number of elements in serverIPs/cmdSockets */
|
f5d23f2e |
#ifdef SESSION
static int *cmdSockets; |
85563fe5 |
static int *cmdSocketsStatus;
static pthread_mutex_t sstatus_mutex = PTHREAD_MUTEX_INITIALIZER;
#define CMDSOCKET_FREE 0
#define CMDSOCKET_INUSE 1
#define CMDSOCKET_DOWN 2
static pthread_cond_t watchdog_cond = PTHREAD_COND_INITIALIZER;
|
f5d23f2e |
#endif /*SESSION*/
|
c6259ac5 |
static const char *postmaster = "postmaster"; |
063b6c33 |
static const char *from = "MAILER-DAEMON"; |
78a78fcc |
static int quitting; |
b151ef55 |
|
434012b8 |
/* |
f7925922 |
* NULL terminated whitelist of target ("to") addresses that we do NOT scan |
434012b8 |
* TODO: read in from a file |
0ac43d02 |
* TODO: add white list of source e-mail addresses that we do NOT scan |
0cb97bcb |
* TODO: items in the list should be regular expressions |
434012b8 |
*/ |
b14e9e77 |
static const char *ignoredEmailAddresses[] = {
/*"Mailer-Daemon@bandsman.co.uk",
"postmaster@bandsman.co.uk",*/
NULL
};
|
b151ef55 |
static void
help(void)
{
printf("\n\tclamav-milter version %s\n", CM_VERSION); |
1392cf22 |
puts("\tCopyright (C) 2004 Nigel Horne <njh@despammed.com>\n"); |
b151ef55 |
|
4e136199 |
puts(_("\t--advisory\t\t-A\tFlag viruses rather than deleting them."));
puts(_("\t--bounce\t\t-b\tSend a failure message to the sender.")); |
e5bb3b37 |
puts(_("\t--broadcast\t\t-B [IFACE]\tBroadcast to a network manager when a virus is found.")); |
4e136199 |
puts(_("\t--config-file=FILE\t-c FILE\tRead configuration from FILE."));
puts(_("\t--debug\t\t\t-D\tPrint debug messages."));
puts(_("\t--dont-log-clean\t-C\tDon't add an entry to syslog that a mail is clean."));
puts(_("\t--dont-scan-on-error\t-d\tPass e-mails through unscanned if a system error occurs."));
puts(_("\t--dont-wait\t\t\tAsk remote end to resend if max-children exceeded."));
puts(_("\t--from=EMAIL\t\t-a EMAIL\tError messages come from here."));
puts(_("\t--force-scan\t\t-f\tForce scan all messages (overrides (-o and -l)."));
puts(_("\t--help\t\t\t-h\tThis message."));
puts(_("\t--headers\t\t-H\tInclude original message headers in the report."));
puts(_("\t--local\t\t\t-l\tScan messages sent from machines on our LAN.")); |
58ae48c5 |
puts(_("\t--max-childen\t\t-m\tMaximum number of concurrent scans.")); |
4e136199 |
puts(_("\t--outgoing\t\t-o\tScan outgoing messages from this machine."));
puts(_("\t--noreject\t\t-N\tDon't reject viruses, silently throw them away."));
puts(_("\t--noxheader\t\t-n\tSuppress X-Virus-Scanned/X-Virus-Status headers."));
puts(_("\t--pidfile=FILE\t\t-i FILE\tLocation of pidfile."));
puts(_("\t--postmaster\t\t-p EMAIL\tPostmaster address [default=postmaster]."));
puts(_("\t--postmaster-only\t-P\tSend warnings only to the postmaster."));
puts(_("\t--quiet\t\t\t-q\tDon't send e-mail notifications of interceptions."));
puts(_("\t--quarantine=USER\t-Q EMAIL\tQuanrantine e-mail account."));
puts(_("\t--quarantine-dir=DIR\t-U DIR\tDirectory to store infected emails."));
puts(_("\t--server=SERVER\t\t-s SERVER\tHostname/IP address of server(s) running clamd (when using TCPsocket)."));
puts(_("\t--sign\t\t\t-S\tAdd a hard-coded signature to each scanned message."));
puts(_("\t--signature-file=FILE\t-F FILE\tLocation of signature file."));
puts(_("\t--template-file=FILE\t-t FILE\tLocation of e-mail template file."));
puts(_("\t--timeout=SECS\t\t-T SECS\tTimeout waiting to childen to die."));
puts(_("\t--version\t\t-V\tPrint the version number of this software.")); |
b151ef55 |
#ifdef CL_DEBUG |
4e136199 |
puts(_("\t--debug-level=n\t\t-x n\tSets the debug level to 'n'.")); |
b151ef55 |
#endif |
4e136199 |
puts(_("\nFor more information type \"man clamav-milter\"."));
puts(_("Report bugs to bugs@clamav.net.")); |
b151ef55 |
}
int
main(int argc, char **argv)
{
extern char *optarg; |
e5bb3b37 |
int i, Bflag = 0; |
b151ef55 |
const char *cfgfile = CL_DEFAULT_CFG;
struct cfgstruct *cpt; |
3454ad43 |
struct passwd *user; |
679ff9e7 |
const char *pidfile = NULL; |
85563fe5 |
#ifdef SESSION
pthread_t tid;
#endif |
b151ef55 |
struct smfiDesc smfilter = {
"ClamAv", /* filter name */
SMFI_VERSION, /* version code -- leave untouched */
SMFIF_ADDHDRS, /* flags - we add headers */
clamfi_connect, /* connection callback */
NULL, /* HELO filter callback */
clamfi_envfrom, /* envelope sender filter callback */
clamfi_envrcpt, /* envelope recipient filter callback */
clamfi_header, /* header filter callback */
clamfi_eoh, /* end of header callback */
clamfi_body, /* body filter callback */
clamfi_eom, /* end of message callback */
clamfi_abort, /* message aborted callback */
clamfi_close, /* connection cleanup callback */
};
|
244be94f |
#if defined(CL_DEBUG) && defined(C_LINUX)
struct rlimit rlim;
rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
if(setrlimit(RLIMIT_CORE, &rlim) < 0)
perror("setrlimit");
#endif |
f0d6f5a1 |
/*
* Temporarily enter guessed value into clamav_version, will
* be overwritten later by the value returned by clamd
*/ |
dd0d5a8c |
snprintf(clamav_version, sizeof(clamav_version),
"ClamAV version %s, clamav-milter version %s",
VERSION, CM_VERSION);
|
4e136199 |
#ifdef C_LINUX
setlocale(LC_ALL, ""); |
f5d23f2e |
bindtextdomain("clamav-milter", DATADIR"/clamav-milter/locale"); |
4e136199 |
textdomain("clamav-milter");
#endif
|
b151ef55 |
for(;;) {
int opt_index = 0;
#ifdef CL_DEBUG |
e5bb3b37 |
const char *args = "a:AbB:c:CDfF:lm:nNop:PqQ:dhHs:St:T:U:Vx:"; |
b151ef55 |
#else |
e5bb3b37 |
const char *args = "a:AbB:c:CDfF:lm:nNop:PqQ:dhHs:St:T:U:V"; |
b151ef55 |
#endif |
bb9979e6 |
|
b151ef55 |
static struct option long_options[] = {
{ |
b61a0781 |
"from", 2, NULL, 'a' |
063b6c33 |
},
{ |
ced83bee |
"advisory", 0, NULL, 'A'
},
{ |
b151ef55 |
"bounce", 0, NULL, 'b'
},
{ |
e5bb3b37 |
"broadcast", 2, NULL, 'B' |
78a78fcc |
},
{ |
b151ef55 |
"config-file", 1, NULL, 'c'
},
{ |
3bc05064 |
"dont-log-clean", 0, NULL, 'C'
},
{ |
b0a42ec6 |
"dont-scan-on-error", 0, NULL, 'd'
},
{ |
ced83bee |
"dont-wait", 0, NULL, 'w'
},
{ |
244be94f |
"debug", 0, NULL, 'D'
},
{ |
3454ad43 |
"force-scan", 0, NULL, 'f' |
b5d15e64 |
},
{ |
cbd7686b |
"headers", 0, NULL, 'H' |
d9b674b2 |
},
{ |
b151ef55 |
"help", 0, NULL, 'h'
},
{ |
679ff9e7 |
"pidfile", 1, NULL, 'i'
},
{ |
b151ef55 |
"local", 0, NULL, 'l'
},
{ |
a7164828 |
"noreject", 0, NULL, 'N'
},
{ |
3613bd91 |
"noxheader", 0, NULL, 'n'
},
{ |
b151ef55 |
"outgoing", 0, NULL, 'o'
},
{ |
434012b8 |
"postmaster", 1, NULL, 'p' |
c6259ac5 |
},
{ |
1c3f1ce1 |
"postmaster-only", 0, NULL, 'P',
},
{
"quiet", 0, NULL, 'q'
},
{ |
bb9979e6 |
"quarantine", 1, NULL, 'Q',
},
{ |
3454ad43 |
"quarantine-dir", 1, NULL, 'U',
},
{ |
b151ef55 |
"max-children", 1, NULL, 'm'
},
{
"server", 1, NULL, 's'
},
{ |
cdbe607d |
"sign", 0, NULL, 'S'
},
{
"signature-file", 1, NULL, 'F' |
e6bffccb |
},
{ |
1392cf22 |
"template-file", 1, NULL, 't'
},
{ |
ad31761c |
"timeout", 1, NULL, 'T'
},
{ |
b151ef55 |
"version", 0, NULL, 'V'
},
#ifdef CL_DEBUG
{
"debug-level", 1, NULL, 'x'
},
#endif
{
NULL, 0, NULL, '\0'
}
};
int ret = getopt_long(argc, argv, args, long_options, &opt_index);
if(ret == -1)
break;
else if(ret == 0)
ret = long_options[opt_index].val;
switch(ret) { |
063b6c33 |
case 'a': /* e-mail errors from here */ |
b61a0781 |
/*
* optarg is optional - if you give --from
* then the --from is set to the orginal,
* probably forged, email address
*/ |
063b6c33 |
from = optarg;
break; |
ced83bee |
case 'A':
advisory++;
smfilter.xxfi_flags |= SMFIF_CHGHDRS;
break; |
b151ef55 |
case 'b': /* bounce worms/viruses */
bflag++;
break; |
78a78fcc |
case 'B': /* broadcast */
Bflag++; |
e5bb3b37 |
if(optarg)
iface = optarg; |
78a78fcc |
break; |
c857005e |
case 'c': /* where is clamd.conf? */ |
b151ef55 |
cfgfile = optarg;
break; |
3bc05064 |
case 'C': /* dont log clean */
logClean = 0;
break; |
b0a42ec6 |
case 'd': /* don't scan on error */
cl_error = SMFIS_ACCEPT;
break; |
244be94f |
case 'D': /* enable debug messages */
cl_debug();
break; |
b5d15e64 |
case 'f': /* force the scan */
fflag++;
break; |
b151ef55 |
case 'h':
help();
return EX_OK; |
d9b674b2 |
case 'H':
hflag++;
break; |
679ff9e7 |
case 'i': /* pidfile */
pidfile = optarg;
break; |
b151ef55 |
case 'l': /* scan mail from the lan */
lflag++;
break; |
c6259ac5 |
case 'm': /* maximum number of children */
max_children = atoi(optarg);
break; |
3613bd91 |
case 'n': /* don't add X-Virus-Scanned */
nflag++; |
bb9979e6 |
smfilter.xxfi_flags &= ~SMFIF_ADDHDRS; |
3613bd91 |
break; |
a7164828 |
case 'N': /* Do we reject mail or silently drop it */
rejectmail = 0;
break; |
b151ef55 |
case 'o': /* scan outgoing mail */
oflag++;
break; |
c6259ac5 |
case 'p': /* postmaster e-mail address */
postmaster = optarg; |
b151ef55 |
break; |
1c3f1ce1 |
case 'P': /* postmaster only */
pflag++;
break;
case 'q': /* send NO notification email */
qflag++;
break; |
bb9979e6 |
case 'Q': /* quarantine e-mail address */
quarantine = optarg;
smfilter.xxfi_flags |= SMFIF_CHGHDRS|SMFIF_ADDRCPT|SMFIF_DELRCPT;
break; |
b151ef55 |
case 's': /* server running clamd */ |
244be94f |
serverHostNames = optarg; |
b151ef55 |
break; |
cdbe607d |
case 'F': /* signature file */
sigFilename = optarg;
signature = NULL;
/* fall through */ |
e6bffccb |
case 'S': /* sign */
smfilter.xxfi_flags |= SMFIF_CHGBODY;
Sflag++;
break; |
1392cf22 |
case 't': /* e-mail template file */
templatefile = optarg;
break; |
ad31761c |
case 'T': /* time to wait for child to die */
child_timeout = atoi(optarg);
break; |
3454ad43 |
case 'U': /* quarantine path */
quarantine_dir = optarg;
break; |
b151ef55 |
case 'V': |
dd0d5a8c |
puts(clamav_version); |
b151ef55 |
return EX_OK; |
ced83bee |
case 'w':
dont_wait++;
break; |
b151ef55 |
#ifdef CL_DEBUG
case 'x':
debug_level = atoi(optarg);
break;
#endif
default:
#ifdef CL_DEBUG |
244be94f |
fprintf(stderr, "Usage: %s [-b] [-c FILE] [-F FILE] [--max-children=num] [-l] [-o] [-p address] [-P] [-q] [-Q USER] [-s SERVER] [-S] [-x#] [-U PATH] socket-addr\n", argv[0]); |
b151ef55 |
#else |
244be94f |
fprintf(stderr, "Usage: %s [-b] [-c FILE] [-F FILE] [--max-children=num] [-l] [-o] [-p address] [-P] [-q] [-Q USER] [-s SERVER] [-S] [-U PATH] socket-addr\n", argv[0]); |
b151ef55 |
#endif
return EX_USAGE;
}
}
if (optind == argc) { |
4e136199 |
fprintf(stderr, _("%s: No socket-addr given\n"), argv[0]); |
b151ef55 |
return EX_USAGE;
}
port = argv[optind];
/*
* Sanity checks on the clamav configuration file
*/ |
819c7c41 |
if((copt = parsecfg(cfgfile, 1)) == NULL) { |
4e136199 |
fprintf(stderr, _("%s: Can't parse the config file %s\n"), |
b151ef55 |
argv[0], cfgfile);
return EX_CONFIG;
}
|
e5bb3b37 |
if(Bflag) {
int on;
broadcastSock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
/*
* SO_BROADCAST doesn't sent to all NICs on Linux, it only
* broadcasts on eth0, which is why there's an optional argument
* to --broadcast to say which NIC to broadcast on. You can use
* SO_BINDTODEVICE to get around that, but you need to have
* uid == 0 for that
*/
on = 1;
if(setsockopt(broadcastSock, SOL_SOCKET, SO_BROADCAST, (int *)&on, sizeof(on)) < 0) {
perror("setsockopt");
return EX_UNAVAILABLE;
}
shutdown(broadcastSock, SHUT_RD);
}
|
434012b8 |
/*
* Drop privileges
*/
if(getuid() == 0) { |
e5bb3b37 |
if(iface) { |
63e6694b |
#ifdef SO_BINDTODEVICE |
e5bb3b37 |
struct ifreq ifr;
memset(&ifr, '\0', sizeof(struct ifreq));
strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1);
if(setsockopt(broadcastSock, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0) {
perror(iface); |
63e6694b |
return EX_CONFIG; |
e5bb3b37 |
} |
3eacc10d |
#else |
63e6694b |
fprintf(stderr, _("%s: The iface option to --broadcast is not supported on your operating system\n"), argv[0]);
return EX_CONFIG; |
3eacc10d |
#endif |
63e6694b |
} |
434012b8 |
if((cpt = cfgopt(copt, "User")) != NULL) {
if((user = getpwnam(cpt->strarg)) == NULL) { |
4e136199 |
fprintf(stderr, _("%s: Can't get information about user %s\n"), argv[0], cpt->strarg); |
434012b8 |
return EX_CONFIG;
} |
bb9979e6 |
|
a0a82124 |
if(cfgopt(copt, "AllowSupplementaryGroups")) {
#ifdef HAVE_INITGROUPS
if(initgroups(cpt->strarg, user->pw_gid) < 0) {
perror(cpt->strarg);
return EX_CONFIG;
}
#else |
4e136199 |
fprintf(stderr, _("%s: AllowSupplementaryGroups: initgroups not supported.\n"), |
a0a82124 |
argv[0]);
return EX_CONFIG;
#endif
} else {
#ifdef HAVE_SETGROUPS
if(setgroups(1, &user->pw_gid) < 0) {
perror(cpt->strarg);
return EX_CONFIG;
}
#endif
} |
434012b8 |
setgid(user->pw_gid); |
d69535ae |
if(setuid(user->pw_uid) < 0)
perror(cpt->strarg);
else |
4e136199 |
cli_dbgmsg(_("Running as user %s (UID %d, GID %d)\n"), |
d69535ae |
cpt->strarg, user->pw_uid, user->pw_gid); |
434012b8 |
} else |
c857005e |
fprintf(stderr, _("%s: running as root is not recommended (check \"User\" in clamd.conf)\n"), argv[0]); |
e5bb3b37 |
} else if(iface) {
fprintf(stderr, _("%s: Only root can set an interface for --broadcast\n"), argv[0]);
return EX_USAGE; |
434012b8 |
} |
e5bb3b37 |
|
ced83bee |
if(advisory && quarantine) { |
4e136199 |
fprintf(stderr, _("%s: Advisory mode doesn't work with quarantine mode\n"), argv[0]); |
ced83bee |
return EX_USAGE;
} |
79f846a3 |
if(quarantine_dir) {
struct stat statb;
|
ced83bee |
if(advisory) { |
4e136199 |
fprintf(stderr, _("%s: Advisory mode doesn't work with quarantine directories\n"), argv[0]); |
ced83bee |
return EX_USAGE;
} |
79f846a3 |
if(access(quarantine_dir, W_OK) < 0) {
perror(quarantine_dir); |
ced83bee |
return EX_USAGE; |
79f846a3 |
}
if(stat(quarantine_dir, &statb) < 0) {
perror(quarantine_dir); |
ced83bee |
return EX_USAGE; |
79f846a3 |
}
/*
* Quit if the quarantine directory is publically readable
* or writeable
*/
if(statb.st_mode & 077) { |
4e136199 |
fprintf(stderr, _("%s: insecure quarantine directory %s (mode 0%o)\n"),
argv[0], quarantine_dir, statb.st_mode & 0777); |
79f846a3 |
return EX_CONFIG;
} |
3454ad43 |
} |
bb9979e6 |
|
cdbe607d |
if(sigFilename && !updateSigFile())
return EX_USAGE;
|
b90c8762 |
if(templatefile && (access(templatefile, R_OK) < 0)) {
perror(templatefile);
return EX_CONFIG; |
1392cf22 |
}
|
b151ef55 |
/* |
b5d15e64 |
* patch from "Richard G. Roberto" <rgr@dedlegend.com>
* If the --max-children flag isn't set, see if MaxThreads
* is set in the config file
*/ |
c15d1d2c |
if((max_children == 0) && ((cpt = cfgopt(copt, "MaxThreads")) != NULL)) |
31268a5c |
max_children = cpt->numarg; |
b5d15e64 |
|
9873b3c1 |
if((cpt = cfgopt(copt, "ReadTimeout")) != NULL) { |
cc511c7a |
readTimeout = cpt->numarg; |
d2efb3fa |
|
cc511c7a |
if(readTimeout < 0) { |
4e136199 |
fprintf(stderr, _("%s: ReadTimeout must not be negative in %s\n"), |
d2efb3fa |
argv[0], cfgfile); |
cc511c7a |
return EX_CONFIG; |
d2efb3fa |
}
} |
cc511c7a |
if((cpt = cfgopt(copt, "StreamMaxLength")) != NULL) {
if(cpt->numarg < 0) { |
4e136199 |
fprintf(stderr, _("%s: StreamMaxLength must not be negative in %s\n"), |
cc511c7a |
argv[0], cfgfile);
return EX_CONFIG;
} |
5691e168 |
streamMaxLength = (long)cpt->numarg; |
cc511c7a |
} |
b5d15e64 |
/* |
b151ef55 |
* Get the outgoing socket details - the way to talk to clamd
*/ |
f0d6f5a1 |
if((cpt = cfgopt(copt, "LocalSocket")) != NULL) { |
85563fe5 |
#ifdef SESSION
struct sockaddr_un server;
#endif
|
f0d6f5a1 |
if(cfgopt(copt, "TCPSocket") != NULL) { |
4e136199 |
fprintf(stderr, _("%s: You can select one server type only (local/TCP) in %s\n"), |
f0d6f5a1 |
argv[0], cfgfile);
return EX_CONFIG;
} |
b151ef55 |
/*
* TODO: check --server hasn't been set
*/
localSocket = cpt->strarg; |
85563fe5 |
#ifndef SESSION |
244be94f |
if(!pingServer(-1)) { |
4e136199 |
fprintf(stderr, _("Can't talk to clamd server via %s\n"), |
f0d6f5a1 |
localSocket); |
4e136199 |
fprintf(stderr, _("Check your entry for LocalSocket in %s\n"), |
f0d6f5a1 |
cfgfile);
return EX_CONFIG;
} |
85563fe5 |
#endif |
f7925922 |
/*if(quarantine_dir == NULL) |
4e136199 |
fprintf(stderr, _("When using Localsocket in %s\nyou may improve performance if you use the --quarantine-dir option\n"), cfgfile);*/ |
9873b3c1 |
|
e27af651 |
umask(077); |
244be94f |
serverIPs = (long *)cli_malloc(sizeof(long));
serverIPs[0] = inet_addr("127.0.0.1"); |
85563fe5 |
#ifdef SESSION
memset((char *)&server, 0, sizeof(struct sockaddr_un));
server.sun_family = AF_UNIX;
strncpy(server.sun_path, localSocket, sizeof(server.sun_path));
cmdSockets = (int *)cli_malloc(sizeof(int));
if((cmdSockets[0] = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
perror(localSocket);
fprintf(stderr, _("Can't talk to clamd server via %s\n"),
localSocket);
fprintf(stderr, _("Check your entry for LocalSocket in %s\n"),
cfgfile);
return EX_CONFIG;
}
if(connect(cmdSockets[0], (struct sockaddr *)&server, sizeof(struct sockaddr_un)) < 0) {
perror(localSocket);
return EX_UNAVAILABLE;
}
if(send(cmdSockets[0], "SESSION\n", 7, 0) < 7) {
perror("send");
if(use_syslog)
syslog(LOG_ERR, _("Can't create a clamd session"));
return EX_UNAVAILABLE;
}
#endif
numServers = 1; |
f0d6f5a1 |
} else if((cpt = cfgopt(copt, "TCPSocket")) != NULL) { |
f5d23f2e |
int activeServers; |
244be94f |
|
b151ef55 |
/*
* TCPSocket is in fact a port number not a full socket
*/ |
3454ad43 |
if(quarantine_dir) { |
4e136199 |
fprintf(stderr, _("%s: --quarantine-dir not supported for remote scanning - use --quarantine\n"), argv[0]); |
3454ad43 |
return EX_CONFIG;
}
|
cbd7686b |
tcpSocket = (in_port_t)cpt->numarg; |
244be94f |
|
bbc9d684 |
/* |
244be94f |
* cli_strtok's fieldno counts from 0 |
bbc9d684 |
*/ |
244be94f |
for(;;) { |
71f25024 |
char *hostname = cli_strtok(serverHostNames, numServers, ":"); |
244be94f |
if(hostname == NULL)
break;
numServers++;
free(hostname); |
bbc9d684 |
}
|
244be94f |
cli_dbgmsg("numServers: %d\n", numServers); |
3454ad43 |
|
244be94f |
serverIPs = (long *)cli_malloc(numServers * sizeof(long)); |
97727aa0 |
activeServers = 0; |
244be94f |
|
85563fe5 |
#ifdef SESSION
/*
* We need to know how many connections to establish to clamd
*/
if(max_children == 0) {
fprintf(stderr, _("%s: Sessions does not multiplex\n"), argv[0]);
return EX_CONFIG;
}
#endif
|
244be94f |
for(i = 0; i < numServers; i++) { |
71f25024 |
char *hostname = cli_strtok(serverHostNames, i, ":"); |
244be94f |
/*
* Translate server's name to IP address
*/
serverIPs[i] = inet_addr(hostname);
if(serverIPs[i] == -1L) {
const struct hostent *h = gethostbyname(hostname);
if(h == NULL) { |
4e136199 |
fprintf(stderr, _("%s: Unknown host %s\n"), |
244be94f |
argv[0], hostname);
return EX_USAGE;
}
memcpy((char *)&serverIPs[i], h->h_addr, sizeof(serverIPs[i]));
}
|
85563fe5 |
#ifndef SESSION |
97727aa0 |
if(pingServer(i))
activeServers++;
else { |
4e136199 |
cli_warnmsg(_("Can't talk to clamd server %s on port %d\n"), |
244be94f |
hostname, tcpSocket);
} |
85563fe5 |
#endif |
244be94f |
free(hostname); |
b151ef55 |
} |
85563fe5 |
#ifdef SESSION
activeServers = numServers;
cmdSockets = (int *)cli_malloc(max_children * sizeof(int));
cmdSocketsStatus = (int *)cli_calloc(max_children, sizeof(int));
for(i = 0; i < max_children; i++)
if(createSession(i) < 0)
return EX_UNAVAILABLE;
#else |
97727aa0 |
if(activeServers == 0) { |
4e136199 |
cli_errmsg(_("Can't find any clamd servers\n"));
cli_errmsg(_("Check your entry for TCPSocket in %s\n"), |
97727aa0 |
cfgfile);
return EX_CONFIG;
} |
85563fe5 |
#endif |
b151ef55 |
} else { |
4e136199 |
fprintf(stderr, _("%s: You must select server type (local/TCP) in %s\n"), |
b151ef55 |
argv[0], cfgfile);
return EX_CONFIG;
}
|
f0d6f5a1 |
if(!cfgopt(copt, "Foreground")) { |
cdbe607d |
#ifdef CL_DEBUG |
4e136199 |
printf(_("When debugging it is recommended that you use Foreground mode in %s\n"), cfgfile);
puts(_("So that you can see all of the messages")); |
cdbe607d |
#endif
|
b151ef55 |
switch(fork()) {
case -1:
perror("fork");
return EX_TEMPFAIL;
case 0: /* child */
break;
default: /* parent */
return EX_OK;
} |
f0d6f5a1 |
close(0); |
a121491a |
open("/dev/null", O_RDONLY);
#ifndef CL_DEBUG |
f0d6f5a1 |
close(1);
close(2); |
62f5a55b |
if((open("/dev/console", O_WRONLY) == 1) ||
(open("/dev/null", O_WRONLY) == 1)) |
f0d6f5a1 |
dup(1); |
a121491a |
#endif
|
65a8d561 |
#ifdef HAVE_SETPGRP
#ifdef SETPGRP_VOID |
0b6bce84 |
setpgrp(); |
65a8d561 |
#else
setpgrp(0,0);
#endif
#else
#ifdef HAVE_SETSID
setsid();
#endif
#endif |
f0d6f5a1 |
} |
b151ef55 |
|
78a78fcc |
atexit(quit);
|
41190088 |
#ifdef SESSION
pthread_create(&tid, NULL, watchdog, NULL);
#endif
|
062dddc9 |
if((cpt = cfgopt(copt, "PidFile")) != NULL)
pidFile = cpt->strarg;
|
b151ef55 |
if(cfgopt(copt, "LogSyslog")) { |
d7e0b4e6 |
int fac = LOG_LOCAL6;
|
d69535ae |
if(cfgopt(copt, "LogVerbose"))
logVerbose = 1;
use_syslog = 1;
|
d7e0b4e6 |
if((cpt = cfgopt(copt, "LogFacility")) != NULL)
if((fac = logg_facility(cpt->strarg)) == -1) {
fprintf(stderr, "%s: LogFacility: %s: No such facility\n",
argv[0], cpt->strarg);
return EX_CONFIG;
}
openlog("clamav-milter", LOG_CONS|LOG_PID, fac); |
d69535ae |
if(logVerbose) |
4e136199 |
syslog(LOG_INFO, _("Starting: %s"), clamav_version); |
d69535ae |
else |
8eb408e5 |
syslog(LOG_INFO, "%s", clamav_version); |
b151ef55 |
#ifdef CL_DEBUG
if(debug_level > 0) |
4e136199 |
syslog(LOG_DEBUG, _("Debugging is on")); |
b151ef55 |
#endif |
1c3f1ce1 |
} else {
if(qflag) |
4e136199 |
fprintf(stderr, _("%s: (-q && !LogSyslog): warning - all interception message methods are off\n"), |
1c3f1ce1 |
argv[0]); |
b151ef55 |
use_syslog = 0; |
1c3f1ce1 |
} |
3eacc10d |
broadcast(_("Starting clamav-milter")); |
b151ef55 |
|
679ff9e7 |
if(pidfile) {
/* save the PID */
FILE *fd;
const mode_t old_umask = umask(0006);
if((fd = fopen(pidfile, "w")) == NULL) {
if(use_syslog) |
4e136199 |
syslog(LOG_WARNING, _("Can't save PID in file %s"), |
679ff9e7 |
pidfile); |
4e136199 |
cli_warnmsg(_("Can't save PID in file %s\n"), pidfile); |
679ff9e7 |
} else {
fprintf(fd, "%d\n", (int)getpid());
fclose(fd);
}
umask(old_umask);
}
|
3a0b4e5b |
if(cfgopt(copt, "FixStaleSocket")) {
/*
* Get the incoming socket details - the way sendmail talks to
* us
* |
434012b8 |
* TODO: There's a security problem here that'll need fixing if |
c857005e |
* the User entry of clamd.conf is not used |
3a0b4e5b |
*/
if(strncasecmp(port, "unix:", 5) == 0) {
if(unlink(&port[5]) < 0) |
462b5251 |
if(errno != ENOENT)
perror(&port[5]); |
3a0b4e5b |
} else if(strncasecmp(port, "local:", 6) == 0) {
if(unlink(&port[6]) < 0) |
462b5251 |
if(errno != ENOENT)
perror(&port[6]); |
3a0b4e5b |
}
} |
b151ef55 |
|
e27af651 |
if(smfi_setconn(port) == MI_FAILURE) { |
4e136199 |
fprintf(stderr, _("%s: smfi_setconn failed\n"), |
e27af651 |
argv[0]);
return EX_SOFTWARE;
}
|
b151ef55 |
if(smfi_register(smfilter) == MI_FAILURE) { |
35d98cf9 |
cli_errmsg("smfi_register failure\n"); |
b151ef55 |
return EX_UNAVAILABLE;
}
signal(SIGPIPE, SIG_IGN);
|
d69535ae |
if(logVerbose) |
4e136199 |
syslog(LOG_INFO, _("Started: %s"), clamav_version); |
d69535ae |
|
b151ef55 |
return smfi_main();
}
|
85563fe5 |
#ifdef SESSION
/*
* Use the SESSION command of clamd.
* Returns -1 for terminal failure, 0 for OK, 1 for nonterminal failure
* The caller must take care of locking the cmdSocketStatus array
*/
static int
createSession(int session)
{ |
7e8d6a11 |
int ret = 0; |
85563fe5 |
struct sockaddr_in server;
const int serverNumber = session % numServers;
memset((char *)&server, 0, sizeof(struct sockaddr_in));
server.sin_family = AF_INET;
server.sin_port = (in_port_t)htons(tcpSocket);
server.sin_addr.s_addr = serverIPs[serverNumber];
if((cmdSockets[session] = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket"); |
7e8d6a11 |
ret = -1;
} else if(connect(cmdSockets[session], (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0) { |
85563fe5 |
perror("connect"); |
7e8d6a11 |
ret = 1;
} else if(send(cmdSockets[session], "SESSION\n", 7, 0) < 7) {
perror("send");
ret = 1; |
85563fe5 |
} |
7e8d6a11 |
if(ret != 0) { |
85563fe5 |
char *hostname = cli_strtok(serverHostNames, serverNumber, ":");
cli_warnmsg(_("Check clamd server %s - it may be down\n"), hostname);
free(hostname);
|
3eacc10d |
broadcast(_("Check clamd server - it may be down\n"));
|
85563fe5 |
cmdSocketsStatus[session] = CMDSOCKET_DOWN;
}
cli_dbgmsg("cmdSockets[%d] = %d\n", session, cmdSockets[session]); |
7e8d6a11 |
return ret; |
85563fe5 |
}
#else
|
b151ef55 |
/*
* Verify that the server is where we think it is
* Returns true or false |
244be94f |
* |
97727aa0 |
* serverNumber counts from 0, but is only used for TCPSocket |
b151ef55 |
*/
static int |
244be94f |
pingServer(int serverNumber) |
b151ef55 |
{ |
f0d6f5a1 |
char *ptr; |
b151ef55 |
int sock, nbytes; |
f0d6f5a1 |
char buf[128]; |
b151ef55 |
|
f0d6f5a1 |
if(localSocket) {
struct sockaddr_un server; |
b151ef55 |
|
f0d6f5a1 |
memset((char *)&server, 0, sizeof(struct sockaddr_un));
server.sun_family = AF_UNIX; |
7cc3891c |
strncpy(server.sun_path, localSocket, sizeof(server.sun_path)); |
f0d6f5a1 |
if((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { |
f5d23f2e |
perror(localSocket); |
f0d6f5a1 |
return 0;
} |
6e945c41 |
checkClamd(); |
f0d6f5a1 |
if(connect(sock, (struct sockaddr *)&server, sizeof(struct sockaddr_un)) < 0) {
perror(localSocket); |
6489ba59 |
close(sock); |
f0d6f5a1 |
return 0;
}
} else {
struct sockaddr_in server;
memset((char *)&server, 0, sizeof(struct sockaddr_in));
server.sin_family = AF_INET; |
cbd7686b |
server.sin_port = (in_port_t)htons(tcpSocket);
|
ca59d8d0 |
assert(serverIPs != NULL); |
244be94f |
assert(serverIPs[0] != -1L); |
bbc9d684 |
|
244be94f |
server.sin_addr.s_addr = serverIPs[serverNumber]; |
f0d6f5a1 |
if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket");
return 0;
}
if(connect(sock, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0) {
perror("connect"); |
9873b3c1 |
close(sock); |
f0d6f5a1 |
return 0;
} |
b151ef55 |
} |
f0d6f5a1 |
/*
* It would be better to use PING, check for PONG then issue the
* VERSION command, since that would better validate that we're
* talking to clamd, however clamd closes the session after
* sending PONG :-(
* So this code does not really validate that we're talking to clamd
* Needs a fix to clamd
* Also version command is verbose: says "clamd / ClamAV version"
* instead of "clamAV version"
*/ |
78a78fcc |
cli_dbgmsg("pingServer%d: sending VERSION\n", serverNumber); |
f0d6f5a1 |
if(send(sock, "VERSION\n", 8, 0) < 8) { |
b151ef55 |
perror("send");
close(sock);
return 0;
}
shutdown(sock, SHUT_WR);
|
d2efb3fa |
nbytes = clamd_recv(sock, buf, sizeof(buf)); |
b151ef55 |
close(sock);
if(nbytes < 0) {
perror("recv");
return 0;
} |
d2efb3fa |
if(nbytes == 0)
return 0;
|
b151ef55 |
buf[nbytes] = '\0';
|
f0d6f5a1 |
/* Remove the trailing new line from the reply */
if((ptr = strchr(buf, '\n')) != NULL)
*ptr = '\0';
/*
* No real validation is done here |
244be94f |
*
* TODO: When connecting to more than one server, give a warning
* if they're running different versions, or if the virus DBs |
0ac43d02 |
* are out of date (say more than a month old) |
f0d6f5a1 |
*/
snprintf(clamav_version, sizeof(clamav_version), |
f5d23f2e |
"%s\n\tclamav-milter version %s", |
f0d6f5a1 |
buf, CM_VERSION); |
bb9979e6 |
|
f0d6f5a1 |
return 1; |
b151ef55 |
} |
85563fe5 |
#endif |
b151ef55 |
|
244be94f |
/* |
97727aa0 |
* Find the best server to connect to. No intelligence to this.
* It is best to weight the order of the servers from most wanted to least
* wanted |
244be94f |
*
* Return value is from 0 - index into serverIPs |
97727aa0 |
*
* If the load balancing fails return the first server in the list, not
* an error, to be on the safe side |
244be94f |
*/ |
85563fe5 |
#ifdef SESSION
static int
findServer(void)
{
int i;
/*
* FIXME: Sessions code isn't flexible at handling servers
* appearing and disappearing, e.g. cmdSockets[n_children] == -1
*/
pthread_mutex_lock(&n_children_mutex);
assert(n_children > 0);
assert(n_children <= max_children);
i = n_children - 1;
pthread_mutex_unlock(&n_children_mutex);
pthread_mutex_lock(&sstatus_mutex);
for(; i < max_children; i++)
if(cmdSocketsStatus[i] == CMDSOCKET_FREE) {
cmdSocketsStatus[i] = CMDSOCKET_INUSE;
pthread_mutex_unlock(&sstatus_mutex);
return i;
}
pthread_mutex_unlock(&sstatus_mutex);
|
41190088 |
if(pthread_cond_broadcast(&watchdog_cond) < 0)
perror("pthread_cond_broadcast"); |
85563fe5 |
pthread_mutex_lock(&sstatus_mutex);
for(; i < max_children; i++)
if(cmdSocketsStatus[i] == CMDSOCKET_FREE) {
cmdSocketsStatus[i] = CMDSOCKET_INUSE;
pthread_mutex_unlock(&sstatus_mutex);
return i;
}
pthread_mutex_unlock(&sstatus_mutex);
cli_warnmsg(_("No free clamd sessions\n"));
return -1; /* none available - must fail */
}
#else |
244be94f |
static int
findServer(void)
{
struct sockaddr_in *servers, *server; |
fce82877 |
int *socks, maxsock = 0, i, j; |
244be94f |
fd_set rfds;
struct timeval tv;
int retval;
assert(tcpSocket != 0);
assert(numServers > 0);
if(numServers == 1)
return 0;
servers = (struct sockaddr_in *)cli_calloc(numServers, sizeof(struct sockaddr_in));
socks = (int *)cli_malloc(numServers * sizeof(int));
FD_ZERO(&rfds);
|
85563fe5 |
if(max_children > 0) {
assert(n_children > 0);
assert(n_children <= max_children);
/*
* Don't worry about no lock - it's doesn't matter if it's
* not really accurate
*/
j = n_children - 1;
} else |
7d7092de |
/*
* cli_rndnum returns 0..(max-1) - the max argument is not
* the maximum number you want it to return, it is in fact
* one *more* than the maximum number you want it to return
*/
j = cli_rndnum(numServers); |
fce82877 |
|
244be94f |
for(i = 0, server = servers; i < numServers; i++, server++) {
int sock;
server->sin_family = AF_INET; |
cbd7686b |
server->sin_port = (in_port_t)htons(tcpSocket); |
fce82877 |
server->sin_addr.s_addr = serverIPs[(i + j) % numServers];
cli_dbgmsg("findServer: try server %d\n",
(i + j) % numServers); |
244be94f |
sock = socks[i] = socket(AF_INET, SOCK_STREAM, 0); |
85563fe5 |
|
244be94f |
if(sock < 0) {
perror("socket");
do
if(socks[i] >= 0)
close(socks[i]);
while(--i >= 0);
free(socks);
free(servers);
return 0; /* Use the first server on failure */
}
if((connect(sock, (struct sockaddr *)server, sizeof(struct sockaddr)) < 0) ||
(send(sock, "PING\n", 5, 0) < 5)) { |
fce82877 |
char *hostname = cli_strtok(serverHostNames, i, ":"); |
4e136199 |
cli_warnmsg(_("Check clamd server %s - it may be down\n"), hostname); |
244be94f |
if(use_syslog) |
97727aa0 |
syslog(LOG_WARNING, |
4e136199 |
_("Check clamd server %s - it may be down"), |
97727aa0 |
hostname); |
6489ba59 |
close(sock); |
fce82877 |
free(hostname); |
3eacc10d |
broadcast(_("Check clamd server - it may be down\n")); |
fce82877 |
socks[i] = -1; |
244be94f |
continue;
}
shutdown(sock, SHUT_WR);
FD_SET(sock, &rfds);
if(sock > maxsock)
maxsock = sock;
}
free(servers);
|
cc511c7a |
tv.tv_sec = readTimeout; |
244be94f |
tv.tv_usec = 0; |
cbd7686b |
|
95ec27d0 |
if(maxsock == 0)
retval = 0;
else
retval = select(maxsock + 1, &rfds, NULL, NULL, &tv);
|
244be94f |
if(retval < 0)
perror("select");
for(i = 0; i < numServers; i++)
if(socks[i] >= 0)
close(socks[i]);
if(retval == 0) {
free(socks); |
85563fe5 |
clamdIsDown(); |
244be94f |
return 0;
} else if(retval < 0) {
free(socks);
if(use_syslog) |
4e136199 |
syslog(LOG_ERR, _("findServer: select failed")); |
244be94f |
return 0;
}
for(i = 0; i < numServers; i++) |
fce82877 |
if((socks[i] >= 0) && (FD_ISSET(socks[i], &rfds))) { |
8eb408e5 |
const int s = (i + j) % numServers; |
fce82877 |
|
244be94f |
free(socks); |
4e136199 |
cli_dbgmsg(_("findServer: using server %d\n"), s); |
8eb408e5 |
return s; |
244be94f |
}
free(socks); |
4e136199 |
cli_dbgmsg(_("findServer: No response from any server\n")); |
244be94f |
if(use_syslog) |
4e136199 |
syslog(LOG_WARNING, _("findServer: No response from any server")); |
244be94f |
return 0;
} |
85563fe5 |
#endif |
244be94f |
|
6489ba59 |
/*
* Sendmail wants to establish a connection to us
*/ |
b151ef55 |
static sfsistat
clamfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr) |
6489ba59 |
{ |
0a9dafb1 |
#if defined(HAVE_INET_NTOP) || defined(WITH_TCPWRAP) |
35d98cf9 |
char ip[INET_ADDRSTRLEN]; /* IPv4 only */ |
0a9dafb1 |
#endif |
4e136199 |
const char *remoteIP; |
b151ef55 |
|
78a78fcc |
if(quitting)
return cl_error;
|
18f9be43 |
if(ctx == NULL) {
if(use_syslog) |
4e136199 |
syslog(LOG_ERR, _("clamfi_connect: ctx is null")); |
18f9be43 |
return cl_error;
} |
2a1ff3e4 |
if(hostname == NULL) {
if(use_syslog) |
4e136199 |
syslog(LOG_ERR, _("clamfi_connect: hostname is null")); |
2a1ff3e4 |
return cl_error;
} |
384e5f09 |
if((hostaddr == NULL) || (&(((struct sockaddr_in *)(hostaddr))->sin_addr) == NULL)) |
40c9a608 |
/*
* According to the sendmail API hostaddr is NULL if
* "the type is not supported in the current version". What |
18f9be43 |
* the documentation doesn't say is the type of what. |
40c9a608 |
* |
d69535ae |
* Possibly the input is not a TCP/IP socket e.g. stdin? |
40c9a608 |
*/
remoteIP = "127.0.0.1";
else { |
0a9dafb1 |
#ifdef HAVE_INET_NTOP |
40c9a608 |
remoteIP = (char *)inet_ntop(AF_INET, &((struct sockaddr_in *)(hostaddr))->sin_addr, ip, sizeof(ip)); |
0a9dafb1 |
#else |
40c9a608 |
remoteIP = inet_ntoa(((struct sockaddr_in *)(hostaddr))->sin_addr); |
0a9dafb1 |
#endif |
2a1ff3e4 |
|
40c9a608 |
if(remoteIP == NULL) {
if(use_syslog) |
4e136199 |
syslog(LOG_ERR, _("clamfi_connect: remoteIP is null")); |
40c9a608 |
return cl_error;
} |
2a1ff3e4 |
} |
80a72b0d |
|
1f025849 |
#ifdef CL_DEBUG |
e2bb746e |
if(debug_level >= 4) {
if(use_syslog) |
4e136199 |
syslog(LOG_NOTICE, _("clamfi_connect: connection from %s [%s]"), hostname, remoteIP);
cli_dbgmsg(_("clamfi_connect: connection from %s [%s]\n"), hostname, remoteIP); |
e2bb746e |
} |
1f025849 |
#endif |
b151ef55 |
|
f8a7079c |
#ifdef WITH_TCPWRAP
/*
* Support /etc/hosts.allow and /etc/hosts.deny
*/ |
8f1f6fb8 |
if(strncasecmp(port, "inet:", 5) == 0) { |
ce14cc31 |
const char *hostmail; |
88ad6ea4 |
struct hostent hostent; |
6d113e37 |
char buf[BUFSIZ]; |
8eb408e5 |
static pthread_mutex_t wrap_mutex = PTHREAD_MUTEX_INITIALIZER; |
35d98cf9 |
|
cbd7686b |
/* |
ce14cc31 |
* Using TCP/IP for the sendmail->clamav-milter connection
*/
if((hostmail = smfi_getsymval(ctx, "{if_name}")) == NULL) {
if(use_syslog) |
4e136199 |
syslog(LOG_ERR, _("Can't get sendmail hostname")); |
62f5a55b |
return cl_error; |
ce14cc31 |
} |
6d113e37 |
if(clamfi_gethostbyname(hostmail, &hostent, buf, sizeof(buf)) != 0) { |
88ad6ea4 |
if(use_syslog) |
4e136199 |
syslog(LOG_WARNING, _("Access Denied: Host Unknown (%s)"), hostname); |
88ad6ea4 |
return cl_error;
} |
35d98cf9 |
|
62f5a55b |
#ifdef HAVE_INET_NTOP |
6d113e37 |
if(hostent.h_addr &&
(inet_ntop(AF_INET, (struct in_addr *)hostent.h_addr, ip, sizeof(ip)) == NULL)) {
perror(hostent.h_name); |
8eb408e5 |
/*strcpy(ip, (char *)inet_ntoa(*(struct in_addr *)hostent.h_addr));*/ |
d69535ae |
if(use_syslog) |
4e136199 |
syslog(LOG_WARNING, _("Access Denied: Can't get IP address for (%s)"), hostent.h_name); |
d69535ae |
return cl_error;
} |
62f5a55b |
#else |
6d113e37 |
strncpy(ip, (char *)inet_ntoa(*(struct in_addr *)hostent.h_addr), sizeof(ip)); |
62f5a55b |
#endif |
ce14cc31 |
/*
* Ask is this is a allowed name or IP number |
8eb408e5 |
*
* hosts_ctl uses strtok so it is not thread safe, see
* hosts_access(3) |
ce14cc31 |
*/ |
8eb408e5 |
pthread_mutex_lock(&wrap_mutex); |
6d113e37 |
if(!hosts_ctl("clamav-milter", hostent.h_name, ip, STRING_UNKNOWN)) { |
8eb408e5 |
pthread_mutex_unlock(&wrap_mutex); |
ce14cc31 |
if(use_syslog) |
4e136199 |
syslog(LOG_WARNING, _("Access Denied for %s[%s]"), hostent.h_name, ip); |
ce14cc31 |
return SMFIS_TEMPFAIL;
} |
8eb408e5 |
pthread_mutex_unlock(&wrap_mutex); |
f8a7079c |
}
#endif
|
b5d15e64 |
if(fflag)
/*
* Patch from "Richard G. Roberto" <rgr@dedlegend.com>
* Always scan whereever the message is from
*/
return SMFIS_CONTINUE;
|
b151ef55 |
if(!oflag)
if(strcmp(remoteIP, "127.0.0.1") == 0) {
#ifdef CL_DEBUG
if(use_syslog) |
4e136199 |
syslog(LOG_DEBUG, _("clamfi_connect: not scanning outgoing messages"));
cli_dbgmsg(_("clamfi_connect: not scanning outgoing messages\n")); |
b151ef55 |
#endif
return SMFIS_ACCEPT;
}
|
503b2956 |
if((!lflag) && isLocalAddr(inet_addr(remoteIP))) { |
b151ef55 |
#ifdef CL_DEBUG |
503b2956 |
if(use_syslog) |
4e136199 |
syslog(LOG_DEBUG, _("clamfi_connect: not scanning local messages")); |
85563fe5 |
cli_dbgmsg(_("clamfi_connect: not scanning local messages\n")); |
b151ef55 |
#endif |
503b2956 |
return SMFIS_ACCEPT; |
b151ef55 |
} |
b7f54bf2 |
|
b151ef55 |
return SMFIS_CONTINUE;
}
static sfsistat
clamfi_envfrom(SMFICTX *ctx, char **argv)
{
struct privdata *privdata;
if(logVerbose)
syslog(LOG_DEBUG, "clamfi_envfrom: %s", argv[0]);
|
6489ba59 |
cli_dbgmsg("clamfi_envfrom: %s\n", argv[0]); |
b151ef55 |
if(max_children > 0) { |
6489ba59 |
int rc = 0; |
b151ef55 |
pthread_mutex_lock(&n_children_mutex);
|
3a805bfd |
/*
* Not a while since sendmail doesn't like it if we
* take too long replying. Effectively this means that
* max_children is more of a hint than a rule
*/
if(n_children >= max_children) { |
b151ef55 |
struct timeval now;
struct timespec timeout;
struct timezone tz;
|
85563fe5 |
cli_dbgmsg((dont_wait) ?
_("hit max-children limit (%u >= %u)\n") :
_("hit max-children limit (%u >= %u): waiting for some to exit\n"),
n_children, max_children);
|
d69535ae |
if(use_syslog)
syslog(LOG_NOTICE, |
85563fe5 |
(dont_wait) ? |
4e136199 |
_("hit max-children limit (%u >= %u)") : |
85563fe5 |
_("hit max-children limit (%u >= %u): waiting for some to exit"), |
d69535ae |
n_children, max_children);
|
ced83bee |
if(dont_wait) {
pthread_mutex_unlock(&n_children_mutex); |
4e136199 |
smfi_setreply(ctx, "451", "4.3.2", _("AV system temporarily overloaded - please try later")); |
ced83bee |
return SMFIS_TEMPFAIL;
} |
b151ef55 |
/* |
ad31761c |
* Wait for an amount of time for a child to go (default
* 60 seconds).
* |
b151ef55 |
* Use pthread_cond_timedwait rather than
* pthread_cond_wait since the sendmail which calls |
ad31761c |
* us will have a timeout that we don't want to exceed,
* stops sendmail getting fidgety. |
d69535ae |
*
* Patch from Damian Menscher <menscher@uiuc.edu> to
* ensure it wakes up when a child goes away |
b151ef55 |
*/ |
ad31761c |
if(child_timeout) {
gettimeofday(&now, &tz);
timeout.tv_sec = now.tv_sec + child_timeout;
timeout.tv_nsec = 0;
} |
b151ef55 |
|
3a805bfd |
do |
ad31761c |
if(child_timeout == 0)
rc = pthread_cond_wait(&n_children_cond, &n_children_mutex);
else
rc = pthread_cond_timedwait(&n_children_cond, &n_children_mutex, &timeout); |
d69535ae |
while((n_children >= max_children) && (rc != ETIMEDOUT)); |
b151ef55 |
}
n_children++;
|
4e136199 |
cli_dbgmsg(_(">n_children = %d\n"), n_children); |
b151ef55 |
pthread_mutex_unlock(&n_children_mutex);
|
ad31761c |
if(child_timeout && (rc == ETIMEDOUT)) { |
b151ef55 |
#ifdef CL_DEBUG
if(use_syslog) |
4e136199 |
syslog(LOG_NOTICE, _("Timeout waiting for a child to die")); |
b151ef55 |
#endif |
4e136199 |
cli_dbgmsg(_("Timeout waiting for a child to die\n")); |
b151ef55 |
}
}
|
64d5612d |
privdata = (struct privdata *)cli_calloc(1, sizeof(struct privdata)); |
063b6c33 |
if(privdata == NULL) |
6ad582fe |
return cl_error; |
063b6c33 |
|
b151ef55 |
privdata->dataSocket = -1; /* 0.4 */ |
f5d23f2e |
#ifndef SESSION |
b151ef55 |
privdata->cmdSocket = -1; /* 0.4 */ |
f5d23f2e |
#endif |
b151ef55 |
|
a121491a |
/*
* Rejection is via 550 until DATA is received. We know that
* DATA has been sent when either we get a header or the end of
* header statement
*/
privdata->rejectCode = "550";
|
b151ef55 |
privdata->from = strdup(argv[0]);
|
cc511c7a |
if(hflag)
privdata->headers = header_list_new(); |
64d5612d |
if(smfi_setpriv(ctx, privdata) == MI_SUCCESS)
return SMFIS_CONTINUE;
clamfi_free(privdata); |
d9b674b2 |
|
64d5612d |
return cl_error; |
b151ef55 |
}
static sfsistat
clamfi_envrcpt(SMFICTX *ctx, char **argv)
{
struct privdata *privdata = (struct privdata *)smfi_getpriv(ctx);
if(logVerbose)
syslog(LOG_DEBUG, "clamfi_envrcpt: %s", argv[0]);
|
6489ba59 |
cli_dbgmsg("clamfi_envrcpt: %s\n", argv[0]); |
b151ef55 |
if(privdata->to == NULL) { |
cbd7686b |
privdata->to = cli_malloc(sizeof(char *) * 2); |
b151ef55 |
assert(privdata->numTo == 0);
} else |
ea815cbc |
privdata->to = cli_realloc(privdata->to, sizeof(char *) * (privdata->numTo + 2)); |
b151ef55 |
|
063b6c33 |
if(privdata->to == NULL) |
6ad582fe |
return cl_error; |
063b6c33 |
|
b151ef55 |
privdata->to[privdata->numTo] = strdup(argv[0]);
privdata->to[++privdata->numTo] = NULL;
return SMFIS_CONTINUE;
}
static sfsistat
clamfi_header(SMFICTX *ctx, char *headerf, char *headerv)
{
struct privdata *privdata = (struct privdata *)smfi_getpriv(ctx);
if(logVerbose)
syslog(LOG_DEBUG, "clamfi_header: %s: %s", headerf, headerv);
#ifdef CL_DEBUG
if(debug_level >= 9) |
6489ba59 |
cli_dbgmsg("clamfi_header: %s: %s\n", headerf, headerv); |
b151ef55 |
else |
cbd7686b |
cli_dbgmsg("clamfi_header\n"); |
b151ef55 |
#endif
|
a121491a |
/*
* The DATA instruction from SMTP (RFC2821) must have been sent
*/
privdata->rejectCode = "554";
|
6489ba59 |
if(privdata->dataSocket == -1)
/*
* First header - make connection with clamd
*/
if(!connect2clamd(privdata)) {
clamfi_cleanup(ctx);
return cl_error;
}
|
0cb97bcb |
if(clamfi_send(privdata, 0, "%s: %s\n", headerf, headerv) <= 0) { |
b151ef55 |
clamfi_cleanup(ctx); |
b0a42ec6 |
return cl_error; |
b151ef55 |
} |
d9b674b2 |
if(hflag) |
cbd7686b |
header_list_add(privdata->headers, headerf, headerv); |
97727aa0 |
else if((strcasecmp(headerf, "Received") == 0) && |
2285693f |
(strncasecmp(headerv, "from ", 5) == 0) &&
(strstr(headerv, "localhost") != 0)) { |
97727aa0 |
if(privdata->received) |
063b6c33 |
free(privdata->received);
privdata->received = strdup(headerv);
}
|
a121491a |
if((strcasecmp(headerf, "Message-ID") == 0) &&
(strncasecmp(headerv, "<MDAEMON", 8) == 0))
privdata->discard = 1;
|
b151ef55 |
return SMFIS_CONTINUE;
}
|
a121491a |
/*
* At this point DATA will have been received, so we really ought to
* send 554 back not 550
*/ |
b151ef55 |
static sfsistat
clamfi_eoh(SMFICTX *ctx)
{
struct privdata *privdata = (struct privdata *)smfi_getpriv(ctx); |
b14e9e77 |
char **to; |
b151ef55 |
if(logVerbose) |
4e136199 |
syslog(LOG_DEBUG, _("clamfi_eoh")); |
b151ef55 |
#ifdef CL_DEBUG |
6489ba59 |
if(debug_level >= 4) |
4e136199 |
cli_dbgmsg(_("clamfi_eoh\n")); |
b151ef55 |
#endif
|
a121491a |
/*
* The DATA instruction from SMTP (RFC2821) must have been sent
*/
privdata->rejectCode = "554";
|
6489ba59 |
if(privdata->dataSocket == -1)
/*
* No headers - make connection with clamd
*/
if(!connect2clamd(privdata)) {
clamfi_cleanup(ctx);
return cl_error;
}
|
0cb97bcb |
if(clamfi_send(privdata, 1, "\n") != 1) { |
b151ef55 |
clamfi_cleanup(ctx); |
b0a42ec6 |
return cl_error; |
b151ef55 |
}
|
b14e9e77 |
/*
* See if the e-mail is only going to members of the list
* of users we don't scan for. If it is, don't scan, otherwise
* scan
*
* scan = false
* FORALL recipients
* IF receipient NOT MEMBER OF white address list
* THEN
* scan = true
* FI
* ENDFOR
*/
for(to = privdata->to; *to; to++) {
const char **s;
for(s = ignoredEmailAddresses; *s; s++)
if(strcasecmp(*s, *to) == 0)
/*
* This recipient is on the whitelist
*/
break;
if(*s == NULL)
/*
* This recipient is not on the whitelist,
* no need to check any further
*/
return SMFIS_CONTINUE;
}
/*
* Didn't find a recipient who is not on the white list, so all
* must be on the white list, so just accept the e-mail
*/
if(use_syslog) |
4e136199 |
syslog(LOG_NOTICE, _("clamfi_eoh: ignoring whitelisted message")); |
b14e9e77 |
#ifdef CL_DEBUG |
4e136199 |
cli_dbgmsg(_("clamfi_eoh: not scanning outgoing messages\n")); |
b14e9e77 |
#endif
clamfi_cleanup(ctx);
return SMFIS_ACCEPT; |
b151ef55 |
}
static sfsistat
clamfi_body(SMFICTX *ctx, u_char *bodyp, size_t len)
{
struct privdata *privdata = (struct privdata *)smfi_getpriv(ctx); |
0cb97bcb |
int nbytes; |
b151ef55 |
if(logVerbose) |
4e136199 |
syslog(LOG_DEBUG, _("clamfi_envbody: %u bytes"), len); |
b151ef55 |
#ifdef CL_DEBUG |
4e136199 |
cli_dbgmsg(_("clamfi_envbody: %u bytes\n"), len); |
b151ef55 |
#endif
|
0cb97bcb |
nbytes = clamfi_send(privdata, len, (char *)bodyp); |
5691e168 |
if(streamMaxLength > 0L) { |
cc511c7a |
if(privdata->numBytes > streamMaxLength) { |
6ad582fe |
if(use_syslog) {
const char *sendmailId = smfi_getsymval(ctx, "i");
if(sendmailId == NULL)
sendmailId = "Unknown"; |
4e136199 |
syslog(LOG_NOTICE, _("%s: Message more than StreamMaxLength (%ld) bytes - not scanned"), |
6ad582fe |
sendmailId, streamMaxLength);
} |
0cb97bcb |
if(!nflag) |
4e136199 |
smfi_addheader(ctx, "X-Virus-Status", _("Not Scanned - StreamMaxLength exceeded")); |
0cb97bcb |
return SMFIS_ACCEPT; /* clamfi_close will be called */ |
cc511c7a |
}
} |
0cb97bcb |
if(nbytes < len) { |
cc511c7a |
clamfi_cleanup(ctx); /* not needed, but just to be safe */ |
b0a42ec6 |
return cl_error; |
b151ef55 |
} |
e6bffccb |
if(Sflag) { |
3166c010 |
if(privdata->body) {
assert(privdata->bodyLen > 0); |
a121491a |
privdata->body = cli_realloc(privdata->body, privdata->bodyLen + len); |
3166c010 |
memcpy(&privdata->body[privdata->bodyLen], bodyp, len);
privdata->bodyLen += len; |
e6bffccb |
} else { |
3166c010 |
assert(privdata->bodyLen == 0); |
cbd7686b |
privdata->body = cli_malloc(len); |
3166c010 |
memcpy(privdata->body, bodyp, len);
privdata->bodyLen = len; |
e6bffccb |
}
} |
b151ef55 |
return SMFIS_CONTINUE;
}
static sfsistat
clamfi_eom(SMFICTX *ctx)
{
int rc = SMFIS_CONTINUE;
char *ptr; |
c96f7461 |
const char *sendmailId; |
b151ef55 |
struct privdata *privdata = (struct privdata *)smfi_getpriv(ctx);
char mess[128];
if(logVerbose)
syslog(LOG_DEBUG, "clamfi_eom");
#ifdef CL_DEBUG |
cbd7686b |
cli_dbgmsg("clamfi_eom\n"); |
b151ef55 |
assert(privdata != NULL); |
f5d23f2e |
#ifndef SESSION |
3454ad43 |
assert((privdata->cmdSocket >= 0) || (privdata->filename != NULL));
assert(!((privdata->cmdSocket >= 0) && (privdata->filename != NULL))); |
f5d23f2e |
#endif |
b151ef55 |
assert(privdata->dataSocket >= 0);
#endif
close(privdata->dataSocket);
privdata->dataSocket = -1;
|
3454ad43 |
if(quarantine_dir != NULL) {
char cmdbuf[1024];
/*
* Create socket to talk to clamd.
*/
struct sockaddr_un server;
int nbytes;
assert(localSocket != NULL);
memset((char *)&server, 0, sizeof(struct sockaddr_un));
server.sun_family = AF_UNIX; |
7cc3891c |
strncpy(server.sun_path, localSocket, sizeof(server.sun_path)); |
3454ad43 |
|
f5d23f2e |
snprintf(cmdbuf, sizeof(cmdbuf) - 1, "SCAN %s", privdata->filename);
nbytes = (int)strlen(cmdbuf);
#ifdef SESSION
if(send(cmdSockets[0], cmdbuf, nbytes, 0) < nbytes) {
perror("send");
clamfi_cleanup(ctx);
if(use_syslog)
syslog(LOG_ERR, _("send failed to clamd"));
return cl_error;
}
#else |
3454ad43 |
if((privdata->cmdSocket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
perror("socket"); |
85c1dbfd |
clamfi_cleanup(ctx); |
3454ad43 |
return cl_error;
}
if(connect(privdata->cmdSocket, (struct sockaddr *)&server, sizeof(struct sockaddr_un)) < 0) {
perror(localSocket); |
85c1dbfd |
clamfi_cleanup(ctx); |
3454ad43 |
return cl_error;
}
if(send(privdata->cmdSocket, cmdbuf, nbytes, 0) < nbytes) {
perror("send");
clamfi_cleanup(ctx);
if(use_syslog) |
4e136199 |
syslog(LOG_ERR, _("send failed to clamd")); |
3454ad43 |
return cl_error;
}
shutdown(privdata->cmdSocket, SHUT_WR); |
f5d23f2e |
#endif |
3454ad43 |
}
|
f5d23f2e |
#ifdef SESSION
if(clamd_recv(cmdSockets[privdata->serverNumber], mess, sizeof(mess)) > 0) {
#else |
d2efb3fa |
if(clamd_recv(privdata->cmdSocket, mess, sizeof(mess)) > 0) { |
f5d23f2e |
#endif |
b151ef55 |
if((ptr = strchr(mess, '\n')) != NULL)
*ptr = '\0';
if(logVerbose) |
4e136199 |
syslog(LOG_DEBUG, _("clamfi_eom: read %s"), mess);
cli_dbgmsg(_("clamfi_eom: read %s\n"), mess); |
b151ef55 |
} else { |
fce82877 |
/*
* TODO: if more than one host has been specified, try
* another one - setting cl_error to SMFIS_TEMPFAIL helps
* by forcing a retry
*/ |
85c1dbfd |
clamfi_cleanup(ctx); |
4e136199 |
syslog(LOG_NOTICE, _("clamfi_eom: read nothing from clamd")); |
b151ef55 |
#ifdef CL_DEBUG |
4e136199 |
cli_dbgmsg(_("clamfi_eom: read nothing from clamd\n")); |
b151ef55 |
#endif |
d2efb3fa |
return cl_error; |
b151ef55 |
}
|
85563fe5 |
#ifdef SESSION
pthread_mutex_lock(&sstatus_mutex);
if(cmdSocketsStatus[privdata->serverNumber] == CMDSOCKET_INUSE)
cmdSocketsStatus[privdata->serverNumber] = CMDSOCKET_FREE;
pthread_mutex_unlock(&sstatus_mutex);
#else |
0b6bce84 |
close(privdata->cmdSocket);
privdata->cmdSocket = -1; |
f5d23f2e |
#endif |
0b6bce84 |
|
c96f7461 |
sendmailId = smfi_getsymval(ctx, "i"); |
6ad582fe |
if(sendmailId == NULL)
sendmailId = "Unknown"; |
6489ba59 |
|
71f25024 |
if(!nflag) {
char buf[1024];
/* |
73ffa07e |
* Include the hostname where the scan took place |
71f25024 |
*/ |
73ffa07e |
if(localSocket) {
char hostname[32];
|
6d113e37 |
if(gethostname(hostname, sizeof(hostname)) < 0) { |
8eb408e5 |
const char *j = smfi_getsymval(ctx, "{j}"); |
6d113e37 |
|
8eb408e5 |
if(j)
strncpy(hostname, j, |
6d113e37 |
sizeof(hostname) - 1);
else |
4e136199 |
strcpy(buf, _("Error determining host")); |
6d113e37 |
} else if(strchr(hostname, '.') == NULL) {
/*
* Determine fully qualified name
*/
struct hostent hostent;
if(clamfi_gethostbyname(hostname, &hostent, buf, sizeof(buf)) == 0)
strncpy(hostname, hostent.h_name, sizeof(hostname));
} |
73ffa07e |
|
39378e7f |
snprintf(buf, sizeof(buf) - 1, "%s\n\ton %s", |
73ffa07e |
clamav_version, hostname);
} else { |
39378e7f |
char *hostname = cli_strtok(serverHostNames, privdata->serverNumber, ":");
if(hostname) {
snprintf(buf, sizeof(buf) - 1, "%s\n\ton %s",
clamav_version,
hostname);
free(hostname);
} else
/* sanity check failed - should issue warning */ |
4e136199 |
strcpy(buf, _("Error determining host")); |
39378e7f |
} |
71f25024 |
smfi_addheader(ctx, "X-Virus-Scanned", buf);
} |
40c9a608 |
|
c96f7461 |
if(strstr(mess, "ERROR") != NULL) { |
de23431a |
if(strstr(mess, "Size limit reached") != NULL) { |
0a867b46 |
/*
* Clamd has stopped on StreamMaxLength before us
*/
if(use_syslog) |
4e136199 |
syslog(LOG_NOTICE, _("%s: Message more than StreamMaxLength (%ld) bytes - not scanned"), |
0a867b46 |
sendmailId, streamMaxLength); |
0cb97bcb |
if(!nflag) |
4e136199 |
smfi_addheader(ctx, "X-Virus-Status", _("Not Scanned - StreamMaxLength exceeded")); |
39378e7f |
clamfi_cleanup(ctx); /* not needed, but just to be safe */ |
0a867b46 |
return SMFIS_ACCEPT;
} |
0cb97bcb |
if(!nflag) |
4e136199 |
smfi_addheader(ctx, "X-Virus-Status", _("Not Scanned")); |
6489ba59 |
|
c96f7461 |
cli_warnmsg("%s: %s\n", sendmailId, mess); |
e27af651 |
if(use_syslog) |
c96f7461 |
syslog(LOG_ERR, "%s: %s\n", sendmailId, mess); |
e27af651 |
clamfi_cleanup(ctx);
return cl_error;
}
|
62f5a55b |
if((ptr = strstr(mess, "FOUND")) == NULL) { |
3613bd91 |
if(!nflag) |
4e136199 |
smfi_addheader(ctx, "X-Virus-Status", _("Clean")); |
b151ef55 |
|
3bc05064 |
if(use_syslog && logClean) |
e2bb746e |
/* Include the sendmail queue ID in the log */ |
4e136199 |
syslog(LOG_NOTICE, _("%s: clean message from %s"), |
c96f7461 |
sendmailId, |
4e136199 |
(privdata->from) ? privdata->from : _("an unknown sender")); |
e6bffccb |
|
3166c010 |
if(privdata->body) { |
e2bb746e |
/*
* Add a signature that all has been scanned OK
*/ |
cdbe607d |
off_t len = updateSigFile(); |
e6bffccb |
|
cdbe607d |
if(len) {
assert(Sflag != 0); |
e6bffccb |
|
a121491a |
privdata->body = cli_realloc(privdata->body, privdata->bodyLen + len);
if(privdata->body) {
memcpy(&privdata->body[privdata->bodyLen], signature, len);
smfi_replacebody(ctx, privdata->body, privdata->bodyLen + len);
} |
cdbe607d |
} |
e6bffccb |
} |
b151ef55 |
} else { |
dd68be92 |
char reject[1024]; |
97727aa0 |
char **to, *virusname; |
62f5a55b |
|
6d113e37 |
/*
Remove the "FOUND" word, and the space before it
*/
*--ptr = '\0'; |
b151ef55 |
|
97727aa0 |
/* skip over 'stream/filename: ' at the start */
if((virusname = strchr(mess, ':')) != NULL)
virusname = &virusname[2];
else
virusname = mess;
|
40c9a608 |
if(!nflag) |
4e136199 |
smfi_addheader(ctx, "X-Virus-Status", _("Infected")); |
40c9a608 |
|
62f5a55b |
if(use_syslog) {
/*
* Setup err as a list of recipients
*/
char *err = (char *)cli_malloc(1024);
int i; |
b151ef55 |
|
62f5a55b |
if(err == NULL) {
clamfi_cleanup(ctx);
return cl_error;
} |
063b6c33 |
|
62f5a55b |
/*
* Use snprintf rather than printf since we don't know the
* length of privdata->from and may get a buffer overrun
*/ |
4e136199 |
snprintf(err, 1023, _("Intercepted virus from %s to"), |
62f5a55b |
privdata->from); |
b151ef55 |
|
62f5a55b |
ptr = strchr(err, '\0'); |
b151ef55 |
|
62f5a55b |
i = 1024; |
c6259ac5 |
|
62f5a55b |
for(to = privdata->to; *to; to++) {
/*
* Re-alloc if we are about run out of buffer space
*/
if(&ptr[strlen(*to) + 2] >= &err[i]) {
i += 1024; |
a121491a |
err = cli_realloc(err, i);
if(err == NULL) {
clamfi_cleanup(ctx);
return cl_error;
} |
62f5a55b |
ptr = strchr(err, '\0');
}
ptr = strrcpy(ptr, " ");
ptr = strrcpy(ptr, *to); |
b151ef55 |
} |
62f5a55b |
(void)strcpy(ptr, "\n"); |
b151ef55 |
|
e2bb746e |
/* Include the sendmail queue ID in the log */ |
503b2956 |
syslog(LOG_NOTICE, "%s: %s %s", sendmailId, mess, err); |
b151ef55 |
#ifdef CL_DEBUG |
85563fe5 |
cli_dbgmsg("%s", err); |
b151ef55 |
#endif |
62f5a55b |
free(err);
} |
b151ef55 |
|
1c3f1ce1 |
if(!qflag) { |
b696653a |
char cmd[128]; |
64d9c7a1 |
FILE *sendmail; |
b696653a |
|
97727aa0 |
/*
* If the oflag is given this sendmail command
* will cause the mail being generated here to be
* scanned. So if oflag is given we just put the
* item in the queue so there's no scanning of two
* messages at once. It'll still be scanned, but
* not at the same time as the incoming message |
fce82877 |
* |
0ac43d02 |
* FIXME: there is a race condition here when sendmail
* and clamav-milter run on the same machine. If the |
fce82877 |
* system is very overloaded this sendmail can
* take a long time to start - and may even fail
* is the LA is > REFUSE_LA. In all the time we're
* taking to start this sendmail, the sendmail that's
* started us may timeout waiting for a response and
* let the virus through (albeit tagged with
* X-Virus-Status: Infected) because we haven't
* sent SMFIS_DISCARD or SMFIS_REJECT |
97727aa0 |
*/
snprintf(cmd, sizeof(cmd) - 1, |
c51c112c |
(oflag || fflag) ? "%s -t -odq" : "%s -t",
SENDMAIL_BIN); |
b696653a |
sendmail = popen(cmd, "w");
|
1c3f1ce1 |
if(sendmail) { |
b61a0781 |
if(from && from[0])
fprintf(sendmail, "From: %s\n", from);
else
fprintf(sendmail, "From: %s\n", privdata->from); |
1c3f1ce1 |
if(bflag) { |
79fa8528 |
/*
* Handle privdata->from not set,
* "Denis Ustimenko" <den@uzsci.net>
*/
fprintf(sendmail, "To: %s\n",
(privdata->from) ?
privdata->from :
smfi_getsymval(ctx, "{mail_addr}"));
|
1c3f1ce1 |
fprintf(sendmail, "Cc: %s\n", postmaster);
} else
fprintf(sendmail, "To: %s\n", postmaster);
if(!pflag)
for(to = privdata->to; *to; to++)
fprintf(sendmail, "Cc: %s\n", *to); |
9873b3c1 |
/* |
d69535ae |
* Auto-submitted is still a draft, keep an |
9873b3c1 |
* eye on its format
*/
fputs("Auto-Submitted: auto-submitted (antivirus notify)\n", sendmail); |
d69535ae |
/* "Sergey Y. Afonin" <asy@kraft-s.ru> */
if((ptr = smfi_getsymval(ctx, "{_}")) != NULL)
fprintf(sendmail,
"X-Infected-Received-From: %s\n",
ptr); |
4e136199 |
fputs(_("Subject: Virus intercepted\n\n"), sendmail); |
1c3f1ce1 |
|
1392cf22 |
if((templatefile == NULL) || |
0b79c1b7 |
(sendtemplate(ctx, templatefile, sendmail, virusname) < 0)) { |
95ec27d0 |
/* |
31d36bd3 |
* Use our own hardcoded template
*/
const char *sender;
/*
* Try to determine who sent the
* message. In the days of faked from
* addresses this is not easy!
*/
if(privdata->from)
sender = (strcmp(privdata->from, "<>") == 0) ?
smfi_getsymval(ctx, "_") :
privdata->from;
else
sender = smfi_getsymval(ctx, "_");
|
1392cf22 |
if(bflag) |
4e136199 |
fputs(_("A message you sent to\n"), sendmail); |
1392cf22 |
else if(pflag)
/* |
31d36bd3 |
* The message is only going to
* the postmaster, so include
* some useful information |
1392cf22 |
*/ |
4e136199 |
fprintf(sendmail, _("The message %1$s sent from %2$s to\n"), |
31d36bd3 |
sendmailId, sender); |
1392cf22 |
else |
4e136199 |
fprintf(sendmail, _("A message sent from %s to\n"), |
31d36bd3 |
sender); |
1392cf22 |
for(to = privdata->to; *to; to++) |
62f5a55b |
fprintf(sendmail, "\t%s\n", *to); |
4e136199 |
fprintf(sendmail, _("contained %s and has not been delivered.\n"), virusname); |
1392cf22 |
if(privdata->filename != NULL) |
79fa8528 |
if(qfile(privdata, virusname) == 0) |
4e136199 |
fprintf(sendmail, _("\nThe message in question has been quarantined as %s\n"), privdata->filename); |
1392cf22 |
|
97727aa0 |
if(hflag) { |
4e136199 |
fprintf(sendmail, _("\nThe message was received by %1$s from %2$s via %3$s\n\n"), |
31d36bd3 |
smfi_getsymval(ctx, "j"), sender, |
97727aa0 |
smfi_getsymval(ctx, "_")); |
4e136199 |
fputs(_("For your information, the original message headers were:\n\n"), sendmail); |
97727aa0 |
header_list_print(privdata->headers, sendmail);
} else if(privdata->received) |
063b6c33 |
/*
* TODO: parse this to find
* real infected machine.
* Need to decide how to find
* if it's a dynamic IP from a
* dial up account in which
* case there may not be much
* we can do if that DHCP has
* set the hostname...
*/ |
4e136199 |
fprintf(sendmail, _("\nThe infected machine is likely to be here:\n%s\t\n"), |
063b6c33 |
privdata->received);
|
d9b674b2 |
} |
3454ad43 |
|
1c3f1ce1 |
pclose(sendmail);
} |
b151ef55 |
} |
3454ad43 |
if(privdata->filename) {
assert(quarantine_dir != NULL);
if(use_syslog) |
4e136199 |
syslog(LOG_NOTICE, _("Quarantined infected mail as %s"), privdata->filename); |
3454ad43 |
/* |
0cb97bcb |
* Cleanup filename here otherwise clamfi_free() will
* delete the file that we wish to keep because it
* is infected |
3454ad43 |
*/
free(privdata->filename);
privdata->filename = NULL;
}
|
bb9979e6 |
if(quarantine) {
for(to = privdata->to; *to; to++) {
smfi_delrcpt(ctx, *to); |
3454ad43 |
smfi_addheader(ctx, "X-Original-To", *to); |
bb9979e6 |
free(*to);
}
free(privdata->to);
privdata->to = NULL; |
c51c112c |
/*
* NOTE: on a closed relay this will not work
* if the recipient is a remote address
*/ |
bb9979e6 |
if(smfi_addrcpt(ctx, quarantine) == MI_FAILURE) {
if(use_syslog) |
4e136199 |
syslog(LOG_DEBUG, _("Can't set quarantine user %s"), quarantine); |
bb9979e6 |
else |
4e136199 |
cli_warnmsg(_("Can't set quarantine user %s\n"), quarantine); |
ced83bee |
} else
setsubject(ctx, virusname);
} else if(advisory)
setsubject(ctx, virusname);
else if(rejectmail) { |
a121491a |
if(privdata->discard)
rc = SMFIS_DISCARD;
else
rc = SMFIS_REJECT; /* Delete the e-mail */
} else |
a7164828 |
rc = SMFIS_DISCARD; |
b151ef55 |
|
da7f01e3 |
snprintf(reject, sizeof(reject) - 1, _("virus %s detected by ClamAV - http://www.clamav.net"), virusname); |
a121491a |
smfi_setreply(ctx, (char *)privdata->rejectCode, "5.7.1", reject); |
3eacc10d |
broadcast(mess); |
b151ef55 |
}
clamfi_cleanup(ctx);
return rc;
}
static sfsistat
clamfi_abort(SMFICTX *ctx)
{
#ifdef CL_DEBUG
if(use_syslog)
syslog(LOG_DEBUG, "clamfi_abort");
#endif
|
74e6c404 |
cli_dbgmsg("clamfi_abort\n"); |
b151ef55 |
/*
* Unlock incase we're called during a cond_timedwait in envfrom
* |
d69535ae |
* TODO: There *must* be a tidier a safer way of doing this! |
b151ef55 |
*/ |
c175cbda |
if((max_children > 0) && (n_children >= max_children)) {
(void)pthread_mutex_trylock(&n_children_mutex); |
3a805bfd |
(void)pthread_mutex_unlock(&n_children_mutex); |
c175cbda |
} |
b151ef55 |
clamfi_cleanup(ctx);
|
b0a42ec6 |
return cl_error; |
b151ef55 |
}
static sfsistat
clamfi_close(SMFICTX *ctx)
{
struct privdata *privdata = (struct privdata *)smfi_getpriv(ctx);
|
cbd7686b |
cli_dbgmsg("clamfi_close\n"); |
0cb97bcb |
if(privdata != NULL)
clamfi_cleanup(ctx); |
b151ef55 |
if(logVerbose)
syslog(LOG_DEBUG, "clamfi_close");
return SMFIS_CONTINUE;
}
static void
clamfi_cleanup(SMFICTX *ctx)
{
struct privdata *privdata = (struct privdata *)smfi_getpriv(ctx);
|
3613bd91 |
if(privdata) { |
64d5612d |
clamfi_free(privdata);
smfi_setpriv(ctx, NULL);
}
}
static void
clamfi_free(struct privdata *privdata)
{ |
85563fe5 |
cli_dbgmsg("clamfi_free\n"); |
64d5612d |
if(privdata) { |
3166c010 |
if(privdata->body)
free(privdata->body);
|
3613bd91 |
if(privdata->dataSocket >= 0) {
close(privdata->dataSocket);
privdata->dataSocket = -1;
} |
b151ef55 |
|
3454ad43 |
if(privdata->filename != NULL) { |
73ffa07e |
/*
* Don't print an error if the file hasn't been created
* yet
*/
if((unlink(privdata->filename) < 0) && (errno != ENOENT)) { |
3454ad43 |
perror(privdata->filename); |
0cb97bcb |
if(use_syslog)
syslog(LOG_ERR, |
4e136199 |
_("Can't remove clean file %s"), |
0cb97bcb |
privdata->filename);
} |
3454ad43 |
free(privdata->filename);
privdata->filename = NULL;
}
|
3613bd91 |
if(privdata->from) { |
b151ef55 |
#ifdef CL_DEBUG |
3613bd91 |
if(debug_level >= 9) |
cbd7686b |
cli_dbgmsg("Free privdata->from\n"); |
b151ef55 |
#endif |
3613bd91 |
free(privdata->from);
privdata->from = NULL;
} |
b151ef55 |
|
3613bd91 |
if(privdata->to) {
char **to; |
b151ef55 |
|
3613bd91 |
for(to = privdata->to; *to; to++) { |
b151ef55 |
#ifdef CL_DEBUG |
3613bd91 |
if(debug_level >= 9) |
cbd7686b |
cli_dbgmsg("Free *privdata->to\n"); |
b151ef55 |
#endif |
3613bd91 |
free(*to);
} |
b151ef55 |
#ifdef CL_DEBUG |
3613bd91 |
if(debug_level >= 9) |
cbd7686b |
cli_dbgmsg("Free privdata->to\n"); |
b151ef55 |
#endif |
3613bd91 |
free(privdata->to);
privdata->to = NULL;
} |
b151ef55 |
|
f5d23f2e |
#ifdef SESSION |
85563fe5 |
pthread_mutex_lock(&sstatus_mutex);
if(cmdSocketsStatus[privdata->serverNumber] == CMDSOCKET_INUSE) { |
d7e0b4e6 |
#if 0 |
85563fe5 |
pthread_mutex_unlock(&sstatus_mutex);
if(readTimeout) {
char buf[64];
const int fd = cmdSockets[privdata->serverNumber]; |
f5d23f2e |
|
85563fe5 |
cli_dbgmsg("clamfi_free: flush server %d fd %d\n",
privdata->serverNumber, fd); |
f5d23f2e |
|
41190088 |
/*
* FIXME: whenever this code gets executed,
* all of the PINGs fail in the next
* watchdog cycle
*/ |
85563fe5 |
while(clamd_recv(fd, buf, sizeof(buf)) > 0)
;
}
pthread_mutex_lock(&sstatus_mutex); |
d7e0b4e6 |
#endif |
85563fe5 |
cmdSocketsStatus[privdata->serverNumber] = CMDSOCKET_FREE; |
f5d23f2e |
} |
85563fe5 |
pthread_mutex_unlock(&sstatus_mutex); |
f5d23f2e |
#else |
3613bd91 |
if(privdata->cmdSocket >= 0) {
char buf[64]; |
b151ef55 |
|
3613bd91 |
/*
* Flush the remote end so that clamd doesn't get a SIGPIPE
*/ |
f5d23f2e |
if(readTimeout)
while(clamd_recv(privdata->cmdSocket, buf, sizeof(buf)) > 0)
; |
3613bd91 |
close(privdata->cmdSocket);
privdata->cmdSocket = -1;
} |
f5d23f2e |
#endif |
d9b674b2 |
if(privdata->headers)
header_list_free(privdata->headers); |
b151ef55 |
#ifdef CL_DEBUG |
3613bd91 |
if(debug_level >= 9) |
cbd7686b |
cli_dbgmsg("Free privdata\n"); |
b151ef55 |
#endif |
97727aa0 |
if(privdata->received) |
063b6c33 |
free(privdata->received); |
3613bd91 |
free(privdata);
} |
b151ef55 |
if(max_children > 0) {
pthread_mutex_lock(&n_children_mutex); |
85563fe5 |
cli_dbgmsg("clamfi_free: n_children = %d\n", n_children); |
b151ef55 |
/*
* Deliberately errs on the side of broadcasting too many times
*/ |
85563fe5 |
if(n_children > 0) { |
390a7661 |
--n_children; |
85563fe5 |
#ifdef SESSION
if(n_children == 0) |
41190088 |
if(pthread_cond_broadcast(&watchdog_cond) < 0)
perror("pthread_cond_broadcast"); |
85563fe5 |
#endif
} |
b151ef55 |
#ifdef CL_DEBUG |
cbd7686b |
cli_dbgmsg("pthread_cond_broadcast\n"); |
b151ef55 |
#endif |
41190088 |
if(pthread_cond_broadcast(&n_children_cond) < 0)
perror("pthread_cond_broadcast"); |
35d98cf9 |
cli_dbgmsg("<n_children = %d\n", n_children); |
b151ef55 |
pthread_mutex_unlock(&n_children_mutex);
}
}
|
6489ba59 |
/* |
0cb97bcb |
* Returns < 0 for failure, otherwise the number of bytes sent |
6489ba59 |
*/ |
b151ef55 |
static int |
0cb97bcb |
clamfi_send(struct privdata *privdata, size_t len, const char *format, ...) |
b151ef55 |
{
char output[BUFSIZ];
const char *ptr; |
0cb97bcb |
int ret = 0; |
b151ef55 |
assert(format != NULL);
if(len > 0)
/*
* It isn't a NUL terminated string. We have a set number of
* bytes to output.
*/
ptr = format;
else {
va_list argp;
va_start(argp, format); |
b90c8762 |
vsnprintf(output, sizeof(output) - 1, format, argp); |
b151ef55 |
va_end(argp);
len = strlen(output);
ptr = output;
}
#ifdef CL_DEBUG |
6489ba59 |
if(debug_level >= 9) {
time_t t;
const struct tm *tm;
time(&t);
tm = localtime(&t);
cli_dbgmsg("%d:%d:%d clamfi_send: len=%u bufsiz=%u, fd=%d\n",
tm->tm_hour, tm->tm_min, tm->tm_sec, len,
sizeof(output), privdata->dataSocket);
} |
b151ef55 |
#endif
while(len > 0) { |
e2bb746e |
const int nbytes = (quarantine_dir) ? |
3454ad43 |
write(privdata->dataSocket, ptr, len) :
send(privdata->dataSocket, ptr, len, 0); |
b151ef55 |
|
ea815cbc |
assert(privdata->dataSocket >= 0);
|
b151ef55 |
if(nbytes == -1) { |
f7925922 |
if(quarantine_dir) {
perror(privdata->filename);
if(use_syslog) {
#ifdef HAVE_STRERROR_R
char buf[32];
strerror_r(errno, buf, sizeof(buf));
syslog(LOG_ERR, |
4e136199 |
_("write failure (%u bytes) to %s: %s"), |
f7925922 |
len, privdata->filename, buf);
#else |
4e136199 |
syslog(LOG_ERR, _("write failure (%u bytes) to %s: %s"), |
f7925922 |
len, privdata->filename,
strerror(errno));
#endif
}
} else {
if(errno == EINTR)
continue;
perror("send");
if(use_syslog) { |
ea815cbc |
#ifdef HAVE_STRERROR_R |
f7925922 |
char buf[32];
strerror_r(errno, buf, sizeof(buf));
syslog(LOG_ERR, |
4e136199 |
_("write failure (%u bytes) to clamd: %s"), |
f7925922 |
len, buf); |
ea815cbc |
#else |
4e136199 |
syslog(LOG_ERR, _("write failure (%u bytes) to clamd: %s"), len, strerror(errno)); |
ea815cbc |
#endif |
f7925922 |
}
checkClamd(); |
ea815cbc |
} |
b151ef55 |
return -1;
} |
0cb97bcb |
ret += nbytes; |
b151ef55 |
len -= nbytes;
ptr = &ptr[nbytes]; |
0cb97bcb |
if(streamMaxLength > 0L) {
privdata->numBytes += nbytes;
if(privdata->numBytes >= streamMaxLength)
break;
} |
b151ef55 |
} |
0cb97bcb |
return ret; |
b151ef55 |
}
/*
* Like strcpy, but return the END of the destination, allowing a quicker
* means of adding to the end of a string than strcat
*/ |
e6bda975 |
#if 0 |
b151ef55 |
static char *
strrcpy(char *dest, const char *source)
{
/* Pre assertions */
assert(dest != NULL);
assert(source != NULL);
assert(dest != source);
while((*dest++ = *source++) != '\0')
;
return(--dest);
} |
e6bda975 |
#endif |
d2efb3fa |
/*
* Read from clamav - timeout if necessary
*/
static int
clamd_recv(int sock, char *buf, size_t len)
{
fd_set rfds;
struct timeval tv;
|
f5d23f2e |
assert(sock >= 0);
|
cc511c7a |
if(readTimeout == 0) |
d2efb3fa |
return recv(sock, buf, len, 0);
FD_ZERO(&rfds);
FD_SET(sock, &rfds);
|
cc511c7a |
tv.tv_sec = readTimeout; |
d2efb3fa |
tv.tv_usec = 0;
switch(select(sock + 1, &rfds, NULL, NULL, &tv)) {
case -1:
perror("select");
return -1;
case 0:
if(use_syslog) |
4e136199 |
syslog(LOG_ERR, _("No data received from clamd in %d seconds\n"), readTimeout); |
d2efb3fa |
return 0;
}
return recv(sock, buf, len, 0);
} |
cdbe607d |
/*
* Read in the signature file
*/
static off_t
updateSigFile(void)
{
struct stat statb;
int fd;
if(sigFilename == NULL)
/* nothing to read */ |
a121491a |
return 0; |
cdbe607d |
if(stat(sigFilename, &statb) < 0) {
perror(sigFilename);
if(use_syslog) |
4e136199 |
syslog(LOG_ERR, _("Can't stat %s"), sigFilename); |
cdbe607d |
return 0;
}
if(statb.st_mtime <= signatureStamp)
return statb.st_size; /* not changed */
fd = open(sigFilename, O_RDONLY);
if(fd < 0) {
perror(sigFilename);
if(use_syslog) |
4e136199 |
syslog(LOG_ERR, _("Can't open %s"), sigFilename); |
cdbe607d |
return 0;
}
signatureStamp = statb.st_mtime;
|
c63633c4 |
signature = cli_realloc(signature, statb.st_size); |
73ffa07e |
if(signature)
read(fd, signature, statb.st_size); |
cdbe607d |
close(fd);
return statb.st_size;
} |
d9b674b2 |
static header_list_t |
8f1f6fb8 |
header_list_new(void) |
d9b674b2 |
{
header_list_t ret;
ret = (header_list_t)cli_malloc(sizeof(struct header_list_struct)); |
063b6c33 |
if(ret) {
ret->first = NULL;
ret->last = NULL;
} |
d9b674b2 |
return ret;
}
static void
header_list_free(header_list_t list)
{
struct header_node_t *iter;
iter = list->first;
while (iter) {
struct header_node_t *iter2 = iter->next;
free(iter->header);
free(iter);
iter = iter2;
}
free(list);
}
static void
header_list_add(header_list_t list, const char *headerf, const char *headerv)
{
char *header;
size_t len;
struct header_node_t *new_node;
|
71f25024 |
len = (size_t)(strlen(headerf) + strlen(headerv) + 3); |
d9b674b2 |
header = (char *)cli_malloc(len); |
063b6c33 |
if(header == NULL)
return;
|
0a867b46 |
sprintf(header, "%s: %s", headerf, headerv); |
cbd7686b |
new_node = (struct header_node_t *)cli_malloc(sizeof(struct header_node_t)); |
063b6c33 |
if(new_node == NULL) {
free(header);
return;
} |
d9b674b2 |
new_node->header = header;
new_node->next = NULL;
if(!list->first)
list->first = new_node;
if(list->last)
list->last->next = new_node;
list->last = new_node;
}
static void |
1941819d |
header_list_print(const header_list_t list, FILE *fp) |
d9b674b2 |
{
const struct header_node_t *iter;
|
1941819d |
if(list == NULL)
return;
|
97727aa0 |
for(iter = list->first; iter; iter = iter->next) { |
c51c112c |
if(strncmp(iter->header, "From ", 5) == 0) |
97727aa0 |
putc('>', fp); |
d9b674b2 |
fprintf(fp, "%s\n", iter->header); |
97727aa0 |
} |
d9b674b2 |
} |
1392cf22 |
|
6489ba59 |
/*
* Establish a connection to clamd
* Returns success (1) or failure (0)
*/
static int
connect2clamd(struct privdata *privdata)
{
char **to; |
0ac43d02 |
char *msg;
int length; |
6489ba59 |
|
a121491a |
assert(privdata != NULL); |
6489ba59 |
assert(privdata->dataSocket == -1);
assert(privdata->from != NULL);
assert(privdata->to != NULL);
#ifdef CL_DEBUG
if((debug_level > 0) && use_syslog)
syslog(LOG_DEBUG, "connect2clamd");
if(debug_level >= 4)
cli_dbgmsg("connect2clamd\n");
#endif
if(quarantine_dir) {
/*
* quarantine_dir is specified
* store message in a temporary file
*/
int ntries = 5; |
c175cbda |
time_t t; |
063b6c33 |
int MM, YY, DD;
const struct tm *tm; |
6489ba59 |
|
063b6c33 |
/*
* Based on an idea by Christian Pelissier
* <Christian.Pelissier@onera.fr>. Store different days
* in different directories to make them easier to manage
*/ |
c175cbda |
t = time((time_t *)0); |
063b6c33 |
tm = localtime(&t);
MM = tm->tm_mon + 1;
YY = tm->tm_year - 100;
DD = tm->tm_mday;
privdata->filename = (char *)cli_malloc(strlen(quarantine_dir) + 19);
sprintf(privdata->filename, "%s/%02d%02d%02d", quarantine_dir, |
97727aa0 |
YY, MM, DD); |
063b6c33 |
|
d80b0903 |
if((mkdir(privdata->filename, 0700) < 0) && (errno != EEXIST)) { |
73ffa07e |
perror(privdata->filename);
if(use_syslog) |
4e136199 |
syslog(LOG_ERR, _("mkdir %s failed"), privdata->filename); |
73ffa07e |
return 0;
} |
6489ba59 |
do { |
063b6c33 |
sprintf(privdata->filename,
"%s/%02d%02d%02d/msg.XXXXXX",
quarantine_dir, YY, MM, DD); |
6489ba59 |
#if defined(C_LINUX) || defined(C_BSD) || defined(HAVE_MKSTEMP) || defined(C_SOLARIS)
privdata->dataSocket = mkstemp(privdata->filename);
#else
if(mktemp(privdata->filename) == NULL) {
if(use_syslog) |
4e136199 |
syslog(LOG_ERR, _("mktemp %s failed"), privdata->filename); |
6489ba59 |
return 0;
}
privdata->dataSocket = open(privdata->filename, O_CREAT|O_EXCL|O_WRONLY|O_TRUNC, 0600);
#endif
} while((--ntries > 0) && (privdata->dataSocket < 0));
if(privdata->dataSocket < 0) { |
a121491a |
perror(privdata->filename); |
6489ba59 |
if(use_syslog) |
4e136199 |
syslog(LOG_ERR, _("Temporary quarantine file %s creation failed"), privdata->filename); |
6489ba59 |
return 0;
} |
f5d23f2e |
privdata->serverNumber = 0; |
6489ba59 |
} else {
int freeServer, nbytes;
struct sockaddr_in reply; |
8eb408e5 |
unsigned short p; |
6489ba59 |
char buf[64];
|
f5d23f2e |
#ifndef SESSION |
6489ba59 |
assert(privdata->cmdSocket == -1); |
f5d23f2e |
#endif |
6489ba59 |
/*
* Create socket to talk to clamd. It will tell us the port to
* use to send the data. That will require another socket.
*/
if(localSocket) { |
f5d23f2e |
#ifndef SESSION |
6489ba59 |
struct sockaddr_un server;
memset((char *)&server, 0, sizeof(struct sockaddr_un));
server.sun_family = AF_UNIX; |
7cc3891c |
strncpy(server.sun_path, localSocket, sizeof(server.sun_path)); |
6489ba59 |
if((privdata->cmdSocket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
perror("socket");
return 0;
}
if(connect(privdata->cmdSocket, (struct sockaddr *)&server, sizeof(struct sockaddr_un)) < 0) {
perror(localSocket);
return 0;
} |
f5d23f2e |
privdata->serverNumber = 0;
#endif |
6489ba59 |
freeServer = 0;
} else { |
f5d23f2e |
#ifdef SESSION
freeServer = findServer();
if(freeServer < 0)
return 0;
#else |
6489ba59 |
struct sockaddr_in server;
memset((char *)&server, 0, sizeof(struct sockaddr_in));
server.sin_family = AF_INET;
server.sin_port = (in_port_t)htons(tcpSocket);
assert(serverIPs != NULL);
freeServer = findServer();
if(freeServer < 0)
return 0;
server.sin_addr.s_addr = serverIPs[freeServer];
if((privdata->cmdSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket");
return 0;
}
if(connect(privdata->cmdSocket, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0) {
perror("connect");
return 0;
} |
f5d23f2e |
#endif |
39378e7f |
privdata->serverNumber = freeServer; |
6489ba59 |
}
|
f5d23f2e |
#ifdef SESSION
if(send(cmdSockets[freeServer], "STREAM\n", 7, 0) < 7) {
perror("send"); |
85563fe5 |
pthread_mutex_lock(&sstatus_mutex);
cmdSocketsStatus[privdata->serverNumber] = CMDSOCKET_DOWN;
pthread_mutex_unlock(&sstatus_mutex);
cli_warnmsg("Failed sending stream to server %d (fd %d) errno %d\n",
freeServer, cmdSockets[freeServer], errno); |
6489ba59 |
if(use_syslog) |
f5d23f2e |
syslog(LOG_ERR, _("send failed to clamd")); |
6489ba59 |
return 0;
} |
f5d23f2e |
#else |
6489ba59 |
if(send(privdata->cmdSocket, "STREAM\n", 7, 0) < 7) {
perror("send");
if(use_syslog) |
4e136199 |
syslog(LOG_ERR, _("send failed to clamd")); |
6489ba59 |
return 0;
}
shutdown(privdata->cmdSocket, SHUT_WR); |
f5d23f2e |
#endif
/*
* Create socket that we'll use to send the data to clamd
*/
if((privdata->dataSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket");
if(use_syslog)
syslog(LOG_ERR, _("failed to create TCPSocket to talk to clamd"));
return 0;
} |
6489ba59 |
|
f5d23f2e |
shutdown(privdata->dataSocket, SHUT_RD);
#ifdef SESSION
nbytes = clamd_recv(cmdSockets[privdata->serverNumber], buf, sizeof(buf));
#else |
6489ba59 |
nbytes = clamd_recv(privdata->cmdSocket, buf, sizeof(buf)); |
f5d23f2e |
#endif |
6489ba59 |
if(nbytes < 0) {
perror("recv");
if(use_syslog) |
4e136199 |
syslog(LOG_ERR, _("recv failed from clamd getting PORT")); |
6489ba59 |
return 0;
}
buf[nbytes] = '\0';
#ifdef CL_DEBUG
if(debug_level >= 4)
cli_dbgmsg("Received: %s", buf);
#endif |
8eb408e5 |
if(sscanf(buf, "PORT %hu\n", &p) != 1) { |
6489ba59 |
if(use_syslog) |
4e136199 |
syslog(LOG_ERR, _("Expected port information from clamd, got '%s'"), |
6489ba59 |
buf);
else |
4e136199 |
cli_warnmsg(_("Expected port information from clamd, got '%s'\n"), |
6489ba59 |
buf); |
63e6694b |
#ifdef SESSION
pthread_mutex_lock(&sstatus_mutex);
cmdSocketsStatus[privdata->serverNumber] = CMDSOCKET_DOWN;
pthread_mutex_unlock(&sstatus_mutex);
#endif |
6489ba59 |
return 0;
}
memset((char *)&reply, 0, sizeof(struct sockaddr_in));
reply.sin_family = AF_INET; |
8eb408e5 |
reply.sin_port = (in_port_t)htons(p); |
6489ba59 |
assert(serverIPs != NULL);
reply.sin_addr.s_addr = serverIPs[freeServer];
#ifdef CL_DEBUG
if(debug_level >= 4) |
4e136199 |
cli_dbgmsg(_("Connecting to local port %d\n"), p); |
6489ba59 |
#endif
if(connect(privdata->dataSocket, (struct sockaddr *)&reply, sizeof(struct sockaddr_in)) < 0) {
perror("connect");
/* 0.4 - use better error message */
if(use_syslog) {
#ifdef HAVE_STRERROR_R
strerror_r(errno, buf, sizeof(buf));
syslog(LOG_ERR, |
4e136199 |
_("Failed to connect to port %d given by clamd: %s"), |
8eb408e5 |
p, buf); |
6489ba59 |
#else |
4e136199 |
syslog(LOG_ERR, _("Failed to connect to port %d given by clamd: %s"), p, strerror(errno)); |
6489ba59 |
#endif
}
return 0;
}
}
/* |
0ac43d02 |
* Combine the To and From into one clamfi_send to save bandwidth
* when sending using TCP/IP to connect to a remote clamd, by band
* width here I mean number of packets |
6489ba59 |
*/ |
0ac43d02 |
length = strlen(privdata->from) + 34; |
6489ba59 |
for(to = privdata->to; *to; to++) |
95ec27d0 |
length += strlen(*to) + 5; |
0ac43d02 |
msg = cli_malloc(length + 1);
if(msg) {
sprintf(msg, "Received: by clamav-milter\nFrom: %s\n",
privdata->from);
for(to = privdata->to; *to; to++) {
char *eom = strchr(msg, '\0');
sprintf(eom, "To: %s\n", *to);
}
if(clamfi_send(privdata, length, msg) != length) {
free(msg); |
6489ba59 |
return 0; |
0ac43d02 |
}
free(msg);
} else {
clamfi_send(privdata, 0,
"Received: by clamav-milter\nFrom: %s\n",
privdata->from);
for(to = privdata->to; *to; to++)
if(clamfi_send(privdata, 0, "To: %s\n", *to) <= 0)
return 0;
} |
6489ba59 |
|
85563fe5 |
cli_dbgmsg("connect2clamd: serverNumber = %d\n", privdata->serverNumber); |
6489ba59 |
return 1;
} |
062dddc9 |
/*
* If possible, check if clamd has died, and report if it has
*/
static void
checkClamd(void)
{
pid_t pid;
int fd, nbytes;
char buf[9];
if(!localSocket) |
ea815cbc |
return; /* communicating via TCP */ |
062dddc9 |
if(pidFile == NULL) |
c857005e |
return; /* PidFile directive missing from clamd.conf */ |
062dddc9 |
fd = open(pidFile, O_RDONLY);
if(fd < 0) {
perror(pidFile);
if(use_syslog) |
4e136199 |
syslog(LOG_ERR, _("Can't open %s"), pidFile); |
062dddc9 |
return;
}
nbytes = read(fd, buf, sizeof(buf) - 1); |
b90c8762 |
if(nbytes < 0)
perror(pidFile);
else
buf[nbytes] = '\0'; |
062dddc9 |
close(fd);
pid = atoi(buf); |
6e945c41 |
if((kill(pid, 0) < 0) && (errno == ESRCH)) {
if(use_syslog) |
4e136199 |
syslog(LOG_ERR, _("Clamd (pid %d) seems to have died"), |
6e945c41 |
pid);
perror("clamd"); |
062dddc9 |
}
} |
1392cf22 |
/*
* Send a templated message about an intercepted message. Very basic for
* now, just to prove it works, will enhance the flexability later, only |
6d113e37 |
* supports %v and $sendmail_variables$ at present. |
063b6c33 |
*
* TODO: more template features
* TODO: allow filename to start with a '|' taken to mean the output of
* a program |
1392cf22 |
*/
static int |
0b79c1b7 |
sendtemplate(SMFICTX *ctx, const char *filename, FILE *sendmail, const char *virusname) |
1392cf22 |
{
FILE *fin = fopen(filename, "r");
struct stat statb; |
6d113e37 |
char *buf, *ptr /* , *ptr2 */; |
95ec27d0 |
struct privdata *privdata = (struct privdata *)smfi_getpriv(ctx); |
1392cf22 |
if(fin == NULL) {
perror(filename);
if(use_syslog) |
4e136199 |
syslog(LOG_ERR, _("Can't open e-mail template file %s"), |
1392cf22 |
filename);
return -1;
}
if(fstat(fileno(fin), &statb) < 0) {
/* File disappeared in race condition? */
perror(filename);
if(use_syslog) |
4e136199 |
syslog(LOG_ERR, _("Can't stat e-mail template file %s"), |
1392cf22 |
filename);
fclose(fin);
return -1;
}
buf = cli_malloc(statb.st_size + 1);
if(buf == NULL) { |
73ffa07e |
fclose(fin); |
1392cf22 |
if(use_syslog) |
4e136199 |
syslog(LOG_ERR, _("Out of memory")); |
1392cf22 |
return -1;
}
fread(buf, sizeof(char), statb.st_size, fin);
fclose(fin);
buf[statb.st_size] = '\0';
|
6d113e37 |
for(ptr = buf; *ptr; ptr++) |
95ec27d0 |
switch(*ptr) {
case '%': /* clamAV variable */
switch(*++ptr) {
case 'v': /* virus name */
fputs(virusname, sendmail);
break;
case '%':
putc('%', sendmail);
break;
case 'h': /* headers */
if(privdata)
header_list_print(privdata->headers, sendmail);
break;
case '\0':
putc('%', sendmail);
--ptr;
continue;
default:
syslog(LOG_ERR, |
4e136199 |
_("%s: Unknown clamAV variable \"%c\"\n"), |
95ec27d0 |
filename, *ptr);
break;
} |
74e42636 |
break; |
95ec27d0 |
case '$': /* sendmail string */ {
const char *val;
char *end = strchr(++ptr, '$'); |
6d113e37 |
|
95ec27d0 |
if(end == NULL) { |
6d113e37 |
syslog(LOG_ERR, |
4e136199 |
_("%s: Unterminated sendmail variable \"%s\"\n"), |
95ec27d0 |
filename, ptr);
continue;
}
*end = '\0';
val = smfi_getsymval(ctx, ptr);
if(val == NULL) {
fputs(ptr, sendmail);
if(use_syslog)
syslog(LOG_ERR, |
4e136199 |
_("%s: Unknown sendmail variable \"%s\"\n"), |
95ec27d0 |
filename, ptr);
} else
fputs(val, sendmail);
ptr = end; |
74e42636 |
break; |
95ec27d0 |
}
case '\\':
if(*++ptr == '\0') {
--ptr;
continue;
}
putc(*ptr, sendmail);
break;
default:
putc(*ptr, sendmail);
} |
1392cf22 |
free(buf);
|
95ec27d0 |
|
6d113e37 |
return 0; |
1392cf22 |
} |
ced83bee |
/* |
79fa8528 |
* Keep the infected file in quarantine, return success (0) or failure
*/
static int
qfile(struct privdata *privdata, const char *virusname)
{ |
0ac43d02 |
char *newname, *ptr; |
d7e0b4e6 |
size_t len; |
79fa8528 |
assert(privdata != NULL);
if((privdata->filename == NULL) || (virusname == NULL))
return -1;
|
d7e0b4e6 |
len = strlen(privdata->filename);
newname = cli_malloc(len + strlen(virusname) + 2); |
79fa8528 |
if(newname == NULL)
return -1;
sprintf(newname, "%s.%s", privdata->filename, virusname); |
0ac43d02 |
/*
* Strip out funnies that may be in the name of the virus, such as '/'
* that would cause the quarantine to fail to save since the name
* of the virus is included in the filename
*/ |
d7e0b4e6 |
for(ptr = &newname[len]; *ptr; ptr++) { |
4e136199 |
#ifdef C_DARWIN
*ptr &= '\177';
#endif |
0ac43d02 |
#if defined(MSDOS) || defined(C_CYGWIN) || defined(WIN32)
if(strchr("/*?<>|\"+=,;: ", *ptr))
#else
if(*ptr == '/')
#endif
*ptr = '_';
} |
79fa8528 |
if(link(privdata->filename, newname) < 0) {
perror(newname);
if(use_syslog) |
4e136199 |
syslog(LOG_WARNING, _("Can't rename %1$s to %2$s"), |
79fa8528 |
privdata->filename, newname);
free(newname);
return -1;
} |
8064efd5 |
unlink(privdata->filename); |
79fa8528 |
free(privdata->filename);
privdata->filename = newname;
return 0;
}
/* |
ced83bee |
* Store the name of the virus in the subject of the e-mail
*/
static void
setsubject(SMFICTX *ctx, const char *virusname)
{
char subject[128];
/* |
79fa8528 |
* FIXME: doesn't work if there's no subject in the email |
ced83bee |
*/ |
4e136199 |
snprintf(subject, sizeof(subject) - 1, _("[Virus] %s"), virusname); |
ced83bee |
smfi_chgheader(ctx, "Subject", 1, subject);
} |
6d113e37 |
/*
* TODO: gethostbyname_r is non-standard so different operating
* systems do it in different ways. Need more examples
*
* Returns 0 for success
*/
static int
clamfi_gethostbyname(const char *hostname, struct hostent *hp, char *buf, size_t len)
{
#if defined(HAVE_GETHOSTBYNAME_R_6)
/* e.g. Linux */
struct hostent *hp2;
int ret;
if((hostname == NULL) || (hp == NULL))
return -1;
if(gethostbyname_r(hostname, hp, buf, len, &hp2, &ret) < 0)
return errno;
#elif defined(HAVE_GETHOSTBYNAME_R_5)
/* e.g. BSD, Solaris, Cygwin */
int ret;
if((hostname == NULL) || (hp == NULL))
return -1;
if(gethostbyname_r(hostname, hp, buf, len, &ret) == NULL)
return errno;
#elif defined(HAVE_GETHOSTBYNAME_R_3)
/* e.g. HP/UX, AIX */
if((hostname == NULL) || (hp == NULL))
return -1;
if(gethostbyname_r(hostname, &hp, (struct hostent_data *)buf) < 0)
return errno;
#else
/* Single thread the code */
struct hostent *hp2;
static pthread_mutex_t hostent_mutex = PTHREAD_MUTEX_INITIALIZER;
if((hostname == NULL) || (hp == NULL))
return -1;
pthread_mutex_lock(&hostent_mutex);
if((hp2 = gethostbyname(hostname)) == NULL) {
pthread_mutex_unlock(&hostent_mutex);
return errno;
}
memcpy(hp, hp2, sizeof(struct hostent));
pthread_mutex_unlock(&hostent_mutex);
#endif
return 0;
} |
503b2956 |
/*
* David Champion <dgc@uchicago.edu>
*
* Check whether addr is on network by applying netmasks.
* addr must be a 32-bit integer-packed IPv4 address in network order.
* For example: |
d7e0b4e6 |
* struct in_addr IPAddress;
* isLocal = isLocalAddr(IPAddress.s_addr); |
503b2956 |
*/
static int
isLocalAddr(in_addr_t addr)
{
const struct cidr_net *net;
for(net = localNets; net->base; net++)
if(htonl(net->base & net->mask) == (addr & htonl(net->mask)))
return 1;
return 0; /* is non-local */
} |
85563fe5 |
/*
* Can't connect to any clamd server. This is serious, we need to inform
* someone. In the absence of SNMP the best way is by e-mail. We
* don't want to flood so there's a need to restrict to
* no more than say one message every 15 minutes
*/
static void
clamdIsDown(void)
{
static time_t lasttime;
time_t thistime, diff;
static pthread_mutex_t time_mutex = PTHREAD_MUTEX_INITIALIZER;
cli_errmsg(_("No response from any clamd server - your AV system is not scanning emails\n"));
if(use_syslog)
syslog(LOG_ERR, _("No response from any clamd server - your AV system is not scanning emails"));
time(&thistime);
pthread_mutex_lock(&time_mutex);
diff = thistime - lasttime;
pthread_mutex_unlock(&time_mutex);
if(diff >= (time_t)(15 * 60)) {
char cmd[128];
FILE *sendmail;
snprintf(cmd, sizeof(cmd) - 1, "%s -t", SENDMAIL_BIN);
sendmail = popen(cmd, "w");
if(sendmail) {
fprintf(sendmail, "To: %s\n", postmaster);
fprintf(sendmail, "From: %s\n", postmaster);
fputs(_("Subject: ClamAV Down\n"), sendmail);
fputs("Priority: High\n\n", sendmail);
fputs(_("This is an automatic message\n\n"), sendmail);
if(numServers == 1)
fputs(_("The clamd program cannot be contacted.\n"), sendmail);
else
fputs(_("No clamd server can be contacted.\n"), sendmail);
fputs(_("Emails may not be being scanned, please check your servers.\n"), sendmail);
if(pclose(sendmail) == 0) {
pthread_mutex_lock(&time_mutex);
time(&lasttime);
pthread_mutex_unlock(&time_mutex);
}
}
}
}
#ifdef SESSION
/*
* Thread to monitor the links to clamd sessions. Any marked as being in
* an error state because of previous I/O errors are restarted, and a heartbeat
* is sent the others
*/
static void *
watchdog(void *a)
{
static pthread_mutex_t watchdog_mutex = PTHREAD_MUTEX_INITIALIZER;
|
41190088 |
assert(cmdSockets != NULL);
|
78a78fcc |
while(!quitting) { |
85563fe5 |
int i;
struct timespec ts;
struct timeval tp;
gettimeofday(&tp, NULL); |
41190088 |
/*
* How often (in seconds) to try to fix broken clamd sessions.
* We may try more often than this e.g. when we're idle or all
* connections are down, so you can put this figure quite high. |
74e42636 |
* But can't be too high because with older clamd SESSION
* didn't remain open if no data goes down for ReadTimeout
* seconds |
41190088 |
*/
ts.tv_sec = tp.tv_sec + readTimeout - 1; |
85563fe5 |
ts.tv_nsec = tp.tv_usec * 1000;
cli_dbgmsg("watchdog sleeps\n"); |
41190088 |
pthread_mutex_lock(&watchdog_mutex);
/*
* Sometimes this returns EPIPE which isn't listed as a
* return value in the Linux man page for pthread_cond_timedwait
* so I'm not sure why it happens
*/
switch(pthread_cond_timedwait(&watchdog_cond, &watchdog_mutex, &ts)) {
case ETIMEDOUT:
case 0:
break;
default:
perror("pthread_cond_timedwait");
} |
85563fe5 |
cli_dbgmsg("watchdog wakes\n"); |
41190088 |
pthread_mutex_unlock(&watchdog_mutex); |
85563fe5 |
pthread_mutex_lock(&sstatus_mutex);
for(i = 0; i < max_children; i++) {
const int sock = cmdSockets[i];
/*
* Check all free sessions are still usable
* This could take some time with many free
* sessions to slow remote servers, so only do this
* when the system is quiet (not 100% accurate when
* determining this since n_children isn't locked but
* that doesn't really matter)
*/
cli_dbgmsg("watchdog: check server %d\n", i);
if((n_children == 0) && (cmdSocketsStatus[i] == CMDSOCKET_FREE)) {
if(send(sock, "PING\n", 5, 0) == 5) {
char buf[6];
buf[5] = '\0';
if(clamd_recv(sock, buf, 5) != 5)
cmdSocketsStatus[i] = CMDSOCKET_DOWN; |
41190088 |
else if(strcmp(buf, "PONG\n") != 0) {
cli_dbgmsg("watchdog: expected \"PONG\", got \"%s\"\n", buf); |
85563fe5 |
cmdSocketsStatus[i] = CMDSOCKET_DOWN; |
41190088 |
}
} else {
perror("send"); |
85563fe5 |
cmdSocketsStatus[i] = CMDSOCKET_DOWN; |
41190088 |
} |
85563fe5 |
if(cmdSocketsStatus[i] == CMDSOCKET_DOWN)
cli_warnmsg("Session %d has gone down\n", i);
}
/*
* Reset all all dead sessions
*/
if(cmdSocketsStatus[i] == CMDSOCKET_DOWN) {
/*
* The END command probably won't get through,
* but let's give it a go anyway
*/
send(sock, "END\n", 4, 0);
close(sock);
cli_dbgmsg("Trying to restart session %d\n", i);
if(createSession(i) == 0) {
cmdSocketsStatus[i] = CMDSOCKET_FREE;
cli_warnmsg("Session %d restarted OK\n", i);
}
}
}
for(i = 0; i < max_children; i++)
if(cmdSocketsStatus[i] != CMDSOCKET_DOWN)
break;
if(i == max_children)
clamdIsDown();
pthread_mutex_unlock(&sstatus_mutex);
} |
41190088 |
cli_errmsg("watchdog quits\n"); |
85563fe5 |
return NULL;
}
#endif |
d7e0b4e6 |
static const struct {
const char *name;
int code;
} facilitymap[] = {
#ifdef LOG_AUTH
{ "LOG_AUTH", LOG_AUTH },
#endif
#ifdef LOG_AUTHPRIV
{ "LOG_AUTHPRIV", LOG_AUTHPRIV },
#endif
#ifdef LOG_CRON
{ "LOG_CRON", LOG_CRON },
#endif
#ifdef LOG_DAEMON
{ "LOG_DAEMON", LOG_DAEMON },
#endif
#ifdef LOG_FTP
{ "LOG_FTP", LOG_FTP },
#endif
#ifdef LOG_KERN
{ "LOG_KERN", LOG_KERN },
#endif
#ifdef LOG_LPR
{ "LOG_LPR", LOG_LPR },
#endif
#ifdef LOG_MAIL
{ "LOG_MAIL", LOG_MAIL },
#endif
#ifdef LOG_NEWS
{ "LOG_NEWS", LOG_NEWS },
#endif
#ifdef LOG_AUTH
{ "LOG_AUTH", LOG_AUTH },
#endif
#ifdef LOG_SYSLOG
{ "LOG_SYSLOG", LOG_SYSLOG },
#endif
#ifdef LOG_USER
{ "LOG_USER", LOG_USER },
#endif
#ifdef LOG_UUCP
{ "LOG_UUCP", LOG_UUCP },
#endif
#ifdef LOG_LOCAL0
{ "LOG_LOCAL0", LOG_LOCAL0 },
#endif
#ifdef LOG_LOCAL1
{ "LOG_LOCAL1", LOG_LOCAL1 },
#endif
#ifdef LOG_LOCAL2
{ "LOG_LOCAL2", LOG_LOCAL2 },
#endif
#ifdef LOG_LOCAL3
{ "LOG_LOCAL3", LOG_LOCAL3 },
#endif
#ifdef LOG_LOCAL4
{ "LOG_LOCAL4", LOG_LOCAL4 },
#endif
#ifdef LOG_LOCAL5
{ "LOG_LOCAL5", LOG_LOCAL5 },
#endif
#ifdef LOG_LOCAL6
{ "LOG_LOCAL6", LOG_LOCAL6 },
#endif
#ifdef LOG_LOCAL7
{ "LOG_LOCAL7", LOG_LOCAL7 },
#endif
{ NULL, -1 }
};
static int
logg_facility(const char *name)
{
int i;
for(i = 0; facilitymap[i].name; i++)
if(strcasecmp(facilitymap[i].name, name) == 0)
return facilitymap[i].code;
return -1;
} |
78a78fcc |
static void
quit(void)
{
#ifdef SESSION
int i;
quitting++;
if(use_syslog) |
3eacc10d |
syslog(LOG_INFO, _("Stopping %s"), clamav_version); |
78a78fcc |
pthread_mutex_lock(&sstatus_mutex);
for(i = 0; i < max_children; i++) {
const int sock = cmdSockets[i];
/*
* Check all free sessions are still usable
* This could take some time with many free
* sessions to slow remote servers, so only do this
* when the system is quiet (not 100% accurate when
* determining this since n_children isn't locked but
* that doesn't really matter)
*/
cli_dbgmsg("quit: close server %d\n", i);
if(cmdSocketsStatus[i] == CMDSOCKET_FREE) {
send(sock, "END\n", 4, 0);
shutdown(sock, SHUT_WR);
cmdSocketsStatus[i] = CMDSOCKET_DOWN;
pthread_mutex_unlock(&sstatus_mutex);
close(sock);
pthread_mutex_lock(&sstatus_mutex);
}
}
pthread_mutex_unlock(&sstatus_mutex);
#else
quitting++;
if(use_syslog) |
e5bb3b37 |
syslog(LOG_INFO, _("Stopping %s"), clamav_version); |
78a78fcc |
#endif |
3eacc10d |
broadcast(_("Stopping clamav-milter")); |
78a78fcc |
}
static void
broadcast(const char *mess)
{
struct sockaddr_in s;
|
3eacc10d |
if(broadcastSock < 0)
return;
|
78a78fcc |
memset(&s, '\0', sizeof(struct sockaddr_in));
s.sin_family = AF_INET;
s.sin_port = (in_port_t)htons(tcpSocket);
s.sin_addr.s_addr = htonl(INADDR_BROADCAST);
|
e5bb3b37 |
if(sendto(broadcastSock, mess, strlen(mess), 0, (struct sockaddr *)&s, sizeof(struct sockaddr_in)) < 0) |
78a78fcc |
perror("sendto");
} |