def test_ls(policy_parser: PolicyParser, setup_testdir): ls = which('ls') text = """ #![profile '%s'] fs('%s', read|exec) fs('/etc/ld.so.cache', read|exec|getattr) fs('/usr/lib/ld-2.31.so', read|exec|getattr) fs('/lib64/ld-linux-x86-64.so.2', read) fs('/usr/lib/libcap.so.2', read|exec|getattr) fs('/usr/lib/libc.so.6', read|exec|getattr) fs('/usr/lib/locale/locale-archive', read|getattr) fs('/usr/share', exec) fs('/proc', exec) fs('/tmp/bpfbox', read|exec|getattr) fs('/tmp/bpfbox/a', getattr) fs('/tmp/bpfbox/b', getattr) fs('/tmp/bpfbox/c', getattr) fs('/tmp/bpfbox/d', getattr) proc('/usr/bin/ls', getattr) """ % (ls, ls) policy_parser.process_policy_text(text) out = subprocess.check_output([ls, '/tmp/bpfbox']).decode('utf-8') assert out.strip() == '\n'.join(sorted(os.listdir('/tmp/bpfbox')))
def test_open_implicit_taint(policy_parser: PolicyParser, setup_testdir): text = """ #![profile '%s'] """ % (OPEN_PATH) policy_parser.process_policy_text(text) with pytest.raises(subprocess.CalledProcessError): subprocess.check_output([OPEN_PATH])
def test_fs_policy_missing_file(policy_parser: PolicyParser, setup_testdir): text = """ #![profile '%s'] fs('/sdfoihsdfo/asdihsdfoui/asdpisdhf', read|write|exec) proc('/sdfoihsdfo/asdihsdfoui/asdpisdhf', getattr) """ % (OPEN_PATH) policy_parser.process_policy_text(text)
def generate_policy(self): logger.info('Generating policy...') policy_files = [] for (dirpath, dirnames, filenames) in os.walk(defs.policy_directory): policy_files.extend([os.path.join(dirpath, f) for f in filenames]) for f in policy_files: logger.info(f'Generating policy for {f}...') try: from bpfbox.dsl import PolicyParser PolicyParser.process_policy_file(f) except Exception as e: logger.error(f'Unable to generate policy for {f}!', exc_info=e) return logger.info('Generated policy successfully!')
def test_open_link_policy(policy_parser: PolicyParser, setup_testdir): text = """ #![profile '%s'] #[taint] fs('/tmp/bpfbox/a', read) fs('/tmp/bpfbox', write) fs('/tmp/bpfbox/a', link) """ % (OPEN_PATH) policy_parser.process_policy_text(text) subprocess.check_call([OPEN_PATH, 'link'])
def test_ipc_policy(policy_parser: PolicyParser, setup_testdir): sleep_path = which('sleep') text = """ #![profile '%s'] #[taint] signal(self, sigcheck) signal('%s', sigkill) """ % (IPC_PATH, sleep_path) policy_parser.process_policy_text(text) target_pid = subprocess.Popen([sleep_path, '10']).pid rc = subprocess.Popen([IPC_PATH, 'kill-target', str(target_pid)]).wait() assert rc == 0
def test_net_policy(policy_parser: PolicyParser, setup_testdir): text = """ #![profile '%s'] #[taint] net(inet, create) #[allow] { net(inet6, create|connect) } """ % (NET_PATH) policy_parser.process_policy_text(text) subprocess.check_call([NET_PATH, 'inet-create-and-connect']) with pytest.raises(subprocess.CalledProcessError): subprocess.check_call([NET_PATH, 'create-unix'])
def test_open_complex_policy_implicit_allow(policy_parser: PolicyParser, setup_testdir): text = """ #![profile '%s'] #[taint] fs('/tmp/bpfbox/a', read) fs('/tmp/bpfbox/a', read|write) fs('/tmp/bpfbox/b', append) fs('/tmp/bpfbox/c', read) fs('/tmp/bpfbox/d', exec) """ % (OPEN_PATH) policy_parser.process_policy_text(text) subprocess.check_call([OPEN_PATH, 'complex'])
def test_open_complex_policy_no_execute_permission(policy_parser: PolicyParser, setup_testdir): text = """ #![profile '%s'] #[taint] fs('/tmp/bpfbox/a', read) #[allow] { fs('/tmp/bpfbox/a', read|write) fs('/tmp/bpfbox/b', append) fs('/tmp/bpfbox/c', read) } """ % (OPEN_PATH) policy_parser.process_policy_text(text) with pytest.raises(subprocess.CalledProcessError): subprocess.check_call([OPEN_PATH, 'complex'])
def test_open_procfs_rules(policy_parser: PolicyParser, setup_testdir): sleep_path = which('sleep') text = """ #![profile '%s'] #[taint] fs('/tmp/bpfbox/a', read) fs('/proc', exec) proc('%s', read|exec) """ % (OPEN_PATH, sleep_path) policy_parser.process_policy_text(text) # /proc/self should always work subprocess.check_call([OPEN_PATH, 'proc-self']) sleep_pid = subprocess.Popen([sleep_path, '10']).pid subprocess.check_call([OPEN_PATH, 'proc-other', str(sleep_pid)])
def test_net_policy_smoke(policy_parser: PolicyParser, setup_testdir): text = """ #![profile '%s'] #[taint] { net(inet, bind|connect) net(inet6, bind|connect) } #[allow] { net(inet, accept|listen|send|recv) net(inet6, accept|listen|send|recv) } #[audit] { net(inet, create|shutdown) net(inet6, create|shutdown) } """ % (NET_PATH) policy_parser.process_policy_text(text)
def test_open_proc_other_not_allowed(policy_parser: PolicyParser, setup_testdir): sleep_path = which('sleep') text = """ #![profile '%s'] #[taint] fs('/tmp/bpfbox/a', read) fs('/proc', exec) """ % (OPEN_PATH) policy_parser.process_policy_text(text) # /proc/self should always work subprocess.check_call([OPEN_PATH, 'proc-self']) sleep_pid = subprocess.Popen([sleep_path, '10']).pid with pytest.raises(subprocess.CalledProcessError): subprocess.check_call([OPEN_PATH, 'proc-other', str(sleep_pid)])
def test_dsl_smoke(policy_parser: PolicyParser): text = """ #![profile "/usr/bin/ls"] #[allow] { #[audit] fs("/tmp/bpfbox/a", read|write|exec) #[taint] fs("/tmp/bpfbox/b", getattr|setattr|ioctl|rm) } fs("/tmp/bpfbox/c", read) #[taint] fs("/tmp/bpfbox/d", write) #[allow] #[audit] { #[taint] net(inet, bind|connect|accept) proc("/foo/bar/qux", read) } #[audit] signal('/usr/bin/grep', sigkill|sigchld) #[allow] ptrace(self) """ policy = policy_parser.parse_policy_text(text) pprint(policy) assert policy.profile == '/usr/bin/ls' assert len(policy.rules) == 8 assert policy.rules[0].action == BPFBOX_ACTION.ALLOW assert policy.rules[0].access == FS_ACCESS.READ assert policy.rules[0].pathname == '/tmp/bpfbox/c' assert policy.rules[1].action == BPFBOX_ACTION.TAINT assert policy.rules[1].access == FS_ACCESS.WRITE assert policy.rules[1].pathname == '/tmp/bpfbox/d' assert policy.rules[2].action == BPFBOX_ACTION.AUDIT assert policy.rules[2].access == IPC_ACCESS.SIGKILL | IPC_ACCESS.SIGCHLD assert policy.rules[2].other_exe == '/usr/bin/grep' assert policy.rules[3].action == BPFBOX_ACTION.ALLOW assert policy.rules[3].access == IPC_ACCESS.PTRACE assert policy.rules[3].other_exe == 'self' assert policy.rules[4].action == BPFBOX_ACTION.ALLOW | BPFBOX_ACTION.AUDIT assert policy.rules[ 4].access == FS_ACCESS.READ | FS_ACCESS.WRITE | FS_ACCESS.EXEC assert policy.rules[4].pathname == '/tmp/bpfbox/a' assert policy.rules[5].action == BPFBOX_ACTION.ALLOW | BPFBOX_ACTION.TAINT assert policy.rules[ 5].access == FS_ACCESS.GETATTR | FS_ACCESS.SETATTR | FS_ACCESS.IOCTL | FS_ACCESS.RM assert policy.rules[5].pathname == '/tmp/bpfbox/b' assert policy.rules[ 6].action == BPFBOX_ACTION.ALLOW | BPFBOX_ACTION.TAINT | BPFBOX_ACTION.AUDIT assert policy.rules[ 6].access == NET_ACCESS.BIND | NET_ACCESS.CONNECT | NET_ACCESS.ACCEPT assert policy.rules[6].family == NET_FAMILY.INET assert policy.rules[7].action == BPFBOX_ACTION.ALLOW | BPFBOX_ACTION.AUDIT assert policy.rules[7].access == FS_ACCESS.READ assert policy.rules[7].other_exe == '/foo/bar/qux'
def policy_parser(bpf_program: BPFProgram): yield PolicyParser()