From dec443e7c55bc615b0b5126ed2f02f5d5e6373ce Mon Sep 17 00:00:00 2001 From: Jeff Vander Stoep <jeffv@google.com> Date: Thu, 17 Aug 2017 14:12:44 -0700 Subject: [PATCH] add searchpolicy.py for automated tests searchpolicy.py provides a subset of the functionality of sesearch. The primary benefit being that it's entirely built in-tree and thus can be packaged for use in automated tests included compatibility test suites. Example searchpolicy.py --libpath out/host/linux-x86/lib64/ --allow --source domain Bug: 63397379 Test: Identical output with sesearch for the following commands --allow --source domain --allow --target domain --allow --target appdomain -p ioctl,open --allow --source lmkd -c file -p ioctl,open --allow --source lmkd -c file,dir -p ioctl,open Change-Id: I89a6c333f1f519d9171fbc1aafe27eaf5ad247f0 --- Android.mk | 1 + tests/Android.bp | 7 ++ tests/include/sepol_wrap.h | 3 + tests/policy.py | 116 +++++++++++++++++++++++++++------ tests/searchpolicy.py | 73 +++++++++++++++++++++ tests/sepol_wrap.cpp | 67 +++++++++++++++---- tests/treble_sepolicy_tests.py | 2 +- 7 files changed, 235 insertions(+), 34 deletions(-) create mode 100644 tests/searchpolicy.py diff --git a/Android.mk b/Android.mk index 5fbed9a78..b666a7eed 100644 --- a/Android.mk +++ b/Android.mk @@ -224,6 +224,7 @@ LOCAL_REQUIRED_MODULES += \ plat_seapp_contexts \ plat_service_contexts \ plat_hwservice_contexts \ + searchpolicy.py \ vndservice_contexts \ ifneq ($(with_asan),true) diff --git a/tests/Android.bp b/tests/Android.bp index 19aca9ccd..75bc91c82 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -32,3 +32,10 @@ cc_prebuilt_binary { host_supported: true, required: ["policy.py"], } + +cc_prebuilt_binary { + name: "searchpolicy.py", + srcs: ["searchpolicy.py"], + host_supported: true, + required: ["policy.py"], +} diff --git a/tests/include/sepol_wrap.h b/tests/include/sepol_wrap.h index 5615913eb..2357421c7 100644 --- a/tests/include/sepol_wrap.h +++ b/tests/include/sepol_wrap.h @@ -9,6 +9,9 @@ void destroy_policy(void *policydbp); void *init_avtab(void *policydbp); void *init_cond_avtab(void *policydbp); void destroy_avtab(void *avtab_iterp); +void *init_expanded_avtab(void *policydbp); +void *init_expanded_cond_avtab(void *policydbp); +void destroy_expanded_avtab(void *avtab_iterp); int get_type(char *out, size_t max_size, void *policydbp, void *type_iterp); void *init_type_iter(void *policydbp, const char *type, bool is_attr); void destroy_type_iter(void *type_iterp); diff --git a/tests/policy.py b/tests/policy.py index b8a362129..a0ddb9096 100644 --- a/tests/policy.py +++ b/tests/policy.py @@ -41,7 +41,8 @@ class TERule: self.rule = rule class Policy: - __Rules = None + __ExpandedRules = set() + __Rules = set() __FcDict = None __libsepolwrap = None __policydbP = None @@ -97,6 +98,50 @@ class Policy: self.__libsepolwrap.destroy_type_iter(TypeIterP) return TypeAttr + def __TERuleMatch(self, Rule, **kwargs): + # Match source type + if ("scontext" in kwargs and + len(kwargs['scontext']) > 0 and + Rule.sctx not in kwargs['scontext']): + return False + # Match target type + if ("tcontext" in kwargs and + len(kwargs['tcontext']) > 0 and + Rule.tctx not in kwargs['tcontext']): + return False + # Match target class + if ("tclass" in kwargs and + len(kwargs['tclass']) > 0 and + not bool(set([Rule.tclass]) & kwargs['tclass'])): + return False + # Match any perms + if ("perms" in kwargs and + len(kwargs['perms']) > 0 and + not bool(Rule.perms & kwargs['perms'])): + return False + return True + + # resolve a type to its attributes or + # resolve an attribute to its types and attributes + # For example if scontext is the domain attribute, then we need to + # include all types with the domain attribute such as untrusted_app and + # priv_app and all the attributes of those types such as appdomain. + def ResolveTypeAttribute(self, Type): + types = self.GetAllTypes(False) + attributes = self.GetAllTypes(True) + + if Type in types: + return self.QueryTypeAttribute(Type, False) + elif Type in attributes: + TypesAndAttributes = set() + Types = self.QueryTypeAttribute(Type, True) + TypesAndAttributes |= Types + for T in Types: + TypesAndAttributes |= self.QueryTypeAttribute(T, False) + return TypesAndAttributes + else: + return set() + # Return all TERules that match: # (any scontext) or (any tcontext) or (any tclass) or (any perms), # perms. @@ -106,23 +151,32 @@ class Policy: # Will return any rule with: # (tcontext="foo" or tcontext="bar") and ("entrypoint" in perms) def QueryTERule(self, **kwargs): - if self.__Rules is None: + if len(self.__Rules) == 0: self.__InitTERules() + + # add any matching types and attributes for scontext and tcontext + if ("scontext" in kwargs and len(kwargs['scontext']) > 0): + scontext = set() + for sctx in kwargs['scontext']: + scontext |= self.ResolveTypeAttribute(sctx) + kwargs['scontext'] = scontext + if ("tcontext" in kwargs and len(kwargs['tcontext']) > 0): + tcontext = set() + for tctx in kwargs['tcontext']: + tcontext |= self.ResolveTypeAttribute(tctx) + kwargs['tcontext'] = tcontext for Rule in self.__Rules: - # Match source type - if "scontext" in kwargs and Rule.sctx not in kwargs['scontext']: - continue - # Match target type - if "tcontext" in kwargs and Rule.tctx not in kwargs['tcontext']: - continue - # Match target class - if "tclass" in kwargs and Rule.tclass not in kwargs['tclass']: - continue - # Match any perms - if "perms" in kwargs and not bool(Rule.perms & set(kwargs['perms'])): - continue - yield Rule + if self.__TERuleMatch(Rule, **kwargs): + yield Rule + # Same as QueryTERule but only using the expanded ruleset. + # i.e. all attributes have been expanded to their various types. + def QueryExpandedTERule(self, **kwargs): + if len(self.__ExpandedRules) == 0: + self.__InitExpandedTERules() + for Rule in self.__ExpandedRules: + if self.__TERuleMatch(Rule, **kwargs): + yield Rule def GetAllTypes(self, isAttr): TypeIterP = self.__libsepolwrap.init_type_iter(self.__policydbP, None, isAttr) @@ -155,9 +209,9 @@ class Policy: return Types - def __GetTERules(self, policydbP, avtabIterP): - if self.__Rules is None: - self.__Rules = set() + def __GetTERules(self, policydbP, avtabIterP, Rules): + if Rules is None: + Rules = set() buf = create_string_buffer(self.__BUFSIZE) ret = 0 while True: @@ -165,7 +219,7 @@ class Policy: policydbP, avtabIterP) if ret == 0: Rule = TERule(buf.value) - self.__Rules.add(Rule) + Rules.add(Rule) continue if ret == 1: break; @@ -176,14 +230,26 @@ class Policy: avtabIterP = self.__libsepolwrap.init_avtab(self.__policydbP) if (avtabIterP == None): sys.exit("Failed to initialize avtab") - self.__GetTERules(self.__policydbP, avtabIterP) + self.__GetTERules(self.__policydbP, avtabIterP, self.__Rules) self.__libsepolwrap.destroy_avtab(avtabIterP) avtabIterP = self.__libsepolwrap.init_cond_avtab(self.__policydbP) if (avtabIterP == None): sys.exit("Failed to initialize conditional avtab") - self.__GetTERules(self.__policydbP, avtabIterP) + self.__GetTERules(self.__policydbP, avtabIterP, self.__Rules) self.__libsepolwrap.destroy_avtab(avtabIterP) + def __InitExpandedTERules(self): + avtabIterP = self.__libsepolwrap.init_expanded_avtab(self.__policydbP) + if (avtabIterP == None): + sys.exit("Failed to initialize avtab") + self.__GetTERules(self.__policydbP, avtabIterP, self.__ExpandedRules) + self.__libsepolwrap.destroy_expanded_avtab(avtabIterP) + avtabIterP = self.__libsepolwrap.init_expanded_cond_avtab(self.__policydbP) + if (avtabIterP == None): + sys.exit("Failed to initialize conditional avtab") + self.__GetTERules(self.__policydbP, avtabIterP, self.__ExpandedRules) + self.__libsepolwrap.destroy_expanded_avtab(avtabIterP) + # load ctypes-ified libsepol wrapper def __InitLibsepolwrap(self, LibPath): if "linux" in sys.platform: @@ -201,6 +267,14 @@ class Policy: lib.load_policy.argtypes = [c_char_p] # void destroy_policy(void *policydbp); lib.destroy_policy.argtypes = [c_void_p] + # void *init_expanded_avtab(void *policydbp); + lib.init_expanded_avtab.restype = c_void_p + lib.init_expanded_avtab.argtypes = [c_void_p] + # void *init_expanded_cond_avtab(void *policydbp); + lib.init_expanded_cond_avtab.restype = c_void_p + lib.init_expanded_cond_avtab.argtypes = [c_void_p] + # void destroy_expanded_avtab(void *avtab_iterp); + lib.destroy_expanded_avtab.argtypes = [c_void_p] # void *init_avtab(void *policydbp); lib.init_avtab.restype = c_void_p lib.init_avtab.argtypes = [c_void_p] diff --git a/tests/searchpolicy.py b/tests/searchpolicy.py new file mode 100644 index 000000000..ff9318b7f --- /dev/null +++ b/tests/searchpolicy.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python + +import argparse +import policy + +parser = argparse.ArgumentParser( + description="SELinux policy rule search tool. Intended to have a similar " + + "API as sesearch, but simplified to use only code availabe in AOSP") +parser.add_argument("policy", help="Path to the SELinux policy to search.", nargs="?") +parser.add_argument("--libpath", dest="libpath", help="Path to the libsepolwrap.so", nargs="?") +tertypes = parser.add_argument_group("TE Rule Types") +tertypes.add_argument("--allow", action="append_const", + const="allow", dest="tertypes", + help="Search allow rules.") +expr = parser.add_argument_group("Expressions") +expr.add_argument("-s", "--source", + help="Source type/role of the TE/RBAC rule.") +expr.add_argument("-t", "--target", + help="Target type/role of the TE/RBAC rule.") +expr.add_argument("-c", "--class", dest="tclass", + help="Comma separated list of object classes") +expr.add_argument("-p", "--perms", metavar="PERMS", + help="Comma separated list of permissions.") + +args = parser.parse_args() + +if not args.tertypes: + parser.error("Must specify \"--allow\"") + +if not args.policy: + parser.error("Must include path to policy") +if not args.libpath: + parser.error("Must include path to libsepolwrap library") + +if not (args.source or args.target or args.tclass or args.perms): + parser.error("Must something to filter on, e.g. --source, --target, etc.") + +pol = policy.Policy(args.policy, None, args.libpath) + +if args.source: + scontext = {args.source} +else: + scontext = set() +if args.target: + tcontext = {args.target} +else: + tcontext = set() +if args.tclass: + tclass = set(args.tclass.split(",")) +else: + tclass = set() +if args.perms: + perms = set(args.perms.split(",")) +else: + perms = set() + +TERules = pol.QueryTERule(scontext=scontext, + tcontext=tcontext, + tclass=tclass, + perms=perms) + +# format rules for printing +rules = [] +for r in TERules: + if len(r.perms) > 1: + rules.append("allow " + r.sctx + " " + r.tctx + ":" + r.tclass + " { " + + " ".join(r.perms) + " };") + else: + rules.append("allow " + r.sctx + " " + r.tctx + ":" + r.tclass + " " + + " ".join(r.perms) + ";") + +for r in sorted(rules): + print r diff --git a/tests/sepol_wrap.cpp b/tests/sepol_wrap.cpp index 8fea2d5b4..d537b7e00 100644 --- a/tests/sepol_wrap.cpp +++ b/tests/sepol_wrap.cpp @@ -181,7 +181,7 @@ void *load_policy(const char *policy_path) /* items needed to iterate over the avtab */ struct avtab_iter { - avtab_t avtab; + avtab_t *avtab; uint32_t i; avtab_ptr_t cur; }; @@ -198,9 +198,9 @@ static int get_avtab_allow_rule(char *out, size_t max_size, policydb_t *db, { size_t len; - for (; avtab_i->i < avtab_i->avtab.nslot; (avtab_i->i)++) { + for (; avtab_i->i < avtab_i->avtab->nslot; (avtab_i->i)++) { if (avtab_i->cur == NULL) { - avtab_i->cur = avtab_i->avtab.htable[avtab_i->i]; + avtab_i->cur = avtab_i->avtab->htable[avtab_i->i]; } for (; avtab_i->cur; avtab_i->cur = (avtab_i->cur)->next) { if (!((avtab_i->cur)->key.specified & AVTAB_ALLOWED)) continue; @@ -233,6 +233,37 @@ int get_allow_rule(char *out, size_t len, void *policydbp, void *avtab_iterp) return get_avtab_allow_rule(out, len, db, avtab_i); } +static avtab_iter *init_avtab_common(avtab_t *in) +{ + struct avtab_iter *out = (struct avtab_iter *) + calloc(1, sizeof(struct avtab_iter)); + if (!out) { + std::cerr << "Failed to allocate avtab iterator" << std::endl; + return NULL; + } + + out->avtab = in; + return out; +} + +void *init_avtab(void *policydbp) +{ + policydb_t *p = static_cast<policydb_t *>(policydbp); + return static_cast<void *>(init_avtab_common(&p->te_avtab)); +} + +void *init_cond_avtab(void *policydbp) +{ + policydb_t *p = static_cast<policydb_t *>(policydbp); + return static_cast<void *>(init_avtab_common(&p->te_cond_avtab)); +} + +void destroy_avtab(void *avtab_iterp) +{ + struct avtab_iter *avtab_i = static_cast<struct avtab_iter *>(avtab_iterp); + free(avtab_i); +} + /* * <sepol/policydb/expand.h->conditional.h> uses 'bool' as a variable name * inside extern "C" { .. } construct, which clang doesn't like. @@ -240,45 +271,57 @@ int get_allow_rule(char *out, size_t len, void *policydbp, void *avtab_iterp) */ extern "C" int expand_avtab(policydb_t *p, avtab_t *a, avtab_t *expa); -static avtab_iter *init_avtab_common(avtab_t *in, policydb_t *p) +static avtab_iter *init_expanded_avtab_common(avtab_t *in, policydb_t *p) { struct avtab_iter *out = (struct avtab_iter *) calloc(1, sizeof(struct avtab_iter)); if (!out) { + std::cerr << "Failed to allocate avtab iterator" << std::endl; + return NULL; + } + + avtab_t *avtab = (avtab_t *) calloc(1, sizeof(avtab_t)); + + if (!avtab) { std::cerr << "Failed to allocate avtab" << std::endl; + free(out); return NULL; } - if (avtab_init(&out->avtab)) { + out->avtab = avtab; + if (avtab_init(out->avtab)) { std::cerr << "Failed to initialize avtab" << std::endl; + free(avtab); free(out); return NULL; } - if (expand_avtab(p, in, &out->avtab)) { + if (expand_avtab(p, in, out->avtab)) { std::cerr << "Failed to expand avtab" << std::endl; + free(avtab); free(out); return NULL; } return out; } -void *init_avtab(void *policydbp) +void *init_expanded_avtab(void *policydbp) { policydb_t *p = static_cast<policydb_t *>(policydbp); - return static_cast<void *>(init_avtab_common(&p->te_avtab, p)); + return static_cast<void *>(init_expanded_avtab_common(&p->te_avtab, p)); } -void *init_cond_avtab(void *policydbp) +void *init_expanded_cond_avtab(void *policydbp) { policydb_t *p = static_cast<policydb_t *>(policydbp); - return static_cast<void *>(init_avtab_common(&p->te_cond_avtab, p)); + return static_cast<void *>(init_expanded_avtab_common(&p->te_cond_avtab, p)); } -void destroy_avtab(void *avtab_iterp) +void destroy_expanded_avtab(void *avtab_iterp) { struct avtab_iter *avtab_i = static_cast<struct avtab_iter *>(avtab_iterp); - avtab_destroy(&avtab_i->avtab); + avtab_destroy(avtab_i->avtab); + free(avtab_i->avtab); free(avtab_i); } diff --git a/tests/treble_sepolicy_tests.py b/tests/treble_sepolicy_tests.py index 58fd85bc3..0e0c0c2c7 100644 --- a/tests/treble_sepolicy_tests.py +++ b/tests/treble_sepolicy_tests.py @@ -129,7 +129,7 @@ def GetCoreDomains(): # def GetDomainEntrypoints(pol): global alldomains - for x in pol.QueryTERule(tclass="file", perms=["entrypoint"]): + for x in pol.QueryExpandedTERule(tclass=set(["file"]), perms=set(["entrypoint"])): if not x.sctx in alldomains: continue alldomains[x.sctx].entrypoints.append(str(x.tctx)) -- GitLab