From: David Mulder Date: Sat, 11 Feb 2017 14:53:07 +0000 (-0700) Subject: gpo: Make the gpoupdate script much more reliable X-Git-Tag: talloc-2.1.11~462 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=115615d836b3616f552d8e3df9984d3b60474d17;p=thirdparty%2Fsamba.git gpo: Make the gpoupdate script much more reliable Using a static file blanks the file when samba_gpoupdate crashes. Transformed to a tdb file and added transactions. Add info logging to monitor gpo changes, etc. Also handle parse errors and log an error message, then recover. Modified the parsing code to use ConfigParser. Also, use the backslash in path names when opening smb files, otherwise it fails against a windows server. Signed-off-by: David Mulder Reviewed-by: Garming Sam Reviewed-by: Andrew Bartlett --- diff --git a/python/samba/gpclass.py b/python/samba/gpclass.py index 304d670e432..0e8d74ad1c6 100644 --- a/python/samba/gpclass.py +++ b/python/samba/gpclass.py @@ -27,6 +27,8 @@ from samba.samdb import SamDB from samba.netcmd import gpo as gpo_user import codecs from samba import NTSTATUSError +from ConfigParser import ConfigParser +from StringIO import StringIO class gp_ext(object): def list(self, rootpath): @@ -42,22 +44,27 @@ class inf_to_ldb(object): parameter to Samba4. Not registry oriented whatsoever. ''' - def __init__(self, ldb, dn, attribute, val): + def __init__(self, logger, ldb, dn, attribute, val): + self.logger = logger self.ldb = ldb self.dn = dn self.attribute = attribute self.val = val def ch_minPwdAge(self, val): + self.logger.info('KDC Minimum Password age was changed from %s to %s' % (self.ldb.get_minPwdAge(), val)) self.ldb.set_minPwdAge(val) def ch_maxPwdAge(self, val): + self.logger.info('KDC Maximum Password age was changed from %s to %s' % (self.ldb.get_maxPwdAge(), val)) self.ldb.set_maxPwdAge(val) def ch_minPwdLength(self, val): + self.logger.info('KDC Minimum Password length was changed from %s to %s' % (self.ldb.get_minPwdLength(), val)) self.ldb.set_minPwdLength(val) def ch_pwdProperties(self, val): + self.logger.info('KDC Password Properties were changed from %s to %s' % (self.ldb.get_pwdProperties(), val)) self.ldb.set_pwdProperties(val) def explicit(self): @@ -95,6 +102,9 @@ class gp_sec_ext(gp_ext): count = 0 + def __init__(self, logger): + self.logger = logger + def __str__(self): return "Security GPO extension" @@ -122,7 +132,7 @@ class gp_sec_ext(gp_ext): ret = False inftable = self.populate_inf() - policy = conn.loadfile(path).decode('utf-16') + policy = conn.loadfile(path.replace('/', '\\')).decode('utf-16') current_section = None LOG = open(attr_log, "a") LOG.write(str(path.split('/')[2]) + '\n') @@ -133,23 +143,20 @@ class gp_sec_ext(gp_ext): # If at any point in time a GPO was applied, # then we return that boolean at the end. - for line in policy.splitlines(): - line = line.strip() - if line[0] == '[': - section = line[1: -1] - current_section = inftable.get(section.encode('ascii', 'ignore')) - - else: - # We must be in a section - if not current_section: - continue - (key, value) = line.split("=") - key = key.strip() + inf_conf = ConfigParser() + inf_conf.optionxform=str + inf_conf.readfp(StringIO(policy)) + + for section in inf_conf.sections(): + current_section = inftable.get(section) + if not current_section: + continue + for key, value in inf_conf.items(section): if current_section.get(key): (att, setter) = current_section.get(key) value = value.encode('ascii', 'ignore') ret = True - setter(self.ldb, self.dn, att, value).update_samba() + setter(self.logger, self.ldb, self.dn, att, value).update_samba() return ret def parse(self, afile, ldb, conn, attr_log): @@ -174,13 +181,10 @@ class gp_sec_ext(gp_ext): return None -def scan_log(sysvol_path): - a = open(sysvol_path, "r") +def scan_log(sysvol_tdb): data = {} - for line in a.readlines(): - line = line.strip() - (guid, version) = line.split(" ") - data[guid] = int(version) + for key in sysvol_tdb.iterkeys(): + data[key] = sysvol_tdb.get(key) return data diff --git a/source4/scripting/bin/samba_gpoupdate b/source4/scripting/bin/samba_gpoupdate index ba83dcf7e91..a5573cece26 100755 --- a/source4/scripting/bin/samba_gpoupdate +++ b/source4/scripting/bin/samba_gpoupdate @@ -26,6 +26,7 @@ import fcntl import sys import tempfile import subprocess +import tdb sys.path.insert(0, "bin/python") @@ -36,6 +37,7 @@ from samba.gpclass import * from samba.net import Net from samba.dcerpc import nbt from samba import smb +import logging # Finds all GPO Files ending in inf @@ -140,19 +142,6 @@ class GPOServiceSetup: return self.creds -def GetBackLog(sys_log): - """Reads BackLog and makes thread aware of which GPO are unchanged or empty - :param String sys_log: path to backLog - :return Dictionary previous_scanned_version: {Unedited GPO: Version Number} - *NOTE on Version below - """ - previous_scanned_version = {} - if os.path.isfile(sys_log): - previous_scanned_version = scan_log(sys_log) - return previous_scanned_version - else: - return None - # Set up the GPO service GPOService = GPOServiceSetup() GPOService.InitializeService() @@ -163,18 +152,35 @@ test_ldb = GPOService.Get_LDB() # Get The lp context lp = GPOService.Get_lp_Content() +# Set up logging +logger = logging.getLogger('samba_gpoupdate') +logger.addHandler(logging.StreamHandler(sys.stdout)) +logger.setLevel(logging.CRITICAL) +log_level = lp.log_level() +if log_level == 1: + logger.setLevel(logging.ERROR) +elif log_level == 2: + logger.setLevel(logging.WARNING) +elif log_level == 3: + logger.setLevel(logging.INFO) +elif log_level >= 4: + logger.setLevel(logging.DEBUG) + # Get the CREDS creds = GPOService.Get_Creds() # Read the readable backLog into a hashmap # then open writable backLog in same location BackLoggedGPO = None -sys_log = '%s/%s' % (lp.get("path", "sysvol"), 'syslog.txt') +sys_log = '%s/%s' % (lp.get("path", "sysvol"), 'gpo.tdb') attr_log = '%s/%s' % (lp.get("path", "sysvol"), 'attrlog.txt') -BackLoggedGPO = GetBackLog(sys_log) -BackLog = open(sys_log, "w") +if os.path.isfile(sys_log): + BackLog = tdb.open(sys_log) +else: + BackLog = tdb.Tdb(sys_log, 0, tdb.DEFAULT, os.O_CREAT|os.O_RDWR) +BackLoggedGPO = scan_log(BackLog) # We need to know writable DC to setup SMB connection @@ -215,13 +221,18 @@ if (GPO_Deleted): Reset_Defaults(test_ldb) GPO_Changed = False +BackLog.transaction_start() for guid_eval in hierarchy_gpos: guid = guid_eval[0] - gp_extensions = [gp_sec_ext()] + gp_extensions = [gp_sec_ext(logger)] local_path = '%s/Policies' % lp.get("realm").lower() + '/' + guid + '/' - version = gpo.gpo_get_sysvol_gpt_version(lp.get("path", "sysvol") + '/' + local_path)[1] + version = int(gpo.gpo_get_sysvol_gpt_version(lp.get("path", "sysvol") + '/' + local_path)[1]) + try: + old_version = int(BackLoggedGPO.get(guid)) + except: + old_version = -1 gpolist = gp_path_list(local_path) - if(version != BackLoggedGPO.get(guid)): + if version != old_version: GPO_Changed = True # If the GPO has a dn that is applicable to Samba if guid_eval[1]: @@ -230,6 +241,13 @@ for guid_eval in hierarchy_gpos: # If it we have not read it before and is not empty # Rewrite entire logfile here if (version != 0) and GPO_Changed == True: - change_backlog = gpo_parser(gpolist, test_ldb, conn, attr_log) + logger.info('GPO %s has changed' % guid) + try: + change_backlog = gpo_parser(gpolist, test_ldb, conn, attr_log) + except: + logger.error('Failed to parse gpo %s' % guid) + continue + BackLog.store(guid, '%i' % version) +BackLog.transaction_commit() +BackLog.close() - BackLog.write('%s %i\n' % (guid, version))