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 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_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
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 as e: x.cleanup() raise x.cleanup() return rc, report
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 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 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
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
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 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 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'")