2
0
mirror of https://gitlab.com/apparmor/apparmor synced 2025-09-08 18:25:29 +00:00
Files
apparmor/utils/apparmor/sandbox.py
2019-08-12 23:58:04 +02:00

727 lines
26 KiB
Python

# ------------------------------------------------------------------
#
# Copyright (C) 2011-2013 Canonical Ltd.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
# License published by the Free Software Foundation.
#
# ------------------------------------------------------------------
from apparmor.common import AppArmorException, debug, error, msg, cmd
import apparmor.easyprof
import optparse
import os
import pwd
import re
import signal
import socket
import sys
import tempfile
import time
def check_requirements(binary):
'''Verify necessary software is installed'''
exes = ['xset', # for detecting free X display
'aa-easyprof', # for templates
'aa-exec', # for changing profile
'sudo', # eventually get rid of this
'pkexec', # eventually get rid of this
binary]
for e in exes:
debug("Searching for '%s'" % e)
rc, report = cmd(['which', e])
if rc != 0:
error("Could not find '%s'" % e, do_exit=False)
return False
return True
def parse_args(args=None, parser=None):
'''Parse arguments'''
if parser == None:
parser = optparse.OptionParser()
parser.add_option('-X', '--with-x',
dest='withx',
default=False,
help='Run in isolated X server',
action='store_true')
parser.add_option('--with-xserver',
dest='xserver',
default='xpra',
help='Nested X server to use: xpra (default), xpra3d, xephyr')
parser.add_option('--with-clipboard',
dest='with_clipboard',
default=False,
help='Allow clipboard access',
action='store_true')
parser.add_option('--with-xauthority',
dest='xauthority',
default=None,
help='Specify Xauthority file to use')
parser.add_option('-d', '--debug',
dest='debug',
default=False,
help='Show debug messages',
action='store_true')
parser.add_option('--with-xephyr-geometry',
dest='xephyr_geometry',
default=None,
help='Geometry for Xephyr window')
parser.add_option('--profile',
dest='profile',
default=None,
help='Specify an existing profile (see aa-status)')
(my_opt, my_args) = parser.parse_args()
if my_opt.debug:
apparmor.common.DEBUGGING = True
valid_xservers = ['xpra', 'xpra3d', 'xephyr']
if my_opt.withx and my_opt.xserver.lower() not in valid_xservers:
error("Invalid server '%s'. Use one of: %s" % (my_opt.xserver, \
", ".join(valid_xservers)))
if my_opt.withx:
if my_opt.xephyr_geometry and my_opt.xserver.lower() != "xephyr":
error("Invalid option --with-xephyr-geometry with '%s'" % my_opt.xserver)
elif my_opt.with_clipboard and my_opt.xserver.lower() == "xephyr":
error("Clipboard not supported with '%s'" % my_opt.xserver)
if my_opt.template == "default":
if my_opt.withx:
my_opt.template = "sandbox-x"
else:
my_opt.template = "sandbox"
return (my_opt, my_args)
def gen_policy_name(binary):
'''Generate a temporary policy based on the binary name'''
return "sandbox-%s%s" % (pwd.getpwuid(os.geteuid())[0],
re.sub(r'/', '_', binary))
def set_environ(env):
keys = env.keys()
keys.sort()
for k in keys:
msg("Using: %s=%s" % (k, env[k]))
os.environ[k] = env[k]
def aa_exec(command, opt, environ={}, verify_rules=[]):
'''Execute binary under specified policy'''
if opt.profile != None:
policy_name = opt.profile
else:
opt.ensure_value("template_var", None)
opt.ensure_value("name", None)
opt.ensure_value("comment", None)
opt.ensure_value("author", None)
opt.ensure_value("copyright", None)
binary = command[0]
policy_name = gen_policy_name(binary)
easyp = apparmor.easyprof.AppArmorEasyProfile(binary, opt)
params = apparmor.easyprof.gen_policy_params(policy_name, opt)
policy = easyp.gen_policy(**params)
debug("\n%s" % policy)
tmp = tempfile.NamedTemporaryFile(prefix = '%s-' % policy_name)
if sys.version_info[0] >= 3:
tmp.write(bytes(policy, 'utf-8'))
else:
tmp.write(policy)
tmp.flush()
debug("using '%s' template" % opt.template)
# TODO: get rid of this
if opt.withx:
rc, report = cmd(['pkexec', 'apparmor_parser', '-r', '%s' % tmp.name])
else:
rc, report = cmd(['sudo', 'apparmor_parser', '-r', tmp.name])
if rc != 0:
raise AppArmorException("Could not load policy")
rc, report = cmd(['sudo', 'apparmor_parser', '-p', tmp.name])
if rc != 0:
raise AppArmorException("Could not dump policy")
# Make sure the dynamic profile has the appropriate line for X
for r in verify_rules:
found = False
for line in report.splitlines():
line = line.strip()
if r == line:
found = True
break
if not found:
raise AppArmorException("Could not find required rule: %s" % r)
set_environ(environ)
args = ['aa-exec', '-p', policy_name, '--'] + command
rc, report = cmd(args)
return rc, report
def run_sandbox(command, opt):
'''Run application'''
# aa-exec
rc, report = aa_exec(command, opt)
return rc, report
class SandboxXserver():
def __init__(self, title, geometry=None,
driver=None,
xauth=None,
clipboard=False):
self.geometry = geometry
self.title = title
self.pids = []
self.driver = driver
self.clipboard = clipboard
self.tempfiles = []
self.timeout = 5 # used by xauth and for server starts
# preserve our environment
self.old_environ = dict()
for env in ['DISPLAY', 'XAUTHORITY', 'UBUNTU_MENUPROXY',
'QT_X11_NO_NATIVE_MENUBAR', 'LIBOVERLAY_SCROLLBAR']:
if env in os.environ:
self.old_environ[env] = os.environ[env]
# prepare the new environment
self.display, self.xauth = self.find_free_x_display()
if xauth:
abs_xauth = os.path.expanduser(xauth)
if os.path.expanduser("~/.Xauthority") == abs_xauth:
raise AppArmorException("Trusted Xauthority file specified. Aborting")
self.xauth = abs_xauth
self.new_environ = dict()
self.new_environ['DISPLAY'] = self.display
self.new_environ['XAUTHORITY'] = self.xauth
# Disable the global menu for now
self.new_environ["UBUNTU_MENUPROXY"] = ""
self.new_environ["QT_X11_NO_NATIVE_MENUBAR"] = "1"
# Disable the overlay scrollbar for now-- they don't track correctly
self.new_environ["LIBOVERLAY_SCROLLBAR"] = "0"
def cleanup(self):
'''Cleanup our forked pids, reset the environment, etc'''
self.pids.reverse()
debug(self.pids)
for pid in self.pids:
# Kill server with TERM
debug("kill %d" % pid)
os.kill(pid, signal.SIGTERM)
for pid in self.pids:
# Shoot the server dead
debug("kill -9 %d" % pid)
os.kill(pid, signal.SIGKILL)
for t in self.tempfiles:
if os.path.exists(t):
os.unlink(t)
if os.path.exists(self.xauth):
os.unlink(self.xauth)
# Reset our environment
set_environ(self.old_environ)
def find_free_x_display(self):
'''Find a free X display'''
old_lang = None
if 'LANG' in os.environ:
old_lang = os.environ['LANG']
os.environ['LANG'] = 'C'
display = ""
current = self.old_environ["DISPLAY"]
for i in range(1,257): # TODO: this puts an artificial limit of 256
# sandboxed applications
tmp = ":%d" % i
os.environ["DISPLAY"] = tmp
rc, report = cmd(['xset', '-q'])
if rc != 0 and 'Invalid MIT-MAGIC-COOKIE-1' not in report:
display = tmp
break
if old_lang:
os.environ['LANG'] = old_lang
os.environ["DISPLAY"] = current
if display == "":
raise AppArmorException("Could not find available X display")
# Use dedicated .Xauthority file
xauth = os.path.join(os.path.expanduser('~'), \
'.Xauthority-sandbox%s' % display.split(':')[1])
return display, xauth
def generate_title(self):
return "(Sandbox%s) %s" % (self.display, self.title)
def verify_host_setup(self):
'''Make sure we have everything we need'''
old_lang = None
if 'LANG' in os.environ:
old_lang = os.environ['LANG']
os.environ['LANG'] = 'C'
rc, report = cmd(['xhost'])
if old_lang:
os.environ['LANG'] = old_lang
if rc != 0:
raise AppArmorException("'xhost' exited with error")
if 'access control enabled' not in report:
raise AppArmorException("Access control currently disabled. Please enable with 'xhost -'")
username = pwd.getpwuid(os.geteuid())[0]
if ':localuser:%s' % username in report:
raise AppArmorException("Access control allows '%s' full access. Please see 'man aa-sandbox' for details" % username)
def start(self):
'''Start a nested X server (need to override)'''
# clean up the old one
if os.path.exists(self.xauth):
os.unlink(self.xauth)
rc, cookie = cmd(['mcookie'])
if rc != 0:
raise AppArmorException("Could not generate magic cookie")
rc, out = cmd(['xauth', '-f', self.xauth, \
'add', \
self.display, \
'MIT-MAGIC-COOKIE-1', \
cookie.strip()])
if rc != 0:
raise AppArmorException("Could not generate '%s'" % self.display)
class SandboxXephyr(SandboxXserver):
def start(self):
for e in ['Xephyr', 'matchbox-window-manager']:
debug("Searching for '%s'" % e)
rc, report = cmd(['which', e])
if rc != 0:
raise AppArmorException("Could not find '%s'" % e)
'''Run any setup code'''
SandboxXserver.start(self)
'''Start a Xephyr server'''
listener_x = os.fork()
if listener_x == 0:
# TODO: break into config file? Which are needed?
x_exts = ['-extension', 'GLX',
'-extension', 'MIT-SHM',
'-extension', 'RENDER',
'-extension', 'SECURITY',
'-extension', 'DAMAGE'
]
# verify_these
x_extra_args = ['-host-cursor', # less secure?
'-fakexa', # for games? seems not needed
'-nodri', # more secure?
]
if not self.geometry:
self.geometry = "640x480"
x_args = ['-nolisten', 'tcp',
'-screen', self.geometry,
'-br', # black background
'-reset', # reset after last client exists
'-terminate', # terminate at server reset
'-title', self.generate_title(),
] + x_exts + x_extra_args
args = ['/usr/bin/Xephyr'] + x_args + [self.display]
debug(" ".join(args))
os.execv(args[0], args)
sys.exit(0)
self.pids.append(listener_x)
time.sleep(1) # FIXME: detect if running
# Next, start the window manager
sys.stdout.flush()
os.chdir(os.environ["HOME"])
listener_wm = os.fork()
if listener_wm == 0:
# update environment
set_environ(self.new_environ)
args = ['/usr/bin/matchbox-window-manager', '-use_titlebar', 'no']
debug(" ".join(args))
cmd(args)
sys.exit(0)
self.pids.append(listener_wm)
time.sleep(1) # FIXME: detect if running
class SandboxXpra(SandboxXserver):
def cleanup(self):
sys.stderr.flush()
listener = os.fork()
if listener == 0:
args = ['/usr/bin/xpra', 'stop', self.display]
debug(" ".join(args))
os.execv(args[0], args)
sys.exit(0)
time.sleep(2)
# Annoyingly, xpra doesn't clean up itself well if the application
# failed for some reason. Try to account for that.
rc, report = cmd(['ps', 'auxww'])
for line in report.splitlines():
if '-for-Xpra-%s' % self.display in line:
self.pids.append(int(line.split()[1]))
SandboxXserver.cleanup(self)
def _get_xvfb_args(self):
'''Setup xvfb arguments'''
# Debugging tip (can also use glxinfo):
# $ xdpyinfo > /tmp/native
# $ aa-sandbox -X -t sandbox-x /usr/bin/xdpyinfo > /tmp/nested
# $ diff -Naur /tmp/native /tmp/nested
xvfb_args = []
if self.driver == None:
# The default from the man page, but be explicit in what we enable
xvfb_args.append('--xvfb=Xvfb')
xvfb_args.append('-screen 0 3840x2560x24+32')
xvfb_args.append('-nolisten tcp')
xvfb_args.append('-noreset')
xvfb_args.append('-auth %s' % self.new_environ['XAUTHORITY'])
xvfb_args.append('+extension Composite')
xvfb_args.append('+extension SECURITY')
xvfb_args.append('-extension GLX')
elif self.driver == 'xdummy':
# The dummy driver allows us to use GLX, etc. See:
# http://xpra.org/Xdummy.html
conf = '''# /usr/share/doc/xpra/examples/dummy.xorg.conf.gz
# http://xpra.org/Xdummy.html
##Xdummy:##
Section "ServerFlags"
Option "DontVTSwitch" "true"
Option "AllowMouseOpenFail" "true"
Option "PciForceNone" "true"
Option "AutoEnableDevices" "false"
Option "AutoAddDevices" "false"
EndSection
##Xdummy:##
Section "InputDevice"
Identifier "NoMouse"
Option "CorePointer" "true"
Driver "void"
EndSection
Section "InputDevice"
Identifier "NoKeyboard"
Option "CoreKeyboard" "true"
Driver "void"
EndSection
##Xdummy:##
Section "Device"
Identifier "Videocard0"
Driver "dummy"
# In kByte
#VideoRam 4096000
#VideoRam 256000
# This should be good for 3840*2560*32bpp: http://winswitch.org/trac/ticket/140
VideoRam 64000
EndSection
##Xdummy:##
Section "Monitor"
Identifier "Monitor0"
HorizSync 10.0 - 300.0
VertRefresh 10.0 - 200.0
DisplaySize 4335 1084
#The following modeline is invalid (calculator overflowed):
#Modeline "32000x32000@0" -38917.43 32000 32032 -115848 -115816 32000 32775 32826 33601
Modeline "16384x8192@10" 2101.93 16384 16416 24400 24432 8192 8390 8403 8602
Modeline "8192x4096@10" 424.46 8192 8224 9832 9864 4096 4195 4202 4301
Modeline "5120x3200@10" 199.75 5120 5152 5904 5936 3200 3277 3283 3361
Modeline "3840x2880@10" 133.43 3840 3872 4376 4408 2880 2950 2955 3025
Modeline "3840x2560@10" 116.93 3840 3872 4312 4344 2560 2622 2627 2689
Modeline "3840x2048@10" 91.45 3840 3872 4216 4248 2048 2097 2101 2151
Modeline "2048x2048@10" 49.47 2048 2080 2264 2296 2048 2097 2101 2151
Modeline "2560x1600@10" 47.12 2560 2592 2768 2800 1600 1639 1642 1681
Modeline "1920x1200@10" 26.28 1920 1952 2048 2080 1200 1229 1231 1261
Modeline "1920x1080@10" 23.53 1920 1952 2040 2072 1080 1106 1108 1135
Modeline "1680x1050@10" 20.08 1680 1712 1784 1816 1050 1075 1077 1103
Modeline "1600x900@20" 33.92 1600 1632 1760 1792 900 921 924 946
Modeline "1440x900@20" 30.66 1440 1472 1584 1616 900 921 924 946
Modeline "1360x768@20" 24.49 1360 1392 1480 1512 768 786 789 807
#common resolutions for android devices (both orientations):
Modeline "800x1280@20" 25.89 800 832 928 960 1280 1310 1315 1345
Modeline "1280x800@20" 24.15 1280 1312 1400 1432 800 819 822 841
Modeline "720x1280@25" 30.22 720 752 864 896 1280 1309 1315 1345
Modeline "1280x720@25" 27.41 1280 1312 1416 1448 720 737 740 757
Modeline "768x1024@25" 24.93 768 800 888 920 1024 1047 1052 1076
Modeline "1024x768@25" 23.77 1024 1056 1144 1176 768 785 789 807
Modeline "600x1024@25" 19.90 600 632 704 736 1024 1047 1052 1076
Modeline "1024x600@25" 18.26 1024 1056 1120 1152 600 614 617 631
Modeline "536x960@25" 16.74 536 568 624 656 960 982 986 1009
Modeline "960x536@25" 15.23 960 992 1048 1080 536 548 551 563
Modeline "600x800@25" 15.17 600 632 688 720 800 818 822 841
Modeline "800x600@25" 14.50 800 832 880 912 600 614 617 631
Modeline "480x854@25" 13.34 480 512 560 592 854 873 877 897
Modeline "848x480@25" 12.09 848 880 920 952 480 491 493 505
Modeline "480x800@25" 12.43 480 512 552 584 800 818 822 841
Modeline "800x480@25" 11.46 800 832 872 904 480 491 493 505
Modeline "320x480@50" 10.73 320 352 392 424 480 490 494 505
Modeline "480x320@50" 9.79 480 512 544 576 320 327 330 337
Modeline "240x400@50" 6.96 240 272 296 328 400 408 412 421
Modeline "400x240@50" 6.17 400 432 448 480 240 245 247 253
Modeline "240x320@50" 5.47 240 272 288 320 320 327 330 337
Modeline "320x240@50" 5.10 320 352 368 400 240 245 247 253
#resolutions for android devices (both orientations)
#minus the status bar
#38px status bar (and width rounded up)
Modeline "800x1242@20" 25.03 800 832 920 952 1242 1271 1275 1305
Modeline "1280x762@20" 22.93 1280 1312 1392 1424 762 780 783 801
Modeline "720x1242@25" 29.20 720 752 856 888 1242 1271 1276 1305
Modeline "1280x682@25" 25.85 1280 1312 1408 1440 682 698 701 717
Modeline "768x986@25" 23.90 768 800 888 920 986 1009 1013 1036
Modeline "1024x730@25" 22.50 1024 1056 1136 1168 730 747 750 767
Modeline "600x986@25" 19.07 600 632 704 736 986 1009 1013 1036
Modeline "1024x562@25" 17.03 1024 1056 1120 1152 562 575 578 591
Modeline "536x922@25" 16.01 536 568 624 656 922 943 947 969
Modeline "960x498@25" 14.09 960 992 1040 1072 498 509 511 523
Modeline "600x762@25" 14.39 600 632 680 712 762 779 783 801
Modeline "800x562@25" 13.52 800 832 880 912 562 575 578 591
Modeline "480x810@25" 12.59 480 512 552 584 810 828 832 851
Modeline "848x442@25" 11.09 848 880 920 952 442 452 454 465
Modeline "480x762@25" 11.79 480 512 552 584 762 779 783 801
Modeline "800x442@25" 10.51 800 832 864 896 442 452 454 465
#32px status bar (no need for rounding):
Modeline "320x448@50" 9.93 320 352 384 416 448 457 461 471
Modeline "480x288@50" 8.75 480 512 544 576 288 294 297 303
#24px status bar:
Modeline "240x376@50" 6.49 240 272 296 328 376 384 387 395
Modeline "400x216@50" 5.50 400 432 448 480 216 220 222 227
Modeline "240x296@50" 5.02 240 272 288 320 296 302 305 311
Modeline "320x216@50" 4.55 320 352 368 400 216 220 222 227
EndSection
##Xdummy:##
Section "Screen"
Identifier "Screen0"
Device "Videocard0"
Monitor "Monitor0"
DefaultDepth 24
SubSection "Display"
Viewport 0 0
Depth 24
Modes "32000x32000" "16384x8192" "8192x4096" "5120x3200" "3840x2880" "3840x2560" "3840x2048" "2048x2048" "2560x1600" "1920x1440" "1920x1200" "1920x1080" "1600x1200" "1680x1050" "1600x900" "1400x1050" "1440x900" "1280x1024" "1366x768" "1280x800" "1024x768" "1024x600" "800x600" "320x200"
#Virtual 32000 32000
#Virtual 16384 8192
#Virtual 8192 4096
# http://winswitch.org/trac/ticket/140
Virtual 3840 2560
EndSubSection
EndSection
Section "ServerLayout"
Identifier "dummy_layout"
Screen "screen0"
InputDevice "NoMouse"
InputDevice "NoKeyboard"
EndSection
'''
tmp, xorg_conf = tempfile.mkstemp(prefix='aa-sandbox-xorg.conf-')
self.tempfiles.append(xorg_conf)
if sys.version_info[0] >= 3:
os.write(tmp, bytes(conf, 'utf-8'))
else:
os.write(tmp, conf)
os.close(tmp)
xvfb_args.append('--xvfb=Xorg')
xvfb_args.append('-dpi 96') # https://www.xpra.org/trac/ticket/163
xvfb_args.append('-nolisten tcp')
xvfb_args.append('-noreset')
xvfb_args.append('-logfile %s' % os.path.expanduser('~/.xpra/%s.log' % self.display))
xvfb_args.append('-auth %s' % self.new_environ['XAUTHORITY'])
xvfb_args.append('-config %s' % xorg_conf)
extensions = ['Composite', 'GLX', 'RANDR', 'RENDER', 'SECURITY']
for i in extensions:
xvfb_args.append('+extension %s' % i)
else:
raise AppArmorException("Unsupported X driver '%s'" % self.driver)
return xvfb_args
def start(self):
debug("Searching for '%s'" % 'xpra')
rc, report = cmd(['which', 'xpra'])
if rc != 0:
raise AppArmorException("Could not find '%s'" % 'xpra')
if self.driver == "xdummy":
# FIXME: is there a better way we can detect this?
drv = "/usr/lib/xorg/modules/drivers/dummy_drv.so"
debug("Searching for '%s'" % drv)
if not os.path.exists(drv):
raise AppArmorException("Could not find '%s'" % drv)
'''Run any setup code'''
SandboxXserver.start(self)
xvfb_args = self._get_xvfb_args()
listener_x = os.fork()
if listener_x == 0:
os.environ['XAUTHORITY'] = self.xauth
# This will clean out any dead sessions
cmd(['xpra', 'list'])
x_args = ['--no-daemon',
#'--no-mmap', # for security?
'--no-pulseaudio']
if not self.clipboard:
x_args.append('--no-clipboard')
if xvfb_args != '':
x_args.append(" ".join(xvfb_args))
args = ['/usr/bin/xpra', 'start', self.display] + x_args
debug(" ".join(args))
sys.stderr.flush()
os.execv(args[0], args)
sys.exit(0)
self.pids.append(listener_x)
started = False
# We need to wait for the xpra socket to exist before attaching
fn = os.path.join(os.environ['HOME'], '.xpra', '%s-%s' % \
(socket.gethostname(), self.display.split(':')[1]))
for i in range(self.timeout * 2): # up to self.timeout seconds to start
if os.path.exists(fn):
debug("Found '%s'! Proceeding to attach" % fn)
break
debug("'%s' doesn't exist yet, waiting" % fn)
time.sleep(0.5)
if not os.path.exists(fn):
sys.stdout.flush()
self.cleanup()
raise AppArmorException("Could not start xpra (try again with -d)")
for i in range(self.timeout): # Up to self.timeout seconds to start
rc, out = cmd(['xpra', 'list'])
if 'DEAD session at %s' % self.display in out:
error("xpra session at '%s' died" % self.display, do_exit=False)
break
search = 'LIVE session at %s' % self.display
if search in out:
started = True
break
time.sleep(0.5)
debug("Could not find '%s' in:\n" % search)
debug(out)
if not started:
sys.stdout.flush()
self.cleanup()
raise AppArmorException("Could not start xpra (try again with -d)")
# Next, attach to xpra
sys.stdout.flush()
os.chdir(os.environ["HOME"])
listener_attach = os.fork()
if listener_attach == 0:
args = ['/usr/bin/xpra', 'attach', self.display,
'--title=%s' % self.generate_title(),
#'--no-mmap', # for security?
'--no-tray',
'--no-pulseaudio']
if not self.clipboard:
args.append('--no-clipboard')
debug(" ".join(args))
sys.stderr.flush()
os.execv(args[0], args)
sys.exit(0)
self.pids.append(listener_attach)
# Make sure that a client has attached
for i in range(self.timeout): # up to self.timeout seconds to attach
time.sleep(1)
rc, out = cmd (['xpra', 'info', self.display])
search = 'clients=1'
if search in out:
debug("Client successfully attached!")
break
debug("Could not find '%s' in:\n" % search)
debug(out)
msg("TODO: filter '~/.xpra/run-xpra'")
def run_xsandbox(command, opt):
'''Run X application in a sandbox'''
old_cwd = os.getcwd()
# first, start X
if opt.xserver.lower() == "xephyr":
x = SandboxXephyr(command[0], geometry=opt.xephyr_geometry,
xauth=opt.xauthority)
elif opt.xserver.lower() == "xpra3d":
x = SandboxXpra(command[0], geometry=None,
driver="xdummy",
xauth=opt.xauthority,
clipboard=opt.with_clipboard)
else:
x = SandboxXpra(command[0], geometry=None,
xauth=opt.xauthority,
clipboard=opt.with_clipboard)
x.verify_host_setup()
# Debug: show old environment
keys = x.old_environ.keys()
keys.sort()
for k in keys:
debug ("Old: %s=%s" % (k, x.old_environ[k]))
try:
x.start()
except Exception as e:
error(e)
os.chdir(old_cwd)
if not opt.read_path:
opt.read_path = []
opt.read_path.append(x.xauth)
# Only used with dynamic profiles
required_rules = ['audit deny @{HOME}/.Xauthority mrwlk,']
# aa-exec
try:
rc, report = aa_exec(command, opt, x.new_environ, required_rules)
except Exception:
x.cleanup()
raise
x.cleanup()
return rc, report