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