MOON
Server: Apache
System: Linux s5.yayogua.com.py 6.12.0-124.55.3.el10_1.x86_64 #1 SMP PREEMPT_DYNAMIC Thu May 7 16:54:02 EDT 2026 x86_64
User: sanbercountryclu (1012)
PHP: 8.2.32
Disabled: NONE
Upload Files
File: //usr/share/crypto-policies/python/policygenerators/nss.py
# SPDX-License-Identifier: LGPL-2.1-or-later

# Copyright (c) 2019 Red Hat, Inc.
# Copyright (c) 2019 Tomáš Mráz <tmraz@fedoraproject.org>

import collections
import ctypes
import ctypes.util
import os
import subprocess
from tempfile import mkstemp

from .configgenerator import ConfigGenerator

NSS_P11_KIT_PROXY = '''
library=p11-kit-proxy.so
name=p11-kit-proxy
'''

# policy mapping as of 3.101 (lib/pk11wrap/pk11pars.c):
#
# ssl: NSS_USE_ALG_IN_SSL
# ssl-key-exchange: NSS_USE_ALG_IN_SSL_KX
# key-exchange: NSS_USE_ALG_IN_KEY_EXCHANGE
# cert-signature: NSS_USE_ALG_IN_CERT_SIGNATURE
# smime-signature, cms-signature: NSS_USE_ALG_IN_SMIME_SIGNATURE
# all-signature: NSS_USE_ALG_IN_SIGNATURE (cert-, smime-, signature)
# pkcs12: NSS_USE_ALG_IN_PKCS12  # (-legacy | encrypt)
# pkcs12-legacy: NSS_USE_ALG_IN_PKCS12_DECRYPT  # for use in allow
# pkcs12-encrypt: NSS_USE_ALG_IN_PKCS12_ENCRYPT  # for use in disallow
# smime: NSS_USE_ALG_IN_SMIME  # (-legacy | -encrypt)
# smime-legacy: NSS_USE_ALG_IN_SMIME_LEGACY  # for use in allow
# smime-encrypt: NSS_USE_ALG_IN_SMIME_ENCRYPT  # for use in disallow
# smime-key-exchange: NSS_USE_ALG_IN_SMIME_KX  # (-legacy | -encrypt})
# smime-key-exchange-legacy: NSS_USE_ALG_IN_SMIME_KX_LEGACY  # for use in allow
# smime-key-exchange-encrypt: NSS_USE_ALG_IN_SMIME_KX_ENCRYPT  # for disallow
# signature: NSS_USE_ALG_IN_ANY_SIGNATURE  # same as all-signature in disallow
#                                          # is needed for smime and cert
# legacy: NSS_USE_ALG_IN_PKCS12_DECRYPT
#       | NSS_USE_ALG_IN_SMIME_LEGACY
#       | NSS_USE_ALG_IN_SMIME_KX_LEGACY
# all: NSS_USE_ALG_IN_SSL        # (ssl)
#    | NSS_USE_ALG_IN_SSL_KX     # (ssl-key-exchange)
#    | NSS_USE_ALG_IN_PKCS12     # (pkcs12)
#    | NSS_USE_ALG_IN_SMIME      # (smime)
#    | NSS_USE_ALG_IN_SIGNATURE  # (signature)
#    | NSS_USE_ALG_IN_SMIME_KX   # (smime-key-exchange)
# none: 0


