[PATCH 7/8] Added modularity to samba_gpoupdate Added BackLog for default WIP make it maintain hierarchy Added the cases where GPO is deleted from OU or altogether, modularized, and fine tuned hierarchy order

David Mulder dmulder at suse.com
Mon Jan 30 15:17:19 UTC 2017


From: Luke Morrison <luc785 at hotmail.com>

Signed-off-by: Garming Sam <garming at catalyst.net.nz>
Signed-off-by: Luke Morrison <luke at hubtrek.com>
---
 Sdfsdfs                               |   0
 python/samba/gpclass.py               | 159 +++++----------
 source4/scripting/bin/samba_gpoupdate | 359 ++++++++++++++++++++++++++--------
 3 files changed, 331 insertions(+), 187 deletions(-)
 create mode 100644 Sdfsdfs

diff --git a/Sdfsdfs b/Sdfsdfs
new file mode 100644
index 0000000..e69de29
diff --git a/python/samba/gpclass.py b/python/samba/gpclass.py
index b7f6e34..c52b7cf 100755
--- a/python/samba/gpclass.py
+++ b/python/samba/gpclass.py
@@ -118,17 +118,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):
+    def read_inf(self, path, conn, attr_log):
+        ret = False
         inftable = self.populate_inf()
-        '''The inf file to be mapped'''
-        #policy = codecs.open(path, encoding='utf-16')
-	try:
+
+        try:
             policy = conn.loadfile(path).decode('utf-16')
         except:
             return None
         current_section = None
+        LOG = open(attr_log, "a")
+        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,101 +152,29 @@ class gp_sec_ext(gp_ext):
                 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()
-    #FIXME read registry files (.pol). Can they ever apply? Define read_registry():
+        return ret
 
-    def parse(self, afile, ldb, conn):
+    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'):
+            ret = self.read_inf(bfile, conn, attr_log)
+
+        bfile = blist[0] + '/' + blist[1] + '/' + 'machine' + '/Microsoft/Windows NT/SecEdit/GptTmpl.inf'
+        if bfile.endswith('inf'):
+            ret = self.read_inf(bfile, conn, attr_log)
+
         if afile.endswith('inf'):
