linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Fwd: [PATCH] secdesc-ui.py: a UI to view the security descriptors on SMB2+ shares
       [not found] ` <20190401035417.13738-2-lsahlber@redhat.com>
@ 2019-04-01  4:51   ` Steve French
  2019-04-01 19:23     ` Pavel Shilovsky
  0 siblings, 1 reply; 2+ messages in thread
From: Steve French @ 2019-04-01  4:51 UTC (permalink / raw)
  To: ronnie sahlberg; +Cc: CIFS, linux-fsdevel, LKML, samba-technical

[-- Attachment #1: Type: text/plain, Size: 18189 bytes --]

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@redhat.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@vger.kernel.org>
Cc: Steve French <smfrench@gmail.com>, Pavel Shilovsky
<pshilov@microsoft.com>, Ronnie Sahlberg <lsahlber@redhat.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@redhat.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

[-- Attachment #2: Screenshot-of-new-secdesc-ui-tool.png --]
[-- Type: image/png, Size: 43005 bytes --]

[-- Attachment #3: secdesc-ui.py --]
[-- Type: text/x-python, Size: 13324 bytes --]

#!/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()


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH] secdesc-ui.py: a UI to view the security descriptors on SMB2+ shares
  2019-04-01  4:51   ` Fwd: [PATCH] secdesc-ui.py: a UI to view the security descriptors on SMB2+ shares Steve French
@ 2019-04-01 19:23     ` Pavel Shilovsky
  0 siblings, 0 replies; 2+ messages in thread
From: Pavel Shilovsky @ 2019-04-01 19:23 UTC (permalink / raw)
  To: Steve French; +Cc: ronnie sahlberg, linux-fsdevel, CIFS, samba-technical, LKML

This is really cool! Thanks Ronnie. I will be targeting this for the
next release of cifs-utils (not the one that I am about to cut off),
so we will have time to stabilize it.

Best regards,
Pavel Shilovsky

вс, 31 мар. 2019 г. в 21:51, Steve French via samba-technical
<samba-technical@lists.samba.org>:

>
> 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@redhat.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@vger.kernel.org>
> Cc: Steve French <smfrench@gmail.com>, Pavel Shilovsky
> <pshilov@microsoft.com>, Ronnie Sahlberg <lsahlber@redhat.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@redhat.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

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2019-04-01 19:24 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <20190401035417.13738-1-lsahlber@redhat.com>
     [not found] ` <20190401035417.13738-2-lsahlber@redhat.com>
2019-04-01  4:51   ` Fwd: [PATCH] secdesc-ui.py: a UI to view the security descriptors on SMB2+ shares Steve French
2019-04-01 19:23     ` Pavel Shilovsky

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).