#!/usr/bin/python3 # ------------------------------------------------------------------ # # Copyright (C) 2010-2011 Canonical Ltd. # Copyright (C) 2020 Christian Boltz # # 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 testlib import write_file prefix = "simple_tests/generated_x" prefix_leading = "simple_tests/generated_perms_leading" prefix_safe = "simple_tests/generated_perms_safe" trans_types = ("p", "P", "c", "C", "u", "i") modifiers = ("i", "u") trans_modifiers = { "p": modifiers, "P": modifiers, "c": modifiers, "C": modifiers, } targets = ("", "target", "target2") # null_target uses "_" instead of "" because "" gets skipped in some for loops. Replace with "" when actually using the value. null_target = ("_",) named_trans = { "p": targets, "P": targets, "c": targets, "C": targets, "u": null_target, "i": null_target, } safe_map = { "p": "unsafe", "P": "safe", "c": "unsafe", "C": "safe", "u": "", "i": "", } invert_safe = { "safe": "unsafe", "unsafe": "safe", '': '', } # audit qualifier disabled for now it really shouldn't affect the conflict # test but it may be worth checking every once in awhile # qualifiers = ("", "owner", "audit", "audit owner") qualifiers = ("", "owner") count = 0 def gen_list(): output = [] for trans in trans_types: if trans in trans_modifiers: for mod in trans_modifiers[trans]: output.append("{}{}x".format(trans, mod)) output.append("{}x".format(trans)) return output def test_gen_list(): """test if gen_list returns the expected output""" expected = "pix pux px Pix Pux Px cix cux cx Cix Cux Cx ux ix".split() actual = gen_list() if actual != expected: raise Exception("gen_list produced unexpected result, expected {}, got {}".format(expected, actual)) def build_rule(leading, qual, name, perm, target): rule = '' if leading: rule += "\t{} {} {}".format(qual, perm, name) else: rule += "\t{} {} {}".format(qual, name, perm) if target: rule += " -> {}".format(target) rule += ",\n" return rule def gen_file(name, xres, leading1, qual1, rule1, perm1, target1, leading2, qual2, rule2, perm2, target2): global count count += 1 content = '' content += "#\n" content += "#=DESCRIPTION {}\n".format(name) content += "#=EXRESULT {}\n".format(xres) content += "#\n" content += "/usr/bin/foo {\n" content += build_rule(leading1, qual1, rule1, perm1, target1) content += build_rule(leading2, qual2, rule2, perm2, target2) content += "}\n" write_file('', name, content) # NOTE: currently we don't do px to cx, or cx to px conversion # so # /foo { # /* px -> /foo//bar, # /* cx -> bar, # # will conflict # # NOTE: conflict tests don't test leading permissions or using unsafe keywords # It is assumed that there are extra tests to verify 1 to 1 correspondance def gen_files(name, rule1, rule2, default): perms = gen_list() for i in perms: for t in named_trans[i[0]]: if t == '_': t = '' for q in qualifiers: for j in perms: for u in named_trans[j[0]]: if u == '_': u = '' for r in qualifiers: file = prefix + '/' + name + '-' + q + i + t + '-' + r + j + u + '.sd' # override failures when transitions are the same xres = default if (i == j and t == u): xres = "PASS" gen_file(file, xres, 0, q, rule1, i, t, 0, r, rule2, j, u) def gen_conflicting_x(): gen_files("conflict", "/bin/cat", "/bin/cat", "FAIL") def gen_overlap_re_exact(): gen_files("exact", "/bin/cat", "/bin/*", "PASS") # we currently don't support this, once supported change to "PASS" def gen_dominate_re_re(): gen_files("dominate", "/bin/*", "/bin/**", "FAIL") def gen_ambiguous_re_re(): gen_files("ambiguous", "/bin/a*", "/bin/*b", "FAIL") # test that rules that lead with permissions don't conflict with # the same rule using trailing permissions. def gen_leading_perms(name, rule1, rule2): perms = gen_list() for i in perms: for t in named_trans[i[0]]: if t == '_': t = '' for q in qualifiers: file = prefix_leading + '/' + name + '-' + q + i + t + ".sd" gen_file(file, "PASS", 0, q, rule1, i, t, 1, q, rule2, i, t) # test for rules with leading safe or unsafe keywords. # check they are equivalent to their counterpart, # or if $invert that they properly conflict with their counterpart def gen_safe_perms(name, xres, invert, rule1, rule2): perms = gen_list() for i in perms: for t in named_trans[i[0]]: if t == '_': t = '' for q in qualifiers: qual = safe_map[i[0]] if invert: qual = invert_safe[qual] if (not invert or qual): file = prefix_safe + '/' + name + '-' + invert + '-' + q + qual + '-' + 'rule-' + i + t + '.sd' gen_file(file, xres, 0, '{} {}'.format(q, qual), rule1, i, t, 1, q, rule2, i, t) file = prefix_safe + '/' + name + '-' + invert + '-' + q + qual + i + '-' + 'rule-' + t + '.sd' gen_file(file, xres, 0, q, rule1, i, t, 1, '{} {}'.format(q, qual), rule2, i, t) test_gen_list() gen_conflicting_x() gen_overlap_re_exact() gen_dominate_re_re() gen_ambiguous_re_re() gen_leading_perms("exact", "/bin/cat", "/bin/cat") gen_leading_perms("exact-re", "/bin/*", "/bin/*") gen_leading_perms("overlap", "/*", "/bin/cat") gen_leading_perms("dominate", "/**", "/*") gen_leading_perms("ambiguous", "/a*", "/*b") gen_safe_perms("exact", "PASS", "", "/bin/cat", "/bin/cat") gen_safe_perms("exact-re", "PASS", "", "/bin/*", "/bin/*") gen_safe_perms("overlap", "PASS", "", "/*", "/bin/cat") gen_safe_perms("dominate", "PASS", "", "/**", "/*") gen_safe_perms("ambiguous", "PASS", "", "/a*", "/*b") gen_safe_perms("exact", "FAIL", "inv", "/bin/cat", "/bin/cat") gen_safe_perms("exact-re", "FAIL", "inv", "/bin/*", "/bin/*") gen_safe_perms("overlap", "PASS", "inv", "/*", "/bin/cat") gen_safe_perms("dominate", "FAIL", "inv", "/**", "/*") gen_safe_perms("ambiguous", "FAIL", "inv", "/a*", "/*b") print("Generated {} xtransition interaction tests".format(count))