#! /usr/bin/env python3 # # GPOA - GPO Applier for Linux # # Copyright (C) 2019-2020 BaseALT Ltd. # # 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 . import os import sys import argparse import subprocess from util.util import ( runcmd , get_backends , get_default_policy_name , get_policy_entries , get_policy_variants ) from util.config import GPConfig from util.paths import get_custom_policy_dir class Runner: __control_path = '/usr/sbin/control' __systemctl_path = '/bin/systemctl' def __init__(self): self.arguments = parse_arguments() def parse_arguments(): ''' Parse CLI arguments. ''' parser = argparse.ArgumentParser(prog='gpupdate-setup') subparsers = parser.add_subparsers(dest='action', metavar='action', help='Group Policy management actions (default action is status)') parser_list = subparsers.add_parser('list', help='List avalable types of local policy') parser_list = subparsers.add_parser('list-backends', help='Show list of available backends') parser_status = subparsers.add_parser('status', help='Show current Group Policy status') parser_enable = subparsers.add_parser('enable', help='Enable Group Policy subsystem') parser_disable = subparsers.add_parser('disable', help='Disable Group Policy subsystem') parser_update = subparsers.add_parser('update', help='Update state') parser_write = subparsers.add_parser('write', help='Operate on Group Policies (enable or disable)') parser_set_backend = subparsers.add_parser('set-backend', help='Set or change currently active backend') parser_default = subparsers.add_parser('default-policy', help='Show name of default policy') parser_active = subparsers.add_parser('active-policy', help='Show name of policy enabled') parser_active_backend = subparsers.add_parser('active-backend', help='Show currently configured backend') parser_set_backend.add_argument('backend', default='samba', type=str, nargs='?', const='backend', choices=['local', 'samba'], help='Backend (source of settings) name') parser_write.add_argument('status', choices=['enable', 'disable'], help='Enable or disable Group Policies') parser_write.add_argument('localpolicy', default=None, nargs='?', help='Name of local policy to enable') parser_write.add_argument('backend', default='samba', type=str, nargs='?', const='backend', choices=['local', 'samba'], help='Backend (source of settings) name') parser_enable.add_argument('--local-policy', default=None, help='Name of local policy to enable') parser_enable.add_argument('--backend', default='samba', type=str, choices=['local', 'samba'], help='Backend (source of settings) name') parser_update.add_argument('--local-policy', default=None, help='Name of local policy to enable') parser_update.add_argument('--backend', default='samba', type=str, choices=['local', 'samba'], help='Backend (source of settings) name') return parser.parse_args() def validate_policy_name(policy_name): return policy_name in [os.path.basename(d) for d in get_policy_variants()] def is_unit_enabled(unit_name, unit_global=False): ''' Check that designated systemd unit is enabled ''' command = ['/bin/systemctl', 'is-enabled', unit_name] if unit_global: command = ['/bin/systemctl', '--global', 'is-enabled', unit_name] value = runcmd(command) # If first line of stdout is equal to "enabled" and return code # is zero then unit is considered enabled. rc = value[0] result = [] try: result = value[1].replace('\n', '') except IndexError as exc: return False if result == 'enabled' and rc == 0: return True return False def get_status(): ''' Check that gpupdate.service and gpupdate-user.service are enabled. ''' is_gpupdate = is_unit_enabled('gpupdate.service') is_gpupdate_user = is_unit_enabled('gpupdate-user.service', unit_global=True) if is_gpupdate and is_gpupdate_user: return True return False def get_active_policy_name(): ''' Show the name of an active Local Policy template ''' config = GPConfig() return os.path.basename(config.get_local_policy_template()) def get_active_backend(): config = GPConfig() return config.get_backend() def rollback_on_error(command_name): ''' Disable group policy services in case command returns error code ''' if 0 != runcmd(command_name)[0]: disable_gp() return False return True def disable_gp(): ''' Consistently disable group policy services ''' cmd_set_global_policy = ['/usr/sbin/control', 'system-policy', 'remote'] cmd_set_local_policy = ['/usr/sbin/control', 'system-policy', 'local'] cmd_disable_gpupdate_service = ['/bin/systemctl', 'disable', 'gpupdate.service'] cmd_disable_gpupdate_user_service = ['/bin/systemctl', '--global', 'disable', 'gpupdate-user.service'] cmd_disable_gpupdate_timer = ['/bin/systemctl', 'disable', 'gpupdate.timer'] cmd_disable_gpupdate_user_timer = ['/bin/systemctl', '--global', 'disable', 'gpupdate-user.timer'] cmd_control_system_auth = ['/usr/sbin/control', 'system-auth'] cmd_disable_gpupdate_scripts_service = ['/bin/systemctl', 'disable', 'gpupdate-scripts-run.service'] cmd_disable_gpupdate_scripts_user_service = ['/bin/systemctl', '--global', 'disable', 'gpupdate-scripts-run-user.service'] config = GPConfig() auth_result = 'local' try: auth_result = runcmd(cmd_control_system_auth)[1][0] except Exception as exc: print(str(exc)) if auth_result != 'local': runcmd(cmd_set_global_policy) else: runcmd(cmd_set_local_policy) runcmd(cmd_disable_gpupdate_service) runcmd(cmd_disable_gpupdate_user_service) runcmd(cmd_disable_gpupdate_timer) runcmd(cmd_disable_gpupdate_user_timer) runcmd(cmd_disable_gpupdate_scripts_service) runcmd(cmd_disable_gpupdate_scripts_user_service) config.set_local_policy_template() config.set_backend() def enable_gp(policy_name, backend_type): ''' Consistently enable group policy services ''' cmd_set_gpupdate_policy = ['/usr/sbin/control', 'system-policy', 'gpupdate'] cmd_gpoa_nodomain = ['/usr/sbin/gpoa', '--nodomain', '--loglevel', '5'] cmd_enable_gpupdate_service = ['/bin/systemctl', 'enable', 'gpupdate.service'] cmd_enable_gpupdate_user_service = ['/bin/systemctl', '--global', 'enable', 'gpupdate-user.service'] cmd_enable_gpupdate_timer = ['/bin/systemctl', 'enable', 'gpupdate.timer'] cmd_enable_gpupdate_user_timer = ['/bin/systemctl', '--global', 'enable', 'gpupdate-user.timer'] cmd_enable_gpupdate_scripts_service = ['/bin/systemctl', 'enable', 'gpupdate-scripts-run.service'] cmd_enable_gpupdate_user_scripts_service = ['/bin/systemctl', '--global', 'enable', 'gpupdate-scripts-run-user.service'] config = GPConfig() custom_policy_dir = get_custom_policy_dir() if not os.path.isdir(custom_policy_dir): os.makedirs(custom_policy_dir) target_policy_name = get_default_policy_name() if policy_name: if validate_policy_name(policy_name): target_policy_name = policy_name print (target_policy_name) config.set_local_policy_template(target_policy_name) config.set_backend(backend_type) # Enable oddjobd_gpupdate in PAM config if not rollback_on_error(cmd_set_gpupdate_policy): return # Bootstrap the Group Policy engine if not rollback_on_error(cmd_gpoa_nodomain): return # Enable gpupdate.service if not rollback_on_error(cmd_enable_gpupdate_service): return if not is_unit_enabled('gpupdate.service'): disable_gp() return # Enable gpupdate-setup.service for all users if not rollback_on_error(cmd_enable_gpupdate_user_service): return if not is_unit_enabled('gpupdate-user.service', unit_global=True): disable_gp() return # Enable gpupdate-scripts-run.service if not rollback_on_error(cmd_enable_gpupdate_scripts_service): return if not is_unit_enabled('gpupdate-scripts-run.service'): disable_gp() return # Enable gpupdate-scripts-run-user.service for all users if not rollback_on_error(cmd_enable_gpupdate_user_scripts_service): return if not is_unit_enabled('gpupdate-scripts-run-user.service', unit_global=True): disable_gp() return # Enable gpupdate.timer if not rollback_on_error(cmd_enable_gpupdate_timer): return if not is_unit_enabled('gpupdate.timer'): disable_gp() return # Enable gpupdate-setup.timer for all users if not rollback_on_error(cmd_enable_gpupdate_user_timer): return if not is_unit_enabled('gpupdate-user.timer', unit_global=True): disable_gp() return def act_list(): ''' Show list of available templates of Local Policy ''' for entry in get_policy_variants(): print(entry.rpartition('/')[2]) def act_list_backends(): ''' List backends supported by GPOA ''' backends = get_backends() for backend in backends: print(backend) def act_status(): ''' Check that group policy services are enabled ''' if get_status(): print('enabled') else: print('disabled') def act_set_backend(backend_name): config = GPConfig() config.set_backend(backend_name) def act_write(status, localpolicy, backend): ''' Enable or disable group policy services ''' if status == 'enable' or status == '#t': enable_gp(localpolicy, backend) if status == 'disable' or status == '#f': disable_gp() def act_enable(localpolicy, backend): ''' Enable group policy services ''' enable_gp(localpolicy, backend) def act_active_policy(): ''' Print active Local Policy template name to stdout ''' print(get_active_policy_name()) def act_active_backend(): ''' Print currently configured backend. ''' print(get_active_backend()) def act_default_policy(): ''' Print default Local Policy template name to stdout ''' print(get_default_policy_name()) def main(): arguments = parse_arguments() action = dict() action['list'] = act_list action['list-backends'] = act_list_backends action['status'] = act_status action['set-backend'] = act_set_backend action['write'] = act_write action['enable'] = act_enable action['update'] = act_enable action['disable'] = disable_gp action['active-policy'] = act_active_policy action['active-backend'] = act_active_backend action['default-policy'] = act_default_policy if arguments.action == None: action['status']() elif arguments.action == 'update': if get_status(): action[arguments.action](arguments.local_policy, arguments.backend) elif arguments.action == 'enable': action[arguments.action](arguments.local_policy, arguments.backend) elif arguments.action == 'write': action[arguments.action](arguments.status, arguments.localpolicy, arguments.backend) elif arguments.action == 'set-backend': action[arguments.action](arguments.backend) else: action[arguments.action]() if __name__ == '__main__': main()