diff --git a/roles/zimbra/files/zmpostfixpolicyd b/roles/zimbra/files/zmpostfixpolicyd
new file mode 100755
index 0000000..9db8962
--- /dev/null
+++ b/roles/zimbra/files/zmpostfixpolicyd
@@ -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 .
+# ***** 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 () {
+ 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", $_;
+ }
+}
diff --git a/roles/zimbra/files/zmpostfixpolicyd_recipient_delim.patch b/roles/zimbra/files/zmpostfixpolicyd_recipient_delim.patch
index 5f28d7f..4daa128 100644
--- a/roles/zimbra/files/zmpostfixpolicyd_recipient_delim.patch
+++ b/roles/zimbra/files/zmpostfixpolicyd_recipient_delim.patch
@@ -1,5 +1,5 @@
--- /opt/zimbra/libexec/zmpostfixpolicyd.bak 2019-07-18 21:24:39.000000000 +0200
-+++ /opt/zimbra/libexec/zmpostfixpolicyd 2020-11-22 17:07:32.387815282 +0100
++++ /opt/zimbra/libexec/zmpostfixpolicyd 2020-11-23 10:02:37.956775250 +0100
@@ -30,7 +30,7 @@
my $syslog_facility="mail";
my $syslog_options="pid";
@@ -35,8 +35,8 @@
- "(&(|(zimbraMailDeliveryAddress=$user"."$robject)(zimbraMailDeliveryAddress=$daddr)(zimbraMailAlias=$user".
- "$robject)(zimbraMailAlias=$daddr)(zimbraMailCatchAllAddress=$user"."$robject)(zimbraMailCatchAllAddress=$robject)".
+ "(&(|(zimbraMailDeliveryAddress=$user"."$robject)(zimbraMailDeliveryAddress=$canon_user"."$robject)".
-+ "(zimbraMailDeliveryAddress=$daddr)(zimbraMailAlias=$user"."$robject)(zimbraMailAlias=$daddr)".
-+ "(zimbraMailCatchAllAddress=$user"."$robject)(zimbraMailCatchAllAddress=$robject)".
++ "(zimbraMailDeliveryAddress=$daddr)(zimbraMailAlias=$user"."$robject)(zimbraMailAlias=$canon_user"."$robject)".
++ "(zimbraMailAlias=$daddr)(zimbraMailCatchAllAddress=$user"."$robject)(zimbraMailCatchAllAddress=$robject)".
"(zimbraMailCatchAllAddress=$daddr))(zimbraMailStatus=enabled))",
\@attrs,
0,
diff --git a/roles/zimbra/tasks/mta.yml b/roles/zimbra/tasks/mta.yml
index 221954f..4c31bd2 100644
--- a/roles/zimbra/tasks/mta.yml
+++ b/roles/zimbra/tasks/mta.yml
@@ -13,10 +13,8 @@
changed_when: False
tags: zcs
-- name: Patch zmpostfixpolicyd to support recipient delimiter
- patch:
- src: zmpostfixpolicyd_recipient_delim.patch
- dest: /opt/zimbra/libexec/zmpostfixpolicyd
+- name: Override zmpostfixpolicyd to support recipient delimiter
+ copy: src=zmpostfixpolicyd dest=/opt/zimbra/libexec/zmpostfixpolicyd
notify: restart zimbra
tags: zcs