[patch] denysoft_greylist: Option per_recipient

2003-11-03 Thread Peter J. Holzer
As mentioned in my last mail, my alias plugin allows setting notes per
recipient, which can be used by other plugins. This patch adds an option
per_recipient to denysoft_greylist. If it is set, the plugin will
immediately return DECLINED unless
$transaction-notes('recipient_options')-{denysoft_greylist} is true.

This allows me enable greylisting selectively for some addresses. E.g.,
my personal addresses have it enabled:

hjp,peter.holzer@wsr.ac.at, wifo.ac.at, wifo.at
:[EMAIL PROTECTED]  (denysoft_greylist)

but the postmaster address doesn't:

postmaster  @wsr.ac.at  :[EMAIL PROTECTED]

hp


-- 
   _  | Peter J. Holzer| We have failed our own creation and given
|_|_) | Sysadmin WSR   | birth something truly awful. We're just too
| |   | [EMAIL PROTECTED] | busy cooing over the pram to notice.
__/   | http://www.hjp.at/ |   -- http://www.internetisshit.org
Index: plugins/denysoft_greylist
===
--- plugins/denysoft_greylist   (revision 22)
+++ plugins/denysoft_greylist   (working copy)
@@ -4,9 +4,9 @@
 
 =head1 DESCRIPTION
 
-Plugin to implement the 'greylisting' algorithm proposed by Evan Harris
-in http://projects.puremagic.com/greylisting/. Greylisting is a form of 
-denysoft filter, where unrecognised new connections are temporarily 
+Plugin to implement the 'greylisting' algorithm proposed by Ed Harris in
+http://projects.puremagic.com/greylisting/. Greylisting is a form of
+denysoft filter, where unrecognised new connections are temporarily
 denied for some initial period, to foil spammers using fire-and-forget 
 spamware, http_proxies, etc.
 
@@ -66,6 +66,13 @@
 but never actually issue DENYSOFTs. Useful for seeding the database 
 and testing without actually impacting deliveries. Default: 0.
 
+=item per_recipient bool
+
+Per recipient flag - if this is set, the plugin is only run for
+recipients which have recipient_option 'denysoft_greylist' set.
+This can be set by the aliases plugin.
+Default: 0.
+
 =back
 
 =head1 AUTHOR
@@ -86,6 +93,8 @@
 my $GREY_DEFAULT  = 3 * 3600 + 10 * 60;
 my $WHITE_DEFAULT = 36 * 24 * 3600;
 
