You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
218 lines
6.8 KiB
218 lines
6.8 KiB
4 years ago
|
#!/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", $_;
|
||
|
}
|
||
|
}
|