class PurposeDeduplicator:
    """Shorten the list of enabled algorithm/purpose pairs.

    For example, given a rule set of
    * ('pkcs12', 'pkcs12-legacy') -> 'pkcs12'
    * ('ssl', 'pkcs12') -> 'all'
    it'll shorten
      ('hmac-md5', 'pkcs12'), ('hmac-md5', 'pkcs12-legacy'), 'hmac-md5', 'ssl')
    to just 'hmac-md5'.
    The order of the first insertion is preserved.

    It was decided to be OK to merge all currently known purposes into '/all',
    even though in the future there could be more purposes added, since:
    1. the list of purposes doesn't change often, and then we should update c-p
    2. this will only result in overenablement for algorithms
       that the administrator already trusts for all currently known purposes,
       and it's likely that they trust it for the new purpose as well
    """

    def __init__(self, rulemap):
        self._alg_purpose_map = collections.defaultdict(list)
        self._rulemap = rulemap

    def add(self, alg, purpose):
        self._alg_purpose_map[alg].append(purpose)

    @staticmethod
    def _deduplicate_single_rule(purpose_list,
                                 purposes_separate, purpose_combined):
        new_purpose_list = []
        for purpose in purpose_list:
            if 'all' in new_purpose_list:
                continue
            if purpose in new_purpose_list:
                continue
            match = (purpose in purposes_separate
                     and all(p in purpose_list for p in purposes_separate))
            if match:
                if purpose_combined in new_purpose_list:
                    continue
                new_purpose_list.append(purpose_combined)
            else:
                new_purpose_list.append(purpose)
        return new_purpose_list

    def _deduplicate_purpose_list(self, purpose_list):
        prev_purpose_list = None
        while purpose_list != prev_purpose_list:
            for purposes_separate, purpose_combined in self._rulemap.items():
                purpose_list = self._deduplicate_single_rule(purpose_list,
                                                             purposes_separate,
                                                             purpose_combined)
            prev_purpose_list = purpose_list
        return purpose_list

    def deduplicated(self):
        deduplicated_alg_purpose_map = {
            alg: self._deduplicate_purpose_list(purpose_list)
            for alg, purpose_list in self._alg_purpose_map.items()
        }
        return (f'{alg}/{",".join(purposes)}' if purposes != ['all'] else alg
                for alg, purposes in deduplicated_alg_purpose_map.items())


