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 configparser
import ConfigParser
26 from io
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
,
39 self
.ini_conf
.optionxform
= str
41 self
.ini_conf
.readfp(StringIO(contents
.decode(self
.encoding
)))
43 def build_xml_parameter(self
, section_xml
, section
, key_ini
, val_ini
):
44 child
= SubElement(section_xml
, 'Parameter')
45 key
= SubElement(child
, 'Key')
46 value
= SubElement(child
, 'Value')
52 def load_xml_parameter(self
, param_xml
, section
):
53 key
= param_xml
.find('Key').text
54 value
= param_xml
.find('Value').text
57 self
.ini_conf
.set(section
, key
, value
)
61 def build_xml_section(self
, root_xml
, sec_ini
):
62 section
= SubElement(root_xml
, 'Section')
63 section
.attrib
['name'] = sec_ini
67 def load_xml_section(self
, section_xml
):
68 section_name
= section_xml
.attrib
['name']
69 self
.ini_conf
.add_section(section_name
)
73 def write_xml(self
, filename
):
74 with
open(filename
, 'wb') as f
:
75 root
= Element('IniFile')
77 for sec_ini
in self
.ini_conf
.sections():
78 section
= self
.build_xml_section(root
, sec_ini
)
80 for key_ini
, val_ini
in self
.ini_conf
.items(sec_ini
, raw
=True):
81 self
.build_xml_parameter(section
, sec_ini
, key_ini
,
84 self
.write_pretty_xml(root
, f
)
86 # from xml.etree.ElementTree import fromstring
87 # contents = codecs.open(filename, encoding='utf-8').read()
88 # self.load_xml(fromstring(contents))
90 def load_xml(self
, root
):
91 # Required dict_type in Python 2.7
92 self
.ini_conf
= ConfigParser(dict_type
=collections
.OrderedDict
,
94 self
.ini_conf
.optionxform
= str
96 for s
in root
.findall('Section'):
97 section_name
= self
.load_xml_section(s
)
99 for param
in s
.findall('Parameter'):
100 self
.load_xml_parameter(param
, section_name
)
102 def write_binary(self
, filename
):
103 with codecs
.open(filename
, 'wb+', self
.encoding
) as f
:
104 self
.ini_conf
.write(f
)
107 class GPTIniParser(GPIniParser
):
110 def parse(self
, contents
):
112 super(GPTIniParser
, self
).parse(contents
)
113 except UnicodeDecodeError:
114 # Required dict_type in Python 2.7
115 self
.ini_conf
= ConfigParser(dict_type
=collections
.OrderedDict
,
117 self
.ini_conf
.optionxform
= str
119 # Fallback to Latin-1 which RSAT appears to use
120 self
.ini_conf
.readfp(StringIO(contents
.decode('iso-8859-1')))
123 class GPScriptsIniParser(GPIniParser
):
124 def build_xml_parameter(self
, section_xml
, section
, key_ini
, val_ini
):
125 parent_return
= super(GPScriptsIniParser
,
126 self
).build_xml_parameter(section_xml
, section
,
129 cmdline
= re
.match('\\d+CmdLine$', key_ini
)
130 if cmdline
is not None:
131 value
= parent_return
.find('Value')
132 value
.attrib
['network_path'] = 'TRUE'
137 class GPFDeploy1IniParser(GPIniParser
):
138 def build_xml_parameter(self
, section_xml
, section
, key_ini
, val_ini
):
139 parent_return
= super(GPFDeploy1IniParser
,
140 self
).build_xml_parameter(section_xml
, section
,
142 # Add generalization metadata and parse out SID list
143 if section
.lower() == 'folder_redirection':
144 # Process the header section
145 # {GUID} = S-1-1-0;S-1-1-0
147 # Remove the un-split SID values
148 key
= parent_return
.find('Value')
149 parent_return
.remove(key
)
151 sid_list
= val_ini
.strip().strip(';').split(';')
154 value
= SubElement(parent_return
, 'Value')
156 value
.attrib
['user_id'] = 'TRUE'
159 # Process redirection sections
160 # Only FullPath should be a network path
161 if key_ini
== 'FullPath':
162 key
= parent_return
.find('Value')
163 key
.attrib
['network_path'] = 'TRUE'
167 def load_xml_parameter(self
, param_xml
, section
):
168 # Re-join the SID list before entering ConfigParser
169 if section
.lower() == 'folder_redirection':
170 key
= param_xml
.find('Key').text
171 values
= param_xml
.findall('Value')
174 # There appears to be a convention of a trailing semi-colon
175 # with only one value in the SID list.
176 value
= values
[0].text
+ ';'
178 value
= ';'.join([x
.text
for x
in values
])
180 self
.ini_conf
.set(section
, key
, value
)
184 # Do the normal ini code for other sections
185 return super(GPFDeploy1IniParser
,
186 self
).load_xml_parameter(param_xml
, section
)
188 def build_xml_section(self
, root_xml
, sec_ini
):
189 section
= SubElement(root_xml
, 'Section')
191 if (sec_ini
.lower() != 'folder_redirection' and
192 sec_ini
.lower() != 'version'):
193 guid
, sid
= sec_ini
.split('_')
194 section
.attrib
['fdeploy_GUID'] = guid
195 section
.attrib
['fdeploy_SID'] = sid
197 section
.attrib
['name'] = sec_ini
201 def load_xml_section(self
, section_xml
):
202 # Construct the name from GUID + SID if no name exists
203 if 'name' in section_xml
.attrib
:
204 section_name
= section_xml
.attrib
['name']
206 guid
= section_xml
.attrib
['fdeploy_GUID']
207 sid
= section_xml
.attrib
['fdeploy_SID']
208 section_name
= guid
+ '_' + sid
210 self
.ini_conf
.add_section(section_name
)
213 def custom_entities(self
, root
, global_entities
):
215 fdeploy_sids
= root
.findall('.//Section[@fdeploy_SID]')
216 fdeploy_sids
.sort(key
= lambda x
: x
.tag
)
218 for sid
in fdeploy_sids
:
219 old_attrib
= sid
.attrib
['fdeploy_SID']
221 if old_attrib
in global_entities
:
222 new_attrib
= global_entities
[old_attrib
]
224 new_attrib
= self
.new_xml_entity(old_attrib
, ENTITY_USER_ID
)
225 entities
.append((new_attrib
, old_attrib
))
227 global_entities
.update([(old_attrib
, new_attrib
)])
229 sid
.attrib
['fdeploy_SID'] = new_attrib