~ubuntu-branches/ubuntu/trusty/apparmor/trusty-updates

« back to all changes in this revision

Viewing changes to utils/apparmor/ui.py

  • Committer: Package Import Robot
  • Author(s): Steve Beattie
  • Date: 2015-04-30 12:18:08 UTC
  • mfrom: (72.1.1 trusty-security)
  • Revision ID: package-import@ubuntu.com-20150430121808-ksonjffnoo5ela3r
Tags: 2.8.95~2430-0ubuntu5.2
* debian/patches/php5-Zend_semaphore-lp1401084.patch: allow php5
  abstraction access to Zend opcache files (LP: #1401084)
* debian/patches/dnsmasq-lxc_networking-lp1403468.patch: update
  profile for lxc support (LP: #1403468)
* debian/patches/profiles-texlive_font_generation-lp1010909.patch:
  allow generation of texlive fonts by sanitized-helpers
  (LP: #1010909)
* debian/apport/source_apparmor.py: fix the apparmor apport hook
  so it does not raise an exception if a non-unicode character is
  found in /var/log/kern.log or in /var/log/syslog. This should
  work under python3 or python2.7 (LP: #1304447)
* debian/patches/profiles-dovecot-updates-lp1296667.patch: update
  dovecot profiles to address several missing permissions.
  (LP: #1296667)
* debian/patches/profiles-adjust_X_for_lightdm-lp1339727.patch:
  adjust X abstraction for LightDM xauthority location (LP: #1339727)
* debian/patches/libapparmor-fix_memory_leaks-lp1340927.patch; fix
  memory leaks in log parsing component of libapparmor (LP: #1340927)
* debian/patches/libapparmor-another_audit_format-lp1399027.patch:
  add support for another log format style (LP: #1399027)
* debian/patches/tests-workaround_for_unix_socket_change-lp1425398.patch:
  work around apparmor kernel behavioral change in regression tests
  (LP: #1425398)
* debian/control: add breaks on python3-apparmor against older
  apparmor-utils that used to be where python bits lived
  (LP: #1373259)
* debian/patches/utils-update_to_2.9.2.patch: update the python
  utilities to the upstream 2.9.2 (LP: #1449769, incorporating a
  large number of fixes and improvements, including:
  - fix aa-genprof traceback with apparmor 2.8.95 (LP: #1294797)
  - fix aa-genprof crashing when selecting scan on Ubuntu 14.04 server
    (LP: #1319829)
  - make aa-logprof read profile instead of program binary
    (LP: #1317176, LP: #1324154)
  - aa-complain: don't traceback when marking multiple profiles
    (LP: #1378095)
  - make python tools able to parse mounts with UTF-8 non-ascii
    characters (LP: #1310598)

Show diffs side-by-side

added added

removed removed

Lines of Context:
13
13
# ----------------------------------------------------------------------
14
14
import sys
15
15
import re
 
16
import readline
16
17
from apparmor.yasti import yastLog, SendDataToYast, GetDataFromYast
17
18
 
18
19
from apparmor.common import readkey, AppArmorException, DebugLogger
27
28
# The operating mode: yast or text, text by default
28
29
UI_mode = 'text'
29
30
 
 
31
# If Python3, wrap input in raw_input so make check passes
 
32
if not 'raw_input' in dir(__builtins__): raw_input = input
 
33
 
30
34
ARROWS = {'A': 'UP', 'B': 'DOWN', 'C': 'RIGHT', 'D': 'LEFT'}
31
35
 
32
36
def getkey():
170
174
    debug_logger.debug('UI_GetString: %s: %s %s' % (UI_mode, text, default))
171
175
    string = default
172
176
    if UI_mode == 'text':
173
 
        sys.stdout.write('\n' + text)
174
 
        string = sys.stdin.readline()
 
177
        readline.set_startup_hook(lambda: readline.insert_text(default))
 
178
        try:
 
179
            string = raw_input('\n' + text)
 
180
        except EOFError:
 
181
            string = ''
 
182
        finally:
 
183
            readline.set_startup_hook()
175
184
    else:
176
185
        SendDataToYast({'type': 'dialog-getstring',
177
186
                        'label': text,
273
282
        'CMD_IGNORE_ENTRY': _('(I)gnore')
274
283
        }
275
284
 
276
 
def UI_PromptUser(q, params=''):
277
 
    cmd = None
278
 
    arg = None
279
 
    if UI_mode == 'text':
280
 
        cmd, arg = Text_PromptUser(q)
281
 
    else:
282
 
        q['type'] = 'wizard'
283
 
        SendDataToYast(q)
284
 
        ypath, yarg = GetDataFromYast()
285
 
        if not cmd:
286
 
            cmd = 'CMD_ABORT'
287
 
        arg = yarg['selected']
288
 
    if cmd == 'CMD_ABORT':
289
 
        confirm_and_abort()
290
 
        cmd = 'XXXINVALIDXXX'
291
 
    return (cmd, arg)
 
285
class PromptQuestion(object):
 
286
    title = None
 
287
    headers = None
 
288
    explanation = None
 
289
    functions = None
 
290
    options = None
 
291
    default = None
 
292
    selected = None
 
293
    helptext = None
 
294
 
 
295
    def __init__(self):
 
296
        self.headers = list()
 
297
        self.functions = list()
 
298
        self.selected = 0
 
299
 
 
300
    def promptUser(self, params=''):
 
301
        cmd = None
 
302
        arg = None
 
303
        if UI_mode == 'text':
 
304
            cmd, arg = self.Text_PromptUser()
 
305
        else:
 
306
            self.type = 'wizard'
 
307
            SendDataToYast(self)
 
308
            ypath, yarg = GetDataFromYast()
 
309
            if not cmd:
 
310
                cmd = 'CMD_ABORT'
 
311
            arg = yarg['selected']
 
312
        if cmd == 'CMD_ABORT':
 
313
            confirm_and_abort()
 
314
            cmd = 'XXXINVALIDXXX'
 
315
        return (cmd, arg)
 
316
 
 
317
    def Text_PromptUser(self):
 
318
        title = self.title
 
319
        explanation = self.explanation
 
320
        headers = self.headers
 
321
        functions = self.functions
 
322
 
 
323
        default = self.default
 
324
        options = self.options
 
325
        selected = self.selected
 
326
        helptext = self.helptext
 
327
 
 
328
        if helptext:
 
329
            functions.append('CMD_HELP')
 
330
 
 
331
        menu_items = list()
 
332
        keys = dict()
 
333
 
 
334
        for cmd in functions:
 
335
            if not CMDS.get(cmd, False):
 
336
                raise AppArmorException(_('PromptUser: Unknown command %s') % cmd)
 
337
 
 
338
            menutext = CMDS[cmd]
 
339
 
 
340
            key = get_translated_hotkey(menutext).lower()
 
341
            # Duplicate hotkey
 
342
            if keys.get(key, False):
 
343
                raise AppArmorException(_('PromptUser: Duplicate hotkey for %(command)s: %(menutext)s ') % { 'command': cmd, 'menutext': menutext })
 
344
 
 
345
            keys[key] = cmd
 
346
 
 
347
            if default and default == cmd:
 
348
                menutext = '[%s]' % menutext
 
349
 
 
350
            menu_items.append(menutext)
 
351
 
 
352
        default_key = 0
 
353
        if default and CMDS[default]:
 
354
            defaulttext = CMDS[default]
 
355
            defmsg = _('PromptUser: Invalid hotkey in default item')
 
356
 
 
357
            default_key = get_translated_hotkey(defaulttext, defmsg).lower()
 
358
 
 
359
            if not keys.get(default_key, False):
 
360
                raise AppArmorException(_('PromptUser: Invalid default %s') % default)
 
361
 
 
362
        widest = 0
 
363
        header_copy = headers[:]
 
364
        while header_copy:
 
365
            header = header_copy.pop(0)
 
366
            header_copy.pop(0)
 
367
            if len(header) > widest:
 
368
                widest = len(header)
 
369
        widest += 1
 
370
 
 
371
        formatstr = '%-' + str(widest) + 's %s\n'
 
372
 
 
373
        function_regexp = '^('
 
374
        function_regexp += '|'.join(keys.keys())
 
375
        if options:
 
376
            function_regexp += '|\d'
 
377
        function_regexp += ')$'
 
378
 
 
379
        ans = 'XXXINVALIDXXX'
 
380
        while not re.search(function_regexp, ans, flags=re.IGNORECASE):
 
381
 
 
382
            prompt = '\n'
 
383
            if title:
 
384
                prompt += '= %s =\n\n' % title
 
385
 
 
386
            if headers:
 
387
                header_copy = headers[:]
 
388
                while header_copy:
 
389
                    header = header_copy.pop(0)
 
390
                    value = header_copy.pop(0)
 
391
                    prompt += formatstr % (header + ':', value)
 
392
                prompt += '\n'
 
393
 
 
394
            if explanation:
 
395
                prompt += explanation + '\n\n'
 
396
 
 
397
            if options:
 
398
                for index, option in enumerate(options):
 
399
                    if selected == index:
 
400
                        format_option = ' [%s - %s]'
 
401
                    else:
 
402
                        format_option = '  %s - %s '
 
403
                    prompt += format_option % (index + 1, option)
 
404
                    prompt += '\n'
 
405
 
 
406
            prompt += ' / '.join(menu_items)
 
407
 
 
408
            sys.stdout.write(prompt + '\n')
 
409
 
 
410
            ans = getkey().lower()
 
411
 
 
412
            if ans:
 
413
                if ans == 'up':
 
414
                    if options and selected > 0:
 
415
                        selected -= 1
 
416
                    ans = 'XXXINVALIDXXX'
 
417
 
 
418
                elif ans == 'down':
 
419
                    if options and selected < len(options) - 1:
 
420
                        selected += 1
 
421
                    ans = 'XXXINVALIDXXX'
 
422
 
 
423
    #             elif keys.get(ans, False) == 'CMD_HELP':
 
424
    #                 sys.stdout.write('\n%s\n' %helptext)
 
425
    #                 ans = 'XXXINVALIDXXX'
 
426
 
 
427
                elif is_number(ans) == 10:
 
428
                    # If they hit return choose default option
 
429
                    ans = default_key
 
430
 
 
431
                elif options and re.search('^\d$', ans):
 
432
                    ans = int(ans)
 
433
                    if ans > 0 and ans <= len(options):
 
434
                        selected = ans - 1
 
435
                    ans = 'XXXINVALIDXXX'
 
436
 
 
437
            if keys.get(ans, False) == 'CMD_HELP':
 
438
                sys.stdout.write('\n%s\n' % helptext)
 
439
                ans = 'again'
 
440
 
 
441
        if keys.get(ans, False):
 
442
            ans = keys[ans]
 
443
 
 
444
        return ans, selected
292
445
 
293
446
def confirm_and_abort():
294
447
    ans = UI_YesNo(_('Are you sure you want to abandon this set of profile changes and exit?'), 'n')
313
466
                    })
314
467
    ypath, yarg = GetDataFromYast()
315
468
 
316
 
 
317
 
def Text_PromptUser(question):
318
 
    title = question['title']
319
 
    explanation = question['explanation']
320
 
    headers = question['headers']
321
 
    functions = question['functions']
322
 
 
323
 
    default = question['default']
324
 
    options = question['options']
325
 
    selected = question.get('selected', 0)
326
 
    helptext = question['helptext']
327
 
    if helptext:
328
 
        functions.append('CMD_HELP')
329
 
 
330
 
    menu_items = []
331
 
    keys = dict()
332
 
 
333
 
    for cmd in functions:
334
 
        if not CMDS.get(cmd, False):
335
 
            raise AppArmorException(_('PromptUser: Unknown command %s') % cmd)
336
 
 
337
 
        menutext = CMDS[cmd]
338
 
 
339
 
        key = get_translated_hotkey(menutext).lower()
340
 
        # Duplicate hotkey
341
 
        if keys.get(key, False):
342
 
            raise AppArmorException(_('PromptUser: Duplicate hotkey for %s: %s ') % (cmd, menutext))
343
 
 
344
 
        keys[key] = cmd
345
 
 
346
 
        if default and default == cmd:
347
 
            menutext = '[%s]' % menutext
348
 
 
349
 
        menu_items.append(menutext)
350
 
 
351
 
    default_key = 0
352
 
    if default and CMDS[default]:
353
 
        defaulttext = CMDS[default]
354
 
        defmsg = _('PromptUser: Invalid hotkey in default item')
355
 
 
356
 
        default_key = get_translated_hotkey(defaulttext, defmsg).lower()
357
 
 
358
 
        if not keys.get(default_key, False):
359
 
            raise AppArmorException(_('PromptUser: Invalid default %s') % default)
360
 
 
361
 
    widest = 0
362
 
    header_copy = headers[:]
363
 
    while header_copy:
364
 
        header = header_copy.pop(0)
365
 
        header_copy.pop(0)
366
 
        if len(header) > widest:
367
 
            widest = len(header)
368
 
    widest += 1
369
 
 
370
 
    formatstr = '%-' + str(widest) + 's %s\n'
371
 
 
372
 
    function_regexp = '^('
373
 
    function_regexp += '|'.join(keys.keys())
374
 
    if options:
375
 
        function_regexp += '|\d'
376
 
    function_regexp += ')$'
377
 
 
378
 
    ans = 'XXXINVALIDXXX'
379
 
    while not re.search(function_regexp, ans, flags=re.IGNORECASE):
380
 
 
381
 
        prompt = '\n'
382
 
        if title:
383
 
            prompt += '= %s =\n\n' % title
384
 
 
385
 
        if headers:
386
 
            header_copy = headers[:]
387
 
            while header_copy:
388
 
                header = header_copy.pop(0)
389
 
                value = header_copy.pop(0)
390
 
                prompt += formatstr % (header + ':', value)
391
 
            prompt += '\n'
392
 
 
393
 
        if explanation:
394
 
            prompt += explanation + '\n\n'
395
 
 
396
 
        if options:
397
 
            for index, option in enumerate(options):
398
 
                if selected == index:
399
 
                    format_option = ' [%s - %s]'
400
 
                else:
401
 
                    format_option = '  %s - %s '
402
 
                prompt += format_option % (index + 1, option)
403
 
                prompt += '\n'
404
 
 
405
 
        prompt += ' / '.join(menu_items)
406
 
 
407
 
        sys.stdout.write(prompt + '\n')
408
 
 
409
 
        ans = getkey().lower()
410
 
 
411
 
        if ans:
412
 
            if ans == 'up':
413
 
                if options and selected > 0:
414
 
                    selected -= 1
415
 
                ans = 'XXXINVALIDXXX'
416
 
 
417
 
            elif ans == 'down':
418
 
                if options and selected < len(options) - 1:
419
 
                    selected += 1
420
 
                ans = 'XXXINVALIDXXX'
421
 
 
422
 
#             elif keys.get(ans, False) == 'CMD_HELP':
423
 
#                 sys.stdout.write('\n%s\n' %helptext)
424
 
#                 ans = 'XXXINVALIDXXX'
425
 
 
426
 
            elif is_number(ans) == 10:
427
 
                # If they hit return choose default option
428
 
                ans = default_key
429
 
 
430
 
            elif options and re.search('^\d$', ans):
431
 
                ans = int(ans)
432
 
                if ans > 0 and ans <= len(options):
433
 
                    selected = ans - 1
434
 
                ans = 'XXXINVALIDXXX'
435
 
 
436
 
        if keys.get(ans, False) == 'CMD_HELP':
437
 
            sys.stdout.write('\n%s\n' % helptext)
438
 
            ans = 'again'
439
 
 
440
 
    if keys.get(ans, False):
441
 
        ans = keys[ans]
442
 
 
443
 
    return ans, selected
444
 
 
445
469
def is_number(number):
446
470
    try:
447
471
        return int(number)