From c334cda32abb186b6729429f919e1b87c02b953c Mon Sep 17 00:00:00 2001 From: David Mulder Date: Wed, 13 Jun 2018 14:42:43 -0600 Subject: [PATCH 1/7] gpo: Disable python3 testing The gpo module doesn't work in python3 yet, causing this test to fail on python3. Signed-off-by: David Mulder --- source4/selftest/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index 069128b2e84..15e92bfb545 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -633,7 +633,7 @@ def planoldpythontestsuite(env, module, name=None, extra_path=[], environ={}, ex planpythontestsuite("ad_dc_ntvfs:local", "samba.tests.dcerpc.rpcecho", py3_compatible=True) planoldpythontestsuite("nt4_dc", "samba.tests.netbios", extra_args=['-U"$USERNAME%$PASSWORD"'], py3_compatible=True) -planoldpythontestsuite("ad_dc:local", "samba.tests.gpo", extra_args=['-U"$USERNAME%$PASSWORD"'], py3_compatible=True) +planoldpythontestsuite("ad_dc:local", "samba.tests.gpo", extra_args=['-U"$USERNAME%$PASSWORD"']) planoldpythontestsuite("ad_dc:local", "samba.tests.dckeytab", extra_args=['-U"$USERNAME%$PASSWORD"'], py3_compatible=True) planoldpythontestsuite("ad_dc:local", "samba.tests.smb", extra_args=['-U"$USERNAME%$PASSWORD"'], py3_compatible=True) From a48d2a5ef26eef1083fde3140be06745920dd20e Mon Sep 17 00:00:00 2001 From: David Mulder Date: Wed, 13 Jun 2018 14:45:09 -0600 Subject: [PATCH 2/7] gpo: add register_gp_extension for registering gp extensions Signed-off-by: David Mulder --- python/samba/gpclass.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/python/samba/gpclass.py b/python/samba/gpclass.py index 0966611b686..bee7bb522ab 100644 --- a/python/samba/gpclass.py +++ b/python/samba/gpclass.py @@ -29,6 +29,8 @@ from samba.dcerpc import nbt from samba import smb import samba.gpo as gpo +from samba.param import LoadParm +from uuid import UUID try: from enum import Enum @@ -479,3 +481,37 @@ def unapply_gp(lp, creds, test_ldb, logger, store, gp_extensions): gp_db.delete(str(attr_obj), attr[0]) gp_db.commit() +def register_gp_extension(guid, name, path, + smb_conf=None, machine=True, user=True): + # Check that the module exists + if not os.path.exists(path): + return False + # Check for valid guid + if not guid[0] == '{' or not guid[-1] == '}': + return False + try: + uuid = guid[1:-1] + UUID(uuid, version=4) + except: + return False + + lp = LoadParm() + if smb_conf is not None: + lp.load(smb_conf) + else: + lp.load_default() + ext_conf = lp.cache_path('gpext.conf') + parser = ConfigParser() + parser.read(ext_conf) + if not guid in parser.sections(): + parser.add_section(guid) + parser.set(guid, 'DllName', path) + parser.set(guid, 'ProcessGroupPolicy', name) + parser.set(guid, 'NoMachinePolicy', 0 if machine else 1) + parser.set(guid, 'NoUserPolicy', 0 if user else 1) + + f = open(ext_conf, 'w') + parser.write(f) + f.close() + + return True From 5a2c0d8eff42a50bbf23b57baff00a682c7c5645 Mon Sep 17 00:00:00 2001 From: David Mulder Date: Wed, 13 Jun 2018 14:46:05 -0600 Subject: [PATCH 3/7] gpo: add unregister_gp_extension for unregistering gp extensions Signed-off-by: David Mulder --- python/samba/gpclass.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/python/samba/gpclass.py b/python/samba/gpclass.py index bee7bb522ab..1cbbfe82c51 100644 --- a/python/samba/gpclass.py +++ b/python/samba/gpclass.py @@ -515,3 +515,31 @@ def register_gp_extension(guid, name, path, f.close() return True + +def unregister_gp_extension(guid, smb_conf=None): + # Check for valid guid + if not guid[0] == '{' or not guid[-1] == '}': + return False + try: + uuid = guid[1:-1] + UUID(uuid, version=4) + except: + return False + + lp = LoadParm() + if smb_conf is not None: + lp.load(smb_conf) + else: + lp.load_default() + ext_conf = lp.cache_path('gpext.conf') + parser = ConfigParser() + parser.read(ext_conf) + + if guid in parser.sections(): + parser.remove_section(guid) + + f = open(ext_conf, 'w') + parser.write(f) + f.close() + + return True From 99a61120ae4dd2d31b89651ef06a7eedabfded46 Mon Sep 17 00:00:00 2001 From: David Mulder Date: Wed, 13 Jun 2018 14:46:30 -0600 Subject: [PATCH 4/7] gpo: add list_gp_extensions for listing registered gp extensions Signed-off-by: David Mulder --- python/samba/gpclass.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/python/samba/gpclass.py b/python/samba/gpclass.py index 1cbbfe82c51..5b0dabcd56c 100644 --- a/python/samba/gpclass.py +++ b/python/samba/gpclass.py @@ -516,6 +516,27 @@ def register_gp_extension(guid, name, path, return True +def list_gp_extensions(smb_conf=None): + lp = LoadParm() + if smb_conf is not None: + lp.load(smb_conf) + else: + lp.load_default() + ext_conf = lp.cache_path('gpext.conf') + parser = ConfigParser() + parser.read(ext_conf) + + results = {} + for guid in parser.sections(): + results[guid] = {} + results[guid]['DllName'] = parser.get(guid, 'DllName') + results[guid]['ProcessGroupPolicy'] = \ + parser.get(guid, 'ProcessGroupPolicy') + results[guid]['MachinePolicy'] = \ + not int(parser.get(guid, 'NoMachinePolicy')) + results[guid]['UserPolicy'] = not int(parser.get(guid, 'NoUserPolicy')) + return results + def unregister_gp_extension(guid, smb_conf=None): # Check for valid guid if not guid[0] == '{' or not guid[-1] == '}': From 768b103f8aee6ac51352f7ba924b514efcd53ef1 Mon Sep 17 00:00:00 2001 From: David Mulder Date: Wed, 9 May 2018 09:24:37 -0600 Subject: [PATCH 5/7] gpo: Tests for gp_ext register/unregister Adds testing for the gp_ext register and unregister functions, as well as testing the list function. Signed-off-by: David Mulder --- python/samba/tests/gpo.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/python/samba/tests/gpo.py b/python/samba/tests/gpo.py index 796a5cb06cb..4d6a5891343 100644 --- a/python/samba/tests/gpo.py +++ b/python/samba/tests/gpo.py @@ -16,6 +16,8 @@ import os from samba import gpo, tests +from samba.gpclass import register_gp_extension, list_gp_extensions, \ + unregister_gp_extension from samba.param import LoadParm poldir = r'\\addom.samba.example.com\sysvol\addom.samba.example.com\Policies' @@ -75,3 +77,20 @@ def test_gpt_version(self): assert gpo.gpo_get_sysvol_gpt_version(gpo_path)[1] == old_vers, \ 'gpo_get_sysvol_gpt_version() did not return the expected version' + def test_gpt_ext_register(self): + ext_path = '/home/dmulder/code/samba/bin/python/samba/gp_sec_ext.py' + ext_guid = '{827D319E-6EAC-11D2-A4EA-00C04F79F83A}' + register_gp_extension(ext_guid, 'gp_sec_ext', ext_path, + smb_conf=self.lp.configfile, + machine=True, user=False) + gp_exts = list_gp_extensions(self.lp.configfile) + assert ext_guid in gp_exts.keys(), \ + 'Failed to list gp exts from registry' + assert gp_exts[ext_guid]['DllName'] == ext_path, \ + 'Failed to list gp exts from registry' + + unregister_gp_extension(ext_guid) + gp_exts = list_gp_extensions(self.lp.configfile) + assert ext_guid not in gp_exts.keys(), \ + 'Failed to unregister gp exts from registry' + From cac785159998e6a61e70ff64031106aa837e7ba0 Mon Sep 17 00:00:00 2001 From: David Mulder Date: Fri, 4 May 2018 13:38:44 -0600 Subject: [PATCH 6/7] gpo: Dynamically load gp_exts This loads Group Policy Client Side Extensions similar to the way that they are loaded on a Windows client. Extensions are installed to a configuration file in the samba cache path where they receive a unique GUID matched with the path to the python gp_ext file. Classes which inherit from the gp_ext class (as defined in gpclass.py) will be dynamically loaded. Signed-off-by: David Mulder --- python/samba/gp_ext_loader.py | 56 +++++++++++++++++++++++++++++++++++ source4/scripting/bin/samba_gpoupdate | 4 +++ 2 files changed, 60 insertions(+) create mode 100644 python/samba/gp_ext_loader.py diff --git a/python/samba/gp_ext_loader.py b/python/samba/gp_ext_loader.py new file mode 100644 index 00000000000..7a16641e7cc --- /dev/null +++ b/python/samba/gp_ext_loader.py @@ -0,0 +1,56 @@ +# Group Policy Client Side Extension Loader +# Copyright (C) David Mulder 2018 +# +# 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 +from samba.gpclass import list_gp_extensions + +try: + import importlib.util + def import_file(name, location): + try: + spec = importlib.util.spec_from_file_location(name, location) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + except AttributeError: + from importlib.machinery import SourceFileLoader + module = SourceFileLoader(name, location).load_module() + return module +except ImportError: + import imp + def import_file(name, location): + return imp.load_source(name, location) + +def get_gp_ext_from_module(name, mod): + import inspect + if mod: + clses = inspect.getmembers(mod, inspect.isclass) + for cls in clses: + if cls[-1].__name__ == name: + return cls[-1] + return None + +def get_gp_client_side_extensions(logger, smb_conf): + machine_exts = [] + gp_exts = list_gp_extensions(smb_conf) + for gp_ext in gp_exts.values(): + module = import_file(gp_ext['ProcessGroupPolicy'], gp_ext['DllName']) + ext = get_gp_ext_from_module(gp_ext['ProcessGroupPolicy'], module) + if ext and gp_ext['MachinePolicy']: + machine_exts.append(ext) + logger.info('Loaded machine extension from %s: %s' + % (gp_ext['DllName'], ext.__name__)) + return machine_exts + diff --git a/source4/scripting/bin/samba_gpoupdate b/source4/scripting/bin/samba_gpoupdate index 89b3ed77616..b459bdac31c 100755 --- a/source4/scripting/bin/samba_gpoupdate +++ b/source4/scripting/bin/samba_gpoupdate @@ -36,6 +36,7 @@ except: SamDB = None from samba.gpclass import apply_gp, unapply_gp, GPOStorage from samba.gp_sec_ext import gp_sec_ext +from samba.gp_ext_loader import get_gp_client_side_extensions import logging if __name__ == "__main__": @@ -84,10 +85,13 @@ if __name__ == "__main__": cache_dir = lp.get('cache directory') store = GPOStorage(os.path.join(cache_dir, 'gpo.tdb')) + machine_exts = get_gp_client_side_extensions(logger, lp.configfile) gp_extensions = [] if opts.machine: if lp.get('server role') == 'active directory domain controller': gp_extensions.append(gp_sec_ext(logger)) + for ext in machine_exts: + gp_extensions.append(ext(logger)) else: pass # User extensions From 581b8358d45076844fae1c61b63c65e9104cdeaf Mon Sep 17 00:00:00 2001 From: David Mulder Date: Fri, 4 May 2018 13:25:25 -0600 Subject: [PATCH 7/7] gpo: Add user policy extensions Signed-off-by: David Mulder --- python/samba/gp_ext_loader.py | 7 ++++++- python/samba/gpclass.py | 7 ++++++- source4/scripting/bin/samba_gpoupdate | 6 ++++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/python/samba/gp_ext_loader.py b/python/samba/gp_ext_loader.py index 7a16641e7cc..32c3517ebda 100644 --- a/python/samba/gp_ext_loader.py +++ b/python/samba/gp_ext_loader.py @@ -43,6 +43,7 @@ def get_gp_ext_from_module(name, mod): return None def get_gp_client_side_extensions(logger, smb_conf): + user_exts = [] machine_exts = [] gp_exts = list_gp_extensions(smb_conf) for gp_ext in gp_exts.values(): @@ -52,5 +53,9 @@ def get_gp_client_side_extensions(logger, smb_conf): machine_exts.append(ext) logger.info('Loaded machine extension from %s: %s' % (gp_ext['DllName'], ext.__name__)) - return machine_exts + if ext and gp_ext['UserPolicy']: + user_exts.append(ext) + logger.info('Loaded user extension from %s: %s' + % (gp_ext['DllName'], ext.__name__)) + return (machine_exts, user_exts) diff --git a/python/samba/gpclass.py b/python/samba/gpclass.py index 5b0dabcd56c..4189c7cc2f7 100644 --- a/python/samba/gpclass.py +++ b/python/samba/gpclass.py @@ -315,7 +315,12 @@ def parse(self, afile, ldb, conn, gp_db, lp): # Fixing the bug where only some Linux Boxes capitalize MACHINE try: blist = afile.split('/') - idx = afile.lower().split('/').index('machine') + index = None + if 'machine' in afile.lower(): + index = 'machine' + elif 'user' in afile.lower(): + index = 'user' + idx = afile.lower().split('/').index(index) for case in [ blist[idx].upper(), blist[idx].capitalize(), diff --git a/source4/scripting/bin/samba_gpoupdate b/source4/scripting/bin/samba_gpoupdate index b459bdac31c..bb5c0b54e82 100755 --- a/source4/scripting/bin/samba_gpoupdate +++ b/source4/scripting/bin/samba_gpoupdate @@ -85,7 +85,8 @@ if __name__ == "__main__": cache_dir = lp.get('cache directory') store = GPOStorage(os.path.join(cache_dir, 'gpo.tdb')) - machine_exts = get_gp_client_side_extensions(logger, lp.configfile) + machine_exts, user_exts = get_gp_client_side_extensions(logger, + lp.configfile) gp_extensions = [] if opts.machine: if lp.get('server role') == 'active directory domain controller': @@ -93,7 +94,8 @@ if __name__ == "__main__": for ext in machine_exts: gp_extensions.append(ext(logger)) else: - pass # User extensions + for ext in user_exts: + gp_extensions.append(ext(logger)) # Get a live instance of Samba if SamDB: