python/samba/gp_parse: PY3 file -> open
[amitay/samba.git] / python / samba / gp_parse / gp_inf.py
1 # GPO Parser for security extensions
2 #
3 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2018
4 # Written by Garming Sam <garming@catalyst.net.nz>
5 #
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.
10 #
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.
15 #
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/>.
18 #
19
20 import codecs
21 import collections
22 import re
23
24 from abc import ABCMeta, abstractmethod
25 from xml.etree.ElementTree import Element, SubElement
26
27 from samba.gp_parse import GPParser
28
29 # [MS-GPSB] Security Protocol Extension
30 class GptTmplInfParser(GPParser):
31     sections = None
32
33     class AbstractParam:
34         __metaclass__ = ABCMeta
35         encoding = 'utf-16le'
36
37         def __init__(self):
38             self.param_list = []
39
40         @abstractmethod
41         def parse(self, line):
42             pass
43
44         @abstractmethod
45         def write_section(self, header, fp):
46             pass
47
48         @abstractmethod
49         def build_xml(self, xml_parent):
50             pass
51
52         @abstractmethod
53         def from_xml(self, section):
54             pass
55
56     class IniParam(AbstractParam):
57         # param_list = [(Key, Value),]
58
59         def parse(self, line):
60             key, val = line.split('=')
61
62             self.param_list.append((key.strip(),
63                                     val.strip()))
64
65             # print key.strip(), val.strip()
66
67         def write_section(self, header, fp):
68             if len(self.param_list) ==  0:
69                 return
70             fp.write((u'[%s]\r\n' % header).encode(self.encoding))
71             for key_out, val_out in self.param_list:
72                 fp.write((u'%s = %s\r\n' % (key_out,
73                                             val_out)).encode(self.encoding))
74
75         def build_xml(self, xml_parent):
76             for key_ini, val_ini in self.param_list:
77                 child = SubElement(xml_parent, 'Parameter')
78                 key = SubElement(child, 'Key')
79                 value = SubElement(child, 'Value')
80                 key.text = key_ini
81                 value.text = val_ini
82
83         def from_xml(self, section):
84             for param in section.findall('Parameter'):
85                 key = param.find('Key').text
86                 value = param.find('Value').text
87                 if value is None:
88                     value = ''
89
90                 self.param_list.append((key, value))
91
92     class RegParam(AbstractParam):
93         # param_list = [Value, Value, ...]
94         def parse(self, line):
95             # = can occur in a registry key, so don't parse these
96             self.param_list.append(line)
97             # print line
98
99         def write_section(self, header, fp):
100             if len(self.param_list) ==  0:
101                 return
102             fp.write((u'[%s]\r\n' % header).encode(self.encoding))
103             for param in self.param_list:
104                 fp.write((u'%s\r\n' % param).encode(self.encoding))
105
106         def build_xml(self, xml_parent):
107             for val_ini in self.param_list:
108                 child = SubElement(xml_parent, 'Parameter')
109                 value = SubElement(child, 'Value')
110                 value.text = val_ini
111
112         def from_xml(self, section):
113             for param in section.findall('Parameter'):
114                 value = param.find('Value').text
115                 if value is None:
116                     value = ''
117
118                 self.param_list.append(value)
119
120     class PrivSIDListParam(AbstractParam):
121         # param_list = [(Key, [SID, SID,..]),
122         def parse(self, line):
123             key, val = line.split('=')
124
125             self.param_list.append((key.strip(),
126                                     [x.strip() for x in val.split(',')]))
127             # print line
128
129         def write_section(self, header, fp):
130             if len(self.param_list) ==  0:
131                 return
132             fp.write((u'[%s]\r\n' % header).encode(self.encoding))
133             for key_out, val in self.param_list:
134                 val_out = u','.join(val)
135                 fp.write((u'%s = %s\r\n' % (key_out, val_out)).encode(self.encoding))
136
137         def build_xml(self, xml_parent):
138             for key_ini, sid_list in self.param_list:
139                 child = SubElement(xml_parent, 'Parameter')
140                 key = SubElement(child, 'Key')
141                 key.text = key_ini
142                 for val_ini in sid_list:
143                     value = SubElement(child, 'Value')
144                     value.attrib['user_id'] = 'TRUE'
145                     value.text = val_ini
146
147         def from_xml(self, section):
148             for param in section.findall('Parameter'):
149                 key = param.find('Key').text
150
151                 sid_list = []
152                 for val in param.findall('Value'):
153                     value = val.text
154                     if value is None:
155                         value = ''
156
157                     sid_list.append(value)
158
159                 self.param_list.append((key, sid_list))
160
161     class NameModeACLParam(AbstractParam):
162         # param_list = [[Name, Mode, ACL],]
163         def parse(self, line):
164             parameters = [None, None, None]
165             current_arg = 0
166
167             while line != '':
168                 # Read quoted string
169                 if line[:1] == '"':
170                     line = line[1:]
171                     findex = line.find('"')
172                     parameters[current_arg] = line[:findex]
173                     line = line[findex + 1:]
174                 # Skip past delimeter
175                 elif line[:1] == ',':
176                     line = line[1:]
177                     current_arg += 1
178                 # Read unquoted string
179                 else:
180                     findex = line.find(',')
181                     parameters[current_arg] = line[:findex]
182                     line = line[findex:]
183
184             # print parameters
185             # print line
186             self.param_list.append(parameters)
187
188         def write_section(self, header, fp):
189             if len(self.param_list) ==  0:
190                 return
191             fp.write((u'[%s]\r\n' % header).encode(self.encoding))
192             for param in self.param_list:
193                 fp.write((u'"%s",%s,"%s"\r\n' % tuple(param)).encode(self.encoding))
194
195         def build_xml(self, xml_parent):
196             for name_mode_acl in self.param_list:
197                 child = SubElement(xml_parent, 'Parameter')
198
199                 value = SubElement(child, 'Value')
200                 value.text = name_mode_acl[0]
201
202                 value = SubElement(child, 'Value')
203                 value.text = name_mode_acl[1]
204
205                 value = SubElement(child, 'Value')
206                 value.attrib['acl'] = 'TRUE'
207                 value.text = name_mode_acl[2]
208
209         def from_xml(self, section):
210             for param in section.findall('Parameter'):
211                 name_mode_acl = [x.text if x.text else '' for x in param.findall('Value')]
212                 self.param_list.append(name_mode_acl)
213
214     class MemberSIDListParam(AbstractParam):
215         # param_list = [([XXXX, Memberof|Members], [SID, SID...]),...]
216         def parse(self, line):
217             key, val = line.split('=')
218
219             key = key.strip()
220
221             self.param_list.append((key.split('__'),
222                                     [x.strip() for x in val.split(',')]))
223             # print line
224
225         def write_section(self, header, fp):
226             if len(self.param_list) ==  0:
227                 return
228             fp.write((u'[%s]\r\n' % header).encode(self.encoding))
229
230             for key, val in self.param_list:
231                 key_out = u'__'.join(key)
232                 val_out = u','.join(val)
233                 fp.write((u'%s = %s\r\n' % (key_out, val_out)).encode(self.encoding))
234
235         def build_xml(self, xml_parent):
236             for key_ini, sid_list in self.param_list:
237                 child = SubElement(xml_parent, 'Parameter')
238                 key = SubElement(child, 'Key')
239                 key.text = key_ini[0]
240                 key.attrib['member_type'] = key_ini[1]
241                 key.attrib['user_id'] = 'TRUE'
242
243                 for val_ini in sid_list:
244                     value = SubElement(child, 'Value')
245                     value.attrib['user_id'] = 'TRUE'
246                     value.text = val_ini
247
248         def from_xml(self, section):
249             for param in section.findall('Parameter'):
250                 key = param.find('Key')
251                 member_type = key.attrib['member_type']
252
253                 sid_list = []
254                 for val in param.findall('Value'):
255                     value = val.text
256                     if value is None:
257                         value = ''
258
259                     sid_list.append(value)
260
261                 self.param_list.append(([key.text, member_type], sid_list))
262
263     class UnicodeParam(AbstractParam):
264         def parse(self, line):
265             # print line
266             pass
267
268         def write_section(self, header, fp):
269             fp.write(u'[Unicode]\r\nUnicode=yes\r\n'.encode(self.encoding))
270
271         def build_xml(self, xml_parent):
272             # We do not bother storing this field
273             pass
274
275         def from_xml(self, section):
276             # We do not bother storing this field
277             pass
278
279     class VersionParam(AbstractParam):
280         def parse(self, line):
281             # print line
282             pass
283
284         def write_section(self, header, fp):
285             out = u'[Version]\r\nsignature="$CHICAGO$"\r\nRevision=1\r\n'
286             fp.write(out.encode(self.encoding))
287
288         def build_xml(self, xml_parent):
289             # We do not bother storing this field
290             pass
291
292         def from_xml(self, section):
293             # We do not bother storing this field
294             pass
295
296     def parse(self, contents):
297         inf_file = contents.decode(self.encoding)
298
299         self.sections = collections.OrderedDict([
300             (u'Unicode', self.UnicodeParam()),
301             (u'Version', self.VersionParam()),
302
303             (u'System Access', self.IniParam()),
304             (u'Kerberos Policy', self.IniParam()),
305             (u'System Log', self.IniParam()),
306             (u'Security Log', self.IniParam()),
307             (u'Application Log', self.IniParam()),
308             (u'Event Audit', self.IniParam()),
309             (u'Registry Values', self.RegParam()),
310             (u'Privilege Rights', self.PrivSIDListParam()),
311             (u'Service General Setting', self.NameModeACLParam()),
312             (u'Registry Keys', self.NameModeACLParam()),
313             (u'File Security', self.NameModeACLParam()),
314             (u'Group Membership', self.MemberSIDListParam()),
315         ])
316
317         current_param_parser = None
318         current_header_name = None
319
320         for line in inf_file.splitlines():
321             match = re.match('\[(.*)\]', line)
322             if match:
323                 header_name = match.group(1)
324                 if header_name in self.sections:
325                     current_param_parser = self.sections[header_name]
326                     # print current_param_parser
327                     continue
328
329             # print 'using', current_param_parser
330             current_param_parser.parse(line)
331
332
333     def write_binary(self, filename):
334         with codecs.open(filename, 'wb+',
335                          self.output_encoding) as f:
336             for s in self.sections:
337                 self.sections[s].write_section(s, f)
338
339     def write_xml(self, filename):
340         with open(filename, 'wb') as f:
341             root = Element('GptTmplInfFile')
342
343             for sec_inf in self.sections:
344                 section = SubElement(root, 'Section')
345                 section.attrib['name'] = sec_inf
346
347                 self.sections[sec_inf].build_xml(section)
348
349             self.write_pretty_xml(root, f)
350
351         # contents = codecs.open(filename, encoding='utf-8').read()
352         # self.load_xml(fromstring(contents))
353
354     def load_xml(self, root):
355         self.sections = collections.OrderedDict([
356             (u'Unicode', self.UnicodeParam()),
357             (u'Version', self.VersionParam()),
358
359             (u'System Access', self.IniParam()),
360             (u'Kerberos Policy', self.IniParam()),
361             (u'System Log', self.IniParam()),
362             (u'Security Log', self.IniParam()),
363             (u'Application Log', self.IniParam()),
364             (u'Event Audit', self.IniParam()),
365             (u'Registry Values', self.RegParam()),
366             (u'Privilege Rights', self.PrivSIDListParam()),
367             (u'Service General Setting', self.NameModeACLParam()),
368             (u'Registry Keys', self.NameModeACLParam()),
369             (u'File Security', self.NameModeACLParam()),
370             (u'Group Membership', self.MemberSIDListParam()),
371         ])
372
373         for s in root.findall('Section'):
374             self.sections[s.attrib['name']].from_xml(s)