diff options
-rw-r--r-- | AUTHORS | 12 | ||||
-rw-r--r-- | COPYING | 31 | ||||
-rw-r--r-- | ChangeLog | 2 | ||||
-rw-r--r-- | Makefile.am | 7 | ||||
-rw-r--r-- | NEWS | 1 | ||||
-rw-r--r-- | README | 5 | ||||
-rw-r--r-- | acsite.m4 | 200 | ||||
-rw-r--r-- | configure.in | 109 | ||||
-rw-r--r-- | src/Makefile.am | 10 | ||||
-rw-r--r-- | src/proxsmtpd.c | 610 | ||||
-rw-r--r-- | src/proxsmtpd.h | 43 |
11 files changed, 1029 insertions, 1 deletions
@@ -1 +1,11 @@ -nielsen@memberwebs.com +AUTHOR: +Nate Nielsen <nielsen@memberwebs.com> + +CONTRIBUTORS: +Andreas Steinmetz <ast@domdv.de> + +PATCHES: +Berk D. Demir <demir@meteksan.net.tr> +João Carlos Mendes Luís <jcmendes@int.gov.br> +Jasper Slits <jasper@insiders.nl> +Yamamoto Takao <takao@oakat.org> @@ -0,0 +1,31 @@ + +Copyright (c) 2004, Nate Nielsen +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + * Redistributions in binary form must reproduce the + above copyright notice, this list of conditions and + the following disclaimer in the documentation and/or + other materials provided with the distribution. + * The names of contributors to this software may not be + used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..383ffce --- /dev/null +++ b/ChangeLog @@ -0,0 +1,2 @@ +0.5 + - Initial fork from clamsmtp version 0.9 diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..33d8e51 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,7 @@ + +EXTRA_DIST = config.sub acsite.m4 config.guess scripts common +SUBDIRS = src doc + +dist-hook: + rm -rf `find $(distdir)/ -name CVS` + @@ -0,0 +1 @@ +See ChangeLog
\ No newline at end of file @@ -0,0 +1,5 @@ +================================================================= + PROXSMTP README + +See the website: +http://memberwebs.com/nielsen/software/proxsmtp/ diff --git a/acsite.m4 b/acsite.m4 new file mode 100644 index 0000000..e8cdb22 --- /dev/null +++ b/acsite.m4 @@ -0,0 +1,200 @@ +dnl Available from the GNU Autoconf Macro Archive at: +dnl http://www.gnu.org/software/ac-archive/htmldoc/acx_pthread.html +dnl +AC_DEFUN([ACX_PTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +AC_LANG_SAVE +AC_LANG_C +acx_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on True64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) + AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes) + AC_MSG_RESULT($acx_pthread_ok) + if test x"$acx_pthread_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all, and "pthread-config" +# which is a program returning the flags for the Pth emulation library. + +acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) + +case "${host_cpu}-${host_os}" in + *solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthread or + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + + acx_pthread_flags="-pthread -pthreads pthread -mt $acx_pthread_flags" + ;; +esac + +if test x"$acx_pthread_ok" = xno; then +for flag in $acx_pthread_flags; do + + case $flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $flag]) + PTHREAD_CFLAGS="$flag" + ;; + + pthread-config) + AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no) + if test x"$acx_pthread_config" = xno; then continue; fi + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$flag]) + PTHREAD_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + save_CFLAGS="$CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + AC_TRY_LINK([#include <pthread.h>], + [pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], + [acx_pthread_ok=yes]) + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + AC_MSG_RESULT($acx_pthread_ok) + if test "x$acx_pthread_ok" = xyes; then + break; + fi + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$acx_pthread_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Detect AIX lossage: threads are created detached by default + # and the JOINABLE attribute has a nonstandard name (UNDETACHED). + AC_MSG_CHECKING([for joinable pthread attribute]) + AC_TRY_LINK([#include <pthread.h>], + [int attr=PTHREAD_CREATE_JOINABLE;], + ok=PTHREAD_CREATE_JOINABLE, ok=unknown) + if test x"$ok" = xunknown; then + AC_TRY_LINK([#include <pthread.h>], + [int attr=PTHREAD_CREATE_UNDETACHED;], + ok=PTHREAD_CREATE_UNDETACHED, ok=unknown) + fi + if test x"$ok" != xPTHREAD_CREATE_JOINABLE; then + AC_DEFINE(PTHREAD_CREATE_JOINABLE, $ok, + [Define to the necessary symbol if this constant + uses a non-standard name on your system.]) + fi + AC_MSG_RESULT(${ok}) + if test x"$ok" = xunknown; then + AC_MSG_WARN([we do not know how to create joinable pthreads]) + fi + + AC_MSG_CHECKING([if more special flags are required for pthreads]) + flag=no + case "${host_cpu}-${host_os}" in + *-aix* | *-freebsd*) flag="-D_THREAD_SAFE";; + *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";; + esac + AC_MSG_RESULT(${flag}) + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + # More AIX lossage: must compile with cc_r + AC_CHECK_PROG(PTHREAD_CC, cc_r, cc_r, ${CC}) +else + PTHREAD_CC="$CC" +fi + +AC_SUBST(PTHREAD_LIBS) +AC_SUBST(PTHREAD_CFLAGS) +AC_SUBST(PTHREAD_CC) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$acx_pthread_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) + : +else + acx_pthread_ok=no + $2 +fi +AC_LANG_RESTORE +])dnl ACX_PTHREAD + diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..018132b --- /dev/null +++ b/configure.in @@ -0,0 +1,109 @@ +dnl +dnl Copyright (c) 2004, Nate Nielsen +dnl All rights reserved. +dnl +dnl Redistribution and use in source and binary forms, with or without +dnl modification, are permitted provided that the following conditions +dnl are met: +dnl +dnl * Redistributions of source code must retain the above +dnl copyright notice, this list of conditions and the +dnl following disclaimer. +dnl * Redistributions in binary form must reproduce the +dnl above copyright notice, this list of conditions and +dnl the following disclaimer in the documentation and/or +dnl other materials provided with the distribution. +dnl * The names of contributors to this software may not be +dnl used to endorse or promote products derived from this +dnl software without specific prior written permission. +dnl +dnl THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +dnl "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +dnl LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +dnl FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +dnl COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +dnl INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +dnl BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +dnl OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +dnl AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +dnl OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +dnl THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +dnl DAMAGE. +dnl +dnl +dnl CONTRIBUTORS +dnl Nate Nielsen <nielsen@memberwebs.com> +dnl + +dnl Process this file with autoconf to produce a configure script. +AC_INIT(proxsmtp, 0.4.90, nielsen@memberwebs.com) +AM_INIT_AUTOMAKE(proxsmtp, 0.4.90) + +LDFLAGS="$LDFLAGS -L/usr/local/lib" +CFLAGS="$CFLAGS -I/usr/local/include" + +AC_CONFIG_SRCDIR([src/proxsmtpd.c]) +AM_CONFIG_HEADER([config.h]) + +# Checks for programs. +AC_PROG_CC +AC_PROG_INSTALL +AC_PROG_LN_S +AC_PROG_MAKE_SET + +# Debug mode +AC_ARG_ENABLE(debug, + AC_HELP_STRING([--enable-debug], + [Compile binaries in debug mode])) + +if test "$enable_debug" = "yes"; then + CFLAGS="$CFLAGS -g -O0 -Wall" + AC_DEFINE_UNQUOTED(_DEBUG, 1, [In debug mode]) + echo "enabling debug compile mode" +fi + +# TODO: Figure out why we need this wierd hack +ACX_PTHREAD( , [echo "ERROR: Pthread support not found."; exit 1] ) + +LIBS="$PTHREAD_LIBS $LIBS" +CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + +# Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS(limits.h,,) +AC_CHECK_HEADERS([unistd.h stdio.h stddef.h fcntl.h stdlib.h assert.h errno.h stdarg.h err.h string.h], , + [echo "ERROR: Required C header missing"; exit 1]) + +# Check for linux type transparent proxy support +AC_CHECK_HEADERS([linux/netfilter_ipv4.h], + AC_DEFINE(LINUX_TRANSPARENT_PROXY, 1, [Whether the system supports a linux type transparent proxy]), + , + [[ + #ifdef HAVE_LIMITS_H + #include <limits.h> + #endif + ]] ) + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_SIZE_T + +# We use error checking mutexes whenever possible +AC_CHECK_DECL(PTHREAD_MUTEX_ERRORCHECK_NP, [AC_DEFINE(HAVE_ERR_MUTEX, 1, "Error Mutex Type")], + [AC_CHECK_DECL(PTHREAD_MUTEX_ERRORCHECK, [AC_DEFINE(HAVE_ERR_MUTEX, 2)], , + [ #include <pthread.h> ])], [ #include <pthread.h> ]) + +# Required Functions +AC_CHECK_FUNCS([memset strerror malloc realloc getopt strchr tolower getaddrinfo], , + [echo "ERROR: Required function missing"; exit 1]) +AC_CHECK_FUNCS([strlwr strlcat strlcpy strncat strncpy]) + +# Have to resolve this for the path below +if test "${prefix}" = "NONE"; then + prefix=$ac_default_prefix +fi + +AC_DEFINE_UNQUOTED(CONF_PREFIX, "`eval echo ${sysconfdir}`", [Installation Prefix] ) + +AC_CONFIG_FILES([Makefile src/Makefile doc/Makefile]) +AC_OUTPUT diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..208ae67 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,10 @@ + +sbin_PROGRAMS = proxsmtpd + +proxsmtpd_SOURCES = proxsmtpd.c proxsmtpd.h \ + ../common/spio.c ../common/smtppass.c ../common/smtppass.h ../common/sppriv.h \ + ../common/stringx.c ../common/stringx.h ../common/sock_any.c ../common/sock_any.h \ + ../common/usuals.h ../common/compat.c ../common/compat.h + +proxsmtpd_CFLAGS = -I${top_srcdir}/common/ -I${top_srcdir}/ + diff --git a/src/proxsmtpd.c b/src/proxsmtpd.c new file mode 100644 index 0000000..897faa9 --- /dev/null +++ b/src/proxsmtpd.c @@ -0,0 +1,610 @@ +/* + * Copyright (c) 2004, Nate Nielsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * + * CONTRIBUTORS + * Nate Nielsen <nielsen@memberwebs.com> + */ + +#include <sys/types.h> +#include <sys/param.h> + +#include <paths.h> +#include <ctype.h> +#include <stdio.h> +#include <unistd.h> +#include <syslog.h> +#include <errno.h> +#include <err.h> + +#include "usuals.h" + +#include "compat.h" +#include "sock_any.h" +#include "stringx.h" +#include "smtppass.h" + +/* ----------------------------------------------------------------------- + * STRUCTURES + */ + +typedef struct pxstate +{ + /* Settings ------------------------------- */ + const char* command; /* The command to pipe email through */ + /* TODO: Timeout for above command */ + const char* directory; /* The directory for temp files */ + int quarantine; /* Leave failed files in temp dir */ + int debug_files; /* Leave all files in temp dir */ +} +pxstate_t; + +/* ----------------------------------------------------------------------- + * STRINGS + */ + +#define CRLF "\r\n" + +XXXXXXXXXXXXXXXXXxx +#define SMTP_DATAVIRUSOK "250 Virus Detected; Discarded Email" CRLF +#define SMTP_DATAVIRUS "550 Virus Detected; Content Rejected" CRLF + +#define DEFAULT_CONFIG CONF_PREFIX "/proxsmtpd.conf" + +#define CFG_FILTER "FilterCommand" +#define CFG_DIRECTORY "TempDirectory" +#define CFG_QUARANTINE "Quarantine" +#define CFG_DEBUGFILES "DebugFiles" + +/* ----------------------------------------------------------------------- + * GLOBALS + */ + +clstate_t g_clstate; + +/* ----------------------------------------------------------------------- + * FORWARD DECLARATIONS + */ + +static void usage(); +static int connect_clam(clctx_t* ctx); +static int disconnect_clam(clctx_t* ctx); +static int quarantine_virus(clctx_t* ctx); +static int transfer_to_cache(clctx_t* ctx); +static int clam_scan_file(clctx_t* ctx); + +/* ----------------------------------------------------------------------- + * SIMPLE MACROS + */ + +/* ---------------------------------------------------------------------------------- + * STARTUP ETC... + */ + +int main(int argc, char* argv[]) +{ + const char* configfile = DEFAULT_CONFIG; + const char* pidfile = NULL; + int dbg_level = -1; + int warnargs = 0; + int ch = 0; + int r; + char* t; + + /* Setup some defaults */ + memset(&g_clstate, 0, sizeof(g_clstate)); + g_clstate.header = DEFAULT_HEADER; + g_clstate.directory = _PATH_TMP; + + /* We need the default to parse into a useable form, so we do this: */ + r = cb_parse_option(CFG_CLAMADDR, DEFAULT_CLAMAV); + ASSERT(r == 1); + + sp_init("clamsmtpd"); + + /* + * We still accept our old arguments for compatibility reasons. + * We fill them into the spstate structure directly + */ + + /* Parse the arguments nicely */ + while((ch = getopt(argc, argv, "bc:d:D:f:h:l:m:p:qt:v")) != -1) + { + switch(ch) + { + /* COMPAT: Actively reject messages */ + case 'b': + if((r = cb_parse_option(CFG_BOUNCE, "on")) < 0) + usage(); + ASSERT(r == 1); + warnargs = 1; + break; + + /* COMPAT: Change the CLAM socket */ + case 'c': + if((r = cb_parse_option(CFG_CLAMADDR, "on")) < 0) + usage(); + ASSERT(r == 1); + warnargs = 1; + break; + + /* Don't daemonize */ + case 'd': + dbg_level = strtol(optarg, &t, 10); + if(*t) /* parse error */ + errx(1, "invalid debug log level"); + dbg_level += LOG_ERR; + break; + + /* COMPAT: The directory for the files */ + case 'D': + if((r = sp_parse_option(CFG_DIRECTORY, optarg)) < 0) + usage(); + ASSERT(r == 1); + warnargs = 1; + break; + + /* The configuration file */ + case 'f': + configfile = optarg; + break; + + /* COMPAT: The header to add */ + case 'h': + if((r = cb_parse_option(CFG_HEADER, optarg)) < 0) + usage(); + ASSERT(r == 1); + warnargs = 1; + break; + + /* COMPAT: Change our listening port */ + case 'l': + if((r = sp_parse_option("Listen", optarg)) < 0) + usage(); + ASSERT(r == 1); + warnargs = 1; + break; + + /* COMPAT: The maximum number of threads */ + case 'm': + if((r = sp_parse_option("MaxConnections", optarg)) < 0) + usage(); + ASSERT(r == 1); + warnargs = 1; + break; + + /* Write out a pid file */ + case 'p': + pidfile = optarg; + break; + + /* COMPAT: The timeout */ + case 't': + if((r = sp_parse_option("TimeOut", optarg)) < 0) + usage(); + ASSERT(r == 1); + warnargs = 1; + break; + + /* COMPAT: Leave virus files in directory */ + case 'q': + if((r = cb_parse_option(CFG_QUARANTINE, "on")) < 0) + usage(); + ASSERT(r == 1); + warnargs = 1; + break; + + /* Print version number */ + case 'v': + printf("clamsmtpd (version %s)\n", VERSION); + printf(" (config: %s)\n", DEFAULT_CONFIG); + exit(0); + break; + + /* COMPAT: Leave all files in the tmp directory */ + case 'X': + if((r = cb_parse_option(CFG_DEBUGFILES, "on")) < 0) + usage(); + ASSERT(r == 1); + warnargs = 1; + break; + + /* Usage information */ + case '?': + default: + usage(); + break; + } + } + + argc -= optind; + argv += optind; + + if(argc > 1) + usage(); + if(argc == 1) + { + /* COMPAT: The out address */ + if((r = sp_parse_option("OutAddress", argv[0])) < 0) + usage(); + ASSERT(r == 1); + warnargs = 1; + } + + if(warnargs) + warnx("please use configuration file instead of command-line flags: %s", configfile); + + r = sp_run(configfile, pidfile, dbg_level); + + sp_done(); + + return r; +} + +static void usage() +{ + fprintf(stderr, "usage: clamsmtpd [-d debuglevel] [-f configfile] [-p pidfile]\n"); + fprintf(stderr, " clamsmtpd -v\n"); + exit(2); +} + +/* ---------------------------------------------------------------------------------- + * SP CALLBACKS + */ + +int cb_check_data(spctx_t* sp) +{ + int r = 0; + clctx_t* ctx = (clctx_t*)sp; + + /* Connect to clamav */ + if(!spio_valid(&(ctx->clam))) + r = connect_clam(ctx); + + /* transfer_to_cache */ + if(r != -1 && (r = transfer_to_cache(ctx)) > 0) + + /* ClamAV doesn't like empty files */ + r = clam_scan_file(ctx); + + switch(r) + { + + /* + * There was an error tell the client. We haven't notified + * the server about any of this yet + */ + case -1: + if(sp_fail_data(sp, NULL) == -1) + return -1; + break; + + /* + * No virus was found. Now we initiate a connection to the server + * and transfer the file to it. + */ + case 0: + if(sp_done_data(sp, g_clstate.header) == -1) + return -1; + break; + + /* + * A virus was found, normally we just drop the email. But if + * requested we can send a simple message back to our client. + * The server doesn't know data was ever sent, and the client can + * choose to reset the connection to reuse it if it wants. + */ + case 1: + /* Any special post operation actions on the virus */ + quarantine_virus(ctx); + + if(sp_fail_data(sp, g_clstate.bounce ? + SMTP_DATAVIRUS : SMTP_DATAVIRUSOK) == -1) + return -1; + break; + + default: + ASSERT(0 && "Invalid clam_scan_file return value"); + break; + }; + + return 0; +} + +int cb_parse_option(const char* name, const char* value) +{ + if(strcasecmp(CFG_CLAMADDR, name) == 0) + { + if(sock_any_pton(value, &(g_clstate.clamaddr), SANY_OPT_DEFLOCAL) == -1) + errx(2, "invalid " CFG_CLAMADDR " socket name: %s", value); + g_clstate.clamname = value; + return 1; + } + + else if(strcasecmp(CFG_HEADER, name) == 0) + { + g_clstate.header = (const char*)trim_space((char*)value); + + if(strlen(g_clstate.header) == 0) + g_clstate.header = NULL; + + return 1; + } + + else if(strcasecmp(CFG_DIRECTORY, name) == 0) + { + g_clstate.directory = value; + return 1; + } + + else if(strcasecmp(CFG_BOUNCE, name) == 0) + { + if((g_clstate.bounce = strtob(value)) == -1) + errx(2, "invalid value for " CFG_BOUNCE); + return 1; + } + + else if(strcasecmp(CFG_QUARANTINE, name) == 0) + { + if((g_clstate.quarantine = strtob(value)) == -1) + errx(2, "invalid value for " CFG_BOUNCE); + return 1; + } + + else if(strcasecmp(CFG_DEBUGFILES, name) == 0) + { + if((g_clstate.debug_files = strtob(value)) == -1) + errx(2, "invalid value for " CFG_DEBUGFILES); + return 1; + } + + return 0; +} + +spctx_t* cb_new_context() +{ + clctx_t* ctx = (clctx_t*)calloc(1, sizeof(clctx_t)); + if(!ctx) + { + sp_messagex(NULL, LOG_CRIT, "out of memory"); + return NULL; + } + + /* Initial preparation of the structure */ + spio_init(&(ctx->clam), "CLAMAV"); + return &(ctx->sp); +} + +void cb_del_context(spctx_t* sp) +{ + clctx_t* ctx = (clctx_t*)sp; + ASSERT(sp); + + disconnect_clam(ctx); + free(ctx); +} + +/* ---------------------------------------------------------------------------------- + * CLAM AV + */ + +static int connect_clam(clctx_t* ctx) +{ + int ret = 0; + spctx_t* sp = &(ctx->sp); + + ASSERT(ctx); + ASSERT(!spio_valid(&(ctx->clam))); + + if(spio_connect(sp, &(ctx->clam), &(g_clstate.clamaddr), g_clstate.clamname) == -1) + RETURN(-1); + + spio_read_junk(sp, &(ctx->clam)); + + /* Send a session and a check header to ClamAV */ + + if(spio_write_data(sp, &(ctx->clam), "SESSION\n") == -1) + RETURN(-1); + + spio_read_junk(sp, &(ctx->clam)); + +/* + if(spio_write_data(sp, &(ctx->clam), "PING\n") == -1 || + spio_read_line(sp, &(ctx->clam), CLIO_DISCARD | CLIO_TRIM) == -1) + RETURN(-1); + + if(strcmp(sp->line, CONNECT_RESPONSE) != 0) + { + sp_message(sp, LOG_ERR, "clamd sent an unexpected response: %s", ctx->line); + RETURN(-1); + } +*/ + +cleanup: + + if(ret < 0) + spio_disconnect(sp, &(ctx->clam)); + + return ret; +} + +static int disconnect_clam(clctx_t* ctx) +{ + spctx_t* sp = &(ctx->sp); + + if(!spio_valid(&(ctx->clam))) + return 0; + + if(spio_write_data(sp, &(ctx->clam), CLAM_DISCONNECT) != -1) + spio_read_junk(sp, &(ctx->clam)); + + spio_disconnect(sp, &(ctx->clam)); + return 0; +} + +static int clam_scan_file(clctx_t* ctx) +{ + int len; + spctx_t* sp = &(ctx->sp); + + /* Needs to be long enough to hold path names */ + ASSERT(SP_LINE_LENGTH > MAXPATHLEN + 32); + + strcpy(sp->line, CLAM_SCAN); + strcat(sp->line, sp->cachename); + strcat(sp->line, "\n"); + + if(spio_write_data(sp, &(ctx->clam), sp->line) == -1) + return -1; + + len = spio_read_line(sp, &(ctx->clam), SPIO_DISCARD | SPIO_TRIM); + if(len == 0) + { + sp_messagex(sp, LOG_ERR, "clamd disconnected unexpectedly"); + return -1; + } + + if(is_last_word(sp->line, CLAM_OK, KL(CLAM_OK))) + { + sp_add_log(sp, "status=", "CLEAN"); + sp_messagex(sp, LOG_DEBUG, "no virus"); + return 0; + } + + if(is_last_word(sp->line, CLAM_FOUND, KL(CLAM_FOUND))) + { + len = strlen(sp->cachename); + + if(sp->linelen > len) + sp_add_log(sp, "status=VIRUS:", sp->line + len + 1); + else + sp_add_log(sp, "status=", "VIRUS"); + + sp_messagex(sp, LOG_DEBUG, "found virus"); + return 1; + } + + if(is_last_word(sp->line, CLAM_ERROR, KL(CLAM_ERROR))) + { + sp_messagex(sp, LOG_ERR, "clamav error: %s", sp->line); + sp_add_log(sp, "status=", "CLAMAV-ERROR"); + return -1; + } + + sp_add_log(sp, "status=", "CLAMAV-ERROR"); + sp_messagex(sp, LOG_ERR, "unexepected response from clamd: %s", sp->line); + return -1; +} + +/* ---------------------------------------------------------------------------------- + * TEMP FILE HANDLING + */ + +static int quarantine_virus(clctx_t* ctx) +{ + char buf[MAXPATHLEN]; + spctx_t* sp = &(ctx->sp); + char* t; + + if(!g_clstate.quarantine) + return 0; + + strlcpy(buf, g_clstate.directory, MAXPATHLEN); + strlcat(buf, "/virus.", MAXPATHLEN); + + /* Points to null terminator */ + t = buf + strlen(buf); + + /* + * Yes, I know we're using mktemp. And yet we're doing it in + * a safe manner due to the link command below not overwriting + * existing files. + */ + for(;;) + { + /* Null terminate off the ending, and replace with X's for mktemp */ + *t = 0; + strlcat(buf, "XXXXXX", MAXPATHLEN); + + if(!mktemp(buf)) + { + sp_message(sp, LOG_ERR, "couldn't create quarantine file name"); + return -1; + } + + /* Try to link the file over to the temp */ + if(link(sp->cachename, buf) == -1) + { + /* We don't want to allow race conditions */ + if(errno == EEXIST) + { + sp_message(sp, LOG_WARNING, "race condition when quarantining virus file: %s", buf); + continue; + } + + sp_message(sp, LOG_ERR, "couldn't quarantine virus file"); + return -1; + } + + break; + } + + sp_messagex(sp, LOG_INFO, "quarantined virus file as: %s", buf); + return 0; +} + +static int transfer_to_cache(clctx_t* ctx) +{ + spctx_t* sp = &(ctx->sp); + int r, count = 0; + const char* data; + + while((r = sp_read_data(sp, &data)) != 0) + { + if(r < 0) + return -1; /* Message already printed */ + + count += r; + + if((r = sp_write_data(sp, data, r)) < 0) + return -1; /* Message already printed */ + } + + /* End the caching */ + if(sp_write_data(sp, NULL, 0) < 0) + return -1; + + sp_messagex(sp, LOG_DEBUG, "wrote %d bytes to temp file", count); + return count; +} + + diff --git a/src/proxsmtpd.h b/src/proxsmtpd.h new file mode 100644 index 0000000..06b2412 --- /dev/null +++ b/src/proxsmtpd.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2004, Nate Nielsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * + * CONTRIBUTORS + * Nate Nielsen <nielsen@memberwebs.com> + * + */ + +#ifndef __PROXSMTPD_H__ +#define __PROXSMTPD_H__ + + +#endif /* __PROXSMTPD_H__ */ |