class NSSGenerator(ConfigGenerator):
    CONFIG_NAME = 'nss'

    mac_map = {
        'HMAC-SHA1': 'HMAC-SHA1',
        'HMAC-MD5': 'HMAC-MD5',
        'HMAC-SHA2-224': 'HMAC-SHA224',
        'HMAC-SHA2-256': 'HMAC-SHA256',
        'HMAC-SHA2-384': 'HMAC-SHA384',
        'HMAC-SHA2-512': 'HMAC-SHA512',
        'HMAC-SHA3-224': 'HMAC-SHA3-224',
        'HMAC-SHA3-256': 'HMAC-SHA3-256',
        'HMAC-SHA3-384': 'HMAC-SHA3-384',
        'HMAC-SHA3-512': 'HMAC-SHA3-512',
    }

    hash_map = {
        'MD2': 'MD2',
        'MD4': 'MD4',
        'MD5': 'MD5',
        'SHA1': 'SHA1',
        'SHA2-224': 'SHA224',
        'SHA2-256': 'SHA256',
        'SHA2-384': 'SHA384',
        'SHA2-512': 'SHA512',
        'SHA3-224': 'SHA3-224',
        'SHA3-256': 'SHA3-256',
        'SHA3-384': 'SHA3-384',
        'SHA3-512': 'SHA3-512',
        'SHAKE-128': None,  # not present as of 3.101
        'SHAKE-256': None,  # not present as of 3.101
    }

    curve_map = {
        # PRIME*, SECP<256, SECP*K1, C2TNB*, SECT*: skip
        'X25519': 'CURVE25519',
        'X448': None,  # not present as of 3.101
        'SECP256R1': 'SECP256R1',
        'SECP384R1': 'SECP384R1',
        'SECP521R1': 'SECP521R1',
        'MLKEM768-X25519': 'mlkem768x25519',
        'P256-MLKEM768': 'secp256r1mlkem768',
        'P384-MLKEM1024': 'secp384r1mlkem1024',
    }

    cipher_map = {
        'AES-256-GCM': ('aes256-gcm',),
        'AES-192-GCM': ('aes192-gcm',),
        'AES-128-GCM': ('aes128-gcm',),
        'AES-256-CBC': ('aes256-cbc',),
        'AES-192-CBC': ('aes192-cbc',),
        'AES-128-CBC': ('aes128-cbc',),
        'CAMELLIA-256-CBC': ('camellia256-cbc',),
        'CAMELLIA-192-CBC': ('camellia192-cbc',),
        'CAMELLIA-128-CBC': ('camellia128-cbc',),
        'CHACHA20-POLY1305': ('chacha20-poly1305',),
        'SEED-CBC': ('seed-cbc',),
        '3DES-CBC': ('des-ede3-cbc',),
        'DES40-CBC': ('des-40-cbc',),
        'DES-CBC': ('des-cbc',),
        'RC4-128': ('rc4',),
        'RC2-CBC': ('rc2', 'rc2-40-cbc', 'rc2-64-cbc', 'rc2-128-cbc'),
        'IDEA': ('idea',),
        'NULL': (),  # hope nobody ever needs it
    }

    key_exchange_ssl_map = {
        'RSA': ('RSA',),
        'DHE-RSA': ('DHE-RSA',),
        'DHE-DSS': ('DHE-DSS',),
        'ECDHE': ('ECDHE-RSA', 'ECDHE-ECDSA'),
        'ECDH': ('ECDH-RSA', 'ECDH-ECDSA'),
        'DH': ('DH-RSA', 'DH-DSS'),
    }
    key_exchange_smime_map = {
        'RSA': ('RSA-PKCS', 'RSA-OAEP'),
        'ECDH': ('ECDH',),
        'DH': ('DH',),
    }

    protocol_map = {
        'SSL3.0': 'ssl3.0',
        'TLS1.0': 'tls1.0',
        'TLS1.1': 'tls1.1',
        'TLS1.2': 'tls1.2',
        'TLS1.3': 'tls1.3',
        'DTLS1.0': 'dtls1.0',
        'DTLS1.2': 'dtls1.2'
    }

    # Depends on a dict being ordered
    sign_prefix_ordmap = {
        'RSA-PSS-': 'RSA-PSS',  # must come before RSA-
        'RSA-': 'RSA-PKCS',
        'ECDSA-': 'ECDSA',
        'DSA-': 'DSA',
        'EDDSA-ED25519': 'ED25519',
        'MLDSA44': 'ML-DSA-44',
        'MLDSA65': 'ML-DSA-65',
        'MLDSA87': 'ML-DSA-87',
    }
    sign_suffix_ordmap = {
        '-MD5': 'MD5',
        '-SHA1': 'SHA1',
        '-SHA2-224': 'SHA224',
        '-SHA2-256': 'SHA256',
        '-SHA2-384': 'SHA384',
        '-SHA2-512': 'SHA512',
        '-SHA3-224': 'SHA3-224',
        '-SHA3-256': 'SHA3-256',
        '-SHA3-384': 'SHA3-384',
        '-SHA3-512': 'SHA3-512',
    }

    @classmethod
    def generate_config(cls, unscoped_policy):
        ssl_policy = unscoped_policy.scoped({'tls', 'ssl', 'nss', 'nss-tls'})
        pkcs12_import_policy = unscoped_policy.scoped({
            'nss', 'pkcs12-import', 'nss-pkcs12-import', 'pkcs12', 'nss-pkcs12'
        })
        pkcs12_export_import_policy = unscoped_policy.scoped({
            'nss', 'pkcs12', 'nss-pkcs12'
        })
        smime_import_policy = unscoped_policy.scoped({
            'nss', 'smime-import', 'nss-smime-import', 'smime', 'nss-smime'
        })
        smime_export_import_policy = unscoped_policy.scoped({
            'nss', 'smime', 'nss-smime'
        })

        # including it unconditionally, because Fedora NSS depends on p11-kit
        cfg = NSS_P11_KIT_PROXY.lstrip() + '\n\n'

        cfg += 'library=\n'
        cfg += 'name=Policy\n'
        cfg += 'NSS=flags=policyOnly,moduleDB\n'
        cfg += 'config="disallow=ALL allow='

        r = []
        macs_and_purposes = PurposeDeduplicator({
            # possible uses as of 3.101: ssl, pkcs12{,-legacy}
            ('pkcs12', 'pkcs12-legacy'): 'pkcs12',
            ('ssl', 'pkcs12'): 'all',
        })
        for i in ssl_policy.enabled['mac']:
            if (mac_alg := cls.mac_map.get(i)) is not None:
                macs_and_purposes.add(mac_alg, 'ssl')
        for i in pkcs12_export_import_policy.enabled['mac']:
            if (mac_alg := cls.mac_map.get(i)) is not None:
                macs_and_purposes.add(mac_alg, 'pkcs12')
        for i in pkcs12_import_policy.enabled['mac']:
            if (mac_alg := cls.mac_map.get(i)) is not None:
                macs_and_purposes.add(mac_alg, 'pkcs12-legacy')
        r.extend(macs_and_purposes.deduplicated())

        groups_and_purposes = PurposeDeduplicator({
            # possible uses as of 3.101: ssl-key-exchange, cert-signature
            # and cert-signature requires signature
            ('ssl-key-exchange', 'cert-signature', 'signature'): 'all',
        })
        for i in ssl_policy.enabled['group']:
            if (group_alg := cls.curve_map.get(i)) is not None:
                groups_and_purposes.add(group_alg, 'ssl-key-exchange')
                groups_and_purposes.add(group_alg, 'cert-signature')
                groups_and_purposes.add(group_alg, 'signature')
        r.extend(groups_and_purposes.deduplicated())

        ciphers_and_purposes = PurposeDeduplicator({
            # possible uses as of 3.101: ssl, pkcs12{,-legacy}, smime{,-legacy}
            ('pkcs12', 'pkcs12-legacy'): 'pkcs12',
            ('smime', 'smime-legacy'): 'smime',
            ('ssl', 'pkcs12', 'smime'): 'all',
        })
        for i in ssl_policy.enabled['cipher']:
            for cipher_alg in cls.cipher_map.get(i, ()):
                ciphers_and_purposes.add(cipher_alg, 'ssl')
        for i in pkcs12_export_import_policy.enabled['cipher']:
            for cipher_alg in cls.cipher_map.get(i, ()):
                ciphers_and_purposes.add(cipher_alg, 'pkcs12')
        for i in pkcs12_import_policy.enabled['cipher']:
            for cipher_alg in cls.cipher_map.get(i, ()):
                ciphers_and_purposes.add(cipher_alg, 'pkcs12-legacy')
        for i in smime_export_import_policy.enabled['cipher']:
            for cipher_alg in cls.cipher_map.get(i, ()):
                ciphers_and_purposes.add(cipher_alg, 'smime')
        for i in smime_import_policy.enabled['cipher']:
            for cipher_alg in cls.cipher_map.get(i, ()):
                ciphers_and_purposes.add(cipher_alg, 'smime-legacy')
        r.extend(ciphers_and_purposes.deduplicated())

        hashes_and_purposes = PurposeDeduplicator({
            # possible uses as of 3.101:
            # ssl-key-exchange, {cert-,smime-,}signature, pkcs12{,-legacy}
            # either of cert-signature and smime-signature requires signature
            ('pkcs12', 'pkcs12-legacy'): 'pkcs12',
            ('cert-signature', 'smime-signature', 'signature'):
                'all-signature',
            ('ssl-key-exchange', 'all-signature', 'pkcs12'): 'all',
        })
        for i in ssl_policy.enabled['hash']:
            if (hash_alg := cls.hash_map.get(i)) is not None:
                hashes_and_purposes.add(hash_alg, 'ssl-key-exchange')
        for i in pkcs12_export_import_policy.enabled['hash']:
            if (hash_alg := cls.hash_map.get(i)) is not None:
                hashes_and_purposes.add(hash_alg, 'pkcs12')
        for i in pkcs12_import_policy.enabled['hash']:
            if (hash_alg := cls.hash_map.get(i)) is not None:
                hashes_and_purposes.add(hash_alg, 'pkcs12-legacy')
        # but for signature purposes, we'd better look at `sign`
        for i in ssl_policy.enabled['sign']:
            for suffix, sighashalg in cls.sign_suffix_ordmap.items():
                if i.endswith(suffix):
                    hashes_and_purposes.add(sighashalg, 'cert-signature')
                    hashes_and_purposes.add(sighashalg, 'signature')
        for i in smime_export_import_policy.enabled['sign']:
            for suffix, sighashalg in cls.sign_suffix_ordmap.items():
                if i.endswith(suffix):
                    hashes_and_purposes.add(sighashalg, 'smime-signature')
        for i in smime_import_policy.enabled['key_exchange']:
            for suffix, sighashalg in cls.sign_suffix_ordmap.items():
                if i.endswith(suffix):
                    hashes_and_purposes.add(sighashalg,
                                            'smime-signature-legacy')
        r.extend(hashes_and_purposes.deduplicated())

        kex_and_purposes = PurposeDeduplicator({
            # possible uses as of 3.101:
            # ssl-key-exchange, smime-key-exchange{,-legacy}
            ('smime-key-exchange', 'smime-key-exchange-legacy'):
                'smime-key-exchange',
            ('ssl-key-exchange', 'smime-key-exchange'): 'all',
        })
        for i in ssl_policy.enabled['key_exchange']:
            for kex_alg in cls.key_exchange_ssl_map.get(i, ()):
                kex_and_purposes.add(kex_alg, 'ssl-key-exchange')
        for i in smime_import_policy.enabled['key_exchange']:
            for kex_alg in cls.key_exchange_smime_map.get(i, ()):
                kex_and_purposes.add(kex_alg, 'smime-key-exchange-legacy')
        for i in smime_export_import_policy.enabled['key_exchange']:
            for kex_alg in cls.key_exchange_smime_map.get(i, ()):
                kex_and_purposes.add(kex_alg, 'smime-key-exchange')
        r.extend(kex_and_purposes.deduplicated())

        sigalgs_and_purposes = PurposeDeduplicator({
            # possible uses as of 3.101:
            # ssl-key-exchange, {cert-,smime-,}signature
            ('cert-signature', 'smime-signature', 'signature'):
                'all-signature',
            ('ssl-key-exchange', 'all-signature'): 'all',
        })
        for i in ssl_policy.enabled['sign']:
            for prefix, sigalg in cls.sign_prefix_ordmap.items():
                if i.startswith(prefix):
                    sigalgs_and_purposes.add(sigalg, 'ssl-key-exchange')
                    sigalgs_and_purposes.add(sigalg, 'cert-signature')
                    sigalgs_and_purposes.add(sigalg, 'signature')
        for i in smime_export_import_policy.enabled['sign']:
            for prefix, sigalg in cls.sign_prefix_ordmap.items():
                if i.startswith(prefix):
                    sigalgs_and_purposes.add(sigalg, 'smime-signature')
                    sigalgs_and_purposes.add(sigalg, 'signature')
        r.extend(sigalgs_and_purposes.deduplicated())

        # Fedora ELN has the default flipped because it doesn't carry the patch
        no_tls_require_ems = os.getenv('NSS_NO_TLS_REQUIRE_EMS', '0') == '1'
        if ssl_policy.enums['__ems'] == 'ENFORCE' and not no_tls_require_ems:
            r.append('TLS-REQUIRE-EMS')

        if ssl_policy.min_tls_version:
            minver = cls.protocol_map[ssl_policy.min_tls_version]
            r.append('tls-version-min=' + minver)
        else:  # FIXME, preserving behaviour, but this is wrong
            r.append('tls-version-min=0')

        if ssl_policy.min_dtls_version:
            minver = cls.protocol_map[ssl_policy.min_dtls_version]
            r.append('dtls-version-min=' + minver)
        else:  # FIXME, preserving behaviour, but this is wrong
            r.append('dtls-version-min=0')

        r.append(f'DH-MIN={ssl_policy.integers["min_dh_size"]}')
        r.append(f'DSA-MIN={ssl_policy.integers["min_dsa_size"]}')
        r.append(f'RSA-MIN={ssl_policy.integers["min_rsa_size"]}')

        cfg += ':'.join(r) + '"\n'

        return cfg

    @classmethod
    def test_config(cls, config):
        nss_path = ctypes.util.find_library('nss3')
        nss_lib = ctypes.CDLL(nss_path)

        nss_lax = os.getenv('NSS_LAX', '0') == '1'
        nss_is_lax_by_default = True
        try:
            if not nss_lib.NSS_VersionCheck(b'3.80'):
                # NSS older than 3.80 uses strict config checking.
                # 3.80 and newer ignores new keywords by default
                # and needs extra switches to be strict.
                nss_is_lax_by_default = False
        except AttributeError:
            cls.eprint('Cannot determine nss version with ctypes, '
                       'assuming >=3.80')
        options = (['-f', 'value', '-f', 'identifier']
                   if nss_is_lax_by_default and not nss_lax else [])

        fd, path = mkstemp()

        try:
            with os.fdopen(fd, 'w') as f:
                f.write(config)
            p = subprocess.run(['nss-policy-check',  # noqa: S607
                                *options, path],
                               check=False,
                               encoding='utf-8',
                               stdout=subprocess.PIPE,
                               stderr=subprocess.STDOUT)
        finally:
            os.unlink(path)

        if p.returncode == 1:  # NSS-POLICY-WARN
            # FUTURE policy currently defines no classic KEX, this is intended
            lines = p.stdout.split('\n')
            for line in ('NSS-POLICY-WARN: NUMBER-OF-ECC: 0',
                         'NSS-POLICY-WARN: NUMBER-OF-SMIME-KX: 0',
                         'NSS-POLICY-WARN'):
                if line in lines:
                    lines.remove(line)
            rest = '\n'.join(lines)
            if 'NSS-POLICY-FAIL' not in rest and 'NSS-POLICY-WARN' not in rest:
                # OK, that's an acceptable warning
                return True
            # otherwise that's some other warning
            cls.eprint("/usr/bin/nss-policy-check: Execution failed")
            cls.eprint("There is a warning in NSS generated policy")
            cls.eprint(f'Policy:\n{config}')
            return False
        if p.returncode:  # NSS-POLICY-FAIL and catchall for unexpected non-0
            cls.eprint("/usr/bin/nss-policy-check: Execution failed")
            cls.eprint("There is an error in NSS generated policy")
            cls.eprint(f'Policy:\n{config}')
            return False
        return True