[PATCH 26/26] Added the cases where GPO is deleted from OU or altogether, modularized, and fine tuned hierarchy order

abartlet at samba.org abartlet at samba.org
Thu Jun 5 04:07:31 MDT 2014


From: Luke Morrison <luc785 at hotmail.com>

---
 python/samba/gpclass.py               |  54 ++++++++------
 source4/scripting/bin/samba_gpoupdate | 137 ++++++++++++++++++++++++++++------
 2 files changed, 148 insertions(+), 43 deletions(-)

diff --git a/python/samba/gpclass.py b/python/samba/gpclass.py
index c728045..714ebfc 100755
--- a/python/samba/gpclass.py
+++ b/python/samba/gpclass.py
@@ -45,24 +45,16 @@ class inf_to_ldb(object):
         self.val = val
 
     def ch_minPwdAge(self, val):
-        #print 'Old value of Minimum Password age = %s' % self.ldb.get_minPwdAge()
         self.ldb.set_minPwdAge(val)
-        #print 'New value of Minimum Password age = %s' % self.ldb.get_minPwdAge()
 
     def ch_maxPwdAge(self, val):
-        #print 'Old value of Maximum Password age = %s' % self.ldb.get_maxPwdAge()
         self.ldb.set_maxPwdAge(val)
-        #print 'New value of Maximum Password age = %s' % self.ldb.get_maxPwdAge()
 
     def ch_minPwdLength(self, val):
-        #print 'Password Min length before is %s ' % ldb.get_minPwdLength()
         self.ldb.set_minPwdLength(val)
-        #print 'Password Min length after is %s ' % ldb.get_minPwdLength()
 
     def ch_pwdProperties(self, val):
-        #print 'Old value of Minimum Password age = %s' % self.ldb.get_minPwdAge()
         self.ldb.set_pwdProperties(val)
-        #print 'New value of Minimum Password age = %s' % self.ldb.get_minPwdAge()
 
     def explicit(self):
         return self.val
@@ -115,20 +107,25 @@ class gp_sec_ext(gp_ext):
                                   "PasswordComplexity": ("pwdProperties", inf_to_ldb),
                                  }
                }
-#FIXME. EACH gpo should have a parser, and a creater. Essentially a gpo is just a file. Possibly a method and class to link it to organization unit (if that already does not exist) so that GPO's can be created arithmetically, possibly with a hashtable for certain GPO, then linked if desired. Also could store a backup folder of gpo's and then configure them without necessarily deploying it.
 
     def read_inf(self, path, conn, attr_log):
+        ret = False
         inftable = self.populate_inf()
+
 	try:
             policy = conn.loadfile(path).decode('utf-16')
         except:
             return None
         current_section = None
-        print attr_log
-        #LOG2 = open(attr_log, "w")
         LOG = open(attr_log, "a")
-        print path.split('/')[2]
         LOG.write(str(path.split('/')[2]) + '\n')
+        '''
+        So here we would declare a boolean
+        that would get changed to TRUE
+        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] == '[':
@@ -144,27 +141,28 @@ class gp_sec_ext(gp_ext):
                 if current_section.get(key):
                     (att, setter) = current_section.get(key)
                     value = value.encode('ascii', 'ignore')
-                    #so value is the value that it contains, and the att is the attribute
-                    LOG.write(att + ' ' + value + '\n')
-                    #copy and paste this logic to backwalk deleted GPO
+                    ret = True
                     setter(self.ldb, self.dn, att, value).update_samba()
+	return ret
 
     def parse(self, afile, ldb, conn, attr_log):
         self.ldb = ldb
         self.dn = ldb.get_default_basedn()
+        ret = False
         #Fixing the bug where only some Linux Boxes Capitalize MACHINE
         blist = afile.split('/')
 
         bfile = blist[0] + '/' + blist[1] + '/' + 'Machine' + '/Microsoft/Windows NT/SecEdit/GptTmpl.inf'
         if bfile.endswith('inf'):
-            self.read_inf(bfile, conn, attr_log)
+            ret = self.read_inf(bfile, conn, attr_log)
 
         bfile = blist[0] + '/' + blist[1] + '/' + 'machine' + '/Microsoft/Windows NT/SecEdit/GptTmpl.inf'
         if bfile.endswith('inf'):
-            self.read_inf(bfile, conn, attr_log)
+            ret = self.read_inf(bfile, conn, attr_log)
 
         if afile.endswith('inf'):
-            self.read_inf(afile, conn, attr_log)
+            ret = self.read_inf(afile, conn, attr_log)
+        return ret
 
 def scan_log(sysvol_path):
     a = open(sysvol_path, "r")
@@ -175,6 +173,18 @@ def scan_log(sysvol_path):
         data[guid] = int(version)
     return data
 
+def Reset_Defaults(test_ldb):
+	test_ldb.set_minPwdAge(str(-25920000000000))
+	test_ldb.set_maxPwdAge(str(-38016000000000))
+	test_ldb.set_minPwdLength(str(7))
+	test_ldb.set_pwdProperties(str(1))
+
+def check_deleted(guid_list, backloggpo):
+	for guid in backloggpo:
+		if guid not in guid_list:
+			return True
+	return False
+
 ########################################################################################################################################
 '''The hierarchy is as per MS http://msdn.microsoft.com/en-us/library/windows/desktop/aa374155%28v=vs.85%29.aspx. It does not care about local GPO, because GPO and snap ins are not made in Linux yet. It follows the linking order and children GPO are last written format. Also, couple further testing with call scripts entitled informant and informant2 that show the explicit returned hierarchically sorted list'''
 
