%PDF- %PDF-
Direktori : /opt/imunify360/venv/lib64/python3.8/site-packages/defence360agent/subsys/panels/generic/ |
Current File : //opt/imunify360/venv/lib64/python3.8/site-packages/defence360agent/subsys/panels/generic/panel.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 functools import json import logging import os from collections import defaultdict from typing import Dict, List, Sequence, Set import cerberus import yaml from defence360agent.api.integration_conf import ( ClIntegrationConfig, IntegrationConfig, ) from defence360agent.api.jwt_issuer import JWTIssuer from defence360agent.application.determine_hosting_panel import ( is_generic_panel_installed, ) from defence360agent.rpc_tools.lookup import UserType from defence360agent.utils import ( CheckRunError, check_run, get_non_system_users, ) from .. import base logger = logging.getLogger(__name__) _SCHEMA_PATH_TMPL = os.path.dirname(__file__)\ + '/users_script_schemas/schema-{}.yaml' ADMIN_LIST_FILE_PATH = '/etc/sysconfig/imunify360/auth.admin' METADATA = 'metadata' @functools.lru_cache(maxsize=2) def _get_validator(script: str) -> cerberus.Validator: """Returns a validator for given script.""" with open(_SCHEMA_PATH_TMPL.format(script)) as schema_file: schema = yaml.safe_load(schema_file) if script is not METADATA: schema[METADATA] = {'required': True} return cerberus.Validator(schema) def get_users_default_impl(): return [dict(name=pw.pw_name) for pw in get_non_system_users()] def _get_conf_path(cl, script): d = cl.to_dict() if 'integration_scripts' in d and script in d['integration_scripts']: return d['integration_scripts'][script] return None async def get_integration_data(script: str): path = _get_conf_path(IntegrationConfig(), script) if not path: path = _get_conf_path(ClIntegrationConfig(), script) if not path: raise IntegrationScriptError( '%s not found neither in ' '/etc/sysconfig/imunify360/integration.conf ' 'nor in /opt/cpvendor/etc/integration.ini.' % script) return await _get_integration_data(script, path) async def _get_integration_data(script: str, path: str): try: stdout = await check_run(path, shell=True) except CheckRunError as e: raise IntegrationScriptError("Integrations script {script} " "failed with exit code {e.returncode} \n" "{e.stderr}".format(script=script, e=e)) try: data = json.loads(stdout.decode()) except (UnicodeDecodeError, json.JSONDecodeError) as e: raise IntegrationScriptError('Cannot decode output of %s as JSON' % path) from e if not isinstance(data, dict): raise IntegrationScriptError('%s should return dict' % path) metadata_validator = _get_validator(METADATA) if not metadata_validator.validate(data): raise IntegrationScriptError('Validation error in metadata ' 'of %s script: %s' % (script, metadata_validator.errors)) if data[METADATA]['result'] != 'ok': metadata_error = data[METADATA]['result'] if 'message' in data[METADATA]: metadata_error += ': %s' % data[METADATA]['message'] raise IntegrationScriptError(metadata_error) validator = _get_validator(script) if not validator.validate(data): raise IntegrationScriptError('Validation error in %s script: %s' % (script, validator.errors)) return data['data'] async def _get_client_data(): try: users = await get_integration_data('users') domains = await get_integration_data('domains') users = {u['username']: [] for u in users} for k, v in domains.items(): user_domains = users.setdefault(v['owner'], []) user_domains.append(k) return [{'name': k, 'domains': v} for k, v in users.items()] except IntegrationScriptError: logger.warning( 'Applying default implementation of users and domains lists') return get_users_default_impl() async def get_domain_data(): try: return await get_integration_data('domains') except IntegrationScriptError: logger.warning('Could not parse domains lists') return {} async def get_admin_list() -> List[str]: script_name = 'admins' admins_set = {'root'} admins_from_integration_scripts = await asyncio.gather( _get_integration_data(script_name, _get_conf_path( IntegrationConfig(), script_name, )), _get_integration_data(script_name, _get_conf_path( ClIntegrationConfig(), script_name, )), return_exceptions=True, ) custom_admins = { admin["name"] for admins in admins_from_integration_scripts if isinstance(admins, list) # skip exceptions for admin in admins } if not custom_admins: logger.warning( "Error occurred during extracting admins " "from integration configs: %s", admins_from_integration_scripts) admins_set |= custom_admins try: with open(ADMIN_LIST_FILE_PATH) as admin_list_file: admins_set.update(admin_list_file.read().splitlines()) except OSError: logger.warning( 'Failed to retrieve admins list from %s', ADMIN_LIST_FILE_PATH) return list(admins_set) class IntegrationScriptError(base.PanelException): def __init__(self, *args, **kwargs): super().__init__(*args) logger.warning(self) class GenericPanel(base.AbstractPanel): """ Panel, UI to which is provided by imunify{-antivirus,360-firewall}-generic.{rpm,deb} """ NAME = 'generic panel' exception = IntegrationScriptError @classmethod def is_installed(cls): return is_generic_panel_installed() # pragma: no cover async def enable_imunify360_plugin(self, name=None): pass async def disable_imunify360_plugin(self, plugin_name=None): pass @classmethod async def version(cls): try: info = await get_integration_data('panel_info') return '{name} {version}'.format(**info) except IntegrationScriptError: return '0' async def get_user_domains(self): users = await _get_client_data() result = [] for user in users: result.extend(user.get('domains', tuple())) return result async def get_users(self) -> List[str]: users = await _get_client_data() return [user['name'] for user in users] async def get_domain_to_owner(self) -> Dict[str, List[str]]: users = await _get_client_data() result = defaultdict(list) for user in users: for domain in user.get('domains', []): result[domain].append(user['name']) return result async def get_domains_per_user(self) -> Dict[str, List[str]]: users = await _get_client_data() return { user['name']: user.get('domains', []) for user in users } def authenticate(self, protocol, data: dict): if protocol._uid != 0 and data['command'] != ['login', 'pam']: token = data['params'].pop('jwt', None) parsed_token = JWTIssuer.parse_token(token) return parsed_token['user_type'], ( parsed_token['user_name'] if parsed_token['user_type'] == UserType.NON_ROOT else None) else: return protocol.user, None def basedirs(self) -> Set[str]: conf = IntegrationConfig().to_dict() if 'malware' in conf and 'basedir' in conf['malware']: return set(conf['malware']['basedir'].split()) return set() async def list_docroots(self) -> Dict[str, str]: domains = await get_domain_data() return {v["document_root"]: domain for domain, v in domains.items()}