man-pages: Update cifs.upcall to mention GSS_USE_PROXY
[cifs-utils.git] / smb2-secdesc
1 #!/usr/bin/env python
2 # coding: utf-8
3
4 import array
5 import enum
6 import fcntl
7 import os
8 import struct
9 import stat
10 import sys
11 from Tkinter import *
12
13 FULL_CONTROL = 0x001f01ff
14 EWRITE = 0x00000116
15 ALL_READ_BITS = 0x00020089
16 EREAD = 0x001200a9
17 CHANGE = 0x001301bf
18
19 TRAV_EXEC = 0x00100020
20 LIST_READ = 0x00100001
21 READ_ATTR = 0x00100080
22 READ_XATT = 0x00100008
23 CREA_WRIT = 0x00100002
24 CREA_APPE = 0x00100004
25 WRIT_ATTR = 0x00100100
26 WRIT_XATT = 0x00100010
27 DELE = 0x00110000
28 READ_PERM = 0x00120000
29 CHAN_PERM = 0x00140000
30 TAKE_OWNR = 0x00180000
31
32 class App:
33         def __init__(self, root, sd, is_dir):
34                 self.sd = sd
35                 self.is_dir = is_dir
36                 self.tf = Frame(bd=1)
37                 self.tf.grid(columnspan=5, rowspan=5, padx=5, pady=5)
38
39                 # Owner
40                 Label(self.tf, text='Owner: %s' % (self.sd.owner)).grid(row=0, column=0, columnspan=6, sticky='W')
41
42                 # Group
43                 Label(self.tf, text='Group: %s' % (self.sd.group)).grid(row=1, column=0, columnspan=6, sticky='W')
44
45                 self.sb = Scrollbar(self.tf, orient=VERTICAL)
46                 self.lb = Listbox(self.tf, height=5, selectmode=SINGLE,
47                                 yscrollcommand=self.sb.set)
48                 self.sb.config(command=self.lb.yview)
49                 self.sb.grid(row=2, column=1, sticky='NS')
50                 self.lb.grid(row=2, column=0, sticky='W')
51
52                 max = 0
53                 for idx, item in enumerate(self.sd.dacl.ace):
54                         if item.type != 0 and item.type != 1:
55                                 continue
56                         sid = '%s %s' % ("ALLOW" if item.type == 0 else "DENY", item.sid)
57                         if max > len(sid):
58                                 max = len(sid)
59                         self.lb.insert(idx, sid)
60                         if not self.lb.curselection():
61                                 self.lb.selection_set(idx)
62                 self.lb.config(width=max)
63                 self.lb.bind("<Double-Button-1>", self.select_sid)
64
65                 self.bas = Button(self.tf, text='Basic', relief=SUNKEN,
66                         command=self.click_bas)
67                 self.bas.grid(row=2, column=2, sticky='NW')
68
69                 self.adv = Button(self.tf, text='Advanced',
70                         command=self.click_adv)
71                 self.adv.grid(row=2, column=3, sticky='NW')
72
73                 # Basic Panel
74                 self.bf_bas = Frame(master=self.tf, bd=1)
75                 self.bf_bas.grid(row=3, column=0, columnspan=4, padx=5, pady=5)
76                 self.bf_bas_name = Label(self.bf_bas, text='')
77                 self.bf_bas_name.grid(row=0, column=0, columnspan=2, sticky='W')
78
79                 row = 1
80                 self.bf_bas_fc=Checkbutton(self.bf_bas, text='Full Control')
81                 self.bf_bas_fc.grid(row=row, column=0, sticky='W')
82                 self.bf_bas_fc.config(state=DISABLED)
83                 row += 1
84
85                 self.bf_bas_mo=Checkbutton(self.bf_bas, text='Modify')
86                 self.bf_bas_mo.grid(row=row, column=0, sticky='W')
87                 self.bf_bas_mo.config(state=DISABLED)
88                 row += 1
89
90                 self.bf_bas_re=Checkbutton(self.bf_bas, text='Read & Execute')
91                 self.bf_bas_re.grid(row=row, column=0, sticky='W')
92                 self.bf_bas_re.config(state=DISABLED)
93                 row += 1
94
95                 self.bf_bas_rd=Checkbutton(self.bf_bas, text='Read')
96                 self.bf_bas_rd.grid(row=row, column=0, sticky='W')
97                 self.bf_bas_rd.config(state=DISABLED)
98                 row += 1
99
100                 self.bf_bas_wr=Checkbutton(self.bf_bas, text='Write')
101                 self.bf_bas_wr.grid(row=row, column=0, sticky='W')
102                 self.bf_bas_wr.config(state=DISABLED)
103                 row += 1
104
105                 self.bf_bas_sp=Checkbutton(self.bf_bas, text='Special')
106                 self.bf_bas_sp.grid(row=row, column=0, sticky='W')
107                 self.bf_bas_sp.config(state=DISABLED)
108                 row += 1
109
110                 self.show_bas = True
111                 self.update_bf_bas()
112
113                 # Advanced Panel
114                 self.bf_adv = Frame(master=self.tf, bd=1)
115                 self.bf_adv.grid(row=3, column=0, columnspan=4, padx=5, pady=5)
116                 self.bf_adv_name = Label(self.bf_adv, text='')
117                 self.bf_adv_name.grid(row=0, column=0, columnspan=2, sticky='W')
118
119                 row = 1
120                 self.bf_adv_fc=Checkbutton(self.bf_adv, text='Full Control')
121                 self.bf_adv_fc.grid(row=row, column=0, sticky='W')
122                 self.bf_adv_fc.config(state=DISABLED)
123                 row += 1
124
125                 self.bf_adv_te=Checkbutton(self.bf_adv, text='Traverse-folder/execute-file')
126                 self.bf_adv_te.grid(row=row, column=0, sticky='W')
127                 self.bf_adv_te.config(state=DISABLED)
128                 row += 1
129
130                 self.bf_adv_lr=Checkbutton(self.bf_adv, text='List-folder/read-data')
131                 self.bf_adv_lr.grid(row=row, column=0, sticky='W')
132                 self.bf_adv_lr.config(state=DISABLED)
133                 row += 1
134
135                 self.bf_adv_ra=Checkbutton(self.bf_adv, text='Read-Attributes')
136                 self.bf_adv_ra.grid(row=row, column=0, sticky='W')
137                 self.bf_adv_ra.config(state=DISABLED)
138                 row += 1
139
140                 self.bf_adv_re=Checkbutton(self.bf_adv, text='Read-Extended-Attributes')
141                 self.bf_adv_re.grid(row=row, column=0, sticky='W')
142                 self.bf_adv_re.config(state=DISABLED)
143                 row += 1
144
145                 self.bf_adv_cw=Checkbutton(self.bf_adv, text='Create-files/write-data')
146                 self.bf_adv_cw.grid(row=row, column=0, sticky='W')
147                 self.bf_adv_cw.config(state=DISABLED)
148                 row += 1
149
150                 self.bf_adv_ca=Checkbutton(self.bf_adv, text='Create-folders/append-data')
151                 self.bf_adv_ca.grid(row=row, column=0, sticky='W')
152                 self.bf_adv_ca.config(state=DISABLED)
153                 row += 1
154
155                 row = 1
156                 self.bf_adv_wa=Checkbutton(self.bf_adv, text='Write-Attributes')
157                 self.bf_adv_wa.grid(row=row, column=1, sticky='W')
158                 self.bf_adv_wa.config(state=DISABLED)
159                 row += 1
160
161                 self.bf_adv_we=Checkbutton(self.bf_adv, text='Write-Extended-Attributes')
162                 self.bf_adv_we.grid(row=row, column=1, sticky='W')
163                 self.bf_adv_we.config(state=DISABLED)
164                 row += 1
165
166                 self.bf_adv_de=Checkbutton(self.bf_adv, text='Delete')
167                 self.bf_adv_de.grid(row=row, column=1, sticky='W')
168                 self.bf_adv_de.config(state=DISABLED)
169                 row += 1
170
171                 self.bf_adv_rp=Checkbutton(self.bf_adv, text='Read-Permissions')
172                 self.bf_adv_rp.grid(row=row, column=1, sticky='W')
173                 self.bf_adv_rp.config(state=DISABLED)
174                 row += 1
175
176                 self.bf_adv_cp=Checkbutton(self.bf_adv, text='Change-Permissions')
177                 self.bf_adv_cp.grid(row=row, column=1, sticky='W')
178                 self.bf_adv_cp.config(state=DISABLED)
179                 row += 1
180
181                 self.bf_adv_to=Checkbutton(self.bf_adv, text='Take-Ownership')
182                 self.bf_adv_to.grid(row=row, column=1, sticky='W')
183                 self.bf_adv_to.config(state=DISABLED)
184                 row += 1
185
186                 self.bf_adv.grid_remove()
187
188         def select_sid(self, event):
189                 self.click_bas()
190
191         def click_bas(self):
192                 self.adv.config(relief=RAISED)
193                 self.bas.config(relief=SUNKEN)
194                 self.bf_adv.grid_remove()
195                 self.update_bf_bas()
196                 self.bf_bas.grid()
197                 self.show_bas = True
198
199         def click_adv(self):
200                 self.adv.config(relief=SUNKEN)
201                 self.bas.config(relief=RAISED)
202                 self.bf_bas.grid_remove()
203                 self.update_bf_adv()
204                 self.bf_adv.grid()
205                 self.show_bas = False
206
207         def update_bf_adv(self):
208                 ace = self.sd.dacl.ace[self.lb.curselection()[0]]
209                 self.bf_adv_name.config(text='Advanced Permissions for %s' % (ace.sid))
210                 if ace.mask == FULL_CONTROL:
211                         self.bf_adv_fc.select()
212                 else:
213                         self.bf_adv_fc.deselect()
214                 if ace.mask & TRAV_EXEC == TRAV_EXEC:
215                         self.bf_adv_te.select()
216                 else:
217                         self.bf_adv_te.deselect()
218                 if ace.mask & LIST_READ == LIST_READ:
219                         self.bf_adv_lr.select()
220                 else:
221                         self.bf_adv_lr.deselect()
222                 if ace.mask & READ_ATTR == READ_ATTR:
223                         self.bf_adv_ra.select()
224                 else:
225                         self.bf_adv_ra.deselect()
226                 if ace.mask & READ_XATT == READ_XATT:
227                         self.bf_adv_re.select()
228                 else:
229                         self.bf_adv_re.deselect()
230                 if ace.mask & CREA_WRIT == CREA_WRIT:
231                         self.bf_adv_cw.select()
232                 else:
233                         self.bf_adv_cw.deselect()
234                 if ace.mask & CREA_APPE == CREA_APPE:
235                         self.bf_adv_ca.select()
236                 else:
237                         self.bf_adv_ca.deselect()
238                 if ace.mask & WRIT_ATTR == WRIT_ATTR:
239                         self.bf_adv_wa.select()
240                 else:
241                         self.bf_adv_wa.deselect()
242                 if ace.mask & WRIT_XATT == WRIT_XATT:
243                         self.bf_adv_we.select()
244                 else:
245                         self.bf_adv_we.deselect()
246                 if ace.mask & DELE == DELE:
247                         self.bf_adv_de.select()
248                 else:
249                         self.bf_adv_de.deselect()
250                 if ace.mask & READ_PERM == READ_PERM:
251                         self.bf_adv_rp.select()
252                 else:
253                         self.bf_adv_rp.deselect()
254                 if ace.mask & CHAN_PERM == CHAN_PERM:
255                         self.bf_adv_rp.select()
256                 else:
257                         self.bf_adv_rp.deselect()
258                 if ace.mask & TAKE_OWNR == TAKE_OWNR:
259                         self.bf_adv_to.select()
260                 else:
261                         self.bf_adv_to.deselect()
262
263         def update_bf_bas(self):
264                 ace = self.sd.dacl.ace[self.lb.curselection()[0]]
265                 self.bf_bas_name.config(text='Permissions for %s' % (ace.sid))
266                 tmp = ace.mask
267                 if ace.mask == FULL_CONTROL:
268                         self.bf_bas_fc.select()
269                         tmp &= ~FULL_CONTROL
270                 else:
271                         self.bf_bas_fc.deselect()
272                 if ace.mask & CHANGE == CHANGE:
273                         self.bf_bas_mo.select()
274                         tmp &= ~CHANGE
275                 else:
276                         self.bf_bas_mo.deselect()
277                 if ace.mask & EREAD == EREAD:
278                         self.bf_bas_re.select()
279                         tmp &= ~EREAD
280                 else:
281                         self.bf_bas_re.deselect()
282                 if ace.mask & ALL_READ_BITS == ALL_READ_BITS:
283                         self.bf_bas_rd.select()
284                         tmp &= ~ALL_READ_BITS
285                 else:
286                         self.bf_bas_rd.deselect()
287                 if ace.mask & EWRITE == EWRITE:
288                         self.bf_bas_wr.select()
289                         tmp &= ~EWRITE
290                 else:
291                         self.bf_bas_wr.deselect()
292                 if tmp:
293                         self.bf_bas_sp.select()
294                 else:
295                         self.bf_bas_sp.deselect()
296
297 CIFS_QUERY_INFO = 0xc018cf07
298
299 def usage():
300         print "Usage: %s <filename>" % (sys.argv[0])
301         sys.exit()
302
303
304 class SID:
305         '''
306         SID implements a Windows Security Identifier as per MS-DTYP:2.4.2.
307         '''
308         def __init__(self, buf):
309                 self.sub_authority_count = buf[1]
310                 self.buffer = buf[:8 + self.sub_authority_count * 4]
311                 self.revision = self.buffer[0]
312                 if self.revision != 1:
313                         raise ValueError('SID Revision %d not supported' %
314                                          (self.revision))
315                 self.identifier_authority = 0
316                 for x in self.buffer[2:8]:
317                         self.identifier_authority = self.identifier_authority * 256 + x
318                 self.sub_authority = []
319                 for i in range(self.sub_authority_count):
320                         self.sub_authority.append(struct.unpack_from('<I', self.buffer, 8 + 4 * i)[0])
321
322         def __str__(self):
323                 s = "S-%u-%u" % (self.revision, self.identifier_authority)
324
325                 for x in self.sub_authority:
326                         s += '-%u' % x
327                 return s
328
329
330 class ACE:
331         '''
332         ACE implements a Windows ACE as per MS-DTYP:2.4.4.
333         '''
334         def __init__(self, buf):
335                 self.type = buf[0]
336                 self.flags = buf[1]
337                 self.size = struct.unpack_from('<H', buf, 2)[0]
338                 self.raw = buf[:self.size]
339                 if self.type in [0, 1]:
340                         self.mask = struct.unpack_from('<I', buf, 4)[0]
341                         self.sid = SID(buf[8:])
342
343         def __str__(self):
344                 s = 'Type:0x%02x ' % (self.type)
345                 s += 'Flags:0x%02x ' % (self.flags)
346                 if self.type in [0, 1]:
347                         s += 'Mask:0x%02x SID:%s' % (self.mask, self.sid)
348                 else:
349                         for i in self.raw[4:]:
350                                 s += '%02x' % (i)
351
352                 return s
353
354         class Type(enum.Enum):
355                 ALLOWED = 0
356                 DENIED = 1
357
358                 def __str__(self):
359                         return self.name
360
361
362 class ACL:
363         '''
364         ACL implements a Windows ACL as per MS-DTYP:2.4.5.
365         '''
366         def __init__(self, buf):
367                 self.revision = buf[0]
368                 if self.revision != 2 and self.revision != 4:
369                         raise ValueError('ACL Revision %d '
370                                          'not supported' % (self.revision))
371                 acl = buf[8:8 + struct.unpack_from('<H', buf, 2)[0]]
372                 self.ace = []
373                 for i in range(struct.unpack_from('<H', buf, 4)[0]):
374                         ace = ACE(acl)
375                         self.ace.append(ace)
376                         acl = acl[ace.size:]
377
378         def __str__(self):
379                 s = 'Revision:0x%02x\n' % (self.revision)
380                 for ace in self.ace:
381                         s += '%s\n' % (ace)
382                 return s
383
384
385 class SecurityDescriptor:
386         '''
387         SecurityDescriptor implements a Windows Security Descriptor as per
388         MS-DTYP:2.4.6.
389         '''
390         def __init__(self, buf):
391                 self.revision = buf[0]
392                 if self.revision != 1:
393                         raise ValueError('Security Descriptor Revision %d '
394                                          'not supported' % (self.revision))
395                 self.control = struct.unpack_from('<H', buf, 2)
396
397                 self.owner = SID(buf[struct.unpack_from('<I', buf, 4)[0]:])
398                 self.group = SID(buf[struct.unpack_from('<I', buf, 8)[0]:])
399
400                 self.dacl = ACL(buf[struct.unpack_from('<I', buf, 16)[0]:])
401
402         def __str__(self):
403                 s = 'Revision:%u\n' % (self.revision)
404                 s += 'Control:0x%04x\n' % (self.control)
405                 s += 'Owner:%s\n' % (self.owner)
406                 s += 'Group:%s\n' % (self.group)
407                 s += 'DACL:\n%s' % (self.dacl)
408                 return s
409
410
411 def main():
412         if len(sys.argv) != 2:
413                 usage()
414
415         buf = array.array('B', [0] * 16384)
416
417         struct.pack_into('<I', buf, 0, 3) # InfoType: Security
418         struct.pack_into('<I', buf, 8, 7) # AddInfo: Group/Owner/Dacl
419         struct.pack_into('<I', buf, 16, 16384) # InputBufferLength
420
421         f = os.open(sys.argv[1], os.O_RDONLY)
422         st = os.fstat(f)
423         fcntl.ioctl(f, CIFS_QUERY_INFO, buf, 1)
424         os.close(f)
425
426         s = struct.unpack_from('<I', buf, 16)
427
428         sd = SecurityDescriptor(buf[24:24 + s[0]])
429
430         root = Tk()
431         app = App(root, sd, stat.S_ISDIR(st.st_mode))
432         root.mainloop()
433
434
435 if __name__ == "__main__":
436         main()