+my @whitelist;
+
 sub register {
   my ($self, $qp, %arg) = @_;
   $self-{_greylist_ip} = $arg{remote_ip};
@@ -98,11 +107,12 @@
   $self-{_greylist_grey}   = $arg{grey_timeout}  || $GREY_DEFAULT;
   $self-{_greylist_white}  = $arg{white_timeout} || $WHITE_DEFAULT;
   $self-{_greylist_testonly} = $arg{testonly};
+  $self-{_greylist_per_recipient} = $arg{per_recipient};
 
   if (open(WL, $DBDIR/denysoft_whitelist)) {
 while (WL) {
-  chomp;
-  $self-{_greylist_whitelist}{$_} = 1;
+  s/\s*(\#.*)$//s;
+  add_whitelist_entry($_);
 }
 close(WL);
   }
@@ -123,6 +133,12 @@
 
 sub rcpt_handler {
   my ($self, $transaction, $rcpt) = @_;
+
+  if ($self-{_greylist_per_recipient}) {
+my $ro = $transaction-notes('recipient_options');
+return DECLINED unless ($ro  $ro-{denysoft_greylist});
+  }
+
   my $sender = $transaction-sender;
   if ($self-{_greylist_rcpt}) {
 my ($status) = $self-denysoft_greylist($transaction, $sender, $rcpt);
@@ -153,15 +169,17 @@
   my $remote_ip = $self-qp-connection-remote_ip;
   my $fmt = %s:%d:%d:%d;
 
-  if ($self-{_greylist_whitelist}{$remote_ip}) {
+  if (lookup_whitelist_entry($remote_ip)) {
 $self-log(2, ip $remote_ip is whitelisted, skipping greylist checks);
 return DECLINED 
   }
   # Check denysoft db
+  print STDERR denysoft_greylist: trying to tie to $DB\n;
   open(LOCK, $DB.lock) or die cannot open $DB.lock: $!;
   flock(LOCK, LOCK_EX) or die cannot lock $DB.lock: $!;
 
   tie my %db, 'AnyDBM_File', $DB, O_CREAT|O_RDWR, 0600 or return (DECLINED);
+  print STDERR denysoft_greylist: tied to $DB\n;
   my @key;
   push @key, $remote_ip if $self-{_greylist_ip};
   push @key, $sender-address || '' if $self-{_greylist_sender};
@@ -215,4 +233,39 @@
   return ($self-{_greylist_testonly} ? DECLINED : DENYSOFT);
 }
 
+sub add_whitelist_entry {
+my ($s) = @_;
+my ($ip, $mask);
+
+if ($s =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)\/(\d+)/) {
+   $ip = ($1  24) | ($2  16) | ($3  8) | $4;
+   $mask = -1  (32 - $5);
+} elsif ($s =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/) {
+   $ip = ($1  24) | ($2  16) | ($3  8) | $4;
+   $mask = -1;
+} else {
+   $self-log(1, cannot parse whitelist entry $s);
+   return;
+}
+my $net = $ip  $mask;
+my $bcast = $ip | ~$mask;
+push @whitelist, [$net, $bcast];
+}
+
+sub lookup_whitelist_entry {
+my ($s) = @_;
+my ($ip, $mask);
+
+if ($s =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/) {
+   $ip = ($1  24) | ($2  16) | ($3  8) | $4;
+} else {
+   $self-log(1, cannot parse ip address $s);
+   return;
+}
+for (@whitelist) {
+   return $_ if ($_-[0] = $ip  $ip = $_-[1]);
+}
+return undef;
+}
+
 1;



pgp0.pgp
Description: PGP signature


postfix backend

2003-11-03 Thread Peter J. Holzer
Ok, this is the last one for today, I promise :-)

A plugin to inject mails into the postfix queue via the cleanup daemon.

The class Qpsmtp::Postfix is a partial implementation of the protocol
the postfix daemons use to talk to each other. Since that is only
documented by source, it may not work with all postscript versions (I'm
using 1.1.12).

hp

-- 
   _  | Peter J. Holzer| We have failed our own creation and given
|_|_) | Sysadmin WSR   | birth something truly awful. We're just too
| |   | [EMAIL PROTECTED] | busy cooing over the pram to notice.
__/   | http://www.hjp.at/ |   -- http://www.internetisshit.org
package Qpsmtpd::Postfix;

=head1 NAME

Qpsmtpd::Postfix

=head2 DESCRIPTION

This package implements the protocol Postfix servers use to communicate
with each other. See src/global/rec_type.h in the postfix source for
details.

=cut

use strict;
use IO::Socket::UNIX;
use vars qw(@ISA);
@ISA = qw(IO::Socket::UNIX);

my %rec_types;

sub init {
  my ($self) = @_;

  %rec_types = (
REC_TYPE_SIZE= 'C',# first record, created by cleanup
REC_TYPE_TIME= 'T',# time stamp, required
REC_TYPE_FULL= 'F',# full name, optional
REC_TYPE_INSP= 'I',# inspector transport
REC_TYPE_FILT= 'L',# loop filter transport
REC_TYPE_FROM= 'S',# sender, required
REC_TYPE_DONE= 'D',# delivered recipient, optional
REC_TYPE_RCPT= 'R',# todo recipient, optional
REC_TYPE_ORCP= 'O',# original recipient, optional
REC_TYPE_WARN= 'W',# warning message time
REC_TYPE_ATTR= 'A',# named attribute for extensions

REC_TYPE_MESG= 'M',# start message records

REC_TYPE_CONT= 'L',# long data record
REC_TYPE_NORM= 'N',# normal data record

REC_TYPE_XTRA= 'X',# start extracted records

REC_TYPE_RRTO= 'r',# return-receipt, from headers
REC_TYPE_ERTO= 'e',# errors-to, from headers
REC_TYPE_PRIO= 'P',# priority
REC_TYPE_VERP= 'V',# VERP delimiters

REC_TYPE_END = 'E',# terminator, required

  );

}

sub print_rec {
  my ($self, $type, @list) = @_;

  die unknown record type unless ($rec_types{$type});
  $self-print($rec_types{$type});

  # the length is a little endian base-128 number where each 
  # byte except the last has the high bit set:
  my $s = @list;
  my $ln = length($s);
  while ($ln = 0x80) {
my $lnl = $ln  0x7F;
$ln = 7;
$self-print(chr($lnl | 0x80));
  }
  $self-print(chr($ln));

  $self-print($s);
}

sub print_rec_size {
  my ($self, $content_size, $data_offset, $rcpt_count) = @_;

  my $s = sprintf(%15ld %15ld %15ld, $content_size, $data_offset, $rcpt_count);
  $self-print_rec('REC_TYPE_SIZE', $s);
}

sub print_rec_time {
  my ($self, $time) = @_;

  $time = time() unless (defined($time));

  my $s = sprintf(%d, $time);
  $self-print_rec('REC_TYPE_TIME', $s);
}

sub open_cleanup {
  my ($class) = @_;
  my $self = IO::Socket::UNIX-new(Type = SOCK_STREAM,
   Peer = /var/spool/postfix/public/cleanup);
  bless ($self, $class);
  $self-init();
  return $self;
}

sub print_attr {
  my ($self, @kv) = @_;
  for (@kv) {
$self-print($_\0);
  }
  $self-print(\0);
}

sub get_attr {
  my ($self) = @_;
  local $/ = \0;
  my %kv;
  for(;;) {
my $k = $self-getline;
chomp($k);
last unless ($k);
my $v = $self-getline;
chomp($v);
$kv{$k} = $v;
  }
  return %kv;
}


=head2 print_msg_line($line)

print one line of a message to cleanup.

This removes any linefeed characters from the end of the line
and splits the line across several records if it is longer than
1024 chars. 

=cut

sub print_msg_line {
  my ($self, $line) = @_;

  $line =~ s/\r?\n$//s;

  # split into 1k chunks. 
  while (length($line)  1024) {
my $s = substr($line, 0, 1024);
$line = substr($line, 1024);
$self-print_rec('REC_TYPE_CONT', $s);
  }
  $self-print_rec('REC_TYPE_NORM', $line);
}

=head2 inject_mail($transaction)

(class method) inject mail in $transaction into postfix queue via cleanup.
$transaction is supposed to be a Qpsmtpd::Transaction object.

=cut

sub inject_mail {
  my ($class, $transaction) = @_;

  my $strm = $class-open_cleanup();

  my %at = $strm-get_attr;
  my $qid = $at{queue_id};
  print STDERR qid=$qid\n;
  $strm-print_attr('flags' = '');
  $strm-print_rec_time();
  $strm-print_rec('REC_TYPE_FROM', $transaction-sender-address|| );
  for (map { $_-address } $transaction-recipients) {
$strm-print_rec('REC_TYPE_RCPT', $_);
  }
  # add an empty message length record.
  # cleanup is supposed to understand that.
  # see src/pickup/pickup.c 
  $strm-print_rec('REC_TYPE_MESG', );

  # a received header has already been added in SMTP.pm
  # so we can just copy the message:

  my $hdr = $transaction-header-as_string;
  for (split(/\r?\n/, $hdr)) {
print STDERR hdr: $_\n;
$strm-print_msg_line($_);
  }
  

Re: [patch] rhsbl: whitelist postmaster

2003-11-03 Thread Robert James Kaes
On Mon, 03 Nov 2003, Peter J. Holzer wrote:
 A mini patch to accept mails to postmaster even if they come from a
 domain in a RHSBL. This is probably not a very good idea and should be
 solved in a more general manner.

I have a plugin that does this already:

  http://www.flarenet.com/consulting/software/qpsmtpd/check_goodrcptto

This should be run before any other RCPT TO test to allow
goodrcptto messages through without being processed by the other
plugins.
-- Robert

-- 
Robert James Kaes---  Flarenet Inc.  ---(519) 426-3782
 http://www.flarenet.com/consulting/
  * Putting the Service Back in Internet Service Provider *


pgp0.pgp
Description: PGP signature


Re: [patch] denysoft_greylist: Option per_recipient

2003-11-03 Thread Gavin Carr
On Mon, Nov 03, 2003 at 12:47:38PM +0100, Peter J. Holzer wrote:
 As mentioned in my last mail, my alias plugin allows setting notes per
 recipient, which can be used by other plugins. This patch adds an option
 per_recipient to denysoft_greylist. If it is set, the plugin will
 immediately return DECLINED unless
 $transaction-notes('recipient_options')-{denysoft_greylist} is true.
 
 This allows me enable greylisting selectively for some addresses. E.g.,
 my personal addresses have it enabled:
 
 hjp,peter.holzer@wsr.ac.at, wifo.ac.at, wifo.at
 :[EMAIL PROTECTED]  (denysoft_greylist)
 
 but the postmaster address doesn't:
 
 postmaster  @wsr.ac.at  :[EMAIL PROTECTED]
 
   hp

The current version of denysoft_greylist (at 
http://www.openfusion.com.au/labs/qpsmtpd/) also supports a more 
general whitelisting scheme using my whitelist_soft plugin, which is a 
more conservative variant of Devin's original whitelist plugin.

We seem to be seeing a bit of duplication lately - do we need a webpage
or something where announced plugins (and patches and forks) can be 
registered (and perhaps rated)? We probably also need to ensure that
third-party plugins included in the distribution or cvs can be updated
periodically by their authors - not sure how that's supposed to work
at the moment?

Cheers,
Gavin



Re: [patch] denysoft_greylist: Option per_recipient

2003-11-03 Thread Matt Sergeant
On 3 Nov 2003, at 20:05, Gavin Carr wrote:

We seem to be seeing a bit of duplication lately - do we need a webpage
or something where announced plugins (and patches and forks) can be
registered (and perhaps rated)? We probably also need to ensure that
third-party plugins included in the distribution or cvs can be updated
periodically by their authors - not sure how that's supposed to work
at the moment?
Unfortunately I find it difficult to keep things straight (simply due 
to $workload), and Ask doesn't seem to be very active any more (Ask?).

As far as a registry goes, how about a Wiki?

Matt.