-            self.read_inf(afile, conn)
-
-class samba4_gpo_hierarchy(object):
-
-    def __init__(self, SamDB, sysvol_guid_list, DC_OU, GLOBAL_DN):
-        """
-        :param SamDB: An instance of the live samba database
-        :param sysvol_guid_list: The complete list of all GPO GUID's listed in sysvol folder
-        :param DC_OU: The respective distinguished name of the Domain Controller
-        :param GLOBAL_DN: The Domain DN that Samba is a part of
-        """
-        self.SamDB = SamDB
-        self.GUID_L = sysvol_guid_list
-        self.DC_OU = DC_OU
-        self.GL_DN = GLOBAL_DN
-        self.sorted_containers = []
-        self.sorted_full = []
-        self.indexed_places = []
-        self.unapplied_gpo = 0
-
-    def update_unapplied_gpo(self):
-        self.update_unapplied_gpo += 1
-
-    '''Returns list of int indexes to where the dn changes'''
-    def container_indexes(self):
-        count = 0
-        container_indexes = []
-        while count < (len(self.GUID_L)-1):
-            if self.sorted_containers[count][2] != self.sorted_containers[count+1][2]:
-                container_indexes.append(count+1)
-            count += 1
-        container_indexes.append(len(self.sorted_containers))
-        return container_indexes
-
-
-    def establish_hierarchy(self):
-        final_list = []
-        count_unapplied_GPO = 0
-        for GUID in self.GUID_L:
-            container_iteration = 0
-            applied = False # Assume first it is not applied
-            gpo_realm = False # Realm only written on last call, if the GPO is linked to multiple places
-            '''Get all of the linked information'''
-            GPO_CONTAINERS = gpo_user.get_gpo_containers(self.SamDB, GUID)
-            for GPO_CONTAINER in GPO_CONTAINERS:
-
-                container_iteration +=1
-
-                if self.DC_OU == str(GPO_CONTAINER.get('dn')):
-                    applied = True
-                    insert_gpo = [GUID, applied, str(GPO_CONTAINER.get('dn'))]
-                    self.sorted_containers.append(insert_gpo)
-                    break
-
-                if self.GL_DN == str(GPO_CONTAINER.get('dn')) and (len(GPO_CONTAINERS) == 1):
-                    gpo_realm = True
-                    applied = True
-                    #REALM_GPO = [GUID, applied, str(GPO_CONTAINER.get('dn'))]
-                    #final_list.insert(count_unapplied_GPO, REALM_GPO)
-
-                if self.GL_DN == str(GPO_CONTAINER.get('dn')) and (len(GPO_CONTAINERS) > 1):
-                    gpo_realm = True
-                    applied = True
-
-                if container_iteration == len(GPO_CONTAINERS):
-                    if gpo_realm == False:
-                        insert_dud = [GUID, applied, str(GPO_CONTAINER.get('dn'))]
-                        self.sorted_containers.insert(0, insert_dud)
-                        self.count_unapplied_GPO()
-                    else :
-                        REALM_GPO = [GUID, applied, str(GPO_CONTAINER.get('dn'))]
-                        self.sorted_containers.insert(count_unapplied_GPO, REALM_GPO)
-
-        '''After GPO are sorted into containers, sort the containers themselves. But first append non-applicable GPO.'''
-        self.indexed_places = self.container_indexes()
-        count = 0
-        unapplied_gpo = []
-        self.sorted_full = []
-        '''Append all empties to final from first change of container'''
-        while count < self.indexed_places[0]:
-            unapplied_gpo.append(self.sorted_containers[count])
-            count += 1
-
-        count = 0
-        self.sorted_full += unapplied_gpo
-        while count < (len(self.indexed_places)-1): # Already accounted for one in empties
-            self.sorted_full += (sort_linked(self.SamDB, self.sorted_containers, self.indexed_places[count], self.indexed_places[count + 1]))
-            count += 1
+            ret = self.read_inf(afile, conn, attr_log)
+        return ret
 
 
 def scan_log(sysvol_path):
@@ -250,6 +186,23 @@ 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):
+    if backloggpo is None:
+        return False
+    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.
@@ -291,18 +244,10 @@ def sort_linked(SAMDB, guid_list, start, end):
         linked_order.append(str(ldap_guid[10:48]))
     count = len(linked_order) - 1
     while count > 0:
-        ret_list.append([linked_order[count], True, guid_list[start][2]])
+        ret_list.append([linked_order[count], guid_list[start][1], guid_list[start][2]])
         count -= 1
     return ret_list
 
-   # Accepts sysvol parameters to return a hierarchically sorted list, with application flag indicators.
-
-
-#A GPO may have a single or multiple links. Get all of the containers (OU, SITE, etc..) and return them'''
-    #def get_gpo_containers( ) :
-    #   return gpo_netcmd_user.get_gpo_containers(self.SamDB, self.GUID)
-
-  #  def
 
 def establish_hierarchy(SamDB, GUID_LIST, DC_OU, global_dn):
     '''Takes a list of GUID from gpo, and sorts them based on OU, and realm.
@@ -358,8 +303,9 @@ 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
@@ -367,6 +313,7 @@ def establish_hierarchy(SamDB, GUID_LIST, DC_OU, global_dn):
 
     # 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]))
         count += 1
diff --git a/source4/scripting/bin/samba_gpoupdate b/source4/scripting/bin/samba_gpoupdate
index bf81496..e6b7db5 100755
--- a/source4/scripting/bin/samba_gpoupdate
+++ b/source4/scripting/bin/samba_gpoupdate
@@ -1,5 +1,8 @@
 #!/usr/bin/env python
-# Copyright Luke Morrison <luc785 at .hotmail.com> 2013
+# Copyright Luke Morrison <luc785 at .hotmail.com> July 2013
+# Co-Edited by Matthieu Pattou July 2013 from original August 2013
+# Edited by Garming Sam Feb. 2014
+# Edited by Luke Morrison April 2014
 
 '''This script reads a log file of previous GPO, gets all GPO from sysvol
 and sorts them by container. Then, it applies the ones that haven't been
