mirror of
https://github.com/altlinux/gpupdate.git
synced 2025-01-29 17:47:32 +03:00
Initial release
This commit is contained in:
commit
66f5b751b6
2
.gear/rules
Normal file
2
.gear/rules
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
tar: .
|
||||||
|
|
0
gpoa/__init__.py
Normal file
0
gpoa/__init__.py
Normal file
27
gpoa/control/__init__.py
Normal file
27
gpoa/control/__init__.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import subprocess
|
||||||
|
import threading
|
||||||
|
|
||||||
|
class control:
|
||||||
|
def __init__(self, name, value):
|
||||||
|
self.control_name = name
|
||||||
|
self.control_value = value
|
||||||
|
self.possible_values = self._query_control_values()
|
||||||
|
|
||||||
|
def _query_control_values(self):
|
||||||
|
proc = subprocess.Popen(['sudo', 'control', self.control_name, 'list'], stdout=subprocess.PIPE)
|
||||||
|
for line in proc.stdout:
|
||||||
|
values = line.split()
|
||||||
|
return values
|
||||||
|
|
||||||
|
def get_control_name(self):
|
||||||
|
return self.control_name
|
||||||
|
|
||||||
|
def get_control_status(self):
|
||||||
|
proc = subprocess.Popen(['sudo', 'control', self.control_name], stdout=subprocess.PIPE)
|
||||||
|
for line in proc.stdout:
|
||||||
|
return line.rstrip('\n\r')
|
||||||
|
|
||||||
|
def set_control_status(self):
|
||||||
|
print('Setting control {} to {}'.format(self.control_name, self.control_value))
|
||||||
|
#proc = subprocess.Popen(['sudo', 'control', self.control_name, status], stdout=subprocess.PIPE)
|
||||||
|
|
2
gpoa/generator/gen.py
Normal file
2
gpoa/generator/gen.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
if __name__ == "__main__":
|
||||||
|
print("main here!")
|
1
gpoa/generator/requirements.txt
Normal file
1
gpoa/generator/requirements.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Jinja2
|
339
gpoa/main.py
Executable file
339
gpoa/main.py
Executable file
@ -0,0 +1,339 @@
|
|||||||
|
#! /usr/bin/env python3
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
# Facility to determine GPTs for user
|
||||||
|
import optparse
|
||||||
|
from samba import getopt as options
|
||||||
|
from samba.gpclass import get_dc_hostname, gpo_version, check_safe_path
|
||||||
|
import samba.gpo
|
||||||
|
|
||||||
|
# Primitives to work with libregistry
|
||||||
|
# samba.registry.str_regtype(2) -> 'REG_EXPAND_SZ'
|
||||||
|
# Taken from python/samba/tests/registry.py
|
||||||
|
from samba import registry
|
||||||
|
|
||||||
|
# PReg object generator
|
||||||
|
from samba.dcerpc import preg
|
||||||
|
from samba.dcerpc import misc
|
||||||
|
import samba.ndr
|
||||||
|
|
||||||
|
from samba.gp_parse.gp_pol import GPPolParser
|
||||||
|
|
||||||
|
# This is needed by Registry.pol file search
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
# This is needed for merging lists of PReg files.
|
||||||
|
import itertools
|
||||||
|
|
||||||
|
# Our native control facility
|
||||||
|
import control
|
||||||
|
|
||||||
|
class applier_backend:
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class hreg_filesystem_backend(applier_backend):
|
||||||
|
_gpupdate_cache = '/var/cache/gpupdate'
|
||||||
|
_base_path = '/tmp/gpoa_scripts'
|
||||||
|
|
||||||
|
def __init__(self, sid):
|
||||||
|
self._sid = sid
|
||||||
|
self._root_path = '{base}/root'.format(base=self._base_path)
|
||||||
|
self._user_path = '{base}/user'.format(base=self._base_path)
|
||||||
|
|
||||||
|
def _get_files(self, dir_path):
|
||||||
|
'''
|
||||||
|
List all files located in hreg cache
|
||||||
|
'''
|
||||||
|
files = list()
|
||||||
|
for entry in os.listdir(dir_path):
|
||||||
|
abspath = os.path.join(dir_path, entry)
|
||||||
|
try:
|
||||||
|
if os.path.isdir(abspath):
|
||||||
|
files.append(self._get_files(abspath))
|
||||||
|
else:
|
||||||
|
files.append(abspath)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return files
|
||||||
|
|
||||||
|
def _read_value(self, path):
|
||||||
|
'''
|
||||||
|
Read hreg-cached value from file and return object representing it.
|
||||||
|
'''
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_values(self):
|
||||||
|
cpath = os.path.join(self._gpupdate_cache, self._sid)
|
||||||
|
|
||||||
|
entry_list = self._get_files(cpath)
|
||||||
|
for entry in entry_list:
|
||||||
|
val = self._read_value(entry)
|
||||||
|
|
||||||
|
|
||||||
|
class samba_backend(applier_backend):
|
||||||
|
_samba_registry_file = '/var/cache/samba/registry.ldb'
|
||||||
|
_mahine_hive = 'HKEY_LOCAL_MACHINE'
|
||||||
|
_user_hive = 'HKEY_CURRENT_USER'
|
||||||
|
_machine_pol_path_pattern = '[Mm][Aa][Cc][Hh][Ii][Nn][Ee]'
|
||||||
|
_user_pol_path_pattern = '[Uu][Ss][Ee][Rr]'
|
||||||
|
|
||||||
|
def _check_sysvol_present(self, gpo):
|
||||||
|
'''
|
||||||
|
Check if there is SYSVOL path for GPO assigned
|
||||||
|
'''
|
||||||
|
if not gpo.file_sys_path:
|
||||||
|
print('No SYSVOL entry assigned to GPO {}'.format(gpo.name))
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _gpo_get_gpt_polfiles(self, gpo_obj):
|
||||||
|
'''
|
||||||
|
Find absolute path to cached GPT directory and return
|
||||||
|
dict of lists with PReg file paths.
|
||||||
|
'''
|
||||||
|
if self._check_sysvol_present(gpo_obj):
|
||||||
|
print('Found SYSVOL entry {} for GPO {}'.format(gpo_obj.file_sys_path, gpo_obj.name))
|
||||||
|
path = check_safe_path(gpo_obj.file_sys_path).upper()
|
||||||
|
gpt_abspath = os.path.join(self.cache_dir, 'gpo_cache', path)
|
||||||
|
print('Path: {}'.format(path))
|
||||||
|
policy_files = self._find_regpol_files(gpt_abspath)
|
||||||
|
|
||||||
|
return policy_files
|
||||||
|
return dict({ 'machine_regpols': [], 'user_regpols': [] })
|
||||||
|
|
||||||
|
def __init__(self, loadparm, creds, sid, dc):
|
||||||
|
# Regular expressions to split PReg files into user and machine parts
|
||||||
|
self._machine_pol_path_regex = re.compile(self._machine_pol_path_pattern)
|
||||||
|
self._user_pol_path_regex = re.compile(self._user_pol_path_pattern)
|
||||||
|
|
||||||
|
# User SID to work with HKCU hive
|
||||||
|
self.sid = sid
|
||||||
|
|
||||||
|
# Look at python-samba tests for code examples
|
||||||
|
self.registry = registry.Registry()
|
||||||
|
self.machine_hive = registry.open_ldb('/tmp/machine_hive.ldb')
|
||||||
|
self.user_hive = registry.open_ldb('/tmp/HKCU-{}.ldb'.format(self.sid))
|
||||||
|
self.registry.mount_hive(self.machine_hive, samba.registry.HKEY_LOCAL_MACHINE)
|
||||||
|
self.registry.mount_hive(self.user_hive, samba.registry.HKEY_CURRENT_USER)
|
||||||
|
|
||||||
|
# Samba objects - LoadParm() and CredentialsOptions()
|
||||||
|
self.loadparm = loadparm
|
||||||
|
self.creds = creds
|
||||||
|
|
||||||
|
self.cache_dir = self.loadparm.get('cache directory')
|
||||||
|
print('Cache directory is: {}'.format(self.cache_dir))
|
||||||
|
|
||||||
|
gpos = get_gpo_list(dc, self.creds, self.loadparm, 'administrator')
|
||||||
|
self.policy_files = dict({ 'machine_regpols': [], 'user_regpols': [] })
|
||||||
|
for gpo in gpos:
|
||||||
|
polfiles = self._gpo_get_gpt_polfiles(gpo)
|
||||||
|
self.policy_files['machine_regpols'] += polfiles['machine_regpols']
|
||||||
|
self.policy_files['user_regpols'] += polfiles['user_regpols']
|
||||||
|
print('Policy files: {}'.format(self.policy_files))
|
||||||
|
|
||||||
|
def _parse_pol_file(self, polfile):
|
||||||
|
'''
|
||||||
|
Parse PReg file and return its preg object
|
||||||
|
'''
|
||||||
|
gpparser = GPPolParser()
|
||||||
|
data = None
|
||||||
|
|
||||||
|
with open(polfile, 'rb') as f:
|
||||||
|
data = f.read()
|
||||||
|
gpparser.parse(data)
|
||||||
|
|
||||||
|
#print(gpparser.pol_file.__ndr_print__())
|
||||||
|
return gpparser.pol_file
|
||||||
|
|
||||||
|
def _merge_entry(self, hive, entry):
|
||||||
|
'''
|
||||||
|
Write preg.entry to hive
|
||||||
|
'''
|
||||||
|
# Build hive key path from PReg's key name and value name.
|
||||||
|
hive_key = '{}\\{}'.format(entry.keyname, entry.valuename)
|
||||||
|
print('Merging {}'.format(hive_key))
|
||||||
|
|
||||||
|
hive.set_value(hive_key, entry.type, entry.data.to_bytes(4, byteorder='big'))
|
||||||
|
|
||||||
|
# Dump data to disk
|
||||||
|
hive.flush()
|
||||||
|
|
||||||
|
def get_values(self):
|
||||||
|
'''
|
||||||
|
Read data from PReg file and return list of NDR objects (samba.preg)
|
||||||
|
'''
|
||||||
|
# FIXME: Return registry and hives instead of samba.preg objects.
|
||||||
|
preg_objs = []
|
||||||
|
print('Parsing machine regpols')
|
||||||
|
for regpol in self.policy_files['machine_regpols']:
|
||||||
|
print('Processing {}'.format(regpol))
|
||||||
|
pregfile = self._parse_pol_file(regpol)
|
||||||
|
preg_objs.append(pregfile)
|
||||||
|
for entry in pregfile.entries:
|
||||||
|
self._merge_entry(self.machine_hive, entry)
|
||||||
|
|
||||||
|
return preg_objs
|
||||||
|
|
||||||
|
def _find_regpol_files(self, gpt_path):
|
||||||
|
'''
|
||||||
|
Seek through given GPT directory absolute path and return the dictionary
|
||||||
|
of user's and machine's Registry.pol files.
|
||||||
|
'''
|
||||||
|
print('Finding regpols in: {}'.format(gpt_path))
|
||||||
|
polfiles = dict({ 'machine_regpols': [], 'user_regpols': [] })
|
||||||
|
for root, dirs, files in os.walk(gpt_path):
|
||||||
|
for gpt_file in files:
|
||||||
|
if gpt_file.endswith('.pol'):
|
||||||
|
regpol_abspath = os.path.join(root, gpt_file)
|
||||||
|
if self._machine_pol_path_regex.search(regpol_abspath):
|
||||||
|
polfiles['machine_regpols'].append(regpol_abspath)
|
||||||
|
else:
|
||||||
|
polfiles['user_regpols'].append(regpol_abspath)
|
||||||
|
print('Polfiles: {}'.format(polfiles))
|
||||||
|
return polfiles
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class applier_frontend:
|
||||||
|
def __init__(self, regobj):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def apply(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class control_applier(applier_frontend):
|
||||||
|
_registry_branch = 'Software\\BaseALT\\Policies\\Control'
|
||||||
|
|
||||||
|
def __init__(self, polfiles):
|
||||||
|
self.polparsers = polfiles
|
||||||
|
self.control_settings = self._get_controls(self.polparsers)
|
||||||
|
self.controls = []
|
||||||
|
for setting in self.control_settings:
|
||||||
|
self.controls.append(control.control(setting.valuename, setting.data))
|
||||||
|
#for e in polfile.pol_file.entries:
|
||||||
|
# print('{}:{}:{}:{}:{}'.format(e.type, e.data, e.valuename, e.keyname))
|
||||||
|
|
||||||
|
def _get_controls(self, polfiles):
|
||||||
|
'''
|
||||||
|
Extract control entries from PReg file
|
||||||
|
'''
|
||||||
|
controls = []
|
||||||
|
for parser in polfiles:
|
||||||
|
for entry in parser.entries:
|
||||||
|
if entry.keyname == self._registry_branch:
|
||||||
|
controls.append(entry)
|
||||||
|
print('Found control setting: {}'.format(entry.valuename))
|
||||||
|
else:
|
||||||
|
# Property names are taken from python/samba/gp_parse/gp_pol.py
|
||||||
|
print('Dropped control setting: {}\\{}'.format(entry.keyname, entry.valuename))
|
||||||
|
return controls
|
||||||
|
|
||||||
|
def apply(self):
|
||||||
|
'''
|
||||||
|
Trigger control facility invocation.
|
||||||
|
'''
|
||||||
|
for control in self.controls:
|
||||||
|
control.set_control_status()
|
||||||
|
|
||||||
|
def dump_settings(self):
|
||||||
|
'''
|
||||||
|
Write actual controls as XML and PReg files
|
||||||
|
'''
|
||||||
|
print('Dumping...')
|
||||||
|
polfile = preg.file()
|
||||||
|
polfile.header.signature = 'PReg'
|
||||||
|
polfile.header.version = 1
|
||||||
|
polfile.num_entries = len(self.control_settings)
|
||||||
|
polfile.entries = self.control_settings
|
||||||
|
print(polfile.__ndr_print__())
|
||||||
|
|
||||||
|
policy_writer = GPPolParser()
|
||||||
|
policy_writer.pol_file = polfile
|
||||||
|
policy_writer.write_xml('test_reg.xml')
|
||||||
|
policy_writer.write_binary('test_reg.pol')
|
||||||
|
|
||||||
|
class applier:
|
||||||
|
def __init__(self, backend):
|
||||||
|
self.backend = backend
|
||||||
|
self.gpvalues = self.load_values()
|
||||||
|
print('Values: {}'.format(self.gpvalues))
|
||||||
|
capplier = control_applier(self.gpvalues)
|
||||||
|
self.appliers = dict({ 'control': capplier })
|
||||||
|
|
||||||
|
def load_values(self):
|
||||||
|
'''
|
||||||
|
This thing returns the list of samba.preg objects for
|
||||||
|
now but it must be transformed to return registry and
|
||||||
|
its hives to read values from.
|
||||||
|
'''
|
||||||
|
print('Get values from backend')
|
||||||
|
return self.backend.get_values()
|
||||||
|
|
||||||
|
def apply_parameters(self):
|
||||||
|
print('Applying')
|
||||||
|
self.appliers['control'].apply()
|
||||||
|
# This thing dumps Registry.pol files to disk from data structures
|
||||||
|
#print('Writing settings to file')
|
||||||
|
#self.appliers['control'].dump_settings()
|
||||||
|
|
||||||
|
def parse_arguments():
|
||||||
|
arguments = argparse.ArgumentParser(description='Generate configuration out of parsed policies')
|
||||||
|
arguments.add_argument('sid',
|
||||||
|
type=str,
|
||||||
|
help='SID to parse policies from')
|
||||||
|
arguments.add_argument('--dc',
|
||||||
|
type=str,
|
||||||
|
help='FQDN of the domain to replicate SYSVOL from')
|
||||||
|
return arguments.parse_args()
|
||||||
|
|
||||||
|
def get_gpo_list(dc_hostname, creds, lp, user):
|
||||||
|
gpos = []
|
||||||
|
ads = samba.gpo.ADS_STRUCT(dc_hostname, lp, creds)
|
||||||
|
if ads.connect():
|
||||||
|
#gpos = ads.get_gpo_list(creds.get_username())
|
||||||
|
gpos = ads.get_gpo_list(user)
|
||||||
|
print('Got GPO list:')
|
||||||
|
for gpo in gpos:
|
||||||
|
# These setters are taken from libgpo/pygpo.c
|
||||||
|
# print(gpo.ds_path) # LDAP entry
|
||||||
|
print('{} ({})'.format(gpo.display_name, gpo.name))
|
||||||
|
print('------')
|
||||||
|
return gpos
|
||||||
|
|
||||||
|
def get_machine_domain():
|
||||||
|
pass
|
||||||
|
|
||||||
|
def select_dc(lp, creds, dc):
|
||||||
|
samba_dc = get_dc_hostname(creds, lp)
|
||||||
|
|
||||||
|
if samba_dc != dc and dc != None:
|
||||||
|
print('Samba DC setting is {} and is overwritten by user setting {}'.format(samba_dc, dc))
|
||||||
|
return dc
|
||||||
|
|
||||||
|
return samba_dc
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = parse_arguments()
|
||||||
|
|
||||||
|
#back = hreg_filesystem_backend(args.sid)
|
||||||
|
parser = optparse.OptionParser('GPO Applier')
|
||||||
|
sambaopts = options.SambaOptions(parser)
|
||||||
|
credopts = options.CredentialsOptions(parser)
|
||||||
|
# Initialize loadparm context
|
||||||
|
lp = sambaopts.get_loadparm()
|
||||||
|
creds = credopts.get_credentials(lp, fallback_machine=True)
|
||||||
|
|
||||||
|
# Determine the default Samba DC for replication and try
|
||||||
|
# to overwrite it with user setting.
|
||||||
|
dc = select_dc(lp, creds, args.dc)
|
||||||
|
|
||||||
|
back = samba_backend(lp, creds, args.sid, dc)
|
||||||
|
|
||||||
|
appl = applier(back)
|
||||||
|
appl.apply_parameters()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
13
gpoa/policy/__init__.py
Normal file
13
gpoa/policy/__init__.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import policy.firewall
|
||||||
|
import policy.removable_devices_perms
|
||||||
|
import policy.printers
|
||||||
|
import policy.shortcuts
|
||||||
|
import policy.applications
|
||||||
|
from policy.common import Perms
|
||||||
|
|
||||||
|
data_roots = {}
|
||||||
|
data_roots.update(policy.firewall.data_roots)
|
||||||
|
data_roots.update(policy.removable_devices_perms.data_roots)
|
||||||
|
data_roots.update(policy.printers.data_roots)
|
||||||
|
data_roots.update(policy.shortcuts.data_roots)
|
||||||
|
data_roots.update(policy.applications.data_roots)
|
24
gpoa/policy/applications.py
Normal file
24
gpoa/policy/applications.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
from policy.common import Policy, Perms
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
class Applications (Policy):
|
||||||
|
def __init__(self):
|
||||||
|
self._Policy__name = "Applications"
|
||||||
|
self._Policy__script_name = "applications.sh"
|
||||||
|
self._Policy__template = "applications.bash.j2"
|
||||||
|
self._Policy__perms = Perms.ROOT
|
||||||
|
|
||||||
|
def process(self, scope, path):
|
||||||
|
applications = []
|
||||||
|
print("{name} processing {path} ({scope})".format(name=self.name,path=path,scope=scope))
|
||||||
|
for p in os.listdir(path):
|
||||||
|
package_name = Path('{}/{}/packageName'.format(path, p)).read_text()
|
||||||
|
product_name = Path('{}/{}/productName'.format(path, p)).read_text()
|
||||||
|
applications.append({'uuid': p, 'package': package_name, 'name': product_name})
|
||||||
|
|
||||||
|
return ({'applications': applications})
|
||||||
|
|
||||||
|
data_roots = {
|
||||||
|
"Applications": Applications
|
||||||
|
}
|
44
gpoa/policy/common.py
Normal file
44
gpoa/policy/common.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
class Perms (Enum):
|
||||||
|
USER = 1
|
||||||
|
ROOT = 2
|
||||||
|
|
||||||
|
class Policy (object):
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
try:
|
||||||
|
return self.__name
|
||||||
|
except AttributeError as e:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
@property
|
||||||
|
def script_name(self):
|
||||||
|
try:
|
||||||
|
return self.__script_name
|
||||||
|
except AttributeError as e:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
@property
|
||||||
|
def template(self):
|
||||||
|
try:
|
||||||
|
return self.__template
|
||||||
|
except AttributeError as e:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
@property
|
||||||
|
def data_roots(self):
|
||||||
|
try:
|
||||||
|
return self.__data_roots
|
||||||
|
except AttributeError as e:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
@property
|
||||||
|
def perms(self):
|
||||||
|
try:
|
||||||
|
return self.__perms
|
||||||
|
except AttributeError as e:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
def process(self, scope, path):
|
||||||
|
raise "process must be implemented in child class"
|
46
gpoa/policy/firewall.py
Normal file
46
gpoa/policy/firewall.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
from policy.common import Policy, Perms
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
class Firewall (Policy):
|
||||||
|
def __init__(self):
|
||||||
|
self._Policy__name = "Firewall"
|
||||||
|
self._Policy__script_name = "firewall.sh"
|
||||||
|
self._Policy__template = "firewall.bash.j2"
|
||||||
|
self._Policy__perms = Perms.ROOT
|
||||||
|
|
||||||
|
def process(self, scope, path):
|
||||||
|
print("{name} processing {path} ({scope})".format(name=self.name,path=path,scope=scope))
|
||||||
|
zoneRules = {}
|
||||||
|
for p in os.listdir(path):
|
||||||
|
if path.split('/')[-1:] == ["WindowsFirewall"]:
|
||||||
|
for zone in os.listdir(path):
|
||||||
|
print("processing {} prifile".format(zone))
|
||||||
|
zoneRules[zone] = self.__process_firewall_rules(zone, path)
|
||||||
|
|
||||||
|
return ({'zoneRules': zoneRules})
|
||||||
|
|
||||||
|
def __process_firewall_rules(self, zone, path):
|
||||||
|
print("processing rules in {} zone".format(zone))
|
||||||
|
base = "{}/{}".format(path, zone)
|
||||||
|
openPortsPath = "GloballyOpenPorts"
|
||||||
|
rules = {}
|
||||||
|
if os.path.exists("{}/{}".format(base, openPortsPath)):
|
||||||
|
listPath = "{}/{}/List".format(base, openPortsPath)
|
||||||
|
openPorts = []
|
||||||
|
for r in os.listdir(listPath):
|
||||||
|
rule = Path('{}/{}'.format(listPath, r)).read_text()
|
||||||
|
[port, proto, srcs, enabled, name] = rule.split(':')
|
||||||
|
sources = srcs.split(',')
|
||||||
|
openPorts.append({'proto': proto, 'port': port, 'sources': sources})
|
||||||
|
rules['openPorts'] = openPorts
|
||||||
|
|
||||||
|
return(rules)
|
||||||
|
|
||||||
|
# for r in os.listdir("{}/{}".format(path,zone)):
|
||||||
|
# print(r)
|
||||||
|
|
||||||
|
data_roots = {
|
||||||
|
"Software/Microsoft/Windows/CurrentVersion/Policies/NetworkAccessProtection/ClientConfig": Firewall,
|
||||||
|
"Software/Microsoft/Windows/CurrentVersion/Policies/WindowsFirewall": Firewall
|
||||||
|
}
|
40
gpoa/policy/printers.py
Normal file
40
gpoa/policy/printers.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
from policy.common import Policy, Perms
|
||||||
|
import os
|
||||||
|
|
||||||
|
class Printers (Policy):
|
||||||
|
def __init__(self):
|
||||||
|
self._Policy__name = "Printers"
|
||||||
|
self._Policy__script_name = "printers.sh"
|
||||||
|
self._Policy__template = "printers.bash.j2"
|
||||||
|
self._Policy__perms = Perms.ROOT
|
||||||
|
|
||||||
|
def process(self, scope, path):
|
||||||
|
print("{name} processing {path} ({scope})".format(name=self.name,path=path,scope=scope))
|
||||||
|
shared_path = "{}/SharedPrinter".format(path)
|
||||||
|
port_path = "{}/PortPrinter".format(path)
|
||||||
|
local_path = "{}/LocalPrinter".format(path)
|
||||||
|
printers = []
|
||||||
|
if os.path.exists(shared_path):
|
||||||
|
print("SharedPrinters")
|
||||||
|
if os.path.exists(port_path):
|
||||||
|
print("PortPrinters")
|
||||||
|
for p in os.listdir(port_path):
|
||||||
|
with open("{}/{}/ipAddress".format(port_path,p)) as f:
|
||||||
|
addr = f.read()
|
||||||
|
with open("{}/{}/localName".format(port_path,p)) as f:
|
||||||
|
name = f.read()
|
||||||
|
prns = {'name': name, 'addr': addr}
|
||||||
|
printers.append(prns)
|
||||||
|
|
||||||
|
if os.path.exists(local_path):
|
||||||
|
print("LocalPrinters")
|
||||||
|
|
||||||
|
return ({'printers': printers})
|
||||||
|
|
||||||
|
@property
|
||||||
|
def data_roots(self):
|
||||||
|
return data_roots
|
||||||
|
|
||||||
|
data_roots = {
|
||||||
|
"Preferences/Printers": Printers
|
||||||
|
}
|
51
gpoa/policy/removable_devices_perms.py
Normal file
51
gpoa/policy/removable_devices_perms.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
from policy.common import Policy, Perms
|
||||||
|
from enum import Enum
|
||||||
|
import os
|
||||||
|
|
||||||
|
class DevType (Enum):
|
||||||
|
CDDVD = "{53f56308-b6bf-11d0-94f2-00a0c91efb8b}"
|
||||||
|
FLOPPY = "{53f56311-b6bf-11d0-94f2-00a0c91efb8b}"
|
||||||
|
REMDISKS = "{53f5630d-b6bf-11d0-94f2-00a0c91efb8b}"
|
||||||
|
TAPEDRIVE = "{53f5630b-b6bf-11d0-94f2-00a0c91efb8b}"
|
||||||
|
WPDDEV1 = "{6AC27878-A6FA-4155-BA85-F98F491D4F33}"
|
||||||
|
WPDDEV2 = "{F33FDC04-D1AC-4E8E-9A30-19BBD4B108AE}"
|
||||||
|
|
||||||
|
class RemovableDevices (Policy):
|
||||||
|
def __init__(self):
|
||||||
|
self._Policy__name = "RemovebleDivicesPerms"
|
||||||
|
self._Policy__script_name = "removable_devices_perms.sh"
|
||||||
|
self._Policy__template = "removable_devices_perms.bash.j2"
|
||||||
|
self._Policy__perms = Perms.ROOT
|
||||||
|
|
||||||
|
def process(self, scope, path):
|
||||||
|
print("{name} processing {path} ({scope})".format(name=self.name,path=path,scope=scope))
|
||||||
|
perms = {}
|
||||||
|
for p in os.listdir(path):
|
||||||
|
if DevType(p) == DevType.REMDISKS:
|
||||||
|
perms['Removable'] = self.__parse_perms('{}/{}'.format(path,p))
|
||||||
|
# elif DevType(p) == DevType.CDDVD:
|
||||||
|
|
||||||
|
perms['CDDVD'] = {'deny_exec': True, 'deny_read': False, 'deny_write': True}
|
||||||
|
return (perms)
|
||||||
|
|
||||||
|
def __parse_perms(self, path):
|
||||||
|
deny_exec = self.__read_perm('{}/Deny_Execute.dword'.format(path))
|
||||||
|
deny_read = self.__read_perm('{}/Deny_Read.dword'.format(path))
|
||||||
|
deny_write = self.__read_perm('{}/Deny_Write.dword'.format(path))
|
||||||
|
return ({'deny_exec': deny_exec, 'deny_read': deny_read, 'deny_write': deny_write})
|
||||||
|
|
||||||
|
def __read_perm(self, path):
|
||||||
|
try:
|
||||||
|
f = open(path, 'r')
|
||||||
|
except FileNotFoundError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
p = f.read()
|
||||||
|
if p == "0x00000001":
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
data_roots = {
|
||||||
|
"Software/Microsoft/Windows/CurrentVersion/Policies/RemovableStorageDevices": RemovableDevices
|
||||||
|
}
|
24
gpoa/policy/shortcuts.py
Normal file
24
gpoa/policy/shortcuts.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
from policy.common import Policy, Perms
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
class Shortcuts (Policy):
|
||||||
|
def __init__(self):
|
||||||
|
self._Policy__name = "Shortcuts"
|
||||||
|
self._Policy__script_name = "shortcuts.sh"
|
||||||
|
self._Policy__template = "shortcuts.bash.j2"
|
||||||
|
self._Policy__perms = Perms.USER
|
||||||
|
|
||||||
|
def process(self, scope, path):
|
||||||
|
shortcuts = []
|
||||||
|
print("{name} processing {path} ({scope})".format(name=self.name,path=path,scope=scope))
|
||||||
|
for p in os.listdir(path):
|
||||||
|
target_path = Path('{}/{}/targetPath'.format(path, p)).read_text()
|
||||||
|
arguments = Path('{}/{}/arguments'.format(path, p)).read_text()
|
||||||
|
shortcuts.append({'name': p, 'target': target_path, 'args': arguments})
|
||||||
|
|
||||||
|
return ({'shortcuts': shortcuts})
|
||||||
|
|
||||||
|
data_roots = {
|
||||||
|
"Preferences/Shortcuts": Shortcuts
|
||||||
|
}
|
7
gpoa/templ.py
Normal file
7
gpoa/templ.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
from jinja2 import Environment, PackageLoader, FileSystemLoader, StrictUndefined, select_autoescape
|
||||||
|
|
||||||
|
templateLoader = FileSystemLoader(searchpath="./templates")
|
||||||
|
env = Environment(loader=templateLoader, undefined=StrictUndefined)
|
||||||
|
template = env.get_template('printers.bash.j2')
|
||||||
|
|
||||||
|
print(template.render(printer_name="HP_via_script", printer_address="10.64.128.250", DEBUG=False))
|
9
gpoa/templates/applications.bash.j2
Normal file
9
gpoa/templates/applications.bash.j2
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{% include "header.bash.j2" %}
|
||||||
|
MODULE_NAME="Applications"
|
||||||
|
|
||||||
|
{% for a in applications %}
|
||||||
|
{%set app_name = a.name %}
|
||||||
|
{%set pkg_name = a.package %}
|
||||||
|
logI "Install {{ pkg_name }} via apt-get"
|
||||||
|
apt-get install -y "${pkg_name}"
|
||||||
|
{% endfor %}
|
22
gpoa/templates/firewall.bash.j2
Normal file
22
gpoa/templates/firewall.bash.j2
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{% include 'header.bash.j2' %}
|
||||||
|
MODULE_NAME="Firewall"
|
||||||
|
|
||||||
|
on_exit() {
|
||||||
|
logI "on_exit in ${MODULE_NAME}"
|
||||||
|
}
|
||||||
|
|
||||||
|
{% for z,rs in zoneRules.items() %}
|
||||||
|
logI "Processing rules in zone {{z}}"
|
||||||
|
if ! firewall-cmd --permanent --list-all-zones | grep -ve '\(^[[:space:]]*$\|\ .*\)' | grep -q "{{z}}"; then
|
||||||
|
firewall-cmd --permanent --new-zone="{{z}}"
|
||||||
|
fi
|
||||||
|
{% if "openPorts" in rs %}
|
||||||
|
logI "Define opened ports"
|
||||||
|
{% for p in rs['openPorts'] %}
|
||||||
|
{% for s in p.sources %}
|
||||||
|
firewall-cmd --permanent --zone="{{z}}" --add-source="{{s}}"
|
||||||
|
{% endfor %}
|
||||||
|
firewall-cmd --permanent --zone={{z}} --add-port={{p.port}}/{{p.proto | lower }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
63
gpoa/templates/header.bash.j2
Normal file
63
gpoa/templates/header.bash.j2
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
{% if DEBUG %}
|
||||||
|
set -x
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
BASE_DIR="${TMP}/gpoA"
|
||||||
|
LOG_DIR="${BASE_DIR}/log"
|
||||||
|
#STDERR_TO="${LOG_DIR}/stderr.log"
|
||||||
|
|
||||||
|
__log () {
|
||||||
|
local level="$1"
|
||||||
|
local msg="$2"
|
||||||
|
local mod_name="${MODULE_NAME}"
|
||||||
|
echo "${mod_name} [${level}] ${msg}"
|
||||||
|
}
|
||||||
|
|
||||||
|
logD () {
|
||||||
|
__log "DEBUG" "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
logI () {
|
||||||
|
__log "INFO" "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
logW () {
|
||||||
|
__log "WARNING" "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
logE () {
|
||||||
|
__log "ERROR" "$1"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
__if_func_exists() {
|
||||||
|
declare -f -F "$1" >/dev/null
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
__on_exit() {
|
||||||
|
__if_func_exists "on_exit" && on_exit
|
||||||
|
logI "Finalizyng..."
|
||||||
|
}
|
||||||
|
|
||||||
|
__on_error() {
|
||||||
|
__if_func_exists "on_error" && on_exit
|
||||||
|
logE "Error..."
|
||||||
|
}
|
||||||
|
|
||||||
|
__init() {
|
||||||
|
[[ -d "${BASE_DIR}" ]] || mkdir "${BASE_DIR}"
|
||||||
|
[[ -d "${LOG_DIR}" ]] || mkdir "${LOG_DIR}"
|
||||||
|
# exec 2> ${STDERR_TO}
|
||||||
|
}
|
||||||
|
|
||||||
|
for sig in SIGINT SIGTERM SIGHUP SIGQUIT EXIT RETURN; do
|
||||||
|
trap __on_exit $sig
|
||||||
|
done
|
||||||
|
|
||||||
|
#for sig in ERR; do
|
||||||
|
# trap __on_error $sig
|
||||||
|
#done
|
||||||
|
|
||||||
|
__init
|
16
gpoa/templates/printers.bash.j2
Normal file
16
gpoa/templates/printers.bash.j2
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{% include "header.bash.j2" %}
|
||||||
|
MODULE_NAME="Printers"
|
||||||
|
|
||||||
|
{% for p in printers %}
|
||||||
|
{%set printer_name = p.name | replace(" ", "_") %}
|
||||||
|
{%set printer_address = p.addr %}
|
||||||
|
logI "Search if {{ printer_name }} is already defined"
|
||||||
|
URI=$(LC_ALL="C" lpstat -v | awk '$3 ~ /{{ printer_name }}:/ { print $4;}')
|
||||||
|
if [[ -z "$URI" ]]; then
|
||||||
|
logI "Register {{ printer_name }} on {{ printer_address }}"
|
||||||
|
lpadmin -p "{{ printer_name }}" -E -v "ipp://{{ printer_address }}/ipp/print" -m everywhere
|
||||||
|
logI "Done"
|
||||||
|
else
|
||||||
|
logI "{{ printer_name }} is already defined as ${URI}. Nothing to do."
|
||||||
|
fi
|
||||||
|
{% endfor %}
|
25
gpoa/templates/removable_devices_perms.bash.j2
Normal file
25
gpoa/templates/removable_devices_perms.bash.j2
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{% include "header.bash.j2" %}
|
||||||
|
MODULE_NAME="Removable Devices Perms"
|
||||||
|
|
||||||
|
mk_udisk2_rules() {
|
||||||
|
local allow="$1"
|
||||||
|
cat <<EOF >/etc/polkit-1/rules.d/60-udisks2.rules
|
||||||
|
polkit.addRule(function(action, subject) {
|
||||||
|
var YES = polkit.Result.YES;
|
||||||
|
var NO = polkit.Result.NO;
|
||||||
|
var perms = {
|
||||||
|
"org.freedesktop.udisks2.filesystem-mount": ${allow},
|
||||||
|
"org.freedesktop.udisks2.filesystem-mount-system": ${allow},
|
||||||
|
"org.freedesktop.udisks2.filesystem-mount-other-seat": ${allow},
|
||||||
|
|
||||||
|
};
|
||||||
|
// if (!subject.isInGroup("wheel")) {
|
||||||
|
return permission[action.id];
|
||||||
|
// }
|
||||||
|
});
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
{% if Removable is defined and (Removable.deny_exec or Removable.deny_read or Removable.deny_write) %}
|
||||||
|
mk_udisk2_rules "NO"
|
||||||
|
{% endif %}
|
11
gpoa/templates/shortcuts.bash.j2
Normal file
11
gpoa/templates/shortcuts.bash.j2
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{% include "header.bash.j2" %}
|
||||||
|
MODULE_NAME="Shortcuts"
|
||||||
|
DESKTOP_DIR=$(xdg-user-dir DESKTOP)
|
||||||
|
|
||||||
|
{% for s in shortcuts %}
|
||||||
|
cat <<EOF >"${DESKTOP_DIR}/{{s.name}}.desktop"
|
||||||
|
[Desktop Entry]
|
||||||
|
Name={{s.name}}
|
||||||
|
Exec={{s.target}} {{s.args}}
|
||||||
|
EOF
|
||||||
|
{% endfor %}
|
BIN
gpoa/test/Registry.pol
Normal file
BIN
gpoa/test/Registry.pol
Normal file
Binary file not shown.
BIN
gpoa/test/test.pol
Normal file
BIN
gpoa/test/test.pol
Normal file
Binary file not shown.
23
gpoa/test/test.xml
Normal file
23
gpoa/test/test.xml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<PolFile num_entries="4" signature="PReg" version="1">
|
||||||
|
<Entry type="4" type_name="REG_DWORD">
|
||||||
|
<Key>Software\BaseALT\Policies\Control</Key>
|
||||||
|
<ValueName>sshd-gssapi-auth</ValueName>
|
||||||
|
<Value>1</Value>
|
||||||
|
</Entry>
|
||||||
|
<Entry type="4" type_name="REG_DWORD">
|
||||||
|
<Key>Software\BaseALT\Policies\Control</Key>
|
||||||
|
<ValueName>ssh-gssapi-auth</ValueName>
|
||||||
|
<Value>1</Value>
|
||||||
|
</Entry>
|
||||||
|
<Entry type="4" type_name="REG_DWORD">
|
||||||
|
<Key>Software\BaseALT\Policies\Control</Key>
|
||||||
|
<ValueName>sudo</ValueName>
|
||||||
|
<Value>0</Value>
|
||||||
|
</Entry>
|
||||||
|
<Entry type="4" type_name="REG_DWORD">
|
||||||
|
<Key>Software\Policies\Microsoft\Windows\System</Key>
|
||||||
|
<ValueName>UserPolicyMode</ValueName>
|
||||||
|
<Value>1</Value>
|
||||||
|
</Entry>
|
||||||
|
</PolFile>
|
BIN
gpoa/test/test_reg.pol
Normal file
BIN
gpoa/test/test_reg.pol
Normal file
Binary file not shown.
18
gpoa/test/test_reg.xml
Normal file
18
gpoa/test/test_reg.xml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<PolFile num_entries="3" signature="PReg" version="1">
|
||||||
|
<Entry type="4" type_name="REG_DWORD">
|
||||||
|
<Key>Software\BaseALT\Policies\Control</Key>
|
||||||
|
<ValueName>sshd-gssapi-auth</ValueName>
|
||||||
|
<Value>1</Value>
|
||||||
|
</Entry>
|
||||||
|
<Entry type="4" type_name="REG_DWORD">
|
||||||
|
<Key>Software\BaseALT\Policies\Control</Key>
|
||||||
|
<ValueName>ssh-gssapi-auth</ValueName>
|
||||||
|
<Value>1</Value>
|
||||||
|
</Entry>
|
||||||
|
<Entry type="4" type_name="REG_DWORD">
|
||||||
|
<Key>Software\BaseALT\Policies\Control</Key>
|
||||||
|
<ValueName>sudo</ValueName>
|
||||||
|
<Value>0</Value>
|
||||||
|
</Entry>
|
||||||
|
</PolFile>
|
306
gpupdate
Executable file
306
gpupdate
Executable file
@ -0,0 +1,306 @@
|
|||||||
|
#! /usr/bin/env python3
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import threading
|
||||||
|
|
||||||
|
import os
|
||||||
|
import errno
|
||||||
|
|
||||||
|
import tempfile
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
|
||||||
|
class gpupdate:
|
||||||
|
_smb_cache = '/var/cache/samba/gpo_cache'
|
||||||
|
_tmp_root = '/tmp/gpupdate'
|
||||||
|
|
||||||
|
def __init__(self, user, domain, controller):
|
||||||
|
self._gpos = []
|
||||||
|
self._user = user
|
||||||
|
self._domain = domain
|
||||||
|
self._controller = controller
|
||||||
|
try:
|
||||||
|
os.makedirs(self._tmp_root)
|
||||||
|
except OSError as exc:
|
||||||
|
if exc.errno != errno.EEXIST:
|
||||||
|
raise
|
||||||
|
pass
|
||||||
|
self._temp_dir = self._mktmp('/tmp/gpupdate')
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
'''
|
||||||
|
Update GPO cache.
|
||||||
|
'''
|
||||||
|
proc = subprocess.Popen(['net', 'ads', 'gpo', 'list', self._user], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
|
#output = subprocess.check_output(['net', 'ads', 'gpo', 'list', self.user])
|
||||||
|
#print(output)
|
||||||
|
thr = threading.Thread(target=self._parse_netads, args=(proc,))
|
||||||
|
thr.start()
|
||||||
|
thr.join()
|
||||||
|
|
||||||
|
return self._fetch_gpos()
|
||||||
|
|
||||||
|
def _parse_netads(self, proc):
|
||||||
|
'''
|
||||||
|
Parse output of `net ads gpo list Administrator` command.
|
||||||
|
'''
|
||||||
|
for line in iter(proc.stdout.readline, b''):
|
||||||
|
decoded_line = line.decode('utf-8')
|
||||||
|
if decoded_line.startswith('filesyspath') and line != b'filesyspath:\t\t(null)\n':
|
||||||
|
self._gpos.append(self._strip_paths(decoded_line))
|
||||||
|
|
||||||
|
|
||||||
|
def _fetch_gpos(self):
|
||||||
|
'''
|
||||||
|
Download GPO directories specified by their GUIDs using smbclient with Kerberos 5 authentication.
|
||||||
|
'''
|
||||||
|
retrieved_gpos = []
|
||||||
|
for pol in self._gpos:
|
||||||
|
policy_tmp_dir = os.path.join(self._temp_dir, pol)
|
||||||
|
os.makedirs(policy_tmp_dir)
|
||||||
|
smbclient_cmd = 'prompt OFF;recurse ON;cd {}/Policies/{};lcd {};mget *'.format(self._domain.lower(), pol, policy_tmp_dir)
|
||||||
|
print('Executing: {}'.format(smbclient_cmd))
|
||||||
|
retcode = subprocess.call('smbclient -k \'\\\\{}\\sysvol\' -N -c \'{}\''.format(self._controller, smbclient_cmd), shell=True)
|
||||||
|
if retcode == 0:
|
||||||
|
print('Successfully retrieved GPO: {}'.format(pol))
|
||||||
|
self._2gpo_cache(policy_tmp_dir, pol)
|
||||||
|
retrieved_gpos.append(pol)
|
||||||
|
else:
|
||||||
|
print('Unable to retrieve GPO: {}'.format(pol))
|
||||||
|
return retrieved_gpos
|
||||||
|
|
||||||
|
def _mktmp(self, tmp):
|
||||||
|
'''
|
||||||
|
Create temporary directory to download GPO
|
||||||
|
'''
|
||||||
|
try:
|
||||||
|
os.makedirs(tmp)
|
||||||
|
except OSError as exc:
|
||||||
|
if exc.errno != errno.EEXIST:
|
||||||
|
raise
|
||||||
|
pass
|
||||||
|
return tempfile.mkdtemp(dir=tmp)
|
||||||
|
|
||||||
|
def _strip_paths(self, policy_path):
|
||||||
|
'''
|
||||||
|
Strip newlines and extra symbols from paths.
|
||||||
|
'''
|
||||||
|
return policy_path[len('filesyspath:\t\t\\\\{}\\SysVol\\{}\\Policies\\'.format(self._domain, self._domain)):].strip()
|
||||||
|
|
||||||
|
def _2gpo_cache(self, obj, pol):
|
||||||
|
'''
|
||||||
|
Move downloaded GPOs to /var/cache/samba/gpo_cache
|
||||||
|
'''
|
||||||
|
gpo_dest = os.path.join(self._smb_cache, self._domain.upper(), 'POLICIES', pol)
|
||||||
|
shutil.rmtree(gpo_dest) # Remove destination GPO if exists
|
||||||
|
shutil.move(obj, gpo_dest)
|
||||||
|
|
||||||
|
class hreg:
|
||||||
|
_hreg_exe = '/usr/bin/hreg'
|
||||||
|
_hreg_cache = '/var/cache/gpupdate'
|
||||||
|
|
||||||
|
def __init__(self, cache_dir, domain, gpos, sid):
|
||||||
|
self._cache_dir = cache_dir
|
||||||
|
self._domain = domain
|
||||||
|
self._gpos = gpos
|
||||||
|
self._sid = sid
|
||||||
|
self._temp_dir = os.path.join(self._hreg_cache, 'tmp')
|
||||||
|
try:
|
||||||
|
os.makedirs(self._temp_dir)
|
||||||
|
except OSError as exc:
|
||||||
|
if exc.errno != errno.EEXIST:
|
||||||
|
raise
|
||||||
|
pass
|
||||||
|
|
||||||
|
def to_fs(self):
|
||||||
|
'''
|
||||||
|
Transform policy files to file system representation for given domain
|
||||||
|
'''
|
||||||
|
dest = self._mktmp(self._temp_dir)
|
||||||
|
for gpo in self._gpos:
|
||||||
|
policy_file = os.path.join(self._cache_dir,
|
||||||
|
self._domain.upper(),
|
||||||
|
'POLICIES',
|
||||||
|
gpo,
|
||||||
|
'User',
|
||||||
|
'Registry.pol')
|
||||||
|
hreg_cmd = '{} apply {} {} -u {}'.format(self._hreg_exe, dest, policy_file, self._sid)
|
||||||
|
subprocess.call(hreg_cmd, shell=True)
|
||||||
|
# Move data to permanent cache
|
||||||
|
permanent_cache = os.path.join(self._hreg_cache, self._sid)
|
||||||
|
try:
|
||||||
|
shutil.rmtree(permanent_cache)
|
||||||
|
except OSError as exc:
|
||||||
|
if exc.errno != errno.ENOENT:
|
||||||
|
raise
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
os.makedirs(permanent_cache)
|
||||||
|
except OSError as exc:
|
||||||
|
if exc.errno != errno.EEXIST:
|
||||||
|
raise
|
||||||
|
pass
|
||||||
|
for i in os.listdir(dest):
|
||||||
|
shutil.move(os.path.join(dest, i), permanent_cache)
|
||||||
|
shutil.rmtree(dest)
|
||||||
|
|
||||||
|
def _mktmp(self, tmp):
|
||||||
|
'''
|
||||||
|
Create temporary directory for GPO VFS
|
||||||
|
'''
|
||||||
|
try:
|
||||||
|
os.makedirs(tmp)
|
||||||
|
except OSError as exc:
|
||||||
|
if exc.errno != errno.EEXIST:
|
||||||
|
raise
|
||||||
|
pass
|
||||||
|
return tempfile.mkdtemp(dir=tmp)
|
||||||
|
|
||||||
|
class gpoa:
|
||||||
|
_gpoa_dir = '/usr/libexec/gpoa'
|
||||||
|
_gpoa_exe = 'main.py'
|
||||||
|
|
||||||
|
def __init__(self, sid):
|
||||||
|
self._sid = sid
|
||||||
|
|
||||||
|
def generate_scripts(self):
|
||||||
|
'''
|
||||||
|
Call gpoa utility to generate scripts
|
||||||
|
'''
|
||||||
|
gpoa_cmd = ['./{}'.format(self._gpoa_exe), self._sid]
|
||||||
|
cwd = os.getcwd()
|
||||||
|
|
||||||
|
os.chdir(self._gpoa_dir)
|
||||||
|
print('Running gpoa')
|
||||||
|
output = subprocess.call(gpoa_cmd)
|
||||||
|
print(output)
|
||||||
|
os.chdir(cwd)
|
||||||
|
|
||||||
|
def wbinfo_getsid(domain, user):
|
||||||
|
'''
|
||||||
|
Get SID using wbinfo
|
||||||
|
'''
|
||||||
|
wbinfo_cmd = ['wbinfo', '-n', '{}\\{}'.format(domain.upper(), user)]
|
||||||
|
output = subprocess.check_output(wbinfo_cmd)
|
||||||
|
sid = output.split()[0].decode('utf-8')
|
||||||
|
return sid
|
||||||
|
|
||||||
|
def machine_kinit():
|
||||||
|
'''
|
||||||
|
Perform kinit with machine credentials
|
||||||
|
'''
|
||||||
|
host = socket.gethostname().split('.', 1)[0].upper() + "$"
|
||||||
|
subprocess.call(['kinit', '-k', host])
|
||||||
|
print('kinit succeed')
|
||||||
|
|
||||||
|
def check_krb_ticket():
|
||||||
|
'''
|
||||||
|
Check if Kerberos 5 ticket present
|
||||||
|
'''
|
||||||
|
try:
|
||||||
|
subprocess.check_call([ 'klist', '-s' ])
|
||||||
|
output = subprocess.check_output('klist', stderr=subprocess.STDOUT).decode()
|
||||||
|
print(output)
|
||||||
|
except:
|
||||||
|
sys.exit( 1 )
|
||||||
|
print('Ticket check succeed')
|
||||||
|
|
||||||
|
def get_domain_name():
|
||||||
|
'''
|
||||||
|
Get current Active Directory domain name
|
||||||
|
'''
|
||||||
|
lookup_cmd = ['net', 'ads', 'lookup']
|
||||||
|
output = subprocess.check_output(lookup_cmd, stderr=subprocess.STDOUT).decode()
|
||||||
|
d = re.search( "Domain:\s*(\S+)\n", output, re.MULTILINE )
|
||||||
|
if d:
|
||||||
|
domain_name = d.group(1)
|
||||||
|
print(domain_name)
|
||||||
|
return domain_name
|
||||||
|
return
|
||||||
|
|
||||||
|
def get_domain_controller():
|
||||||
|
'''
|
||||||
|
Get current Active Directory domain name
|
||||||
|
'''
|
||||||
|
lookup_cmd = ['net', 'ads', 'lookup']
|
||||||
|
output = subprocess.check_output(lookup_cmd, stderr=subprocess.STDOUT).decode()
|
||||||
|
d = re.search( "^Domain Controller:\s*(\S+)\n", output, re.MULTILINE )
|
||||||
|
if d:
|
||||||
|
domain_controller = d.group(1)
|
||||||
|
print(domain_controller)
|
||||||
|
return domain_controller
|
||||||
|
return
|
||||||
|
|
||||||
|
def parse_cli_arguments():
|
||||||
|
'''
|
||||||
|
Command line argument parser
|
||||||
|
'''
|
||||||
|
argparser = argparse.ArgumentParser(description='Update group policies for the specified user')
|
||||||
|
argparser.add_argument('-u',
|
||||||
|
'--user',
|
||||||
|
default='Administrator',
|
||||||
|
help='Name of the user for GPO update')
|
||||||
|
argparser.add_argument('-d',
|
||||||
|
'--domain',
|
||||||
|
help='Name of the AD domain for replication')
|
||||||
|
argparser.add_argument('-c',
|
||||||
|
'--controller',
|
||||||
|
help='AD controller to connect to')
|
||||||
|
argparser.add_argument('-p',
|
||||||
|
'--password',
|
||||||
|
help='Kerberos 5 password for the specified user')
|
||||||
|
argparser.add_argument('-i',
|
||||||
|
'--sid',
|
||||||
|
help='Specify SID for User target')
|
||||||
|
argparser.add_argument('-t',
|
||||||
|
'--target',
|
||||||
|
default='User',
|
||||||
|
help='Computer or User')
|
||||||
|
argparser.add_argument('-f',
|
||||||
|
'--force',
|
||||||
|
help='Reapply all policy settings')
|
||||||
|
argparser.add_argument('-w',
|
||||||
|
'--wait',
|
||||||
|
default=600,
|
||||||
|
help='Wait for specified number of seconds')
|
||||||
|
argparser.add_argument('-l',
|
||||||
|
'--logoff',
|
||||||
|
help='Force logoff after settings are applied')
|
||||||
|
argparser.add_argument('-b',
|
||||||
|
'--boot',
|
||||||
|
help='Force reboot after settings are applied')
|
||||||
|
argparser.add_argument('-s',
|
||||||
|
'--sync',
|
||||||
|
help='Perform next GPO application synchronously')
|
||||||
|
|
||||||
|
return argparser.parse_args()
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = parse_cli_arguments()
|
||||||
|
|
||||||
|
machine_kinit()
|
||||||
|
check_krb_ticket()
|
||||||
|
|
||||||
|
domain = get_domain_name()
|
||||||
|
print('Domain: {}'.format(domain))
|
||||||
|
controller = get_domain_controller()
|
||||||
|
print('Controller: {}'.format(controller))
|
||||||
|
|
||||||
|
updater = gpupdate(args.user, domain, controller)
|
||||||
|
retrieved_gpos = updater.update()
|
||||||
|
|
||||||
|
sid = wbinfo_getsid(domain, args.user)
|
||||||
|
hreg_util = hreg('/var/cache/samba/gpo_cache', domain, retrieved_gpos, sid)
|
||||||
|
hreg_util.to_fs()
|
||||||
|
|
||||||
|
gpo_applier = gpoa(sid)
|
||||||
|
gpo_applier.generate_scripts()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
|
40
gpupdate.spec
Normal file
40
gpupdate.spec
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
%define _unpackaged_files_terminate_build 1
|
||||||
|
|
||||||
|
Name: gpupdate
|
||||||
|
Version: 0.0.1
|
||||||
|
Release: alt1
|
||||||
|
|
||||||
|
Summary: GPT applier
|
||||||
|
License: MIT
|
||||||
|
Group: Other
|
||||||
|
Url: http://git.altlinux.org/
|
||||||
|
BuildArch: noarch
|
||||||
|
|
||||||
|
Requires: hreg
|
||||||
|
Requires: control
|
||||||
|
Requires: samba-dc
|
||||||
|
|
||||||
|
Source0: %name-%version.tar
|
||||||
|
|
||||||
|
%description
|
||||||
|
GPT applier
|
||||||
|
|
||||||
|
%prep
|
||||||
|
%setup -q
|
||||||
|
|
||||||
|
%install
|
||||||
|
install -pD -m755 gpupdate \
|
||||||
|
%buildroot%_bindir/gpupdate
|
||||||
|
mkdir -p \
|
||||||
|
%buildroot%prefix/libexec
|
||||||
|
cp -r gpoa \
|
||||||
|
%buildroot%prefix/libexec
|
||||||
|
|
||||||
|
%files
|
||||||
|
%prefix/bin/gpupdate
|
||||||
|
%prefix/libexec/gpoa
|
||||||
|
|
||||||
|
%changelog
|
||||||
|
* Thu Nov 14 2019 Igor Chudov <nir@altlinux.org> 0.0.1-alt1
|
||||||
|
- Initial release
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user