From 37f56600adb6f38a030a926a6201067e7f0bf710 Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Mon, 13 Sep 2004 17:56:15 +0000 Subject: Initial fork from clamsmtp --- AUTHORS | 12 +- COPYING | 31 +++ ChangeLog | 2 + Makefile.am | 7 + NEWS | 1 + README | 5 + acsite.m4 | 200 +++++++++++++++++++ configure.in | 109 ++++++++++ src/Makefile.am | 10 + src/proxsmtpd.c | 610 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/proxsmtpd.h | 43 ++++ 11 files changed, 1029 insertions(+), 1 deletion(-) create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100644 acsite.m4 create mode 100644 configure.in create mode 100644 src/Makefile.am create mode 100644 src/proxsmtpd.c create mode 100644 src/proxsmtpd.h diff --git a/AUTHORS b/AUTHORS index d2e76d6..a80252d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1 +1,11 @@ -nielsen@memberwebs.com +AUTHOR: +Nate Nielsen + +CONTRIBUTORS: +Andreas Steinmetz + +PATCHES: +Berk D. Demir +João Carlos Mendes Luís +Jasper Slits +Yamamoto Takao diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..763af15 --- /dev/null +++ b/COPYING @@ -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` + diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..c7ab92a --- /dev/null +++ b/NEWS @@ -0,0 +1 @@ +See ChangeLog \ No newline at end of file diff --git a/README b/README new file mode 100644 index 0000000..4c7af45 --- /dev/null +++ b/README @@ -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_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 ], + [int attr=PTHREAD_CREATE_JOINABLE;], + ok=PTHREAD_CREATE_JOINABLE, ok=unknown) + if test x"$ok" = xunknown; then + AC_TRY_LINK([#include ], + [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 +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 + #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 ])], [ #include ]) + +# 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 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 + * + */ + +#ifndef __PROXSMTPD_H__ +#define __PROXSMTPD_H__ + + +#endif /* __PROXSMTPD_H__ */ -- cgit v1.2.3