From 32aa1540f0f25864816eced0b2f569a6cdfd3973 Mon Sep 17 00:00:00 2001 From: Stanislav Levin Date: Thu, 12 Nov 2020 18:52:51 +0300 Subject: [PATCH] EPN: Enable certificate validation and hostname checking https://pagure.io/freeipa/issue/8579 Signed-off-by: Stanislav Levin Reviewed-By: Rob Crittenden Reviewed-By: Alexander Bokovoy --- 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 17b0420a3..79fa8f5a0 100644 --- a/ipaclient/install/ipa_epn.py +++ b/ipaclient/install/ipa_epn.py @@ -29,6 +29,7 @@ import os 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 @@ class EPN(admintool.AdminTool): 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 @@ class EPN(admintool.AdminTool): 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 @@ class EPN(admintool.AdminTool): 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 @@ class MTAClient: 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 @@ class MTAClient: 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 @@ class MTAClient: 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 @@ class MailUserAgent: 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 @@ class MailUserAgent: 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 afc8489b6..2594e5b58 100644 --- a/ipatests/test_integration/test_epn.py +++ b/ipatests/test_integration/test_epn.py @@ -37,6 +37,7 @@ from subprocess import CalledProcessError 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 @@ USER_EPN_CONF = DEFAULT_EPN_CONF + textwrap.dedent( 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 @@ class TestEPN(IntegrationTest): # 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 @@ class TestEPN(IntegrationTest): '/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 @@ class TestEPN(IntegrationTest): """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 @@ class TestEPN(IntegrationTest): """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 @@ class TestEPN(IntegrationTest): """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 @@ class TestEPN(IntegrationTest): """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, )