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 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 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 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)
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 reload_profile(self, profile): if not self.do_reload: return cmd_info = cmd([apparmor.parser, '-I%s' % apparmor.profile_dir, '--base', apparmor.profile_dir, '-r', profile]) if cmd_info[0] != 0: raise apparmor.AppArmorException(cmd_info[1])
def unload_profile(self, profile): if not self.do_reload: return # FIXME: should ensure profile is loaded before unloading cmd_info = cmd([apparmor.parser, '-I%s' % apparmor.profile_dir, '--base', apparmor.profile_dir, '-R', profile]) if cmd_info[0] != 0: raise apparmor.AppArmorException(cmd_info[1])
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 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 test_network_keyword_list(self): rc, output = cmd('../../common/list_af_names.sh') self.assertEqual(rc, 0) af_names = [] af_pairs = output.replace('AF_', '').strip().lower().split(",") for af_pair in af_pairs: af_name = af_pair.lstrip().split(" ")[0] # skip max af name definition if len(af_name) > 0 and af_name != "max": af_names.append(af_name) missing_af_names = [] for keyword in af_names: if keyword not in network_domain_keywords: # keywords missing in the system are ok (= older kernel), but network_domain_keywords needs to have the full list missing_af_names.append(keyword) self.assertEqual( missing_af_names, [], 'Missing af_names in NetworkRule network_domain_keywords. This test is likely running ' 'on an newer kernel and will require updating the list of network domain keywords in utils/apparmor/rule/network.py' )
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'")