source: tools/suggest/suggest.py @ c8c3208

abac0-leakabac0-meicompt_changesmei-idmei-rt0-nmei_rt0tvf-new-xml
Last change on this file since c8c3208 was c8c3208, checked in by Ted Faber <faber@…>, 12 years ago

Command line file

  • Property mode set to 100644
File size: 9.9 KB
Line 
1#!/usr/local/bin/python
2
3import gtk
4import gobject
5
6import ABAC
7import Creddy
8
9import sys, os
10import re
11import copy
12import base64
13import ConfigParser
14
15from tempfile import mkdtemp
16from shutil import rmtree
17
18class proof:
19    def __init__(self, name, prover, role, principal, creds):
20        self.name = name
21        self.prover = prover
22        self.role = role
23        self.principal = principal
24        self.ctxt = ABAC.Context()
25        self.keyid_to_cn = { }
26        self.cn_to_keyid = { }
27        attrs = []
28        try:
29            d = mkdtemp()
30            for c in creds:
31                cc = self.pem_to_der(c)
32                succ = self.ctxt.load_id_chunk(cc) 
33                if succ == ABAC.ABAC_CERT_SUCCESS: 
34                    try:
35                        fn = os.path.join(d, 'file.pem')
36                        f = open(fn, 'w')
37                        f.write(cc)
38                        f.close()
39                        cid = Creddy.ID(fn)
40                        base_cn = cn = re.sub('_ID.pem$','',cid.cert_filename())
41                        i = 0
42                        while cn in self.cn_to_keyid:
43                            cn = '%s%03d' % (base_cn, i)
44                            i += 1
45                        self.cn_to_keyid[cn] = cid.keyid()
46                        self.keyid_to_cn[cid.keyid()] = cn
47                    except EnvironmentError, e:
48                        print >>sys.stderr, '%s: %s' % (e.filename, e.strerror)
49                else:
50                    attrs.append(cc)
51            for c in attrs:
52                self.ctxt.load_attribute_chunk(c) 
53        finally:
54            rmtree(d)
55
56
57    @staticmethod
58    def pem_to_der(c):
59        pat = '-----BEGIN CERTIFICATE-----(.*)-----END CERTIFICATE'
60        m = re.match(pat, c, re.DOTALL)
61        if m: return base64.b64decode(m.group(1))
62        else: return c
63
64    def replace_keyids(self, s):
65        for k, v in self.keyid_to_cn.items():
66            s = re.sub(k, v, s)
67        return s
68
69    def __str__(self):
70        s = 'Name: %s\n' % self.name
71        s += 'Prover: %s\n' % self.prover
72        s += 'Principal: %s\n' % self.principal
73        s += 'Role: %s\n' % self.role
74        s += 'Creds: \n'
75        for c in self.ctxt.credentials():
76            s += self.replace_keyids(
77                    '%s <- %s\n' % ( c.head().string(), c.tail().string()))
78        return s
79
80class window(gtk.Window):
81    '''
82    The main GUI class.  It presents the various TreeViews and menus to
83    save/load/add, to add credentials, identities and actions and to change the
84    policy translation variable.  It keeps its current size and location in the
85    .abac_policy_tool.cfg file in the user's home.
86    '''
87
88    # Definition of the menus
89    ui_def = '''
90    <ui>
91        <menubar>
92            <menu action="FileMenu">
93                <menuitem name="Load" action="FileLoad"/>
94                <menuitem name="Quit" action="FileQuit"/>
95            </menu>
96        </menubar>
97    </ui>
98    '''
99    # Path to the configuration
100    cfg_path = os.path.join(os.path.expanduser('~'), '.abac_proof_explainer.cfg')
101
102    @staticmethod
103    def wrapit(widget):
104        '''
105        Put widget into a ScrolledWindow with automatic scrollbars on both
106        directions, and return the ScrolledWindow.
107        '''
108        sw = gtk.ScrolledWindow()
109        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
110        sw.add(widget)
111        return sw
112
113    def translate_keyids(self, s):
114        for k, n in self.key_to_name:
115            s = re.sub(k, n, s)
116        return s
117
118    def makeListView(self, l, title):
119        lm = gtk.ListStore(gobject.TYPE_STRING)
120        tv = gtk.TreeView(lm)
121        tv.append_column(gtk.TreeViewColumn(title, 
122            gtk.CellRendererText(), text=0))
123        for v in l:
124            lm.append((self.translate_keyids(v),))
125        return tv
126
127
128    def report_error(self, message):
129        '''
130        Put a MessageDialog up with the given message.  This is a member method
131        so that it can be centered on the window.
132        '''
133        md = gtk.MessageDialog(self, gtk.DIALOG_MODAL, 
134                gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, 
135                message)
136        md.run()
137        md.destroy()
138
139    def __init__(self, fn):
140        '''
141        Initialize all the GTK hooks for menus, put the various TreeViews up
142        (connected to the policy) and read teh configuration for current
143        position.
144        '''
145        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
146        self.key_to_name = []
147        try:
148            f = open('./names', 'r')
149            for l in f:
150                m = re.match('(\\S+)\s+(\\S+)', l)
151                if m:
152                    self.key_to_name.append((m.group(1), m.group(2)))
153            f.close()
154        except EnvironmentError:
155            pass
156
157        self.set_title('ABAC Policy Tool')
158        self.connect('destroy', self.quit)
159        self.connect('show', self.shown)
160        self.connect('configure-event', self.changed)
161        self.pos = (0,0)
162        self.size = (500, 500)
163        self.read_config()
164        self.proofs = []
165
166        # Make the Menus real
167        ui = gtk.UIManager()
168        ag = gtk.ActionGroup('action')
169        ag.add_actions((
170            ('FileMenu', None, 'File'),
171            ('FileQuit', gtk.STOCK_QUIT, None, None, None, self.quit),
172            ))
173        # load and append call the same method with different user data -
174        # whether to clear current policy or not.
175        ag.add_actions((
176            ('FileLoad', gtk.STOCK_OPEN, None, None, None, self.load),
177            ), True)
178        ui.insert_action_group(ag, -1)
179        ui.add_ui_from_string(window.ui_def)
180
181        # Put it all together and show it.
182        mb = ui.get_widget('ui/menubar')
183        vb = gtk.VBox()
184        vb.pack_start(mb, False, False, 0)
185        #vb.pack_start(nb, True, True, 0)
186
187        self.add(vb)
188        if fn is not None:
189            self.read_proofs(fn)
190            # XXX multiple proofs
191            self.get_child().add(self.interpret_proof(self.proofs[0]))
192        self.show_all()
193   
194    def quit(self, widget=None, data=None):
195        '''
196        Called from File->Quit in the menu.  Save location/size and exit
197        '''
198        self.save_config()
199        gtk.main_quit()
200
201    def load(self, widget=None, data=None):
202        '''
203        Called to either load or append to the loaded policy.  data is the
204        clearit parameter to the load call.  Other than that, just put up a
205        requester and do the thing.
206        '''
207        d = gtk.FileChooserDialog('Load file', self, 
208                gtk.FILE_CHOOSER_ACTION_OPEN, (
209            gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
210            gtk.STOCK_OK, gtk.RESPONSE_OK))
211        d.set_select_multiple(False)
212        d.set_current_folder('.')
213        d.set_do_overwrite_confirmation(True)
214        rv = d.run()
215        d.hide()
216        if rv == gtk.RESPONSE_OK:
217            self.read_proofs(d.get_filename())
218            # XXX multiple proofs
219            self.get_child().add(self.interpret_proof(self.proofs[0]))
220            self.show_all()
221        d.destroy()
222
223
224    def shown(self, w):
225        '''
226        Handles an event where the window appears.  Move to the saved position
227        and size.
228        '''
229        self.move(*self.pos)
230        self.resize(*self.size)
231
232    def changed(self, w, e):
233        '''
234        Handles an event where the window changes (resizes or moves).  Remember
235        the size and position.
236        '''
237        self.pos = self.get_position()
238        self.size = self.get_size()
239
240    def get_intpair(self, sect, opt):
241        '''
242        Utility to pull a pair of integers from a configuration file.  The size
243        and position are thsi kind of data, so this is used a couple places.
244        '''
245        if not self.cfg.has_section(sect):
246            self.cfg.add_section(sect)
247
248        if self.cfg.has_option(sect, opt):
249            try:
250                return [int(x) for x in self.cfg.get(sect, opt).split(',', 1)]
251            except ValueError:
252                return None
253        else:
254            return None
255
256    def read_config(self):
257        '''
258        Get the saved size and position from the config file, if any
259        '''
260        self.cfg = ConfigParser.SafeConfigParser()
261        self.cfg.read(window.cfg_path)
262
263        self.pos = self.get_intpair('geom', 'pos') or ( 0, 0)
264        self.size = self.get_intpair('geom', 'size') or ( 500, 500)
265
266
267    def save_config(self):
268        '''
269        Save the current postion to the default config file.
270        '''
271        self.cfg.set('geom', 'pos', '%d,%d' % self.pos)
272        self.cfg.set('geom', 'size', '%d,%d' % self.size)
273        try:
274            f = open(window.cfg_path, 'w')
275            self.cfg.write(f)
276            f.close()
277        except EnvironmentError, e:
278            pass
279
280    def read_proofs(self, fn):
281        self.proofs = []
282        try:
283            f = open(fn, 'r')
284            creds = []
285            for line in f:
286                line = line.strip()
287                if line == '<proof>':
288                    prover = None
289                    principal = None
290                    role = None
291                    creds = []
292                elif line == '</proof>' :
293                    p = proof(name, prover, role, principal, creds)
294                    ok, pp = p.ctxt.query(role, principal)
295                    if not ok: self.proofs.append(p)
296               
297                m = re.match('<comment>(.*)</comment>', line)
298                if m is not None:
299                    name = m.group(1)
300
301                m = re.match('<principal>([0-9a-f]+)</principal>', line)
302                if m is not None:
303                    principal = m.group(1)
304
305                m = re.match('<prover>([0-9a-f]+)</prover>', line)
306                if m is not None:
307                    prover = m.group(1)
308
309                m = re.match('<attribute>(.*)</attribute>', line)
310                if m is not None:
311                    role = m.group(1)
312
313                m = re.match('<credential>(.*)</credential>', line)
314                if m is not None:
315                    creds.append(base64.b64decode(m.group(1)))
316            f.close()
317        except EnvironmentError, e:
318            self.report_error("Cannot open %s: %s" % (e.filename, e.strerror))
319            return
320
321    def interpret_proof(self, p):
322        roles = set()
323        direct_roles = {}
324        groles = set()
325        principals = set()
326        goals = set()
327        attrs = set()
328        ok, proof = p.ctxt.query(p.role, p.principal)
329        for c in p.ctxt.credentials():
330            role = c.tail()
331            if role.is_principal():
332                if role.string() != p.principal: 
333                    principals.add(role.string())
334                else:
335                    assigner, r = c.head().string().split('.')
336                    direct_roles[r] = assigner
337            else: 
338                r = role.string()
339                for s in r.split('&'):
340                    roles.add(s.strip())
341                groles.add(r)
342
343            role = c.head()
344            roles.add(role.string())
345            groles.add(role.string())
346
347        for r in groles:
348            ok, proof =  p.ctxt.query(p.role, r)
349            if ok :
350                goals.add(r)
351        for r in roles:
352            ok, proof = p.ctxt.query(r, p.principal)
353            if ok:
354                attrs.add(r)
355
356
357        split_goals = [ [s.strip() for s in g.split('&')] for g in goals ]
358        plans = []
359        for sg in split_goals:
360            pl = []
361            for g in sg:
362                if g in attrs:
363                    continue
364                if g.count('.') == 2:
365                    # linking role
366                    pr, rr, lr = g.split('.')
367                    if lr in direct_roles:
368                        pl.append('add %s to %s' % (direct_roles[lr], rr))
369                    else:
370                        pl.append('someone with %s.%s must delegate %s to %s' % \
371                                (pr, rr, lr, p.principal))
372                elif g.count('.') == 1:
373                    pl.append('add %s to %s' % (g, principal))
374            plans.append('\n'.join(pl))
375
376        vb = gtk.VBox()
377        vb.set_border_width(20)
378        vb.pack_start(
379                self.wrapit(
380                    self.makeListView([p.prover], "Entity Blocking Access")
381                ), True, True,0)
382        vb.pack_start(
383                self.wrapit(
384                    self.makeListView(plans, "Suggested Actions")
385                ), True, True,0)
386        vb.pack_start(
387                self.wrapit(
388                    self.makeListView(goals, "Required Slice Attributes")
389                ), True, True,0)
390
391        vb.pack_start(
392                self.wrapit(
393                    self.makeListView(attrs, "Slice Attributes Present")
394                ), True, True,0)
395        vb.show_all()
396        return vb
397
398if len(sys.argv) > 1:
399    fn = sys.argv[1]
400else:
401    fn = None
402w = window(fn)
403gtk.main()
Note: See TracBrowser for help on using the repository browser.