CVE-2020-15720 (Python, published 2020-07-14T14:15Z)
Description: value: In Dogtag PKI through 10.8.3, the pki.client.PKIConnection class did not enable python-requests certificate validation. Since the verify parameter was hard-coded in all request functions, it was not possible to override the setting. As a result, tools making use of this class, such as the pki-server command, may have been vulnerable to Person-in-the-Middle attacks in certain non-localhost use cases. This is fixed in 10.9.0-b1.
Severity: MEDIUM (CVSS v3 base score: 6.8)
Weakness: CWE-295 \u2014 Improper Certificate Validation
The product does not validate, or incorrectly validates, a certificate.
Repository: https://github.com/dogtagpki/pki
Files changed:
base/common/python/pki/client.py
Commit message: Enable certificate verification in PKIConnection

To PKIConnection's initialization handler, we introduce a new argument,
cert_paths, which takes a string or iterable; each unit of which is
treated as a capath or cafile depending on whether or not it is a
directory. See ssl.SSLContext.load_verify_locations for more
information. This enables both PKI and IPA to specify independent CA
file locations at the same time and have fallback if this does not work.

Because some users might've already loaded the CA certificate into the
system-wide CA certificate store (if they're running Dogtag in
production), we also inclue the global trust store.

Resolves: rh-bz#1426572

Signed-off-by: Alexander Scheel <ascheel@redhat.com>


Vulnerable code:
    """Custom SSLContext Adapter for requests
                 trust_env=None, verify=False):
        self.session.mount("https://", SSLContextAdapter())

Fixed code:
import os
from requests.adapters import DEFAULT_POOLBLOCK, DEFAULT_POOLSIZE, DEFAULT_RETRIES
    Custom SSLContext Adapter for requests
    """

    def __init__(self, pool_connections=DEFAULT_POOLSIZE,
                 pool_maxsize=DEFAULT_POOLSIZE, max_retries=DEFAULT_RETRIES,
                 pool_block=DEFAULT_POOLBLOCK, verify=True,
                 cert_paths=None):
        self.verify = verify
        self.cafiles = []
        self.capaths = []

        cert_paths = cert_paths or []

        if isinstance(cert_paths, str):
            cert_paths = [cert_paths]

        for path in cert_paths:
            path = path and os.path.expanduser(path)

            if os.path.isdir(path):
                self.capaths.append(path)
            elif os.path.exists(path):
                self.cafiles.append(path)
            else:
                logger.warning("cert_path missing; not used for validation: %s",
                               path)

        # adapters.HTTPAdapter.__init__ calls our init_poolmanager, which needs
        # our cafiles/capaths variables we set up above.
        super(SSLContextAdapter, self).__init__(pool_connections=pool_connections,
                                                pool_maxsize=pool_maxsize,
                                                max_retries=max_retries,
                                                pool_block=pool_block)

        # Load from the system trust store when possible; per documentation
        # this call could silently fail and refuse to configure any
        # certificates. In this instance, the user should provide a
        # certificate manually.
        context.set_default_verify_paths()

        # Load any specific certificate paths that have been specified during
        # adapter initialization.
        for cafile in self.cafiles:
            context.load_verify_locations(cafile=cafile)
        for capath in self.capaths:
            context.load_verify_locations(capath=capath)

        if self.verify:
            # Enable certificate verification
            context.verify_mode = ssl.VerifyMode.CERT_REQUIRED  # pylint: disable=no-member

                 trust_env=None, verify=True, cert_paths=None):
        :param cert_paths: paths to CA certificates / directories in OpenSSL
          format. (default: None)
        :type cert_paths: None, str, list
        self.session.mount("https://", SSLContextAdapter(verify=verify, cert_paths=cert_paths))