Your IP : 13.59.89.140


Current Path : /proc/thread-self/root/opt/imunify360/venv/lib/python3.11/site-packages/imav/patchman/
Upload File :
Current File : //proc/thread-self/root/opt/imunify360/venv/lib/python3.11/site-packages/imav/patchman/license.py

"""
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License,
or (at your option) any later version.


This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
See the GNU General Public License for more details.


You should have received a copy of the GNU General Public License
 along with this program.  If not, see <https://www.gnu.org/licenses/>.

Copyright © 2019 Cloud Linux Software Inc.

This software is also available under ImunifyAV commercial license,
see <https://www.imunify360.com/legal/eula>
"""
import asyncio
import logging
import ssl
import urllib
from contextlib import suppress
from enum import IntEnum
from pathlib import Path
from typing import List, Optional

from defence360agent.utils import importer

# use lazy import to reduce memory consumption when not in use
x509 = importer.LazyImport("cryptography.x509")
asn1_decoder = importer.LazyImport("pyasn1.codec.der.decoder")

logger = logging.getLogger(__name__)

CA_ROOT_FILE = "/etc/patchman/ca.crt"
LICENSE_CERT_FILE = "/etc/patchman/license/patchman.crt"
LICENSE_KEY_FILE = "/etc/patchman/license/patchman.key"
IMUNIFY_KEY_URL = "https://licensing.patchman.co/v2/imunifyav_key"
FEATURES_EXTENSION_OID = "1.3.6.1.4.1.44098.2.1.3"


class Feature(IntEnum):
    """
    Patchman functionality depending on the license.
    See patchman agent implementation for more details.
    """

    Core = 0
    CoreMalwareQuarantine = 1
    CoreVulnerabilitiesPatch = 2
    RulesMalwareScan = 3
    RulesMalwareClean = 4
    EcommerceVulnerabilitiesScan = 5
    EcommerceVulnerabilitiesPatch = 6
    PluginVulnerabilitiesScan = 7
    PluginVulnerabilitiesPatch = 8
    ScanningMultithreaded = 13
    ScanningRealtime = 14
    PatchDependencies = 16
    Waf = 17


class PatchmanLicenseError(Exception):
    pass


class License:
    @classmethod
    def is_active(cls):
        return Path(LICENSE_CERT_FILE).exists()

    @classmethod
    def get_features(cls) -> List[Feature]:
        """
        Return the list of features available for the current patchman license
        """
        features = []
        if cls.is_active():
            with open(LICENSE_CERT_FILE, "rb") as f:
                cert = x509.load_pem_x509_certificate(f.read())
            if ext := next(
                (
                    ext
                    for ext in cert.extensions
                    if ext.oid.dotted_string == FEATURES_EXTENSION_OID
                ),
                None,
            ):
                asn1_data, _ = asn1_decoder.decode(ext.value.value)
                # functional settings are specified as a bitstring
                bitstring = asn1_data.asBinary()
                # FeatureCore always present
                features.append(Feature.Core)
                for pos, value in enumerate(bitstring, start=1):
                    if value == "1":
                        # ignore unknown or deprecated features
                        with suppress(ValueError):
                            features.append(Feature(pos))
        logger.info("Patchman license features: %s", features)
        return features

    @classmethod
    def has_clean_mode(cls) -> bool:
        return Feature.RulesMalwareClean in cls.get_features()

    @classmethod
    def has_realtime_enabled(cls):
        return Feature.ScanningRealtime in cls.get_features()

    @classmethod
    def _get_imunify_key(cls) -> Optional[str]:
        """
        Get imunify registration key from the Patchman license service.
        See LICENSING-84 for details.
        """
        context = ssl.create_default_context()
        context.load_verify_locations(cafile=CA_ROOT_FILE)
        context.load_cert_chain(
            certfile=LICENSE_CERT_FILE,
            keyfile=LICENSE_KEY_FILE,
        )
        with urllib.request.urlopen(
            IMUNIFY_KEY_URL, context=context
        ) as response:
            status = response.status
            if response.status == 200:  # key found
                return response.read().decode()
            elif status == 404:  # key was not found
                return None
            raise PatchmanLicenseError(
                f"Patchman license service returns {response.status} response"
            )

    @classmethod
    async def get_imunify_key(cls) -> Optional[str]:
        loop = asyncio.get_event_loop()
        try:
            return await asyncio.wait_for(
                loop.run_in_executor(None, cls._get_imunify_key),
                timeout=300,
            )
        except Exception as exc:
            logger.error("Can't get imunify key due to %s", exc)
        return None

?>