#!/usr/bin/perl # # filter.pl # # This script is used to re-write and re-send e-mail # messages checked against spam on remote computers. # This program is useful if and only if for some reason # you do not want or cannot afford to run spamassassin # on your incoming mail server. # # Author: Can Ugur Ayfer # cayfer at bilkent edu tr # May 2004 # Bilkent University - Ankara - Turkey # # IMPORTANT NOTE: If you are running spamassassin # on the same computer with the # SMTP server which delivers the messages # to your mailbox, then you DO NOT NEED # this piece of software. procmail should # be enough! # # This script is intended to process the output of # spamassassin. That is, it expects a single e-mail # message, scanned for spam and tagged if it is so. # Spam tags are expected as some text prepended to # the subject lines of messages. e.g: # # Subject: ***SPAM*** Free Viagra # # This program examines the headers of an email message and # extracts the original e-mail address, # extracts the original sender and original subject; # then composes a new message; # then adds a header line "X-Filter: ..." # and resends the message to its original recipient. # # Licensing: # GPL of course... Do whatever you like with # this piece of software. I only ask you to # keep my name as the first author and drop # me a mail if you make any changes that me and # others will benefit from. # Requirements: # Some UNIX flavor # Perl # The Perl module Mail::Sender (see cpan.org) # Spamassassin (see spamassassin.org) # # Installation: # 1. Install the module Mail::Sender the usual way # most Perl modules are installed. i.e.; # Read the READMEs # perl Makefile.pl # make # make test # make install # 2. Make sure spamassassin is installed on your system and # spamd (daemonized version of spamassassin) is running. # Modify spamassassin configuration so that it tags # spam messages by prepending "*****SPAM*****" # to their subject lines. i.e. set # subject_tag *****SPAM***** # in the local config file. (usually in /etc/mail/spamassassin/local.cf) # 3. Place this filter.pl script in some nice place (my choice # is /usr/share/spamassassin) # 4. Make sure the script is world executable. root ownership # is fine as long as you do not set it SUID. # "chmod 755 filter.pl" should be fine. # 5. Create a mail alias such as "filter": # filter: "| /usr/bin/spamc -t 10 | /usr/share/spamassassin/filter.pl" # /usr/bin/spamc is the spamassassin client and this should # be automatically installed if you have followed the proper # spamassassin install procedures. # 6. Modify the first few lines in filter.pl to tailor it for your # system. You need to tell filter.pl about the smtp server you want # to use for sending out re-written mail. You might also want to # change $spam_filter_tag string which marks those mails handled by this script # A null value will also work fine. # 7. Tell your users who want their incoming mail to be scanned against # spam to create two files in their home directories: ( If their # home directories are not on a UNIX/Linux box, then pity for them; # or rather pity for their sysadmin), namely # # .forward and .procmailrc # # The .forward file should contain the line # "|IFS=' ' && exec /usr/bin/procmail -f- || exit 75 #user" # (Please note that the double quotes should be included.) # and the .procmailrc file should contain the lines: # :0 # * ^X-Filter.*Bilkent # $DEFAULT # # :0 # * < 256000 # ! filter@remote_spam_scanner.college.edu # # :0 # $DEFAULT # # Please note that .procmailrc and .forward files must be # owned by respective users and should be writable only # by the owner. # 8. If you want this script to be effective for all your users; # that is if you are installing this script as a system-wide # script, then you can use /etc/procmailrc instead of # every user placing one in their homes. Users should # place a .forward file though or you must find another # way to forward everybody's incoming mail to # filter@remote_spam_scanner.college.edu # (Remember, you are using UNIX and there is solution for # everything.) # # # Administration: # 1. As a good sysadmin, you should be collecting spam mail # samples and occasionally re-training spamassassin with # you new spam samples. # Reminder: Suppose you collect spam samples in a mail # folder called "sample_spam". # You should execute the command # sa-learn --mbox --showdots --spam sample_spam # whenever you feel like re-training spamassassin. # 2. filter.pl logs each mail it processes together with # the results reported by spamassassin to syslog. # You can view filter.pl entries using the command: # grep SPAM /var/log/syslog # If you not want log records, just comment out the # two relevant lines at the end of the script. use strict; use Mail::Sender; # # Config stuff ----------------------------------------- my $smtp_server = "localhost"; my $spam_filter_tag = "Bilkent University - SPAM Filter"; # Enf of Config stuff ---------------------------------- my $whole_msg = ""; my $from = ""; my $to = ""; my $subj = ""; my $headers_done = 0; # will be 1 when "to", "from" and "subj" are extracted my (@msg_lines, $temp, $line, @others, @froms, $msg_body, $msg_headers, $spam_flag, $score, $threshold, @subjects); while (<>) { $whole_msg .= $_; $temp = $_; chomp($temp); $headers_done = 1 if ($temp eq ""); if ( ! $headers_done ) { $line = $_; chomp($line); if ( $line=~ /^From / ) { # To whom shall we send this back after processing (undef, $to) = split(/ /, $line); # "From ugur@cayfer.bilkent.edu.tr Sat May 1 17:15:44 2004" push(@froms, $to); } if ( $line=~ /^From:/ ) { (undef, @others) = split(/ /, $line); # From: Can Ugur Ayfer $from = join(" ", @others); push(@froms, $from); # pushes "Can Ugur Ayfer " } if ( $line=~ /^Subject:/ ) { (undef, @others) = split(/ /, $line); # Subject: blah blah blah $subj = join(" ", @others); push(@subjects, $subj); # pushes "blah blah blah" } # is the msg marked spam? if ( $line=~ /^X-Spam-Flag: YES/i) { $spam_flag="SPAM"; } # Can we get the score? if ( $line=~ /^X-Spam-Status:.*hits=(.*) required=(.*) tests/) { $score=$1; $threshold=$2; } } } # # extract all the headers of the $whole_msg # a blank line separates headers and the msg body $msg_body=""; $msg_headers=""; @msg_lines = split(/\n/, $whole_msg); my $processing_header_lines = 1; foreach $line (@msg_lines) { $line=~ s/\x0A/\n/g; chomp($line); if ( ($line eq "") && $processing_header_lines ) { # the first blank line separating hdrs and body $processing_header_lines = 0; next; } if ( $processing_header_lines ) { # # Modify message headers so that postfix does not detect any loops $line=~ s/^Message-ID/X-Message-ID/; $line=~ s/^Received/X-Received/; $line=~ s/^Delivered-To/X-Delivered-To/; $line=~ s/^From/X-From/; $line=~ s/^To:/X-To:/; $msg_headers .= $line . "\n"; } else { $msg_body .= $line . "\n"; } } # compose and send back the filtered mail my $now = localtime(); $from = pop(@froms); $subj = pop(@subjects); $msg_headers=~ s/\x0a/\n/g; # get rid of new lines $msg_headers=~ s/^\n$//g; # get rid of blank lines my $sender = new Mail::Sender { smtp => 'localhost', from => $from, on_errors => undef }; my $new_hdr = 'X-Filter: '.$spam_filter_tag.' - '."$now\n".'X-Headers: '.$msg_headers; $new_hdr=~ s/\x0a\x0a/\x0a/g; chomp($new_hdr); chomp($to); chomp($from); chomp($subj); chomp($msg_body); my $sender = Mail::Sender -> new; $sender->Open({ to => $to, from => $from, fake_from => $from, replyto => $from, smtp => $smtp_server, subject => $subj, headers => $new_hdr }); $sender->SendLineEnc( $msg_body ); $sender->Close(); $subj=~ s/\**SPAM\**//; $subj=~ s/^ //g; my $log_msg = "From:$from|To:$to|$spam_flag|Score:$score|$threshold|$subj"; my $log_cmd = "/usr/bin/logger \"SPAM-FILTER:$log_msg\""; system($log_cmd); ### Functions ### sub trim { my $param = shift; $param =~ s/^ //g; $param =~ s/ $//g; chomp($param); return $param; }