From patchwork Tue Apr 9 02:39:29 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ronnie Sahlberg X-Patchwork-Id: 10890381 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id BEE6C922 for ; Tue, 9 Apr 2019 02:39:40 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A22802847E for ; Tue, 9 Apr 2019 02:39:40 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 8F59E28521; Tue, 9 Apr 2019 02:39:40 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8201A2847A for ; Tue, 9 Apr 2019 02:39:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726461AbfDICjj (ORCPT ); Mon, 8 Apr 2019 22:39:39 -0400 Received: from mx1.redhat.com ([209.132.183.28]:52176 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726456AbfDICji (ORCPT ); Mon, 8 Apr 2019 22:39:38 -0400 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 1E27C3078DD7; Tue, 9 Apr 2019 02:39:38 +0000 (UTC) Received: from test1135.test.redhat.com (vpn2-54-110.bne.redhat.com [10.64.54.110]) by smtp.corp.redhat.com (Postfix) with ESMTP id 063F55D719; Tue, 9 Apr 2019 02:39:36 +0000 (UTC) From: Ronnie Sahlberg To: linux-cifs Cc: Steve French , Pavel Shilovsky , Ronnie Sahlberg Subject: [PATCH] secdesc-ui.py: a UI to view the security descriptors on SMB2+ shares Date: Tue, 9 Apr 2019 12:39:29 +1000 Message-Id: <20190409023929.15098-2-lsahlber@redhat.com> In-Reply-To: <20190409023929.15098-1-lsahlber@redhat.com> References: <20190409023929.15098-1-lsahlber@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.43]); Tue, 09 Apr 2019 02:39:38 +0000 (UTC) Sender: linux-cifs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-cifs@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Signed-off-by: Ronnie Sahlberg --- secdesc-ui.py | 437 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 437 insertions(+) create mode 100755 secdesc-ui.py diff --git a/secdesc-ui.py b/secdesc-ui.py new file mode 100755 index 0000000..26db582 --- /dev/null +++ b/secdesc-ui.py @@ -0,0 +1,437 @@ +#!/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("", 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 " % (sys.argv[0]) + sys.exit() + + +class SID: + ''' + SID implements a Windows Security Identifier as per MS-DTYP:2.4.2. + ''' + 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('