From: Garming Sam Date: Tue, 15 May 2018 05:12:17 +0000 (+1200) Subject: gp_inf: Parse the GptTmpl.inf file which stores security settings X-Git-Tag: tdb-1.3.17~2160 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=dac3c204bef32a524b442db877dcd8af9757ddea;p=thirdparty%2Fsamba.git gp_inf: Parse the GptTmpl.inf file which stores security settings This is NOT an ini file and CANNOT be parsed by Python ConfigParser without losing information (it would likely eat meaningful whitespace and so should not be done). There are three main types of settings: * Name,Mode,ACL * key = value * registry key and value Note: This appears as key=value, but registry keys in the general case may have = in their names, so we record the entire string in order to be as safe as possible. Signed-off-by: Garming Sam Reviewed-by: Andrew Bartlett --- diff --git a/python/samba/gp_parse/gp_inf.py b/python/samba/gp_parse/gp_inf.py new file mode 100644 index 00000000000..4d39cb0f03a --- /dev/null +++ b/python/samba/gp_parse/gp_inf.py @@ -0,0 +1,385 @@ +# GPO Parser for security extensions +# +# Copyright (C) Andrew Bartlett 2018 +# Written by Garming Sam +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import codecs +import collections +import re + +from abc import ABCMeta, abstractmethod +from xml.etree.ElementTree import Element, SubElement + +from samba.gp_parse import GPParser + +# [MS-GPSB] Security Protocol Extension +class GptTmplInfParser(GPParser): + sections = None + + class AbstractParam: + __metaclass__ = ABCMeta + encoding = 'utf-16le' + + def __init__(self): + self.param_list = [] + + @abstractmethod + def parse(self, line): + pass + + @abstractmethod + def write_section(self, header, fp): + pass + + @abstractmethod + def build_xml(self, xml_parent): + pass + + @abstractmethod + def from_xml(self, section): + pass + + class IniParam(AbstractParam): + # param_list = [(Key, Value),] + + def parse(self, line): + key, val = line.split('=') + + self.param_list.append((key.strip(), + val.strip())) + + # print key.strip(), val.strip() + + def write_section(self, header, fp): + if len(self.param_list) == 0: + return + fp.write((u'[%s]\r\n' % header).encode(self.encoding)) + for key_out, val_out in self.param_list: + fp.write((u'%s = %s\r\n' % (key_out, + val_out)).encode(self.encoding)) + + def build_xml(self, xml_parent): + for key_ini, val_ini in self.param_list: + child = SubElement(xml_parent, 'Parameter') + key = SubElement(child, 'Key') + value = SubElement(child, 'Value') + key.text = key_ini + value.text = val_ini + + def from_xml(self, section): + for param in section.findall('Parameter'): + key = param.find('Key').text + value = param.find('Value').text + if value is None: + value = '' + + self.param_list.append((key, value)) + + class RegParam(AbstractParam): + # param_list = [Value, Value, ...] + def parse(self, line): + # = can occur in a registry key, so don't parse these + self.param_list.append(line) + # print line + + def write_section(self, header, fp): + if len(self.param_list) == 0: + return + fp.write((u'[%s]\r\n' % header).encode(self.encoding)) + for param in self.param_list: + fp.write((u'%s\r\n' % param).encode(self.encoding)) + + def build_xml(self, xml_parent): + for val_ini in self.param_list: + child = SubElement(xml_parent, 'Parameter') + value = SubElement(child, 'Value') + value.text = val_ini + + def from_xml(self, section): + for param in section.findall('Parameter'): + value = param.find('Value').text + if value is None: + value = '' + + self.param_list.append(value) + + class PrivSIDListParam(AbstractParam): + # param_list = [(Key, [SID, SID,..]), + def parse(self, line): + key, val = line.split('=') + + self.param_list.append((key.strip(), + [x.strip() for x in val.split(',')])) + # print line + + def write_section(self, header, fp): + if len(self.param_list) == 0: + return + fp.write((u'[%s]\r\n' % header).encode(self.encoding)) + for key_out, val in self.param_list: + val_out = u','.join(val) + fp.write((u'%s = %s\r\n' % (key_out, val_out)).encode(self.encoding)) + + def build_xml(self, xml_parent): + for key_ini, sid_list in self.param_list: + child = SubElement(xml_parent, 'Parameter') + key = SubElement(child, 'Key') + key.text = key_ini + for val_ini in sid_list: + value = SubElement(child, 'Value') + value.attrib['user_id'] = 'TRUE' + value.text = val_ini + + def from_xml(self, section): + for param in section.findall('Parameter'): + key = param.find('Key').text + + sid_list = [] + for val in param.findall('Value'): + value = val.text + if value is None: + value = '' + + sid_list.append(value) + + self.param_list.append((key, sid_list)) + + class NameModeACLParam(AbstractParam): + # param_list = [[Name, Mode, ACL],] + def parse(self, line): + parameters = [None, None, None] + index = 0 + current_arg = 0 + + while line != '': + # Read quoted string + if line[:1] == '"': + line = line[1:] + findex = line.find('"') + parameters[current_arg] = line[:findex] + line = line[findex + 1:] + # Skip past delimeter + elif line[:1] == ',': + line = line[1:] + current_arg += 1 + # Read unquoted string + else: + findex = line.find(',') + parameters[current_arg] = line[:findex] + line = line[findex:] + + # print parameters + # print line + self.param_list.append(parameters) + + def from_xml(self, section): + for param in section.findall('Parameter'): + key = param.find('Key').text + value = param.find('Value').text + if value is None: + value = '' + + self.param_list.append((key, value)) + + def write_section(self, header, fp): + if len(self.param_list) == 0: + return + fp.write((u'[%s]\r\n' % header).encode(self.encoding)) + for param in self.param_list: + fp.write((u'"%s",%s,"%s"\r\n' % tuple(param)).encode(self.encoding)) + + def build_xml(self, xml_parent): + for name_mode_acl in self.param_list: + child = SubElement(xml_parent, 'Parameter') + + value = SubElement(child, 'Value') + value.text = name_mode_acl[0] + + value = SubElement(child, 'Value') + value.text = name_mode_acl[1] + + value = SubElement(child, 'Value') + value.attrib['acl'] = 'TRUE' + value.text = name_mode_acl[2] + + def from_xml(self, section): + for param in section.findall('Parameter'): + name_mode_acl = [x.text if x.text else '' for x in param.findall('Value')] + self.param_list.append(name_mode_acl) + + class MemberSIDListParam(AbstractParam): + # param_list = [([XXXX, Memberof|Members], [SID, SID...]),...] + def parse(self, line): + key, val = line.split('=') + + key = key.strip() + + self.param_list.append((key.split('__'), + [x.strip() for x in val.split(',')])) + # print line + + def write_section(self, header, fp): + if len(self.param_list) == 0: + return + fp.write((u'[%s]\r\n' % header).encode(self.encoding)) + + for key, val in self.param_list: + key_out = u'__'.join(key) + val_out = u','.join(val) + fp.write((u'%s = %s\r\n' % (key_out, val_out)).encode(self.encoding)) + + def build_xml(self, xml_parent): + for key_ini, sid_list in self.param_list: + child = SubElement(xml_parent, 'Parameter') + key = SubElement(child, 'Key') + key.text = key_ini[0] + key.attrib['member_type'] = key_ini[1] + key.attrib['user_id'] = 'TRUE' + + for val_ini in sid_list: + value = SubElement(child, 'Value') + value.attrib['user_id'] = 'TRUE' + value.text = val_ini + + def from_xml(self, section): + for param in section.findall('Parameter'): + key = param.find('Key') + member_type = key.attrib['member_type'] + + sid_list = [] + for val in param.findall('Value'): + value = val.text + if value is None: + value = '' + + sid_list.append(value) + + self.param_list.append(([key.text, member_type], sid_list)) + + class UnicodeParam(AbstractParam): + def parse(self, line): + # print line + pass + + def write_section(self, header, fp): + fp.write(u'[Unicode]\r\nUnicode=yes\r\n'.encode(self.encoding)) + + def build_xml(self, xml_parent): + # We do not bother storing this field + pass + + def from_xml(self, section): + # We do not bother storing this field + pass + + class VersionParam(AbstractParam): + def parse(self, line): + # print line + pass + + def write_section(self, header, fp): + out = u'[Version]\r\nsignature="$CHICAGO$"\r\nRevision=1\r\n' + fp.write(out.encode(self.encoding)) + + def build_xml(self, xml_parent): + # We do not bother storing this field + pass + + def from_xml(self, section): + # We do not bother storing this field + pass + + def parse(self, contents): + inf_file = contents.decode(self.encoding) + + self.sections = collections.OrderedDict([ + (u'Unicode', self.UnicodeParam()), + (u'Version', self.VersionParam()), + + (u'System Access', self.IniParam()), + (u'Kerberos Policy', self.IniParam()), + (u'System Log', self.IniParam()), + (u'Security Log', self.IniParam()), + (u'Application Log', self.IniParam()), + (u'Event Audit', self.IniParam()), + (u'Registry Values', self.RegParam()), + (u'Privilege Rights', self.PrivSIDListParam()), + (u'Service General Setting', self.NameModeACLParam()), + (u'Registry Keys', self.NameModeACLParam()), + (u'File Security', self.NameModeACLParam()), + (u'Group Membership', self.MemberSIDListParam()), + ]) + + current_param_parser = None + current_header_name = None + + for line in inf_file.splitlines(): + match = re.match('\[(.*)\]', line) + if match: + header_name = match.group(1) + if header_name in self.sections: + current_param_parser = self.sections[header_name] + current_header_name = header_name + # print current_param_parser + continue + + # print 'using', current_param_parser + current_param_parser.parse(line) + + + def write_binary(self, filename): + with codecs.open(filename, 'wb+', + self.output_encoding) as f: + for s in self.sections: + self.sections[s].write_section(s, f) + + def write_xml(self, filename): + with file(filename, 'w') as f: + root = Element('GptTmplInfFile') + + for sec_inf in self.sections: + section = SubElement(root, 'Section') + section.attrib['name'] = sec_inf + + self.sections[sec_inf].build_xml(section) + + self.write_pretty_xml(root, f) + + # contents = codecs.open(filename, encoding='utf-8').read() + # self.load_xml(fromstring(contents)) + + def load_xml(self, root): + self.sections = collections.OrderedDict([ + (u'Unicode', self.UnicodeParam()), + (u'Version', self.VersionParam()), + + (u'System Access', self.IniParam()), + (u'Kerberos Policy', self.IniParam()), + (u'System Log', self.IniParam()), + (u'Security Log', self.IniParam()), + (u'Application Log', self.IniParam()), + (u'Event Audit', self.IniParam()), + (u'Registry Values', self.RegParam()), + (u'Privilege Rights', self.PrivSIDListParam()), + (u'Service General Setting', self.NameModeACLParam()), + (u'Registry Keys', self.NameModeACLParam()), + (u'File Security', self.NameModeACLParam()), + (u'Group Membership', self.MemberSIDListParam()), + ]) + + for s in root.findall('Section'): + self.sections[s.attrib['name']].from_xml(s)