From 37daa5b0e6d3e60474aeaec04646cfce36e67ee6 Mon Sep 17 00:00:00 2001 From: Dominik Chilla Date: Wed, 7 Jul 2021 01:41:17 +0200 Subject: [PATCH] Do more cleanup due to connection reusing --- app/exota-milter.py | 25 ++-- tests/miltertest_conn_reuse.lua | 138 +++++++++++++++++++ tests/miltertest_conn_reuse_fail_pass.lua | 161 ++++++++++++++++++++++ 3 files changed, 312 insertions(+), 12 deletions(-) create mode 100644 tests/miltertest_conn_reuse.lua create mode 100644 tests/miltertest_conn_reuse_fail_pass.lua diff --git a/app/exota-milter.py b/app/exota-milter.py index 8a49c43..ddeb81d 100644 --- a/app/exota-milter.py +++ b/app/exota-milter.py @@ -64,7 +64,6 @@ g_milter_ldap_dkim_enabled_attr = 'exotaMilterDkimEnabled' # ENV[MILTER_LDAP_DKIM_ALIGNMENT_REQIRED_ATTR] g_milter_ldap_dkim_alignment_required_attr = 'exotaMilterDkimAlignmentRequired' - # Another globals g_policy_backend = None g_re_domain = re.compile(r'^.*@(\S+)$', re.IGNORECASE) @@ -75,10 +74,11 @@ class ExOTAMilter(Milter.Base): def __init__(self): self.x509_client_valid = False self.client_ip = None + self.client_port = None self.reset() + log_debug(self.mconn_id + " INIT: {0}".format(self.__dict__)) def reset(self): - self.conn_reused = False self.hdr_from = None self.hdr_from_domain = None self.hdr_resent_from = None @@ -95,7 +95,7 @@ class ExOTAMilter(Milter.Base): self.mconn_id = g_milter_name + ': ' + ''.join( random.choice(string.ascii_lowercase + string.digits) for _ in range(8) ) - log_debug(self.mconn_id + " reset()") + log_debug(self.mconn_id + " reset(): {0}".format(self.__dict__)) def smfir_reject(self, **kwargs): message = g_milter_reject_message @@ -108,6 +108,7 @@ class ExOTAMilter(Milter.Base): log_info(self.mconn_id + "/" + str(self.getsymval('i')) + ": milter_action=reject message={0}".format(message) ) + self.reset() self.setreply('550','5.7.1', message) return Milter.REJECT @@ -122,6 +123,7 @@ class ExOTAMilter(Milter.Base): log_info(self.mconn_id + "/" + str(self.getsymval('i')) + ": milter_action=tempfail message={0}".format(message) ) + self.reset() self.setreply('450','4.7.1', message) return Milter.TEMPFAIL @@ -140,20 +142,18 @@ class ExOTAMilter(Milter.Base): return self.smfir_continue() def connect(self, IPname, family, hostaddr): + self.reset() self.client_ip = hostaddr[0] + self.client_port = hostaddr[1] + log_debug(self.mconn_id + "/CONNECT client_ip={0} client_port={1}".format( + self.client_ip, self.client_port + )) return self.smfir_continue() # Mandatory callback def envfrom(self, mailfrom, *str): log_debug(self.mconn_id + "/FROM 5321.from={0}".format(mailfrom)) - # Instance member values remain within reused SMTP-connections! - if self.conn_reused: - # Milter connection reused! - log_debug(self.mconn_id + "/FROM connection reused!") - self.reset() - else: - self.conn_reused = True - log_debug(self.mconn_id + "/FROM client_ip={0}".format(self.client_ip)) + log_debug(self.mconn_id + "/FROM {0}".format(self.__dict__)) return self.smfir_continue() # Mandatory callback @@ -489,6 +489,7 @@ class ExOTAMilter(Milter.Base): log_info(self.mconn_id + "/" + str(self.getsymval('i')) + "/EOM: Tenant successfully authorized" ) + self.reset() return self.smfir_continue() def abort(self): @@ -499,7 +500,7 @@ class ExOTAMilter(Milter.Base): def close(self): # Always called, even when abort is called. # Clean up any external resources here. - log_debug(self.mconn_id + "/CLOSE") + log_debug(self.mconn_id + "/CLOSE {0}".format(self.__dict__)) return self.smfir_continue() if __name__ == "__main__": diff --git a/tests/miltertest_conn_reuse.lua b/tests/miltertest_conn_reuse.lua new file mode 100644 index 0000000..8ff6077 --- /dev/null +++ b/tests/miltertest_conn_reuse.lua @@ -0,0 +1,138 @@ +-- https://mopano.github.io/sendmail-filter-api/constant-values.html#com.sendmail.milter.MilterConstants +-- http://www.opendkim.org/miltertest.8.html + +-- socket must be defined as miltertest global variable (-D) +conn = mt.connect(socket) +if conn == nil then + error "mt.connect() failed" +end +if mt.conninfo(conn, "localhost", "::1") ~= nil then + error "mt.conninfo() failed" +end + +mt.set_timeout(60) + +-- 5321.FROM +if mt.mailfrom(conn, "envelope.sender@example.org") ~= 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", '{cert_subject}', "mail.protection.outlook.comx") +if mt.rcptto(conn, "") ~= nil then + error "mt.rcptto() failed" +end +if mt.getreply(conn) ~= SMFIR_CONTINUE then + error "mt.rcptto() unexpected reply" +end + +-- HEADER +--if mt.header(conn, "fRoM", '"Blah Blubb" ') ~= nil then +-- error "mt.header(From) failed" +--end +if mt.header(conn, "fRoM", '"Blah Blubb" ') ~= nil then + error "mt.header(From) failed" +end +if mt.header(conn, "resent-fRoM", '"Blah Blubb" ') ~= nil then + error "mt.header(From) failed" +end +if mt.header(conn, "x-mS-EXCHANGE-crosstenant-id", "1234abcd-18c5-45e8-88de-123456789abc") ~= nil then + error "mt.header(Subject) failed" +end +--if mt.header(conn, "X-MS-Exchange-CrossTenant-Id", "4321abcd-18c5-45e8-88de-blahblubb") ~= nil then +-- error "mt.header(Subject) failed" +--end +if mt.header(conn, "Authentication-Results", "another-wrong-auth-serv-id;\n dkim=fail header.d=yad.onmicrosoft.com header.s=selector1-yad-onmicrosoft-com header.b=mmmjFpv8") ~= nil then + error "mt.header(Subject) failed" +end +if mt.header(conn, "Authentication-Results", "wrong-auth-serv-id;\n dkim=pass header.d=yad.onmicrosoft.com header.s=selector1-yad-onmicrosoft-com header.b=mmmjFpv8") ~= nil then + error "mt.header(Subject) failed" +end +if mt.header(conn, "Authentication-Results", "my-auth-serv-id;\n exota=pass") ~= nil then + error "mt.header(Subject) failed" +end +if mt.header(conn, "Authentication-RESULTS", "my-auth-serv-id;\n dkim=pass header.d=yad.onmicrosoft.comx header.s=selector1-yad-onmicrosoft-com header.b=mmmjFpv8") ~= nil then + error "mt.header(Subject) failed" +end +if mt.header(conn, "Authentication-RESULTS", "my-auth-serv-id;\n dkim=pass header.d=chillout2k.de header.s=selector1-yad-onmicrosoft-com header.b=mmmjFpv8") ~= nil then + error "mt.header(Subject) failed" +end +if mt.header(conn, "Authentication-Results", "my-auth-serv-id;\n dkim=fail header.d=yad.onmicrosoft.com header.s=selector2-asdf header.b=mmmjFpv8") ~= nil then + error "mt.header(Subject) failed" +end +if mt.header(conn, "Authentication-Results", "some-validating-host;\n dkim=pass header.d=paypal.de header.s=pp-dkim1 header.b=PmTtUzer;\n dmarc=pass (policy=reject) header.from=paypal.de;\n spf=pass (some-validating-host: domain of service@paypal.de designates 173.0.84.226 as permitted sender) smtp.mailfrom=service@paypal.de") ~= nil then + error "mt.header(Subject) failed" +end +if mt.header(conn, "X-ExOTA-Authentication-Results", "my-auth-serv-id;\n exota=pass") ~= nil then + error "mt.header(Subject) failed" +end + +-- EOM +if mt.eom(conn) ~= nil then + error "mt.eom() failed" +end +mt.echo("EOM: " .. mt.getreply(conn)) +if mt.getreply(conn) == SMFIR_CONTINUE then + mt.echo("EOM-continue") +elseif mt.getreply(conn) == SMFIR_REPLYCODE then + mt.echo("EOM-reject") +end + +if not mt.eom_check(conn, MT_HDRADD, "X-ExOTA-Authentication-Results") then + mt.echo("no header added") +else + mt.echo("X-ExOTA-Authentication-Results header added") +end + +-- next message + +-- 5321.FROM +if mt.mailfrom(conn, "envelope.sender2@example.org") ~= 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", "4CgSNs5Q9sz7Sll2", '{cert_subject}', "mail.protection.outlook.comx") +if mt.rcptto(conn, "") ~= nil then + error "mt.rcptto() failed" +end +if mt.getreply(conn) ~= SMFIR_CONTINUE then + error "mt.rcptto() unexpected reply" +end + +-- HEADER +if mt.header(conn, "fRoM", '"Blah Blubb" ') ~= nil then + error "mt.header(From) failed" +end +if mt.header(conn, "x-mS-EXCHANGE-crosstenant-id", "1234abcd-18c5-45e8-88de-123456789abc") ~= nil then + error "mt.header(Subject) failed" +end +if mt.header(conn, "Authentication-RESULTS", "my-auth-serv-id;\n dkim=pass header.d=chillout2k.de header.s=selector1-yad-onmicrosoft-com header.b=mmmjFpv8") ~= nil then + error "mt.header(Subject) failed" +end + +-- EOM +if mt.eom(conn) ~= nil then + error "mt.eom() failed" +end +mt.echo("EOM: " .. mt.getreply(conn)) +if mt.getreply(conn) == SMFIR_CONTINUE then + mt.echo("EOM-continue") +elseif mt.getreply(conn) == SMFIR_REPLYCODE then + mt.echo("EOM-reject") +end + +if not mt.eom_check(conn, MT_HDRADD, "X-ExOTA-Authentication-Results") then + mt.echo("no header added") +else + mt.echo("X-ExOTA-Authentication-Results header added") +end + +-- DISCONNECT +mt.disconnect(conn) \ No newline at end of file diff --git a/tests/miltertest_conn_reuse_fail_pass.lua b/tests/miltertest_conn_reuse_fail_pass.lua new file mode 100644 index 0000000..d734266 --- /dev/null +++ b/tests/miltertest_conn_reuse_fail_pass.lua @@ -0,0 +1,161 @@ +-- https://mopano.github.io/sendmail-filter-api/constant-values.html#com.sendmail.milter.MilterConstants +-- http://www.opendkim.org/miltertest.8.html + +-- socket must be defined as miltertest global variable (-D) +conn = mt.connect(socket) +if conn == nil then + error "mt.connect() failed" +end +if mt.conninfo(conn, "localhost", "::1") ~= nil then + error "mt.conninfo() failed" +end + +mt.set_timeout(60) + +-- FIRST MESSAGE (should fail due to dkim-fail) +-- 5321.FROM +if mt.mailfrom(conn, "envelope.sender@example.org") ~= 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", "Queue-ID-1", '{cert_subject}', "mail.protection.outlook.comx") +if mt.rcptto(conn, "") ~= nil then + error "mt.rcptto() failed" +end +if mt.getreply(conn) ~= SMFIR_CONTINUE then + error "mt.rcptto() unexpected reply" +end + +-- HEADER +if mt.header(conn, "fRoM", '"Blah Blubb" ') ~= nil then + error "mt.header(From) failed" +end +if mt.header(conn, "x-mS-EXCHANGE-crosstenant-id", "1234abcd-18c5-45e8-88de-123456789abcXXX") ~= nil then + error "mt.header(Subject) failed" +end +if mt.header(conn, "Authentication-RESULTS", "my-auth-serv-id;\n dkim=fail header.d=chillout2k.de header.s=selector1-yad-onmicrosoft-com header.b=mmmjFpv8") ~= nil then + error "mt.header(Subject) failed" +end +if mt.header(conn, "X-ExOTA-Authentication-Results", "my-auth-serv-id;\n exota=pass") ~= nil then + error "mt.header(Subject) failed" +end + +-- EOM +if mt.eom(conn) ~= nil then + error "mt.eom() failed" +end +mt.echo("EOM: " .. mt.getreply(conn)) +if mt.getreply(conn) == SMFIR_CONTINUE then + mt.echo("EOM-continue") +elseif mt.getreply(conn) == SMFIR_REPLYCODE then + mt.echo("EOM-reject") +end + +if not mt.eom_check(conn, MT_HDRADD, "X-ExOTA-Authentication-Results") then + mt.echo("no header added") +else + mt.echo("X-ExOTA-Authentication-Results header added") +end + +-- SECOND MESSAGE (should pass) + +-- 5321.FROM +if mt.mailfrom(conn, "envelope.sender2@example.org") ~= 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", "Queue-ID-2", '{cert_subject}', "mail.protection.outlook.comx") +if mt.rcptto(conn, "") ~= nil then + error "mt.rcptto() failed" +end +if mt.getreply(conn) ~= SMFIR_CONTINUE then + error "mt.rcptto() unexpected reply" +end + +-- HEADER +if mt.header(conn, "fRoM", '"Blah Blubb" ') ~= nil then + error "mt.header(From) failed" +end +if mt.header(conn, "x-mS-EXCHANGE-crosstenant-id", "1234abcd-18c5-45e8-88de-123456789abc") ~= nil then + error "mt.header(Subject) failed" +end +if mt.header(conn, "Authentication-RESULTS", "my-auth-serv-id;\n dkim=pass header.d=chillout2k.de header.s=selector1-yad-onmicrosoft-com header.b=mmmjFpv8") ~= nil then + error "mt.header(Subject) failed" +end + +-- EOM +if mt.eom(conn) ~= nil then + error "mt.eom() failed" +end +mt.echo("EOM: " .. mt.getreply(conn)) +if mt.getreply(conn) == SMFIR_CONTINUE then + mt.echo("EOM-continue") +elseif mt.getreply(conn) == SMFIR_REPLYCODE then + mt.echo("EOM-reject") +end + +if not mt.eom_check(conn, MT_HDRADD, "X-ExOTA-Authentication-Results") then + mt.echo("no header added") +else + mt.echo("X-ExOTA-Authentication-Results header added") +end + +-- THIRD MESSAGE (should fail due to dkim-fail) +-- 5321.FROM +if mt.mailfrom(conn, "envelope.sender@example.org") ~= 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", "Queue-ID-3", '{cert_subject}', "mail.protection.outlook.comx") +if mt.rcptto(conn, "") ~= nil then + error "mt.rcptto() failed" +end +if mt.getreply(conn) ~= SMFIR_CONTINUE then + error "mt.rcptto() unexpected reply" +end + +-- HEADER +if mt.header(conn, "fRoM", '"Blah Blubb" ') ~= nil then + error "mt.header(From) failed" +end +if mt.header(conn, "x-mS-EXCHANGE-crosstenant-id", "1234abcd-18c5-45e8-88de-123456789abcXXX") ~= nil then + error "mt.header(Subject) failed" +end +if mt.header(conn, "Authentication-RESULTS", "my-auth-serv-id;\n dkim=fail header.d=chillout2k.de header.s=selector1-yad-onmicrosoft-com header.b=mmmjFpv8") ~= nil then + error "mt.header(Subject) failed" +end +if mt.header(conn, "X-ExOTA-Authentication-Results", "my-auth-serv-id;\n exota=pass") ~= nil then + error "mt.header(Subject) failed" +end + +-- EOM +if mt.eom(conn) ~= nil then + error "mt.eom() failed" +end +mt.echo("EOM: " .. mt.getreply(conn)) +if mt.getreply(conn) == SMFIR_CONTINUE then + mt.echo("EOM-continue") +elseif mt.getreply(conn) == SMFIR_REPLYCODE then + mt.echo("EOM-reject") +end + +if not mt.eom_check(conn, MT_HDRADD, "X-ExOTA-Authentication-Results") then + mt.echo("no header added") +else + mt.echo("X-ExOTA-Authentication-Results header added") +end + +-- DISCONNECT +mt.disconnect(conn) \ No newline at end of file