@@ -198,7 +208,6 @@ def sort_linked(SAMDB, guid_list, start, end):
         if right_container.get('dn') == guid_list[start][2]:
             break
     gplink = str(right_container.get('gPLink'))
-    #print str(right_container.get('gPLink'))
     gplink_split = gplink.split('[')
     linked_order = []
     ret_list = []
@@ -257,14 +266,15 @@ def establish_hierarchy(SamDB, GUID_LIST, DC_OU, global_dn):
     unapplied_gpo = []
     '''Sorted by container'''
     sorted_gpo_list = []
-    '''Since the unapplied GPO are put at the front of the list, just once again append them to the linked container sorted list'''
-    while count < indexed_places[0]:
+    '''Unapplied GPO live at start of list, append them to final list'''
+    while final_list[0][1] == False:
         unapplied_gpo.append(final_list[count])
         count += 1
     count = 0
     sorted_gpo_list += unapplied_gpo
     '''A single container call gets the linked order for all GPO in container. So we need one call per container - > index of the Original list'''
+    indexed_places.insert(0,0)
     while count < (len(indexed_places)-1):
-        sorted_gpo_list += (sort_linked(SamDB, final_list, indexed_places[count], indexed_places[count + 1]))
+        sorted_gpo_list += (sort_linked(SamDB, final_list, indexed_places[count], indexed_places[count+1]))
         count += 1
     return sorted_gpo_list
diff --git a/source4/scripting/bin/samba_gpoupdate b/source4/scripting/bin/samba_gpoupdate
index d41d8e5..04884fd 100755
--- a/source4/scripting/bin/samba_gpoupdate
+++ b/source4/scripting/bin/samba_gpoupdate
@@ -42,11 +42,21 @@ def gp_path_list(path):
 no return except a newly updated Samba
 '''
 def gpo_parser(GPO_LIST, ldb, conn, attr_log):
+	ret = False
+	for entry in GPO_LIST:
+		(ext, thefile) = entry
+		if ret == False:
+			ret = ext.parse(thefile, ldb, conn, attr_log)
+		else:
+			temp = ext.parse(thefile, ldb, conn, attr_log)
+	return ret
+'''
+def gpo_parser(GPO_LIST, ldb, conn, attr_log):
 
     for entry in GPO_LIST:
         (ext, thefile) = entry
         ext.parse(thefile, ldb, conn, attr_log)