@@ -21,82 +24,168 @@ from samba.net import Net
 from samba.dcerpc import nbt
 from samba import smb
 
-# Finds all GPO Files ending in inf
-def gp_path_list(path):
 
+"""Do I needs this?
+schemadn = test_ldb.get_schema_basedn()
+basedn = test_ldb.get_default_basedn()
+
+#guid_list = os.listdir(path)
+#guid_list = establish_hierarchy(test_ldb, guid_list, DC_OU, global_dn)
+"""
+
+
+def gp_path_list(path):
     GPO_LIST = []
     for ext in gp_extensions:
         GPO_LIST.append((ext, ext.list(path)))
     return GPO_LIST
 
-# Reads the GPOs and sends them to their proper handlers
-def gpo_parser(GPO_LIST, ldb, conn):
-    for entry in GPO_LIST:
-        (ext, thefile) = entry
-        ext.parse(thefile, ldb, conn)
-
-parser = optparse.OptionParser("testsearchdn [options]")
-
-sambaopts = options.SambaOptions(parser)
 
-parser.add_option_group(sambaopts)
-parser.add_option_group(options.VersionOptions(parser))
+def gpo_parser(GPO_LIST, ldb, conn, attr_log):
+    '''The API method to parse the GPO
+    :param GPO_LIST:
+    :param ldb: Live instance of an LDB object AKA Samba
+    :param conn: Live instance of a CIFS connection
+    :param attr_log: backlog path for GPO and attribute to be written
+    no return except a newly updated Samba
+    '''
 
-credopts = options.CredentialsOptions(parser)
-
-parser.add_option("-H", dest = "url", help="URL for the samdb")
-
-parser.add_option_group(credopts)
-
-opts, args = parser.parse_args()
-lp = sambaopts.get_loadparm()
-
-smbconf = lp.configfile
-creds = credopts.get_credentials(lp, fallback_machine=True)
-
-session = system_session()
-
-if not opts.url:
-    url = lp.samdb_url()
-else:
-    url = opts.url
-
-#########################
-#Inialize Samba Database#
-#########################
-
-test_ldb = SamDB(url, session_info=session,
-                 credentials=creds,lp=lp)
-
-schemadn = test_ldb.get_schema_basedn()
-
-basedn = test_ldb.get_default_basedn()
+    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):
 
