From f06be7b0c38abe31d48ad71fe9a45af11ebc8376 Mon Sep 17 00:00:00 2001 From: Dominik Chilla Date: Mon, 28 Feb 2022 23:41:30 +0100 Subject: [PATCH] refactoring; more logging --- app/lam.py | 99 ++++++++++------- app/lam_backends.py | 30 ++++++ app/{lam_config.py => lam_config_backend.py} | 100 ++++++++++++++---- app/lam_exceptions.py | 2 +- app/lam_globals.py | 26 ----- app/lam_policy_backend.py | 64 ++++++----- app/run_milter.py | 16 ++- tests/miltertest-ip-conn_reuse.lua | 12 --- tests/miltertest-ip-multiple_rcpts.lua | 3 - tests/miltertest-noauth.lua | 7 -- tests/miltertest-null_sender.lua | 0 tests/miltertest-sasl-wildcard.lua | 6 -- tests/miltertest-sasl.lua | 6 -- tests/miltertest-whitelisted_sender.lua | 0 tests/miltertest-x509.lua | 6 -- tests/miltertest-x509_5321-fail_dkim-pass.lua | 6 -- 16 files changed, 204 insertions(+), 179 deletions(-) create mode 100644 app/lam_backends.py rename app/{lam_config.py => lam_config_backend.py} (55%) delete mode 100644 app/lam_globals.py create mode 100644 tests/miltertest-null_sender.lua create mode 100644 tests/miltertest-whitelisted_sender.lua diff --git a/app/lam.py b/app/lam.py index cc99450..ceb3d33 100644 --- a/app/lam.py +++ b/app/lam.py @@ -5,7 +5,7 @@ import random import re import email.utils import authres -from lam_globals import g_config, g_policy_backend +from lam_backends import g_config_backend, g_policy_backend from lam_rex import g_rex_domain, g_rex_srs from lam_logger import log_debug, log_info, log_warning, log_error from lam_exceptions import LamSoftException, LamHardException @@ -57,7 +57,7 @@ class LdapAclMilter(Milter.Base): self.passed_dkim_results = [] self.log_debug("reset(): {}".format(self.__dict__)) # https://stackoverflow.com/a/2257449 - self.mconn_id = g_config.milter_name + ': ' + ''.join( + self.mconn_id = g_config_backend.milter_name + ': ' + ''.join( random.choice(string.ascii_lowercase + string.digits) for _ in range(8) ) @@ -69,12 +69,12 @@ class LdapAclMilter(Milter.Base): smtp_code = None smtp_ecode = None if kwargs['action'] == 'reject': - message = g_config.milter_reject_message + message = g_config_backend.milter_reject_message smtp_code = '550' smtp_ecode = '5.7.1' smfir = Milter.REJECT elif kwargs['action'] == 'tmpfail': - message = g_config.milter_tmpfail_message + message = g_config_backend.milter_tmpfail_message smtp_code = '450' smtp_ecode = '4.7.1' smfir = Milter.TEMPFAIL @@ -93,8 +93,8 @@ 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.log_info("{0} - milter_action={1} message={2}".format( + self.mconn_id, kwargs['action'], message )) self.setreply(smtp_code, smtp_ecode, message) return smfir @@ -119,7 +119,7 @@ class LdapAclMilter(Milter.Base): def envfrom(self, mailfrom, *str): self.reset() self.proto_stage = 'FROM' - if g_config.milter_expect_auth: + if g_config_backend.milter_expect_auth: try: # this may fail, if no x509 client certificate was used. # postfix only passes this macro to milters if the TLS connection @@ -181,24 +181,28 @@ class LdapAclMilter(Milter.Base): to = to.replace(">","") to = to.lower() self.log_debug("5321.rcpt={}".format(to)) - if to in g_config.milter_whitelisted_rcpts: + if to in g_config_backend.milter_whitelisted_rcpts: return self.milter_action(action = 'continue') - if g_config.milter_dkim_enabled: + if g_config_backend.milter_dkim_enabled: # Collect all envelope-recipients for later # investigation (EOM). Do not perform any # policy action at this protocol phase. self.env_rcpts.append(to) else: + # DKIM disabled. Policy enforcement takes place here. try: - ret = g_policy_backend.check_policy( - from_addr=self.env_from, rcpt_addr=to, from_source='5321.from', lam_session=self + g_policy_backend.check_policy( + from_addr = self.env_from, + rcpt_addr = to, + from_source = 'envelope', + lam_session = self ) - self.log_info(ret) + self.env_rcpts.append(to) except LamSoftException as e: - if g_config.milter_mode == 'reject': + if g_config_backend.milter_mode == 'reject': return self.milter_action(action = 'tmpfail') except LamHardException as e: - if g_config.milter_mode == 'reject': + if g_config_backend.milter_mode == 'reject': return self.milter_action( action = 'reject', reason = e.message @@ -210,7 +214,7 @@ class LdapAclMilter(Milter.Base): def header(self, hname, hval): self.proto_stage = 'HDR' self.queue_id = self.getsymval('i') - if g_config.milter_dkim_enabled == True: + if g_config_backend.milter_dkim_enabled == True: # Parse RFC-5322-From header if(hname.lower() == "From".lower()): hdr_5322_from = email.utils.parseaddr(hval) @@ -232,7 +236,7 @@ class LdapAclMilter(Milter.Base): ar = authres.AuthenticationResultsHeader.parse( "{0}: {1}".format(hname, hval) ) - if ar.authserv_id.lower() == g_config.milter_trusted_authservid.lower(): + if ar.authserv_id.lower() == g_config_backend.milter_trusted_authservid.lower(): for ar_result in ar.results: if ar_result.method.lower() == 'dkim': if ar_result.result.lower() == 'pass': @@ -249,13 +253,13 @@ class LdapAclMilter(Milter.Base): def eom(self): self.proto_stage = 'EOM' - if g_config.milter_max_rcpt_enabled: - if len(self.env_rcpts) > int(g_config.milter_max_rcpt): - if g_config.milter_mode == 'reject': + if g_config_backend.milter_max_rcpt_enabled: + if len(self.env_rcpts) > int(g_config_backend.milter_max_rcpt): + if g_config_backend.milter_mode == 'reject': return self.milter_action(action='reject', reason='Too many recipients!') else: self.do_log("TEST-Mode: Too many recipients!") - if g_config.milter_dkim_enabled: + if g_config_backend.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 )) @@ -272,42 +276,55 @@ class LdapAclMilter(Milter.Base): for rcpt in self.env_rcpts: try: # Check 5321.from against policy - ret = g_policy_backend.check_policy( - from_addr=self.env_from, rcpt_addr=rcpt, from_source='5321.from', lam_session=self + g_policy_backend.check_policy( + from_addr=self.env_from, + rcpt_addr=rcpt, + from_source='envelope', + lam_session=self + ) + self.log_info( + "action=pass 5321.from={0} 5321.rcpt={1}".format(self.env_from, rcpt) ) - self.log_info(ret) except LamSoftException as e: - self.log_info(str(e)) - if g_config.milter_mode == 'reject': + self.log_info(e.message) + if g_config_backend.milter_mode == 'reject': return self.milter_action(action = 'tmpfail') else: self.log_info("TEST-Mode - tmpfail") except LamHardException as e: + self.log_info(e.message) if self.dkim_aligned: try: # Check 5322.from against policy - ret = g_policy_backend.check_policy( - from_addr=self.hdr_from, rcpt_addr=rcpt, from_source='5322.from', lam_session=self + g_policy_backend.check_policy( + from_addr=self.hdr_from, + rcpt_addr=rcpt, + from_source='from-header', + lam_session=self + ) + self.log_info( + "action=pass 5322.from={0} 5321.rcpt={1}".format(self.hdr_from, rcpt) ) - self.log_info(ret) - 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_config_backend.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!" + ) + else: + # * DKIM check disabled + # Iterate through all accepted envelope recipients and log + for rcpt in self.env_rcpts: + self.log_info("action=pass 5321.from={0} 5321.rcpt={1}".format(self.env_from, rcpt)) - if reject_message: - if g_config.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 = 'continue') def abort(self): diff --git a/app/lam_backends.py b/app/lam_backends.py new file mode 100644 index 0000000..26384ca --- /dev/null +++ b/app/lam_backends.py @@ -0,0 +1,30 @@ +import traceback +from lam_exceptions import ( + LamInitException, LamPolicyBackendException, LamConfigBackendException +) +from lam_logger import init_logger +from lam_config_backend import LamConfigBackend +from lam_policy_backend import LamPolicyBackend + +init_logger() + +g_config_backend = None +try: + if g_config_backend is None: + g_config_backend = LamConfigBackend() +except LamConfigBackendException as e: + raise LamInitException(e.message) from e +except Exception as e: + raise LamInitException(traceback.format_exc()) from e + +# Instantiate the LDAP policy backend and +# establish a permanent connection with the LDAP server +# which will be reused on any milter connection +g_policy_backend = None +try: + if g_policy_backend is None: + g_policy_backend = LamPolicyBackend(g_config_backend) +except LamPolicyBackendException as e: + raise LamInitException(e.message) from e +except Exception as e: + raise LamInitException(traceback.format_exc()) from e \ No newline at end of file diff --git a/app/lam_config.py b/app/lam_config_backend.py similarity index 55% rename from app/lam_config.py rename to app/lam_config_backend.py index 7e57f28..31a700a 100644 --- a/app/lam_config.py +++ b/app/lam_config_backend.py @@ -1,23 +1,25 @@ import re import os from lam_logger import log_info -from lam_exceptions import LamConfigException +from lam_exceptions import LamConfigBackendException from lam_rex import g_rex_email -class LamConfig(): +class LamConfigBackend(): def __init__(self): - self.milter_mode = 'test' self.milter_name = 'ldap-acl-milter' + self.milter_mode = 'test' self.milter_socket = '/socket/{}'.format(self.milter_name) + self.milter_timeout = 60 self.milter_reject_message = 'Security policy violation!' self.milter_tmpfail_message = 'Service temporarily not available! Please try again later.' self.ldap_server = 'ldap://127.0.0.1:389' + self.ldap_server_connect_timeout = 3 self.ldap_binddn = 'cn=ldap-reader,ou=binds,dc=example,dc=org' self.ldap_bindpw = 'TopSecret;-)' self.ldap_base = 'ou=lam,ou=services,dc=example,dc=org' self.ldap_query = '(&(mail=%rcpt%)(allowedEnvelopeSender=%from%))' self.milter_schema = False - self.milter_schema_wildcard_domain = False # works only if milter_schema == True + self.milter_schema_wildcard_domain = False self.milter_expect_auth = False self.milter_whitelisted_rcpts = {} self.milter_dkim_enabled = False @@ -25,75 +27,127 @@ class LamConfig(): self.milter_max_rcpt_enabled = False self.milter_max_rcpt = 1 + if 'MILTER_NAME' in os.environ: + self.milter_name = os.environ['MILTER_NAME'] + if 'MILTER_MODE' in os.environ: if re.match(r'^test|reject$',os.environ['MILTER_MODE'], re.IGNORECASE): self.milter_mode = os.environ['MILTER_MODE'].lower() - if 'MILTER_NAME' in os.environ: - self.milter_name = os.environ['MILTER_NAME'] + log_info("ENV[MILTER_MODE]: {}". format(self.milter_mode)) + + if 'MILTER_SOCKET' in os.environ: + self.milter_socket = os.environ['MILTER_SOCKET'] + log_info("ENV[MILTER_SOCKET]: {}".format(self.milter_socket)) + + if 'MILTER_TIMEOUT' in os.environ: + if os.environ['MILTER_TIMEOUT'].isnumeric(): + self.milter_timeout = int(os.environ['MILTER_TIMEOUT']) + else: + raise LamConfigBackendException("ENV[MILTER_TIMEOUT] must be numeric!") + log_info("ENV[MILTER_TIMEOUT]: {}".format(self.milter_timeout)) + if 'MILTER_SCHEMA' in os.environ: if re.match(r'^true$', os.environ['MILTER_SCHEMA'], re.IGNORECASE): self.milter_schema = True if 'MILTER_SCHEMA_WILDCARD_DOMAIN' in os.environ: if re.match(r'^true$', os.environ['MILTER_SCHEMA_WILDCARD_DOMAIN'], re.IGNORECASE): self.milter_schema_wildcard_domain = True + log_info("ENV[MILTER_SCHEMA]: {}".format(self.milter_schema)) + log_info( + "ENV[MILTER_SCHEMA_WILDCARD_DOMAIN]: {}".format( + self.milter_schema_wildcard_domain + ) + ) + if 'LDAP_SERVER' not in os.environ: - raise LamConfigException("Missing ENV[LDAP_SERVER], e.g. {}".format(self.ldap_server)) + raise LamConfigBackendException( + "Missing ENV[LDAP_SERVER], e.g. {}".format(self.ldap_server) + ) self.ldap_server = os.environ['LDAP_SERVER'] + log_info("ENV[LDAP_SERVER]: {}".format(self.ldap_server)) + + if 'LDAP_SERVER_CONNECT_TIMEOUT' in os.environ: + if not os.environ['LDAP_SERVER_CONNECT_TIMEOUT'].isnumeric(): + raise LamConfigBackendException( + "ENV[LDAP_SERVER_CONNECT_TIMEOUT] must be numeric!" + ) + self.ldap_server_connect_timeout = int(os.environ['LDAP_SERVER_CONNECT_TIMEOUT']) + log_info("ENV[LDAP_SERVER_CONNECT_TIMEOUT]: {}".format( + self.ldap_server_connect_timeout + )) + if 'LDAP_BINDDN' in os.environ: self.ldap_binddn = os.environ['LDAP_BINDDN'] if 'LDAP_BINDPW' in os.environ: self.ldap_bindpw = os.environ['LDAP_BINDPW'] + if 'LDAP_BASE' not in os.environ: - raise LamConfigException( + raise LamConfigBackendException( "Missing ENV[LDAP_BASE], e.g. {}".format(self.ldap_base) ) self.ldap_base = os.environ['LDAP_BASE'] + log_info("ENV[LDAP_BASE]: {}".format(self.ldap_base)) + if 'LDAP_QUERY' not in os.environ: if self.milter_schema == False: - raise LamConfigException( + raise LamConfigBackendException( "ENV[MILTER_SCHEMA] is disabled and ENV[LDAP_QUERY] is not set instead!" ) - if 'LDAP_QUERY' in os.environ: + else: self.ldap_query = os.environ['LDAP_QUERY'] - if 'MILTER_SOCKET' in os.environ: - self.milter_socket = os.environ['MILTER_SOCKET'] + log_info("ENV[LDAP_QUERY]: {}".format(self.ldap_query)) + if 'MILTER_REJECT_MESSAGE' in os.environ: self.milter_reject_message = os.environ['MILTER_REJECT_MESSAGE'] + log_info("ENV[MILTER_REJECT_MESSAGE]: {}".format(self.milter_reject_message)) + if 'MILTER_TMPFAIL_MESSAGE' in os.environ: self.milter_tmpfail_message = os.environ['MILTER_TMPFAIL_MESSAGE'] + log_info("ENV[MILTER_TMPFAIL_MESSAGE]: {}".format(self.milter_tmpfail_message)) + if 'MILTER_EXPECT_AUTH' in os.environ: if re.match(r'^true$', os.environ['MILTER_EXPECT_AUTH'], re.IGNORECASE): self.milter_expect_auth = True + log_info("ENV[MILTER_EXPECT_AUTH]: {}".format(self.milter_expect_auth)) + if 'MILTER_WHITELISTED_RCPTS' in os.environ: # A blank separated list is expected whitelisted_rcpts_str = os.environ['MILTER_WHITELISTED_RCPTS'] for whitelisted_rcpt in re.split(',|\s', whitelisted_rcpts_str): if g_rex_email.match(whitelisted_rcpt) == None: - raise LamConfigException( + raise LamConfigBackendException( "ENV[MILTER_WHITELISTED_RCPTS]: invalid email address: {}" .format(whitelisted_rcpt) ) else: - log_info("ENV[MILTER_WHITELISTED_RCPTS]: {}".format( - whitelisted_rcpt - )) self.milter_whitelisted_rcpts[whitelisted_rcpt] = {} + log_info( + "ENV[MILTER_WHITELISTED_RCPTS]: {}".format( + self.milter_whitelisted_rcpts + ) + ) + if 'MILTER_DKIM_ENABLED' in os.environ: self.milter_dkim_enabled = True if 'MILTER_TRUSTED_AUTHSERVID' in os.environ: self.milter_trusted_authservid = os.environ['MILTER_TRUSTED_AUTHSERVID'].lower() - log_info("ENV[MILTER_TRUSTED_AUTHSERVID]: {0}".format( - self.milter_trusted_authservid - )) + log_info( + "ENV[MILTER_TRUSTED_AUTHSERVID]: {}".format( + self.milter_trusted_authservid + ) + ) else: - raise LamConfigException("ENV[MILTER_TRUSTED_AUTHSERVID] is mandatory!") - log_info("ENV[MILTER_DKIM_ENABLED]: {0}".format(self.milter_dkim_enabled)) + raise LamConfigBackendException("ENV[MILTER_TRUSTED_AUTHSERVID] is mandatory!") + log_info("ENV[MILTER_DKIM_ENABLED]: {}".format(self.milter_dkim_enabled)) + if 'MILTER_MAX_RCPT_ENABLED' in os.environ: self.milter_max_rcpt_enabled = True if 'MILTER_MAX_RCPT' in os.environ: if os.environ['MILTER_MAX_RCPT'].isnumeric(): - self.milter_max_rcpt = os.environ['MILTER_MAX_RCPT'] + self.milter_max_rcpt = int(os.environ['MILTER_MAX_RCPT']) + log_info("ENV[MILTER_MAX_RCPT]: {}".format(self.milter_max_rcpt)) else: - raise LamConfigException("ENV[MILTER_MAX_RCPT] must be numeric!") + raise LamConfigBackendException("ENV[MILTER_MAX_RCPT] must be numeric!") + log_info("ENV[MILTER_MAX_RCPT_ENABLED]: {}".format(self.milter_max_rcpt_enabled)) diff --git a/app/lam_exceptions.py b/app/lam_exceptions.py index 3d44cf2..0f234e9 100644 --- a/app/lam_exceptions.py +++ b/app/lam_exceptions.py @@ -16,5 +16,5 @@ class LamHardException(LamException): class LamPolicyBackendException(LamException): pass -class LamConfigException(LamException): +class LamConfigBackendException(LamException): pass \ No newline at end of file diff --git a/app/lam_globals.py b/app/lam_globals.py deleted file mode 100644 index b7b7595..0000000 --- a/app/lam_globals.py +++ /dev/null @@ -1,26 +0,0 @@ -import sys -from lam_exceptions import ( - LamInitException, LamPolicyBackendException, LamConfigException -) -from lam_config import LamConfig -from lam_logger import init_logger -from lam_policy_backend import LamPolicyBackend - -init_logger() - -g_config = None -try: - if g_config is None: - g_config = LamConfig() -except LamConfigException as e: - raise LamInitException(e.message) from e - -# Instantiate the LDAP policy backend and -# establish a permanent connection with the LDAP server -# which will be reused on any milter connection -g_policy_backend = None -try: - if g_policy_backend is None: - g_policy_backend = LamPolicyBackend(g_config) -except LamPolicyBackendException as e: - raise LamInitException(e.message) from e \ No newline at end of file diff --git a/app/lam_policy_backend.py b/app/lam_policy_backend.py index 0e2b722..b92c5e6 100644 --- a/app/lam_policy_backend.py +++ b/app/lam_policy_backend.py @@ -17,8 +17,8 @@ class LamPolicyBackend(): set_config_parameter("RESTARTABLE_SLEEPTIME", 2) set_config_parameter("RESTARTABLE_TRIES", 2) server = Server( - self.config.ldap_server, - connect_timeout = 3, + host = self.config.ldap_server, + connect_timeout = self.config.ldap_server_connect_timeout, get_info = NONE ) self.ldap_conn = Connection(server, @@ -34,19 +34,19 @@ class LamPolicyBackend(): "Connection to LDAP-server failed: {}".format(str(e)) ) from e - def check_policy(self, **kwargs) -> str: + def check_policy(self, **kwargs): from_addr = kwargs['from_addr'] rcpt_addr = kwargs['rcpt_addr'] from_source = kwargs['from_source'] lam_session = kwargs['lam_session'] + mcid = "{}/Policy".format(lam_session.mconn_id) m = g_rex_domain.match(from_addr) if m == None: - log_info("Could not determine domain of from={0}".format( - from_addr - )) - raise LamSoftException() + raise LamHardException( + "Could not determine domain of from={0}".format(from_addr) + ) from_domain = m.group(1) - log_debug("from_domain={}".format(from_domain)) + log_debug("{0} from_domain={1}".format(mcid, from_domain)) m = g_rex_domain.match(rcpt_addr) if m == None: raise LamHardException( @@ -55,7 +55,7 @@ class LamPolicyBackend(): ) ) rcpt_domain = m.group(1) - log_debug("rcpt_domain={}".format(rcpt_domain)) + log_debug("{0} rcpt_domain={1}".format(mcid, rcpt_domain)) try: if self.config.milter_schema == True: # LDAP-ACL-Milter schema @@ -77,7 +77,7 @@ class LamPolicyBackend(): ) else: auth_method = auth_method.replace('%X509_AUTH%','') - log_debug("auth_method: {}".format(auth_method)) + log_debug("{0} auth_method: {1}".format(mcid, auth_method)) if self.config.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. @@ -126,26 +126,28 @@ class LamPolicyBackend(): ) if len(self.ldap_conn.entries) == 0: # Policy not found in LDAP - ex = "policy mismatch: from={0} from_src={1} rcpt={2}".format( - from_addr, from_source, rcpt_addr - ) - if self.config.milter_expect_auth == True: - ex = "policy mismatch: from={0} from_src={1} rcpt={2} auth_method={3}".format( - from_addr, from_source, rcpt_addr, auth_method + raise LamHardException( + "mismatch: from_src={0} from={1} rcpt={2}".format( + from_source, from_addr, rcpt_addr ) - raise LamHardException(ex) + ) elif len(self.ldap_conn.entries) == 1: + if from_source == 'from-header': + log_info("{0} 5322.from={1} authorized by DKIM signature".format( + mcid, from_addr + )) # Policy found in LDAP, but which one? entry = self.ldap_conn.entries[0] - return "policy match: '{0}' from_src={1}".format( - entry.policyID.value, from_source - ) + log_info("{0} match: '{1}' from_src={2}".format( + mcid, entry.policyID.value, from_source + )) elif len(self.ldap_conn.entries) > 1: # Something went wrong!? There shouldn´t be more than one entries! - log_error("More than one policies found! from={0} rcpt={1} auth_method={2}".format( - from_addr, rcpt_addr, auth_method - )) - raise LamHardException("More than one policies found!") + raise LamHardException( + "More than one policies found! from={0} rcpt={1} auth_method={2}".format( + from_addr, rcpt_addr, auth_method + ) + ) else: # Custom LDAP schema # 'build' a LDAP query per recipient @@ -156,18 +158,14 @@ class LamPolicyBackend(): query = query.replace("%sasl_user%", lam_session.sasl_user) query = query.replace("%from_domain%", from_domain) query = query.replace("%rcpt_domain%", rcpt_domain) - log_debug("LDAP query: {}".format(query)) + log_debug("{0} LDAP query: {1}".format(mcid, query)) self.ldap_conn.search(self.config.ldap_base, query) if len(self.ldap_conn.entries) == 0: - log_info( - "policy mismatch from={0} from_src={1} rcpt={2}".format( - from_addr, from_source, rcpt_addr + raise LamHardException( + "mismatch from_src={0} from={1} rcpt={2}".format( + from_source, from_addr, rcpt_addr ) ) - raise LamHardException("policy mismatch") - return "policy match: '{0}' from_src={1}".format( - entry.policyID.value, from_source - ) + log_info("{0} match from_src={1}".format(mcid, from_source)) except LDAPException as e: - log_error("LDAP exception: {}".format(str(e))) raise LamSoftException("LDAP exception: " + str(e)) from e diff --git a/app/run_milter.py b/app/run_milter.py index d54d77e..020296f 100644 --- a/app/run_milter.py +++ b/app/run_milter.py @@ -4,7 +4,7 @@ import traceback from lam_exceptions import LamInitException from lam_logger import log_info, log_error try: - import lam_globals + import lam_backends except LamInitException as e: log_error("Init exception: {}".format(e.message)) sys.exit(1) @@ -12,24 +12,22 @@ from lam import LdapAclMilter if __name__ == "__main__": try: - timeout = 600 + timeout = lam_backends.g_config_backend.milter_timeout # 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) - log_info("Starting {0}@socket: {1} in mode {2}".format( - lam_globals.g_config.milter_name, - lam_globals.g_config.milter_socket, - lam_globals.g_config.milter_mode + log_info("Starting {}".format( + lam_backends.g_config_backend.milter_name )) Milter.runmilter( - lam_globals.g_config.milter_name, - lam_globals.g_config.milter_socket, + lam_backends.g_config_backend.milter_name, + lam_backends.g_config_backend.milter_socket, timeout, True ) - log_info("Shutdown {}".format(lam_globals.g_config.milter_name)) + log_info("Shutdown {}".format(lam_backends.g_config_backend.milter_name)) except: log_error("MAIN-EXCEPTION: {}".format(traceback.format_exc())) sys.exit(1) diff --git a/tests/miltertest-ip-conn_reuse.lua b/tests/miltertest-ip-conn_reuse.lua index 805df31..310cf6a 100644 --- a/tests/miltertest-ip-conn_reuse.lua +++ b/tests/miltertest-ip-conn_reuse.lua @@ -16,18 +16,12 @@ mt.set_timeout(60) if mt.mailfrom(conn, "tester-ip@test.blah") ~= nil then error "mt.mailfrom() failed" end -if mt.getreply(conn) ~= SMFIR_CONTINUE then - error "mt.mailfrom() unexpected reply" -end -- 5321.RCPT+MACROS mt.macro(conn, SMFIC_RCPT, "i", "4CgSNs5Q9sz7SllQ") if mt.rcptto(conn, "") ~= nil then error "mt.rcptto() failed" end -if mt.getreply(conn) ~= SMFIR_CONTINUE then - error "mt.rcptto() unexpected reply" -end -- 5322.HEADERS if mt.header(conn, "fRoM", '"Blah Blubb" ') ~= nil then @@ -54,18 +48,12 @@ end if mt.mailfrom(conn, "tester-ip2@test.blah") ~= nil then error "mt.mailfrom() failed" end -if mt.getreply(conn) ~= SMFIR_CONTINUE then - error "mt.mailfrom() unexpected reply" -end -- 5321.RCPT+MACROS mt.macro(conn, SMFIC_RCPT, "i", "conn-reused-QID") if mt.rcptto(conn, "") ~= nil then error "mt.rcptto() failed" end -if mt.getreply(conn) ~= SMFIR_CONTINUE then - error "mt.rcptto() unexpected reply" -end -- 5322.HEADERS if mt.header(conn, "fRoM", '"Blah Blubb" ') ~= nil then diff --git a/tests/miltertest-ip-multiple_rcpts.lua b/tests/miltertest-ip-multiple_rcpts.lua index 0432ae4..bc012dc 100644 --- a/tests/miltertest-ip-multiple_rcpts.lua +++ b/tests/miltertest-ip-multiple_rcpts.lua @@ -16,9 +16,6 @@ mt.set_timeout(60) if mt.mailfrom(conn, "tester-ipx@test.blah") ~= nil then error "mt.mailfrom() failed" end -if mt.getreply(conn) ~= SMFIR_CONTINUE then - error "mt.mailfrom() unexpected reply" -end -- FIRST 5321.RCPT if mt.rcptto(conn, "") ~= nil then diff --git a/tests/miltertest-noauth.lua b/tests/miltertest-noauth.lua index 77651ec..16b1d7b 100644 --- a/tests/miltertest-noauth.lua +++ b/tests/miltertest-noauth.lua @@ -16,19 +16,12 @@ mt.set_timeout(60) if mt.mailfrom(conn, "tester-noauth@test.blah") ~= nil then error "mt.mailfrom() failed" end -if mt.getreply(conn) ~= SMFIR_CONTINUE then - error "mt.mailfrom() unexpected reply" -end -- 5321.RCPT+MACROS mt.macro(conn, SMFIC_RCPT, "i", "4CgSNs5Q9sz7SllQ") if mt.rcptto(conn, "") ~= nil then --- if mt.rcptto(conn, "") ~= nil then error "mt.rcptto() failed" end -if mt.getreply(conn) ~= SMFIR_CONTINUE then - error "mt.rcptto() unexpected reply" -end -- 5322.HEADERS if mt.header(conn, "fRoM", '"Blah Blubb" ') ~= nil then diff --git a/tests/miltertest-null_sender.lua b/tests/miltertest-null_sender.lua new file mode 100644 index 0000000..e69de29 diff --git a/tests/miltertest-sasl-wildcard.lua b/tests/miltertest-sasl-wildcard.lua index 3e27396..6d23d79 100644 --- a/tests/miltertest-sasl-wildcard.lua +++ b/tests/miltertest-sasl-wildcard.lua @@ -17,18 +17,12 @@ mt.macro(conn, SMFIC_MAIL, "{auth_authen}", "blubb-user-wild") if mt.mailfrom(conn, "tester-invalid@test.blah") ~= nil then error "mt.mailfrom() failed" end -if mt.getreply(conn) ~= SMFIR_CONTINUE then - error "mt.mailfrom() unexpected reply" -end -- 5321.RCPT+MACROS mt.macro(conn, SMFIC_RCPT, "i", "test-wildcard-qid") if mt.rcptto(conn, "") ~= nil then error "mt.rcptto() failed" end -if mt.getreply(conn) ~= SMFIR_CONTINUE then - error "mt.rcptto() unexpected reply" -end -- 5322.HEADERS if mt.header(conn, "fRoM", '"Blah Blubb" ') ~= nil then diff --git a/tests/miltertest-sasl.lua b/tests/miltertest-sasl.lua index dfccb7c..d8966fe 100644 --- a/tests/miltertest-sasl.lua +++ b/tests/miltertest-sasl.lua @@ -17,18 +17,12 @@ mt.macro(conn, SMFIC_MAIL, "{auth_authen}", "blubb-user1") if mt.mailfrom(conn, "tester@test.blah") ~= nil then error "mt.mailfrom() failed" end -if mt.getreply(conn) ~= SMFIR_CONTINUE then - error "mt.mailfrom() unexpected reply" -end -- 5321.RCPT+MACROS mt.macro(conn, SMFIC_RCPT, "i", "4CgSNs5Q9sz7SllQ") if mt.rcptto(conn, "") ~= nil then error "mt.rcptto() failed" end -if mt.getreply(conn) ~= SMFIR_CONTINUE then - error "mt.rcptto() unexpected reply" -end -- 5322.HEADERS if mt.header(conn, "fRoM", '"Blah Blubb" ') ~= nil then diff --git a/tests/miltertest-whitelisted_sender.lua b/tests/miltertest-whitelisted_sender.lua new file mode 100644 index 0000000..e69de29 diff --git a/tests/miltertest-x509.lua b/tests/miltertest-x509.lua index 6a8920a..f9838cf 100644 --- a/tests/miltertest-x509.lua +++ b/tests/miltertest-x509.lua @@ -18,18 +18,12 @@ mt.macro(conn, SMFIC_MAIL, "{cert_issuer}", "x509-issuer", "{cert_subject}", "x5 if mt.mailfrom(conn, "tester-x509@test.blah") ~= nil then error "mt.mailfrom() failed" end -if mt.getreply(conn) ~= SMFIR_CONTINUE then - error "mt.mailfrom() unexpected reply" -end -- 5321.RCPT+MACROS mt.macro(conn, SMFIC_RCPT, "i", "4CgSNs5Q9sz7SllQ") if mt.rcptto(conn, "") ~= nil then error "mt.rcptto() failed" end -if mt.getreply(conn) ~= SMFIR_CONTINUE then - error "mt.rcptto() unexpected reply" -end -- 5322.HEADERS if mt.header(conn, "fRoM", '"Blah Blubb" ') ~= nil then diff --git a/tests/miltertest-x509_5321-fail_dkim-pass.lua b/tests/miltertest-x509_5321-fail_dkim-pass.lua index 3cf833f..e8fbed1 100644 --- a/tests/miltertest-x509_5321-fail_dkim-pass.lua +++ b/tests/miltertest-x509_5321-fail_dkim-pass.lua @@ -17,18 +17,12 @@ mt.macro(conn, SMFIC_MAIL, "{cert_issuer}", "x509-issuer", "{cert_subject}", "x5 if mt.mailfrom(conn, "tester-x509-invalid@test.blah") ~= nil then error "mt.mailfrom() failed" end -if mt.getreply(conn) ~= SMFIR_CONTINUE then - error "mt.mailfrom() unexpected reply" -end -- 5321.RCPT+MACROS mt.macro(conn, SMFIC_RCPT, "i", "4CgSNs5Q9sz7SllQ") if mt.rcptto(conn, "") ~= nil then error "mt.rcptto() failed" end -if mt.getreply(conn) ~= SMFIR_CONTINUE then - error "mt.rcptto() unexpected reply" -end -- 5322.HEADERS if mt.header(conn, "fRoM", '"Blah Blubb" ') ~= nil then