@ -1,3 +1,4 @@
from argparse import Action
import Milter
from ldap3 import (
Server , Connection , NONE , set_config_parameter
@ -10,6 +11,7 @@ import logging
import string
import random
import re
from timeit import default_timer as timer
import email . utils
import authres
@ -27,7 +29,9 @@ g_ldap_query = '(&(mail=%rcpt%)(allowedEnvelopeSender=%from%))'
g_re_domain = re . compile ( r ' ^ \ S*@( \ S+)$ ' )
# http://emailregex.com/ -> Python
g_re_email = re . compile ( r " (^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+ \ .[a-zA-Z0-9-.]+$) " )
g_loglevel = logging . INFO
g_milter_mode = ' test '
g_milter_default_policy = ' reject '
g_milter_schema = False
g_milter_schema_wildcard_domain = False # works only if g_milter_schema == True
g_milter_expect_auth = False
@ -35,14 +39,10 @@ g_milter_whitelisted_rcpts = {}
g_milter_dkim_enabled = False
g_milter_trusted_authservid = None
g_re_srs = re . compile ( r " ^SRS0=.+=.+=( \ S+)=( \ S+) \ @.+$ " )
g_milter_max_rcpt_enabled = False
g_milter_max_rcpt = 1
class LamException ( Exception ) :
def __init__ ( self , message = " General exception message " ) :
self . message = message
def __str__ ( self ) :
return self . message
class LamSoftException ( LamException ) :
pass
@ -56,43 +56,8 @@ class LdapAclMilter(Milter.Base):
# client_addr gets overriden on any connect()
self . client_addr = None
def do_log ( self , * * kwargs ) :
if ' level ' not in kwargs :
print ( " do_log(): ' level ' arg missing! " )
sys . exit ( 1 )
if ' log_message ' not in kwargs :
print ( " do_log(): ' log_message ' arg missing! " )
sys . exit ( 1 )
log_line = ' '
if hasattr ( self , ' mconn_id ' ) :
log_line = " {} " . format ( self . mconn_id )
if self . queue_id != ' invalid ' :
log_line = " {0} / {1} " . format ( log_line , self . queue_id )
if self . proto_stage != ' invalid ' :
log_line = " {0} / {1} " . format ( log_line , self . proto_stage )
log_line = " {0} {1} " . format ( log_line , kwargs [ ' log_message ' ] )
if kwargs [ ' level ' ] == ' error ' :
logging . error ( log_line )
elif kwargs [ ' level ' ] == ' warn ' or kwargs [ ' level ' ] == ' warning ' :
logging . warning ( log_line )
elif kwargs [ ' level ' ] == ' info ' :
logging . info ( log_line )
elif kwargs [ ' level ' ] == ' debug ' :
logging . debug ( log_line )
else :
print ( " do_log(): invalid ' level ' {} " . format ( kwargs [ ' level ' ] ) )
sys . exit ( 1 )
def log_error ( self , log_message ) :
self . do_log ( level = ' error ' , log_message = log_message )
def log_warn ( self , log_message ) :
self . do_log ( level = ' warn ' , log_message = log_message )
def log_info ( self , log_message ) :
self . do_log ( level = ' info ' , log_message = log_message )
def log_debug ( self , log_message ) :
self . do_log ( level = ' debug ' , log_message = log_message )
def reset ( self ) :
self . proto_stage = ' invalid '
self . proto_stage = ' proto-stage '
self . env_from = None
self . sasl_user = None
self . x509_subject = None
@ -104,7 +69,7 @@ class LdapAclMilter(Milter.Base):
self . dkim_valid = False
self . dkim_aligned = False
self . passed_dkim_results = [ ]
self . log_ debug( " reset(): {} " . format ( self . __dict__ ) )
logging . debug( " reset(): {} " . format ( self . __dict__ ) )
# https://stackoverflow.com/a/2257449
self . mconn_id = g_milter_name + ' : ' + ' ' . join (
random . choice ( string . ascii_lowercase + string . digits ) for _ in range ( 8 )
@ -142,33 +107,37 @@ class LdapAclMilter(Milter.Base):
if ' reason ' in kwargs :
message = " {0} - reason: {1} " . format ( message , kwargs [ ' reason ' ] )
if kwargs [ ' action ' ] == ' reject ' or kwargs [ ' action ' ] == ' tmpfail ' :
self . log_info ( " milter_action= {0} message= {1} " . format (
kwargs [ ' action ' ] , message
) )
self . setreply ( smtp_code , smtp_ecode , message )
logging . info ( self . mconn_id + " / " +
self . proto_stage + " : milter_action= {0} message= {1} " . format ( kwargs [ ' action ' ] , message )
)
return smfir
def check_policy ( self , * * kwargs ) :
from_addr = kwargs [ ' from_addr ' ]
rcpt_addr = kwargs [ ' rcpt_addr ' ]
from_source = kwargs [ ' from_source ' ]
def check_policy ( self , from_addr , rcpt_addr ) :
logging . info ( self . mconn_id +
" / {0} from= {1} rcpt= {2} " . format (
self . proto_stage , from_addr , rcpt_addr
)
)
m = g_re_domain . match ( from_addr )
if m == None :
self . log_info ( " Could not determine domain of from= {0} " . format (
from_addr
) )
raise LamSoftException ( )
from_domain = m . group ( 1 )
self . log_debug ( " from_domain= {} " . format ( from_domain ) )
m = g_re_domain . match ( rcpt_addr )
if m == None :
raise LamHardException (
" Could not determine domain of rcpt= {0} " . format (
rcpt_addr
logging . info ( self . mconn_id +
" / {0} Could not determine domain of from= {1} " . format (
self . proto_stage , from_addr
)
)
raise LamSoftException ( )
from_domain = m . group ( 1 )
logging . debug ( self . mconn_id +
" / {0} from_domain= {1} " . format ( self . queue_id , from_domain )
)
m = g_re_domain . match ( rcpt_addr )
if m == None :
raise LamSoftException ( " Could not determine domain of rcpt= {} " . format ( rcpt_addr ) )
rcpt_domain = m . group ( 1 )
self . log_debug ( " rcpt_domain= {} " . format ( rcpt_domain ) )
logging . debug ( self . mconn_id +
" / {0} rcpt_domain= {1} " . format ( self . queue_id , rcpt_domain )
)
try :
if g_milter_schema == True :
# LDAP-ACL-Milter schema
@ -190,7 +159,9 @@ class LdapAclMilter(Milter.Base):
)
else :
auth_method = auth_method . replace ( ' %X 509_AUTH % ' , ' ' )
self . log_debug ( " auth_method: {} " . format ( auth_method ) )
logging . debug ( self . mconn_id +
" auth_method: " + auth_method
)
if g_milter_schema_wildcard_domain == True :
# The asterisk (*) character is in term of local part
# RFC5322 compliant and expected as a wildcard literal in this code.
@ -240,29 +211,32 @@ class LdapAclMilter(Milter.Base):
if len ( g_ldap_conn . entries ) == 0 :
# Policy not found in LDAP
if g_milter_expect_auth == True :
self . log_info (
" policy mismatch: from= {0} from_src= {1} rcpt= {2} auth_method= {3} " . format (
from_addr , from_source , rcpt_addr , auth_method
)
logging . info ( self . mconn_id + " " + " policy mismatch "
" from= " + from_addr + " , rcpt= " + rcpt_addr +
" , auth_method= " + auth_method
)
else :
self . log_info (
" policy mismatch: from= {0} from_src= {1} rcpt= {2} " . format (
from_addr , from_source , rcpt_addr
)
logging . info ( self . mconn_id + " " + " policy mismatch "
" from= " + f rom_addr + " , rcpt= " + rcpt_addr
)
if g_milter_mode == ' reject ' :
raise LamHardException ( " policy not found! " )
else :
logging . info ( self . mconn_id + " TEST_MODE " +
g_milter_reject_message
)
raise LamHardException ( " policy mismatch! " )
elif len ( g_ldap_conn . entries ) == 1 :
# Policy found in LDAP, but which one?
entry = g_ldap_conn . entries [ 0 ]
self . log_info ( " policy match: ' {0} ' from_src= {1} " . format (
entry . policyID . value , from_source
) )
logging . info ( self . mconn_id +
" / {0} Policy match: {1} " . format ( self . proto_stage , entry . policyID . value )
)
elif len ( g_ldap_conn . entries ) > 1 :
# Something went wrong!? There shouldn´ t be more than one entries!
self . log_warn ( " More than one policies found! from= {0} rcpt= {1} auth_method= {2} " . format (
from_addr , rcpt_addr , auth_method
) )
logging . warning ( self . mconn_id + " More than one policies found! " +
" from= " + from_addr + " , rcpt= " + rcpt_addr +
" , auth_method= " + auth_method
)
raise LamHardException ( " More than one policies found! " )
else :
# Custom LDAP schema
@ -274,21 +248,21 @@ class LdapAclMilter(Milter.Base):
query = query . replace ( " %s asl_user % " , self . sasl_user )
query = query . replace ( " %f rom_domain % " , from_domain )
query = query . replace ( " %r cpt_domain % " , rcpt_domain )
self . log_debug ( " LDAP query: {} " . format ( query ) )
logging . debug ( self . mconn_id + " " + query )
g_ldap_conn . search ( g_ldap_base , query )
if len ( g_ldap_conn . entries ) == 0 :
self . log_info (
" policy mismatch from= {0} from_src= {1} rcpt= {2} " . format (
from_addr , from_source , rcpt_addr
)
logging . info ( self . mconn_id + " " + " policy mismatch "
" from: " + from_addr + " and rcpt: " + rcpt_addr
)
raise LamHardException ( " policy mismatch " )
self . log_info ( " policy match: ' {0} ' from_src= {1} " . format (
entry . policyID . value , from_source
) )
if g_milter_mode == ' reject ' :
raise LamHardException ( " policy mismatch " )
else :
logging . info ( self . mconn_id + " TEST_MODE " +
g_milter_reject_message
)
except LDAPException as e :
self . log_error ( " LDAP exception: {} " . format ( str ( e ) ) )
raise LamSoftException ( " LDAP exception : " + str ( e ) ) from e ;
logging . error ( self . mconn_id + " LDAP: " + str ( e ) )
raise LamSoftException ( " LDAP: " + str ( e ) ) from e ;
return self . milter_action ( action = ' continue ' )
# Not registered/used callbacks
@ -303,8 +277,8 @@ class LdapAclMilter(Milter.Base):
self . reset ( )
self . proto_stage = ' CONNECT '
self . client_addr = hostaddr [ 0 ]
self . log_debug ( " client_addr= {0} , client_port= {1} " . format (
self . client_addr , hostaddr [ 1 ] )
logging . debug ( self . mconn_id +
" /CONNECT client_addr=[ " + self . client_addr + " ]: " + str ( hostaddr [ 1 ] )
)
return self . milter_action ( action = ' continue ' )
@ -321,29 +295,29 @@ class LdapAclMilter(Milter.Base):
x509_subject = self . getsymval ( ' {cert_subject} ' )
if x509_subject != None :
self . x509_subject = x509_subject
self . log_debug ( " x509_subject= {} " . format ( self . x509_subject ) )
logging . debug ( self . mconn_id + " /FROM x509_subject= " + self . x509_subject )
else :
self . log_debug ( " No x509_subject registered" )
logging . debug ( self . mconn_id + " /FROM No x509_subject registered" )
x509_issuer = self . getsymval ( ' {cert_issuer} ' )
if x509_issuer != None :
self . x509_issuer = x509_issuer
self . log_debug ( " x509_issuer= {} " . format ( self . x509_issuer ) )
logging . debug ( self . mconn_id + " /FROM x509_issuer= " + self . x509_issuer )
else :
self . log_debug ( " No x509_issuer registered" )
logging . debug ( self . mconn_id + " /FROM No x509_issuer registered" )
except :
self . log_error ( " x509 exception: {} " . format ( traceback . format_exc ( ) ) )
logging . error ( self . mconn_id + " /FROM x509 " + traceback . format_exc ( ) )
try :
# this may fail, if no SASL authentication preceded
sasl_user = self . getsymval ( ' {auth_authen} ' )
if sasl_user != None :
self . sasl_user = sasl_user
self . log_debug ( " sasl_user= {} " . format ( self . sasl_user ) )
logging . debug ( self . mconn_id + " /FROM sasl_user= " + self . sasl_user )
else :
self . log_debug ( " No sasl_user registered" )
logging . debug ( self . mconn_id + " /FROM No sasl_user registered" )
except :
self . log_error ( " sasl_user exception: {} " . format ( traceback . format_exc ( ) ) )
self . log_info (
" auth: client_ip={0} x509_subject={1} x509_issuer={2} sasl_user={3} " . format (
logging . error ( self . mconn_id + " /FROM sasl_user " + traceback . format_exc ( ) )
logging . info ( self . mconn_id + " /FROM auth: " +
" client_ip={0} , x509_subject={1} , x509_issuer={2} , sasl_user={3} " . format (
self . client_addr , self . x509_subject , self . x509_issuer , self . sasl_user
)
)
@ -355,16 +329,20 @@ class LdapAclMilter(Milter.Base):
# SRS (https://www.libsrs2.org/srs/srs.pdf)
m_srs = g_re_srs . match ( mailfrom )
if m_srs != None :
self . log_info ( " Found SRS-encoded envelope-sender: {} " . format ( mailfrom ) )
logging . info ( self . mconn_id + " /FROM " +
" Found SRS-encoded envelope-sender: " + mailfrom
)
mailfrom = m_srs . group ( 2 ) + ' @ ' + m_srs . group ( 1 )
self . log_info ( " SRS envelope-sender replaced with: {} " . format ( mailfrom ) )
logging . info ( self . mconn_id + " /FROM " +
" SRS envelope-sender replaced with: " + mailfrom
)
self . env_from = mailfrom . lower ( )
self . log_debug ( " 5321.from= {} " . format ( self . env_from ) )
logging . debug ( self . mconn_id + " /FROM 5321.from={} " . format ( self . env_from ) )
m = g_re_domain . match ( self . env_from )
if m == None :
return self . milter_action (
action = ' rejec t' ,
reason = " Could not determine domain of 5321.from= {} " . format ( self . env_from )
action = ' tmpfail ' ,
reason = " Could not determine domain of 5321.from= " + self . env_from
)
return self . milter_action ( action = ' continue ' )
@ -373,7 +351,9 @@ class LdapAclMilter(Milter.Base):
to = to . replace ( " < " , " " )
to = to . replace ( " > " , " " )
to = to . lower ( )
self . log_debug ( " 5321.rcpt= {} " . format ( to ) )
logging . debug ( self . mconn_id +
" /RCPT env_rcpt= {} " . format ( to )
)
if to in g_milter_whitelisted_rcpts :
return self . milter_action ( action = ' continue ' )
if g_milter_dkim_enabled :
@ -383,20 +363,14 @@ class LdapAclMilter(Milter.Base):
self . env_rcpts . append ( to )
else :
try :
return self . check_policy (
from_addr = self . env_from , rcpt_addr = to , from_source = ' 5321.from '
)
return self . check_policy ( self . env_from , to )
except LamSoftException as e :
if g_milter_mode == ' reject ' :
return self . milter_action ( action = ' tmpfail ' )
return self . milter_action ( action = ' tmpfail ' )
except LamHardException as e :
if g_milter_mode == ' reject ' :
return self . milter_action (
action = ' reject ' ,
reason = e . message
)
else :
self . log_info ( " TEST-Mode: {} " . format ( e . message ) )
return self . milter_action (
action = ' reject ' ,
reason = e . message
)
return self . milter_action ( action = ' continue ' )
def header ( self , hname , hval ) :
@ -414,9 +388,11 @@ class LdapAclMilter(Milter.Base):
reason = " Could not determine domain-part of 5322.from= " + self . hdr_from
)
self . hdr_from_domain = m . group ( 1 )
self . log_debug ( " 5322.from= {0} , 5322.from_domain= {1} " . format (
self . hdr_from , self . hdr_from_domain
) )
logging . info ( self . mconn_id + " / " + str ( self . queue_id ) +
" /HDR: 5322.from= {0} , 5322.from_domain= {1} " . format (
self . hdr_from , self . hdr_from_domain
)
)
# Parse RFC-7601 Authentication-Results header
elif ( hname . lower ( ) == " Authentication-Results " . lower ( ) ) :
ar = None
@ -429,74 +405,61 @@ class LdapAclMilter(Milter.Base):
if ar_result . method . lower ( ) == ' dkim ' :
if ar_result . result . lower ( ) == ' pass ' :
self . passed_dkim_results . append ( ar_result . header_d . lower ( ) )
self . log_debug ( " dkim=pass sdid= {} " . format ( ar_result . header_d ) )
logging . debug ( self . mconn_id + " / " + str ( self . queue_id ) +
" /HDR: dkim=pass sdid= {0} " . format ( ar_result . header_d )
)
self . dkim_valid = True
else :
self . log_debug ( " Ignoring authentication results of {} " . format (
ar . authserv_id )
logging . debug ( self . mconn_id + " / " + str ( self . queue_id ) +
" /HDR: Ignoring authentication results of {0} " . format ( ar . authserv_id )
)
except Exception as e :
self . log_info ( " AR-parse exception: {0} " . format ( str ( e ) ) )
logging . info ( self . mconn_id + " / " + str ( self . queue_id ) +
" /HDR: AR-parse exception: {0} " . format ( str ( e ) )
)
return self . milter_action ( action = ' continue ' )
def eom ( self ) :
self . proto_stage = ' EOM '
if g_milter_max_rcpt_enabled :
if len ( self . env_rcpts ) > int ( g_milter_max_rcpt ) :
if g_milter_mode == ' reject ' :
return self . milter_action ( action = ' reject ' , reason = ' Too many recipients! ' )
else :
self . do_log ( " TEST-Mode: Too many recipients! " )
if g_milter_dkim_enabled :
self . log_info ( " 5321.from= {0} 5322.from= {1} 5322.from_domain= {2} 5321.rcpt= {3} " . format (
self . env_from , self . hdr_from , self . hdr_from_domain , self . env_rcpts
) )
if self . dkim_valid :
# There is at least one valid DKIM signature!
# Check if one of them is also aligned
for passed_dkim_sdid in self . passed_dkim_results :
if self . hdr_from_domain . lower ( ) == passed_dkim_sdid . lower ( ) :
self . dkim_aligned = True
self . log_info ( " Found aligned DKIM signature for SDID: {0} " . format (
passed_dkim_sdid
) )
logging . info ( self . mconn_id + " / " + str ( self . queue_id ) +
" /EOM: Found aligned DKIM signature for SDID: {0} " . format (
passed_dkim_sdid
)
)
reject_message = False
for rcpt in self . env_rcpts :
try :
# Check 5321.from against policy
self . check_policy (
from_addr = self . env_from , rcpt_addr = rcpt , from_source = ' 5321.from '
)
# Check 5321.sender against policy
self . check_policy ( self . env_from , rcpt )
except LamSoftException as e :
if g_milter_mode == ' reject ' :
return self . milter_action ( action = ' tmpfail ' )
else :
self . log_info ( " TEST-Mode: {} " . format ( e . message ) )
return self . milter_action ( action = ' tmpfail ' )
except LamHardException as e :
if self . dkim_aligned :
try :
# Check 5322.from against policy
self . check_policy (
from_addr = self . hdr_from , rcpt_addr = rcpt , from_source = ' 5322.from '
# Check 5322.sender against policy
self . check_policy ( self . hdr_from , rcpt )
logging . info ( self . mconn_id +
" / {0} / {1} from= {2} authorized by DKIM signature " . format (
self . queue_id , self . proto_stage , self . hdr_from
)
)
self . log_info ( " 5322.from= {} authorized by DKIM signature " . format (
self . hdr_from
) )
except LamHardException as e :
reject_message = True
else :
reject_message = True
if reject_message :
if g_milter_mode == ' reject ' :
return self . milter_action (
action = ' reject ' ,
reason = ' policy mismatch! Message rejected for all recipients! '
)
else :
self . log_info (
" TEST-Mode: policy mismatch! Message would be rejected for all recipients! "
)
return self . milter_action (
action = ' reject ' ,
reason = ' EOM - Policy mismatch! All recipients were rejected! '
)
return self . milter_action ( action = ' continue ' )
def abort ( self ) :
@ -512,25 +475,30 @@ class LdapAclMilter(Milter.Base):
if __name__ == " __main__ " :
try :
log_level = logging . INFO
if ' LOG_LEVEL ' in os . environ :
if re . match ( r ' ^info$ ' , os . environ [ ' LOG_LEVEL ' ] , re . IGNORECASE ) :
log_ level = logging . INFO
g_ loglevel = logging . INFO
elif re . match ( r ' ^warn|warning$ ' , os . environ [ ' LOG_LEVEL ' ] , re . IGNORECASE ) :
log_ level = logging . WARN
g_ loglevel = logging . WARN
elif re . match ( r ' ^error$ ' , os . environ [ ' LOG_LEVEL ' ] , re . IGNORECASE ) :
log_ level = logging . ERROR
g_ loglevel = logging . ERROR
elif re . match ( r ' debug ' , os . environ [ ' LOG_LEVEL ' ] , re . IGNORECASE ) :
log_level = logging . DEBUG
log_format = ' %(asctime)s : %(levelname)s %(message)s '
g_loglevel = logging . DEBUG
logging . basicConfig (
filename = None , # log to stdout
format = log_format ,
level = log_ level
filename = None , # log to stdout
format = ' %(asctime)s : %(levelname)s %(message)s ' ,
level = g_ loglevel
)
if ' MILTER_MODE ' in os . environ :
if re . match ( r ' ^test|reject$ ' , os . environ [ ' MILTER_MODE ' ] , re . IGNORECASE ) :
g_milter_mode = os . environ [ ' MILTER_MODE ' ] . lower ( )
g_milter_mode = os . environ [ ' MILTER_MODE ' ]
if ' MILTER_DEFAULT_POLICY ' in os . environ :
if re . match ( r ' ^reject|permit$ ' , os . environ [ ' MILTER_DEFAULT_POLICY ' ] , re . IGNORECASE ) :
g_milter_default_policy = str ( os . environ [ ' MILTER_DEFAULT_POLICY ' ] ) . lower ( )
else :
logging . warning ( " MILTER_DEFAULT_POLICY invalid value: " +
os . environ [ ' MILTER_DEFAULT_POLICY ' ]
)
if ' MILTER_NAME ' in os . environ :
g_milter_name = os . environ [ ' MILTER_NAME ' ]
if ' MILTER_SCHEMA ' in os . environ :
@ -540,7 +508,7 @@ if __name__ == "__main__":
if re . match ( r ' ^true$ ' , os . environ [ ' MILTER_SCHEMA_WILDCARD_DOMAIN ' ] , re . IGNORECASE ) :
g_milter_schema_wildcard_domain = True
if ' LDAP_SERVER ' not in os . environ :
logging . error ( " Missing ENV[LDAP_SERVER], e.g. {} " . format ( g_ldap_server ) )
logging . error ( " Missing ENV[LDAP_SERVER], e.g. " + g_ldap_server )
sys . exit ( 1 )
g_ldap_server = os . environ [ ' LDAP_SERVER ' ]
if ' LDAP_BINDDN ' in os . environ :
@ -548,7 +516,7 @@ if __name__ == "__main__":
if ' LDAP_BINDPW ' in os . environ :
g_ldap_bindpw = os . environ [ ' LDAP_BINDPW ' ]
if ' LDAP_BASE ' not in os . environ :
logging . error ( " Missing ENV[LDAP_BASE], e.g. {} " . format ( g_ldap_base ) )
logging . error ( " Missing ENV[LDAP_BASE], e.g. " + g_ldap_base )
sys . exit ( 1 )
g_ldap_base = os . environ [ ' LDAP_BASE ' ]
if ' LDAP_QUERY ' not in os . environ :
@ -574,57 +542,43 @@ if __name__ == "__main__":
for whitelisted_rcpt in re . split ( ' ,| \ s ' , whitelisted_rcpts_str ) :
if g_re_email . match ( whitelisted_rcpt ) == None :
logging . error (
" ENV[MILTER_WHITELISTED_RCPTS]: invalid email address: {} "
. format ( whitelisted_rcpt )
" ENV[MILTER_WHITELISTED_RCPTS]: invalid email address: " +
whitelisted_rcpt
)
sys . exit ( 1 )
else :
logging . info ( " ENV[MILTER_WHITELISTED_RCPTS]: {} " . format (
whitelisted_rcpt
) )
logging . info ( " ENV[MILTER_WHITELISTED_RCPTS]: " + whitelisted_rcpt )
g_milter_whitelisted_rcpts [ whitelisted_rcpt ] = { }
if ' MILTER_DKIM_ENABLED ' in os . environ :
g_milter_dkim_enabled = True
if ' MILTER_TRUSTED_AUTHSERVID ' in os . environ :
g_milter_trusted_authservid = os . environ [ ' MILTER_TRUSTED_AUTHSERVID ' ] . lower ( )
logging . info ( " ENV[MILTER_TRUSTED_AUTHSERVID]: {0} " . format (
g_milter_trusted_authservid
) )
logging . info ( " ENV[MILTER_TRUSTED_AUTHSERVID]: {0} " . format ( g_milter_trusted_authservid ) )
else :
logging . error ( " ENV[MILTER_TRUSTED_AUTHSERVID] is mandatory! " )
sys . exit ( 1 )
logging . info ( " ENV[MILTER_DKIM_ENABLED]: {0} " . format ( g_milter_dkim_enabled ) )
if ' MILTER_MAX_RCPT_ENABLED ' in os . environ :
g_milter_max_rcpt_enabled = True
if ' MILTER_MAX_RCPT ' in os . environ :
if os . environ [ ' MILTER_MAX_RCPT ' ] . isnumeric ( ) :
g_milter_max_rcpt = os . environ [ ' MILTER_MAX_RCPT ' ]
else :
print ( " ENV[MILTER_MAX_RCPT] must be numeric! " )
sys . exit ( 1 )
try :
set_config_parameter ( " RESTARTABLE_SLEEPTIME " , 2 )
set_config_parameter ( " RESTARTABLE_TRIES " , 2 )
server = Server ( g_ldap_server , get_info = NONE )
g_ldap_conn = Connection ( server ,
g_ldap_binddn , g_ldap_bindpw ,
auto_bind = True , raise_exceptions = True ,
client_strategy = ' RESTARTABLE '
)
logging . info ( " Connected to LDAP-server: " + g_ldap_server )
except LDAPException as e :
raise Exception ( " Connection to LDAP-server failed: {} " . format ( str ( e ) ) ) from e
set_config_parameter ( " RESTARTABLE_SLEEPTIME " , 2 )
set_config_parameter ( " RESTARTABLE_TRIES " , 2 )
server = Server ( g_ldap_server , get_info = NONE )
g_ldap_conn = Connection ( server ,
g_ldap_binddn , g_ldap_bindpw ,
auto_bind = True , raise_exceptions = True ,
client_strategy = ' RESTARTABLE '
)
logging . info ( " Connected to LDAP-server: " + g_ldap_server )
timeout = 600
# Register to have the Milter factory create instances of your class:
Milter . factory = LdapAclMilter
# Tell the MTA which features we use
flags = Milter . ADDHDRS
Milter . set_flags ( flags )
logging . info ( " Starting {0} @socket: {1} in mode {2} " . format (
g_milter_name , g_milter_socket , g_milter_mode
) )
logging . info ( " Startup " + g_milter_name +
" @socket: " + g_milter_socket +
" in mode: " + g_milter_mode
)
Milter . runmilter ( g_milter_name , g_milter_socket , timeout , True )
logging . info ( " Shutdown {} " . format ( g_milter_name ) )
logging . info ( " Shutdown " + g_milter_name )
except :
logging . error ( " MAIN-EXCEPTION: {} " . format ( traceback . format_exc ( ) ) )
logging . error ( " MAIN-EXCEPTION: " + traceback . format_exc ( ) )
sys . exit ( 1 )