-'''Will need sysvol to write a basic GUID version dynamic log file'''
-#path = '%s/%s/%s' % (lp.get("path", "sysvol"), lp.get("realm").lower(), 'Policies')
-path = '%s/Policies' % lp.get("realm").lower()
+    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
+        a Samba lp context (smb.conf) and Samba LDB context
+        """
+
+        self.parser = optparse.OptionParser("testsearchdn [options]")
+        self.sambaopts = options.SambaOptions(self.parser)
+        self.credopts = None
+        self.opts = None
+        self.args = None
+        self.lp = None
+        self.smbconf = None
+        self.creds = None
+        self.url = None
+
+    # Setters or Initializers
+    def init_parser(self):
+        '''Get the command line options'''
+        self.parser.add_option_group(self.sambaopts)
+        self.parser.add_option_group(options.VersionOptions(self.parser))
+        self.init_credopts()
+        self.parser.add_option("-H", dest="url", help="URL for the samdb")
+        self.parser.add_option_group(self.credopts)
+
+    def init_argsopts(self):
+        '''Set the options and the arguments'''
+        (opts, args) = self.parser.parse_args()
+
+        self.opts = opts
+        self.args = args
+
+    def init_credopts(self):
+        '''Set Credential operations'''
+        self.credopts = options.CredentialsOptions(self.parser)
+
+    def init_lp(self):
+        '''Set the loadparm context'''
+        self.lp = self.sambaopts.get_loadparm()
+        self.smbconf = self.lp.configfile
+        if (not self.opts.url):
+            self.url = self.lp.samdb_url()
+        else:
+            self.url = self.opts.url
+
+    def init_session(self):
+        '''Initialize the session'''
+        self.creds = self.credopts.get_credentials(self.lp,
+            fallback_machine=True)
+        self.session = system_session()
+
+    def InitializeService(self):
+        '''Inializer for the thread'''
+        self.init_parser()
+        self.init_argsopts()
+        self.init_lp()
+        self.init_session()
+
+    # Getters
+    def Get_LDB(self):
+        '''Return a live instance of Samba'''
+        SambaDB = SamDB(self.url, session_info=self.session,
+            credentials=self.creds, lp=self.lp)
+        return SambaDB
+
+    def Get_lp_Content(self):
+        '''Return an instance of a local lp context'''
+        return self.lp
+
+    def Get_Creds(self):
+        '''Return an instance of a local creds'''
+        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()
+
+# Get the Samba Instance
+test_ldb = GPOService.Get_LDB()
+
+# Get The lp context
+lp = GPOService.Get_lp_Content()
+
+# 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')
+attr_log = '%s/%s' % (lp.get("path", "sysvol"), 'attrlog.txt')
+BackLoggedGPO = GetBackLog(sys_log)
 
-'''Returns dict from previous logfile, then scraps the logfile '''
-previous_scanned_version = {'a' : 4}
-if os.path.isfile(sys_log):
-    previous_scanned_version = scan_log(sys_log)
-sys_log = open(sys_log, "w")
 
-'''Establishes the hierarchy TODO - insert the link fom Microsoft and vouch why we dont care about site or local'''
-specific_ou = "OU=Domain Controllers"
-'''TODO Definitely get DC from Samba'''
-global_dn = test_ldb.domain_dn()
-print 'The global DN for this domain is ' + global_dn
-DC_OU = specific_ou + ',' + global_dn
+BackLog = open(sys_log, "w")
 
-net = Net(creds=creds, lp=lp)
 
 # We need to know writable DC to setup SMB connection
-flags = (nbt.NBT_SERVER_LDAP |
-	 nbt.NBT_SERVER_DS |
-	 nbt.NBT_SERVER_WRITABLE)
-cldap_ret = net.finddc(domain=lp.get('realm'), flags=flags)
+net = Net(creds=creds, lp=lp)
+'''flags = (nbt.NBT_SERVER_LDAP |
+     nbt.NBT_SERVER_DS |
+     nbt.NBT_SERVER_WRITABLE)'''
+cldap_ret = net.finddc(domain=lp.get('realm'), flags=(nbt.NBT_SERVER_LDAP |
+    nbt.NBT_SERVER_DS |
+    nbt.NBT_SERVER_WRITABLE))
 dc_hostname = cldap_ret.pdc_dns_name
 
 try:
@@ -104,28 +193,136 @@ try:
 except Exception, e:
     raise Exception("Error connecting to '%s' using SMB" % dc_hostname, e)
 
-guid_list = [x['name'] for x in conn.list(path)]
-#guid_list = os.listdir(path)
-#guid_list = establish_hierarchy(test_ldb, guid_list, DC_OU, global_dn)
-
-hierarchy_gpos = samba4_gpo_hierarchy(test_ldb, guid_list, DC_OU, global_dn)
-hierarchy_gpos.establish_hierarchy()
-
-
-for guid_eval in hierarchy_gpos.sorted_full:
+# Get the dn of the domain, and the dn of readable/writable DC
+global_dn = test_ldb.domain_dn()
+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')
+
+hierarchy_gpos = establish_hierarchy(test_ldb, guid_list, DC_OU, global_dn)
+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 = path + '/' + guid + '/'
-    version = gpo.gpo_get_sysvol_gpt_version(lp.get("path", "sysvol") + '/' + local_path)[1]
-
+    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)
-    print 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 (version != previous_scanned_version.get(guid)) and (version != 0):
-                print ('GPO %s has changed' % guid)
-                gpo_parser(gpolist, test_ldb, conn)
+            # 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)
+
+    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
+
 
-    sys_log.write('%s %i\n' % (guid,version))
+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'''
-- 
2.10.2




More information about the samba-technical mailing list