-
+'''
 class GPOServiceSetup:
 	def __init__(self):
 		"""Initialize all components necessary to return instances of
@@ -61,7 +71,7 @@ class GPOServiceSetup:
 		self.creds = None
 		self.url = None
 
-	#Initializers
+	#Setters or Initializers
 	def init_parser(self):
 		'''Get the command line options'''
 		self.parser.add_option_group(self.sambaopts)
@@ -147,6 +157,8 @@ BackLoggedGPO = None
 sys_log = '%s/%s' % (lp.get("path", "sysvol"), 'syslog.txt')
 attr_log = '%s/%s' % (lp.get("path", "sysvol"), 'attrlog.txt')
 BackLoggedGPO  = GetBackLog(sys_log)
+
+
 BackLog = open(sys_log, "w")
 
 
@@ -172,43 +184,126 @@ DC_OU = "OU=Domain Controllers" + ',' + global_dn
 
 #Set up a List of the GUID for all GPO's
 guid_list = [x['name'] for x in conn.list('%s/Policies' % lp.get("realm").lower() )]
+SYSV_PATH = '%s/%s/%s' % (lp.get("path", "sysvol"), lp.get("realm"), 'Policies')
 
-#Power through the Microsoft Hierarchy with Linking Order included
-#FIXME could be SO much more efficient if we get the dn if the DC, then use that to get all its containers
-#Then use the acquired containers of DC dn, to get all GPO in a convenient linking order with linking order already taken into account
 hierarchy_gpos = establish_hierarchy(test_ldb, guid_list, DC_OU, global_dn)
-
-#At this point we have a list of hierarchically applied GPOS
-#Say for example a GPO was deleted, we can either
-#1)Reset ALL Defaults and then call script to apply all GPO
-#2)Find which attribute, fall back on previous attribute. THat way when we call this script, it only still applies the GPO that has changed
-#2 is more favorable because we are minimally changing Samba
+change_backlog = False
+
+#Take a local list of all current GPO list and run it against previous GPO's to see if something has changed. If so reset default and re-apply GPO
+Applicable_GPO = []
+for i in hierarchy_gpos:
+	Applicable_GPO += i
+
+#Flag gets set when
+GPO_Changed = False
+GPO_Deleted = check_deleted(Applicable_GPO, BackLoggedGPO)
+if (GPO_Deleted):
+	#Null the backlog
+	BackLoggedGPO = {}
+	#Reset Defaults then overwrite them
+	Reset_Defaults(test_ldb)
+	GPO_Changed = False
 
 for guid_eval in hierarchy_gpos:
 	guid = guid_eval[0]
 	gp_extensions = [gp_sec_ext()]
 	local_path = '%s/Policies' % lp.get("realm").lower() + '/' + guid + '/'
 	version = gpo.gpo_get_sysvol_gpt_version(lp.get("path", "sysvol") + '/' + local_path)[1]
-
 	gpolist = gp_path_list(local_path)
-
-
-	'''If an important GPO parse it. Will not parse if it has not changed, is empty, or is not in the right container'''
+	if(version != BackLoggedGPO.get(guid)):
+		GPO_Changed = True
 	#If the GPO has a dn that is applicable to Samba
 	if guid_eval[1]:
 		#If it has a GPO file that could apply to Samba
 	    if gpolist[0][1]:
 		#If it we have not read it before and is not empty
-	        #We need to rewrite
-	        if (version != BackLoggedGPO.get(guid)) and (version != 0):
-	            print ('GPO %s has changed' % guid)
-	            #Apply to Samba
-	            gpo_parser(gpolist, test_ldb, conn, attr_log)
+	        #Rewrite entire logfile here
+	        if  (version != 0) and GPO_Changed == True:
+	            change_backlog = gpo_parser(gpolist, test_ldb, conn, attr_log)
 
 	BackLog.write('%s %i\n' % (guid,version))
 
 
+
+'''
+def ResetAttributeLog(gpolist, hierarchy_gpos, attr_log):
+	log = open(attr_log, "w")
+
+	for guid_eval in hierarchy_gpos:
+		guid = guid_eval[0]
+		gp_extensions = [gp_sec_ext()]
+		local_path = '%s/Policies' % lp.get("realm").lower() + '/' + guid + '/'
+		version = gpo.gpo_get_sysvol_gpt_version(lp.get("path", "sysvol") + '/' + local_path)[1]
+
+		gpolist = gp_path_list(local_path)
+
+		#If the GPO has a dn that is applicable to Samba
+		if guid_eval[1]:
+			#If it has a GPO file that could apply to Samba
+			if gpolist[0][1]:
+				#If it we have not read it before and is not empty
+			    #Rewrite entire logfile here
+			    BackLogger(gpolist,
+def gpo_player(GPO_LIST, ldb, conn, attr_log):
+	ret = False
+	for entry in GPO_LIST:
+		(ext, thefile) = entry
+		if ret == False:
+			ret = ext.parse(thefile, ldb, conn, attr_log)
+		else:
+			temp = ext.parse(thefile, ldb, conn, attr_log)
+	return ret
+
+
+def Backer(afile, attr_log):
+    #Fixing the bug where only some Linux Boxes Capitalize MACHINE
+    blist = afile.split('/')
+
+    bfile = blist[0] + '/' + blist[1] + '/' + 'Machine' + '/Microsoft/Windows NT/SecEdit/GptTmpl.inf'
+    if bfile.endswith('inf'):
+        BackLogger(afile, attr_log)
+
+    bfile = blist[0] + '/' + blist[1] + '/' + 'machine' + '/Microsoft/Windows NT/SecEdit/GptTmpl.inf'
+    if bfile.endswith('inf'):
+        BackLogger(bfile, attr_log)
+
+    if afile.endswith('inf'):
+        BackLogger(bfile, attr_log):
+    return ret
+
+def BackLogger(path, attr_log):
+    #We need to get that inftable
+    temp = gp_sec_ext()
+    inftable = temp.populate_inf()
+
+try:
+        policy = conn.loadfile(path).decode('utf-16')
+    except:
+        return None
+    current_section = None
+    LOG = open(attr_log, "a")
+    print path.split('/')[2]
+    LOG.write(str(path.split('/')[2]) + '\n')
+
+
+    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()
+            if current_section.get(key):
+                (att, setter) = current_section.get(key)
+                value = value.encode('ascii', 'ignore')
+					log.write(att + str(value)
+
 #NOTE Version: *A GPO will :
 	#1)Change Version Number if edited content (policy added or removed)
 	#2)Have a Version Number = 0 if Completely empty of content
-#Obviously we will only Apply GPO that do not fall either of these catagories
+#Obviously we will only Apply GPO that do not fall either of these catagories'''
-- 
1.9.3



More information about the samba-technical mailing list