lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date:   Sun, 31 Mar 2019 23:51:19 -0500
From:   Steve French <smfrench@...il.com>
To:     ronnie sahlberg <ronniesahlberg@...il.com>
Cc:     CIFS <linux-cifs@...r.kernel.org>,
        linux-fsdevel <linux-fsdevel@...r.kernel.org>,
        LKML <linux-kernel@...r.kernel.org>,
        samba-technical <samba-technical@...ts.samba.org>
Subject: Fwd: [PATCH] secdesc-ui.py: a UI to view the security descriptors on
 SMB2+ shares

The tool that Ronnie proposed below looks useful (see below) and
attached screenshot.  With this as a sample (along with 'smbinfo' tool
in cifs-utils) and a starting point, those with python/GUI interest
should be able to extend it in very interesting ways now that we have
the ability to query server information much more broadly.  Managing
ACLs, quotas, snapshots, alerts and many other fun features across
such a broad set of servers (from Samba, to Windows, Azure and the
Cloud, Macs, NetApp and various filers).

Ronnie,
Great idea.

---------- Forwarded message ---------
From: Ronnie Sahlberg <lsahlber@...hat.com>
Date: Sun, Mar 31, 2019 at 10:54 PM
Subject: [PATCH] secdesc-ui.py: a UI to view the security descriptors
on SMB2+ shares
To: linux-cifs <linux-cifs@...r.kernel.org>
Cc: Steve French <smfrench@...il.com>, Pavel Shilovsky
<pshilov@...rosoft.com>, Ronnie Sahlberg <lsahlber@...hat.com>

a simple python program with a basic UI to view the security descriptor
for SMB2+ resources.

With a basic starting point like this my hope is we can get some interest
from people with python skills that may want to make it better until
it becomes a full-fledged utility.

Signed-off-by: Ronnie Sahlberg <lsahlber@...hat.com>
---
 secdesc-ui.py | 421 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 421 insertions(+)
 create mode 100755 secdesc-ui.py

