parent
4ff50e8a97
commit
7f5d5cddff
3 changed files with 222 additions and 7 deletions
@ -0,0 +1,217 @@ |
||||
#!/usr/bin/perl |
||||
# |
||||
# ***** BEGIN LICENSE BLOCK ***** |
||||
# Zimbra Collaboration Suite Server |
||||
# Copyright (C) 2008, 2009, 2010, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. |
||||
# |
||||
# This program is free software: you can redistribute it and/or modify it under |
||||
# the terms of the GNU General Public License as published by the Free Software Foundation, |
||||
# version 2 of the License. |
||||
# |
||||
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; |
||||
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
||||
# See the GNU General Public License for more details. |
||||
# You should have received a copy of the GNU General Public License along with this program. |
||||
# If not, see <https://www.gnu.org/licenses/>. |
||||
# ***** END LICENSE BLOCK ***** |
||||
|
||||
use strict; |
||||
use lib '/opt/zimbra/common/lib/perl5'; |
||||
use Sys::Syslog qw(:DEFAULT setlogsock); |
||||
use Net::LDAPapi; |
||||
use XML::Simple; |
||||
|
||||
# |
||||
# Syslogging options for verbose mode and for fatal errors. |
||||
# NOTE: comment out the $syslog_socktype line if syslogging does not |
||||
# work on your system. |
||||
# |
||||
my $syslog_socktype = 'unix'; |
||||
my $syslog_facility="mail"; |
||||
my $syslog_options="pid"; |
||||
our $syslog_priority="info"; |
||||
our ($verbose, %attr, @ldap_url, $ldap_starttls_supported, $postfix_pw, $zimbra_pw, $delim_re); |
||||
my ($option, $action, $ldap_url, @val); |
||||
|
||||
$ENV{'HOME'}='/opt/zimbra'; |
||||
setlogsock $syslog_socktype; |
||||
openlog $0, $syslog_options, $syslog_facility; |
||||
|
||||
my $localxml = XMLin("/opt/zimbra/conf/localconfig.xml"); |
||||
|
||||
$ldap_starttls_supported = $localxml->{key}->{ldap_starttls_supported}->{value}; |
||||
chomp ($ldap_starttls_supported); |
||||
$postfix_pw = $localxml->{key}->{ldap_postfix_password}->{value}; |
||||
chomp($postfix_pw); |
||||
$zimbra_pw = $localxml->{key}->{zimbra_ldap_password}->{value}; |
||||
chomp($zimbra_pw); |
||||
$ldap_url = $localxml->{key}->{ldap_url}->{value}; |
||||
chomp($ldap_url); |
||||
@ldap_url = split / /, $ldap_url; |
||||
|
||||
sub smtpd_access_policy { |
||||
my($domain, $ldap, $mesg, $user, $canon_user, $daddr, @attrs, $result); |
||||
$daddr = lc $attr{recipient}; |
||||
($user, $domain) = split /\@/, lc $attr{recipient}; |
||||
$canon_user = (defined $delim_re) ? (split /$delim_re/, $user)[0] : $user; |
||||
syslog $syslog_priority, "Recipient Domain: %s", $domain if $verbose; |
||||
syslog $syslog_priority, "Recipient userid: %s", $user if $verbose; |
||||
foreach my $url (@ldap_url) { |
||||
$ldap=Net::LDAPapi->new(-url=>$url); |
||||
if ( $ldap_starttls_supported ) { |
||||
$mesg = $ldap->start_tls_s(); |
||||
if ($mesg != 0) { |
||||
next; |
||||
} |
||||
} |
||||
$mesg = $ldap->bind_s("uid=zmpostfix,cn=appaccts,cn=zimbra",$postfix_pw); |
||||
if ($mesg != 0) { |
||||
next; |
||||
} else { |
||||
last; |
||||
} |
||||
} |
||||
if ($mesg != 0) { |
||||
syslog $syslog_priority, "Error: zmpostfixpolicyd unable to find working LDAP server"; |
||||
return "dunno"; |
||||
} |
||||
@attrs=('zimbraDomainType', 'zimbraMailCatchAllForwardingAddress'); |
||||
$mesg = $ldap->search_s( |
||||
"", |
||||
LDAP_SCOPE_SUBTREE, |
||||
"(&(zimbraDomainName=$domain)(objectClass=zimbraDomain))", |
||||
\@attrs, |
||||
0, |
||||
$result |
||||
); |
||||
my $ent = $ldap->first_entry(); |
||||
if ($ent != 0) { |
||||
if (lc(($ldap->get_values("zimbraDomainType"))[0]) eq "alias") { |
||||
my $robject = ($ldap->get_values("zimbraMailCatchAllForwardingAddress"))[0]; |
||||
syslog $syslog_priority, "Real Domain: %s", $robject if $verbose; |
||||
@attrs=('1.1'); |
||||
$mesg = $ldap->search_s( |
||||
"", |
||||
LDAP_SCOPE_SUBTREE, |
||||
"(&(|(zimbraMailDeliveryAddress=$user"."$robject)(zimbraMailDeliveryAddress=$canon_user"."$robject)". |
||||
"(zimbraMailDeliveryAddress=$daddr)(zimbraMailAlias=$user"."$robject)(zimbraMailAlias=$canon_user"."$robject)". |
||||
"(zimbraMailAlias=$daddr)(zimbraMailCatchAllAddress=$user"."$robject)(zimbraMailCatchAllAddress=$robject)". |
||||
"(zimbraMailCatchAllAddress=$daddr))(zimbraMailStatus=enabled))", |
||||
\@attrs, |
||||
0, |
||||
$result |
||||
); |
||||
$ent = $ldap->first_entry(); |
||||
$ldap->unbind; |
||||
if ($ent != 0) { |
||||
return "dunno"; |
||||
} else { |
||||
return "reject 5.1.1 Mailbox unavailable"; |
||||
} |
||||
} else { |
||||
$ldap->unbind; |
||||
return "dunno"; |
||||
} |
||||
} |
||||
$ldap->unbind; |
||||
return "dunno"; |
||||
} |
||||
|
||||
# |
||||
# Log an error and abort. |
||||
# |
||||
sub fatal_exit { |
||||
my($first) = shift(@_); |
||||
syslog "err", "fatal: $first", @_; |
||||
exit 1; |
||||
} |
||||
|
||||
# |
||||
# We don't need getopt() for now. |
||||
# |
||||
while ($option = shift(@ARGV)) { |
||||
if ($option eq "-v") { |
||||
$verbose = 1; |
||||
} else { |
||||
syslog $syslog_priority, "Invalid option: %s. Usage: %s [-v]", |
||||
$option, $0; |
||||
exit 1; |
||||
} |
||||
} |
||||
|
||||
# |
||||
# Unbuffer standard output. |
||||
# |
||||
select((select(STDOUT), $| = 1)[0]); |
||||
|
||||
# Try to get recipient delimiter, if defined |
||||
# This will allow checking for valid recipient on alias domains |
||||
# even for recipient using delimiter. Eg user+foobar@alias.example.org |
||||
# will correctly check if user@example.org is valid |
||||
my ($ldap, $mesg, @attrs, $result); |
||||
foreach my $url (@ldap_url) { |
||||
$ldap=Net::LDAPapi->new(-url=>$url); |
||||
if ( $ldap_starttls_supported ) { |
||||
$mesg = $ldap->start_tls_s(); |
||||
if ($mesg != 0) { |
||||
next; |
||||
} |
||||
} |
||||
$mesg = $ldap->bind_s("uid=zimbra,cn=admins,cn=zimbra",$zimbra_pw); |
||||
if ($mesg != 0) { |
||||
next; |
||||
} else { |
||||
last; |
||||
} |
||||
} |
||||
if ($mesg == 0){ |
||||
@attrs=('zimbraMtaRecipientDelimiter'); |
||||
$mesg = $ldap->search_s( |
||||
"", |
||||
LDAP_SCOPE_SUBTREE, |
||||
"(&(cn=config)(objectClass=zimbraGlobalConfig))", |
||||
\@attrs, |
||||
0, |
||||
$result |
||||
); |
||||
my $ent = $ldap->first_entry(); |
||||
if ($ent != 0){ |
||||
my $delim = ($ldap->get_values('zimbraMtaRecipientDelimiter'))[0]; |
||||
if ($delim ne ''){ |
||||
$delim_re = qr{[$delim]}; |
||||
syslog $syslog_priority, "Recipient delimiter regex is $delim_re" if $verbose; |
||||
} else { |
||||
syslog $syslog_priority, "Recipient delimiter is an empty string so it won't be used" if $verbose; |
||||
} |
||||
} else { |
||||
syslog $syslog_priority, "Recipient delimiter not found" if $verbose; |
||||
} |
||||
# Unbind, everything else will bind with the postfix LDAP user |
||||
$ldap->unbind; |
||||
} else { |
||||
syslog $syslog_priority, "Couldn't bind with zimbra account, recipient delimiter won't be used" if $verbose; |
||||
} |
||||
|
||||
# |
||||
# Receive a bunch of attributes, evaluate the policy, send the result. |
||||
# |
||||
while (<STDIN>) { |
||||
if (/([^=]+)=(.*)\n/) { |
||||
$attr{substr($1, 0, 512)} = substr($2, 0, 512); |
||||
} elsif ($_ eq "\n") { |
||||
if ($verbose) { |
||||
for (keys %attr) { |
||||
syslog $syslog_priority, "Attribute: %s=%s", $_, $attr{$_}; |
||||
} |
||||
} |
||||
fatal_exit "unrecognized request type: '%s'", $attr{"request"} |
||||
unless $attr{"request"} eq "smtpd_access_policy"; |
||||
$action = smtpd_access_policy(); |
||||
syslog $syslog_priority, "Action: %s", $action if $verbose; |
||||
print STDOUT "action=$action\n\n"; |
||||
%attr = (); |
||||
} else { |
||||
chop; |
||||
syslog $syslog_priority, "warning: ignoring garbage: %.100s", $_; |
||||
} |
||||
} |
Loading…
Reference in new issue