mirror of
				https://git.lapiole.org/dani/ansible-roles.git
				synced 2025-10-27 17:01:28 +01:00 
			
		
		
		
	
		
			
	
	
		
			218 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
		
		
			
		
	
	
			218 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
|   | #!/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", $_; | ||
|  |     } | ||
|  | } |