diff --git a/secdesc-ui.py b/secdesc-ui.py
new file mode 100755
index 0000000..dcb9dbf
--- /dev/null
+++ b/secdesc-ui.py
@@ -0,0 +1,421 @@
+#!/usr/bin/env python
+# coding: utf-8
+
+import array
+import enum
+import fcntl
+import os
+import struct
+import stat
+import sys
+from Tkinter import *
+
+FULL_CONTROL = 0x001f01ff
+EWRITE = 0x00000116
+ALL_READ_BITS = 0x00020089
+EREAD = 0x001200a9
+CHANGE = 0x001301bf
+
+TRAV_EXEC = 0x00100020
+LIST_READ = 0x00100001
+READ_ATTR = 0x00100080
+READ_XATT = 0x00100008
+CREA_WRIT = 0x00100002
+CREA_APPE = 0x00100004
+WRIT_ATTR = 0x00100100
+WRIT_XATT = 0x00100010
+DELE = 0x00110000
+READ_PERM = 0x00120000
+CHAN_PERM = 0x00140000
+TAKE_OWNR = 0x00180000
+
+class App:
+       def __init__(self, root, sd, is_dir):
+               self.sd = sd
+                self.is_dir = is_dir
+               self.tf = Frame(bd=1)
+               self.tf.grid(columnspan=5, rowspan=5, padx=5, pady=5)
+
+               # Owner
+               Label(self.tf, text='Owner: %s' %
(self.sd.owner)).grid(row=0, column=0, columnspan=6, sticky='W')
+
+               # Group
+               Label(self.tf, text='Group: %s' %
(self.sd.group)).grid(row=1, column=0, columnspan=6, sticky='W')
+
+               self.sb = Scrollbar(self.tf, orient=VERTICAL)
+               self.lb = Listbox(self.tf, height=5, selectmode=SINGLE,
+                               yscrollcommand=self.sb.set)
+               self.sb.config(command=self.lb.yview)
+               self.sb.grid(row=2, column=1, sticky='NS')
+               self.lb.grid(row=2, column=0, sticky='W')
+
+                max = 0
+               for idx, item in enumerate(self.sd.dacl.ace):
+                       if item.type != 0 and item.type != 1:
+                               continue
+                        sid = '%s %s' % ("ALLOW" if item.type == 0
else "DENY", item.sid)
+                        if max > len(sid):
+                                max = len(sid)
+                       self.lb.insert(idx, sid)
+                       if not self.lb.curselection():
+                               self.lb.selection_set(idx)
+                self.lb.config(width=max)
+                self.lb.bind("<Double-Button-1>", self.select_sid)
+
+               self.bas = Button(self.tf, text='Basic', relief=SUNKEN,
+                       command=self.click_bas)
+               self.bas.grid(row=2, column=2, sticky='NW')
+
+               self.adv = Button(self.tf, text='Advanced',
+                       command=self.click_adv)
+               self.adv.grid(row=2, column=3, sticky='NW')
+
+               # Basic Panel
+               self.bf_bas = Frame(master=self.tf, bd=1)
+               self.bf_bas.grid(row=3, column=0, columnspan=4, padx=5, pady=5)
+               self.bf_bas_name = Label(self.bf_bas, text='')
+               self.bf_bas_name.grid(row=0, column=0, columnspan=2, sticky='W')
+
+                row = 1
+               self.bf_bas_fc=Checkbutton(self.bf_bas, text='Full Control')
+               self.bf_bas_fc.grid(row=row, column=0, sticky='W')
+               self.bf_bas_fc.config(state=DISABLED)
+                row += 1
+
+               self.bf_bas_mo=Checkbutton(self.bf_bas, text='Modify')
+               self.bf_bas_mo.grid(row=row, column=0, sticky='W')
+               self.bf_bas_mo.config(state=DISABLED)
+                row += 1
+
+               self.bf_bas_re=Checkbutton(self.bf_bas, text='Read & Execute')
+               self.bf_bas_re.grid(row=row, column=0, sticky='W')
+               self.bf_bas_re.config(state=DISABLED)
+                row += 1
+
+               self.bf_bas_rd=Checkbutton(self.bf_bas, text='Read')
+               self.bf_bas_rd.grid(row=row, column=0, sticky='W')
+               self.bf_bas_rd.config(state=DISABLED)
+                row += 1
+
+               self.bf_bas_wr=Checkbutton(self.bf_bas, text='Write')
+               self.bf_bas_wr.grid(row=row, column=0, sticky='W')
+               self.bf_bas_wr.config(state=DISABLED)
+                row += 1
+
+               self.bf_bas_sp=Checkbutton(self.bf_bas, text='Special')
+               self.bf_bas_sp.grid(row=row, column=0, sticky='W')
+               self.bf_bas_sp.config(state=DISABLED)
+                row += 1
+
+               self.show_bas = True
+               self.update_bf_bas()
+
+               # Advanced Panel
+               self.bf_adv = Frame(master=self.tf, bd=1)
+               self.bf_adv.grid(row=3, column=0, columnspan=4, padx=5, pady=5)
+               self.bf_adv_name = Label(self.bf_adv, text='')
+               self.bf_adv_name.grid(row=0, column=0, columnspan=2, sticky='W')
+
+                row = 1
+               self.bf_adv_fc=Checkbutton(self.bf_adv, text='Full Control')
+               self.bf_adv_fc.grid(row=row, column=0, sticky='W')
+               self.bf_adv_fc.config(state=DISABLED)
+                row += 1
+
+               self.bf_adv_te=Checkbutton(self.bf_adv,
text='Traverse-folder/execute-file')
+               self.bf_adv_te.grid(row=row, column=0, sticky='W')
+               self.bf_adv_te.config(state=DISABLED)
+                row += 1
+
+               self.bf_adv_lr=Checkbutton(self.bf_adv,
text='List-folder/read-data')
+               self.bf_adv_lr.grid(row=row, column=0, sticky='W')
+               self.bf_adv_lr.config(state=DISABLED)
+                row += 1
+
+               self.bf_adv_ra=Checkbutton(self.bf_adv, text='Read-Attributes')
+               self.bf_adv_ra.grid(row=row, column=0, sticky='W')
+               self.bf_adv_ra.config(state=DISABLED)
+                row += 1
+
+               self.bf_adv_re=Checkbutton(self.bf_adv,
text='Read-Extended-Attributes')
+               self.bf_adv_re.grid(row=row, column=0, sticky='W')
+               self.bf_adv_re.config(state=DISABLED)
+                row += 1
+
+               self.bf_adv_cw=Checkbutton(self.bf_adv,
text='Create-files/write-data')
+               self.bf_adv_cw.grid(row=row, column=0, sticky='W')
+               self.bf_adv_cw.config(state=DISABLED)
+                row += 1
+
+               self.bf_adv_ca=Checkbutton(self.bf_adv,
text='Create-folders/append-data')
+               self.bf_adv_ca.grid(row=row, column=0, sticky='W')
+               self.bf_adv_ca.config(state=DISABLED)
+                row += 1
+
+                row = 1
+               self.bf_adv_wa=Checkbutton(self.bf_adv, text='Write-Attributes')
+               self.bf_adv_wa.grid(row=row, column=1, sticky='W')
+               self.bf_adv_wa.config(state=DISABLED)
+                row += 1
+
+               self.bf_adv_we=Checkbutton(self.bf_adv,
text='Write-Extended-Attributes')
+               self.bf_adv_we.grid(row=row, column=1, sticky='W')
+               self.bf_adv_we.config(state=DISABLED)
+                row += 1
+
+               self.bf_adv_de=Checkbutton(self.bf_adv, text='Delete')
+               self.bf_adv_de.grid(row=row, column=1, sticky='W')
+               self.bf_adv_de.config(state=DISABLED)
+                row += 1
+
+               self.bf_adv_rp=Checkbutton(self.bf_adv, text='Read-Permissions')
+               self.bf_adv_rp.grid(row=row, column=1, sticky='W')
+               self.bf_adv_rp.config(state=DISABLED)
+                row += 1
+
+               self.bf_adv_cp=Checkbutton(self.bf_adv,
text='Change-Permissions')
+               self.bf_adv_cp.grid(row=row, column=1, sticky='W')
+               self.bf_adv_cp.config(state=DISABLED)
+                row += 1
+
+               self.bf_adv_to=Checkbutton(self.bf_adv, text='Take-Ownership')
+               self.bf_adv_to.grid(row=row, column=1, sticky='W')
+               self.bf_adv_to.config(state=DISABLED)
+                row += 1
+
+               self.bf_adv.grid_remove()
+
+       def select_sid(self, event):
+                self.click_bas()
+
+       def click_bas(self):
+               self.adv.config(relief=RAISED)
+               self.bas.config(relief=SUNKEN)
+               self.bf_adv.grid_remove()
+               self.update_bf_bas()
+               self.bf_bas.grid()
+               self.show_bas = True
+
+       def click_adv(self):
+               self.adv.config(relief=SUNKEN)
+               self.bas.config(relief=RAISED)
+               self.bf_bas.grid_remove()
+               self.update_bf_adv()
+               self.bf_adv.grid()
+               self.show_bas = False
+
+       def update_bf_adv(self):
+               ace = self.sd.dacl.ace[self.lb.curselection()[0]]
+               self.bf_adv_name.config(text='Advanced Permissions for
%s' % (ace.sid))
+               if ace.mask == FULL_CONTROL:
+                       self.bf_adv_fc.select()
+               else:
+                       self.bf_adv_fc.deselect()
+               if ace.mask & TRAV_EXEC == TRAV_EXEC:
+                       self.bf_adv_te.select()
+               else:
+                       self.bf_adv_te.deselect()
+               if ace.mask & LIST_READ == LIST_READ:
+                       self.bf_adv_lr.select()
+               else:
+                       self.bf_adv_lr.deselect()
+               if ace.mask & READ_ATTR == READ_ATTR:
+                       self.bf_adv_ra.select()
+               else:
+                       self.bf_adv_ra.deselect()
+               if ace.mask & READ_XATT == READ_XATT:
+                       self.bf_adv_re.select()
+               else:
+                       self.bf_adv_re.deselect()
+               if ace.mask & CREA_WRIT == CREA_WRIT:
+                       self.bf_adv_cw.select()
+               else:
+                       self.bf_adv_cw.deselect()
+               if ace.mask & CREA_APPE == CREA_APPE:
+                       self.bf_adv_ca.select()
+               else:
+                       self.bf_adv_ca.deselect()
+               if ace.mask & WRIT_ATTR == WRIT_ATTR:
+                       self.bf_adv_wa.select()
+               else:
+                       self.bf_adv_wa.deselect()
+               if ace.mask & WRIT_XATT == WRIT_XATT:
+                       self.bf_adv_we.select()
+               else:
+                       self.bf_adv_we.deselect()
+               if ace.mask & DELE == DELE:
+                       self.bf_adv_de.select()
+               else:
+                       self.bf_adv_de.deselect()
+               if ace.mask & READ_PERM == READ_PERM:
+                       self.bf_adv_rp.select()
+               else:
+                       self.bf_adv_rp.deselect()
+               if ace.mask & CHAN_PERM == CHAN_PERM:
+                       self.bf_adv_rp.select()
+               else:
+                       self.bf_adv_rp.deselect()
+               if ace.mask & TAKE_OWNR == TAKE_OWNR:
+                       self.bf_adv_to.select()
+               else:
+                       self.bf_adv_to.deselect()
+
+       def update_bf_bas(self):
+               ace = self.sd.dacl.ace[self.lb.curselection()[0]]
+               self.bf_bas_name.config(text='Permissions for %s' % (ace.sid))
+               tmp = ace.mask
+               if ace.mask == FULL_CONTROL:
+                       self.bf_bas_fc.select()
+                       tmp &= ~FULL_CONTROL
+               else:
+                       self.bf_bas_fc.deselect()
+               if ace.mask & CHANGE == CHANGE:
+                       self.bf_bas_mo.select()
+                       tmp &= ~CHANGE
+               else:
+                       self.bf_bas_mo.deselect()
+               if ace.mask & EREAD == EREAD:
+                       self.bf_bas_re.select()
+                       tmp &= ~EREAD
+               else:
+                       self.bf_bas_re.deselect()
+               if ace.mask & ALL_READ_BITS == ALL_READ_BITS:
+                       self.bf_bas_rd.select()
+                       tmp &= ~ALL_READ_BITS
+               else:
+                       self.bf_bas_rd.deselect()
+               if ace.mask & EWRITE == EWRITE:
+                       self.bf_bas_wr.select()
+                       tmp &= ~EWRITE
+               else:
+                       self.bf_bas_wr.deselect()
+               if tmp:
+                       self.bf_bas_sp.select()
+               else:
+                       self.bf_bas_sp.deselect()
+
+CIFS_QUERY_INFO = 0xc018cf07
+
+def usage():
+        print "Usage: %s <filename>" % (sys.argv[0])
+        sys.exit()
+
+class SID:
+        def __init__(self, buf):
+                self.sub_authority_count = buf[1]
+                self.buffer = buf[:8 + self.sub_authority_count * 4]
+                self.revision = self.buffer[0]
+                if self.revision != 1:
+                        raise ValueError('SID Revision %d not supported' %
+                                         (self.revision))
+                self.identifier_authority = 0
+                for x in self.buffer[2:8]:
+                        self.identifier_authority =
self.identifier_authority * 256 + x
+                self.sub_authority = []
+                for i in range(self.sub_authority_count):
+
self.sub_authority.append(struct.unpack_from('<I', self.buffer, 8 + 4
* i)[0])
+
+        def __str__(self):
+                s = "S-%u-%u" % (self.revision, self.identifier_authority)
+
+                for x in self.sub_authority:
+                        s += '-%u' % x
+                return s
+
+
+class ACE:
+        def __init__(self, buf):
+                self.type = buf[0]
+                self.flags = buf[1]
+                self.size = struct.unpack_from('<H', buf, 2)[0]
+                self.raw = buf[:self.size]
+                if self.type in [0, 1]:
+                        self.mask = struct.unpack_from('<I', buf, 4)[0]
+                        self.sid = SID(buf[8:])
+
+        def __str__(self):
+                s = 'Type:0x%02x ' % (self.type)
+                s += 'Flags:0x%02x ' % (self.flags)
+                if self.type in [0, 1]:
+                        s += 'Mask:0x%02x SID:%s' % (self.mask, self.sid)
+                else:
+                        for i in self.raw[4:]:
+                                s += '%02x' % (i)
+
+                return s
+
+        class Type(enum.Enum):
+                ALLOWED = 0
+                DENIED = 1
+
+                def __str__(self):
+                        return self.name
+
+class ACL:
+        def __init__(self, buf):
+                self.revision = buf[0]
+                if self.revision != 2 and self.revision != 4:
+                        raise ValueError('ACL Revision %d '
+                                         'not supported' % (self.revision))
+                acl = buf[8:8 + struct.unpack_from('<H', buf, 2)[0]]
+                self.ace = []
+                for i in range(struct.unpack_from('<H', buf, 4)[0]):
+                        ace = ACE(acl)
+                        self.ace.append(ace)
+                        acl = acl[ace.size:]
+
+        def __str__(self):
+                s = 'Revision:0x%02x\n' % (self.revision)
+                for ace in self.ace:
+                        s += '%s\n' % (ace)
+                return s
+
+class SecurityDescriptor:
+        def __init__(self, buf):
+                self.revision = buf[0]
+                if self.revision != 1:
+                        raise ValueError('Security Descriptor Revision %d '
+                                         'not supported' % (self.revision))
+                self.control = struct.unpack_from('<H', buf, 2)
+
+                self.owner = SID(buf[struct.unpack_from('<I', buf, 4)[0]:])
+                self.group = SID(buf[struct.unpack_from('<I', buf, 8)[0]:])
+
+                self.dacl = ACL(buf[struct.unpack_from('<I', buf, 16)[0]:])
+
+        def __str__(self):
+                s = 'Revision:%u\n' % (self.revision)
+                s += 'Control:0x%04x\n' % (self.control)
+                s += 'Owner:%s\n' % (self.owner)
+                s += 'Group:%s\n' % (self.group)
+                s += 'DACL:\n%s' % (self.dacl)
+                return s
+
+def main():
+        if len(sys.argv) != 2:
+                usage()
+
+        buf = array.array('B', [0] * 16384)
+
+        struct.pack_into('<I', buf, 0, 3) # InfoType: Security
+        struct.pack_into('<I', buf, 8, 7) # AddInfo: Group/Owner/Dacl
+        struct.pack_into('<I', buf, 16, 16384) # InputBufferLength
+
+        #with open(sys.argv[1], 'r') as f:
+        f = os.open(sys.argv[1], os.O_RDONLY)
+        st = os.fstat(f)
+        fcntl.ioctl(f, CIFS_QUERY_INFO, buf, 1)
+       os.close(f)
+
+        s = struct.unpack_from('<I', buf, 16)
+
+        sd = SecurityDescriptor(buf[24:24 + s[0]])
+
+        root = Tk()
+        app = App(root, sd, stat.S_ISDIR(st.st_mode))
+        root.mainloop()
+
+
+if __name__ == "__main__":
+       main()
+
--
2.13.6



-- 
Thanks,

Steve

Download attachment "Screenshot-of-new-secdesc-ui-tool.png" of type "image/png" (43005 bytes)

View attachment "secdesc-ui.py" of type "text/x-python" (13324 bytes)

Powered by blists - more mailing lists