URL: https://github.com/freeipa/freeipa/pull/5268
Author: abbra
 Title: #5268: [Backport][ipa-4-8] [EPN] SMTP client enhancements
Action: opened

PR body:
"""
This PR was opened automatically because PR #5257 was pushed to master and 
backport to ipa-4-8 is required.
"""

To pull the PR as Git branch:
git remote add ghfreeipa https://github.com/freeipa/freeipa
git fetch ghfreeipa pull/5268/head:pr5268
git checkout pr5268
From 51a61cae7d29d98543c82009cea711b76f2c31a5 Mon Sep 17 00:00:00 2001
From: Stanislav Levin <s...@altlinux.org>
Date: Mon, 30 Sep 2019 14:59:25 +0300
Subject: [PATCH 1/6] ipatests: Respect platform's openssl dir

There are different build configurations of OpenSSL from one distro
to another. For example,

Debian: '--openssldir=/usr/lib/ssl',
Fedora: '--openssldir=/etc/pki/tls',
openSUSE: '--openssldir=/etc/ssl',
ALTLinux: '--openssldir=/var/lib/ssl'.
Signed-off-by: Stanislav Levin <s...@altlinux.org>
---
 ipaplatform/base/paths.py                     |  3 +
 ipaplatform/debian/paths.py                   |  3 +
 ipaplatform/suse/paths.py                     |  3 +
 ipatests/test_integration/test_cert.py        | 92 ++++++++++++-------
 ipatests/test_integration/test_epn.py         | 55 +++++++++--
 .../test_replica_promotion.py                 |  9 +-
 6 files changed, 119 insertions(+), 46 deletions(-)

diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py
index 0c5494612d8..024d9b167fe 100644
--- a/ipaplatform/base/paths.py
+++ b/ipaplatform/base/paths.py
@@ -208,6 +208,9 @@ class BasePathNamespace:
     ODS_ENFORCER = "/usr/sbin/ods-enforcer"
     ODS_ENFORCER_DB_SETUP = "/usr/sbin/ods-enforcer-db-setup"
     OPENSSL = "/usr/bin/openssl"
+    OPENSSL_DIR = "/etc/pki/tls"
+    OPENSSL_CERTS_DIR = "/etc/pki/tls/certs"
+    OPENSSL_PRIVATE_DIR = "/etc/pki/tls/private"
     PK12UTIL = "/usr/bin/pk12util"
     SOFTHSM2_UTIL = "/usr/bin/softhsm2-util"
     SSLGET = "/usr/bin/sslget"
diff --git a/ipaplatform/debian/paths.py b/ipaplatform/debian/paths.py
index c97007acead..e9ba639ee98 100644
--- a/ipaplatform/debian/paths.py
+++ b/ipaplatform/debian/paths.py
@@ -43,6 +43,9 @@ class DebianPathNamespace(BasePathNamespace):
     NAMED_MANAGED_KEYS_DIR = "/var/cache/bind/dynamic"
     CHRONY_CONF = "/etc/chrony/chrony.conf"
     OPENLDAP_LDAP_CONF = "/etc/ldap/ldap.conf"
+    OPENSSL_DIR = "/usr/lib/ssl"
+    OPENSSL_CERTS_DIR = "/usr/lib/ssl/certs"
+    OPENSSL_PRIVATE_DIR = "/usr/lib/ssl/private"
     ETC_DEBIAN_VERSION = "/etc/debian_version"
     # Old versions of freeipa wrote all trusted certificates to a single
     # file, which is not supported by ca-certificates.
diff --git a/ipaplatform/suse/paths.py b/ipaplatform/suse/paths.py
index e5baf30b8bb..383f191db03 100644
--- a/ipaplatform/suse/paths.py
+++ b/ipaplatform/suse/paths.py
@@ -29,6 +29,9 @@ class SusePathNamespace(BasePathNamespace):
     NAMED_CUSTOM_OPTIONS_CONF = "/etc/named.d/ipa-options-ext.conf"
     NAMED_VAR_DIR = "/var/lib/named"
     NAMED_MANAGED_KEYS_DIR = "/var/lib/named/dyn"
+    OPENSSL_DIR = "/etc/ssl"
+    OPENSSL_CERTS_DIR = "/etc/ssl/certs"
+    OPENSSL_PRIVATE_DIR = "/etc/ssl/private"
     IPA_P11_KIT = "/etc/pki/trust/ipa.p11-kit"
     # Those files are only here to be able to configure them, we copy those in
     # rpm spec to fillupdir
diff --git a/ipatests/test_integration/test_cert.py b/ipatests/test_integration/test_cert.py
index 2ac32a72f33..d84c7f1fc7f 100644
--- a/ipatests/test_integration/test_cert.py
+++ b/ipatests/test_integration/test_cert.py
@@ -6,6 +6,8 @@
 Module provides tests which testing ability of various certificate
 related scenarios.
 """
+import os
+
 import ipaddress
 import pytest
 import random
@@ -78,11 +80,13 @@ def test_cacert_file_appear_with_option_F(self):
 
         related: https://pagure.io/freeipa/issue/8105
         """
-        cmd_arg = ['ipa-getcert', 'request',
-                   '-f', '/etc/pki/tls/certs/test.pem',
-                   '-k', '/etc/pki/tls/private/test.key',
-                   '-K', 'test/%s' % self.clients[0].hostname,
-                   '-F', '/etc/pki/tls/test.CA']
+        cmd_arg = [
+            "ipa-getcert", "request",
+            "-f", os.path.join(paths.OPENSSL_CERTS_DIR, "test.pem"),
+            "-k", os.path.join(paths.OPENSSL_PRIVATE_DIR, "test.key"),
+            "-K", "test/%s" % self.clients[0].hostname,
+            "-F", os.path.join(paths.OPENSSL_DIR, "test.CA"),
+        ]
         result = self.clients[0].run_command(cmd_arg)
         request_id = re.findall(r'\d+', result.stdout_text)
 
@@ -90,13 +94,15 @@ def test_cacert_file_appear_with_option_F(self):
         status = tasks.wait_for_request(self.clients[0], request_id[0], 50)
         assert status == "MONITORING"
 
-        self.clients[0].run_command(['ls', '-l', '/etc/pki/tls/test.CA'])
+        self.clients[0].run_command(
+            ["ls", "-l", os.path.join(paths.OPENSSL_DIR, "test.CA")]
+        )
 
     def test_ipa_getcert_san_aci(self):
         """Test for DNS and IP SAN extensions + ACIs
         """
         hostname = self.clients[0].hostname
-        certfile = '/etc/pki/tls/certs/test2.pem'
+        certfile = os.path.join(paths.OPENSSL_CERTS_DIR, "test2.pem")
 
         tasks.kinit_admin(self.master)
 
