gp_csv: Parse the audit.csv file which records audit settings
authorGarming Sam <garming@catalyst.net.nz>
Thu, 10 May 2018 05:15:23 +0000 (17:15 +1200)
committerAndrew Bartlett <abartlet@samba.org>
Thu, 16 Aug 2018 21:42:21 +0000 (23:42 +0200)
Based on the setting, the csv will omit certain fields. Using this we
can later infer as to how to generalize the ACLs and SIDs.

Signed-off-by: Garming Sam <garming@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
python/samba/gp_parse/gp_csv.py [new file with mode: 0644]

diff --git a/python/samba/gp_parse/gp_csv.py b/python/samba/gp_parse/gp_csv.py
new file mode 100644 (file)
index 0000000..4c7e7cf
--- /dev/null
@@ -0,0 +1,164 @@
+# GPO Parser for audit 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/>.
+#
+
+import codecs
+import csv
+import io
+
+from io import BytesIO
+from xml.etree.ElementTree import Element, SubElement
+
+from samba.gp_parse import GPParser
+
+# [MS-GPAC] Group Policy Audit Configuration
+class GPAuditCsvParser(GPParser):
+    encoding = 'utf-8'
+    header = None
+    lines = []
+
+    def parse(self, contents):
+        self.lines = []
+        reader = UnicodeReader(BytesIO(contents),
+                               encoding=self.encoding)
+
+        self.header = reader.next()
+        for row in reader:
+            line = {}
+            for i, x in enumerate(row):
+                line[self.header[i]] = x
+
+            self.lines.append(line)
+            # print line
+
+    def write_xml(self, filename):
+        with file(filename, 'wb') as f:
+            root = Element('CsvFile')
+            child = SubElement(root, 'Row')
+            for e in self.header:
+                value = SubElement(child, 'Value')
+                value.text = e
+
+            for line in self.lines:
+                child = SubElement(root, 'Row')
+                for e in [line[x] for x in self.header]:
+                    value = SubElement(child, 'Value')
+                    value.text = e
+
+            self.write_pretty_xml(root, f)
+
+
+        # contents = codecs.open(filename, encoding='utf-8').read()
+        # self.load_xml(fromstring(contents))
+
+    def load_xml(self, root):
+        header = True
+        self.lines = []
+
+        for r in root.findall('Row'):
+            if header:
+                header = False
+                self.header = []
+                for v in r.findall('Value'):
+                    self.header.append(v.text.decode(self.output_encoding))
+            else:
+                line = {}
+                for i, v in enumerate(r.findall('Value')):
+                    line[self.header[i]] = v.text if v.text is not None else ''
+                    line[self.header[i]] = line[self.header[i]].decode(self.output_encoding)
+
+                self.lines.append(line)
+
+    def write_binary(self, filename):
+        with file(filename, 'wb') as f:
+            # This should be using a unicode writer, but it seems to be in the
+            # right encoding at least by default.
+            #
+            # writer = UnicodeWriter(f, quoting=csv.QUOTE_MINIMAL)
+            writer = csv.writer(f, quoting=csv.QUOTE_MINIMAL)
+            writer.writerow(self.header)
+            for line in self.lines:
+                writer.writerow([line[x] for x in self.header])
+
+
+# The following classes come from the Python documentation
+# https://docs.python.org/3.0/library/csv.html
+
+
+class UTF8Recoder:
+    """
+    Iterator that reads an encoded stream and reencodes the input to UTF-8
+    """
+    def __init__(self, f, encoding):
+        self.reader = codecs.getreader(encoding)(f)
+
+    def __iter__(self):
+        return self
+
+    def next(self):
+        return next(self.reader).encode("utf-8")
+
+    __next__ = next
+
+class UnicodeReader:
+    """
+    A CSV reader which will iterate over lines in the CSV file "f",
+    which is encoded in the given encoding.
+    """
+
+    def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds):
+        f = UTF8Recoder(f, encoding)
+        self.reader = csv.reader(f, dialect=dialect, **kwds)
+
+    def next(self):
+        row = next(self.reader)
+        return [unicode(s, "utf-8") for s in row]
+
+    def __iter__(self):
+        return self
+
+    __next__ = next
+
+class UnicodeWriter:
+    """
+    A CSV writer which will write rows to CSV file "f",
+    which is encoded in the given encoding.
+    """
+
+    def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds):
+        # Redirect output to a queue
+        self.queue = io.StringIO()
+        self.writer = csv.writer(self.queue, dialect=dialect, **kwds)
+        self.stream = f
+        self.encoder = codecs.getincrementalencoder(encoding)()
+
+    def writerow(self, row):
+        self.writer.writerow([s.encode("utf-8") for s in row])
+        # Fetch UTF-8 output from the queue ...
+        data = self.queue.getvalue()
+        data = data.decode("utf-8")
+        # ... and reencode it into the target encoding
+        data = self.encoder.encode(data)
+        # write to the target stream
+        self.stream.write(data)
+        # empty queue
+        self.queue.truncate(0)
+
+    def writerows(self, rows):
+        for row in rows:
+            self.writerow(row)