HEX
Server: Apache
System: Linux scp1.abinfocom.com 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64
User: confeduphaar (1010)
PHP: 8.1.33
Disabled: exec,passthru,shell_exec,system
Upload Files
File: //lib/mysqlsh/lib/python3.8/site-packages/oci/fips.py
# coding: utf-8
# Copyright (c) 2016, 2025, Oracle and/or its affiliates.  All rights reserved.
# This software is dual-licensed to you under the Universal Permissive License (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose either license.

from __future__ import absolute_import
import ctypes.util
import ctypes
import logging
import os
import re
import ssl
import sys


class DevNull:
    """
    Simple class to supress errors which may occur when importing hashlib
    in FIPS mode.
    """
    def write(self, msg):
        pass


def get_openssl_version():
    version_str = ssl.OPENSSL_VERSION
    match = re.search(r'OpenSSL (\d+\.\d+\.\d+)', version_str)
    if match:
        return match.group(1)
    else:
        return version_str


def override_libcrypto(fips_libcrypto_path):
    """
    Override libcrypto and add FIPS_mode function to ssl if it is not there
    """
    logger = logging.getLogger("{}.{}".format(__name__, id(override_libcrypto)))
    logger.addHandler(logging.NullHandler())
    openssl_version = get_openssl_version()
    if openssl_version.startswith("1") and fips_libcrypto_path is not None:
        # Handle Openssl 1
        _bs_crypto = ctypes.CDLL(fips_libcrypto_path)
        _bs_crypto.FIPS_mode_set(ctypes.c_int(1))
        import ssl  # noqa: E402
        if not hasattr(ssl, 'FIPS_mode'):
            ssl.FIPS_mode = _bs_crypto.FIPS_mode


def md5(initial_message=''):
    """
    Placeholder md5 function for hashlib so it won't segfault when called after
    enabling FIPS mode.
    """
    raise ValueError("md5 disabled for fips")
    return None


def patch_hashlib_md5():
    """
    hashlib.md5 is imported by urllib3, which is required by requests,
    which is used by oci (python sdk).  This will cause errors so we need to
    patch hashlib.
    """

    stderr = sys.stderr
    try:
        sys.stderr = DevNull()
        import hashlib  # noqa: E402
    except (RuntimeError, ValueError):
        pass
    sys.stderr = stderr
    hashlib.md5 = md5


def is_fips_mode():
    """
    Verify that ssl.FIPS_mode() returns 1 and that using md5 raises an
    exception
    """
    logger = logging.getLogger("{}.{}".format(__name__, id(is_fips_mode)))
    logger.addHandler(logging.NullHandler())
    openssl_version = get_openssl_version()
    if openssl_version.startswith("1"):
        import ssl
        if not hasattr(ssl, 'FIPS_mode'):
            return False
        elif ssl.FIPS_mode() != 1:
            return False

    # check if hashlib has been disabled
    import hashlib
    try:
        digest = hashlib.md5(b"Hello World\n").hexdigest()  # noqa: F841
        return False
    except ValueError:
        # Expect to get this exception so do nothing
        pass
    return True


def enable_fips_mode(fips_libcrypto_path=None):
    """
    Enable FIPS mode by overriding libcrypto and patching hashlib
    """
    openssl_version = get_openssl_version()
    logger = logging.getLogger("{}.{}".format(__name__, id(enable_fips_mode)))
    logger.addHandler(logging.NullHandler())

    if not fips_libcrypto_path:
        if 'FIPS_LIBCRYPTO_PATH' in os.environ:
            fips_libcrypto_path = os.environ['FIPS_LIBCRYPTO_PATH']
        elif 'OCI_PYTHON_SDK_FIPS_LIBCRYPTO_PATH' in os.environ:
            fips_libcrypto_path = os.environ['OCI_PYTHON_SDK_FIPS_LIBCRYPTO_PATH']

    if fips_libcrypto_path:
        if openssl_version.startswith("1"):
            override_libcrypto(fips_libcrypto_path)

            import hashlib
            try:
                digest = hashlib.md5(b"Hello World\n").hexdigest()  # noqa: F841

                # If the previous line did not raise an exception md5 needs to be
                # patched
                patch_hashlib_md5()

            except ValueError:
                # Expect to get this exception so do nothing
                pass

            logger.info("Using '{}' for libcypto".format(fips_libcrypto_path))
            if is_fips_mode():
                logger.info("FIPS mode is active")
            else:
                logger.error("Failed to enter FIPS mode")
        else:
            # Warn users who set the FIPS crypto path via environment variable that FIPS mode needs to be set in OS
            logger.warning("FIPS mode for openssl version {} needs to be set in your operating system!".format(openssl_version))