1 # GPO Parser for extensions with ini files
3 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2018
4 # Written by Garming Sam <garming@catalyst.net.nz>
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 from xml.etree.ElementTree import Element, SubElement
25 from samba.compat import ConfigParser
26 from samba.compat import StringIO
28 from samba.gp_parse import GPParser, ENTITY_USER_ID
30 # [MS-GPFR] Group Policy Folder Redirection
31 # [MS-GPSCR] Scripts Extension
32 class GPIniParser(GPParser):
35 def parse(self, contents):
36 # Required dict_type in Python 2.7
37 self.ini_conf = ConfigParser(dict_type=collections.OrderedDict)
38 self.ini_conf.optionxform = str
40 self.ini_conf.readfp(StringIO(contents.decode(self.encoding)))
42 def build_xml_parameter(self, section_xml, section, key_ini, val_ini):
43 child = SubElement(section_xml, 'Parameter')
44 key = SubElement(child, 'Key')
45 value = SubElement(child, 'Value')
51 def load_xml_parameter(self, param_xml, section):
52 key = param_xml.find('Key').text
53 value = param_xml.find('Value').text
56 self.ini_conf.set(section, key, value)
60 def build_xml_section(self, root_xml, sec_ini):
61 section = SubElement(root_xml, 'Section')
62 section.attrib['name'] = sec_ini
66 def load_xml_section(self, section_xml):
67 section_name = section_xml.attrib['name']
68 self.ini_conf.add_section(section_name)
72 def write_xml(self, filename):
73 with open(filename, 'w') as f:
74 root = Element('IniFile')
76 for sec_ini in self.ini_conf.sections():
77 section = self.build_xml_section(root, sec_ini)
79 for key_ini, val_ini in self.ini_conf.items(sec_ini, raw=True):
80 self.build_xml_parameter(section, sec_ini, key_ini,
83 self.write_pretty_xml(root, f)
85 # from xml.etree.ElementTree import fromstring
86 # contents = codecs.open(filename, encoding='utf-8').read()
87 # self.load_xml(fromstring(contents))
89 def load_xml(self, root):
90 # Required dict_type in Python 2.7
91 self.ini_conf = ConfigParser(dict_type=collections.OrderedDict)
92 self.ini_conf.optionxform = str
94 for s in root.findall('Section'):
95 section_name = self.load_xml_section(s)
97 for param in s.findall('Parameter'):
98 self.load_xml_parameter(param, section_name)
100 def write_binary(self, filename):
101 with codecs.open(filename, 'wb+', self.encoding) as f:
102 self.ini_conf.write(f)
105 class GPTIniParser(GPIniParser):
109 class GPScriptsIniParser(GPIniParser):
110 def build_xml_parameter(self, section_xml, section, key_ini, val_ini):
111 parent_return = super(GPScriptsIniParser,
112 self).build_xml_parameter(section_xml, section,
115 cmdline = re.match('\\d+CmdLine$', key_ini)
116 if cmdline is not None:
117 value = parent_return.find('Value')
118 value.attrib['network_path'] = 'TRUE'
123 class GPFDeploy1IniParser(GPIniParser):
124 def build_xml_parameter(self, section_xml, section, key_ini, val_ini):
125 parent_return = super(GPFDeploy1IniParser,
126 self).build_xml_parameter(section_xml, section,
128 # Add generalization metadata and parse out SID list
129 if section.lower() == 'folder_redirection':
130 # Process the header section
131 # {GUID} = S-1-1-0;S-1-1-0
133 # Remove the un-split SID values
134 key = parent_return.find('Value')
135 parent_return.remove(key)
137 sid_list = val_ini.strip().strip(';').split(';')
140 value = SubElement(parent_return, 'Value')
142 value.attrib['user_id'] = 'TRUE'
145 # Process redirection sections
146 # Only FullPath should be a network path
147 if key_ini == 'FullPath':
148 key = parent_return.find('Value')
149 key.attrib['network_path'] = 'TRUE'
153 def load_xml_parameter(self, param_xml, section):
154 # Re-join the SID list before entering ConfigParser
155 if section.lower() == 'folder_redirection':
156 key = param_xml.find('Key').text
157 values = param_xml.findall('Value')
160 # There appears to be a convention of a trailing semi-colon
161 # with only one value in the SID list.
162 value = values[0].text + ';'
164 value = ';'.join([x.text for x in values])
166 self.ini_conf.set(section, key, value)
170 # Do the normal ini code for other sections
171 return super(GPFDeploy1IniParser,
172 self).load_xml_parameter(param_xml, section)
174 def build_xml_section(self, root_xml, sec_ini):
175 section = SubElement(root_xml, 'Section')
177 if (sec_ini.lower() != 'folder_redirection' and
178 sec_ini.lower() != 'version'):
179 guid, sid = sec_ini.split('_')
180 section.attrib['fdeploy_GUID'] = guid
181 section.attrib['fdeploy_SID'] = sid
183 section.attrib['name'] = sec_ini
187 def load_xml_section(self, section_xml):
188 # Construct the name from GUID + SID if no name exists
189 if 'name' in section_xml.attrib:
190 section_name = section_xml.attrib['name']
192 guid = section_xml.attrib['fdeploy_GUID']
193 sid = section_xml.attrib['fdeploy_SID']
194 section_name = guid + '_' + sid
196 self.ini_conf.add_section(section_name)
199 def custom_entities(self, root, global_entities):
201 fdeploy_sids = root.findall('.//Section[@fdeploy_SID]')
202 fdeploy_sids.sort(key = lambda x: x.tag)
204 for sid in fdeploy_sids:
205 old_attrib = sid.attrib['fdeploy_SID']
207 if old_attrib in global_entities:
208 new_attrib = global_entities[old_attrib]
210 new_attrib = self.new_xml_entity(old_attrib, ENTITY_USER_ID)
211 entities.append((new_attrib, old_attrib))
213 global_entities.update([(old_attrib, new_attrib)])
215 sid.attrib['fdeploy_SID'] = new_attrib