mirror of
https://github.com/samba-team/samba.git
synced 2024-12-24 21:34:56 +03:00
97fb06a260
Signed-off-by: Andreas Schneider <asn@samba.org> Reviewed-by: Andrew Bartlett <abartlet@samba.org>
186 lines
5.9 KiB
Python
186 lines
5.9 KiB
Python
# GPO Parser for generic extensions
|
|
#
|
|
# Copyright (C) Andrew Bartlett <abartlet@samba.org> 2018
|
|
# Written by Garming Sam <garming@catalyst.net.nz>
|
|
#
|
|
# 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 <http://www.gnu.org/licenses/>.
|
|
#
|
|
|
|
from xml.dom import minidom
|
|
from io import BytesIO
|
|
from xml.etree.ElementTree import ElementTree, fromstring, tostring
|
|
from hashlib import md5
|
|
from samba.common import get_bytes
|
|
|
|
|
|
ENTITY_USER_ID = 0
|
|
ENTITY_SDDL_ACL = 1
|
|
ENTITY_NETWORK_PATH = 2
|
|
|
|
|
|
class GPNoParserException(Exception):
|
|
pass
|
|
|
|
class GPGeneralizeException(Exception):
|
|
pass
|
|
|
|
|
|
def entity_type_to_string(ent_type):
|
|
type_str = None
|
|
|
|
if ent_type == ENTITY_USER_ID:
|
|
type_str = "USER_ID"
|
|
elif ent_type == ENTITY_SDDL_ACL:
|
|
type_str = "SDDL_ACL"
|
|
elif ent_type == ENTITY_NETWORK_PATH:
|
|
type_str = "NETWORK_PATH"
|
|
|
|
return type_str
|
|
|
|
|
|
# [MS-GPIPSEC] (LDAP)
|
|
# [MS-GPDPC] Deployed Printer Connections (LDAP)
|
|
# [MS-GPPREF] Preferences Extension (XML)
|
|
# [MS-GPWL] Wireless/Wired Protocol Extension (LDAP)
|
|
class GPParser(object):
|
|
encoding = 'utf-16'
|
|
output_encoding = 'utf-8'
|
|
|
|
def parse(self, contents):
|
|
pass
|
|
|
|
def write_xml(self, filename):
|
|
with open(filename, 'w') as f:
|
|
f.write('<?xml version="1.0" encoding="utf-8"?><UnknownFile/>')
|
|
|
|
def load_xml(self, filename):
|
|
pass
|
|
|
|
def write_binary(self, filename):
|
|
raise GPNoParserException("This file has no parser available.")
|
|
|
|
def write_pretty_xml(self, xml_element, handle):
|
|
# Add the xml header as well as format it nicely.
|
|
# ElementTree doesn't have a pretty-print, so use minidom.
|
|
|
|
et = ElementTree(xml_element)
|
|
temporary_bytes = BytesIO()
|
|
et.write(temporary_bytes, encoding=self.output_encoding,
|
|
xml_declaration=True)
|
|
minidom_parsed = minidom.parseString(temporary_bytes.getvalue())
|
|
handle.write(minidom_parsed.toprettyxml(encoding=self.output_encoding))
|
|
|
|
def new_xml_entity(self, name, ent_type):
|
|
identifier = md5(get_bytes(name)).hexdigest()
|
|
|
|
type_str = entity_type_to_string(ent_type)
|
|
|
|
if type_str is None:
|
|
raise GPGeneralizeException("No such entity type")
|
|
|
|
# For formatting reasons, align the length of the entities
|
|
longest = entity_type_to_string(ENTITY_NETWORK_PATH)
|
|
type_str = type_str.center(len(longest), '_')
|
|
|
|
return "&SAMBA__{}__{}__;".format(type_str, identifier)
|
|
|
|
def generalize_xml(self, root, out_file, global_entities):
|
|
entities = []
|
|
|
|
# Locate all user_id and all ACLs
|
|
user_ids = root.findall('.//*[@user_id="TRUE"]')
|
|
user_ids.sort(key = lambda x: x.tag)
|
|
|
|
for elem in user_ids:
|
|
old_text = elem.text
|
|
if old_text is None or old_text == '':
|
|
continue
|
|
|
|
if old_text in global_entities:
|
|
elem.text = global_entities[old_text]
|
|
entities.append((elem.text, old_text))
|
|
else:
|
|
elem.text = self.new_xml_entity(old_text,
|
|
ENTITY_USER_ID)
|
|
|
|
entities.append((elem.text, old_text))
|
|
global_entities.update([(old_text, elem.text)])
|
|
|
|
acls = root.findall('.//*[@acl="TRUE"]')
|
|
acls.sort(key = lambda x: x.tag)
|
|
|
|
for elem in acls:
|
|
old_text = elem.text
|
|
|
|
if old_text is None or old_text == '':
|
|
continue
|
|
|
|
if old_text in global_entities:
|
|
elem.text = global_entities[old_text]
|
|
entities.append((elem.text, old_text))
|
|
else:
|
|
elem.text = self.new_xml_entity(old_text,
|
|
ENTITY_SDDL_ACL)
|
|
|
|
entities.append((elem.text, old_text))
|
|
global_entities.update([(old_text, elem.text)])
|
|
|
|
share_paths = root.findall('.//*[@network_path="TRUE"]')
|
|
share_paths.sort(key = lambda x: x.tag)
|
|
|
|
for elem in share_paths:
|
|
old_text = elem.text
|
|
|
|
if old_text is None or old_text == '':
|
|
continue
|
|
|
|
stripped = old_text.lstrip('\\')
|
|
file_server = stripped.split('\\')[0]
|
|
|
|
server_index = old_text.find(file_server)
|
|
|
|
remaining = old_text[server_index + len(file_server):]
|
|
old_text = old_text[:server_index] + file_server
|
|
|
|
if old_text in global_entities:
|
|
elem.text = global_entities[old_text] + remaining
|
|
to_put = global_entities[old_text]
|
|
entities.append((to_put, old_text))
|
|
else:
|
|
to_put = self.new_xml_entity(old_text,
|
|
ENTITY_NETWORK_PATH)
|
|
elem.text = to_put + remaining
|
|
|
|
entities.append((to_put, old_text))
|
|
global_entities.update([(old_text, to_put)])
|
|
|
|
# Call any file specific customization of entities
|
|
# (which appear in any subclasses).
|
|
entities.extend(self.custom_entities(root, global_entities))
|
|
|
|
output_xml = tostring(root)
|
|
|
|
for ent in entities:
|
|
entb = get_bytes(ent[0])
|
|
output_xml = output_xml.replace(entb.replace(b'&', b'&'), entb)
|
|
|
|
with open(out_file, 'wb') as f:
|
|
f.write(output_xml)
|
|
|
|
return entities
|
|
|
|
def custom_entities(self, root, global_entities):
|
|
# Override this method to do special entity handling
|
|
return []
|