--- /opt/zimbra/libexec/zmpostfixpolicyd.bak	2019-07-18 21:24:39.000000000 +0200
+++ /opt/zimbra/libexec/zmpostfixpolicyd	2020-11-23 10:02:37.956775250 +0100
@@ -30,7 +30,7 @@
 my $syslog_facility="mail";
 my $syslog_options="pid";
 our $syslog_priority="info";
-our ($verbose, %attr, @ldap_url, $ldap_starttls_supported, $postfix_pw);
+our ($verbose, %attr, @ldap_url, $ldap_starttls_supported, $postfix_pw, $zimbra_pw, $delim_re);
 my ($option, $action, $ldap_url, @val);
 
 $ENV{'HOME'}='/opt/zimbra';
@@ -43,14 +43,17 @@
 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, $daddr, @attrs, $result);
+  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) {
@@ -90,8 +93,9 @@
       $mesg = $ldap->search_s(
                 "",
                 LDAP_SCOPE_SUBTREE,
-                "(&(|(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=$canon_user"."$robject)".
+                "(zimbraMailAlias=$daddr)(zimbraMailCatchAllAddress=$user"."$robject)(zimbraMailCatchAllAddress=$robject)".
 		"(zimbraMailCatchAllAddress=$daddr))(zimbraMailStatus=enabled))",
                 \@attrs,
                 0,
@@ -140,6 +144,54 @@
 #
 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.
 #
