--- ../simscan-1.4.0-patchedorg/simscan.c +++ ./simscan.c @@ -84,6 +84,11 @@ #define MAX_SPAMC_ARGS 20 char *spamc_args[MAX_SPAMC_ARGS]; +#define MAX_RSPAMC_ARGS 20 +char *rpamc_args[MAX_RSPAMC_ARGS]; +int check_rspam(); +int is_rspam(char *spambuf); + /* --stdout is required for reading virus names */ char *viri_args[] = { "clamdscan", "--stdout", NULL }; @@ -132,6 +137,7 @@ int PerDomainClam; int PerDomainSpam; +int PerDomainRSpam; int PerDomainTrophie; int PerDomainSpamPassthru; int MaxDomains; @@ -250,7 +256,7 @@ /* print out version information if requested */ if ( argc > 1 && strcmp(argv[1],"-v" )==0 ) { - printf("simscan version: %s\n", VERSION); + printf("simscan version: %s (with rspamc patch)\n", VERSION); exit(0); } @@ -672,13 +678,16 @@ /* set the standard input to be the new file */ if ( fd_move(0,fd) == -1 ) { if ( DebugFlag > 0 ) { - fprintf(stderr, "simscan: spam could not fd_move\n"); + fprintf(stderr, "simscan: WARNING: spam could not fd_move\n"); } exit_clean(EXIT_400); } /* optionally check for spam with spamassassin */ snprintf(spam_message_name, sizeof(spam_message_name), "spamc.msg.%s", unique_ext); + if ( DebugFlag > 0 ) { + fprintf(stderr, "simscan: check_spam will now read %s and write to spam_message_name %s\n", message_name, spam_message_name); + } ret = check_spam(); switch ( ret ) { /* spamassassin not enabled for this domain */ @@ -732,7 +741,95 @@ } #endif +// #if defined(ENABLE_RSPAM) + /* The following code is copied from the Spamassassin check below, with the proper adjustments */ /* re-open the file read only */ + if (getenv("ENABLE_RSPAM")) { +#ifdef ENABLE_PER_DOMAIN + if ( PerDomainRSpam == 1 ) { +#endif /* ENABLE_PER_DOMAIN */ + if ( DebugFlag > 0 ) { + fprintf(stderr, "simscan: prepare for rspamc: %s\n", message_name); + } + if ( (fd = open(message_name, O_RDONLY)) == -1 ) { + if ( DebugFlag > 0 ) { + fprintf(stderr, "simscan: can not open file: %s\n", message_name); + } + exit_clean(EXIT_400); + } + + /* set the standard input to be the new file */ + if ( fd_move(0,fd) == -1 ) { + if ( DebugFlag > 0 ) { + fprintf(stderr, "simscan: could not fd_move\n"); + } + exit_clean(EXIT_400); + } + + /* check for spam with RSPAM */ + snprintf(spam_message_name, sizeof(spam_message_name), "rspamc.msg.%s", unique_ext); + IsSpam = 0; + ret = check_rspam(); + switch ( ret ) { + /* not enabled for this domain */ + case 2: + /* re-open the message file file read only */ + /* do nothing, message_name gets openend in any case*/ + break; + /* spam detected, refuse message */ + case 1: + if ( DebugFlag > 0 ) { + fprintf(stderr, "simscan: RSPAM reported message as being SPAM\n"); + } + close(fd); +#ifdef QUARANTINEDIR + quarantine_msg(spam_message_name); +#endif + +#ifdef ENABLE_DROPMSG + if ( DebugFlag > 0 ) { + fprintf(stderr, "simscan: dropping the message\n"); + } + exit_clean(EXIT_0); + /* Drop the message, returning success to sender. */ +#else +#ifdef ENABLE_CUSTOM_SMTP_REJECT + snprintf(RejectMsg,sizeof(RejectMsg), "DYour email is considered spam, kind regards from rspamc"); + write(4,RejectMsg, strlen(RejectMsg)); + exit_clean(EXIT_MSG); +#else + exit_clean(EXIT_500); +#endif +#endif + break; + + /* rspam processed message and no spam detected */ + case 0: + if ( DebugFlag > 0 ) { + fprintf(stderr, "simscan: RSPAM reported message %s as being HAM\n", spam_message_name); + fprintf(stderr, "simscan: message %s, spam_message %s, closing fd %d\n", message_name, spam_message_name, fd); + } + /* open the spam file read only */ + strncpy(message_name,spam_message_name,BUFFER_SIZE); + close(fd); + break; + /* errors , return temporary error */ + default: + if ( DebugFlag > 0 ) { + fprintf(stderr, "simscan: check_rspam had an error ret: %d\n", ret); + } + close(fd); + exit_clean(EXIT_400); + } +#ifdef ENABLE_PER_DOMAIN + } else { + fprintf(stderr, "simscan: skipping check_rspam, disabled for this domain\n"); + } +#endif /* ENABLE_PER_DOMAIN */ + } +// #endif /* ENABLE_RSPAM */ + + /* re-open the file read only */ if ( (fd = open(message_name, O_RDONLY)) == -1 ) { if ( DebugFlag > 0 ) { fprintf(stderr, "simscan: could not re-open file: %s\n", message_name); @@ -1081,6 +1178,186 @@ } #endif +#ifdef ENABLE_RSPAM +int check_rspam() +{ + int rspam_pid; + int rspam_fd; + int rmstat; + int pim[2]; + char *tmpstr; + char *tmpbuf; + int i; + FILE *spamfs; + +#ifndef ENABLE_SPAM_AUTH_USER + /* don't scan email from authenticated senders */ + if (getenv("RELAYCLIENT")) { + log_message("RELAYCLIENT", "-", 0); + return 2; + } +#endif + + if ( (rspam_fd=open(spam_message_name, O_RDWR|O_CREAT|O_TRUNC,0644)) ==- 1) { + if ( DebugFlag > 0 ) { + fprintf(stderr, "simscan: check_rspam could not open file: %s\n", + spam_message_name); + } + return(-1); + } + + if ( DebugFlag > 0 ) { + fprintf(stderr, "simscan: calling rspamc\n"); + } + + // RSPAMC command + tmpbuf = malloc(strlen(RSPAMC_ARGS)+1); + strcpy(tmpbuf, RSPAMC_ARGS); + + char *rspamc_args[MAX_RSPAMC_ARGS]; + rspamc_args[0] = "rspamc"; + tmpstr = strtok(tmpbuf," "); + + for(i=1;i 0 ) { + fprintf(stderr, "simscan: calling %s ", RSPAMC); + i=0; + while(rspamc_args[i] != NULL){ + fprintf(stderr, " %s", rspamc_args[i]); + ++i; + } + fprintf(stderr, "\n"); + } + if ( pipe(pim) == 0 ) { + /* fork rspamc */ + switch(rspam_pid = vfork()) { + case -1: + close(pim[0]); + close(pim[1]); + close(rspam_fd); + return(-1); + case 0: + close(pim[0]); + dup2(pim[1],1); + close(pim[1]); + execve(RSPAMC, rspamc_args, 0); + _exit(-1); + } + close(pim[1]); + dup2(pim[0],0); + close(pim[0]); + } else { + close(rspam_fd); + return(0); + } + + InHeaders = 1; + SpamHits = 0.0; + ReqHits = 0.0; + IsSpam = 0; + memset(buffer,0,sizeof(buffer)); + spamfs = fdopen(0,"r"); + while(fgets(buffer,sizeof(buffer)-1,spamfs) != NULL ) { + if ( InHeaders == 1 ) { + is_rspam(buffer); + } + write(rspam_fd, buffer,strlen(buffer)); + memset(buffer,0,sizeof(buffer)); + } + close(rspam_fd); + fclose(spamfs); + + /* wait for rspamc to finish */ + if (waitpid(rspam_pid,&rmstat, 0) == -1) { + return(-1); + } + if ( DebugFlag > 0 ) { + fprintf(stderr, "simscan: rspamc done\n"); + } + + /* check if the child died on a signal */ + if ( WIFSIGNALED(rmstat) ) return(-1); + +#ifdef SPAM_HITS + if ( PerDomainHits==1 && ( SpamHits >= PDHits ) ) { +#ifdef ENABLE_DROPMSG + log_message("SPAM DROPPED", Subject, 1); +#else + log_message("SPAM REJECT", Subject,1); +#endif + return(1); + } else if ( PerDomainHits==0 && ( SpamHits >= SPAM_HITS ) ) { +#ifdef ENABLE_DROPMSG + log_message("SPAM DROPPED", Subject, 1); +#else + log_message("SPAM REJECT", Subject,1); +#endif + return(1); + } + + if (SpamHits >= SPAM_HITS) { +#ifdef ENABLE_DROPMSG + log_message("SPAM DROPPED", Subject, 1); +#else + log_message("SPAM REJECT", Subject,1); +#endif + } else { + log_message("CLEAN", Subject,1); + } +#else + +#ifdef ENABLE_SPAM_PASSTHRU + #ifdef ENABLE_PER_DOMAIN + if ( PerDomainSpamPassthru == 1) { + if (( IsSpam == 1 ) && (DebugFlag > 0)){ + fprintf(stderr, + "simscan: delivering spam because spam-passthru is defined in this domain\n"); + } + log_message("PASSTHRU", Subject,1); + return(0); + } else { + if ( IsSpam == 1 ) { +#ifdef ENABLE_DROPMSG + log_message("SPAM DROPPED", Subject, 1); +#else + log_message("SPAM REJECT", Subject,1); +#endif + return(1); + } else { + log_message("CLEAN", Subject,1); + } + } + #else + if ( IsSpam == 1 ) { + log_message("PASSTHRU", Subject,1); + } else { + log_message("CLEAN", Subject,1); + } + return(0); + #endif +#else + if ( IsSpam == 1 ) { +#ifdef ENABLE_DROPMSG + log_message("SPAM DROPPED", Subject, 1); +#else + log_message("SPAM REJECT", Subject,1); +#endif + return(1); + } else { + log_message("CLEAN", Subject,1); + } +#endif + +#endif + return(0); + +} +#endif /* ENABLE_RSPAM */ + /* * optionally check for spam * @@ -1815,8 +2092,16 @@ if ( parm != NULL ) val = strsep(&data, PER_DOMAIN_TOKENS); while ( parm != NULL && val != NULL) { if ( DebugFlag > 1 ) fprintf(stderr, "simscan: pelookup %s = %s\n", parm,val); - if ( strcasecmp(parm,"clam") == 0 ) { + if ( strcasecmp(parm,"rspam") == 0 ) { if ( strcasecmp(val, "yes") == 0 ) { + PerDomainRSpam = 1; + } else if ( strcasecmp(val, "no") == 0 ) { + PerDomainRSpam = 0; + } + if ( DebugFlag > 1 ) fprintf(stderr, "simscan: rspam = %s\n", (PerDomainRSpam ? "enabled" : "disabled")); + } + else if ( strcasecmp(parm,"clam") == 0 ) { + if ( strcasecmp(val, "yes") == 0 ) { PerDomainClam = 1; } else if ( strcasecmp(val, "no") == 0 ) { PerDomainClam = 0; @@ -1995,6 +2280,61 @@ #endif +/* Check for a spam message based on rspam + * This is done by checking for a matching line + * in the email headers for X-Spam: YES + * + * Return 1 if spam + * Return 0 if not spam + * Return -1 on error + */ +int is_rspam(char *spambuf) +{ + int l; + char *tmpstr; + char hits[10]; + + if ( spambuf[0] == '\n' || spambuf[1] == '\n' ) { + InHeaders = 0; + return(0); + } + + if ( strncasecmp(spambuf, "X-Spam: yes", 11 ) == 0 ) { + IsSpam = 1; + /* still in the headers get Subject */ + } else if ( strncmp(spambuf, "Subject:", 8 ) == 0 ) { + strncpy(Subject, &spambuf[9], sizeof(Subject)-1); + /* replace : char with _ and null terminate on + * newline or carrage return + */ + for(l=0;Subject[l]!=0 && l= 0x30 && *tmpstr < 0x3A)||*tmpstr == '.') ; ++l, ++tmpstr) { + hits[l] = *tmpstr; + } + ReqHits = atof(hits); + // fprintf(stderr, "SpamHits: %f / %f required\n", SpamHits, ReqHits); + } + return(0); +} #ifdef ENABLE_SPAM /* Check for a spam message --- ../simscan-1.4.0-patchedorg/configure.in +++ ./configure.in @@ -252,9 +252,85 @@ ;; esac +#---------------------------------------------------------------------- +AC_ARG_ENABLE(rspam, [ --enable-rspam=y|n Turn on rspam scanning. default no.], + ENABLE_RSPAM="$enableval", + [ + ENABLE_RSPAM=no + ] ) +case $ENABLE_RSPAM in +1*|y*|Y*) + ENABLE_RSPAM=1 + AC_DEFINE_UNQUOTED([ENABLE_RSPAM], $ENABLE_RSPAM, [enable rspam scanning]) + ;; +*) + ENABLE_RSPAM=0 + ;; +esac + +case $ENABLE_RSPAM in + 1*|y*|Y*) + + AC_ARG_ENABLE(spam-passthru, [ --enable-spam-passthru=y|n Pass spam email thru or reject (default disable, reject).], + ENABLE_SPAM_PASSTHRU="$enableval", + [ + ENABLE_SPAM_PASSTHRU=no + ] ) + case $ENABLE_SPAM_PASSTHRU in + 1*|y*|Y*) + ENABLE_SPAM_PASSTHRU=1 + AC_DEFINE([ENABLE_SPAM_PASSTHRU], [], [pass spam through to user]) + ;; + *) + ENABLE_SPAM_PASSTHRU=0 + ;; + esac + ;; +esac + #---------------------------------------------------------------------- +case $ENABLE_RSPAM in +1*|y*|Y*) + + AC_MSG_CHECKING(whether we can locate the rspamc program) + rspamc="" + for f in /usr/bin/rspamc /usr/local/bin/rspamc + do + if test -f $f + then + rspamc=$f + break + fi + done + + AC_ARG_ENABLE(rspam-path, + [ --enable-rspam-path=PATH Full path to rpamc program.], + rspamc="$enableval", + [ + if test "$rspamc" = "" + then + AC_MSG_RESULT(no) + AC_MSG_ERROR([Unable to find your rspamc program, specify --enable-rspam-path.]) + fi + ] + ) + AC_MSG_RESULT(yes) + AC_DEFINE_UNQUOTED(RSPAMC,"$rspamc","rspamc program") + + AC_ARG_ENABLE(rspam-args, [ --enable-rspam-args="args" Override the default rspamc arguments.], + ENABLE_RSPAM_ARGS="$enableval", + [ + ENABLE_RSPAM_ARGS="" + ]) + AC_DEFINE_UNQUOTED(RSPAMC_ARGS,"$ENABLE_RSPAM_ARGS","rspamc arguments") + ;; + +esac + +#---------------------------------------------------------------------- + AC_ARG_ENABLE(spam, [ --enable-spam=y|n Turn on spam scanning. default no.], ENABLE_SPAM="$enableval", [ @@ -678,6 +754,7 @@ AC_DEFINE([RCVD_CLAM_KEY], "clamav", [key for clamav]) AC_DEFINE([RCVD_TROPHIE_KEY], "trophie", [key for clamav]) AC_DEFINE([RCVD_DSPAM_KEY], "dspam", [key for dspam]) + AC_DEFINE([RCVD_RSPAM_KEY], "rspam", [key for rspam]) ;; *) ENABLE_RECEIVED=0 @@ -925,5 +1002,25 @@ echo " dspam scanning = OFF" ;; esac + +case $ENABLE_RSPAM in + 1*|y*|Y*) + echo " rspam scanning = ON" + echo " rspam program = $rspamc" + echo " rspam arguments = $ENABLE_RSPAM_ARGS" + case $ENABLE_SPAM_PASSTHRU in + 0*|n*|N*) + echo " spam passthru = OFF" + ;; + *) + echo " spam passthru = ON" + ;; + esac + ;; + *) + echo " rspam scanning = OFF" + ;; +esac + echo ""