@@ -117,7 +123,7 @@ def test_ipa_getcert_san_aci(self):
         cmd_arg = [
             'ipa-getcert', 'request', '-v', '-w',
             '-f', certfile,
-            '-k', '/etc/pki/tls/private/test2.key',
+            '-k', os.path.join(paths.OPENSSL_PRIVATE_DIR, "test2.key"),
             '-K', f'test/{hostname}',
             '-D', hostname,
             '-A', self.clients[0].ip,
@@ -182,9 +188,11 @@ def test_subca_certs(self):
         self.master.run_command(["ipa", "ca-disable", "mysubca"])
         self.master.run_command(["ipa", "ca-del", "mysubca"])
         self.master.run_command(
-            ["rm", "-fv", "/etc/pki/tls/private/test.key"]
+            ["rm", "-fv", os.path.join(paths.OPENSSL_PRIVATE_DIR, "test.key")]
+        )
+        self.master.run_command(
+            ["rm", "-fv", os.path.join(paths.OPENSSL_CERTS_DIR, "test.pem")]
         )
-        self.master.run_command(["rm", "-fv", "/etc/pki/tls/certs/test.pem"])
 
     def test_getcert_list_profile_using_subca(self, test_subca_certs):
         """
@@ -199,10 +207,8 @@ def test_getcert_list_profile_using_subca(self, test_subca_certs):
             "ipa",
             "-I",
             "test-request",
-            "-k",
-            "/etc/pki/tls/private/test.key",
-            "-f",
-            "/etc/pki/tls/certs/test.pem",
+            "-k", os.path.join(paths.OPENSSL_PRIVATE_DIR, "test.key"),
+            "-f", os.path.join(paths.OPENSSL_CERTS_DIR, "test.pem"),
             "-D",
             self.master.hostname,
             "-K",
@@ -245,12 +251,21 @@ def request_cert(self):
                 string.ascii_lowercase
             ) for i in range(10)
         )
-        self.master.run_command([
-            'ipa-getcert', 'request',
-            '-f', '/etc/pki/tls/certs/{}.pem'.format(self.request_id),
-            '-k', '/etc/pki/tls/private/{}.key'.format(self.request_id),
-            '-I', self.request_id,
-            '-K', 'test/{}'.format(self.master.hostname)])
+        self.master.run_command(
+            [
+                'ipa-getcert', 'request',
+                '-f',
+                os.path.join(
+                    paths.OPENSSL_CERTS_DIR, f"{self.request_id}.pem",
+                ),
+                '-k',
+                os.path.join(
+                    paths.OPENSSL_PRIVATE_DIR, f"{self.request_id}.key"
+                ),
+                '-I', self.request_id,
+                '-K', 'test/{}'.format(self.master.hostname)
+            ]
+        )
         status = tasks.wait_for_request(self.master, self.request_id, 100)
         assert status == "MONITORING"
 
@@ -260,16 +275,20 @@ def request_cert(self):
                                  '-i', self.request_id])
         self.master.run_command(
             [
-                'rm',
-                '-rf',
-                '/etc/pki/tls/certs/{}.pem'.format(self.request_id)
+                "rm",
+                "-rf",
+                os.path.join(
+                    paths.OPENSSL_CERTS_DIR, f"{self.request_id}.pem"
+                ),
             ]
         )
         self.master.run_command(
             [
-                'rm',
-                '-rf',
-                '/etc/pki/tls/private/{}.key'.format(self.request_id)
+                "rm",
+                "-rf",
+                os.path.join(
+                    paths.OPENSSL_PRIVATE_DIR, f"{self.request_id}.key"
+                ),
             ]
         )
 
@@ -283,7 +302,7 @@ def test_certmonger_rekey_keysize(self, request_cert):
         related: https://bugzilla.redhat.com/show_bug.cgi?id=1249165
         """
         certdata = self.master.get_file_contents(
-            '/etc/pki/tls/certs/{}.pem'.format(self.request_id)
+            os.path.join(paths.OPENSSL_CERTS_DIR, f"{self.request_id}.pem")
         )
         cert = x509.load_pem_x509_certificate(
             certdata, default_backend()
@@ -299,7 +318,7 @@ def test_certmonger_rekey_keysize(self, request_cert):
         assert status == "MONITORING"
 
         certdata = self.master.get_file_contents(
-            '/etc/pki/tls/certs/{}.pem'.format(self.request_id)
+            os.path.join(paths.OPENSSL_CERTS_DIR, f"{self.request_id}.pem")
         )
         cert = x509.load_pem_x509_certificate(
             certdata, default_backend()
@@ -352,11 +371,14 @@ def test_rekey_keytype_DSA(self):
 
         related: https://bugzilla.redhat.com/show_bug.cgi?id=1249165
         """
-        result = self.master.run_command([
-            'ipa-getcert', 'request',
-            '-f', '/etc/pki/tls/certs/test_dsa.pem',
-            '-k', '/etc/pki/tls/private/test_dsa.key',
-            '-K', 'test/{}'.format(self.master.hostname)])
+        result = self.master.run_command(
+            [
+                'ipa-getcert', 'request',
+                '-f', os.path.join(paths.OPENSSL_CERTS_DIR, "test_dsa.pem"),
+                '-k', os.path.join(paths.OPENSSL_PRIVATE_DIR, "test_dsa.key"),
+                '-K', 'test/{}'.format(self.master.hostname),
+            ]
+        )
         req_id = re.findall(r'\d+', result.stdout_text)
         status = tasks.wait_for_request(self.master, req_id[0], 100)
         assert status == "MONITORING"
@@ -369,7 +391,9 @@ def test_rekey_keytype_DSA(self):
         time.sleep(100)
         # look for keytpe as DSA in request file
         self.master.run_command([
-            'grep', 'DSA', '/var/lib/certmonger/requests/{}'.format(req_id[0])
+            'grep',
+            'DSA',
+            os.path.join(paths.CERTMONGER_REQUESTS_DIR, req_id[0]),
         ])
 
         err_msg = 'Unable to create enrollment request: Invalid Request'
diff --git a/ipatests/test_integration/test_epn.py b/ipatests/test_integration/test_epn.py
index c66d316a62c..3a79929696f 100644
--- a/ipatests/test_integration/test_epn.py
+++ b/ipatests/test_integration/test_epn.py
@@ -35,6 +35,7 @@
 
 from subprocess import CalledProcessError
 
+from ipaplatform.paths import paths
 from ipatests.test_integration.base import IntegrationTest
 from ipatests.pytest_ipa.integration import tasks
 
@@ -108,11 +109,17 @@ def configure_starttls(host):
        Depends on configure_postfix() being executed first.
     """
 
-    host.run_command(r'rm -f /etc/pki/tls/private/postfix.key')
-    host.run_command(r'rm -f /etc/pki/tls/certs/postfix.pem')
+    host.run_command(
+        ["rm", "-f", os.path.join(paths.OPENSSL_PRIVATE_DIR, "postfix.key")]
+    )
+    host.run_command(
+        ["rm", "-f", os.path.join(paths.OPENSSL_CERTS_DIR, "postfix.pem")]
+    )
     host.run_command(["ipa-getcert", "request",
-                      "-f", "/etc/pki/tls/certs/postfix.pem",
-                      "-k", "/etc/pki/tls/private/postfix.key",
+                      "-f",
+                      os.path.join(paths.OPENSSL_CERTS_DIR, "postfix.pem"),
+                      "-k",
+                      os.path.join(paths.OPENSSL_PRIVATE_DIR, "postfix.key"),
                       "-K", "smtp/%s" % host.hostname,
                       "-D", host.hostname,
                       "-O", "postfix",
@@ -123,8 +130,18 @@ def configure_starttls(host):
                       ])
     postconf(host, 'smtpd_tls_loglevel = 1')
     postconf(host, 'smtpd_tls_auth_only = yes')
-    postconf(host, 'smtpd_tls_key_file = /etc/pki/tls/private/postfix.key')
-    postconf(host, 'smtpd_tls_cert_file = /etc/pki/tls/certs/postfix.pem')
+    postconf(
+        host,
+        "smtpd_tls_key_file = {}".format(
+            os.path.join(paths.OPENSSL_PRIVATE_DIR, "postfix.key")
+        )
+    )
+    postconf(
+        host,
+        "smtpd_tls_cert_file = {}".format(
+            os.path.join(paths.OPENSSL_CERTS_DIR, "postfix.pem")
+        )
+    )
     postconf(host, 'smtpd_tls_received_header = yes')
     postconf(host, 'smtpd_tls_session_cache_timeout = 3600s')
 
@@ -246,10 +263,28 @@ def uninstall(cls, mh):
         tasks.uninstall_packages(cls.clients[0], EPN_PKG)
         tasks.uninstall_packages(cls.clients[0], ["postfix"])
         cls.master.run_command(r'rm -f /etc/postfix/smtp.keytab')
-        cls.master.run_command(r'getcert stop-tracking -f '
-                               '/etc/pki/tls/certs/postfix.pem')
-        cls.master.run_command(r'rm -f /etc/pki/tls/private/postfix.key')
-        cls.master.run_command(r'rm -f /etc/pki/tls/certs/postfix.pem')
+        cls.master.run_command(
+            [
+                "getcert",
+                "stop-tracking",
+                "-f",
+                os.path.join(paths.OPENSSL_CERTS_DIR, "postfix.pem"),
+            ]
+        )
+        cls.master.run_command(
+            [
+                "rm",
+                "-f",
+                os.path.join(paths.OPENSSL_PRIVATE_DIR, "postfix.key"),
+            ]
+        )
+        cls.master.run_command(
+            [
+                "rm",
+                "-f",
+                os.path.join(paths.OPENSSL_CERTS_DIR, "postfix.pem"),
+            ]
+        )
 
     @pytest.mark.skip_if_platform(
         "debian", reason="Cannot check installed packages using RPM"
diff --git a/ipatests/test_integration/test_replica_promotion.py b/ipatests/test_integration/test_replica_promotion.py
index c1019aa5573..5071fcbfe75 100644
--- a/ipatests/test_integration/test_replica_promotion.py
+++ b/ipatests/test_integration/test_replica_promotion.py
@@ -4,6 +4,7 @@
 
 from __future__ import absolute_import
 
+import os
 import time
 import re
 import textwrap
@@ -626,8 +627,12 @@ def test_sign_with_subca_on_replica(self):
         master = self.master
         replica = self.replicas[0]
 
-        TEST_KEY_FILE = '/etc/pki/tls/private/test_subca.key'
-        TEST_CRT_FILE = '/etc/pki/tls/private/test_subca.crt'
+        TEST_KEY_FILE = os.path.join(
+            paths.OPENSSL_PRIVATE_DIR, 'test_subca.key'
+        )
+        TEST_CRT_FILE = os.path.join(
+            paths.OPENSSL_PRIVATE_DIR, 'test_subca.crt'
+        )
 
         caacl_cmd = [
             'ipa', 'caacl-add-ca', 'hosts_services_caIPAserviceCert',

From aea98c8b41691744787ce954bd8b315a20ef2ced Mon Sep 17 00:00:00 2001
From: Stanislav Levin <s...@altlinux.org>
Date: Thu, 12 Nov 2020 14:45:50 +0300
Subject: [PATCH 2/6] EPN: Don't downgrade security

If an administrator requests `smtp_security=starttls`, but SMTP
server disables STARTTLS, then EPN downgrade security to `none`,
which means plain text. Administrator doesn't expect such behavior.

Fixes: https://pagure.io/freeipa/issue/8578
Signed-off-by: Stanislav Levin <s...@altlinux.org>
---
 ipaclient/install/ipa_epn.py          | 12 +---
 ipatests/test_integration/test_epn.py | 86 ++++++++++++++++++++++-----
 2 files changed, 74 insertions(+), 24 deletions(-)

diff --git a/ipaclient/install/ipa_epn.py b/ipaclient/install/ipa_epn.py
index 88c926e88fc..17b0420a338 100644
--- a/ipaclient/install/ipa_epn.py
+++ b/ipaclient/install/ipa_epn.py
@@ -685,20 +685,14 @@ def _connect(self):
                 e,
             )
 
-        if (
-            self._conn.has_extn("STARTTLS")
-            and self._security_protocol.lower() == "starttls"
-        ):
+        if self._security_protocol.lower() == "starttls":
             try:
                 self._conn.starttls()
                 self._conn.ehlo()
             except smtplib.SMTPException as e:
-                logger.error(
+                raise RuntimeError(
                     "IPA-EPN: Unable to create an encrypted session to "
-                    "%s:%s: %s",
-                    self._smtp_hostname,
-                    self._smtp_port,
-                    e,
+                    "%s:%s: %s" % (self._smtp_hostname, self._smtp_port, e)
                 )
 
         if self._username and self._password:
diff --git a/ipatests/test_integration/test_epn.py b/ipatests/test_integration/test_epn.py
index 3a79929696f..b9cde8fe680 100644
--- a/ipatests/test_integration/test_epn.py
+++ b/ipatests/test_integration/test_epn.py
@@ -43,6 +43,25 @@
 
 EPN_PKG = ["*ipa-client-epn"]
 
+STARTTLS_EPN_CONF = textwrap.dedent(
+    """\
+    [global]
+    smtp_user={user}
+    smtp_password={password}
+    smtp_security=starttls
+    """
+)
+
+SSL_EPN_CONF = textwrap.dedent(
+    """\
+    [global]
+    smtp_user={user}
+    smtp_password={password}
+    smtp_port=465
+    smtp_security=ssl
+    """
+)
+
 
 def datetime_to_generalized_time(dt):
     """Convert datetime to LDAP_GENERALIZED_TIME_FORMAT
@@ -93,6 +112,11 @@ def configure_postfix(host, realm):
     postconf(host, 'broken_sasl_auth_clients = yes')
     postconf(host, 'smtpd_sasl_authenticated_header = yes')
     postconf(host, 'smtpd_sasl_local_domain = %s' % realm)
+    # TLS will not be used
+    postconf(host, 'smtpd_tls_security_level = none')
+
+    # disable procmail if exists, make use of default local(8) delivery agent
+    postconf(host, "mailbox_command=")
 
     host.run_command(["systemctl", "restart", "saslauthd"])
 
@@ -144,6 +168,8 @@ def configure_starttls(host):
     )
     postconf(host, 'smtpd_tls_received_header = yes')
     postconf(host, 'smtpd_tls_session_cache_timeout = 3600s')
+    # announce STARTTLS support to remote SMTP clients, not require
+    postconf(host, 'smtpd_tls_security_level = may')
 
     host.run_command(["systemctl", "restart", "postfix"])
 
@@ -319,6 +345,43 @@ def test_EPN_connection_refused(self):
             stderr_text
         assert rc > 0
 
+    def test_EPN_no_security_downgrade_starttls(self):
+        """Configure postfix without starttls and test no auth happens
+        """
+        epn_conf = STARTTLS_EPN_CONF.format(
+            user=self.master.config.admin_name,
+            password=self.master.config.admin_password,
+        )
+        self.master.put_file_contents('/etc/ipa/epn.conf', epn_conf)
+
+        (unused, stderr_text, rc) = self._check_epn_output(
+            self.master, mailtest=True,
+            raiseonerr=False, validatejson=False
+        )
+        expected_msg = "IPA-EPN: Unable to create an encrypted session to"
+        assert expected_msg in stderr_text
+        assert rc > 0
+
+    def test_EPN_no_security_downgrade_tls(self):
+        """Configure postfix without tls and test no auth happens
+        """
+        epn_conf = SSL_EPN_CONF.format(
+            user=self.master.config.admin_name,
+            password=self.master.config.admin_password,
+        )
+        self.master.put_file_contents('/etc/ipa/epn.conf', epn_conf)
+
+        (unused, stderr_text, rc) = self._check_epn_output(
+            self.master, mailtest=True,
+            raiseonerr=False, validatejson=False
+        )
+        expected_msg = (
+            "IPA-EPN: Could not connect to the configured SMTP "
+            "server"
+        )
+        assert expected_msg in stderr_text
+        assert rc > 0
+
     def test_EPN_smoketest_1(self):
         """No users except admin. Check --dry-run output.
            With the default configuration, the result should be an empty list.
@@ -611,13 +674,10 @@ def test_mailtest_dry_run(self):
     def test_EPN_starttls(self, cleanupmail):
         """Configure with starttls and test delivery
         """
-        epn_conf = textwrap.dedent('''
-            [global]
-            smtp_user={user}
-            smtp_password={password}
-            smtp_security=starttls
-        '''.format(user=self.master.config.admin_name,
-                   password=self.master.config.admin_password))
+        epn_conf = STARTTLS_EPN_CONF.format(
+            user=self.master.config.admin_name,
+            password=self.master.config.admin_password,
+        )
         self.master.put_file_contents('/etc/ipa/epn.conf', epn_conf)
         configure_starttls(self.master)
 
@@ -629,14 +689,10 @@ def test_EPN_starttls(self, cleanupmail):
     def test_EPN_ssl(self, cleanupmail):
         """Configure with ssl and test delivery
         """
-        epn_conf = textwrap.dedent('''
-            [global]
-            smtp_user={user}
-            smtp_password={password}
-            smtp_port=465
-            smtp_security=ssl
-        '''.format(user=self.master.config.admin_name,
-                   password=self.master.config.admin_password))
+        epn_conf = SSL_EPN_CONF.format(
+            user=self.master.config.admin_name,
+            password=self.master.config.admin_password,
+        )
         self.master.put_file_contents('/etc/ipa/epn.conf', epn_conf)
         configure_ssl(self.master)
 

From 23a9f75de62d13202ae512fa5656d992e4e43d68 Mon Sep 17 00:00:00 2001
From: Stanislav Levin <s...@altlinux.org>
Date: Thu, 12 Nov 2020 18:35:14 +0300
Subject: [PATCH 3/6] test_epn: Standardize EPN configs for deduplication

Signed-off-by: Stanislav Levin <s...@altlinux.org>
---
 ipatests/test_integration/test_epn.py | 71 ++++++++++++++-------------
 1 file changed, 38 insertions(+), 33 deletions(-)

diff --git a/ipatests/test_integration/test_epn.py b/ipatests/test_integration/test_epn.py
index b9cde8fe680..afc8489b6fe 100644
--- a/ipatests/test_integration/test_epn.py
+++ b/ipatests/test_integration/test_epn.py
@@ -43,20 +43,27 @@
 
 EPN_PKG = ["*ipa-client-epn"]
 
-STARTTLS_EPN_CONF = textwrap.dedent(
+DEFAULT_EPN_CONF = textwrap.dedent(
     """\
     [global]
+    """
+)
+
+USER_EPN_CONF = DEFAULT_EPN_CONF + textwrap.dedent(
+    """\
     smtp_user={user}
     smtp_password={password}
+    """
+)
+
+STARTTLS_EPN_CONF = USER_EPN_CONF + textwrap.dedent(
+    """\
     smtp_security=starttls
     """
 )
 
-SSL_EPN_CONF = textwrap.dedent(
+SSL_EPN_CONF = USER_EPN_CONF + textwrap.dedent(
     """\
-    [global]
-    smtp_user={user}
-    smtp_password={password}
     smtp_port=465
     smtp_security=ssl
     """
@@ -387,10 +394,7 @@ def test_EPN_smoketest_1(self):
            With the default configuration, the result should be an empty list.
            Also check behavior on master and client alike.
         """
-        epn_conf = textwrap.dedent('''
-            [global]
-        ''')
-        self.master.put_file_contents('/etc/ipa/epn.conf', epn_conf)
+        self.master.put_file_contents('/etc/ipa/epn.conf', DEFAULT_EPN_CONF)
         # check EPN on client (LDAP+GSSAPI)
         (stdout_text, unused, _unused) = self._check_epn_output(
             self.clients[0], dry_run=True
@@ -609,12 +613,10 @@ def test_EPN_nbdays_input_4(self):
     def test_EPN_authenticated(self, cleanupmail):
         """Enable authentication and test that mail is delivered
         """
-        epn_conf = textwrap.dedent('''
-            [global]
-            smtp_user={user}
-            smtp_password={password}
-        '''.format(user=self.master.config.admin_name,
-                   password=self.master.config.admin_password))
+        epn_conf = USER_EPN_CONF.format(
+            user=self.master.config.admin_name,
+            password=self.master.config.admin_password,
+        )
         self.master.put_file_contents('/etc/ipa/epn.conf', epn_conf)
 
         tasks.ipa_epn(self.master)
@@ -648,14 +650,18 @@ def test_mailtest(self, cleanupmail):
 
            Using a non-expired user here, user2, to receive the result.
         """
-        epn_conf = textwrap.dedent('''
-            [global]
-            smtp_user={user}
-            smtp_password={password}
-            smtp_admin=user2@{domain}
-        '''.format(user=self.master.config.admin_name,
-                   password=self.master.config.admin_password,
-                   domain=self.master.domain.name))
+        epn_conf = (
+            USER_EPN_CONF
+            + textwrap.dedent(
+                """\
+                smtp_admin=user2@{domain}
+                """
+            )
+        ).format(
+            user=self.master.config.admin_name,
+            password=self.master.config.admin_password,
+            domain=self.master.domain.name,
+        )
         self.master.put_file_contents('/etc/ipa/epn.conf', epn_conf)
 
         tasks.ipa_epn(self.master, mailtest=True)
@@ -704,19 +710,21 @@ def test_EPN_ssl(self, cleanupmail):
     def test_EPN_delay_config(self, cleanupmail):
         """Test the smtp_delay configuration option
         """
-        epn_conf = textwrap.dedent('''
-            [global]
+        epn_conf = DEFAULT_EPN_CONF + textwrap.dedent(
+            """\
             smtp_delay=A
-        ''')
+            """
+        )
         self.master.put_file_contents('/etc/ipa/epn.conf', epn_conf)
 
         result = tasks.ipa_epn(self.master, raiseonerr=False)
         assert "could not convert string to float: 'A'" in result.stderr_text
 
-        epn_conf = textwrap.dedent('''
-            [global]
+        epn_conf = DEFAULT_EPN_CONF + textwrap.dedent(
+            """\
             smtp_delay=-1
-        ''')
+            """
+        )
         self.master.put_file_contents('/etc/ipa/epn.conf', epn_conf)
         result = tasks.ipa_epn(self.master, raiseonerr=False)
         assert "smtp_delay cannot be less than zero" in result.stderr_text
@@ -726,10 +734,7 @@ def test_EPN_admin(self):
            It also doesn't by default have an e-mail address
            Check --dry-run output.
         """
-        epn_conf = textwrap.dedent('''
-            [global]
-        ''')
-        self.master.put_file_contents('/etc/ipa/epn.conf', epn_conf)
+        self.master.put_file_contents('/etc/ipa/epn.conf', DEFAULT_EPN_CONF)
         self.master.run_command(
             ['ipa', 'user-mod', 'admin', '--password-expiration',
              datetime_to_generalized_time(

From 662876b3b14ed691470000e95b5ee718a624e224 Mon Sep 17 00:00:00 2001
From: Stanislav Levin <s...@altlinux.org>
Date: Thu, 12 Nov 2020 18:52:51 +0300
Subject: [PATCH 4/6] EPN: Enable certificate validation and hostname checking

https://pagure.io/freeipa/issue/8579
Signed-off-by: Stanislav Levin <s...@altlinux.org>
---
 ipaclient/install/ipa_epn.py          | 23 ++++++++++++++++++++---
 ipatests/test_integration/test_epn.py | 25 ++++++++++++++++++-------
 2 files changed, 38 insertions(+), 10 deletions(-)

diff --git a/ipaclient/install/ipa_epn.py b/ipaclient/install/ipa_epn.py
index 17b0420a338..79fa8f5a01f 100644
--- a/ipaclient/install/ipa_epn.py
+++ b/ipaclient/install/ipa_epn.py
@@ -29,6 +29,7 @@
 import pwd
 import logging
 import smtplib
+import ssl
 import time
 
 from collections import deque
@@ -205,6 +206,7 @@ class EPN(admintool.AdminTool):
     def __init__(self, options, args):
         super(EPN, self).__init__(options, args)
         self._conn = None
+        self._ssl_context = None
         self._expiring_password_user_list = EPNUserList()
         self._ldap_data = []
         self._date_ranges = []
@@ -291,12 +293,15 @@ def run(self):
             logger.error("IPA client is not configured on this system.")
             raise admintool.ScriptError()
 
+        # tasks required privileges
         self._get_krb5_ticket()
         self._read_configuration()
         self._validate_configuration()
         self._parse_configuration()
         self._get_connection()
         self._read_ipa_configuration()
+        self._create_ssl_context()
+
         drop_privileges()
         if self.options.mailtest:
             self._gentestdata()
@@ -316,6 +321,7 @@ def run(self):
                 smtp_timeout=api.env.smtp_timeout,
                 smtp_username=api.env.smtp_user,
                 smtp_password=api.env.smtp_password,
+                ssl_context=self._ssl_context,
                 x_mailer=self.command_name,
                 msg_subtype=api.env.msg_subtype,
                 msg_charset=api.env.msg_charset,
@@ -457,6 +463,14 @@ def _get_connection(self):
 
         return self._conn
 
+    def _create_ssl_context(self):
+        """Create SSL context.
+           This must be done before the dropping priviliges to allow
+           read in the smtp client's certificate and private key if specified.
+        """
+        if api.env.smtp_security.lower() in ("starttls", "ssl"):
+            self._ssl_context = ssl.create_default_context()
+
     def _fetch_data_from_ldap(self, date_range):
         """Run a LDAP query to fetch a list of user entries whose passwords
            would expire in the near future. Store in self._ldap_data.
@@ -603,15 +617,15 @@ def __init__(
         smtp_timeout=60,
         smtp_username=None,
         smtp_password=None,
+        ssl_context=None,
     ):
-        # We only support "none" (cleartext) for now.
-        # Future values: "ssl", "starttls"
         self._security_protocol = security_protocol
         self._smtp_hostname = smtp_hostname
         self._smtp_port = smtp_port
         self._smtp_timeout = smtp_timeout
         self._username = smtp_username
         self._password = smtp_password
+        self._ssl_context = ssl_context
 
         # This should not be touched
         self._conn = None
@@ -664,6 +678,7 @@ def _connect(self):
                     host=self._smtp_hostname,
                     port=self._smtp_port,
                     timeout=self._smtp_timeout,
+                    context=self._ssl_context,
                 )
         except (socketerror, smtplib.SMTPException) as e:
             msg = \
@@ -687,7 +702,7 @@ def _connect(self):
 
         if self._security_protocol.lower() == "starttls":
             try:
-                self._conn.starttls()
+                self._conn.starttls(context=self._ssl_context)
                 self._conn.ehlo()
             except smtplib.SMTPException as e:
                 raise RuntimeError(
@@ -743,6 +758,7 @@ def __init__(
         smtp_timeout=60,
         smtp_username=None,
         smtp_password=None,
+        ssl_context=None,
         x_mailer=None,
         msg_subtype="plain",
         msg_charset="utf8",
@@ -766,6 +782,7 @@ def __init__(
             smtp_timeout=smtp_timeout,
             smtp_username=smtp_username,
             smtp_password=smtp_password,
+            ssl_context=ssl_context,
         )
 
     def cleanup(self):
diff --git a/ipatests/test_integration/test_epn.py b/ipatests/test_integration/test_epn.py
index afc8489b6fe..2594e5b58d3 100644
--- a/ipatests/test_integration/test_epn.py
+++ b/ipatests/test_integration/test_epn.py
@@ -37,6 +37,7 @@
 
 from ipaplatform.paths import paths
 from ipatests.test_integration.base import IntegrationTest
+from ipatests.pytest_ipa.integration.firewall import Firewall
 from ipatests.pytest_ipa.integration import tasks
 
 logger = logging.getLogger(__name__)
@@ -58,12 +59,14 @@
 
 STARTTLS_EPN_CONF = USER_EPN_CONF + textwrap.dedent(
     """\
+    smtp_server={server}
     smtp_security=starttls
     """
 )
 
 SSL_EPN_CONF = USER_EPN_CONF + textwrap.dedent(
     """\
+    smtp_server={server}
     smtp_port=465
     smtp_security=ssl
     """
@@ -125,6 +128,9 @@ def configure_postfix(host, realm):
     # disable procmail if exists, make use of default local(8) delivery agent
     postconf(host, "mailbox_command=")
 
+    # listen on all active interfaces
+    postconf(host, "inet_interfaces = all")
+
     host.run_command(["systemctl", "restart", "saslauthd"])
 
     result = host.run_command(["postconf", "mydestination"])
@@ -264,6 +270,7 @@ def install(cls, mh):
         #   doesn't know about.
         # - Adds a class variable, pkg, containing the package name of
         #   the downloaded *ipa-client-epn rpm.
+        hosts = [cls.master, cls.clients[0]]
         tasks.uninstall_packages(cls.clients[0],EPN_PKG)
         pkgdir = tasks.download_packages(cls.clients[0], EPN_PKG)
         pkg = cls.clients[0].run_command(r'ls -1 {}'.format(pkgdir))
@@ -273,20 +280,20 @@ def install(cls, mh):
                                     '/tmp'])
         cls.clients[0].run_command(r'rm -rf {}'.format(pkgdir))
 
-        tasks.install_packages(cls.master, EPN_PKG)
-        tasks.install_packages(cls.master, ["postfix"])
-        tasks.install_packages(cls.clients[0], EPN_PKG)
-        tasks.install_packages(cls.clients[0], ["postfix"])
-        for host in (cls.master, cls.clients[0]):
+        for host in hosts:
+            tasks.install_packages(host, EPN_PKG + ["postfix"])
             try:
                 tasks.install_packages(host, ["cyrus-sasl"])
             except Exception:
                 # the package is likely already installed
                 pass
+
         tasks.install_master(cls.master, setup_dns=True)
         tasks.install_client(cls.master, cls.clients[0])
-        configure_postfix(cls.master, cls.master.domain.realm)
-        configure_postfix(cls.clients[0], cls.master.domain.realm)
+        for host in hosts:
+            configure_postfix(host, cls.master.domain.realm)
+            Firewall(host).enable_services(["smtp", "smtps"])
+
 
     @classmethod
     def uninstall(cls, mh):
@@ -356,6 +363,7 @@ def test_EPN_no_security_downgrade_starttls(self):
         """Configure postfix without starttls and test no auth happens
         """
         epn_conf = STARTTLS_EPN_CONF.format(
+            server=self.master.hostname,
             user=self.master.config.admin_name,
             password=self.master.config.admin_password,
         )
@@ -373,6 +381,7 @@ def test_EPN_no_security_downgrade_tls(self):
         """Configure postfix without tls and test no auth happens
         """
         epn_conf = SSL_EPN_CONF.format(
+            server=self.master.hostname,
             user=self.master.config.admin_name,
             password=self.master.config.admin_password,
         )
@@ -681,6 +690,7 @@ def test_EPN_starttls(self, cleanupmail):
         """Configure with starttls and test delivery
         """
         epn_conf = STARTTLS_EPN_CONF.format(
+            server=self.master.hostname,
             user=self.master.config.admin_name,
             password=self.master.config.admin_password,
         )
@@ -696,6 +706,7 @@ def test_EPN_ssl(self, cleanupmail):
         """Configure with ssl and test delivery
         """
         epn_conf = SSL_EPN_CONF.format(
+            server=self.master.hostname,
             user=self.master.config.admin_name,
             password=self.master.config.admin_password,
         )

From 801e1da6070d4d9e43cb95837e5466ef4aea58f1 Mon Sep 17 00:00:00 2001
From: Stanislav Levin <s...@altlinux.org>
Date: Thu, 12 Nov 2020 19:21:05 +0300
Subject: [PATCH 5/6] EPN: Allow authentication by SMTP client's certificate

SMTP server may ask or require client's certificate for verification.
To support this the underlying Python's functionality is used [0].

Added 3 new options(corresponds to `load_cert_chain`):
- smtp_client_cert - the path to a single file in PEM format containing the
  certificate.
- smtp_client_key - the path to a file containing the private key in.
- smtp_client_key_pass - the password for decrypting the private key.

[0]: https://docs.python.org/3/library/ssl.html#ssl.SSLContext.load_cert_chain

Fixes: https://pagure.io/freeipa/issue/8580
Signed-off-by: Stanislav Levin <s...@altlinux.org>
---
 client/man/epn.conf.5                 |   9 ++
 client/share/epn.conf                 |  17 ++++
 ipaclient/install/ipa_epn.py          |   9 ++
 ipatests/test_integration/test_epn.py | 141 ++++++++++++++++++--------
 4 files changed, 136 insertions(+), 40 deletions(-)

diff --git a/client/man/epn.conf.5 b/client/man/epn.conf.5
index df1f0156c85..60508d292bd 100644
--- a/client/man/epn.conf.5
+++ b/client/man/epn.conf.5
@@ -60,6 +60,15 @@ Specifies the id of the user to authenticate with the SMTP server. Default None.
 .B smtp_password <password>
 Specifies the password for the authorized user. Default None.
 .TP
+.B smtp_client_cert <certificate>
+Specifies the path to a single file in PEM format containing the certificate. Default None.
+.TP
+.B smtp_client_key <private key>
+Specifies the path to a file containing the private key in. Otherwise the private key will be taken from certfile as well. Default None.
+.TP
+.B smtp_client_key_pass <private key password>
+Specifies the password for decrypting the private key. Default None.
+.TP
 .B smtp_timeout <seconds>
 Specifies the number of seconds to wait for SMTP to respond. Default 60.
 .TP
diff --git a/client/share/epn.conf b/client/share/epn.conf
index e3645801cbb..fa0bc299cf1 100644
--- a/client/share/epn.conf
+++ b/client/share/epn.conf
@@ -23,6 +23,23 @@ smtp_port = 25
 # Default None (empty value).
 # smtp_password =
 
+# Specifies the path to a single file in PEM format containing the certificate.
+# https://docs.python.org/3/library/ssl.html#ssl.SSLContext.load_cert_chain
+# Default None (empty value).
+# smtp_client_cert =
+
+# Specifies the path to a file containing the private key in. Otherwise the
+# private key will be taken from certfile as well.
+# https://docs.python.org/3/library/ssl.html#ssl.SSLContext.load_cert_chain
+# Default None (empty value).
+# smtp_client_key =
+
+# Specifies the password for decrypting the private key. It will be ignored if
+# the private key is not encrypted and no password is needed.
+# https://docs.python.org/3/library/ssl.html#ssl.SSLContext.load_cert_chain
+# Default None (empty value).
+# smtp_client_key_pass =
+
 # Specifies the number of seconds to wait for SMTP to respond.
 smtp_timeout = 60
 
diff --git a/ipaclient/install/ipa_epn.py b/ipaclient/install/ipa_epn.py
index 79fa8f5a01f..14bf686f72d 100644
--- a/ipaclient/install/ipa_epn.py
+++ b/ipaclient/install/ipa_epn.py
@@ -56,6 +56,9 @@
     "smtp_port": 25,
     "smtp_user": None,
     "smtp_password": None,
+    "smtp_client_cert": None,
+    "smtp_client_key": None,
+    "smtp_client_key_pass": None,
     "smtp_timeout": 60,
     "smtp_security": "none",
     "smtp_admin": "root@localhost",
@@ -470,6 +473,12 @@ def _create_ssl_context(self):
         """
         if api.env.smtp_security.lower() in ("starttls", "ssl"):
             self._ssl_context = ssl.create_default_context()
+            if api.env.smtp_client_cert:
+                self._ssl_context.load_cert_chain(
+                    certfile=api.env.smtp_client_cert,
+                    keyfile=api.env.smtp_client_key,
+                    password=str(api.env.smtp_client_key_pass),
+                )
 
     def _fetch_data_from_ldap(self, date_range):
         """Run a LDAP query to fetch a list of user entries whose passwords
diff --git a/ipatests/test_integration/test_epn.py b/ipatests/test_integration/test_epn.py
index 2594e5b58d3..8b1b11c340e 100644
--- a/ipatests/test_integration/test_epn.py
+++ b/ipatests/test_integration/test_epn.py
@@ -44,6 +44,13 @@
 
 EPN_PKG = ["*ipa-client-epn"]
 
+SMTP_CLIENT_CERT = os.path.join(paths.OPENSSL_CERTS_DIR, "smtp_client.pem")
+SMTP_CLIENT_KEY = os.path.join(paths.OPENSSL_PRIVATE_DIR, "smtp_client.key")
+SMTP_CLIENT_KEY_PASS = "Secret123"
+
+SMTPD_KEY = os.path.join(paths.OPENSSL_PRIVATE_DIR, "postfix.key")
+SMTPD_CERT = os.path.join(paths.OPENSSL_CERTS_DIR, "postfix.pem")
+
 DEFAULT_EPN_CONF = textwrap.dedent(
     """\
     [global]
@@ -72,6 +79,13 @@
     """
 )
 
+CLIENT_CERT_EPN_CONF = textwrap.dedent(
+    """\
+    smtp_client_cert={client_cert}
+    smtp_client_key={client_key}
+    smtp_client_key_pass={client_key_pass}
+    """
+)
 
 def datetime_to_generalized_time(dt):
     """Convert datetime to LDAP_GENERALIZED_TIME_FORMAT
@@ -146,17 +160,10 @@ def configure_starttls(host):
        Depends on configure_postfix() being executed first.
     """
 
-    host.run_command(
-        ["rm", "-f", os.path.join(paths.OPENSSL_PRIVATE_DIR, "postfix.key")]
-    )
-    host.run_command(
-        ["rm", "-f", os.path.join(paths.OPENSSL_CERTS_DIR, "postfix.pem")]
-    )
+    host.run_command(["rm", "-f", SMTPD_KEY, SMTPD_CERT])
     host.run_command(["ipa-getcert", "request",
-                      "-f",
-                      os.path.join(paths.OPENSSL_CERTS_DIR, "postfix.pem"),
-                      "-k",
-                      os.path.join(paths.OPENSSL_PRIVATE_DIR, "postfix.key"),
+                      "-f", SMTPD_CERT,
+                      "-k", SMTPD_KEY,
                       "-K", "smtp/%s" % host.hostname,
                       "-D", host.hostname,
                       "-O", "postfix",
@@ -167,18 +174,8 @@ def configure_starttls(host):
                       ])
     postconf(host, 'smtpd_tls_loglevel = 1')
     postconf(host, 'smtpd_tls_auth_only = yes')
-    postconf(
-        host,
-        "smtpd_tls_key_file = {}".format(
-            os.path.join(paths.OPENSSL_PRIVATE_DIR, "postfix.key")
-        )
-    )
-    postconf(
-        host,
-        "smtpd_tls_cert_file = {}".format(
-            os.path.join(paths.OPENSSL_CERTS_DIR, "postfix.pem")
-        )
-    )
+    postconf(host, "smtpd_tls_key_file = {}".format(SMTPD_KEY))
+    postconf(host, "smtpd_tls_cert_file = {}".format(SMTPD_CERT))
     postconf(host, 'smtpd_tls_received_header = yes')
     postconf(host, 'smtpd_tls_session_cache_timeout = 3600s')
     # announce STARTTLS support to remote SMTP clients, not require
@@ -187,6 +184,33 @@ def configure_starttls(host):
     host.run_command(["systemctl", "restart", "postfix"])
 
 
+def configure_ssl_client_cert(host):
+    """Obtain a TLS cert for the SMTP client and configure postfix for client
+       certificate verification.
+
+       Depends on configure_starttls().
+    """
+    host.run_command(["rm", "-f", SMTP_CLIENT_KEY, SMTP_CLIENT_CERT])
+
+    host.run_command(["ipa-getcert", "request",
+                      "-f", SMTP_CLIENT_CERT,
+                      "-k", SMTP_CLIENT_KEY,
+                      "-K", "smtp_client/%s" % host.hostname,
+                      "-D", host.hostname,
+                      "-P", "Secret123",
+                      "-w",
+                      ])
+
+    # mandatory TLS encryption
+    postconf(host, "smtpd_tls_security_level = encrypt")
+    # require a trusted remote SMTP client certificate
+    postconf(host, "smtpd_tls_req_ccert = yes")
+    # CA certificates of root CAs trusted to sign remote SMTP client cert
+    postconf(host, f"smtpd_tls_CAfile = {paths.IPA_CA_CRT}")
+
+    host.run_command(["systemctl", "restart", "postfix"])
+
+
 def configure_ssl(host):
     """Enable the ssl listener on port 465.
     """
@@ -303,26 +327,18 @@ def uninstall(cls, mh):
         tasks.uninstall_packages(cls.clients[0], EPN_PKG)
         tasks.uninstall_packages(cls.clients[0], ["postfix"])
         cls.master.run_command(r'rm -f /etc/postfix/smtp.keytab')
-        cls.master.run_command(
-            [
-                "getcert",
-                "stop-tracking",
-                "-f",
-                os.path.join(paths.OPENSSL_CERTS_DIR, "postfix.pem"),
-            ]
-        )
-        cls.master.run_command(
-            [
-                "rm",
-                "-f",
-                os.path.join(paths.OPENSSL_PRIVATE_DIR, "postfix.key"),
-            ]
-        )
+
+        for cert in [SMTPD_CERT, SMTP_CLIENT_CERT]:
+            cls.master.run_command(["getcert", "stop-tracking", "-f", cert])
+
         cls.master.run_command(
             [
                 "rm",
                 "-f",
-                os.path.join(paths.OPENSSL_CERTS_DIR, "postfix.pem"),
+                SMTPD_CERT,
+                SMTPD_KEY,
+                SMTP_CLIENT_CERT,
+                SMTP_CLIENT_KEY,
             ]
         )
 
@@ -342,7 +358,7 @@ def test_EPN_config_file(self):
         assert epn_conf in cmd1.stdout_text
         assert epn_template in cmd1.stdout_text
         cmd2 = self.master.run_command(["sha256sum", epn_conf])
-        ck = "192481b52fb591112afd7b55b12a44c6618fdbc7e05a3b1866fd67ec579c51df"
+        ck = "9977d846539d4945900bd04bae25bf746ac75fb561d3769014002db04e1790b8"
         assert cmd2.stdout_text.find(ck) == 0
 
     def test_EPN_connection_refused(self):
@@ -433,8 +449,10 @@ def cleanupusers(self):
     @pytest.fixture
     def cleanupmail(self):
         """Cleanup any existing mail that has been sent."""
+        cmd = ["rm", "-f"]
         for i in range(30):
-            self.master.run_command(["rm", "-f", "/var/mail/user%d" % i])
+            cmd.append("/var/mail/user%d" % i)
+        self.master.run_command(cmd)
 
     def test_EPN_smoketest_2(self, cleanupusers):
         """Add a user without password.
@@ -718,6 +736,49 @@ def test_EPN_ssl(self, cleanupmail):
             validate_mail(self.master, i,
                           "Hi test user,\nYour login entry user%d is going" % i)
 
+    def test_EPN_ssl_client_cert(self, cleanupmail):
+        """Configure with ssl + client certificate and test delivery
+        """
+        epn_conf = (SSL_EPN_CONF + CLIENT_CERT_EPN_CONF).format(
+            server=self.master.hostname,
+            user=self.master.config.admin_name,
+            password=self.master.config.admin_password,
+            client_cert=SMTP_CLIENT_CERT,
+            client_key=SMTP_CLIENT_KEY,
+            client_key_pass=SMTP_CLIENT_KEY_PASS,
+        )
+        self.master.put_file_contents('/etc/ipa/epn.conf', epn_conf)
+        configure_ssl_client_cert(self.master)
+
+        tasks.ipa_epn(self.master)
+        for i in self.notify_ttls:
+            validate_mail(
+                self.master,
+                i,
+                "Hi test user,\nYour login entry user%d is going" % i
+            )
+
+    def test_EPN_starttls_client_cert(self, cleanupmail):
+        """Configure with starttls + client certificate and test delivery
+        """
+        epn_conf = (STARTTLS_EPN_CONF + CLIENT_CERT_EPN_CONF).format(
+            server=self.master.hostname,
+            user=self.master.config.admin_name,
+            password=self.master.config.admin_password,
+            client_cert=SMTP_CLIENT_CERT,
+            client_key=SMTP_CLIENT_KEY,
+            client_key_pass=SMTP_CLIENT_KEY_PASS,
+        )
+        self.master.put_file_contents('/etc/ipa/epn.conf', epn_conf)
+
+        tasks.ipa_epn(self.master)
+        for i in self.notify_ttls:
+            validate_mail(
+                self.master,
+                i,
+                "Hi test user,\nYour login entry user%d is going" % i
+            )
+
     def test_EPN_delay_config(self, cleanupmail):
         """Test the smtp_delay configuration option
         """

From de86f1d00010e7bd43d0e2b8d78ab10b9f7fa99f Mon Sep 17 00:00:00 2001
From: Stanislav Levin <s...@altlinux.org>
Date: Fri, 13 Nov 2020 11:58:52 +0300
Subject: [PATCH 6/6] ipatests: Collect EPN log for debugging

Signed-off-by: Stanislav Levin <s...@altlinux.org>
---
 ipatests/pytest_ipa/integration/__init__.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/ipatests/pytest_ipa/integration/__init__.py b/ipatests/pytest_ipa/integration/__init__.py
index ab7f9114e7a..8fbf5247cf9 100644
--- a/ipatests/pytest_ipa/integration/__init__.py
+++ b/ipatests/pytest_ipa/integration/__init__.py
@@ -62,6 +62,8 @@
     # IPA backup and restore logs
     paths.IPARESTORE_LOG,
     paths.IPABACKUP_LOG,
+    # EPN log
+    paths.IPAEPN_LOG,
     # kerberos related logs
     paths.KADMIND_LOG,
     paths.KRB5KDC_LOG,
_______________________________________________
FreeIPA-devel mailing list -- freeipa-devel@lists.fedorahosted.org
To unsubscribe send an email to freeipa-devel-le...@lists.fedorahosted.org
Fedora Code of Conduct: 
https://docs.fedoraproject.org/en-US/project/code-of-conduct/
List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines
List Archives: 
https://lists.fedorahosted.org/archives/list/freeipa-devel@lists.fedorahosted.org

Reply via email to