steffen: server/kolabd/kolabd kolab_smtpdpolicy.in,1.5,1.6
cvs at intevation.de
cvs at intevation.de
Mon Nov 28 05:16:03 CET 2005
Author: steffen
Update of /kolabrepository/server/kolabd/kolabd
In directory doto:/tmp/cvs-serv32156/kolabd
Modified Files:
kolab_smtpdpolicy.in
Log Message:
Cleaned up smtpd policy server. Besides the section with the "fishy" comment I am quite confident the other checks dont pose any risks of being incompatible with internet email
Index: kolab_smtpdpolicy.in
===================================================================
RCS file: /kolabrepository/server/kolabd/kolabd/kolab_smtpdpolicy.in,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -d -r1.5 -r1.6
--- kolab_smtpdpolicy.in 27 Nov 2005 23:57:02 -0000 1.5
+++ kolab_smtpdpolicy.in 28 Nov 2005 04:16:01 -0000 1.6
@@ -1,4 +1,4 @@
-#!@PERL@
+#!@PERL@ -w
##
## Copyright (c) 2004 Klaraelvdalens Datakonsult AB
@@ -19,6 +19,7 @@
## Project's homepage; see <http://www.gnu.org/licenses/gpl.html>.
##
+use strict;
use Fcntl;
use Sys::Syslog qw(:DEFAULT setlogsock);
use URI;
@@ -34,7 +35,7 @@
# kolabdelegated Postfix SMTPD policy server for Kolab. This server implements
# various policies for Kolab:
#
-# 1) Only authenticated users can use From addresses <username>@$domain
+# 1) Only authenticated users can use sender <username>@$domain
# 2) Some distribution lists are only available to authenticated users
#
# Logging is sent to syslogd.
@@ -64,7 +65,7 @@
#
# To test this script by hand, execute:
#
-# % perl kolab_smtpd_policy.pl
+# % perl kolab_smtpd_policy
#
# Each query is a bunch of attributes. Order does not matter, and
# the demo script uses only a few of all the attributes shown below:
@@ -98,6 +99,9 @@
# work on your system.
#
my %conf;
+my %attr;
+my $ldap;
+my $verbose;
my $syslog_socktype = 'unix'; # inet, unix, stream, console
my $syslog_facility="mail";
my $syslog_options="pid";
@@ -105,6 +109,20 @@
my $ldap_max_tries = 5;
+#
+# Read options from config-file
+#
+my $conf_allowunauth = 0;
+%conf = readConfig( %conf, "@sysconfdir@/kolab/kolab_smtpdpolicy.conf" );
+my $conf_ldapuri = $conf{'ldap_uri'};
+my $conf_basedn = $conf{'basedn'};
+my $conf_binddn = $conf{'binddn'};
+my $conf_bindpw = $conf{'bindpw'};
+my @conf_domain = $conf{'domain'};
+$conf_allowunauth = 1 if( $conf{'allow_unauth'} );
+my @conf_permithosts = split /\s*,\s*/, $conf{'permithosts'};
+
+
sub mylog {
my $prio = shift;
my $fmt = shift;
@@ -113,6 +131,7 @@
#Kolab::log( 'P', $text );
syslog $prio, $text;
+ print "$text\n";
}
sub contains {
@@ -137,132 +156,151 @@
}
}
-#
-# SMTPD access policy routine. The result is an action just like
-# it would be specified on the right-hand side of a Postfix access
-# table. Request attributes are available via the %attr hash.
-#
-sub smtpd_access_policy {
-
- # Get relevant attributes
- my $sender = $attr{'sender'};
- my $recip = $attr{'recipient'};
- my $username = $attr{'sasl_username'};
- my $client_addr = $attr{'client_address'};
-
- mylog($syslog_priority, "Checking sender=\"$sender\", recipient=\"$recip\", username=\"$username\", domains=".join(',', at conf_domain)." permithosts=".join(',', at conf_permithosts).", conf_allowunauth=$conf_allowunauth") if $verbose;
-
- #### This should probably be simplifed to
- #### reject sender <anything>@domain.tld if the user is
- #### not authenticated
-
- CHECKPERMITHOSTS:
- # First check if the sender is a privileged kolabhost
- # Kolab hosts use un-authenticated smtp currently
- if( !$username ) {
- for my $host (@conf_permithosts) {
- unless ($h = gethost($host)) {
- mylog($syslog_priority,"No such host $host\n");
- next;
- }
- for my $addr ( @{$h->addr_list} ) {
- return "DUNNO" if inet_ntoa($addr) eq $client_addr;
- }
- }
- }
-
- # Translate uid to mail:
+sub lookup_uid {
my $tries = 0;
- LOOKUPUID:
+ my $uid = shift;
+ AGAIN:
my $mesg = $ldap->search( base=> $conf_basedn,
scope=> 'sub',
- filter=> "(&(objectClass=kolabinetorgperson)(|(mail=$username)(uid=$username)))");
+ filter=> "(&(objectClass=kolabinetorgperson)(|(mail=$uid)(uid=$uid)))",
+ attrs => [ 'uid'] );
if( !$mesg->code && $mesg->count() > 0 ) {
mylog($syslog_priority, "LDAP search returned ".$mesg->count()." objects") if $verbose;
my $ldapobject = $mesg->entry(0);
- $username = lc($ldapobject->get_value('mail'));
- mylog($syslog_priority, "Translated username to $username") if $verbose;
+ $uid = lc($ldapobject->get_value('uid'));
+ mylog($syslog_priority, "Translated username to $uid") if $verbose;
} elsif( $mesg->code && $mesg->code != LDAP_NO_SUCH_OBJECT ) {
if( $tries++ <= $ldap_max_tries ) {
ldap_connect;
- goto LOOKUPUID;
+ goto AGAIN;
} else {
mylog($syslog_priority, "LDAP Connection error during LOOKUPUID: ".
$mesg->error." after $ldap_max_tries attempts to reconnect. Giving up!" );
- return "DEFER_IF_PERMIT LDAP Error during LOOKUPUID:: ".$mesg->error;
+ die( "LDAP Error looking up uid: ".$mesg->error );
}
}
- # Check for allowed sender
- $tries = 0;
- CHECKSENDER:
+ return $uid;
+}
- $mesg = $ldap->search( base=> $conf_basedn,
+sub check_permithosts {
+ my $client_addr = shift;
+ for my $host (@conf_permithosts) {
+ my $h;
+ unless ($h = gethost($host)) {
+ mylog($syslog_priority,"No such host $host\n");
+ next;
+ }
+ for my $addr ( @{$h->addr_list} ) {
+ return 1 if inet_ntoa($addr) eq $client_addr;
+ }
+ }
+ return undef;
+}
+
+sub lookup_sender_uids {
+ my $sender = shift;
+ my $tries = 0;
+ my @result;
+ AGAIN:
+ my $mesg = $ldap->search( base=> $conf_basedn,
scope=> 'sub',
- filter=> "(&(objectClass=kolabinetorgperson)(|(mail=$sender)(alias=$sender)))");
- if( !$mesg->code ) {
+ filter=> "(&(objectClass=kolabinetorgperson)(|(mail=$sender)(alias=$sender)))",
+ attrs => [ 'uid' ]);
+ if( !$mesg->code && $mesg->count() > 0 ) {
mylog($syslog_priority, "LDAP search returned ".$mesg->count()." objects") if $verbose;
- foreach $ldapobject ($mesg->entries) {
- mylog($syslog_priority, "Got object ".$ldapobject->get_value('uid') ) if $verbose;
- mylog($syslog_priority, "Got delegates ".join(", ", @{$ldapobject->get_value('kolabdelegate', asref => 1 )})) if $verbose;
- if( $username && ( lc($username) eq lc($ldapobject->get_value('uid')) ||
- lc($username) eq lc($ldapobject->get_value('mail')) ||
- contains( $username, $ldapobject->get_value('kolabdelegate', asref => 1 ))) ) {
- # All OK, user is sending as herself or as kolabdelegate
- mylog($syslog_priority, "$username using valid from address $sender") if $verbose;
- goto CHECKDISTLIST;
- }
+ foreach my $entry ( $mesg->entries ) {
+ mylog($syslog_priority, lc($entry->get_value('uid')." is allowed to use ".$sender)) if $verbose;
+ push @result, lc($entry->get_value('uid'));
+ }
+ } elsif( $mesg->code && $mesg->code != LDAP_NO_SUCH_OBJECT ) {
+ if( $tries++ <= $ldap_max_tries ) {
+ ldap_connect;
+ goto AGAIN;
+ } else {
+ die( "LDAP Error looking up uid for sender £sender: ".$mesg->error );
}
- } else {
- # LDAP error?
- if( $mesg->code != LDAP_NO_SUCH_OBJECT && $tries++ <= $ldap_max_tries ) {
- mylog($syslog_priority, "LDAP Connection error during CHECKSENDER: ".$mesg->error.", trying to reconnect" );
- ldap_connect;
- goto CHECKSENDER;
- } else {
- mylog($syslog_priority, "Query returned error during CHECKSENDER: ".$mesg->error ) if $verbose;
- return "DEFER_IF_PERMIT Temporary LDAP error during CHECKSENDER: ".$mesg->error;
- }
}
- if( $conf_allowunauth && !$username ) {
- # Dont reject mail from other domains
- $sender =~ /(.*)@(.*)/;
- my $domain = $2;
- mylog($syslog_priority, "sender=$sender, domain=$domain") if $verbose;
- if( !contains( $domain, \@conf_domain ) ) {
- # Ok
- mylog($syslog_priority, "sending from other domains OK") if $verbose;
- goto CHECKDISTLIST;
+ return @result;
+};
+
+sub check_dist_list {
+ my $username = shift;
+ my $recipient = shift;
+ my $tries = 0;
+ AGAIN:
+ if( !$username ) {
+ my $mesg = $ldap->search( base=> "cn=internal,".$conf_basedn,
+ scope=> 'one', filter=> "(&(mail=$recipient)(objectClass=kolabgroupofnames))");
+ if( !$mesg->code && $mesg->count() > 0 ) {
+ # Ups, recipient is a restricted list, reject
+ mylog( $syslog_priority, "Attempt from $username to access restricted list $recipient" ) if $verbose;
+ return undef;
+ } elsif( $mesg->code && $mesg->code != LDAP_NO_SUCH_OBJECT && $tries++ <= $ldap_max_tries ) {
+ mylog($syslog_priority, "LDAP Connection error during CHECKDISTLIST: ".$mesg->error.", trying to reconnect" );
+ ldap_connect;
+ goto AGAIN;
+ } elsif( $mesg->code ) {
+ mylog( $syslog_priority, "LDAP Error during CHECKDISTLIST: ".$mesg->error ) if $verbose;
+ # Just fall through and accept the message in case there was an LDAP problem.
}
}
- # UPS, fake sender
- mylog($syslog_priority, "Attempt to fake address $sender") if $verbose;
- return "REJECT Invalid sender";
+ return 1;
+}
- # Check for valid access to restricted distribution lists
- $tries = 0;
- CHECKDISTLIST:
- if( !$username or $username eq '' ) {
- $recip =~ /(.*)@(.*)/;
- my $cn = $1;
- my $domain = $2;
- if( contains($domain,\@conf_domain ) ) {
- $mesg = $ldap->search( base=> "cn=internal,".$conf_basedn,
- scope=> 'one', filter=> "(&(cn=$cn)(objectClass=kolabgroupofnames))");
- if( !$mesg->code && $mesg->count() > 0 ) {
- # Ups, recipient is a restricted list, reject
- mylog( $syslog_priority, "Attempt from $sender to access restricted list $recip" ) if $verbose;
- return "REJECT Access denied";
- } elsif( $mesg->code && $mesg->code != LDAP_NO_SUCH_OBJECT && $tries++ <= $ldap_max_tries ) {
- mylog($syslog_priority, "LDAP Connection error during CHECKDISTLIST: ".$mesg->error.", trying to reconnect" );
- ldap_connect;
- goto CHECKDISTLIST;
- } elsif( $mesg->code ) {
- mylog( $syslog_priority, "LDAP Error during CHECKDISTLIST: ".$mesg->error ) if $verbose;
- # Just fall through and accept the message in case there was an LDAP problem.
- }
+#
+# SMTPD access policy routine. The result is an action just like
+# it would be specified on the right-hand side of a Postfix access
+# table. Request attributes are available via the %attr hash.
+#
+sub smtpd_access_policy {
+
+ # Get relevant attributes
+ my $sender = lc($attr{'sender'});
+ my $recipient = lc($attr{'recipient'});
+ my $username = lc($attr{'sasl_username'});
+ my $client_addr = lc($attr{'client_address'});
+
+ mylog($syslog_priority, "Checking sender=\"$sender\", recipient=\"$recipient\", username=\"$username\", domains=".join(',', at conf_domain)." permithosts=".join(',', at conf_permithosts).", conf_allowunauth=$conf_allowunauth") if $verbose;
+
+ # First check if the sender is a privileged kolabhost
+ # Kolab hosts use un-authenticated smtp currently
+ return "DUNNO" if( !$username && check_permithosts($client_addr) );
+
+ # Reject anything else from unauthenticated users
+ # if conf_allowunauth is false
+ return "REJECT Acces denied" if( !$username && !$conf_allowunauth );
+
+ eval{ $username = lookup_uid($username) }; return "DEFER_IF_PERMIT $@" if $@;
+
+ # See if sender is owned by someone
+ my @uids;
+ eval { @uids = lookup_sender_uids($sender) }; return "DEFER_IF_PERMIT $@" if $@;
+ if( scalar(@uids) > 0 ) {
+ if( contains( $username, \@uids ) ) {
+ mylog($syslog_priority, "$username using $sender is OK, accepting") if $verbose;
+ return "DUNNO";
+ } else {
+ mylog($syslog_priority, "$username trying to use $sender is NOT OK, rejecting") if $verbose;
+ return "REJECT Invalid sender";
+ }
+ } else {
+ # OK, here things get fishy! The above check
+ # ensures that nobody is using someone else's
+ # email address. That is perfectly valid, but
+ # people want tighter restrictions and disallow
+ # use of _any_ (real or imagined) email address
+ # that the user is not explicitly allowed to use.
+ # Do _have_ to allow the empty sender though,
+ # otherwise hell breaks loose...
+ if( $username ne '' && $sender ne '' ) {
+ mylog($syslog_priority, "$username trying to use $sender is NOT OK, rejecting") if $verbose;
+ return "REJECT Invalid sender";
}
}
+ # Check for valid access to restricted distribution lists
+ return "REJECT Access denied" unless check_dist_list($username, $recipient);
+
# The result can be any action that is allowed in a Postfix access(5) map.
#
# To label mail, return ``PREPEND'' headername: headertext
@@ -273,7 +311,7 @@
# In case of failure, specify ``DEFER_IF_PERMIT optional text...''
# so that mail can still be blocked by other access restrictions.
- mylog($syslog_priority, "sender $sender, recipient $recip seems ok") if $verbose;
+ mylog($syslog_priority, "sender $sender, recipient $recipient seems ok") if $verbose;
return "DUNNO";
}
@@ -305,22 +343,9 @@
openlog $0, $syslog_options, $syslog_facility;
#
-# Read options from config-file
-#
-$conf_allowunauth = 0;
-%conf = readConfig( %conf, "@sysconfdir@/kolab/kolab_smtpdpolicy.conf" );
-$conf_ldapuri = $conf{'ldap_uri'};
-$conf_basedn = $conf{'basedn'};
-$conf_bindn = $conf{'binddn'};
-$conf_bindpw = $conf{'bindpw'};
-$conf_domain = $conf{'domain'};
-$conf_allowunauth = 1 if( $conf{'allow_unauth'} );
- at conf_permithosts = split /\s*,\s*/, $conf{'permithosts'};
-
-#
# Allow user to override on commandline
#
-while ($option = shift(@ARGV)) {
+while (my $option = shift(@ARGV)) {
if ($option eq "-v") {
$verbose = 1;
} elsif ($option eq '-ldap') {
@@ -370,9 +395,9 @@
mylog( $syslog_priority, "Attribute: %s=%s", $_, $attr{$_});
}
}
- fatal_exit "unrecognized request type: '%s'", $attr{'request'}
+ fatal_exit("unrecognized request type: '".$attr{'request'}."'")
unless $attr{'request'} eq "smtpd_access_policy";
- $action = smtpd_access_policy();
+ my $action = smtpd_access_policy();
mylog( $syslog_priority, "Action: %s", $action) if $verbose;
print STDOUT "action=$action\n\n";
%attr = ();
More information about the commits
mailing list