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@xxxxxxxxxx> 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@xxxxxxxxxxxxxxx> Cc: Steve French <smfrench@xxxxxxxxx>, Pavel Shilovsky <pshilov@xxxxxxxxxxxxx>, Ronnie Sahlberg <lsahlber@xxxxxxxxxx> 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@xxxxxxxxxx> --- 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:
Screenshot-of-new-secdesc-ui-tool.png
Description: PNG image
#!/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()