From 6d676cac41d0f84d5396a67bd445ef8cfd4b8e0c Mon Sep 17 00:00:00 2001 From: David Mulder Date: Tue, 20 Jul 2021 09:13:06 -0600 Subject: [PATCH] gpo: Enable user policy application Signed-off-by: David Mulder Reviewed-by: Jeremy Allison --- python/samba/gpclass.py | 85 ++++++++++++++++++++++------ python/samba/tests/gpo.py | 54 ++++++++++++------ python/samba/tests/gpo_member.py | 3 +- source4/scripting/bin/samba-gpupdate | 18 +++++- 4 files changed, 122 insertions(+), 38 deletions(-) diff --git a/python/samba/gpclass.py b/python/samba/gpclass.py index 17d7d0c9243..1b8f825e47b 100644 --- a/python/samba/gpclass.py +++ b/python/samba/gpclass.py @@ -19,6 +19,7 @@ import sys import os, shutil import errno import tdb +import pwd sys.path.insert(0, "bin/python") from samba import NTSTATUSError from configparser import ConfigParser @@ -294,11 +295,12 @@ class GPOStorage: class gp_ext(object): __metaclass__ = ABCMeta - def __init__(self, logger, lp, creds, store): + def __init__(self, logger, lp, creds, username, store): self.logger = logger self.lp = lp self.creds = creds - self.gp_db = store.get_gplog(creds.get_username()) + self.username = username + self.gp_db = store.get_gplog(username) @abstractmethod def process_group_policy(self, deleted_gpo_list, changed_gpo_list): @@ -364,11 +366,12 @@ def get_dc_hostname(creds, lp): ''' Fetch a list of GUIDs for applicable GPOs ''' -def get_gpo_list(dc_hostname, creds, lp): +def get_gpo_list(dc_hostname, creds, lp, username): gpos = [] ads = gpo.ADS_STRUCT(dc_hostname, lp, creds) if ads.connect(): - gpos = ads.get_gpo_list(creds.get_username()) + # username is DOM\\SAM, but get_gpo_list expects SAM + gpos = ads.get_gpo_list(username.split('\\')[-1]) return gpos @@ -433,10 +436,10 @@ def gpo_version(lp, path): return int(gpo.gpo_get_sysvol_gpt_version(gpt_path)[1]) -def apply_gp(lp, creds, logger, store, gp_extensions, force=False): - gp_db = store.get_gplog(creds.get_username()) +def apply_gp(lp, creds, logger, store, gp_extensions, username, target, force=False): + gp_db = store.get_gplog(username) dc_hostname = get_dc_hostname(creds, lp) - gpos = get_gpo_list(dc_hostname, creds, lp) + gpos = get_gpo_list(dc_hostname, creds, lp, username) del_gpos = get_deleted_gpos_list(gp_db, gpos) try: check_refresh_gpo_list(dc_hostname, lp, creds, gpos) @@ -464,8 +467,12 @@ def apply_gp(lp, creds, logger, store, gp_extensions, force=False): store.start() for ext in gp_extensions: try: - ext = ext(logger, lp, creds, store) - ext.process_group_policy(del_gpos, changed_gpos) + ext = ext(logger, lp, creds, username, store) + if target == 'Computer': + ext.process_group_policy(del_gpos, changed_gpos) + else: + drop_privileges(creds.get_principal(), ext.process_group_policy, + del_gpos, changed_gpos) except Exception as e: logger.error('Failed to apply extension %s' % str(ext)) logger.error('Message was: %s: %s' % (type(e).__name__, str(e))) @@ -481,16 +488,20 @@ def apply_gp(lp, creds, logger, store, gp_extensions, force=False): store.commit() -def unapply_gp(lp, creds, logger, store, gp_extensions): - gp_db = store.get_gplog(creds.get_username()) +def unapply_gp(lp, creds, logger, store, gp_extensions, username, target): + gp_db = store.get_gplog(username) gp_db.state(GPOSTATE.UNAPPLY) # Treat all applied gpos as deleted del_gpos = gp_db.get_applied_settings(gp_db.get_applied_guids()) store.start() for ext in gp_extensions: try: - ext = ext(logger, lp, creds, store) - ext.process_group_policy(del_gpos, []) + ext = ext(logger, lp, creds, username, store) + if target == 'Computer': + ext.process_group_policy(del_gpos, []) + else: + drop_privileges(username, ext.process_group_policy, + del_gpos, []) except Exception as e: logger.error('Failed to unapply extension %s' % str(ext)) logger.error('Message was: ' + str(e)) @@ -509,9 +520,9 @@ def __rsop_vals(vals, level=4): else: return vals -def rsop(lp, creds, logger, store, gp_extensions, target): +def rsop(lp, creds, logger, store, gp_extensions, username, target): dc_hostname = get_dc_hostname(creds, lp) - gpos = get_gpo_list(dc_hostname, creds, lp) + gpos = get_gpo_list(dc_hostname, creds, lp, username) check_refresh_gpo_list(dc_hostname, lp, creds, gpos) print('Resultant Set of Policy') @@ -523,7 +534,7 @@ def rsop(lp, creds, logger, store, gp_extensions, target): print('GPO: %s' % gpo.display_name) print('='*term_width) for ext in gp_extensions: - ext = ext(logger, lp, creds, store) + ext = ext(logger, lp, creds, username, store) cse_name_m = re.findall("'([\w\.]+)'", str(type(ext))) if len(cse_name_m) > 0: cse_name = cse_name_m[-1].split('.')[-1] @@ -616,3 +627,45 @@ def unregister_gp_extension(guid, smb_conf=None): atomic_write_conf(lp, parser) return True + + +def set_privileges(username, uid, gid): + ''' + Set current process privileges + ''' + + os.setegid(gid) + os.seteuid(uid) + + +def drop_privileges(username, func, *args): + ''' + Run supplied function with privileges for specified username. + ''' + current_uid = os.getuid() + + if not current_uid == 0: + raise Exception('Not enough permissions to drop privileges') + + user_uid = pwd.getpwnam(username).pw_uid + user_gid = pwd.getpwnam(username).pw_gid + + # Drop privileges + set_privileges(username, user_uid, user_gid) + + # We need to catch exception in order to be able to restore + # privileges later in this function + out = None + exc = None + try: + out = func(*args) + except Exception as e: + exc = e + + # Restore privileges + set_privileges('root', current_uid, 0) + + if exc: + raise exc + + return out diff --git a/python/samba/tests/gpo.py b/python/samba/tests/gpo.py index b5dc09543ad..52510e50595 100644 --- a/python/samba/tests/gpo.py +++ b/python/samba/tests/gpo.py @@ -478,7 +478,8 @@ class GPOTests(tests.TestCase): machine_creds.set_machine_account() # Initialize the group policy extension - ext = gp_krb_ext(logger, self.lp, machine_creds, store) + ext = gp_krb_ext(logger, self.lp, machine_creds, + machine_creds.get_username(), store) ads = gpo.ADS_STRUCT(self.server, self.lp, machine_creds) if ads.connect(): @@ -532,7 +533,8 @@ class GPOTests(tests.TestCase): machine_creds.set_machine_account() # Initialize the group policy extension - ext = gp_scripts_ext(logger, self.lp, machine_creds, store) + ext = gp_scripts_ext(logger, self.lp, machine_creds, + machine_creds.get_username(), store) ads = gpo.ADS_STRUCT(self.server, self.lp, machine_creds) if ads.connect(): @@ -590,7 +592,8 @@ class GPOTests(tests.TestCase): machine_creds.set_machine_account() # Initialize the group policy extension - ext = gp_sudoers_ext(logger, self.lp, machine_creds, store) + ext = gp_sudoers_ext(logger, self.lp, machine_creds, + machine_creds.get_username(), store) ads = gpo.ADS_STRUCT(self.server, self.lp, machine_creds) if ads.connect(): @@ -641,7 +644,8 @@ class GPOTests(tests.TestCase): machine_creds.set_machine_account() # Initialize the group policy extension - ext = vgp_sudoers_ext(logger, self.lp, machine_creds, store) + ext = vgp_sudoers_ext(logger, self.lp, machine_creds, + machine_creds.get_username(), store) ads = gpo.ADS_STRUCT(self.server, self.lp, machine_creds) if ads.connect(): @@ -734,7 +738,8 @@ class GPOTests(tests.TestCase): machine_creds.guess(self.lp) machine_creds.set_machine_account() - ext = gp_inf_ext(logger, self.lp, machine_creds, store) + ext = gp_inf_ext(logger, self.lp, machine_creds, + machine_creds.get_username(), store) test_data = '[Kerberos Policy]\nMaxTicketAge = 99\n' with NamedTemporaryFile() as f: @@ -819,7 +824,8 @@ class GPOTests(tests.TestCase): self.assertTrue(ret, 'Could not create the target %s' % (reg_pol % g.name)) for ext in gp_extensions: - ext = ext(logger, self.lp, machine_creds, store) + ext = ext(logger, self.lp, machine_creds, + machine_creds.get_username(), store) ret = ext.rsop(g) self.assertEquals(len(ret.keys()), 1, 'A single policy should have been displayed') @@ -918,7 +924,8 @@ class GPOTests(tests.TestCase): remove = [] with TemporaryDirectory() as dname: for ext in gp_extensions: - ext = ext(logger, self.lp, machine_creds, store) + ext = ext(logger, self.lp, machine_creds, + machine_creds.get_username(), store) if type(ext) == gp_krb_ext: ext.process_group_policy([], gpos) ret = store.get_int('kdc:user_ticket_lifetime') @@ -994,7 +1001,8 @@ class GPOTests(tests.TestCase): lp = LoadParm(f.name) # Initialize the group policy extension - ext = gp_smb_conf_ext(logger, lp, machine_creds, store) + ext = gp_smb_conf_ext(logger, lp, machine_creds, + machine_creds.get_username(), store) ext.process_group_policy([], gpos) lp = LoadParm(f.name) @@ -1041,7 +1049,8 @@ class GPOTests(tests.TestCase): machine_creds.set_machine_account() # Initialize the group policy extension - ext = gp_msgs_ext(logger, self.lp, machine_creds, store) + ext = gp_msgs_ext(logger, self.lp, machine_creds, + machine_creds.get_username(), store) ads = gpo.ADS_STRUCT(self.server, self.lp, machine_creds) if ads.connect(): @@ -1104,7 +1113,8 @@ class GPOTests(tests.TestCase): machine_creds.set_machine_account() # Initialize the group policy extension - ext = vgp_symlink_ext(logger, self.lp, machine_creds, store) + ext = vgp_symlink_ext(logger, self.lp, machine_creds, + machine_creds.get_username(), store) ads = gpo.ADS_STRUCT(self.server, self.lp, machine_creds) if ads.connect(): @@ -1181,7 +1191,8 @@ class GPOTests(tests.TestCase): machine_creds.set_machine_account() # Initialize the group policy extension - ext = vgp_files_ext(logger, self.lp, machine_creds, store) + ext = vgp_files_ext(logger, self.lp, machine_creds, + machine_creds.get_username(), store) ads = gpo.ADS_STRUCT(self.server, self.lp, machine_creds) if ads.connect(): @@ -1265,7 +1276,8 @@ class GPOTests(tests.TestCase): machine_creds.set_machine_account() # Initialize the group policy extension - ext = vgp_openssh_ext(logger, self.lp, machine_creds, store) + ext = vgp_openssh_ext(logger, self.lp, machine_creds, + machine_creds.get_username(), store) ads = gpo.ADS_STRUCT(self.server, self.lp, machine_creds) if ads.connect(): @@ -1335,7 +1347,8 @@ class GPOTests(tests.TestCase): machine_creds.set_machine_account() # Initialize the group policy extension - ext = vgp_startup_scripts_ext(logger, self.lp, machine_creds, store) + ext = vgp_startup_scripts_ext(logger, self.lp, machine_creds, + machine_creds.get_username(), store) ads = gpo.ADS_STRUCT(self.server, self.lp, machine_creds) if ads.connect(): @@ -1451,7 +1464,8 @@ class GPOTests(tests.TestCase): machine_creds.set_machine_account() # Initialize the group policy extension - ext = vgp_motd_ext(logger, self.lp, machine_creds, store) + ext = vgp_motd_ext(logger, self.lp, machine_creds, + machine_creds.get_username(), store) ads = gpo.ADS_STRUCT(self.server, self.lp, machine_creds) if ads.connect(): @@ -1500,7 +1514,8 @@ class GPOTests(tests.TestCase): machine_creds.set_machine_account() # Initialize the group policy extension - ext = vgp_issue_ext(logger, self.lp, machine_creds, store) + ext = vgp_issue_ext(logger, self.lp, machine_creds, + machine_creds.get_username(), store) ads = gpo.ADS_STRUCT(self.server, self.lp, machine_creds) if ads.connect(): @@ -1551,7 +1566,8 @@ class GPOTests(tests.TestCase): machine_creds.set_machine_account() # Initialize the group policy extension - ext = vgp_access_ext(logger, self.lp, machine_creds, store) + ext = vgp_access_ext(logger, self.lp, machine_creds, + machine_creds.get_username(), store) ads = gpo.ADS_STRUCT(self.server, self.lp, machine_creds) if ads.connect(): @@ -1679,7 +1695,8 @@ class GPOTests(tests.TestCase): machine_creds.set_machine_account() # Initialize the group policy extension - ext = gp_gnome_settings_ext(logger, self.lp, machine_creds, store) + ext = gp_gnome_settings_ext(logger, self.lp, machine_creds, + machine_creds.get_username(), store) ads = gpo.ADS_STRUCT(self.server, self.lp, machine_creds) if ads.connect(): @@ -1901,7 +1918,8 @@ class GPOTests(tests.TestCase): machine_creds.set_machine_account() # Initialize the group policy extension - ext = gp_cert_auto_enroll_ext(logger, self.lp, machine_creds, store) + ext = gp_cert_auto_enroll_ext(logger, self.lp, machine_creds, + machine_creds.get_username(), store) ads = gpo.ADS_STRUCT(self.server, self.lp, machine_creds) if ads.connect(): diff --git a/python/samba/tests/gpo_member.py b/python/samba/tests/gpo_member.py index 3d614f53d04..18e8cb9715f 100644 --- a/python/samba/tests/gpo_member.py +++ b/python/samba/tests/gpo_member.py @@ -39,6 +39,7 @@ class GPOTests(tests.TestCase): cache_dir = self.lp.get('cache directory') store = GPOStorage(os.path.join(cache_dir, 'gpo.tdb')) try: - gp_access_ext(logger, self.lp, self.creds, store) + gp_access_ext(logger, self.lp, self.creds, + self.creds.get_username(), store) except Exception: self.fail('Initializing gp_access_ext should not require ad-dc') diff --git a/source4/scripting/bin/samba-gpupdate b/source4/scripting/bin/samba-gpupdate index df784e7744a..4d13f9dae10 100755 --- a/source4/scripting/bin/samba-gpupdate +++ b/source4/scripting/bin/samba-gpupdate @@ -46,6 +46,7 @@ from samba.vgp_startup_scripts_ext import vgp_startup_scripts_ext from samba.vgp_access_ext import vgp_access_ext from samba.gp_gnome_settings_ext import gp_gnome_settings_ext from samba.gp_cert_auto_enroll_ext import gp_cert_auto_enroll_ext +from samba.credentials import Credentials import logging if __name__ == "__main__": @@ -73,6 +74,15 @@ if __name__ == "__main__": lp = sambaopts.get_loadparm() creds = credopts.get_credentials(lp, fallback_machine=True) + # Apply policy to the command line specified user + if opts.target == 'Computer': + username = creds.get_username() + elif opts.target == 'User': + username = '%s\\%s' % (creds.get_domain(), creds.get_username()) + # Always supply the machine creds for fetching the gpo list + creds = Credentials() + creds.guess(lp) + creds.set_machine_account(lp) # Set up logging logger = logging.getLogger('samba-gpupdate') @@ -116,9 +126,11 @@ if __name__ == "__main__": gp_extensions.extend(user_exts) if opts.rsop: - rsop(lp, creds, logger, store, gp_extensions, opts.target) + rsop(lp, creds, logger, store, gp_extensions, username, opts.target) elif not opts.unapply: - apply_gp(lp, creds, logger, store, gp_extensions, opts.force) + apply_gp(lp, creds, logger, store, gp_extensions, username, + opts.target, opts.force) else: - unapply_gp(lp, creds, logger, store, gp_extensions) + unapply_gp(lp, creds, logger, store, gp_extensions, username, + opts.target) -- 2.34.1